// Copyright 2015 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/fast-accessor-assembler.h" #include "src/base/logging.h" #include "src/code-stub-assembler.h" #include "src/code-stubs.h" // For CallApiCallbackStub. #include "src/handles-inl.h" #include "src/objects-inl.h" #include "src/objects.h" // For FAA::LoadInternalField impl. namespace v8 { namespace internal { using compiler::Node; using compiler::CodeAssemblerLabel; using compiler::CodeAssemblerVariable; FastAccessorAssembler::FastAccessorAssembler(Isolate* isolate) : zone_(isolate->allocator(), ZONE_NAME), isolate_(isolate), assembler_state_(new compiler::CodeAssemblerState( isolate, zone(), 1, Code::ComputeFlags(Code::STUB), "FastAccessorAssembler")), assembler_(new CodeStubAssembler(assembler_state_.get())), state_(kBuilding) {} FastAccessorAssembler::~FastAccessorAssembler() { Clear(); } FastAccessorAssembler::ValueId FastAccessorAssembler::IntegerConstant( int const_value) { CHECK_EQ(kBuilding, state_); return FromRaw(assembler_->NumberConstant(const_value)); } FastAccessorAssembler::ValueId FastAccessorAssembler::GetReceiver() { CHECK_EQ(kBuilding, state_); // For JS functions, the receiver is parameter 0. return FromRaw(assembler_->Parameter(0)); } FastAccessorAssembler::ValueId FastAccessorAssembler::LoadInternalField( ValueId value_id, int field_no) { CHECK_EQ(kBuilding, state_); CodeAssemblerVariable result(assembler_.get(), MachineRepresentation::kTagged); LabelId is_not_jsobject = MakeLabel(); CodeAssemblerLabel merge(assembler_.get(), &result); CheckIsJSObjectOrJump(value_id, is_not_jsobject); Node* internal_field = assembler_->LoadObjectField( FromId(value_id), JSObject::kHeaderSize + kPointerSize * field_no); result.Bind(internal_field); assembler_->Goto(&merge); // Return null, mimicking the C++ counterpart. SetLabel(is_not_jsobject); result.Bind(assembler_->NullConstant()); assembler_->Goto(&merge); // Return. assembler_->Bind(&merge); return FromRaw(result.value()); } FastAccessorAssembler::ValueId FastAccessorAssembler::LoadInternalFieldUnchecked(ValueId value_id, int field_no) { CHECK_EQ(kBuilding, state_); // Defensive debug checks. if (FLAG_debug_code) { LabelId is_jsobject = MakeLabel(); LabelId is_not_jsobject = MakeLabel(); CheckIsJSObjectOrJump(value_id, is_not_jsobject); assembler_->Goto(FromId(is_jsobject)); SetLabel(is_not_jsobject); assembler_->DebugBreak(); assembler_->Goto(FromId(is_jsobject)); SetLabel(is_jsobject); } Node* result = assembler_->LoadObjectField( FromId(value_id), JSObject::kHeaderSize + kPointerSize * field_no); return FromRaw(result); } FastAccessorAssembler::ValueId FastAccessorAssembler::LoadValue( ValueId value_id, int offset) { CHECK_EQ(kBuilding, state_); return FromRaw(assembler_->LoadBufferObject(FromId(value_id), offset, MachineType::IntPtr())); } FastAccessorAssembler::ValueId FastAccessorAssembler::LoadObject( ValueId value_id, int offset) { CHECK_EQ(kBuilding, state_); return FromRaw(assembler_->LoadBufferObject( assembler_->LoadBufferObject(FromId(value_id), offset), 0, MachineType::AnyTagged())); } FastAccessorAssembler::ValueId FastAccessorAssembler::ToSmi(ValueId value_id) { CHECK_EQ(kBuilding, state_); return FromRaw(assembler_->SmiTag(FromId(value_id))); } void FastAccessorAssembler::ReturnValue(ValueId value_id) { CHECK_EQ(kBuilding, state_); assembler_->Return(FromId(value_id)); } void FastAccessorAssembler::CheckFlagSetOrReturnNull(ValueId value_id, int mask) { CHECK_EQ(kBuilding, state_); CodeAssemblerLabel pass(assembler_.get()); CodeAssemblerLabel fail(assembler_.get()); Node* value = FromId(value_id); assembler_->Branch( assembler_->IsSetWord(assembler_->BitcastTaggedToWord(value), mask), &pass, &fail); assembler_->Bind(&fail); assembler_->Return(assembler_->NullConstant()); assembler_->Bind(&pass); } void FastAccessorAssembler::CheckNotZeroOrReturnNull(ValueId value_id) { CHECK_EQ(kBuilding, state_); CodeAssemblerLabel is_null(assembler_.get()); CodeAssemblerLabel not_null(assembler_.get()); assembler_->Branch( assembler_->WordEqual(FromId(value_id), assembler_->SmiConstant(0)), &is_null, ¬_null); assembler_->Bind(&is_null); assembler_->Return(assembler_->NullConstant()); assembler_->Bind(¬_null); } FastAccessorAssembler::LabelId FastAccessorAssembler::MakeLabel() { CHECK_EQ(kBuilding, state_); return FromRaw(new CodeAssemblerLabel(assembler_.get())); } void FastAccessorAssembler::SetLabel(LabelId label_id) { CHECK_EQ(kBuilding, state_); assembler_->Bind(FromId(label_id)); } void FastAccessorAssembler::Goto(LabelId label_id) { CHECK_EQ(kBuilding, state_); assembler_->Goto(FromId(label_id)); } void FastAccessorAssembler::CheckNotZeroOrJump(ValueId value_id, LabelId label_id) { CHECK_EQ(kBuilding, state_); CodeAssemblerLabel pass(assembler_.get()); assembler_->Branch( assembler_->WordEqual(FromId(value_id), assembler_->SmiConstant(0)), FromId(label_id), &pass); assembler_->Bind(&pass); } FastAccessorAssembler::ValueId FastAccessorAssembler::Call( FunctionCallback callback_function, ValueId arg) { CHECK_EQ(kBuilding, state_); // Wrap the FunctionCallback in an ExternalReference. ApiFunction callback_api_function(FUNCTION_ADDR(callback_function)); ExternalReference callback(&callback_api_function, ExternalReference::DIRECT_API_CALL, isolate()); // Create & call API callback via stub. const int kJSParameterCount = 1; CallApiCallbackStub stub(isolate(), kJSParameterCount, true, true); CallInterfaceDescriptor descriptor = stub.GetCallInterfaceDescriptor(); DCHECK_EQ(4, descriptor.GetParameterCount()); DCHECK_EQ(0, descriptor.GetStackParameterCount()); Node* context = assembler_->GetJSContextParameter(); Node* target = assembler_->HeapConstant(stub.GetCode()); Node* call = assembler_->CallStub( descriptor, target, context, assembler_->UndefinedConstant(), // callee (there's no JSFunction) assembler_->UndefinedConstant(), // call_data (undefined) assembler_->Parameter(0), // receiver (same as holder in this case) assembler_->ExternalConstant(callback), // API callback function FromId(arg)); // JS argument, on stack return FromRaw(call); } void FastAccessorAssembler::CheckIsJSObjectOrJump(ValueId value_id, LabelId label_id) { CHECK_EQ(kBuilding, state_); // Determine the 'value' object's instance type. Node* instance_type = assembler_->LoadInstanceType(FromId(value_id)); CodeAssemblerLabel is_jsobject(assembler_.get()); // Check whether we have a proper JSObject. assembler_->GotoIf( assembler_->Word32Equal( instance_type, assembler_->Int32Constant(Internals::kJSObjectType)), &is_jsobject); // JSApiObject?. assembler_->GotoIfNot( assembler_->Word32Equal(instance_type, assembler_->Int32Constant( Internals::kJSApiObjectType)), FromId(label_id)); // Continue. assembler_->Goto(&is_jsobject); assembler_->Bind(&is_jsobject); } MaybeHandle FastAccessorAssembler::Build() { CHECK_EQ(kBuilding, state_); Handle code = compiler::CodeAssembler::GenerateCode(assembler_state_.get()); state_ = !code.is_null() ? kBuilt : kError; Clear(); return code; } FastAccessorAssembler::ValueId FastAccessorAssembler::FromRaw(Node* node) { nodes_.push_back(node); ValueId value_id = {nodes_.size() - 1}; return value_id; } FastAccessorAssembler::LabelId FastAccessorAssembler::FromRaw( CodeAssemblerLabel* label) { labels_.push_back(label); LabelId label_id = {labels_.size() - 1}; return label_id; } Node* FastAccessorAssembler::FromId(ValueId value) const { CHECK_LT(value.value_id, nodes_.size()); CHECK_NOT_NULL(nodes_.at(value.value_id)); return nodes_.at(value.value_id); } CodeAssemblerLabel* FastAccessorAssembler::FromId(LabelId label) const { CHECK_LT(label.label_id, labels_.size()); CHECK_NOT_NULL(labels_.at(label.label_id)); return labels_.at(label.label_id); } void FastAccessorAssembler::Clear() { for (auto label : labels_) { delete label; } nodes_.clear(); labels_.clear(); } } // namespace internal } // namespace v8