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, &not_null);
142   assembler_->Bind(&is_null);
143   assembler_->Return(assembler_->NullConstant());
144   assembler_->Bind(&not_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