[compiler] Initial TurboFan code stubs for abstract relational comparison.

This adds new code stubs for abstract relational comparison,
namely LessThanStub, LessThanOrEqualStub, GreaterThanStub and
GreaterThanOrEqualStub, and hooks them up for Ignition and TurboFan.
These stubs implement the full compare operation without any
unpredictable bailouts. Currently they still go to C++ for string
comparisons, and also use the %ToPrimitive_Number runtime entry, as
we still lack a stub for the ToPrimitive operation. These issues
will be addressed separately in follow-up CLs.

Drive-by-fix: Add support for deferred code in the RawMachineAssembler
and CodeStubAssembler. A block can be marked as deferred by marking its
Label as deferred, which will then make the register allocator penalize
this block and prefer better register assignments for the other blocks.

R=epertoso@chromium.org

Review URL: https://codereview.chromium.org/1759133002

Cr-Commit-Position: refs/heads/master@{#34463}
This commit is contained in:
bmeurer 2016-03-03 08:38:55 -08:00 committed by Commit bot
parent d61b8cee8c
commit 62bc168d6e
12 changed files with 676 additions and 75 deletions

View File

@ -194,6 +194,30 @@ Callable CodeFactory::RegExpExec(Isolate* isolate) {
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::LessThan(Isolate* isolate) {
LessThanStub stub(isolate);
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::LessThanOrEqual(Isolate* isolate) {
LessThanOrEqualStub stub(isolate);
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::GreaterThan(Isolate* isolate) {
GreaterThanStub stub(isolate);
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::GreaterThanOrEqual(Isolate* isolate) {
GreaterThanOrEqualStub stub(isolate);
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::StrictEqual(Isolate* isolate) {
StrictEqualStub stub(isolate);

View File

@ -75,6 +75,10 @@ class CodeFactory final {
static Callable RegExpConstructResult(Isolate* isolate);
static Callable RegExpExec(Isolate* isolate);
static Callable LessThan(Isolate* isolate);
static Callable LessThanOrEqual(Isolate* isolate);
static Callable GreaterThan(Isolate* isolate);
static Callable GreaterThanOrEqual(Isolate* isolate);
static Callable StrictEqual(Isolate* isolate);
static Callable StrictNotEqual(Isolate* isolate);

View File

@ -501,10 +501,353 @@ void StringLengthStub::GenerateAssembly(
namespace {
enum AbstractRelationalComparisonMode {
kLessThan,
kLessThanOrEqual,
kGreaterThan,
kGreaterThanOrEqual
};
void GenerateAbstractRelationalComparison(
compiler::CodeStubAssembler* assembler,
AbstractRelationalComparisonMode mode) {
typedef compiler::CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef compiler::CodeStubAssembler::Variable Variable;
Node* context = assembler->Parameter(2);
Label return_true(assembler), return_false(assembler);
// Shared entry for floating point comparison.
Label do_fcmp(assembler);
Variable var_fcmp_lhs(assembler, MachineRepresentation::kFloat64),
var_fcmp_rhs(assembler, MachineRepresentation::kFloat64);
// We might need to loop several times due to ToPrimitive and/or ToNumber
// conversions.
Variable var_lhs(assembler, MachineRepresentation::kTagged),
var_rhs(assembler, MachineRepresentation::kTagged);
Variable* loop_vars[2] = {&var_lhs, &var_rhs};
Label loop(assembler, 2, loop_vars);
var_lhs.Bind(assembler->Parameter(0));
var_rhs.Bind(assembler->Parameter(1));
assembler->Goto(&loop);
assembler->Bind(&loop);
{
// Load the current {lhs} and {rhs} values.
Node* lhs = var_lhs.value();
Node* rhs = var_rhs.value();
// Check if the {lhs} is a Smi or a HeapObject.
Label if_lhsissmi(assembler), if_lhsisnotsmi(assembler);
assembler->Branch(assembler->WordIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi);
assembler->Bind(&if_lhsissmi);
{
// Check if {rhs} is a Smi or a HeapObject.
Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler);
assembler->Branch(assembler->WordIsSmi(rhs), &if_rhsissmi,
&if_rhsisnotsmi);
assembler->Bind(&if_rhsissmi);
{
// Both {lhs} and {rhs} are Smi, so just perform a fast Smi comparison.
switch (mode) {
case kLessThan:
assembler->BranchIfSmiLessThan(lhs, rhs, &return_true,
&return_false);
break;
case kLessThanOrEqual:
assembler->BranchIfSmiLessThanOrEqual(lhs, rhs, &return_true,
&return_false);
break;
case kGreaterThan:
assembler->BranchIfSmiLessThan(rhs, lhs, &return_true,
&return_false);
break;
case kGreaterThanOrEqual:
assembler->BranchIfSmiLessThanOrEqual(rhs, lhs, &return_true,
&return_false);
break;
}
}
assembler->Bind(&if_rhsisnotsmi);
{
// Load the map of {rhs}.
Node* rhs_map = assembler->LoadObjectField(rhs, HeapObject::kMapOffset);
// Check if the {rhs} is a HeapNumber.
Node* number_map = assembler->HeapNumberMapConstant();
Label if_rhsisnumber(assembler),
if_rhsisnotnumber(assembler, Label::kDeferred);
assembler->Branch(assembler->WordEqual(rhs_map, number_map),
&if_rhsisnumber, &if_rhsisnotnumber);
assembler->Bind(&if_rhsisnumber);
{
// Convert the {lhs} and {rhs} to floating point values, and
// perform a floating point comparison.
var_fcmp_lhs.Bind(assembler->SmiToFloat64(lhs));
var_fcmp_rhs.Bind(assembler->LoadHeapNumberValue(rhs));
assembler->Goto(&do_fcmp);
}
assembler->Bind(&if_rhsisnotnumber);
{
// Convert the {rhs} to a Number; we don't need to perform the
// dedicated ToPrimitive(rhs, hint Number) operation, as the
// ToNumber(rhs) will by itself already invoke ToPrimitive with
// a Number hint.
Callable callable = CodeFactory::ToNumber(assembler->isolate());
var_rhs.Bind(assembler->CallStub(callable, context, rhs));
assembler->Goto(&loop);
}
}
}
assembler->Bind(&if_lhsisnotsmi);
{
// Load the HeapNumber map for later comparisons.
Node* number_map = assembler->HeapNumberMapConstant();
// Load the map of {lhs}.
Node* lhs_map = assembler->LoadObjectField(lhs, HeapObject::kMapOffset);
// Check if {rhs} is a Smi or a HeapObject.
Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler);
assembler->Branch(assembler->WordIsSmi(rhs), &if_rhsissmi,
&if_rhsisnotsmi);
assembler->Bind(&if_rhsissmi);
{
// Check if the {lhs} is a HeapNumber.
Label if_lhsisnumber(assembler),
if_lhsisnotnumber(assembler, Label::kDeferred);
assembler->Branch(assembler->WordEqual(lhs_map, number_map),
&if_lhsisnumber, &if_lhsisnotnumber);
assembler->Bind(&if_lhsisnumber);
{
// Convert the {lhs} and {rhs} to floating point values, and
// perform a floating point comparison.
var_fcmp_lhs.Bind(assembler->LoadHeapNumberValue(lhs));
var_fcmp_rhs.Bind(assembler->SmiToFloat64(rhs));
assembler->Goto(&do_fcmp);
}
assembler->Bind(&if_lhsisnotnumber);
{
// Convert the {lhs} to a Number; we don't need to perform the
// dedicated ToPrimitive(lhs, hint Number) operation, as the
// ToNumber(lhs) will by itself already invoke ToPrimitive with
// a Number hint.
Callable callable = CodeFactory::ToNumber(assembler->isolate());
var_lhs.Bind(assembler->CallStub(callable, context, lhs));
assembler->Goto(&loop);
}
}
assembler->Bind(&if_rhsisnotsmi);
{
// Load the map of {rhs}.
Node* rhs_map = assembler->LoadObjectField(rhs, HeapObject::kMapOffset);
// Check if {lhs} is a HeapNumber.
Label if_lhsisnumber(assembler), if_lhsisnotnumber(assembler);
assembler->Branch(assembler->WordEqual(lhs_map, number_map),
&if_lhsisnumber, &if_lhsisnotnumber);
assembler->Bind(&if_lhsisnumber);
{
// Check if {rhs} is also a HeapNumber.
Label if_rhsisnumber(assembler),
if_rhsisnotnumber(assembler, Label::kDeferred);
assembler->Branch(assembler->WordEqual(lhs_map, rhs_map),
&if_rhsisnumber, &if_rhsisnotnumber);
assembler->Bind(&if_rhsisnumber);
{
// Convert the {lhs} and {rhs} to floating point values, and
// perform a floating point comparison.
var_fcmp_lhs.Bind(assembler->LoadHeapNumberValue(lhs));
var_fcmp_rhs.Bind(assembler->LoadHeapNumberValue(rhs));
assembler->Goto(&do_fcmp);
}
assembler->Bind(&if_rhsisnotnumber);
{
// Convert the {rhs} to a Number; we don't need to perform
// dedicated ToPrimitive(rhs, hint Number) operation, as the
// ToNumber(rhs) will by itself already invoke ToPrimitive with
// a Number hint.
Callable callable = CodeFactory::ToNumber(assembler->isolate());
var_rhs.Bind(assembler->CallStub(callable, context, rhs));
assembler->Goto(&loop);
}
}
assembler->Bind(&if_lhsisnotnumber);
{
// Load the instance type of {lhs}.
Node* lhs_instance_type = assembler->LoadMapInstanceType(lhs_map);
// Check if {lhs} is a String.
Label if_lhsisstring(assembler),
if_lhsisnotstring(assembler, Label::kDeferred);
assembler->Branch(assembler->Int32LessThan(
lhs_instance_type,
assembler->Int32Constant(FIRST_NONSTRING_TYPE)),
&if_lhsisstring, &if_lhsisnotstring);
assembler->Bind(&if_lhsisstring);
{
// Load the instance type of {rhs}.
Node* rhs_instance_type = assembler->LoadMapInstanceType(rhs_map);
// Check if {rhs} is also a String.
Label if_rhsisstring(assembler),
if_rhsisnotstring(assembler, Label::kDeferred);
assembler->Branch(assembler->Int32LessThan(
rhs_instance_type, assembler->Int32Constant(
FIRST_NONSTRING_TYPE)),
&if_rhsisstring, &if_rhsisnotstring);
assembler->Bind(&if_rhsisstring);
{
// Both {lhs} and {rhs} are strings.
// TODO(bmeurer): Hook up String<Compare>Stub once we have it.
switch (mode) {
case kLessThan:
assembler->TailCallRuntime(Runtime::kStringLessThan, context,
lhs, rhs);
break;
case kLessThanOrEqual:
assembler->TailCallRuntime(Runtime::kStringLessThanOrEqual,
context, lhs, rhs);
break;
case kGreaterThan:
assembler->TailCallRuntime(Runtime::kStringGreaterThan,
context, lhs, rhs);
break;
case kGreaterThanOrEqual:
assembler->TailCallRuntime(Runtime::kStringGreaterThanOrEqual,
context, lhs, rhs);
break;
}
}
assembler->Bind(&if_rhsisnotstring);
{
// The {lhs} is a String, while {rhs} is neither a Number nor a
// String, so we need to call ToPrimitive(rhs, hint Number) if
// {rhs} is a receiver or ToNumber(lhs) and ToNumber(rhs) in the
// other cases.
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
Label if_rhsisreceiver(assembler, Label::kDeferred),
if_rhsisnotreceiver(assembler, Label::kDeferred);
assembler->Branch(
assembler->Int32LessThanOrEqual(
assembler->Int32Constant(FIRST_JS_RECEIVER_TYPE),
rhs_instance_type),
&if_rhsisreceiver, &if_rhsisnotreceiver);
assembler->Bind(&if_rhsisreceiver);
{
// Convert {rhs} to a primitive first passing Number hint.
// TODO(bmeurer): Hook up ToPrimitiveStub here, once it's there.
var_rhs.Bind(assembler->CallRuntime(
Runtime::kToPrimitive_Number, context, rhs));
assembler->Goto(&loop);
}
assembler->Bind(&if_rhsisnotreceiver);
{
// Convert both {lhs} and {rhs} to Number.
Callable callable = CodeFactory::ToNumber(assembler->isolate());
var_lhs.Bind(assembler->CallStub(callable, context, lhs));
var_rhs.Bind(assembler->CallStub(callable, context, rhs));
assembler->Goto(&loop);
}
}
}
assembler->Bind(&if_lhsisnotstring);
{
// The {lhs} is neither a Number nor a String, so we need to call
// ToPrimitive(lhs, hint Number) if {lhs} is a receiver or
// ToNumber(lhs) and ToNumber(rhs) in the other cases.
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
Label if_lhsisreceiver(assembler, Label::kDeferred),
if_lhsisnotreceiver(assembler, Label::kDeferred);
assembler->Branch(
assembler->Int32LessThanOrEqual(
assembler->Int32Constant(FIRST_JS_RECEIVER_TYPE),
lhs_instance_type),
&if_lhsisreceiver, &if_lhsisnotreceiver);
assembler->Bind(&if_lhsisreceiver);
{
// Convert {lhs} to a primitive first passing Number hint.
// TODO(bmeurer): Hook up ToPrimitiveStub here, once it's there.
var_lhs.Bind(assembler->CallRuntime(Runtime::kToPrimitive_Number,
context, lhs));
assembler->Goto(&loop);
}
assembler->Bind(&if_lhsisnotreceiver);
{
// Convert both {lhs} and {rhs} to Number.
Callable callable = CodeFactory::ToNumber(assembler->isolate());
var_lhs.Bind(assembler->CallStub(callable, context, lhs));
var_rhs.Bind(assembler->CallStub(callable, context, rhs));
assembler->Goto(&loop);
}
}
}
}
}
}
assembler->Bind(&do_fcmp);
{
// Load the {lhs} and {rhs} floating point values.
Node* lhs = var_fcmp_lhs.value();
Node* rhs = var_fcmp_rhs.value();
// Perform a fast floating point comparison.
switch (mode) {
case kLessThan:
assembler->BranchIfFloat64LessThan(lhs, rhs, &return_true,
&return_false);
break;
case kLessThanOrEqual:
assembler->BranchIfFloat64LessThanOrEqual(lhs, rhs, &return_true,
&return_false);
break;
case kGreaterThan:
assembler->BranchIfFloat64GreaterThan(lhs, rhs, &return_true,
&return_false);
break;
case kGreaterThanOrEqual:
assembler->BranchIfFloat64GreaterThanOrEqual(lhs, rhs, &return_true,
&return_false);
break;
}
}
assembler->Bind(&return_true);
assembler->Return(assembler->BooleanConstant(true));
assembler->Bind(&return_false);
assembler->Return(assembler->BooleanConstant(false));
}
enum ResultMode { kDontNegateResult, kNegateResult };
void GenerateStrictEqual(compiler::CodeStubAssembler* assembler,
Isolate* isolate, ResultMode mode) {
ResultMode mode) {
// Here's pseudo-code for the algorithm below in case of kDontNegateResult
// mode; for kNegateResult mode we properly negate the result.
//
@ -706,9 +1049,10 @@ void GenerateStrictEqual(compiler::CodeStubAssembler* assembler,
assembler->Bind(&if_rhsisstring);
{
Callable callable = (mode == kDontNegateResult)
? CodeFactory::StringEqual(isolate)
: CodeFactory::StringNotEqual(isolate);
Callable callable =
(mode == kDontNegateResult)
? CodeFactory::StringEqual(assembler->isolate())
: CodeFactory::StringNotEqual(assembler->isolate());
assembler->TailCallStub(callable, context, lhs, rhs);
}
@ -969,14 +1313,34 @@ void GenerateStringEqual(compiler::CodeStubAssembler* assembler,
} // namespace
void LessThanStub::GenerateAssembly(
compiler::CodeStubAssembler* assembler) const {
GenerateAbstractRelationalComparison(assembler, kLessThan);
}
void LessThanOrEqualStub::GenerateAssembly(
compiler::CodeStubAssembler* assembler) const {
GenerateAbstractRelationalComparison(assembler, kLessThanOrEqual);
}
void GreaterThanStub::GenerateAssembly(
compiler::CodeStubAssembler* assembler) const {
GenerateAbstractRelationalComparison(assembler, kGreaterThan);
}
void GreaterThanOrEqualStub::GenerateAssembly(
compiler::CodeStubAssembler* assembler) const {
GenerateAbstractRelationalComparison(assembler, kGreaterThanOrEqual);
}
void StrictEqualStub::GenerateAssembly(
compiler::CodeStubAssembler* assembler) const {
GenerateStrictEqual(assembler, isolate(), kDontNegateResult);
GenerateStrictEqual(assembler, kDontNegateResult);
}
void StrictNotEqualStub::GenerateAssembly(
compiler::CodeStubAssembler* assembler) const {
GenerateStrictEqual(assembler, isolate(), kNegateResult);
GenerateStrictEqual(assembler, kNegateResult);
}
void StringEqualStub::GenerateAssembly(

View File

@ -101,6 +101,10 @@ namespace internal {
V(AllocateHeapNumber) \
V(AllocateMutableHeapNumber) \
V(StringLength) \
V(LessThan) \
V(LessThanOrEqual) \
V(GreaterThan) \
V(GreaterThanOrEqual) \
V(StrictEqual) \
V(StrictNotEqual) \
V(StringEqual) \
@ -351,11 +355,10 @@ class CodeStub BASE_EMBEDDED {
Handle<Code> GenerateCode() override; \
DEFINE_CODE_STUB(NAME, SUPER)
#define DEFINE_TURBOFAN_CODE_STUB(NAME, SUPER) \
public: \
CallInterfaceDescriptor GetCallInterfaceDescriptor() const override { \
return DESC##Descriptor(isolate()); \
}; \
#define DEFINE_TURBOFAN_CODE_STUB(NAME, SUPER) \
public: \
void GenerateAssembly(compiler::CodeStubAssembler* assembler) \
const override; \
DEFINE_CODE_STUB(NAME, SUPER)
#define DEFINE_HANDLER_CODE_STUB(NAME, SUPER) \
@ -629,60 +632,81 @@ class StringLengthStub : public TurboFanCodeStub {
InlineCacheState GetICState() const override { return MONOMORPHIC; }
ExtraICState GetExtraICState() const override { return Code::LOAD_IC; }
void GenerateAssembly(compiler::CodeStubAssembler* assembler) const override;
DEFINE_CALL_INTERFACE_DESCRIPTOR(LoadWithVector);
DEFINE_CODE_STUB(StringLength, TurboFanCodeStub);
DEFINE_TURBOFAN_CODE_STUB(StringLength, TurboFanCodeStub);
};
class LessThanStub final : public TurboFanCodeStub {
public:
explicit LessThanStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
DEFINE_CALL_INTERFACE_DESCRIPTOR(Compare);
DEFINE_TURBOFAN_CODE_STUB(LessThan, TurboFanCodeStub);
};
class LessThanOrEqualStub final : public TurboFanCodeStub {
public:
explicit LessThanOrEqualStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
DEFINE_CALL_INTERFACE_DESCRIPTOR(Compare);
DEFINE_TURBOFAN_CODE_STUB(LessThanOrEqual, TurboFanCodeStub);
};
class GreaterThanStub final : public TurboFanCodeStub {
public:
explicit GreaterThanStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
DEFINE_CALL_INTERFACE_DESCRIPTOR(Compare);
DEFINE_TURBOFAN_CODE_STUB(GreaterThan, TurboFanCodeStub);
};
class GreaterThanOrEqualStub final : public TurboFanCodeStub {
public:
explicit GreaterThanOrEqualStub(Isolate* isolate)
: TurboFanCodeStub(isolate) {}
DEFINE_CALL_INTERFACE_DESCRIPTOR(Compare);
DEFINE_TURBOFAN_CODE_STUB(GreaterThanOrEqual, TurboFanCodeStub);
};
class StrictEqualStub final : public TurboFanCodeStub {
public:
explicit StrictEqualStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
void GenerateAssembly(compiler::CodeStubAssembler* assembler) const final;
DEFINE_CALL_INTERFACE_DESCRIPTOR(Compare);
DEFINE_CODE_STUB(StrictEqual, TurboFanCodeStub);
DEFINE_TURBOFAN_CODE_STUB(StrictEqual, TurboFanCodeStub);
};
class StrictNotEqualStub final : public TurboFanCodeStub {
public:
explicit StrictNotEqualStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
void GenerateAssembly(compiler::CodeStubAssembler* assembler) const final;
DEFINE_CALL_INTERFACE_DESCRIPTOR(Compare);
DEFINE_CODE_STUB(StrictNotEqual, TurboFanCodeStub);
DEFINE_TURBOFAN_CODE_STUB(StrictNotEqual, TurboFanCodeStub);
};
class StringEqualStub final : public TurboFanCodeStub {
public:
explicit StringEqualStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
void GenerateAssembly(compiler::CodeStubAssembler* assembler) const final;
DEFINE_CALL_INTERFACE_DESCRIPTOR(Compare);
DEFINE_CODE_STUB(StringEqual, TurboFanCodeStub);
DEFINE_TURBOFAN_CODE_STUB(StringEqual, TurboFanCodeStub);
};
class StringNotEqualStub final : public TurboFanCodeStub {
public:
explicit StringNotEqualStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
void GenerateAssembly(compiler::CodeStubAssembler* assembler) const final;
DEFINE_CALL_INTERFACE_DESCRIPTOR(Compare);
DEFINE_CODE_STUB(StringNotEqual, TurboFanCodeStub);
DEFINE_TURBOFAN_CODE_STUB(StringNotEqual, TurboFanCodeStub);
};
class ToBooleanStub final : public TurboFanCodeStub {
public:
explicit ToBooleanStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
void GenerateAssembly(compiler::CodeStubAssembler* assembler) const final;
DEFINE_CALL_INTERFACE_DESCRIPTOR(ToBoolean);
DEFINE_CODE_STUB(ToBoolean, TurboFanCodeStub);
DEFINE_TURBOFAN_CODE_STUB(ToBoolean, TurboFanCodeStub);
};
enum StringAddFlags {

View File

@ -163,6 +163,14 @@ Node* CodeStubAssembler::SmiAdd(Node* a, Node* b) { return IntPtrAdd(a, b); }
Node* CodeStubAssembler::SmiEqual(Node* a, Node* b) { return WordEqual(a, b); }
Node* CodeStubAssembler::SmiLessThan(Node* a, Node* b) {
return IntPtrLessThan(a, b);
}
Node* CodeStubAssembler::SmiLessThanOrEqual(Node* a, Node* b) {
return IntPtrLessThanOrEqual(a, b);
}
#define DEFINE_CODE_STUB_ASSEMBER_BINARY_OP(name) \
Node* CodeStubAssembler::name(Node* a, Node* b) { \
return raw_assembler_->name(a, b); \
@ -416,6 +424,27 @@ Node* CodeStubAssembler::BitFieldDecode(Node* word32, uint32_t shift,
raw_assembler_->Int32Constant(shift));
}
void CodeStubAssembler::BranchIfSmiLessThan(Node* a, Node* b, Label* if_true,
Label* if_false) {
Label if_lessthan(this), if_notlessthan(this);
Branch(SmiLessThan(a, b), &if_lessthan, &if_notlessthan);
Bind(&if_lessthan);
Goto(if_true);
Bind(&if_notlessthan);
Goto(if_false);
}
void CodeStubAssembler::BranchIfSmiLessThanOrEqual(Node* a, Node* b,
Label* if_true,
Label* if_false) {
Label if_lessthanorequal(this), if_notlessthanorequal(this);
Branch(SmiLessThanOrEqual(a, b), &if_lessthanorequal, &if_notlessthanorequal);
Bind(&if_lessthanorequal);
Goto(if_true);
Bind(&if_notlessthanorequal);
Goto(if_false);
}
void CodeStubAssembler::BranchIfFloat64Equal(Node* a, Node* b, Label* if_true,
Label* if_false) {
Label if_equal(this), if_notequal(this);
@ -426,6 +455,52 @@ void CodeStubAssembler::BranchIfFloat64Equal(Node* a, Node* b, Label* if_true,
Goto(if_false);
}
void CodeStubAssembler::BranchIfFloat64LessThan(Node* a, Node* b,
Label* if_true,
Label* if_false) {
Label if_lessthan(this), if_notlessthan(this);
Branch(Float64LessThan(a, b), &if_lessthan, &if_notlessthan);
Bind(&if_lessthan);
Goto(if_true);
Bind(&if_notlessthan);
Goto(if_false);
}
void CodeStubAssembler::BranchIfFloat64LessThanOrEqual(Node* a, Node* b,
Label* if_true,
Label* if_false) {
Label if_lessthanorequal(this), if_notlessthanorequal(this);
Branch(Float64LessThanOrEqual(a, b), &if_lessthanorequal,
&if_notlessthanorequal);
Bind(&if_lessthanorequal);
Goto(if_true);
Bind(&if_notlessthanorequal);
Goto(if_false);
}
void CodeStubAssembler::BranchIfFloat64GreaterThan(Node* a, Node* b,
Label* if_true,
Label* if_false) {
Label if_greaterthan(this), if_notgreaterthan(this);
Branch(Float64GreaterThan(a, b), &if_greaterthan, &if_notgreaterthan);
Bind(&if_greaterthan);
Goto(if_true);
Bind(&if_notgreaterthan);
Goto(if_false);
}
void CodeStubAssembler::BranchIfFloat64GreaterThanOrEqual(Node* a, Node* b,
Label* if_true,
Label* if_false) {
Label if_greaterthanorequal(this), if_notgreaterthanorequal(this);
Branch(Float64GreaterThanOrEqual(a, b), &if_greaterthanorequal,
&if_notgreaterthanorequal);
Bind(&if_greaterthanorequal);
Goto(if_true);
Bind(&if_notgreaterthanorequal);
Goto(if_false);
}
Node* CodeStubAssembler::CallN(CallDescriptor* descriptor, Node* code_target,
Node** args) {
CallPrologue();
@ -515,6 +590,12 @@ Node* CodeStubAssembler::TailCallRuntime(Runtime::FunctionId function_id,
context);
}
Node* CodeStubAssembler::CallStub(Callable const& callable, Node* context,
Node* arg1, size_t result_size) {
Node* target = HeapConstant(callable.code());
return CallStub(callable.descriptor(), target, context, arg1, result_size);
}
Node* CodeStubAssembler::CallStub(const CallInterfaceDescriptor& descriptor,
Node* target, Node* context, Node* arg1,
size_t result_size) {
@ -709,27 +790,20 @@ bool CodeStubAssembler::Variable::IsBound() const {
return impl_->value_ != nullptr;
}
CodeStubAssembler::Label::Label(CodeStubAssembler* assembler)
: bound_(false), merge_count_(0), assembler_(assembler), label_(nullptr) {
void* buffer = assembler->zone()->New(sizeof(RawMachineLabel));
label_ = new (buffer) RawMachineLabel();
}
CodeStubAssembler::Label::Label(CodeStubAssembler* assembler,
int merged_value_count,
CodeStubAssembler::Variable** merged_variables)
CodeStubAssembler::Variable** merged_variables,
CodeStubAssembler::Label::Type type)
: bound_(false), merge_count_(0), assembler_(assembler), label_(nullptr) {
void* buffer = assembler->zone()->New(sizeof(RawMachineLabel));
label_ = new (buffer) RawMachineLabel();
label_ = new (buffer)
RawMachineLabel(type == kDeferred ? RawMachineLabel::kDeferred
: RawMachineLabel::kNonDeferred);
for (int i = 0; i < merged_value_count; ++i) {
variable_phis_[merged_variables[i]->impl_] = nullptr;
}
}
CodeStubAssembler::Label::Label(CodeStubAssembler* assembler,
CodeStubAssembler::Variable* merged_variable)
: CodeStubAssembler::Label(assembler, 1, &merged_variable) {}
void CodeStubAssembler::Label::MergeVariables() {
++merge_count_;
for (auto var : assembler_->variables_) {
@ -760,16 +834,17 @@ void CodeStubAssembler::Label::MergeVariables() {
assembler_->raw_assembler_->AppendPhiInput(phi->second, node);
} else {
auto i = variable_merges_.find(var);
USE(i);
// If the following assert fires, then you've declared a variable that
// has the same bound value along all paths up until the point you bound
// this label, but then later merged a path with a new value for the
// variable after the label bind (it's not possible to add phis to the
// bound label after the fact, just make sure to list the variable in
// the label's constructor's list of merged variables).
DCHECK(find_if(i->second.begin(), i->second.end(),
[node](Node* e) -> bool { return node != e; }) ==
i->second.end());
if (i != variable_merges_.end()) {
// If the following assert fires, then you've declared a variable that
// has the same bound value along all paths up until the point you
// bound this label, but then later merged a path with a new value for
// the variable after the label bind (it's not possible to add phis to
// the bound label after the fact, just make sure to list the variable
// in the label's constructor's list of merged variables).
DCHECK(find_if(i->second.begin(), i->second.end(),
[node](Node* e) -> bool { return node != e; }) ==
i->second.end());
}
}
}
}

View File

@ -39,13 +39,17 @@ class Schedule;
V(Float64Equal) \
V(Float64LessThan) \
V(Float64LessThanOrEqual) \
V(Float64GreaterThan) \
V(Float64GreaterThanOrEqual) \
V(IntPtrAdd) \
V(IntPtrSub) \
V(Int32Add) \
V(Int32Sub) \
V(Int32Mul) \
V(Int32GreaterThan) \
V(Int32GreaterThanOrEqual) \
V(Int32LessThan) \
V(Int32LessThanOrEqual) \
V(WordEqual) \
V(WordNotEqual) \
V(WordOr) \
@ -72,6 +76,8 @@ class Schedule;
V(Word64Shr) \
V(Word64Sar) \
V(Word64Ror) \
V(IntPtrLessThan) \
V(IntPtrLessThanOrEqual) \
V(UintPtrGreaterThanOrEqual)
#define CODE_STUB_ASSEMBLER_UNARY_OP_LIST(V) \
@ -202,6 +208,9 @@ class CodeStubAssembler {
Node* TailCallRuntime(Runtime::FunctionId function_id, Node* context,
Node* arg1, Node* arg2, Node* arg3, Node* arg4);
Node* CallStub(Callable const& callable, Node* context, Node* arg1,
size_t result_size = 1);
Node* CallStub(const CallInterfaceDescriptor& descriptor, Node* target,
Node* context, Node* arg1, size_t result_size = 1);
Node* CallStub(const CallInterfaceDescriptor& descriptor, Node* target,
@ -242,6 +251,8 @@ class CodeStubAssembler {
// Smi operations.
Node* SmiAdd(Node* a, Node* b);
Node* SmiEqual(Node* a, Node* b);
Node* SmiLessThan(Node* a, Node* b);
Node* SmiLessThanOrEqual(Node* a, Node* b);
// Load a value from the root array.
Node* LoadRoot(Heap::RootListIndex root_index);
@ -281,18 +292,31 @@ class CodeStubAssembler {
// Branching helpers.
// TODO(danno): Can we be more cleverish wrt. edge-split?
void BranchIfSmiLessThan(Node* a, Node* b, Label* if_true, Label* if_false);
void BranchIfSmiLessThanOrEqual(Node* a, Node* b, Label* if_true,
Label* if_false);
void BranchIfFloat64Equal(Node* a, Node* b, Label* if_true, Label* if_false);
void BranchIfFloat64LessThan(Node* a, Node* b, Label* if_true,
Label* if_false);
void BranchIfFloat64LessThanOrEqual(Node* a, Node* b, Label* if_true,
Label* if_false);
void BranchIfFloat64GreaterThan(Node* a, Node* b, Label* if_true,
Label* if_false);
void BranchIfFloat64GreaterThanOrEqual(Node* a, Node* b, Label* if_true,
Label* if_false);
void BranchIfFloat64IsNaN(Node* value, Label* if_true, Label* if_false) {
BranchIfFloat64Equal(value, value, if_false, if_true);
}
protected:
// Protected helpers which delegate to RawMachineAssembler.
Graph* graph() const;
// Helpers which delegate to RawMachineAssembler.
Factory* factory() const;
Isolate* isolate() const;
Zone* zone() const;
protected:
// Protected helpers which delegate to RawMachineAssembler.
Graph* graph() const;
// Enables subclasses to perform operations before and after a call.
virtual void CallPrologue();
virtual void CallEpilogue();
@ -323,11 +347,21 @@ DEFINE_OPERATORS_FOR_FLAGS(CodeStubAssembler::AllocationFlags);
class CodeStubAssembler::Label {
public:
explicit Label(CodeStubAssembler* assembler);
Label(CodeStubAssembler* assembler, int merged_variable_count,
CodeStubAssembler::Variable** merged_variables);
enum Type { kDeferred, kNonDeferred };
explicit Label(CodeStubAssembler* assembler,
CodeStubAssembler::Label::Type type =
CodeStubAssembler::Label::kNonDeferred)
: CodeStubAssembler::Label(assembler, 0, nullptr, type) {}
Label(CodeStubAssembler* assembler,
CodeStubAssembler::Variable* merged_variable);
CodeStubAssembler::Variable* merged_variable,
CodeStubAssembler::Label::Type type =
CodeStubAssembler::Label::kNonDeferred)
: CodeStubAssembler::Label(assembler, 1, &merged_variable, type) {}
Label(CodeStubAssembler* assembler, int merged_variable_count,
CodeStubAssembler::Variable** merged_variables,
CodeStubAssembler::Label::Type type =
CodeStubAssembler::Label::kNonDeferred);
~Label() {}
private:

View File

@ -89,10 +89,6 @@ REPLACE_BINARY_OP_IC_CALL(JSModulus, Token::MOD)
}
REPLACE_RUNTIME_CALL(JSEqual, Runtime::kEqual)
REPLACE_RUNTIME_CALL(JSNotEqual, Runtime::kNotEqual)
REPLACE_RUNTIME_CALL(JSLessThan, Runtime::kLessThan)
REPLACE_RUNTIME_CALL(JSGreaterThan, Runtime::kGreaterThan)
REPLACE_RUNTIME_CALL(JSLessThanOrEqual, Runtime::kLessThanOrEqual)
REPLACE_RUNTIME_CALL(JSGreaterThanOrEqual, Runtime::kGreaterThanOrEqual)
REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext)
REPLACE_RUNTIME_CALL(JSCreateModuleContext, Runtime::kPushModuleContext)
REPLACE_RUNTIME_CALL(JSConvertReceiver, Runtime::kConvertReceiver)
@ -104,6 +100,10 @@ REPLACE_RUNTIME_CALL(JSConvertReceiver, Runtime::kConvertReceiver)
Callable callable = CodeFactory::Name(isolate()); \
ReplaceWithStubCall(node, callable, flags); \
}
REPLACE_STUB_CALL(LessThan)
REPLACE_STUB_CALL(LessThanOrEqual)
REPLACE_STUB_CALL(GreaterThan)
REPLACE_STUB_CALL(GreaterThanOrEqual)
REPLACE_STUB_CALL(StrictEqual)
REPLACE_STUB_CALL(StrictNotEqual)
#undef REPLACE_STUB_CALL

View File

@ -428,6 +428,7 @@ void RawMachineAssembler::Bind(RawMachineLabel* label) {
DCHECK(!label->bound_);
label->bound_ = true;
current_block_ = EnsureBlock(label);
current_block_->set_deferred(label->deferred_);
}
@ -480,11 +481,6 @@ Node* RawMachineAssembler::MakeNode(const Operator* op, int input_count,
return graph()->NewNodeUnchecked(op, input_count, inputs);
}
RawMachineLabel::RawMachineLabel()
: block_(nullptr), used_(false), bound_(false) {}
RawMachineLabel::~RawMachineLabel() { DCHECK(bound_ || !used_); }
} // namespace compiler

View File

@ -700,13 +700,17 @@ class RawMachineAssembler {
class RawMachineLabel final {
public:
RawMachineLabel();
enum Type { kDeferred, kNonDeferred };
explicit RawMachineLabel(Type type = kNonDeferred)
: deferred_(type == kDeferred) {}
~RawMachineLabel();
private:
BasicBlock* block_;
bool used_;
bool bound_;
BasicBlock* block_ = nullptr;
bool used_ = false;
bool bound_ = false;
bool deferred_;
friend class RawMachineAssembler;
DISALLOW_COPY_AND_ASSIGN(RawMachineLabel);
};

View File

@ -1205,7 +1205,7 @@ void Interpreter::DoTestNotEqualStrict(InterpreterAssembler* assembler) {
//
// Test if the value in the <src> register is less than the accumulator.
void Interpreter::DoTestLessThan(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kLessThan, assembler);
DoBinaryOp(CodeFactory::LessThan(isolate_), assembler);
}
@ -1213,7 +1213,7 @@ void Interpreter::DoTestLessThan(InterpreterAssembler* assembler) {
//
// Test if the value in the <src> register is greater than the accumulator.
void Interpreter::DoTestGreaterThan(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kGreaterThan, assembler);
DoBinaryOp(CodeFactory::GreaterThan(isolate_), assembler);
}
@ -1222,7 +1222,7 @@ void Interpreter::DoTestGreaterThan(InterpreterAssembler* assembler) {
// Test if the value in the <src> register is less than or equal to the
// accumulator.
void Interpreter::DoTestLessThanOrEqual(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kLessThanOrEqual, assembler);
DoBinaryOp(CodeFactory::LessThanOrEqual(isolate_), assembler);
}
@ -1231,7 +1231,7 @@ void Interpreter::DoTestLessThanOrEqual(InterpreterAssembler* assembler) {
// Test if the value in the <src> register is greater than or equal to the
// accumulator.
void Interpreter::DoTestGreaterThanOrEqual(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kGreaterThanOrEqual, assembler);
DoBinaryOp(CodeFactory::GreaterThanOrEqual(isolate_), assembler);
}

View File

@ -1145,6 +1145,78 @@ RUNTIME_FUNCTION(Runtime_NewString) {
return *result;
}
RUNTIME_FUNCTION(Runtime_StringLessThan) {
HandleScope handle_scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
switch (String::Compare(x, y)) {
case ComparisonResult::kLessThan:
return isolate->heap()->true_value();
case ComparisonResult::kEqual:
case ComparisonResult::kGreaterThan:
return isolate->heap()->false_value();
case ComparisonResult::kUndefined:
break;
}
UNREACHABLE();
return Smi::FromInt(0);
}
RUNTIME_FUNCTION(Runtime_StringLessThanOrEqual) {
HandleScope handle_scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
switch (String::Compare(x, y)) {
case ComparisonResult::kEqual:
case ComparisonResult::kLessThan:
return isolate->heap()->true_value();
case ComparisonResult::kGreaterThan:
return isolate->heap()->false_value();
case ComparisonResult::kUndefined:
break;
}
UNREACHABLE();
return Smi::FromInt(0);
}
RUNTIME_FUNCTION(Runtime_StringGreaterThan) {
HandleScope handle_scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
switch (String::Compare(x, y)) {
case ComparisonResult::kGreaterThan:
return isolate->heap()->true_value();
case ComparisonResult::kEqual:
case ComparisonResult::kLessThan:
return isolate->heap()->false_value();
case ComparisonResult::kUndefined:
break;
}
UNREACHABLE();
return Smi::FromInt(0);
}
RUNTIME_FUNCTION(Runtime_StringGreaterThanOrEqual) {
HandleScope handle_scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
switch (String::Compare(x, y)) {
case ComparisonResult::kEqual:
case ComparisonResult::kGreaterThan:
return isolate->heap()->true_value();
case ComparisonResult::kLessThan:
return isolate->heap()->false_value();
case ComparisonResult::kUndefined:
break;
}
UNREACHABLE();
return Smi::FromInt(0);
}
RUNTIME_FUNCTION(Runtime_StringEqual) {
HandleScope handle_scope(isolate);
DCHECK_EQ(2, args.length());

View File

@ -862,6 +862,10 @@ namespace internal {
F(StringTrim, 3, 1) \
F(TruncateString, 2, 1) \
F(NewString, 2, 1) \
F(StringLessThan, 2, 1) \
F(StringLessThanOrEqual, 2, 1) \
F(StringGreaterThan, 2, 1) \
F(StringGreaterThanOrEqual, 2, 1) \
F(StringEqual, 2, 1) \
F(StringNotEqual, 2, 1) \
F(FlattenString, 1, 1) \