Commit dabf6003 authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[bigint] Support <, <=, >, >=.

... mainly by adapting Object::Compare and
CodeStubAssembler::RelationalComparison.

TBR=rmcilroy@chromium.org

Change-Id: I34448d45b4950b9318263c4a667aa9db7d77232d
Bug: v8:6791
Reviewed-on: https://chromium-review.googlesource.com/730730
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48873}
parent 0105522f
......@@ -128,6 +128,15 @@ TF_BUILTIN(NonNumberToNumeric, CodeStubAssembler) {
Return(NonNumberToNumeric(context, input));
}
TF_BUILTIN(ToNumeric, CodeStubAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* input = Parameter(Descriptor::kArgument);
Return(Select(IsNumber(input), [=] { return input; },
[=] { return NonNumberToNumeric(context, input); },
MachineRepresentation::kTagged));
}
// ES6 section 7.1.3 ToNumber ( argument )
TF_BUILTIN(ToNumber, CodeStubAssembler) {
Node* context = Parameter(Descriptor::kContext);
......
......@@ -186,6 +186,7 @@ namespace internal {
TFC(NonNumberToNumber, TypeConversion, 1) \
TFC(NonNumberToNumeric, TypeConversion, 1) \
TFC(ToNumber, TypeConversion, 1) \
TFC(ToNumeric, TypeConversion, 1) \
TFC(ToString, TypeConversion, 1) \
TFC(ToInteger, TypeConversion, 1) \
TFC(ToLength, TypeConversion, 1) \
......
......@@ -874,19 +874,22 @@ TF_BUILTIN(BitwiseXor, NumberBuiltinsAssembler) {
}
TF_BUILTIN(LessThan, NumberBuiltinsAssembler) {
RelationalComparisonBuiltin<Descriptor>(kLessThan);
RelationalComparisonBuiltin<Descriptor>(RelationalComparisonMode::kLessThan);
}
TF_BUILTIN(LessThanOrEqual, NumberBuiltinsAssembler) {
RelationalComparisonBuiltin<Descriptor>(kLessThanOrEqual);
RelationalComparisonBuiltin<Descriptor>(
RelationalComparisonMode::kLessThanOrEqual);
}
TF_BUILTIN(GreaterThan, NumberBuiltinsAssembler) {
RelationalComparisonBuiltin<Descriptor>(kGreaterThan);
RelationalComparisonBuiltin<Descriptor>(
RelationalComparisonMode::kGreaterThan);
}
TF_BUILTIN(GreaterThanOrEqual, NumberBuiltinsAssembler) {
RelationalComparisonBuiltin<Descriptor>(kGreaterThanOrEqual);
RelationalComparisonBuiltin<Descriptor>(
RelationalComparisonMode::kGreaterThanOrEqual);
}
TF_BUILTIN(Equal, CodeStubAssembler) {
......
......@@ -14,7 +14,6 @@
namespace v8 {
namespace internal {
typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode;
typedef compiler::Node Node;
template <class T>
using TNode = compiler::TNode<T>;
......
This diff is collapsed.
......@@ -1720,13 +1720,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
void InitializeFieldsWithRoot(Node* object, Node* start_offset,
Node* end_offset, Heap::RootListIndex root);
enum RelationalComparisonMode {
kLessThan,
kLessThanOrEqual,
kGreaterThan,
kGreaterThanOrEqual
};
Node* RelationalComparison(RelationalComparisonMode mode, Node* lhs,
Node* rhs, Node* context,
Variable* var_type_feedback = nullptr);
......
......@@ -1818,20 +1818,22 @@ class InterpreterCompareOpAssembler : public InterpreterAssembler {
result = StrictEqual(lhs, rhs, &var_type_feedback);
break;
case Token::LT:
result = RelationalComparison(CodeStubAssembler::kLessThan, lhs, rhs,
context, &var_type_feedback);
result = RelationalComparison(RelationalComparisonMode::kLessThan, lhs,
rhs, context, &var_type_feedback);
break;
case Token::GT:
result = RelationalComparison(CodeStubAssembler::kGreaterThan, lhs, rhs,
context, &var_type_feedback);
result = RelationalComparison(RelationalComparisonMode::kGreaterThan,
lhs, rhs, context, &var_type_feedback);
break;
case Token::LTE:
result = RelationalComparison(CodeStubAssembler::kLessThanOrEqual, lhs,
rhs, context, &var_type_feedback);
result =
RelationalComparison(RelationalComparisonMode::kLessThanOrEqual,
lhs, rhs, context, &var_type_feedback);
break;
case Token::GTE:
result = RelationalComparison(CodeStubAssembler::kGreaterThanOrEqual,
lhs, rhs, context, &var_type_feedback);
result =
RelationalComparison(RelationalComparisonMode::kGreaterThanOrEqual,
lhs, rhs, context, &var_type_feedback);
break;
default:
UNREACHABLE();
......
......@@ -491,7 +491,6 @@ ComparisonResult NumberCompare(double x, double y) {
}
}
bool NumberEquals(double x, double y) {
// Must check explicitly for NaN's on Windows, but -0 works fine.
if (std::isnan(x)) return false;
......@@ -499,18 +498,25 @@ bool NumberEquals(double x, double y) {
return x == y;
}
bool NumberEquals(const Object* x, const Object* y) {
return NumberEquals(x->Number(), y->Number());
}
bool NumberEquals(Handle<Object> x, Handle<Object> y) {
return NumberEquals(*x, *y);
}
} // namespace
ComparisonResult Invert(ComparisonResult result) {
if (result == ComparisonResult::kLessThan) {
return ComparisonResult::kGreaterThan;
}
if (result == ComparisonResult::kGreaterThan) {
return ComparisonResult::kLessThan;
}
return result;
}
} // anonymous namespace
// static
Maybe<ComparisonResult> Object::Compare(Handle<Object> x, Handle<Object> y) {
......@@ -525,10 +531,23 @@ Maybe<ComparisonResult> Object::Compare(Handle<Object> x, Handle<Object> y) {
String::Compare(Handle<String>::cast(x), Handle<String>::cast(y)));
}
// ES6 section 7.2.11 Abstract Relational Comparison step 6.
if (!Object::ToNumber(x).ToHandle(&x) || !Object::ToNumber(y).ToHandle(&y)) {
if (!Object::ToNumeric(x).ToHandle(&x) ||
!Object::ToNumeric(y).ToHandle(&y)) {
return Nothing<ComparisonResult>();
}
return Just(NumberCompare(x->Number(), y->Number()));
bool x_is_number = x->IsNumber();
bool y_is_number = y->IsNumber();
if (x_is_number && y_is_number) {
return Just(NumberCompare(x->Number(), y->Number()));
} else if (!x_is_number && !y_is_number) {
return Just(BigInt::CompareToBigInt(Handle<BigInt>::cast(x),
Handle<BigInt>::cast(y)));
} else if (x_is_number) {
return Just(Invert(BigInt::CompareToNumber(Handle<BigInt>::cast(y), x)));
} else {
return Just(BigInt::CompareToNumber(Handle<BigInt>::cast(x), y));
}
}
......
......@@ -894,6 +894,12 @@ enum FixedArraySubInstanceType {
LAST_FIXED_ARRAY_SUB_TYPE = WEAK_NEW_SPACE_OBJECT_TO_CODE_SUB_TYPE
};
enum class RelationalComparisonMode {
kLessThan,
kLessThanOrEqual,
kGreaterThan,
kGreaterThanOrEqual
};
// TODO(bmeurer): Remove this in favor of the ComparisonResult below.
enum CompareResult {
......@@ -904,7 +910,6 @@ enum CompareResult {
NOT_EQUAL = GREATER
};
// Result of an abstract relational comparison of x and y, implemented according
// to ES6 section 7.2.11 Abstract Relational Comparison.
enum class ComparisonResult {
......@@ -914,6 +919,24 @@ enum class ComparisonResult {
kUndefined // at least one of x or y was undefined or NaN
};
// (Returns false whenever {result} is kUndefined.)
static inline bool ComparisonResultToBool(RelationalComparisonMode mode,
ComparisonResult result) {
switch (mode) {
case RelationalComparisonMode::kLessThan:
return result == ComparisonResult::kLessThan;
case RelationalComparisonMode::kLessThanOrEqual:
return result == ComparisonResult::kLessThan ||
result == ComparisonResult::kEqual;
case RelationalComparisonMode::kGreaterThan:
return result == ComparisonResult::kGreaterThan;
case RelationalComparisonMode::kGreaterThanOrEqual:
return result == ComparisonResult::kGreaterThan ||
result == ComparisonResult::kEqual;
}
UNREACHABLE();
}
class AbstractCode;
class AccessorPair;
class AllocationSite;
......
......@@ -174,11 +174,13 @@ ComparisonResult UnequalSign(bool left_negative) {
return left_negative ? ComparisonResult::kLessThan
: ComparisonResult::kGreaterThan;
}
// Produces result for |x| > |y|, with {both_negative} == sign(x) == sign(y);
ComparisonResult AbsoluteGreater(bool both_negative) {
return both_negative ? ComparisonResult::kLessThan
: ComparisonResult::kGreaterThan;
}
// Produces result for |x| < |y|, with {both_negative} == sign(x) == sign(y).
ComparisonResult AbsoluteLess(bool both_negative) {
return both_negative ? ComparisonResult::kGreaterThan
......@@ -187,6 +189,7 @@ ComparisonResult AbsoluteLess(bool both_negative) {
} // namespace
// (Never returns kUndefined.)
ComparisonResult BigInt::CompareToBigInt(Handle<BigInt> x, Handle<BigInt> y) {
bool x_sign = x->sign();
if (x_sign != y->sign()) return UnequalSign(x_sign);
......@@ -197,6 +200,25 @@ ComparisonResult BigInt::CompareToBigInt(Handle<BigInt> x, Handle<BigInt> y) {
return ComparisonResult::kEqual;
}
bool BigInt::CompareToBigIntReturnBool(RelationalComparisonMode mode,
Handle<BigInt> x, Handle<BigInt> y) {
ComparisonResult result = CompareToBigInt(x, y);
DCHECK_NE(result, ComparisonResult::kUndefined);
switch (mode) {
case RelationalComparisonMode::kLessThan:
return result == ComparisonResult::kLessThan;
case RelationalComparisonMode::kLessThanOrEqual:
return result == ComparisonResult::kLessThan ||
result == ComparisonResult::kEqual;
case RelationalComparisonMode::kGreaterThan:
return result == ComparisonResult::kGreaterThan;
case RelationalComparisonMode::kGreaterThanOrEqual:
return result == ComparisonResult::kGreaterThan ||
result == ComparisonResult::kEqual;
}
UNREACHABLE();
}
bool BigInt::EqualToBigInt(BigInt* x, BigInt* y) {
if (x->sign() != y->sign()) return false;
if (x->length() != y->length()) return false;
......
......@@ -38,6 +38,8 @@ class V8_EXPORT_PRIVATE BigInt : public HeapObject {
Handle<BigInt> y);
// More convenient version of "bool LessThan(x, y)".
static ComparisonResult CompareToBigInt(Handle<BigInt> x, Handle<BigInt> y);
static bool CompareToBigIntReturnBool(RelationalComparisonMode mode,
Handle<BigInt> x, Handle<BigInt> y);
static bool EqualToBigInt(BigInt* x, BigInt* y);
static Handle<BigInt> BitwiseAnd(Handle<BigInt> x, Handle<BigInt> y);
static Handle<BigInt> BitwiseXor(Handle<BigInt> x, Handle<BigInt> y);
......@@ -56,6 +58,8 @@ class V8_EXPORT_PRIVATE BigInt : public HeapObject {
static bool EqualToString(Handle<BigInt> x, Handle<String> y);
static bool EqualToNumber(Handle<BigInt> x, Handle<Object> y);
static ComparisonResult CompareToNumber(Handle<BigInt> x, Handle<Object> y);
static bool CompareToNumberReturnBool(RelationalComparisonMode mode,
Handle<BigInt> x, Handle<Object> y);
// Exposed for tests, do not call directly. Use CompareToNumber() instead.
static ComparisonResult CompareToDouble(Handle<BigInt> x, double y);
......
......@@ -13,6 +13,30 @@
namespace v8 {
namespace internal {
RUNTIME_FUNCTION(Runtime_BigIntCompareToBigInt) {
SealHandleScope shs(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(Smi, mode, 0);
CONVERT_ARG_HANDLE_CHECKED(BigInt, lhs, 1);
CONVERT_ARG_HANDLE_CHECKED(BigInt, rhs, 2);
bool result = ComparisonResultToBool(
static_cast<RelationalComparisonMode>(mode->value()),
BigInt::CompareToBigInt(lhs, rhs));
return *isolate->factory()->ToBoolean(result);
}
RUNTIME_FUNCTION(Runtime_BigIntCompareToNumber) {
SealHandleScope shs(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(Smi, mode, 0);
CONVERT_ARG_HANDLE_CHECKED(BigInt, lhs, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, rhs, 2);
bool result = ComparisonResultToBool(
static_cast<RelationalComparisonMode>(mode->value()),
BigInt::CompareToNumber(lhs, rhs));
return *isolate->factory()->ToBoolean(result);
}
RUNTIME_FUNCTION(Runtime_BigIntEqual) {
SealHandleScope shs(isolate);
DCHECK_EQ(2, args.length());
......@@ -22,6 +46,7 @@ RUNTIME_FUNCTION(Runtime_BigIntEqual) {
BigInt::EqualToBigInt(BigInt::cast(*lhs), BigInt::cast(*rhs));
return *isolate->factory()->ToBoolean(result);
// TODO(neis): Remove IsBigInt checks?
// TODO(neis): Rename to BigIntEqualToBigInt.
}
RUNTIME_FUNCTION(Runtime_BigIntEqualToNumber) {
......
......@@ -71,6 +71,8 @@ namespace internal {
#define FOR_EACH_INTRINSIC_BIGINT(F) \
F(BigIntBinaryOp, 3, 1) \
F(BigIntCompareToBigInt, 3, 1) \
F(BigIntCompareToNumber, 3, 1) \
F(BigIntEqual, 2, 1) \
F(BigIntEqualToNumber, 2, 1) \
F(BigIntEqualToString, 2, 1) \
......
......@@ -2686,8 +2686,8 @@ TEST(BranchIfNumericRelationalComparison) {
CodeStubAssembler m(asm_tester.state());
Label return_true(&m), return_false(&m);
m.BranchIfNumericRelationalComparison(
CodeStubAssembler::kGreaterThanOrEqual, m.Parameter(0), m.Parameter(1),
&return_true, &return_false);
RelationalComparisonMode::kGreaterThanOrEqual, m.Parameter(0),
m.Parameter(1), &return_true, &return_false);
m.BIND(&return_true);
m.Return(m.BooleanConstant(true));
m.BIND(&return_false);
......
......@@ -342,3 +342,137 @@ const six = BigInt(6);
assertTrue(%SameValueZero(one, one));
assertTrue(%SameValueZero(one, another_one));
}
// Abstract comparison
{
let undef = Symbol();
assertEquals(%Compare(zero, zero, undef), 0);
assertEquals(%Compare(zero, one, undef), -1);
assertEquals(%Compare(one, zero, undef), +1);
assertEquals(%Compare(minus_one, one, undef), -1);
assertEquals(%Compare(one, minus_one, undef), +1);
assertEquals(%Compare(zero, -0, undef), 0);
assertEquals(%Compare(-0, zero, undef), 0);
assertEquals(%Compare(zero, 0, undef), 0);
assertEquals(%Compare(0, zero, undef), 0);
assertEquals(%Compare(minus_one, 1, undef), -1);
assertEquals(%Compare(1, minus_one, undef), +1);
assertEquals(%Compare(six, NaN, undef), undef);
assertEquals(%Compare(NaN, six, undef), undef);
assertEquals(%Compare(six, Infinity, undef), -1);
assertEquals(%Compare(Infinity, six, undef), +1);
assertEquals(%Compare(six, -Infinity, undef), +1);
assertEquals(%Compare(-Infinity, six, undef), -1);
assertEquals(%Compare(six, 5.99999999, undef), +1);
assertEquals(%Compare(5.99999999, six, undef), -1);
assertEquals(%Compare(zero, "", undef), 0);
assertEquals(%Compare("", zero, undef), 0);
assertEquals(%Compare(minus_one, "\t-1 ", undef), 0);
assertEquals(%Compare("\t-1 ", minus_one, undef), 0);
assertEquals(%Compare(minus_one, "-0x1", undef), undef);
assertEquals(%Compare("-0x1", minus_one, undef), undef);
const unsafe = "9007199254740993"; // 2**53 + 1
assertEquals(%Compare(BigInt.parseInt(unsafe), unsafe, undef), +1);
assertEquals(%Compare(unsafe, BigInt.parseInt(unsafe), undef), -1);
assertThrows(() => %Compare(six, Symbol(6), undef), TypeError);
assertThrows(() => %Compare(Symbol(6), six, undef), TypeError);
assertEquals(%Compare(six, {valueOf() {return Object(5)},
toString() {return 6}}, undef), 0);
assertEquals(%Compare({valueOf() {return Object(5)},
toString() {return 6}}, six, undef), 0);
}{
assertFalse(zero < zero);
assertTrue(zero <= zero);
assertTrue(zero < one);
assertTrue(zero <= one);
assertFalse(one < zero);
assertFalse(one <= zero);
assertTrue(minus_one < one);
assertTrue(minus_one <= one);
assertFalse(one < minus_one);
assertFalse(one <= minus_one);
assertFalse(zero < -0);
assertTrue(zero <= -0);
assertFalse(-0 < zero);
assertTrue(-0 <= zero);
assertFalse(zero < 0);
assertTrue(zero <= 0);
assertFalse(0 < zero);
assertTrue(0 <= zero);
assertTrue(minus_one < 1);
assertTrue(minus_one <= 1);
assertFalse(1 < minus_one);
assertFalse(1 <= minus_one);
assertFalse(six < NaN);
assertFalse(six <= NaN);
assertFalse(NaN < six);
assertFalse(NaN <= six);
assertTrue(six < Infinity);
assertTrue(six <= Infinity);
assertFalse(Infinity < six);
assertFalse(Infinity <= six);
assertFalse(six < -Infinity);
assertFalse(six <= -Infinity);
assertTrue(-Infinity < six);
assertTrue(-Infinity <= six);
assertFalse(six < 5.99999999);
assertFalse(six <= 5.99999999);
assertTrue(5.99999999 < six);
assertTrue(5.99999999 <= six);
assertFalse(zero < "");
assertTrue(zero <= "");
assertFalse("" < zero);
assertTrue("" <= zero);
assertFalse(minus_one < "\t-1 ");
assertTrue(minus_one <= "\t-1 ");
assertFalse("\t-1 " < minus_one);
assertTrue("\t-1 " <= minus_one);
assertFalse(minus_one < "-0x1");
assertFalse(minus_one <= "-0x1");
assertFalse("-0x1" < minus_one);
assertFalse("-0x1" <= minus_one);
const unsafe = "9007199254740993"; // 2**53 + 1
assertFalse(BigInt.parseInt(unsafe) < unsafe);
assertFalse(BigInt.parseInt(unsafe) <= unsafe);
assertTrue(unsafe < BigInt.parseInt(unsafe));
assertTrue(unsafe <= BigInt.parseInt(unsafe));
assertThrows(() => six < Symbol(6), TypeError);
assertThrows(() => six <= Symbol(6), TypeError);
assertThrows(() => Symbol(6) < six, TypeError);
assertThrows(() => Symbol(6) <= six, TypeError);
assertFalse(six < {valueOf() {return Object(5)}, toString() {return 6}});
assertTrue(six <= {valueOf() {return Object(5)}, toString() {return 6}});
assertFalse({valueOf() {return Object(5)}, toString() {return 6}} < six);
assertTrue({valueOf() {return Object(5)}, toString() {return 6}} <= six);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment