1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/fast-accessor-assembler.h"
6
7 #include "src/base/logging.h"
8 #include "src/code-stub-assembler.h"
9 #include "src/code-stubs.h" // For CallApiCallbackStub.
10 #include "src/handles-inl.h"
11 #include "src/objects.h" // For FAA::LoadInternalField impl.
12
13 using v8::internal::CodeStubAssembler;
14 using v8::internal::compiler::Node;
15
16 namespace v8 {
17 namespace internal {
18
FastAccessorAssembler(Isolate * isolate)19 FastAccessorAssembler::FastAccessorAssembler(Isolate* isolate)
20 : zone_(isolate->allocator(), ZONE_NAME),
21 isolate_(isolate),
22 assembler_(new CodeStubAssembler(isolate, zone(), 1,
23 Code::ComputeFlags(Code::STUB),
24 "FastAccessorAssembler")),
25 state_(kBuilding) {}
26
~FastAccessorAssembler()27 FastAccessorAssembler::~FastAccessorAssembler() { Clear(); }
28
IntegerConstant(int const_value)29 FastAccessorAssembler::ValueId FastAccessorAssembler::IntegerConstant(
30 int const_value) {
31 CHECK_EQ(kBuilding, state_);
32 return FromRaw(assembler_->NumberConstant(const_value));
33 }
34
GetReceiver()35 FastAccessorAssembler::ValueId FastAccessorAssembler::GetReceiver() {
36 CHECK_EQ(kBuilding, state_);
37
38 // For JS functions, the receiver is parameter 0.
39 return FromRaw(assembler_->Parameter(0));
40 }
41
LoadInternalField(ValueId value,int field_no)42 FastAccessorAssembler::ValueId FastAccessorAssembler::LoadInternalField(
43 ValueId value, int field_no) {
44 CHECK_EQ(kBuilding, state_);
45
46 CodeStubAssembler::Variable result(assembler_.get(),
47 MachineRepresentation::kTagged);
48 LabelId is_not_jsobject = MakeLabel();
49 CodeStubAssembler::Label merge(assembler_.get(), &result);
50
51 CheckIsJSObjectOrJump(value, is_not_jsobject);
52
53 Node* internal_field = assembler_->LoadObjectField(
54 FromId(value), JSObject::kHeaderSize + kPointerSize * field_no,
55 MachineType::Pointer());
56
57 result.Bind(internal_field);
58 assembler_->Goto(&merge);
59
60 // Return null, mimicking the C++ counterpart.
61 SetLabel(is_not_jsobject);
62 result.Bind(assembler_->NullConstant());
63 assembler_->Goto(&merge);
64
65 // Return.
66 assembler_->Bind(&merge);
67 return FromRaw(result.value());
68 }
69
70 FastAccessorAssembler::ValueId
LoadInternalFieldUnchecked(ValueId value,int field_no)71 FastAccessorAssembler::LoadInternalFieldUnchecked(ValueId value, int field_no) {
72 CHECK_EQ(kBuilding, state_);
73
74 // Defensive debug checks.
75 if (FLAG_debug_code) {
76 LabelId is_jsobject = MakeLabel();
77 LabelId is_not_jsobject = MakeLabel();
78 CheckIsJSObjectOrJump(value, is_not_jsobject);
79 assembler_->Goto(FromId(is_jsobject));
80
81 SetLabel(is_not_jsobject);
82 assembler_->DebugBreak();
83 assembler_->Goto(FromId(is_jsobject));
84
85 SetLabel(is_jsobject);
86 }
87
88 Node* result = assembler_->LoadObjectField(
89 FromId(value), JSObject::kHeaderSize + kPointerSize * field_no,
90 MachineType::Pointer());
91
92 return FromRaw(result);
93 }
94
LoadValue(ValueId value,int offset)95 FastAccessorAssembler::ValueId FastAccessorAssembler::LoadValue(ValueId value,
96 int offset) {
97 CHECK_EQ(kBuilding, state_);
98 return FromRaw(assembler_->LoadBufferObject(FromId(value), offset,
99 MachineType::IntPtr()));
100 }
101
LoadObject(ValueId value,int offset)102 FastAccessorAssembler::ValueId FastAccessorAssembler::LoadObject(ValueId value,
103 int offset) {
104 CHECK_EQ(kBuilding, state_);
105 return FromRaw(assembler_->LoadBufferObject(
106 assembler_->LoadBufferObject(FromId(value), offset,
107 MachineType::Pointer()),
108 0, MachineType::AnyTagged()));
109 }
110
ToSmi(ValueId value)111 FastAccessorAssembler::ValueId FastAccessorAssembler::ToSmi(ValueId value) {
112 CHECK_EQ(kBuilding, state_);
113 return FromRaw(assembler_->SmiTag(FromId(value)));
114 }
115
ReturnValue(ValueId value)116 void FastAccessorAssembler::ReturnValue(ValueId value) {
117 CHECK_EQ(kBuilding, state_);
118 assembler_->Return(FromId(value));
119 }
120
CheckFlagSetOrReturnNull(ValueId value,int mask)121 void FastAccessorAssembler::CheckFlagSetOrReturnNull(ValueId value, int mask) {
122 CHECK_EQ(kBuilding, state_);
123 CodeStubAssembler::Label pass(assembler_.get());
124 CodeStubAssembler::Label fail(assembler_.get());
125 assembler_->Branch(
126 assembler_->Word32Equal(
127 assembler_->Word32And(FromId(value), assembler_->Int32Constant(mask)),
128 assembler_->Int32Constant(0)),
129 &fail, &pass);
130 assembler_->Bind(&fail);
131 assembler_->Return(assembler_->NullConstant());
132 assembler_->Bind(&pass);
133 }
134
CheckNotZeroOrReturnNull(ValueId value)135 void FastAccessorAssembler::CheckNotZeroOrReturnNull(ValueId value) {
136 CHECK_EQ(kBuilding, state_);
137 CodeStubAssembler::Label is_null(assembler_.get());
138 CodeStubAssembler::Label not_null(assembler_.get());
139 assembler_->Branch(
140 assembler_->WordEqual(FromId(value), assembler_->IntPtrConstant(0)),
141 &is_null, ¬_null);
142 assembler_->Bind(&is_null);
143 assembler_->Return(assembler_->NullConstant());
144 assembler_->Bind(¬_null);
145 }
146
MakeLabel()147 FastAccessorAssembler::LabelId FastAccessorAssembler::MakeLabel() {
148 CHECK_EQ(kBuilding, state_);
149 return FromRaw(new CodeStubAssembler::Label(assembler_.get()));
150 }
151
SetLabel(LabelId label_id)152 void FastAccessorAssembler::SetLabel(LabelId label_id) {
153 CHECK_EQ(kBuilding, state_);
154 assembler_->Bind(FromId(label_id));
155 }
156
Goto(LabelId label_id)157 void FastAccessorAssembler::Goto(LabelId label_id) {
158 CHECK_EQ(kBuilding, state_);
159 assembler_->Goto(FromId(label_id));
160 }
161
CheckNotZeroOrJump(ValueId value_id,LabelId label_id)162 void FastAccessorAssembler::CheckNotZeroOrJump(ValueId value_id,
163 LabelId label_id) {
164 CHECK_EQ(kBuilding, state_);
165 CodeStubAssembler::Label pass(assembler_.get());
166 assembler_->Branch(
167 assembler_->WordEqual(FromId(value_id), assembler_->IntPtrConstant(0)),
168 FromId(label_id), &pass);
169 assembler_->Bind(&pass);
170 }
171
Call(FunctionCallback callback_function,ValueId arg)172 FastAccessorAssembler::ValueId FastAccessorAssembler::Call(
173 FunctionCallback callback_function, ValueId arg) {
174 CHECK_EQ(kBuilding, state_);
175
176 // Wrap the FunctionCallback in an ExternalReference.
177 ApiFunction callback_api_function(FUNCTION_ADDR(callback_function));
178 ExternalReference callback(&callback_api_function,
179 ExternalReference::DIRECT_API_CALL, isolate());
180
181 // Create & call API callback via stub.
182 const int kJSParameterCount = 1;
183 CallApiCallbackStub stub(isolate(), kJSParameterCount, true, true);
184 CallInterfaceDescriptor descriptor = stub.GetCallInterfaceDescriptor();
185 DCHECK_EQ(4, descriptor.GetParameterCount());
186 DCHECK_EQ(0, descriptor.GetStackParameterCount());
187 // TODO(vogelheim): There is currently no clean way to retrieve the context
188 // parameter for a stub and the implementation details are hidden in
189 // compiler/*. The context_paramter is computed as:
190 // Linkage::GetJSCallContextParamIndex(descriptor->JSParameterCount())
191 const int kContextParameter = 3;
192 Node* context = assembler_->Parameter(kContextParameter);
193 Node* target = assembler_->HeapConstant(stub.GetCode());
194
195 int param_count = descriptor.GetParameterCount();
196 Node** args = zone()->NewArray<Node*>(param_count + 1 + kJSParameterCount);
197 // Stub/register parameters:
198 args[0] = assembler_->UndefinedConstant(); // callee (there's no JSFunction)
199 args[1] = assembler_->UndefinedConstant(); // call_data (undefined)
200 args[2] = assembler_->Parameter(0); // receiver (same as holder in this case)
201 args[3] = assembler_->ExternalConstant(callback); // API callback function
202
203 // JS arguments, on stack:
204 args[4] = FromId(arg);
205
206 // Context.
207 args[5] = context;
208
209 Node* call =
210 assembler_->CallStubN(descriptor, kJSParameterCount, target, args);
211
212 return FromRaw(call);
213 }
214
CheckIsJSObjectOrJump(ValueId value_id,LabelId label_id)215 void FastAccessorAssembler::CheckIsJSObjectOrJump(ValueId value_id,
216 LabelId label_id) {
217 CHECK_EQ(kBuilding, state_);
218
219 // Determine the 'value' object's instance type.
220 Node* object_map = assembler_->LoadObjectField(
221 FromId(value_id), Internals::kHeapObjectMapOffset,
222 MachineType::Pointer());
223
224 Node* instance_type = assembler_->WordAnd(
225 assembler_->LoadObjectField(object_map,
226 Internals::kMapInstanceTypeAndBitFieldOffset,
227 MachineType::Uint16()),
228 assembler_->IntPtrConstant(0xff));
229
230 CodeStubAssembler::Label is_jsobject(assembler_.get());
231
232 // Check whether we have a proper JSObject.
233 assembler_->GotoIf(
234 assembler_->WordEqual(
235 instance_type, assembler_->IntPtrConstant(Internals::kJSObjectType)),
236 &is_jsobject);
237
238 // JSApiObject?.
239 assembler_->GotoUnless(
240 assembler_->WordEqual(instance_type, assembler_->IntPtrConstant(
241 Internals::kJSApiObjectType)),
242 FromId(label_id));
243
244 // Continue.
245 assembler_->Goto(&is_jsobject);
246 assembler_->Bind(&is_jsobject);
247 }
248
Build()249 MaybeHandle<Code> FastAccessorAssembler::Build() {
250 CHECK_EQ(kBuilding, state_);
251 Handle<Code> code = assembler_->GenerateCode();
252 state_ = !code.is_null() ? kBuilt : kError;
253 Clear();
254 return code;
255 }
256
FromRaw(Node * node)257 FastAccessorAssembler::ValueId FastAccessorAssembler::FromRaw(Node* node) {
258 nodes_.push_back(node);
259 ValueId value = {nodes_.size() - 1};
260 return value;
261 }
262
FromRaw(CodeStubAssembler::Label * label)263 FastAccessorAssembler::LabelId FastAccessorAssembler::FromRaw(
264 CodeStubAssembler::Label* label) {
265 labels_.push_back(label);
266 LabelId label_id = {labels_.size() - 1};
267 return label_id;
268 }
269
FromId(ValueId value) const270 Node* FastAccessorAssembler::FromId(ValueId value) const {
271 CHECK_LT(value.value_id, nodes_.size());
272 CHECK_NOT_NULL(nodes_.at(value.value_id));
273 return nodes_.at(value.value_id);
274 }
275
FromId(LabelId label) const276 CodeStubAssembler::Label* FastAccessorAssembler::FromId(LabelId label) const {
277 CHECK_LT(label.label_id, labels_.size());
278 CHECK_NOT_NULL(labels_.at(label.label_id));
279 return labels_.at(label.label_id);
280 }
281
Clear()282 void FastAccessorAssembler::Clear() {
283 for (auto label : labels_) {
284 delete label;
285 }
286 nodes_.clear();
287 labels_.clear();
288 }
289
290 } // namespace internal
291 } // namespace v8
292