// 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.h" // For FAA::LoadInternalField impl. using v8::internal::CodeStubAssembler; using v8::internal::compiler::Node; namespace v8 { namespace internal { FastAccessorAssembler::FastAccessorAssembler(Isolate* isolate) : zone_(isolate->allocator(), ZONE_NAME), isolate_(isolate), assembler_(new CodeStubAssembler(isolate, zone(), 1, Code::ComputeFlags(Code::STUB), "FastAccessorAssembler")), 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, int field_no) { CHECK_EQ(kBuilding, state_); CodeStubAssembler::Variable result(assembler_.get(), MachineRepresentation::kTagged); LabelId is_not_jsobject = MakeLabel(); CodeStubAssembler::Label merge(assembler_.get(), &result); CheckIsJSObjectOrJump(value, is_not_jsobject); Node* internal_field = assembler_->LoadObjectField( FromId(value), JSObject::kHeaderSize + kPointerSize * field_no, MachineType::Pointer()); 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, 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, 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), JSObject::kHeaderSize + kPointerSize * field_no, MachineType::Pointer()); return FromRaw(result); } FastAccessorAssembler::ValueId FastAccessorAssembler::LoadValue(ValueId value, int offset) { CHECK_EQ(kBuilding, state_); return FromRaw(assembler_->LoadBufferObject(FromId(value), offset, MachineType::IntPtr())); } FastAccessorAssembler::ValueId FastAccessorAssembler::LoadObject(ValueId value, int offset) { CHECK_EQ(kBuilding, state_); return FromRaw(assembler_->LoadBufferObject( assembler_->LoadBufferObject(FromId(value), offset, MachineType::Pointer()), 0, MachineType::AnyTagged())); } FastAccessorAssembler::ValueId FastAccessorAssembler::ToSmi(ValueId value) { CHECK_EQ(kBuilding, state_); return FromRaw(assembler_->SmiTag(FromId(value))); } void FastAccessorAssembler::ReturnValue(ValueId value) { CHECK_EQ(kBuilding, state_); assembler_->Return(FromId(value)); } void FastAccessorAssembler::CheckFlagSetOrReturnNull(ValueId value, int mask) { CHECK_EQ(kBuilding, state_); CodeStubAssembler::Label pass(assembler_.get()); CodeStubAssembler::Label fail(assembler_.get()); assembler_->Branch( assembler_->Word32Equal( assembler_->Word32And(FromId(value), assembler_->Int32Constant(mask)), assembler_->Int32Constant(0)), &fail, &pass); assembler_->Bind(&fail); assembler_->Return(assembler_->NullConstant()); assembler_->Bind(&pass); } void FastAccessorAssembler::CheckNotZeroOrReturnNull(ValueId value) { CHECK_EQ(kBuilding, state_); CodeStubAssembler::Label is_null(assembler_.get()); CodeStubAssembler::Label not_null(assembler_.get()); assembler_->Branch( assembler_->WordEqual(FromId(value), assembler_->IntPtrConstant(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 CodeStubAssembler::Label(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_); CodeStubAssembler::Label pass(assembler_.get()); assembler_->Branch( assembler_->WordEqual(FromId(value_id), assembler_->IntPtrConstant(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()); // TODO(vogelheim): There is currently no clean way to retrieve the context // parameter for a stub and the implementation details are hidden in // compiler/*. The context_paramter is computed as: // Linkage::GetJSCallContextParamIndex(descriptor->JSParameterCount()) const int kContextParameter = 3; Node* context = assembler_->Parameter(kContextParameter); Node* target = assembler_->HeapConstant(stub.GetCode()); int param_count = descriptor.GetParameterCount(); Node** args = zone()->NewArray<Node*>(param_count + 1 + kJSParameterCount); // Stub/register parameters: args[0] = assembler_->UndefinedConstant(); // callee (there's no JSFunction) args[1] = assembler_->UndefinedConstant(); // call_data (undefined) args[2] = assembler_->Parameter(0); // receiver (same as holder in this case) args[3] = assembler_->ExternalConstant(callback); // API callback function // JS arguments, on stack: args[4] = FromId(arg); // Context. args[5] = context; Node* call = assembler_->CallStubN(descriptor, kJSParameterCount, target, args); return FromRaw(call); } void FastAccessorAssembler::CheckIsJSObjectOrJump(ValueId value_id, LabelId label_id) { CHECK_EQ(kBuilding, state_); // Determine the 'value' object's instance type. Node* object_map = assembler_->LoadObjectField( FromId(value_id), Internals::kHeapObjectMapOffset, MachineType::Pointer()); Node* instance_type = assembler_->WordAnd( assembler_->LoadObjectField(object_map, Internals::kMapInstanceTypeAndBitFieldOffset, MachineType::Uint16()), assembler_->IntPtrConstant(0xff)); CodeStubAssembler::Label is_jsobject(assembler_.get()); // Check whether we have a proper JSObject. assembler_->GotoIf( assembler_->WordEqual( instance_type, assembler_->IntPtrConstant(Internals::kJSObjectType)), &is_jsobject); // JSApiObject?. assembler_->GotoUnless( assembler_->WordEqual(instance_type, assembler_->IntPtrConstant( Internals::kJSApiObjectType)), FromId(label_id)); // Continue. assembler_->Goto(&is_jsobject); assembler_->Bind(&is_jsobject); } MaybeHandle<Code> FastAccessorAssembler::Build() { CHECK_EQ(kBuilding, state_); Handle<Code> code = assembler_->GenerateCode(); state_ = !code.is_null() ? kBuilt : kError; Clear(); return code; } FastAccessorAssembler::ValueId FastAccessorAssembler::FromRaw(Node* node) { nodes_.push_back(node); ValueId value = {nodes_.size() - 1}; return value; } FastAccessorAssembler::LabelId FastAccessorAssembler::FromRaw( CodeStubAssembler::Label* 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); } CodeStubAssembler::Label* 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