// Copyright 2012 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/code-stubs.h" #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" #include "src/ic/handler-compiler.h" #include "src/ic/ic.h" #include "src/macro-assembler.h" #include "src/parsing/parser.h" #include "src/profiler/cpu-profiler.h" namespace v8 { namespace internal { RUNTIME_FUNCTION(UnexpectedStubMiss) { FATAL("Unexpected deopt of a stub"); return Smi::FromInt(0); } CodeStubDescriptor::CodeStubDescriptor(CodeStub* stub) : call_descriptor_(stub->GetCallInterfaceDescriptor()), stack_parameter_count_(no_reg), hint_stack_parameter_count_(-1), function_mode_(NOT_JS_FUNCTION_STUB_MODE), deoptimization_handler_(NULL), miss_handler_(), has_miss_handler_(false) { stub->InitializeDescriptor(this); } CodeStubDescriptor::CodeStubDescriptor(Isolate* isolate, uint32_t stub_key) : stack_parameter_count_(no_reg), hint_stack_parameter_count_(-1), function_mode_(NOT_JS_FUNCTION_STUB_MODE), deoptimization_handler_(NULL), miss_handler_(), has_miss_handler_(false) { CodeStub::InitializeDescriptor(isolate, stub_key, this); } void CodeStubDescriptor::Initialize(Address deoptimization_handler, int hint_stack_parameter_count, StubFunctionMode function_mode) { deoptimization_handler_ = deoptimization_handler; hint_stack_parameter_count_ = hint_stack_parameter_count; function_mode_ = function_mode; } void CodeStubDescriptor::Initialize(Register stack_parameter_count, Address deoptimization_handler, int hint_stack_parameter_count, StubFunctionMode function_mode) { Initialize(deoptimization_handler, hint_stack_parameter_count, function_mode); stack_parameter_count_ = stack_parameter_count; } bool CodeStub::FindCodeInCache(Code** code_out) { UnseededNumberDictionary* stubs = isolate()->heap()->code_stubs(); int index = stubs->FindEntry(GetKey()); if (index != UnseededNumberDictionary::kNotFound) { *code_out = Code::cast(stubs->ValueAt(index)); return true; } return false; } void CodeStub::RecordCodeGeneration(Handle<Code> code) { std::ostringstream os; os << *this; PROFILE(isolate(), 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 code->VerifyEmbeddedObjects(); #endif } Code::Kind CodeStub::GetCodeKind() const { return Code::STUB; } Code::Flags CodeStub::GetCodeFlags() const { return Code::ComputeFlags(GetCodeKind(), GetICState(), GetExtraICState(), GetStubType()); } Handle<Code> CodeStub::GetCodeCopy(const Code::FindAndReplacePattern& pattern) { Handle<Code> ic = GetCode(); ic = isolate()->factory()->CopyCode(ic); ic->FindAndReplace(pattern); RecordCodeGeneration(ic); return ic; } Handle<Code> PlatformCodeStub::GenerateCode() { Factory* factory = isolate()->factory(); // Generate the new code. MacroAssembler masm(isolate(), NULL, 256, CodeObjectRequired::kYes); { // Update the static counter each time a new code stub is generated. isolate()->counters()->code_stubs()->Increment(); // Generate the code for the stub. masm.set_generating_stub(true); // TODO(yangguo): remove this once we can serialize IC stubs. masm.enable_serializer(); NoCurrentFrameScope scope(&masm); Generate(&masm); } // Create the code object. CodeDesc desc; masm.GetCode(&desc); // Copy the generated code into a heap object. Code::Flags flags = Code::ComputeFlags( GetCodeKind(), GetICState(), GetExtraICState(), GetStubType()); Handle<Code> new_object = factory->NewCode( desc, flags, masm.CodeObject(), NeedsImmovableCode()); return new_object; } Handle<Code> CodeStub::GetCode() { Heap* heap = isolate()->heap(); Code* code; if (UseSpecialCache() ? FindCodeInSpecialCache(&code) : FindCodeInCache(&code)) { DCHECK(GetCodeKind() == code->kind()); return Handle<Code>(code); } { HandleScope scope(isolate()); Handle<Code> new_object = GenerateCode(); new_object->set_stub_key(GetKey()); FinishCode(new_object); RecordCodeGeneration(new_object); #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) { CodeTracer::Scope trace_scope(isolate()->GetCodeTracer()); OFStream os(trace_scope.file()); std::ostringstream name; name << *this; new_object->Disassemble(name.str().c_str(), os); os << "\n"; } #endif if (UseSpecialCache()) { AddToSpecialCache(new_object); } else { // Update the dictionary and the root in Heap. Handle<UnseededNumberDictionary> dict = UnseededNumberDictionary::AtNumberPut( Handle<UnseededNumberDictionary>(heap->code_stubs()), GetKey(), new_object); heap->SetRootCodeStubs(*dict); } code = *new_object; } Activate(code); DCHECK(!NeedsImmovableCode() || heap->lo_space()->Contains(code) || heap->code_space()->FirstPage()->Contains(code->address())); return Handle<Code>(code, isolate()); } const char* CodeStub::MajorName(CodeStub::Major major_key) { switch (major_key) { #define DEF_CASE(name) case name: return #name "Stub"; CODE_STUB_LIST(DEF_CASE) #undef DEF_CASE case NoCache: return "<NoCache>Stub"; case NUMBER_OF_IDS: UNREACHABLE(); return NULL; } return NULL; } void CodeStub::PrintBaseName(std::ostream& os) const { // NOLINT os << MajorName(MajorKey()); } void CodeStub::PrintName(std::ostream& os) const { // NOLINT PrintBaseName(os); PrintState(os); } void CodeStub::Dispatch(Isolate* isolate, uint32_t key, void** value_out, DispatchedCall call) { switch (MajorKeyFromKey(key)) { #define DEF_CASE(NAME) \ case NAME: { \ NAME##Stub stub(key, isolate); \ CodeStub* pstub = &stub; \ call(pstub, value_out); \ break; \ } CODE_STUB_LIST(DEF_CASE) #undef DEF_CASE case NUMBER_OF_IDS: case NoCache: UNREACHABLE(); break; } } static void InitializeDescriptorDispatchedCall(CodeStub* stub, void** value_out) { CodeStubDescriptor* descriptor_out = reinterpret_cast<CodeStubDescriptor*>(value_out); stub->InitializeDescriptor(descriptor_out); descriptor_out->set_call_descriptor(stub->GetCallInterfaceDescriptor()); } void CodeStub::InitializeDescriptor(Isolate* isolate, uint32_t key, CodeStubDescriptor* desc) { void** value_out = reinterpret_cast<void**>(desc); Dispatch(isolate, key, value_out, &InitializeDescriptorDispatchedCall); } void CodeStub::GetCodeDispatchCall(CodeStub* stub, void** value_out) { Handle<Code>* code_out = reinterpret_cast<Handle<Code>*>(value_out); // Code stubs with special cache cannot be recreated from stub key. *code_out = stub->UseSpecialCache() ? Handle<Code>() : stub->GetCode(); } MaybeHandle<Code> CodeStub::GetCode(Isolate* isolate, uint32_t key) { HandleScope scope(isolate); Handle<Code> code; void** value_out = reinterpret_cast<void**>(&code); Dispatch(isolate, key, value_out, &GetCodeDispatchCall); return scope.CloseAndEscape(code); } // static void BinaryOpICStub::GenerateAheadOfTime(Isolate* isolate) { // Generate the uninitialized versions of the stub. for (int op = Token::BIT_OR; op <= Token::MOD; ++op) { BinaryOpICStub stub(isolate, static_cast<Token::Value>(op)); stub.GetCode(); } // Generate special versions of the stub. BinaryOpICState::GenerateAheadOfTime(isolate, &GenerateAheadOfTime); } void BinaryOpICStub::PrintState(std::ostream& os) const { // NOLINT os << state(); } // static void BinaryOpICStub::GenerateAheadOfTime(Isolate* isolate, const BinaryOpICState& state) { BinaryOpICStub stub(isolate, state); stub.GetCode(); } // static void BinaryOpICWithAllocationSiteStub::GenerateAheadOfTime(Isolate* isolate) { // Generate special versions of the stub. BinaryOpICState::GenerateAheadOfTime(isolate, &GenerateAheadOfTime); } void BinaryOpICWithAllocationSiteStub::PrintState( std::ostream& os) const { // NOLINT os << state(); } // static void BinaryOpICWithAllocationSiteStub::GenerateAheadOfTime( Isolate* isolate, const BinaryOpICState& state) { if (state.CouldCreateAllocationMementos()) { BinaryOpICWithAllocationSiteStub stub(isolate, state); stub.GetCode(); } } std::ostream& operator<<(std::ostream& os, const StringAddFlags& flags) { switch (flags) { case STRING_ADD_CHECK_NONE: return os << "CheckNone"; case STRING_ADD_CHECK_LEFT: return os << "CheckLeft"; case STRING_ADD_CHECK_RIGHT: return os << "CheckRight"; case STRING_ADD_CHECK_BOTH: return os << "CheckBoth"; case STRING_ADD_CONVERT_LEFT: return os << "ConvertLeft"; case STRING_ADD_CONVERT_RIGHT: return os << "ConvertRight"; case STRING_ADD_CONVERT: break; } UNREACHABLE(); return os; } void StringAddStub::PrintBaseName(std::ostream& os) const { // NOLINT os << "StringAddStub_" << flags() << "_" << pretenure_flag(); } InlineCacheState CompareICStub::GetICState() const { CompareICState::State state = Max(left(), right()); switch (state) { case CompareICState::UNINITIALIZED: return ::v8::internal::UNINITIALIZED; case CompareICState::BOOLEAN: case CompareICState::SMI: case CompareICState::NUMBER: case CompareICState::INTERNALIZED_STRING: case CompareICState::STRING: case CompareICState::UNIQUE_NAME: case CompareICState::RECEIVER: case CompareICState::KNOWN_RECEIVER: return MONOMORPHIC; case CompareICState::GENERIC: return ::v8::internal::GENERIC; } UNREACHABLE(); return ::v8::internal::UNINITIALIZED; } Condition CompareICStub::GetCondition() const { return CompareIC::ComputeCondition(op()); } void CompareICStub::AddToSpecialCache(Handle<Code> new_object) { DCHECK(*known_map_ != NULL); Isolate* isolate = new_object->GetIsolate(); Factory* factory = isolate->factory(); return Map::UpdateCodeCache(known_map_, strict() ? factory->strict_compare_ic_string() : factory->compare_ic_string(), new_object); } bool CompareICStub::FindCodeInSpecialCache(Code** code_out) { Factory* factory = isolate()->factory(); Code::Flags flags = Code::ComputeFlags( GetCodeKind(), UNINITIALIZED); Handle<Object> probe( known_map_->FindInCodeCache( strict() ? *factory->strict_compare_ic_string() : *factory->compare_ic_string(), flags), isolate()); if (probe->IsCode()) { *code_out = Code::cast(*probe); #ifdef DEBUG CompareICStub decode((*code_out)->stub_key(), isolate()); DCHECK(op() == decode.op()); DCHECK(left() == decode.left()); DCHECK(right() == decode.right()); DCHECK(state() == decode.state()); #endif return true; } return false; } void CompareICStub::Generate(MacroAssembler* masm) { switch (state()) { case CompareICState::UNINITIALIZED: GenerateMiss(masm); break; case CompareICState::BOOLEAN: GenerateBooleans(masm); break; case CompareICState::SMI: GenerateSmis(masm); break; case CompareICState::NUMBER: GenerateNumbers(masm); break; case CompareICState::STRING: GenerateStrings(masm); break; case CompareICState::INTERNALIZED_STRING: GenerateInternalizedStrings(masm); break; case CompareICState::UNIQUE_NAME: GenerateUniqueNames(masm); break; case CompareICState::RECEIVER: GenerateReceivers(masm); break; case CompareICState::KNOWN_RECEIVER: DCHECK(*known_map_ != NULL); GenerateKnownReceivers(masm); break; case CompareICState::GENERIC: GenerateGeneric(masm); break; } } Handle<Code> TurboFanCodeStub::GenerateCode() { const char* name = CodeStub::MajorName(MajorKey()); Zone zone; CallInterfaceDescriptor descriptor(GetCallInterfaceDescriptor()); compiler::CodeStubAssembler assembler(isolate(), &zone, descriptor, GetCodeFlags(), name); GenerateAssembly(&assembler); return assembler.GenerateCode(); } void AllocateHeapNumberStub::GenerateAssembly( compiler::CodeStubAssembler* assembler) const { compiler::Node* result = assembler->Allocate( HeapNumber::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()->heap_number_map())); assembler->Return(result); } void AllocateMutableHeapNumberStub::GenerateAssembly( compiler::CodeStubAssembler* assembler) const { compiler::Node* result = assembler->Allocate( HeapNumber::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()->mutable_heap_number_map())); assembler->Return(result); } void StringLengthStub::GenerateAssembly( compiler::CodeStubAssembler* assembler) const { compiler::Node* value = assembler->Parameter(0); compiler::Node* string = assembler->LoadObjectField(value, JSValue::kValueOffset); compiler::Node* result = assembler->LoadObjectField(string, String::kLengthOffset); 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::ToNumber(assembler->isolate()); var_rhs.Bind(assembler->CallStub(callable, context, rhs)); assembler->Goto(&loop); } } } assembler->Bind(&if_lhsisnotsmi); { // Load the HeapNumber map for later comparisons. Node* number_map = assembler->HeapNumberMapConstant(); // Load the map of {lhs}. Node* lhs_map = assembler->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::ToNumber(assembler->isolate()); var_lhs.Bind(assembler->CallStub(callable, context, lhs)); assembler->Goto(&loop); } } assembler->Bind(&if_rhsisnotsmi); { // Load the map of {rhs}. Node* rhs_map = assembler->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::ToNumber(assembler->isolate()); var_rhs.Bind(assembler->CallStub(callable, context, rhs)); assembler->Goto(&loop); } } assembler->Bind(&if_lhsisnotnumber); { // Load the instance type of {lhs}. Node* lhs_instance_type = assembler->LoadMapInstanceType(lhs_map); // Check if {lhs} is a String. Label if_lhsisstring(assembler), if_lhsisnotstring(assembler, Label::kDeferred); assembler->Branch(assembler->Int32LessThan( lhs_instance_type, assembler->Int32Constant(FIRST_NONSTRING_TYPE)), &if_lhsisstring, &if_lhsisnotstring); assembler->Bind(&if_lhsisstring); { // Load the instance type of {rhs}. Node* rhs_instance_type = assembler->LoadMapInstanceType(rhs_map); // Check if {rhs} is also a String. Label if_rhsisstring(assembler), if_rhsisnotstring(assembler, Label::kDeferred); assembler->Branch(assembler->Int32LessThan( rhs_instance_type, assembler->Int32Constant( FIRST_NONSTRING_TYPE)), &if_rhsisstring, &if_rhsisnotstring); assembler->Bind(&if_rhsisstring); { // Both {lhs} and {rhs} are strings. 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::ToNumber(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::ToNumber(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->BranchIfInt32LessThan(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 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)); } } } template<class StateType> void HydrogenCodeStub::TraceTransition(StateType from, StateType to) { // Note: Although a no-op transition is semantically OK, it is hinting at a // bug somewhere in our state transition machinery. DCHECK(from != to); if (!FLAG_trace_ic) return; OFStream os(stdout); os << "["; PrintBaseName(os); os << ": " << from << "=>" << to << "]" << std::endl; } // TODO(svenpanne) Make this a real infix_ostream_iterator. class SimpleListPrinter { public: explicit SimpleListPrinter(std::ostream& os) : os_(os), first_(true) {} void Add(const char* s) { if (first_) { first_ = false; } else { os_ << ","; } os_ << s; } private: std::ostream& os_; bool first_; }; void CallICStub::PrintState(std::ostream& os) const { // NOLINT os << state(); } void JSEntryStub::FinishCode(Handle<Code> code) { Handle<FixedArray> handler_table = code->GetIsolate()->factory()->NewFixedArray(1, TENURED); handler_table->set(0, Smi::FromInt(handler_offset_)); code->set_handler_table(*handler_table); } void LoadDictionaryElementStub::InitializeDescriptor( CodeStubDescriptor* descriptor) { descriptor->Initialize( FUNCTION_ADDR(Runtime_KeyedLoadIC_MissFromStubFailure)); } void KeyedLoadGenericStub::InitializeDescriptor( CodeStubDescriptor* descriptor) { descriptor->Initialize( Runtime::FunctionForId(Runtime::kKeyedGetProperty)->entry); } void HandlerStub::InitializeDescriptor(CodeStubDescriptor* descriptor) { if (kind() == Code::STORE_IC) { descriptor->Initialize(FUNCTION_ADDR(Runtime_StoreIC_MissFromStubFailure)); } else if (kind() == Code::KEYED_LOAD_IC) { descriptor->Initialize( FUNCTION_ADDR(Runtime_KeyedLoadIC_MissFromStubFailure)); } else if (kind() == Code::KEYED_STORE_IC) { descriptor->Initialize( FUNCTION_ADDR(Runtime_KeyedStoreIC_MissFromStubFailure)); } } CallInterfaceDescriptor HandlerStub::GetCallInterfaceDescriptor() const { if (kind() == Code::LOAD_IC || kind() == Code::KEYED_LOAD_IC) { return LoadWithVectorDescriptor(isolate()); } else { DCHECK(kind() == Code::STORE_IC || kind() == Code::KEYED_STORE_IC); return VectorStoreICDescriptor(isolate()); } } void StoreFastElementStub::InitializeDescriptor( CodeStubDescriptor* descriptor) { descriptor->Initialize( FUNCTION_ADDR(Runtime_KeyedStoreIC_MissFromStubFailure)); } void ElementsTransitionAndStoreStub::InitializeDescriptor( CodeStubDescriptor* descriptor) { descriptor->Initialize( FUNCTION_ADDR(Runtime_ElementsTransitionAndStoreIC_Miss)); } void ToObjectStub::InitializeDescriptor(CodeStubDescriptor* descriptor) { descriptor->Initialize(Runtime::FunctionForId(Runtime::kToObject)->entry); } CallInterfaceDescriptor StoreTransitionStub::GetCallInterfaceDescriptor() const { return VectorStoreTransitionDescriptor(isolate()); } CallInterfaceDescriptor ElementsTransitionAndStoreStub::GetCallInterfaceDescriptor() const { return VectorStoreTransitionDescriptor(isolate()); } void FastNewClosureStub::InitializeDescriptor(CodeStubDescriptor* descriptor) { descriptor->Initialize(Runtime::FunctionForId(Runtime::kNewClosure)->entry); } void FastNewContextStub::InitializeDescriptor(CodeStubDescriptor* d) {} void TypeofStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {} void NumberToStringStub::InitializeDescriptor(CodeStubDescriptor* descriptor) { NumberToStringDescriptor call_descriptor(isolate()); descriptor->Initialize( Runtime::FunctionForId(Runtime::kNumberToString)->entry); } void FastCloneRegExpStub::InitializeDescriptor(CodeStubDescriptor* descriptor) { FastCloneRegExpDescriptor call_descriptor(isolate()); descriptor->Initialize( Runtime::FunctionForId(Runtime::kCreateRegExpLiteral)->entry); } void FastCloneShallowArrayStub::InitializeDescriptor( CodeStubDescriptor* descriptor) { FastCloneShallowArrayDescriptor call_descriptor(isolate()); descriptor->Initialize( Runtime::FunctionForId(Runtime::kCreateArrayLiteralStubBailout)->entry); } void FastCloneShallowObjectStub::InitializeDescriptor( CodeStubDescriptor* descriptor) { FastCloneShallowObjectDescriptor call_descriptor(isolate()); descriptor->Initialize( Runtime::FunctionForId(Runtime::kCreateObjectLiteral)->entry); } void CreateAllocationSiteStub::InitializeDescriptor(CodeStubDescriptor* d) {} void CreateWeakCellStub::InitializeDescriptor(CodeStubDescriptor* d) {} void RegExpConstructResultStub::InitializeDescriptor( CodeStubDescriptor* descriptor) { descriptor->Initialize( Runtime::FunctionForId(Runtime::kRegExpConstructResult)->entry); } void TransitionElementsKindStub::InitializeDescriptor( CodeStubDescriptor* descriptor) { descriptor->Initialize( Runtime::FunctionForId(Runtime::kTransitionElementsKind)->entry); } void AllocateHeapNumberStub::InitializeDescriptor( CodeStubDescriptor* descriptor) { descriptor->Initialize( Runtime::FunctionForId(Runtime::kAllocateHeapNumber)->entry); } void AllocateMutableHeapNumberStub::InitializeDescriptor( CodeStubDescriptor* descriptor) { descriptor->Initialize(); } void AllocateInNewSpaceStub::InitializeDescriptor( CodeStubDescriptor* descriptor) { descriptor->Initialize(); } void ToBooleanICStub::InitializeDescriptor(CodeStubDescriptor* descriptor) { descriptor->Initialize(FUNCTION_ADDR(Runtime_ToBooleanIC_Miss)); descriptor->SetMissHandler(ExternalReference( Runtime::FunctionForId(Runtime::kToBooleanIC_Miss), isolate())); } void BinaryOpICStub::InitializeDescriptor(CodeStubDescriptor* descriptor) { descriptor->Initialize(FUNCTION_ADDR(Runtime_BinaryOpIC_Miss)); descriptor->SetMissHandler(ExternalReference( Runtime::FunctionForId(Runtime::kBinaryOpIC_Miss), isolate())); } void BinaryOpWithAllocationSiteStub::InitializeDescriptor( CodeStubDescriptor* descriptor) { descriptor->Initialize( FUNCTION_ADDR(Runtime_BinaryOpIC_MissWithAllocationSite)); } void StringAddStub::InitializeDescriptor(CodeStubDescriptor* descriptor) { descriptor->Initialize(Runtime::FunctionForId(Runtime::kStringAdd)->entry); } void GrowArrayElementsStub::InitializeDescriptor( CodeStubDescriptor* descriptor) { descriptor->Initialize( Runtime::FunctionForId(Runtime::kGrowArrayElements)->entry); } void TypeofStub::GenerateAheadOfTime(Isolate* isolate) { TypeofStub stub(isolate); stub.GetCode(); } void CreateAllocationSiteStub::GenerateAheadOfTime(Isolate* isolate) { CreateAllocationSiteStub stub(isolate); stub.GetCode(); } void CreateWeakCellStub::GenerateAheadOfTime(Isolate* isolate) { CreateWeakCellStub stub(isolate); stub.GetCode(); } void StoreElementStub::Generate(MacroAssembler* masm) { DCHECK_EQ(DICTIONARY_ELEMENTS, elements_kind()); ElementHandlerCompiler::GenerateStoreSlow(masm); } // static void StoreFastElementStub::GenerateAheadOfTime(Isolate* isolate) { StoreFastElementStub(isolate, false, FAST_HOLEY_ELEMENTS, STANDARD_STORE) .GetCode(); StoreFastElementStub(isolate, false, FAST_HOLEY_ELEMENTS, STORE_AND_GROW_NO_TRANSITION).GetCode(); for (int i = FIRST_FAST_ELEMENTS_KIND; i <= LAST_FAST_ELEMENTS_KIND; i++) { ElementsKind kind = static_cast<ElementsKind>(i); StoreFastElementStub(isolate, true, kind, STANDARD_STORE).GetCode(); StoreFastElementStub(isolate, true, kind, STORE_AND_GROW_NO_TRANSITION) .GetCode(); } } void ArrayConstructorStub::PrintName(std::ostream& os) const { // NOLINT os << "ArrayConstructorStub"; switch (argument_count()) { case ANY: os << "_Any"; break; case NONE: os << "_None"; break; case ONE: os << "_One"; break; case MORE_THAN_ONE: os << "_More_Than_One"; break; } return; } std::ostream& ArrayConstructorStubBase::BasePrintName( std::ostream& os, // NOLINT const char* name) const { os << name << "_" << ElementsKindToString(elements_kind()); if (override_mode() == DISABLE_ALLOCATION_SITES) { os << "_DISABLE_ALLOCATION_SITES"; } return os; } bool ToBooleanICStub::UpdateStatus(Handle<Object> object) { Types new_types = types(); Types old_types = new_types; bool to_boolean_value = new_types.UpdateStatus(object); TraceTransition(old_types, new_types); set_sub_minor_key(TypesBits::update(sub_minor_key(), new_types.ToIntegral())); return to_boolean_value; } void ToBooleanICStub::PrintState(std::ostream& os) const { // NOLINT os << types(); } std::ostream& operator<<(std::ostream& os, const ToBooleanICStub::Types& s) { os << "("; SimpleListPrinter p(os); if (s.IsEmpty()) p.Add("None"); 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 ToBooleanICStub::Types::UpdateStatus(Handle<Object> object) { if (object->IsUndefined()) { Add(UNDEFINED); return false; } else if (object->IsBoolean()) { Add(BOOLEAN); return object->IsTrue(); } else if (object->IsNull()) { Add(NULL_TYPE); return false; } else if (object->IsSmi()) { Add(SMI); return Smi::cast(*object)->value() != 0; } else if (object->IsJSReceiver()) { Add(SPEC_OBJECT); return !object->IsUndetectable(); } else if (object->IsString()) { 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->IsUndetectable()); Add(HEAP_NUMBER); double value = HeapNumber::cast(*object)->value(); return value != 0 && !std::isnan(value); } else if (object->IsSimd128Value()) { Add(SIMD_VALUE); return true; } else { // We should never see an internal object at runtime here! UNREACHABLE(); return true; } } bool ToBooleanICStub::Types::NeedsMap() const { return Contains(ToBooleanICStub::SPEC_OBJECT) || Contains(ToBooleanICStub::STRING) || Contains(ToBooleanICStub::SYMBOL) || Contains(ToBooleanICStub::HEAP_NUMBER) || Contains(ToBooleanICStub::SIMD_VALUE); } void StubFailureTrampolineStub::GenerateAheadOfTime(Isolate* isolate) { StubFailureTrampolineStub stub1(isolate, NOT_JS_FUNCTION_STUB_MODE); StubFailureTrampolineStub stub2(isolate, JS_FUNCTION_STUB_MODE); stub1.GetCode(); stub2.GetCode(); } void ProfileEntryHookStub::EntryHookTrampoline(intptr_t function, intptr_t stack_pointer, Isolate* isolate) { FunctionEntryHook entry_hook = isolate->function_entry_hook(); DCHECK(entry_hook != NULL); entry_hook(function, stack_pointer); } ArrayConstructorStub::ArrayConstructorStub(Isolate* isolate) : PlatformCodeStub(isolate) { minor_key_ = ArgumentCountBits::encode(ANY); ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate); } ArrayConstructorStub::ArrayConstructorStub(Isolate* isolate, int argument_count) : PlatformCodeStub(isolate) { if (argument_count == 0) { minor_key_ = ArgumentCountBits::encode(NONE); } else if (argument_count == 1) { minor_key_ = ArgumentCountBits::encode(ONE); } else if (argument_count >= 2) { minor_key_ = ArgumentCountBits::encode(MORE_THAN_ONE); } else { UNREACHABLE(); } ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate); } InternalArrayConstructorStub::InternalArrayConstructorStub( Isolate* isolate) : PlatformCodeStub(isolate) { InternalArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate); } Representation RepresentationFromType(Type* type) { if (type->Is(Type::UntaggedIntegral())) { return Representation::Integer32(); } if (type->Is(Type::TaggedSigned())) { return Representation::Smi(); } if (type->Is(Type::UntaggedPointer())) { return Representation::External(); } DCHECK(!type->Is(Type::Untagged())); return Representation::Tagged(); } } // namespace internal } // namespace v8