diff options
author | Michaël Zasso <targos@protonmail.com> | 2016-05-27 16:37:42 +0200 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2016-06-29 09:04:28 +0200 |
commit | 2cc29517966de7257a2f1b34c58c77225a21e05d (patch) | |
tree | 210bd177df2f06eec16e1e22edafdbcbffe66f8a /deps/v8/src/code-stubs.cc | |
parent | bbf3838c70aaec1dd296fa75ae334fd1c7866df3 (diff) | |
download | android-node-v8-2cc29517966de7257a2f1b34c58c77225a21e05d.tar.gz android-node-v8-2cc29517966de7257a2f1b34c58c77225a21e05d.tar.bz2 android-node-v8-2cc29517966de7257a2f1b34c58c77225a21e05d.zip |
deps: update V8 to 5.1.281.69
Pick up the latest branch-head for V8 5.1. This branch brings in
improved language support and performance improvements. For full
details: http://v8project.blogspot.com/2016/04/v8-release-51.html
* Picks up the latest branch head for 5.1 [1]
* Edit v8 gitignore to allow trace_event copy
* Update V8 DEP trace_event as per deps/v8/DEPS [2]
[1] https://chromium.googlesource.com/v8/v8.git/+/dc81244
[2] https://chromium.googlesource.com/chromium/src/base/trace_event/common/+/c8c8665
PR-URL: https://github.com/nodejs/node/pull/7016
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Diffstat (limited to 'deps/v8/src/code-stubs.cc')
-rw-r--r-- | deps/v8/src/code-stubs.cc | 2748 |
1 files changed, 2638 insertions, 110 deletions
diff --git a/deps/v8/src/code-stubs.cc b/deps/v8/src/code-stubs.cc index 4e5efcd8e0..60b350cd93 100644 --- a/deps/v8/src/code-stubs.cc +++ b/deps/v8/src/code-stubs.cc @@ -7,6 +7,7 @@ #include <sstream> #include "src/bootstrapper.h" +#include "src/code-factory.h" #include "src/compiler/code-stub-assembler.h" #include "src/factory.h" #include "src/gdb-jit.h" @@ -82,7 +83,8 @@ void CodeStub::RecordCodeGeneration(Handle<Code> code) { std::ostringstream os; os << *this; PROFILE(isolate(), - CodeCreateEvent(Logger::STUB_TAG, *code, os.str().c_str())); + CodeCreateEvent(Logger::STUB_TAG, AbstractCode::cast(*code), + os.str().c_str())); Counters* counters = isolate()->counters(); counters->total_stubs_code_size()->Increment(code->instruction_size()); #ifdef DEBUG @@ -451,31 +453,9 @@ void CompareICStub::Generate(MacroAssembler* masm) { } -void CompareNilICStub::UpdateStatus(Handle<Object> object) { - State state = this->state(); - DCHECK(!state.Contains(GENERIC)); - State old_state = state; - if (object->IsNull()) { - state.Add(NULL_TYPE); - } else if (object->IsUndefined()) { - state.Add(UNDEFINED); - } else if (object->IsUndetectableObject() || object->IsSmi()) { - state.RemoveAll(); - state.Add(GENERIC); - } else if (IsMonomorphic()) { - state.RemoveAll(); - state.Add(GENERIC); - } else { - state.Add(MONOMORPHIC_MAP); - } - TraceTransition(old_state, state); - set_sub_minor_key(TypesBits::update(sub_minor_key(), state.ToIntegral())); -} - - Handle<Code> TurboFanCodeStub::GenerateCode() { const char* name = CodeStub::MajorName(MajorKey()); - Zone zone; + Zone zone(isolate()->allocator()); CallInterfaceDescriptor descriptor(GetCallInterfaceDescriptor()); compiler::CodeStubAssembler assembler(isolate(), &zone, descriptor, GetCodeFlags(), name); @@ -483,6 +463,40 @@ Handle<Code> TurboFanCodeStub::GenerateCode() { return assembler.GenerateCode(); } +void AllocateHeapNumberStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + typedef compiler::Node Node; + + Node* result = assembler->AllocateHeapNumber(); + assembler->Return(result); +} + +void AllocateMutableHeapNumberStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + typedef compiler::Node Node; + + Node* result = assembler->Allocate(HeapNumber::kSize); + assembler->StoreMapNoWriteBarrier( + result, + assembler->HeapConstant(isolate()->factory()->mutable_heap_number_map())); + assembler->Return(result); +} + +#define SIMD128_GEN_ASM(TYPE, Type, type, lane_count, lane_type) \ + void Allocate##Type##Stub::GenerateAssembly( \ + compiler::CodeStubAssembler* assembler) const { \ + compiler::Node* result = assembler->Allocate( \ + Simd128Value::kSize, compiler::CodeStubAssembler::kNone); \ + compiler::Node* map_offset = \ + assembler->IntPtrConstant(HeapObject::kMapOffset - kHeapObjectTag); \ + compiler::Node* map = assembler->IntPtrAdd(result, map_offset); \ + assembler->StoreNoWriteBarrier( \ + MachineRepresentation::kTagged, map, \ + assembler->HeapConstant(isolate()->factory()->type##_map())); \ + assembler->Return(result); \ + } +SIMD128_TYPES(SIMD128_GEN_ASM) +#undef SIMD128_GEN_ASM void StringLengthStub::GenerateAssembly( compiler::CodeStubAssembler* assembler) const { @@ -494,6 +508,2575 @@ void StringLengthStub::GenerateAssembly( assembler->Return(result); } +void AddStub::GenerateAssembly(compiler::CodeStubAssembler* assembler) const { + typedef compiler::CodeStubAssembler::Label Label; + typedef compiler::Node Node; + typedef compiler::CodeStubAssembler::Variable Variable; + + Node* context = assembler->Parameter(2); + + // Shared entry for floating point addition. + Label do_fadd(assembler); + Variable var_fadd_lhs(assembler, MachineRepresentation::kFloat64), + var_fadd_rhs(assembler, MachineRepresentation::kFloat64); + + // We might need to loop several times due to ToPrimitive, ToString 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 the {rhs} is also a Smi. + Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler); + assembler->Branch(assembler->WordIsSmi(rhs), &if_rhsissmi, + &if_rhsisnotsmi); + + assembler->Bind(&if_rhsissmi); + { + // Try fast Smi addition first. + Node* pair = assembler->SmiAddWithOverflow(lhs, rhs); + Node* overflow = assembler->Projection(1, pair); + + // Check if the Smi additon overflowed. + Label if_overflow(assembler), if_notoverflow(assembler); + assembler->Branch(overflow, &if_overflow, &if_notoverflow); + + assembler->Bind(&if_overflow); + { + var_fadd_lhs.Bind(assembler->SmiToFloat64(lhs)); + var_fadd_rhs.Bind(assembler->SmiToFloat64(rhs)); + assembler->Goto(&do_fadd); + } + + assembler->Bind(&if_notoverflow); + assembler->Return(assembler->Projection(0, pair)); + } + + assembler->Bind(&if_rhsisnotsmi); + { + // Load the map of {rhs}. + Node* rhs_map = assembler->LoadObjectField(rhs, HeapObject::kMapOffset); + + // Check if the {rhs} is a HeapNumber. + Label if_rhsisnumber(assembler), + if_rhsisnotnumber(assembler, Label::kDeferred); + Node* number_map = assembler->HeapNumberMapConstant(); + assembler->Branch(assembler->WordEqual(rhs_map, number_map), + &if_rhsisnumber, &if_rhsisnotnumber); + + assembler->Bind(&if_rhsisnumber); + { + var_fadd_lhs.Bind(assembler->SmiToFloat64(lhs)); + var_fadd_rhs.Bind(assembler->LoadHeapNumberValue(rhs)); + assembler->Goto(&do_fadd); + } + + assembler->Bind(&if_rhsisnotnumber); + { + // Load the instance type of {rhs}. + Node* rhs_instance_type = assembler->LoadMapInstanceType(rhs_map); + + // Check if the {rhs} is a String. + Label if_rhsisstring(assembler, Label::kDeferred), + 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); + { + // Convert {lhs}, which is a Smi, to a String and concatenate the + // resulting string with the String {rhs}. + Callable callable = CodeFactory::StringAdd( + assembler->isolate(), STRING_ADD_CONVERT_LEFT, NOT_TENURED); + assembler->TailCallStub(callable, context, lhs, rhs); + } + + assembler->Bind(&if_rhsisnotstring); + { + // Check if {rhs} is a JSReceiver. + 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 no hint. + // TODO(bmeurer): Hook up ToPrimitiveStub here, once it's there. + var_rhs.Bind( + assembler->CallRuntime(Runtime::kToPrimitive, context, rhs)); + assembler->Goto(&loop); + } + + assembler->Bind(&if_rhsisnotreceiver); + { + // Convert {rhs} to a Number first. + Callable callable = + CodeFactory::NonNumberToNumber(assembler->isolate()); + var_rhs.Bind(assembler->CallStub(callable, context, rhs)); + assembler->Goto(&loop); + } + } + } + } + } + + assembler->Bind(&if_lhsisnotsmi); + { + // Load the map and instance type of {lhs}. + Node* lhs_instance_type = assembler->LoadInstanceType(lhs); + + // Check if {lhs} is a String. + Label if_lhsisstring(assembler), if_lhsisnotstring(assembler); + assembler->Branch(assembler->Int32LessThan( + lhs_instance_type, + assembler->Int32Constant(FIRST_NONSTRING_TYPE)), + &if_lhsisstring, &if_lhsisnotstring); + + assembler->Bind(&if_lhsisstring); + { + // Convert {rhs} to a String (using the sequence of ToPrimitive with + // no hint followed by ToString) and concatenate the strings. + Callable callable = CodeFactory::StringAdd( + assembler->isolate(), STRING_ADD_CONVERT_RIGHT, NOT_TENURED); + assembler->TailCallStub(callable, context, lhs, rhs); + } + + assembler->Bind(&if_lhsisnotstring); + { + // Check if {rhs} is a Smi. + Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler); + assembler->Branch(assembler->WordIsSmi(rhs), &if_rhsissmi, + &if_rhsisnotsmi); + + assembler->Bind(&if_rhsissmi); + { + // Check if {lhs} is a Number. + Label if_lhsisnumber(assembler), + if_lhsisnotnumber(assembler, Label::kDeferred); + assembler->Branch(assembler->Word32Equal( + lhs_instance_type, + assembler->Int32Constant(HEAP_NUMBER_TYPE)), + &if_lhsisnumber, &if_lhsisnotnumber); + + assembler->Bind(&if_lhsisnumber); + { + // The {lhs} is a HeapNumber, the {rhs} is a Smi, just add them. + var_fadd_lhs.Bind(assembler->LoadHeapNumberValue(lhs)); + var_fadd_rhs.Bind(assembler->SmiToFloat64(rhs)); + assembler->Goto(&do_fadd); + } + + assembler->Bind(&if_lhsisnotnumber); + { + // The {lhs} is neither a Number nor a String, and the {rhs} is a + // Smi. + 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 no hint. + // TODO(bmeurer): Hook up ToPrimitiveStub here, once it's there. + var_lhs.Bind( + assembler->CallRuntime(Runtime::kToPrimitive, context, lhs)); + assembler->Goto(&loop); + } + + assembler->Bind(&if_lhsisnotreceiver); + { + // Convert {lhs} to a Number first. + Callable callable = + CodeFactory::NonNumberToNumber(assembler->isolate()); + var_lhs.Bind(assembler->CallStub(callable, context, lhs)); + assembler->Goto(&loop); + } + } + } + + assembler->Bind(&if_rhsisnotsmi); + { + // Load the instance type of {rhs}. + Node* rhs_instance_type = assembler->LoadInstanceType(rhs); + + // Check if {rhs} is a String. + Label if_rhsisstring(assembler), if_rhsisnotstring(assembler); + assembler->Branch(assembler->Int32LessThan( + rhs_instance_type, + assembler->Int32Constant(FIRST_NONSTRING_TYPE)), + &if_rhsisstring, &if_rhsisnotstring); + + assembler->Bind(&if_rhsisstring); + { + // Convert {lhs} to a String (using the sequence of ToPrimitive with + // no hint followed by ToString) and concatenate the strings. + Callable callable = CodeFactory::StringAdd( + assembler->isolate(), STRING_ADD_CONVERT_LEFT, NOT_TENURED); + assembler->TailCallStub(callable, context, lhs, rhs); + } + + assembler->Bind(&if_rhsisnotstring); + { + // Check if {lhs} is a HeapNumber. + Label if_lhsisnumber(assembler), if_lhsisnotnumber(assembler); + assembler->Branch(assembler->Word32Equal( + lhs_instance_type, + assembler->Int32Constant(HEAP_NUMBER_TYPE)), + &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->Word32Equal( + rhs_instance_type, + assembler->Int32Constant(HEAP_NUMBER_TYPE)), + &if_rhsisnumber, &if_rhsisnotnumber); + + assembler->Bind(&if_rhsisnumber); + { + // Perform a floating point addition. + var_fadd_lhs.Bind(assembler->LoadHeapNumberValue(lhs)); + var_fadd_rhs.Bind(assembler->LoadHeapNumberValue(rhs)); + assembler->Goto(&do_fadd); + } + + assembler->Bind(&if_rhsisnotnumber); + { + // Check if {rhs} is a JSReceiver. + 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 no hint. + // TODO(bmeurer): Hook up ToPrimitiveStub here too. + var_rhs.Bind(assembler->CallRuntime(Runtime::kToPrimitive, + context, rhs)); + assembler->Goto(&loop); + } + + assembler->Bind(&if_rhsisnotreceiver); + { + // Convert {rhs} to a Number first. + Callable callable = + CodeFactory::NonNumberToNumber(assembler->isolate()); + var_rhs.Bind(assembler->CallStub(callable, context, rhs)); + assembler->Goto(&loop); + } + } + } + + assembler->Bind(&if_lhsisnotnumber); + { + // Check if {lhs} is a JSReceiver. + Label if_lhsisreceiver(assembler, Label::kDeferred), + if_lhsisnotreceiver(assembler); + 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 no hint. + // TODO(bmeurer): Hook up ToPrimitiveStub here, once it's there. + var_lhs.Bind(assembler->CallRuntime(Runtime::kToPrimitive, + context, lhs)); + assembler->Goto(&loop); + } + + assembler->Bind(&if_lhsisnotreceiver); + { + // Check if {rhs} is a JSReceiver. + 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 no hint. + // TODO(bmeurer): Hook up ToPrimitiveStub here too. + var_rhs.Bind(assembler->CallRuntime(Runtime::kToPrimitive, + context, rhs)); + assembler->Goto(&loop); + } + + assembler->Bind(&if_rhsisnotreceiver); + { + // Convert {lhs} to a Number first. + Callable callable = + CodeFactory::NonNumberToNumber(assembler->isolate()); + var_lhs.Bind(assembler->CallStub(callable, context, lhs)); + assembler->Goto(&loop); + } + } + } + } + } + } + } + } + + assembler->Bind(&do_fadd); + { + Node* lhs_value = var_fadd_lhs.value(); + Node* rhs_value = var_fadd_rhs.value(); + Node* value = assembler->Float64Add(lhs_value, rhs_value); + Node* result = assembler->ChangeFloat64ToTagged(value); + assembler->Return(result); + } +} + +void SubtractStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + typedef compiler::CodeStubAssembler::Label Label; + typedef compiler::Node Node; + typedef compiler::CodeStubAssembler::Variable Variable; + + Node* context = assembler->Parameter(2); + + // Shared entry for floating point subtraction. + Label do_fsub(assembler); + Variable var_fsub_lhs(assembler, MachineRepresentation::kFloat64), + var_fsub_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 the {rhs} is also a Smi. + Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler); + assembler->Branch(assembler->WordIsSmi(rhs), &if_rhsissmi, + &if_rhsisnotsmi); + + assembler->Bind(&if_rhsissmi); + { + // Try a fast Smi subtraction first. + Node* pair = assembler->SmiSubWithOverflow(lhs, rhs); + Node* overflow = assembler->Projection(1, pair); + + // Check if the Smi subtraction overflowed. + Label if_overflow(assembler), if_notoverflow(assembler); + assembler->Branch(overflow, &if_overflow, &if_notoverflow); + + assembler->Bind(&if_overflow); + { + // The result doesn't fit into Smi range. + var_fsub_lhs.Bind(assembler->SmiToFloat64(lhs)); + var_fsub_rhs.Bind(assembler->SmiToFloat64(rhs)); + assembler->Goto(&do_fsub); + } + + assembler->Bind(&if_notoverflow); + assembler->Return(assembler->Projection(0, pair)); + } + + assembler->Bind(&if_rhsisnotsmi); + { + // Load the map of the {rhs}. + Node* rhs_map = assembler->LoadMap(rhs); + + // Check if {rhs} is a HeapNumber. + Label if_rhsisnumber(assembler), + if_rhsisnotnumber(assembler, Label::kDeferred); + Node* number_map = assembler->HeapNumberMapConstant(); + assembler->Branch(assembler->WordEqual(rhs_map, number_map), + &if_rhsisnumber, &if_rhsisnotnumber); + + assembler->Bind(&if_rhsisnumber); + { + // Perform a floating point subtraction. + var_fsub_lhs.Bind(assembler->SmiToFloat64(lhs)); + var_fsub_rhs.Bind(assembler->LoadHeapNumberValue(rhs)); + assembler->Goto(&do_fsub); + } + + assembler->Bind(&if_rhsisnotnumber); + { + // Convert the {rhs} to a Number first. + Callable callable = CodeFactory::NonNumberToNumber(isolate()); + var_rhs.Bind(assembler->CallStub(callable, context, rhs)); + assembler->Goto(&loop); + } + } + } + + assembler->Bind(&if_lhsisnotsmi); + { + // Load the map of the {lhs}. + Node* lhs_map = assembler->LoadMap(lhs); + + // Check if the {lhs} is a HeapNumber. + Label if_lhsisnumber(assembler), + if_lhsisnotnumber(assembler, Label::kDeferred); + Node* number_map = assembler->HeapNumberMapConstant(); + assembler->Branch(assembler->WordEqual(lhs_map, number_map), + &if_lhsisnumber, &if_lhsisnotnumber); + + assembler->Bind(&if_lhsisnumber); + { + // Check if the {rhs} is a Smi. + Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler); + assembler->Branch(assembler->WordIsSmi(rhs), &if_rhsissmi, + &if_rhsisnotsmi); + + assembler->Bind(&if_rhsissmi); + { + // Perform a floating point subtraction. + var_fsub_lhs.Bind(assembler->LoadHeapNumberValue(lhs)); + var_fsub_rhs.Bind(assembler->SmiToFloat64(rhs)); + assembler->Goto(&do_fsub); + } + + assembler->Bind(&if_rhsisnotsmi); + { + // Load the map of the {rhs}. + Node* rhs_map = assembler->LoadMap(rhs); + + // Check if the {rhs} is a HeapNumber. + 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); + { + // Perform a floating point subtraction. + var_fsub_lhs.Bind(assembler->LoadHeapNumberValue(lhs)); + var_fsub_rhs.Bind(assembler->LoadHeapNumberValue(rhs)); + assembler->Goto(&do_fsub); + } + + assembler->Bind(&if_rhsisnotnumber); + { + // Convert the {rhs} to a Number first. + Callable callable = CodeFactory::NonNumberToNumber(isolate()); + var_rhs.Bind(assembler->CallStub(callable, context, rhs)); + assembler->Goto(&loop); + } + } + } + + assembler->Bind(&if_lhsisnotnumber); + { + // Convert the {lhs} to a Number first. + Callable callable = CodeFactory::NonNumberToNumber(isolate()); + var_lhs.Bind(assembler->CallStub(callable, context, lhs)); + assembler->Goto(&loop); + } + } + } + + assembler->Bind(&do_fsub); + { + Node* lhs_value = var_fsub_lhs.value(); + Node* rhs_value = var_fsub_rhs.value(); + Node* value = assembler->Float64Sub(lhs_value, rhs_value); + Node* result = assembler->ChangeFloat64ToTagged(value); + assembler->Return(result); + } +} + +void BitwiseAndStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + using compiler::Node; + + Node* lhs = assembler->Parameter(0); + Node* rhs = assembler->Parameter(1); + Node* context = assembler->Parameter(2); + Node* lhs_value = assembler->TruncateTaggedToWord32(context, lhs); + Node* rhs_value = assembler->TruncateTaggedToWord32(context, rhs); + Node* value = assembler->Word32And(lhs_value, rhs_value); + Node* result = assembler->ChangeInt32ToTagged(value); + assembler->Return(result); +} + +void BitwiseOrStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + using compiler::Node; + + Node* lhs = assembler->Parameter(0); + Node* rhs = assembler->Parameter(1); + Node* context = assembler->Parameter(2); + Node* lhs_value = assembler->TruncateTaggedToWord32(context, lhs); + Node* rhs_value = assembler->TruncateTaggedToWord32(context, rhs); + Node* value = assembler->Word32Or(lhs_value, rhs_value); + Node* result = assembler->ChangeInt32ToTagged(value); + assembler->Return(result); +} + +void BitwiseXorStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + using compiler::Node; + + Node* lhs = assembler->Parameter(0); + Node* rhs = assembler->Parameter(1); + Node* context = assembler->Parameter(2); + Node* lhs_value = assembler->TruncateTaggedToWord32(context, lhs); + Node* rhs_value = assembler->TruncateTaggedToWord32(context, rhs); + Node* value = assembler->Word32Xor(lhs_value, rhs_value); + Node* result = assembler->ChangeInt32ToTagged(value); + assembler->Return(result); +} + +namespace { + +enum RelationalComparisonMode { + kLessThan, + kLessThanOrEqual, + kGreaterThan, + kGreaterThanOrEqual +}; + +void GenerateAbstractRelationalComparison( + compiler::CodeStubAssembler* assembler, RelationalComparisonMode 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->LoadMap(rhs); + + // 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::NonNumberToNumber(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->LoadMap(lhs); + + // 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::NonNumberToNumber(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->LoadMap(rhs); + + // 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::NonNumberToNumber(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. + switch (mode) { + case kLessThan: + assembler->TailCallStub( + CodeFactory::StringLessThan(assembler->isolate()), + context, lhs, rhs); + break; + case kLessThanOrEqual: + assembler->TailCallStub( + CodeFactory::StringLessThanOrEqual(assembler->isolate()), + context, lhs, rhs); + break; + case kGreaterThan: + assembler->TailCallStub( + CodeFactory::StringGreaterThan(assembler->isolate()), + context, lhs, rhs); + break; + case kGreaterThanOrEqual: + assembler->TailCallStub(CodeFactory::StringGreaterThanOrEqual( + assembler->isolate()), + 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 GenerateEqual_Same(compiler::CodeStubAssembler* assembler, + compiler::Node* value, + compiler::CodeStubAssembler::Label* if_equal, + compiler::CodeStubAssembler::Label* if_notequal) { + // In case of abstract or strict equality checks, we need additional checks + // for NaN values because they are not considered equal, even if both the + // left and the right hand side reference exactly the same value. + // TODO(bmeurer): This seems to violate the SIMD.js specification, but it + // seems to be what is tested in the current SIMD.js testsuite. + + typedef compiler::CodeStubAssembler::Label Label; + typedef compiler::Node Node; + + // Check if {value} is a Smi or a HeapObject. + Label if_valueissmi(assembler), if_valueisnotsmi(assembler); + assembler->Branch(assembler->WordIsSmi(value), &if_valueissmi, + &if_valueisnotsmi); + + assembler->Bind(&if_valueisnotsmi); + { + // Load the map of {value}. + Node* value_map = assembler->LoadMap(value); + + // Check if {value} (and therefore {rhs}) is a HeapNumber. + Node* number_map = assembler->HeapNumberMapConstant(); + Label if_valueisnumber(assembler), if_valueisnotnumber(assembler); + assembler->Branch(assembler->WordEqual(value_map, number_map), + &if_valueisnumber, &if_valueisnotnumber); + + assembler->Bind(&if_valueisnumber); + { + // Convert {value} (and therefore {rhs}) to floating point value. + Node* value_value = assembler->LoadHeapNumberValue(value); + + // Check if the HeapNumber value is a NaN. + assembler->BranchIfFloat64IsNaN(value_value, if_notequal, if_equal); + } + + assembler->Bind(&if_valueisnotnumber); + assembler->Goto(if_equal); + } + + assembler->Bind(&if_valueissmi); + assembler->Goto(if_equal); +} + +void GenerateEqual_Simd128Value_HeapObject( + compiler::CodeStubAssembler* assembler, compiler::Node* lhs, + compiler::Node* lhs_map, compiler::Node* rhs, compiler::Node* rhs_map, + compiler::CodeStubAssembler::Label* if_equal, + compiler::CodeStubAssembler::Label* if_notequal) { + typedef compiler::CodeStubAssembler::Label Label; + typedef compiler::Node Node; + + // Check if {lhs} and {rhs} have the same map. + Label if_mapsame(assembler), if_mapnotsame(assembler); + assembler->Branch(assembler->WordEqual(lhs_map, rhs_map), &if_mapsame, + &if_mapnotsame); + + assembler->Bind(&if_mapsame); + { + // Both {lhs} and {rhs} are Simd128Values with the same map, need special + // handling for Float32x4 because of NaN comparisons. + Label if_float32x4(assembler), if_notfloat32x4(assembler); + Node* float32x4_map = + assembler->HeapConstant(assembler->factory()->float32x4_map()); + assembler->Branch(assembler->WordEqual(lhs_map, float32x4_map), + &if_float32x4, &if_notfloat32x4); + + assembler->Bind(&if_float32x4); + { + // Both {lhs} and {rhs} are Float32x4, compare the lanes individually + // using a floating point comparison. + for (int offset = Float32x4::kValueOffset - kHeapObjectTag; + offset < Float32x4::kSize - kHeapObjectTag; + offset += sizeof(float)) { + // Load the floating point values for {lhs} and {rhs}. + Node* lhs_value = assembler->Load(MachineType::Float32(), lhs, + assembler->IntPtrConstant(offset)); + Node* rhs_value = assembler->Load(MachineType::Float32(), rhs, + assembler->IntPtrConstant(offset)); + + // Perform a floating point comparison. + Label if_valueequal(assembler), if_valuenotequal(assembler); + assembler->Branch(assembler->Float32Equal(lhs_value, rhs_value), + &if_valueequal, &if_valuenotequal); + assembler->Bind(&if_valuenotequal); + assembler->Goto(if_notequal); + assembler->Bind(&if_valueequal); + } + + // All 4 lanes match, {lhs} and {rhs} considered equal. + assembler->Goto(if_equal); + } + + assembler->Bind(&if_notfloat32x4); + { + // For other Simd128Values we just perform a bitwise comparison. + for (int offset = Simd128Value::kValueOffset - kHeapObjectTag; + offset < Simd128Value::kSize - kHeapObjectTag; + offset += kPointerSize) { + // Load the word values for {lhs} and {rhs}. + Node* lhs_value = assembler->Load(MachineType::Pointer(), lhs, + assembler->IntPtrConstant(offset)); + Node* rhs_value = assembler->Load(MachineType::Pointer(), rhs, + assembler->IntPtrConstant(offset)); + + // Perform a bitwise word-comparison. + Label if_valueequal(assembler), if_valuenotequal(assembler); + assembler->Branch(assembler->WordEqual(lhs_value, rhs_value), + &if_valueequal, &if_valuenotequal); + assembler->Bind(&if_valuenotequal); + assembler->Goto(if_notequal); + assembler->Bind(&if_valueequal); + } + + // Bitwise comparison succeeded, {lhs} and {rhs} considered equal. + assembler->Goto(if_equal); + } + } + + assembler->Bind(&if_mapnotsame); + assembler->Goto(if_notequal); +} + +// ES6 section 7.2.12 Abstract Equality Comparison +void GenerateEqual(compiler::CodeStubAssembler* assembler, ResultMode mode) { + // This is a slightly optimized version of Object::Equals represented as + // scheduled TurboFan graph utilizing the CodeStubAssembler. Whenever you + // change something functionality wise in here, remember to update the + // Object::Equals method as well. + typedef compiler::CodeStubAssembler::Label Label; + typedef compiler::Node Node; + typedef compiler::CodeStubAssembler::Variable Variable; + + Node* context = assembler->Parameter(2); + + Label if_equal(assembler), if_notequal(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 {lhs} and {rhs} refer to the same object. + Label if_same(assembler), if_notsame(assembler); + assembler->Branch(assembler->WordEqual(lhs, rhs), &if_same, &if_notsame); + + assembler->Bind(&if_same); + { + // The {lhs} and {rhs} reference the exact same value, yet we need special + // treatment for HeapNumber, as NaN is not equal to NaN. + GenerateEqual_Same(assembler, lhs, &if_equal, &if_notequal); + } + + assembler->Bind(&if_notsame); + { + // Check if {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); + assembler->Goto(&if_notequal); + + assembler->Bind(&if_rhsisnotsmi); + { + // Load the map of {rhs}. + Node* rhs_map = assembler->LoadMap(rhs); + + // Check if {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 {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); + { + // Load the instance type of the {rhs}. + Node* rhs_instance_type = assembler->LoadMapInstanceType(rhs_map); + + // Check if the {rhs} is a String. + Label if_rhsisstring(assembler, Label::kDeferred), + 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); + { + // Convert the {rhs} to a Number. + Callable callable = + CodeFactory::StringToNumber(assembler->isolate()); + var_rhs.Bind(assembler->CallStub(callable, context, rhs)); + assembler->Goto(&loop); + } + + assembler->Bind(&if_rhsisnotstring); + { + // Check if the {rhs} is a Boolean. + Node* boolean_map = assembler->BooleanMapConstant(); + Label if_rhsisboolean(assembler, Label::kDeferred), + if_rhsisnotboolean(assembler, Label::kDeferred); + assembler->Branch(assembler->WordEqual(rhs_map, boolean_map), + &if_rhsisboolean, &if_rhsisnotboolean); + + assembler->Bind(&if_rhsisboolean); + { + // The {rhs} is a Boolean, load its number value. + var_rhs.Bind( + assembler->LoadObjectField(rhs, Oddball::kToNumberOffset)); + assembler->Goto(&loop); + } + + assembler->Bind(&if_rhsisnotboolean); + { + // Check if the {rhs} is a Receiver. + 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 no hint). + // TODO(bmeurer): Hook up ToPrimitiveStub here once it exists. + var_rhs.Bind(assembler->CallRuntime(Runtime::kToPrimitive, + context, rhs)); + assembler->Goto(&loop); + } + + assembler->Bind(&if_rhsisnotreceiver); + assembler->Goto(&if_notequal); + } + } + } + } + } + + assembler->Bind(&if_lhsisnotsmi); + { + // 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); + { + // The {lhs} is a HeapObject and the {rhs} is a Smi; swapping {lhs} + // and {rhs} is not observable and doesn't matter for the result, so + // we can just swap them and use the Smi handling above (for {lhs} + // being a Smi). + var_lhs.Bind(rhs); + var_rhs.Bind(lhs); + assembler->Goto(&loop); + } + + assembler->Bind(&if_rhsisnotsmi); + { + Label if_lhsisstring(assembler), if_lhsisnumber(assembler), + if_lhsissymbol(assembler), if_lhsissimd128value(assembler), + if_lhsisoddball(assembler), if_lhsisreceiver(assembler); + + // Both {lhs} and {rhs} are HeapObjects, load their maps + // and their instance types. + Node* lhs_map = assembler->LoadMap(lhs); + Node* rhs_map = assembler->LoadMap(rhs); + + // Load the instance types of {lhs} and {rhs}. + Node* lhs_instance_type = assembler->LoadMapInstanceType(lhs_map); + Node* rhs_instance_type = assembler->LoadMapInstanceType(rhs_map); + + // Dispatch based on the instance type of {lhs}. + size_t const kNumCases = FIRST_NONSTRING_TYPE + 4; + Label* case_labels[kNumCases]; + int32_t case_values[kNumCases]; + for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) { + case_labels[i] = new Label(assembler); + case_values[i] = i; + } + case_labels[FIRST_NONSTRING_TYPE + 0] = &if_lhsisnumber; + case_values[FIRST_NONSTRING_TYPE + 0] = HEAP_NUMBER_TYPE; + case_labels[FIRST_NONSTRING_TYPE + 1] = &if_lhsissymbol; + case_values[FIRST_NONSTRING_TYPE + 1] = SYMBOL_TYPE; + case_labels[FIRST_NONSTRING_TYPE + 2] = &if_lhsissimd128value; + case_values[FIRST_NONSTRING_TYPE + 2] = SIMD128_VALUE_TYPE; + case_labels[FIRST_NONSTRING_TYPE + 3] = &if_lhsisoddball; + case_values[FIRST_NONSTRING_TYPE + 3] = ODDBALL_TYPE; + assembler->Switch(lhs_instance_type, &if_lhsisreceiver, case_values, + case_labels, arraysize(case_values)); + for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) { + assembler->Bind(case_labels[i]); + assembler->Goto(&if_lhsisstring); + delete case_labels[i]; + } + + assembler->Bind(&if_lhsisstring); + { + // 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 of type String, just do the + // string comparison then. + Callable callable = + (mode == kDontNegateResult) + ? CodeFactory::StringEqual(assembler->isolate()) + : CodeFactory::StringNotEqual(assembler->isolate()); + assembler->TailCallStub(callable, context, lhs, rhs); + } + + assembler->Bind(&if_rhsisnotstring); + { + // The {lhs} is a String and the {rhs} is some other HeapObject. + // Swapping {lhs} and {rhs} is not observable and doesn't matter + // for the result, so we can just swap them and use the String + // handling below (for {rhs} being a String). + var_lhs.Bind(rhs); + var_rhs.Bind(lhs); + assembler->Goto(&loop); + } + } + + assembler->Bind(&if_lhsisnumber); + { + // Check if {rhs} is also a HeapNumber. + Label if_rhsisnumber(assembler), + if_rhsisnotnumber(assembler, Label::kDeferred); + assembler->Branch( + assembler->Word32Equal(lhs_instance_type, rhs_instance_type), + &if_rhsisnumber, &if_rhsisnotnumber); + + assembler->Bind(&if_rhsisnumber); + { + // Convert {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); + { + // The {lhs} is a Number, the {rhs} is some other HeapObject. + Label if_rhsisstring(assembler, Label::kDeferred), + if_rhsisnotstring(assembler); + assembler->Branch( + assembler->Int32LessThan( + rhs_instance_type, + assembler->Int32Constant(FIRST_NONSTRING_TYPE)), + &if_rhsisstring, &if_rhsisnotstring); + + assembler->Bind(&if_rhsisstring); + { + // The {rhs} is a String and the {lhs} is a HeapNumber; we need + // to convert the {rhs} to a Number and compare the output to + // the Number on the {lhs}. + Callable callable = + CodeFactory::StringToNumber(assembler->isolate()); + var_rhs.Bind(assembler->CallStub(callable, context, rhs)); + assembler->Goto(&loop); + } + + assembler->Bind(&if_rhsisnotstring); + { + // Check if the {rhs} is a JSReceiver. + Label if_rhsisreceiver(assembler, Label::kDeferred), + if_rhsisnotreceiver(assembler); + STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); + assembler->Branch( + assembler->Int32LessThanOrEqual( + assembler->Int32Constant(FIRST_JS_RECEIVER_TYPE), + rhs_instance_type), + &if_rhsisreceiver, &if_rhsisnotreceiver); + + assembler->Bind(&if_rhsisreceiver); + { + // The {lhs} is a Primitive and the {rhs} is a JSReceiver. + // Swapping {lhs} and {rhs} is not observable and doesn't + // matter for the result, so we can just swap them and use + // the JSReceiver handling below (for {lhs} being a + // JSReceiver). + var_lhs.Bind(rhs); + var_rhs.Bind(lhs); + assembler->Goto(&loop); + } + + assembler->Bind(&if_rhsisnotreceiver); + { + // Check if {rhs} is a Boolean. + Label if_rhsisboolean(assembler), + if_rhsisnotboolean(assembler); + Node* boolean_map = assembler->BooleanMapConstant(); + assembler->Branch(assembler->WordEqual(rhs_map, boolean_map), + &if_rhsisboolean, &if_rhsisnotboolean); + + assembler->Bind(&if_rhsisboolean); + { + // The {rhs} is a Boolean, convert it to a Smi first. + var_rhs.Bind(assembler->LoadObjectField( + rhs, Oddball::kToNumberOffset)); + assembler->Goto(&loop); + } + + assembler->Bind(&if_rhsisnotboolean); + assembler->Goto(&if_notequal); + } + } + } + } + + assembler->Bind(&if_lhsisoddball); + { + // The {lhs} is an Oddball and {rhs} is some other HeapObject. + Label if_lhsisboolean(assembler), if_lhsisnotboolean(assembler); + Node* boolean_map = assembler->BooleanMapConstant(); + assembler->Branch(assembler->WordEqual(lhs_map, boolean_map), + &if_lhsisboolean, &if_lhsisnotboolean); + + assembler->Bind(&if_lhsisboolean); + { + // The {lhs} is a Boolean, check if {rhs} is also a Boolean. + Label if_rhsisboolean(assembler), if_rhsisnotboolean(assembler); + assembler->Branch(assembler->WordEqual(rhs_map, boolean_map), + &if_rhsisboolean, &if_rhsisnotboolean); + + assembler->Bind(&if_rhsisboolean); + { + // Both {lhs} and {rhs} are distinct Boolean values. + assembler->Goto(&if_notequal); + } + + assembler->Bind(&if_rhsisnotboolean); + { + // Convert the {lhs} to a Number first. + var_lhs.Bind( + assembler->LoadObjectField(lhs, Oddball::kToNumberOffset)); + assembler->Goto(&loop); + } + } + + assembler->Bind(&if_lhsisnotboolean); + { + // The {lhs} is either Null or Undefined; check if the {rhs} is + // undetectable (i.e. either also Null or Undefined or some + // undetectable JSReceiver). + Node* rhs_bitfield = assembler->LoadMapBitField(rhs_map); + assembler->BranchIfWord32Equal( + assembler->Word32And( + rhs_bitfield, + assembler->Int32Constant(1 << Map::kIsUndetectable)), + assembler->Int32Constant(0), &if_notequal, &if_equal); + } + } + + assembler->Bind(&if_lhsissymbol); + { + // Check if the {rhs} is a JSReceiver. + Label if_rhsisreceiver(assembler, Label::kDeferred), + if_rhsisnotreceiver(assembler); + STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); + assembler->Branch( + assembler->Int32LessThanOrEqual( + assembler->Int32Constant(FIRST_JS_RECEIVER_TYPE), + rhs_instance_type), + &if_rhsisreceiver, &if_rhsisnotreceiver); + + assembler->Bind(&if_rhsisreceiver); + { + // The {lhs} is a Primitive and the {rhs} is a JSReceiver. + // Swapping {lhs} and {rhs} is not observable and doesn't + // matter for the result, so we can just swap them and use + // the JSReceiver handling below (for {lhs} being a JSReceiver). + var_lhs.Bind(rhs); + var_rhs.Bind(lhs); + assembler->Goto(&loop); + } + + assembler->Bind(&if_rhsisnotreceiver); + { + // The {rhs} is not a JSReceiver and also not the same Symbol + // as the {lhs}, so this is equality check is considered false. + assembler->Goto(&if_notequal); + } + } + + assembler->Bind(&if_lhsissimd128value); + { + // Check if the {rhs} is also a Simd128Value. + Label if_rhsissimd128value(assembler), + if_rhsisnotsimd128value(assembler); + assembler->Branch( + assembler->Word32Equal(lhs_instance_type, rhs_instance_type), + &if_rhsissimd128value, &if_rhsisnotsimd128value); + + assembler->Bind(&if_rhsissimd128value); + { + // Both {lhs} and {rhs} is a Simd128Value. + GenerateEqual_Simd128Value_HeapObject(assembler, lhs, lhs_map, + rhs, rhs_map, &if_equal, + &if_notequal); + } + + assembler->Bind(&if_rhsisnotsimd128value); + { + // Check if the {rhs} is a JSReceiver. + Label if_rhsisreceiver(assembler, Label::kDeferred), + if_rhsisnotreceiver(assembler); + STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); + assembler->Branch( + assembler->Int32LessThanOrEqual( + assembler->Int32Constant(FIRST_JS_RECEIVER_TYPE), + rhs_instance_type), + &if_rhsisreceiver, &if_rhsisnotreceiver); + + assembler->Bind(&if_rhsisreceiver); + { + // The {lhs} is a Primitive and the {rhs} is a JSReceiver. + // Swapping {lhs} and {rhs} is not observable and doesn't + // matter for the result, so we can just swap them and use + // the JSReceiver handling below (for {lhs} being a JSReceiver). + var_lhs.Bind(rhs); + var_rhs.Bind(lhs); + assembler->Goto(&loop); + } + + assembler->Bind(&if_rhsisnotreceiver); + { + // The {rhs} is some other Primitive. + assembler->Goto(&if_notequal); + } + } + } + + assembler->Bind(&if_lhsisreceiver); + { + // Check if the {rhs} is also a JSReceiver. + Label if_rhsisreceiver(assembler), if_rhsisnotreceiver(assembler); + STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); + assembler->Branch( + assembler->Int32LessThanOrEqual( + assembler->Int32Constant(FIRST_JS_RECEIVER_TYPE), + rhs_instance_type), + &if_rhsisreceiver, &if_rhsisnotreceiver); + + assembler->Bind(&if_rhsisreceiver); + { + // Both {lhs} and {rhs} are different JSReceiver references, so + // this cannot be considered equal. + assembler->Goto(&if_notequal); + } + + assembler->Bind(&if_rhsisnotreceiver); + { + // Check if {rhs} is Null or Undefined (an undetectable check + // is sufficient here, since we already know that {rhs} is not + // a JSReceiver). + Label if_rhsisundetectable(assembler), + if_rhsisnotundetectable(assembler, Label::kDeferred); + Node* rhs_bitfield = assembler->LoadMapBitField(rhs_map); + assembler->BranchIfWord32Equal( + assembler->Word32And( + rhs_bitfield, + assembler->Int32Constant(1 << Map::kIsUndetectable)), + assembler->Int32Constant(0), &if_rhsisnotundetectable, + &if_rhsisundetectable); + + assembler->Bind(&if_rhsisundetectable); + { + // Check if {lhs} is an undetectable JSReceiver. + Node* lhs_bitfield = assembler->LoadMapBitField(lhs_map); + assembler->BranchIfWord32Equal( + assembler->Word32And( + lhs_bitfield, + assembler->Int32Constant(1 << Map::kIsUndetectable)), + assembler->Int32Constant(0), &if_notequal, &if_equal); + } + + assembler->Bind(&if_rhsisnotundetectable); + { + // The {rhs} is some Primitive different from Null and + // Undefined, need to convert {lhs} to Primitive first. + // TODO(bmeurer): Hook up ToPrimitiveStub here once it exists. + var_lhs.Bind(assembler->CallRuntime(Runtime::kToPrimitive, + context, lhs)); + 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. + assembler->BranchIfFloat64Equal(lhs, rhs, &if_equal, &if_notequal); + } + + assembler->Bind(&if_equal); + assembler->Return(assembler->BooleanConstant(mode == kDontNegateResult)); + + assembler->Bind(&if_notequal); + assembler->Return(assembler->BooleanConstant(mode == kNegateResult)); +} + +void GenerateStrictEqual(compiler::CodeStubAssembler* assembler, + ResultMode mode) { + // Here's pseudo-code for the algorithm below in case of kDontNegateResult + // mode; for kNegateResult mode we properly negate the result. + // + // if (lhs == rhs) { + // if (lhs->IsHeapNumber()) return HeapNumber::cast(lhs)->value() != NaN; + // return true; + // } + // if (!lhs->IsSmi()) { + // if (lhs->IsHeapNumber()) { + // if (rhs->IsSmi()) { + // return Smi::cast(rhs)->value() == HeapNumber::cast(lhs)->value(); + // } else if (rhs->IsHeapNumber()) { + // return HeapNumber::cast(rhs)->value() == + // HeapNumber::cast(lhs)->value(); + // } else { + // return false; + // } + // } else { + // if (rhs->IsSmi()) { + // return false; + // } else { + // if (lhs->IsString()) { + // if (rhs->IsString()) { + // return %StringEqual(lhs, rhs); + // } else { + // return false; + // } + // } else if (lhs->IsSimd128()) { + // if (rhs->IsSimd128()) { + // return %StrictEqual(lhs, rhs); + // } + // } else { + // return false; + // } + // } + // } + // } else { + // if (rhs->IsSmi()) { + // return false; + // } else { + // if (rhs->IsHeapNumber()) { + // return Smi::cast(lhs)->value() == HeapNumber::cast(rhs)->value(); + // } else { + // return false; + // } + // } + // } + + typedef compiler::CodeStubAssembler::Label Label; + typedef compiler::Node Node; + + Node* lhs = assembler->Parameter(0); + Node* rhs = assembler->Parameter(1); + Node* context = assembler->Parameter(2); + + Label if_equal(assembler), if_notequal(assembler); + + // Check if {lhs} and {rhs} refer to the same object. + Label if_same(assembler), if_notsame(assembler); + assembler->Branch(assembler->WordEqual(lhs, rhs), &if_same, &if_notsame); + + assembler->Bind(&if_same); + { + // The {lhs} and {rhs} reference the exact same value, yet we need special + // treatment for HeapNumber, as NaN is not equal to NaN. + GenerateEqual_Same(assembler, lhs, &if_equal, &if_notequal); + } + + assembler->Bind(&if_notsame); + { + // The {lhs} and {rhs} reference different objects, yet for Smi, HeapNumber, + // String and Simd128Value they can still be considered equal. + Node* number_map = assembler->HeapNumberMapConstant(); + + // Check if {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_lhsisnotsmi); + { + // Load the map of {lhs}. + Node* lhs_map = assembler->LoadMap(lhs); + + // 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 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); + { + // Convert {lhs} and {rhs} to floating point values. + Node* lhs_value = assembler->LoadHeapNumberValue(lhs); + Node* rhs_value = assembler->SmiToFloat64(rhs); + + // Perform a floating point comparison of {lhs} and {rhs}. + assembler->BranchIfFloat64Equal(lhs_value, rhs_value, &if_equal, + &if_notequal); + } + + assembler->Bind(&if_rhsisnotsmi); + { + // Load the map of {rhs}. + Node* rhs_map = assembler->LoadMap(rhs); + + // Check if {rhs} is also a HeapNumber. + Label if_rhsisnumber(assembler), if_rhsisnotnumber(assembler); + assembler->Branch(assembler->WordEqual(rhs_map, number_map), + &if_rhsisnumber, &if_rhsisnotnumber); + + assembler->Bind(&if_rhsisnumber); + { + // Convert {lhs} and {rhs} to floating point values. + Node* lhs_value = assembler->LoadHeapNumberValue(lhs); + Node* rhs_value = assembler->LoadHeapNumberValue(rhs); + + // Perform a floating point comparison of {lhs} and {rhs}. + assembler->BranchIfFloat64Equal(lhs_value, rhs_value, &if_equal, + &if_notequal); + } + + assembler->Bind(&if_rhsisnotnumber); + assembler->Goto(&if_notequal); + } + } + + assembler->Bind(&if_lhsisnotnumber); + { + // 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); + assembler->Goto(&if_notequal); + + assembler->Bind(&if_rhsisnotsmi); + { + // 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); + 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->LoadInstanceType(rhs); + + // Check if {rhs} is also a String. + Label if_rhsisstring(assembler), if_rhsisnotstring(assembler); + assembler->Branch(assembler->Int32LessThan( + rhs_instance_type, assembler->Int32Constant( + FIRST_NONSTRING_TYPE)), + &if_rhsisstring, &if_rhsisnotstring); + + assembler->Bind(&if_rhsisstring); + { + Callable callable = + (mode == kDontNegateResult) + ? CodeFactory::StringEqual(assembler->isolate()) + : CodeFactory::StringNotEqual(assembler->isolate()); + assembler->TailCallStub(callable, context, lhs, rhs); + } + + assembler->Bind(&if_rhsisnotstring); + assembler->Goto(&if_notequal); + } + + assembler->Bind(&if_lhsisnotstring); + { + // Check if {lhs} is a Simd128Value. + Label if_lhsissimd128value(assembler), + if_lhsisnotsimd128value(assembler); + assembler->Branch(assembler->Word32Equal( + lhs_instance_type, + assembler->Int32Constant(SIMD128_VALUE_TYPE)), + &if_lhsissimd128value, &if_lhsisnotsimd128value); + + assembler->Bind(&if_lhsissimd128value); + { + // Load the map of {rhs}. + Node* rhs_map = assembler->LoadMap(rhs); + + // Check if {rhs} is also a Simd128Value that is equal to {lhs}. + GenerateEqual_Simd128Value_HeapObject(assembler, lhs, lhs_map, + rhs, rhs_map, &if_equal, + &if_notequal); + } + + assembler->Bind(&if_lhsisnotsimd128value); + assembler->Goto(&if_notequal); + } + } + } + } + + assembler->Bind(&if_lhsissmi); + { + // We already know that {lhs} and {rhs} are not reference equal, and {lhs} + // is a Smi; so {lhs} and {rhs} can only be strictly equal if {rhs} is a + // HeapNumber with an equal floating point value. + + // 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); + assembler->Goto(&if_notequal); + + assembler->Bind(&if_rhsisnotsmi); + { + // Load the map of the {rhs}. + Node* rhs_map = assembler->LoadMap(rhs); + + // The {rhs} could be a HeapNumber with the same value as {lhs}. + Label if_rhsisnumber(assembler), if_rhsisnotnumber(assembler); + assembler->Branch(assembler->WordEqual(rhs_map, number_map), + &if_rhsisnumber, &if_rhsisnotnumber); + + assembler->Bind(&if_rhsisnumber); + { + // Convert {lhs} and {rhs} to floating point values. + Node* lhs_value = assembler->SmiToFloat64(lhs); + Node* rhs_value = assembler->LoadHeapNumberValue(rhs); + + // Perform a floating point comparison of {lhs} and {rhs}. + assembler->BranchIfFloat64Equal(lhs_value, rhs_value, &if_equal, + &if_notequal); + } + + assembler->Bind(&if_rhsisnotnumber); + assembler->Goto(&if_notequal); + } + } + } + + assembler->Bind(&if_equal); + assembler->Return(assembler->BooleanConstant(mode == kDontNegateResult)); + + assembler->Bind(&if_notequal); + assembler->Return(assembler->BooleanConstant(mode == kNegateResult)); +} + +void GenerateStringRelationalComparison(compiler::CodeStubAssembler* assembler, + RelationalComparisonMode mode) { + typedef compiler::CodeStubAssembler::Label Label; + typedef compiler::Node Node; + typedef compiler::CodeStubAssembler::Variable Variable; + + Node* lhs = assembler->Parameter(0); + Node* rhs = assembler->Parameter(1); + Node* context = assembler->Parameter(2); + + Label if_less(assembler), if_equal(assembler), if_greater(assembler); + + // Fast check to see if {lhs} and {rhs} refer to the same String object. + Label if_same(assembler), if_notsame(assembler); + assembler->Branch(assembler->WordEqual(lhs, rhs), &if_same, &if_notsame); + + assembler->Bind(&if_same); + assembler->Goto(&if_equal); + + assembler->Bind(&if_notsame); + { + // Load instance types of {lhs} and {rhs}. + Node* lhs_instance_type = assembler->LoadInstanceType(lhs); + Node* rhs_instance_type = assembler->LoadInstanceType(rhs); + + // Combine the instance types into a single 16-bit value, so we can check + // both of them at once. + Node* both_instance_types = assembler->Word32Or( + lhs_instance_type, + assembler->Word32Shl(rhs_instance_type, assembler->Int32Constant(8))); + + // Check that both {lhs} and {rhs} are flat one-byte strings. + int const kBothSeqOneByteStringMask = + kStringEncodingMask | kStringRepresentationMask | + ((kStringEncodingMask | kStringRepresentationMask) << 8); + int const kBothSeqOneByteStringTag = + kOneByteStringTag | kSeqStringTag | + ((kOneByteStringTag | kSeqStringTag) << 8); + Label if_bothonebyteseqstrings(assembler), + if_notbothonebyteseqstrings(assembler); + assembler->Branch(assembler->Word32Equal( + assembler->Word32And(both_instance_types, + assembler->Int32Constant( + kBothSeqOneByteStringMask)), + assembler->Int32Constant(kBothSeqOneByteStringTag)), + &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings); + + assembler->Bind(&if_bothonebyteseqstrings); + { + // Load the length of {lhs} and {rhs}. + Node* lhs_length = assembler->LoadObjectField(lhs, String::kLengthOffset); + Node* rhs_length = assembler->LoadObjectField(rhs, String::kLengthOffset); + + // Determine the minimum length. + Node* length = assembler->SmiMin(lhs_length, rhs_length); + + // Compute the effective offset of the first character. + Node* begin = assembler->IntPtrConstant(SeqOneByteString::kHeaderSize - + kHeapObjectTag); + + // Compute the first offset after the string from the length. + Node* end = assembler->IntPtrAdd(begin, assembler->SmiUntag(length)); + + // Loop over the {lhs} and {rhs} strings to see if they are equal. + Variable var_offset(assembler, MachineType::PointerRepresentation()); + Label loop(assembler, &var_offset); + var_offset.Bind(begin); + assembler->Goto(&loop); + assembler->Bind(&loop); + { + // Check if {offset} equals {end}. + Node* offset = var_offset.value(); + Label if_done(assembler), if_notdone(assembler); + assembler->Branch(assembler->WordEqual(offset, end), &if_done, + &if_notdone); + + assembler->Bind(&if_notdone); + { + // Load the next characters from {lhs} and {rhs}. + Node* lhs_value = assembler->Load(MachineType::Uint8(), lhs, offset); + Node* rhs_value = assembler->Load(MachineType::Uint8(), rhs, offset); + + // Check if the characters match. + Label if_valueissame(assembler), if_valueisnotsame(assembler); + assembler->Branch(assembler->Word32Equal(lhs_value, rhs_value), + &if_valueissame, &if_valueisnotsame); + + assembler->Bind(&if_valueissame); + { + // Advance to next character. + var_offset.Bind( + assembler->IntPtrAdd(offset, assembler->IntPtrConstant(1))); + } + assembler->Goto(&loop); + + assembler->Bind(&if_valueisnotsame); + assembler->BranchIf(assembler->Uint32LessThan(lhs_value, rhs_value), + &if_less, &if_greater); + } + + assembler->Bind(&if_done); + { + // All characters up to the min length are equal, decide based on + // string length. + Label if_lengthisequal(assembler), if_lengthisnotequal(assembler); + assembler->Branch(assembler->SmiEqual(lhs_length, rhs_length), + &if_lengthisequal, &if_lengthisnotequal); + + assembler->Bind(&if_lengthisequal); + assembler->Goto(&if_equal); + + assembler->Bind(&if_lengthisnotequal); + assembler->BranchIfSmiLessThan(lhs_length, rhs_length, &if_less, + &if_greater); + } + } + } + + assembler->Bind(&if_notbothonebyteseqstrings); + { + // TODO(bmeurer): Add fast case support for flattened cons strings; + // also add support for two byte string relational comparisons. + 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_less); + switch (mode) { + case kLessThan: + case kLessThanOrEqual: + assembler->Return(assembler->BooleanConstant(true)); + break; + + case kGreaterThan: + case kGreaterThanOrEqual: + assembler->Return(assembler->BooleanConstant(false)); + break; + } + + assembler->Bind(&if_equal); + switch (mode) { + case kLessThan: + case kGreaterThan: + assembler->Return(assembler->BooleanConstant(false)); + break; + + case kLessThanOrEqual: + case kGreaterThanOrEqual: + assembler->Return(assembler->BooleanConstant(true)); + break; + } + + assembler->Bind(&if_greater); + switch (mode) { + case kLessThan: + case kLessThanOrEqual: + assembler->Return(assembler->BooleanConstant(false)); + break; + + case kGreaterThan: + case kGreaterThanOrEqual: + assembler->Return(assembler->BooleanConstant(true)); + break; + } +} + +void GenerateStringEqual(compiler::CodeStubAssembler* assembler, + ResultMode mode) { + // Here's pseudo-code for the algorithm below in case of kDontNegateResult + // mode; for kNegateResult mode we properly negate the result. + // + // if (lhs == rhs) return true; + // if (lhs->length() != rhs->length()) return false; + // if (lhs->IsInternalizedString() && rhs->IsInternalizedString()) { + // return false; + // } + // if (lhs->IsSeqOneByteString() && rhs->IsSeqOneByteString()) { + // for (i = 0; i != lhs->length(); ++i) { + // if (lhs[i] != rhs[i]) return false; + // } + // return true; + // } + // return %StringEqual(lhs, rhs); + + typedef compiler::CodeStubAssembler::Label Label; + typedef compiler::Node Node; + typedef compiler::CodeStubAssembler::Variable Variable; + + Node* lhs = assembler->Parameter(0); + Node* rhs = assembler->Parameter(1); + Node* context = assembler->Parameter(2); + + Label if_equal(assembler), if_notequal(assembler); + + // Fast check to see if {lhs} and {rhs} refer to the same String object. + Label if_same(assembler), if_notsame(assembler); + assembler->Branch(assembler->WordEqual(lhs, rhs), &if_same, &if_notsame); + + assembler->Bind(&if_same); + assembler->Goto(&if_equal); + + assembler->Bind(&if_notsame); + { + // The {lhs} and {rhs} don't refer to the exact same String object. + + // Load the length of {lhs} and {rhs}. + Node* lhs_length = assembler->LoadObjectField(lhs, String::kLengthOffset); + Node* rhs_length = assembler->LoadObjectField(rhs, String::kLengthOffset); + + // Check if the lengths of {lhs} and {rhs} are equal. + Label if_lengthisequal(assembler), if_lengthisnotequal(assembler); + assembler->Branch(assembler->WordEqual(lhs_length, rhs_length), + &if_lengthisequal, &if_lengthisnotequal); + + assembler->Bind(&if_lengthisequal); + { + // Load instance types of {lhs} and {rhs}. + Node* lhs_instance_type = assembler->LoadInstanceType(lhs); + Node* rhs_instance_type = assembler->LoadInstanceType(rhs); + + // Combine the instance types into a single 16-bit value, so we can check + // both of them at once. + Node* both_instance_types = assembler->Word32Or( + lhs_instance_type, + assembler->Word32Shl(rhs_instance_type, assembler->Int32Constant(8))); + + // Check if both {lhs} and {rhs} are internalized. + int const kBothInternalizedMask = + kIsNotInternalizedMask | (kIsNotInternalizedMask << 8); + int const kBothInternalizedTag = + kInternalizedTag | (kInternalizedTag << 8); + Label if_bothinternalized(assembler), if_notbothinternalized(assembler); + assembler->Branch(assembler->Word32Equal( + assembler->Word32And(both_instance_types, + assembler->Int32Constant( + kBothInternalizedMask)), + assembler->Int32Constant(kBothInternalizedTag)), + &if_bothinternalized, &if_notbothinternalized); + + assembler->Bind(&if_bothinternalized); + { + // Fast negative check for internalized-to-internalized equality. + assembler->Goto(&if_notequal); + } + + assembler->Bind(&if_notbothinternalized); + { + // Check that both {lhs} and {rhs} are flat one-byte strings. + int const kBothSeqOneByteStringMask = + kStringEncodingMask | kStringRepresentationMask | + ((kStringEncodingMask | kStringRepresentationMask) << 8); + int const kBothSeqOneByteStringTag = + kOneByteStringTag | kSeqStringTag | + ((kOneByteStringTag | kSeqStringTag) << 8); + Label if_bothonebyteseqstrings(assembler), + if_notbothonebyteseqstrings(assembler); + assembler->Branch( + assembler->Word32Equal( + assembler->Word32And( + both_instance_types, + assembler->Int32Constant(kBothSeqOneByteStringMask)), + assembler->Int32Constant(kBothSeqOneByteStringTag)), + &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings); + + assembler->Bind(&if_bothonebyteseqstrings); + { + // Compute the effective offset of the first character. + Node* begin = assembler->IntPtrConstant( + SeqOneByteString::kHeaderSize - kHeapObjectTag); + + // Compute the first offset after the string from the length. + Node* end = + assembler->IntPtrAdd(begin, assembler->SmiUntag(lhs_length)); + + // Loop over the {lhs} and {rhs} strings to see if they are equal. + Variable var_offset(assembler, MachineType::PointerRepresentation()); + Label loop(assembler, &var_offset); + var_offset.Bind(begin); + assembler->Goto(&loop); + assembler->Bind(&loop); + { + // Check if {offset} equals {end}. + Node* offset = var_offset.value(); + Label if_done(assembler), if_notdone(assembler); + assembler->Branch(assembler->WordEqual(offset, end), &if_done, + &if_notdone); + + assembler->Bind(&if_notdone); + { + // Load the next characters from {lhs} and {rhs}. + Node* lhs_value = + assembler->Load(MachineType::Uint8(), lhs, offset); + Node* rhs_value = + assembler->Load(MachineType::Uint8(), rhs, offset); + + // Check if the characters match. + Label if_valueissame(assembler), if_valueisnotsame(assembler); + assembler->Branch(assembler->Word32Equal(lhs_value, rhs_value), + &if_valueissame, &if_valueisnotsame); + + assembler->Bind(&if_valueissame); + { + // Advance to next character. + var_offset.Bind( + assembler->IntPtrAdd(offset, assembler->IntPtrConstant(1))); + } + assembler->Goto(&loop); + + assembler->Bind(&if_valueisnotsame); + assembler->Goto(&if_notequal); + } + + assembler->Bind(&if_done); + assembler->Goto(&if_equal); + } + } + + assembler->Bind(&if_notbothonebyteseqstrings); + { + // TODO(bmeurer): Add fast case support for flattened cons strings; + // also add support for two byte string equality checks. + Runtime::FunctionId function_id = (mode == kDontNegateResult) + ? Runtime::kStringEqual + : Runtime::kStringNotEqual; + assembler->TailCallRuntime(function_id, context, lhs, rhs); + } + } + } + + assembler->Bind(&if_lengthisnotequal); + { + // Mismatch in length of {lhs} and {rhs}, cannot be equal. + assembler->Goto(&if_notequal); + } + } + + assembler->Bind(&if_equal); + assembler->Return(assembler->BooleanConstant(mode == kDontNegateResult)); + + assembler->Bind(&if_notequal); + assembler->Return(assembler->BooleanConstant(mode == kNegateResult)); +} + +} // 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 EqualStub::GenerateAssembly(compiler::CodeStubAssembler* assembler) const { + GenerateEqual(assembler, kDontNegateResult); +} + +void NotEqualStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + GenerateEqual(assembler, kNegateResult); +} + +void StrictEqualStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + GenerateStrictEqual(assembler, kDontNegateResult); +} + +void StrictNotEqualStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + GenerateStrictEqual(assembler, kNegateResult); +} + +void StringEqualStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + GenerateStringEqual(assembler, kDontNegateResult); +} + +void StringNotEqualStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + GenerateStringEqual(assembler, kNegateResult); +} + +void StringLessThanStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + GenerateStringRelationalComparison(assembler, kLessThan); +} + +void StringLessThanOrEqualStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + GenerateStringRelationalComparison(assembler, kLessThanOrEqual); +} + +void StringGreaterThanStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + GenerateStringRelationalComparison(assembler, kGreaterThan); +} + +void StringGreaterThanOrEqualStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + GenerateStringRelationalComparison(assembler, kGreaterThanOrEqual); +} + +void ToLengthStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + typedef compiler::CodeStubAssembler::Label Label; + typedef compiler::Node Node; + typedef compiler::CodeStubAssembler::Variable Variable; + + Node* context = assembler->Parameter(1); + + // We might need to loop once for ToNumber conversion. + Variable var_len(assembler, MachineRepresentation::kTagged); + Label loop(assembler, &var_len); + var_len.Bind(assembler->Parameter(0)); + assembler->Goto(&loop); + assembler->Bind(&loop); + { + // Shared entry points. + Label return_len(assembler), + return_two53minus1(assembler, Label::kDeferred), + return_zero(assembler, Label::kDeferred); + + // Load the current {len} value. + Node* len = var_len.value(); + + // Check if {len} is a positive Smi. + assembler->GotoIf(assembler->WordIsPositiveSmi(len), &return_len); + + // Check if {len} is a (negative) Smi. + assembler->GotoIf(assembler->WordIsSmi(len), &return_zero); + + // Check if {len} is a HeapNumber. + Label if_lenisheapnumber(assembler), + if_lenisnotheapnumber(assembler, Label::kDeferred); + assembler->Branch(assembler->WordEqual(assembler->LoadMap(len), + assembler->HeapNumberMapConstant()), + &if_lenisheapnumber, &if_lenisnotheapnumber); + + assembler->Bind(&if_lenisheapnumber); + { + // Load the floating-point value of {len}. + Node* len_value = assembler->LoadHeapNumberValue(len); + + // Check if {len} is not greater than zero. + assembler->GotoUnless(assembler->Float64GreaterThan( + len_value, assembler->Float64Constant(0.0)), + &return_zero); + + // Check if {len} is greater than or equal to 2^53-1. + assembler->GotoIf( + assembler->Float64GreaterThanOrEqual( + len_value, assembler->Float64Constant(kMaxSafeInteger)), + &return_two53minus1); + + // Round the {len} towards -Infinity. + Node* value = assembler->Float64Floor(len_value); + Node* result = assembler->ChangeFloat64ToTagged(value); + assembler->Return(result); + } + + assembler->Bind(&if_lenisnotheapnumber); + { + // Need to convert {len} to a Number first. + Callable callable = CodeFactory::NonNumberToNumber(assembler->isolate()); + var_len.Bind(assembler->CallStub(callable, context, len)); + assembler->Goto(&loop); + } + + assembler->Bind(&return_len); + assembler->Return(var_len.value()); + + assembler->Bind(&return_two53minus1); + assembler->Return(assembler->NumberConstant(kMaxSafeInteger)); + + assembler->Bind(&return_zero); + assembler->Return(assembler->SmiConstant(Smi::FromInt(0))); + } +} + +void ToBooleanStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + typedef compiler::Node Node; + typedef compiler::CodeStubAssembler::Label Label; + + Node* value = assembler->Parameter(0); + Label if_valueissmi(assembler), if_valueisnotsmi(assembler); + + // Check if {value} is a Smi or a HeapObject. + assembler->Branch(assembler->WordIsSmi(value), &if_valueissmi, + &if_valueisnotsmi); + + assembler->Bind(&if_valueissmi); + { + // The {value} is a Smi, only need to check against zero. + Label if_valueiszero(assembler), if_valueisnotzero(assembler); + assembler->Branch(assembler->SmiEqual(value, assembler->SmiConstant(0)), + &if_valueiszero, &if_valueisnotzero); + + assembler->Bind(&if_valueiszero); + assembler->Return(assembler->BooleanConstant(false)); + + assembler->Bind(&if_valueisnotzero); + assembler->Return(assembler->BooleanConstant(true)); + } + + assembler->Bind(&if_valueisnotsmi); + { + Label if_valueisstring(assembler), if_valueisheapnumber(assembler), + if_valueisoddball(assembler), if_valueisother(assembler); + + // The {value} is a HeapObject, load its map. + Node* value_map = assembler->LoadMap(value); + + // Load the {value}s instance type. + Node* value_instance_type = assembler->Load( + MachineType::Uint8(), value_map, + assembler->IntPtrConstant(Map::kInstanceTypeOffset - kHeapObjectTag)); + + // Dispatch based on the instance type; we distinguish all String instance + // types, the HeapNumber type and the Oddball type. + size_t const kNumCases = FIRST_NONSTRING_TYPE + 2; + Label* case_labels[kNumCases]; + int32_t case_values[kNumCases]; + for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) { + case_labels[i] = new Label(assembler); + case_values[i] = i; + } + case_labels[FIRST_NONSTRING_TYPE + 0] = &if_valueisheapnumber; + case_values[FIRST_NONSTRING_TYPE + 0] = HEAP_NUMBER_TYPE; + case_labels[FIRST_NONSTRING_TYPE + 1] = &if_valueisoddball; + case_values[FIRST_NONSTRING_TYPE + 1] = ODDBALL_TYPE; + assembler->Switch(value_instance_type, &if_valueisother, case_values, + case_labels, arraysize(case_values)); + for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) { + assembler->Bind(case_labels[i]); + assembler->Goto(&if_valueisstring); + delete case_labels[i]; + } + + assembler->Bind(&if_valueisstring); + { + // Load the string length field of the {value}. + Node* value_length = + assembler->LoadObjectField(value, String::kLengthOffset); + + // Check if the {value} is the empty string. + Label if_valueisempty(assembler), if_valueisnotempty(assembler); + assembler->Branch( + assembler->SmiEqual(value_length, assembler->SmiConstant(0)), + &if_valueisempty, &if_valueisnotempty); + + assembler->Bind(&if_valueisempty); + assembler->Return(assembler->BooleanConstant(false)); + + assembler->Bind(&if_valueisnotempty); + assembler->Return(assembler->BooleanConstant(true)); + } + + assembler->Bind(&if_valueisheapnumber); + { + Node* value_value = assembler->Load( + MachineType::Float64(), value, + assembler->IntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag)); + + Label if_valueispositive(assembler), if_valueisnotpositive(assembler), + if_valueisnegative(assembler), if_valueisnanorzero(assembler); + assembler->Branch(assembler->Float64LessThan( + assembler->Float64Constant(0.0), value_value), + &if_valueispositive, &if_valueisnotpositive); + + assembler->Bind(&if_valueispositive); + assembler->Return(assembler->BooleanConstant(true)); + + assembler->Bind(&if_valueisnotpositive); + assembler->Branch(assembler->Float64LessThan( + value_value, assembler->Float64Constant(0.0)), + &if_valueisnegative, &if_valueisnanorzero); + + assembler->Bind(&if_valueisnegative); + assembler->Return(assembler->BooleanConstant(true)); + + assembler->Bind(&if_valueisnanorzero); + assembler->Return(assembler->BooleanConstant(false)); + } + + assembler->Bind(&if_valueisoddball); + { + // The {value} is an Oddball, and every Oddball knows its boolean value. + Node* value_toboolean = + assembler->LoadObjectField(value, Oddball::kToBooleanOffset); + assembler->Return(value_toboolean); + } + + assembler->Bind(&if_valueisother); + { + Node* value_map_bitfield = assembler->Load( + MachineType::Uint8(), value_map, + assembler->IntPtrConstant(Map::kBitFieldOffset - kHeapObjectTag)); + Node* value_map_undetectable = assembler->Word32And( + value_map_bitfield, + assembler->Int32Constant(1 << Map::kIsUndetectable)); + + // Check if the {value} is undetectable. + Label if_valueisundetectable(assembler), + if_valueisnotundetectable(assembler); + assembler->Branch(assembler->Word32Equal(value_map_undetectable, + assembler->Int32Constant(0)), + &if_valueisnotundetectable, &if_valueisundetectable); + + assembler->Bind(&if_valueisundetectable); + assembler->Return(assembler->BooleanConstant(false)); + + assembler->Bind(&if_valueisnotundetectable); + assembler->Return(assembler->BooleanConstant(true)); + } + } +} + +void ToIntegerStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + typedef compiler::CodeStubAssembler::Label Label; + typedef compiler::Node Node; + typedef compiler::CodeStubAssembler::Variable Variable; + + Node* context = assembler->Parameter(1); + + // We might need to loop once for ToNumber conversion. + Variable var_arg(assembler, MachineRepresentation::kTagged); + Label loop(assembler, &var_arg); + var_arg.Bind(assembler->Parameter(0)); + assembler->Goto(&loop); + assembler->Bind(&loop); + { + // Shared entry points. + Label return_arg(assembler), return_zero(assembler, Label::kDeferred); + + // Load the current {arg} value. + Node* arg = var_arg.value(); + + // Check if {arg} is a Smi. + assembler->GotoIf(assembler->WordIsSmi(arg), &return_arg); + + // Check if {arg} is a HeapNumber. + Label if_argisheapnumber(assembler), + if_argisnotheapnumber(assembler, Label::kDeferred); + assembler->Branch(assembler->WordEqual(assembler->LoadMap(arg), + assembler->HeapNumberMapConstant()), + &if_argisheapnumber, &if_argisnotheapnumber); + + assembler->Bind(&if_argisheapnumber); + { + // Load the floating-point value of {arg}. + Node* arg_value = assembler->LoadHeapNumberValue(arg); + + // Check if {arg} is NaN. + assembler->GotoUnless(assembler->Float64Equal(arg_value, arg_value), + &return_zero); + + // Truncate {arg} towards zero. + Node* value = assembler->Float64Trunc(arg_value); + var_arg.Bind(assembler->ChangeFloat64ToTagged(value)); + assembler->Goto(&return_arg); + } + + assembler->Bind(&if_argisnotheapnumber); + { + // Need to convert {arg} to a Number first. + Callable callable = CodeFactory::NonNumberToNumber(assembler->isolate()); + var_arg.Bind(assembler->CallStub(callable, context, arg)); + assembler->Goto(&loop); + } + + assembler->Bind(&return_arg); + assembler->Return(var_arg.value()); + + assembler->Bind(&return_zero); + assembler->Return(assembler->SmiConstant(Smi::FromInt(0))); + } +} + +void StoreInterceptorStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + typedef compiler::Node Node; + Node* receiver = assembler->Parameter(0); + Node* name = assembler->Parameter(1); + Node* value = assembler->Parameter(2); + Node* context = assembler->Parameter(3); + assembler->TailCallRuntime(Runtime::kStorePropertyWithInterceptor, context, + receiver, name, value); +} + +void LoadIndexedInterceptorStub::GenerateAssembly( + compiler::CodeStubAssembler* assembler) const { + typedef compiler::Node Node; + typedef compiler::CodeStubAssembler::Label Label; + Node* receiver = assembler->Parameter(0); + Node* key = assembler->Parameter(1); + Node* slot = assembler->Parameter(2); + Node* vector = assembler->Parameter(3); + Node* context = assembler->Parameter(4); + + Label if_keyispositivesmi(assembler), if_keyisinvalid(assembler); + assembler->Branch(assembler->WordIsPositiveSmi(key), &if_keyispositivesmi, + &if_keyisinvalid); + assembler->Bind(&if_keyispositivesmi); + assembler->TailCallRuntime(Runtime::kLoadElementWithInterceptor, context, + receiver, key); + + assembler->Bind(&if_keyisinvalid); + assembler->TailCallRuntime(Runtime::kKeyedLoadIC_Miss, context, receiver, key, + slot, vector); +} template<class StateType> void HydrogenCodeStub::TraceTransition(StateType from, StateType to) { @@ -508,17 +3091,6 @@ void HydrogenCodeStub::TraceTransition(StateType from, StateType to) { } -void CompareNilICStub::PrintBaseName(std::ostream& os) const { // NOLINT - CodeStub::PrintBaseName(os); - os << ((nil_value() == kNullValue) ? "(NullValue)" : "(UndefinedValue)"); -} - - -void CompareNilICStub::PrintState(std::ostream& os) const { // NOLINT - os << state(); -} - - // TODO(svenpanne) Make this a real infix_ostream_iterator. class SimpleListPrinter { public: @@ -539,45 +3111,6 @@ class SimpleListPrinter { }; -std::ostream& operator<<(std::ostream& os, const CompareNilICStub::State& s) { - os << "("; - SimpleListPrinter p(os); - if (s.IsEmpty()) p.Add("None"); - if (s.Contains(CompareNilICStub::UNDEFINED)) p.Add("Undefined"); - if (s.Contains(CompareNilICStub::NULL_TYPE)) p.Add("Null"); - if (s.Contains(CompareNilICStub::MONOMORPHIC_MAP)) p.Add("MonomorphicMap"); - if (s.Contains(CompareNilICStub::GENERIC)) p.Add("Generic"); - return os << ")"; -} - - -Type* CompareNilICStub::GetType(Zone* zone, Handle<Map> map) { - State state = this->state(); - if (state.Contains(CompareNilICStub::GENERIC)) return Type::Any(); - - Type* result = Type::None(); - if (state.Contains(CompareNilICStub::UNDEFINED)) { - result = Type::Union(result, Type::Undefined(), zone); - } - if (state.Contains(CompareNilICStub::NULL_TYPE)) { - result = Type::Union(result, Type::Null(), zone); - } - if (state.Contains(CompareNilICStub::MONOMORPHIC_MAP)) { - Type* type = map.is_null() ? Type::Detectable() : Type::Class(map, zone); - result = Type::Union(result, type, zone); - } - - return result; -} - - -Type* CompareNilICStub::GetInputType(Zone* zone, Handle<Map> map) { - Type* output_type = GetType(zone, map); - Type* nil_type = nil_value() == kNullValue ? Type::Null() : Type::Undefined(); - return Type::Union(output_type, nil_type, zone); -} - - void CallICStub::PrintState(std::ostream& os) const { // NOLINT os << state(); } @@ -671,7 +3204,6 @@ void TypeofStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {} void NumberToStringStub::InitializeDescriptor(CodeStubDescriptor* descriptor) { - NumberToStringDescriptor call_descriptor(isolate()); descriptor->Initialize( Runtime::FunctionForId(Runtime::kNumberToString)->entry); } @@ -732,21 +3264,21 @@ void AllocateMutableHeapNumberStub::InitializeDescriptor( descriptor->Initialize(); } +#define SIMD128_INIT_DESC(TYPE, Type, type, lane_count, lane_type) \ + void Allocate##Type##Stub::InitializeDescriptor( \ + CodeStubDescriptor* descriptor) { \ + descriptor->Initialize( \ + Runtime::FunctionForId(Runtime::kCreate##Type)->entry); \ + } +SIMD128_TYPES(SIMD128_INIT_DESC) +#undef SIMD128_INIT_DESC void AllocateInNewSpaceStub::InitializeDescriptor( CodeStubDescriptor* descriptor) { descriptor->Initialize(); } - -void CompareNilICStub::InitializeDescriptor(CodeStubDescriptor* descriptor) { - descriptor->Initialize(FUNCTION_ADDR(Runtime_CompareNilIC_Miss)); - descriptor->SetMissHandler(ExternalReference( - Runtime::FunctionForId(Runtime::kCompareNilIC_Miss), isolate())); -} - - -void ToBooleanStub::InitializeDescriptor(CodeStubDescriptor* descriptor) { +void ToBooleanICStub::InitializeDescriptor(CodeStubDescriptor* descriptor) { descriptor->Initialize(FUNCTION_ADDR(Runtime_ToBooleanIC_Miss)); descriptor->SetMissHandler(ExternalReference( Runtime::FunctionForId(Runtime::kToBooleanIC_Miss), isolate())); @@ -848,8 +3380,7 @@ std::ostream& ArrayConstructorStubBase::BasePrintName( return os; } - -bool ToBooleanStub::UpdateStatus(Handle<Object> object) { +bool ToBooleanICStub::UpdateStatus(Handle<Object> object) { Types new_types = types(); Types old_types = new_types; bool to_boolean_value = new_types.UpdateStatus(object); @@ -858,30 +3389,27 @@ bool ToBooleanStub::UpdateStatus(Handle<Object> object) { return to_boolean_value; } - -void ToBooleanStub::PrintState(std::ostream& os) const { // NOLINT +void ToBooleanICStub::PrintState(std::ostream& os) const { // NOLINT os << types(); } - -std::ostream& operator<<(std::ostream& os, const ToBooleanStub::Types& s) { +std::ostream& operator<<(std::ostream& os, const ToBooleanICStub::Types& s) { os << "("; SimpleListPrinter p(os); if (s.IsEmpty()) p.Add("None"); - if (s.Contains(ToBooleanStub::UNDEFINED)) p.Add("Undefined"); - if (s.Contains(ToBooleanStub::BOOLEAN)) p.Add("Bool"); - if (s.Contains(ToBooleanStub::NULL_TYPE)) p.Add("Null"); - if (s.Contains(ToBooleanStub::SMI)) p.Add("Smi"); - if (s.Contains(ToBooleanStub::SPEC_OBJECT)) p.Add("SpecObject"); - if (s.Contains(ToBooleanStub::STRING)) p.Add("String"); - if (s.Contains(ToBooleanStub::SYMBOL)) p.Add("Symbol"); - if (s.Contains(ToBooleanStub::HEAP_NUMBER)) p.Add("HeapNumber"); - if (s.Contains(ToBooleanStub::SIMD_VALUE)) p.Add("SimdValue"); + if (s.Contains(ToBooleanICStub::UNDEFINED)) p.Add("Undefined"); + if (s.Contains(ToBooleanICStub::BOOLEAN)) p.Add("Bool"); + if (s.Contains(ToBooleanICStub::NULL_TYPE)) p.Add("Null"); + if (s.Contains(ToBooleanICStub::SMI)) p.Add("Smi"); + if (s.Contains(ToBooleanICStub::SPEC_OBJECT)) p.Add("SpecObject"); + if (s.Contains(ToBooleanICStub::STRING)) p.Add("String"); + if (s.Contains(ToBooleanICStub::SYMBOL)) p.Add("Symbol"); + if (s.Contains(ToBooleanICStub::HEAP_NUMBER)) p.Add("HeapNumber"); + if (s.Contains(ToBooleanICStub::SIMD_VALUE)) p.Add("SimdValue"); return os << ")"; } - -bool ToBooleanStub::Types::UpdateStatus(Handle<Object> object) { +bool ToBooleanICStub::Types::UpdateStatus(Handle<Object> object) { if (object->IsUndefined()) { Add(UNDEFINED); return false; @@ -896,16 +3424,16 @@ bool ToBooleanStub::Types::UpdateStatus(Handle<Object> object) { return Smi::cast(*object)->value() != 0; } else if (object->IsJSReceiver()) { Add(SPEC_OBJECT); - return !object->IsUndetectableObject(); + return !object->IsUndetectable(); } else if (object->IsString()) { - DCHECK(!object->IsUndetectableObject()); + DCHECK(!object->IsUndetectable()); Add(STRING); return String::cast(*object)->length() != 0; } else if (object->IsSymbol()) { Add(SYMBOL); return true; } else if (object->IsHeapNumber()) { - DCHECK(!object->IsUndetectableObject()); + DCHECK(!object->IsUndetectable()); Add(HEAP_NUMBER); double value = HeapNumber::cast(*object)->value(); return value != 0 && !std::isnan(value); @@ -919,12 +3447,12 @@ bool ToBooleanStub::Types::UpdateStatus(Handle<Object> object) { } } - -bool ToBooleanStub::Types::NeedsMap() const { - return Contains(ToBooleanStub::SPEC_OBJECT) || - Contains(ToBooleanStub::STRING) || Contains(ToBooleanStub::SYMBOL) || - Contains(ToBooleanStub::HEAP_NUMBER) || - Contains(ToBooleanStub::SIMD_VALUE); +bool ToBooleanICStub::Types::NeedsMap() const { + return Contains(ToBooleanICStub::SPEC_OBJECT) || + Contains(ToBooleanICStub::STRING) || + Contains(ToBooleanICStub::SYMBOL) || + Contains(ToBooleanICStub::HEAP_NUMBER) || + Contains(ToBooleanICStub::SIMD_VALUE); } |