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-inl.h"
12 #include "src/objects.h"  // For FAA::LoadInternalField impl.
13 
14 namespace v8 {
15 namespace internal {
16 
17 using compiler::Node;
18 using compiler::CodeAssemblerLabel;
19 using compiler::CodeAssemblerVariable;
20 
FastAccessorAssembler(Isolate * isolate)21 FastAccessorAssembler::FastAccessorAssembler(Isolate* isolate)
22     : zone_(isolate->allocator(), ZONE_NAME),
23       isolate_(isolate),
24       assembler_state_(new compiler::CodeAssemblerState(
25           isolate, zone(), 1, Code::ComputeFlags(Code::STUB),
26           "FastAccessorAssembler")),
27       assembler_(new CodeStubAssembler(assembler_state_.get())),
28       state_(kBuilding) {}
29 
~FastAccessorAssembler()30 FastAccessorAssembler::~FastAccessorAssembler() { Clear(); }
31 
IntegerConstant(int const_value)32 FastAccessorAssembler::ValueId FastAccessorAssembler::IntegerConstant(
33     int const_value) {
34   CHECK_EQ(kBuilding, state_);
35   return FromRaw(assembler_->NumberConstant(const_value));
36 }
37 
GetReceiver()38 FastAccessorAssembler::ValueId FastAccessorAssembler::GetReceiver() {
39   CHECK_EQ(kBuilding, state_);
40 
41   // For JS functions, the receiver is parameter 0.
42   return FromRaw(assembler_->Parameter(0));
43 }
44 
LoadInternalField(ValueId value_id,int field_no)45 FastAccessorAssembler::ValueId FastAccessorAssembler::LoadInternalField(
46     ValueId value_id, int field_no) {
47   CHECK_EQ(kBuilding, state_);
48 
49   CodeAssemblerVariable result(assembler_.get(),
50                                MachineRepresentation::kTagged);
51   LabelId is_not_jsobject = MakeLabel();
52   CodeAssemblerLabel merge(assembler_.get(), &result);
53 
54   CheckIsJSObjectOrJump(value_id, is_not_jsobject);
55 
56   Node* internal_field = assembler_->LoadObjectField(
57       FromId(value_id), JSObject::kHeaderSize + kPointerSize * field_no);
58 
59   result.Bind(internal_field);
60   assembler_->Goto(&merge);
61 
62   // Return null, mimicking the C++ counterpart.
63   SetLabel(is_not_jsobject);
64   result.Bind(assembler_->NullConstant());
65   assembler_->Goto(&merge);
66 
67   // Return.
68   assembler_->Bind(&merge);
69   return FromRaw(result.value());
70 }
71 
72 FastAccessorAssembler::ValueId
LoadInternalFieldUnchecked(ValueId value_id,int field_no)73 FastAccessorAssembler::LoadInternalFieldUnchecked(ValueId value_id,
74                                                   int field_no) {
75   CHECK_EQ(kBuilding, state_);
76 
77   // Defensive debug checks.
78   if (FLAG_debug_code) {
79     LabelId is_jsobject = MakeLabel();
80     LabelId is_not_jsobject = MakeLabel();
81     CheckIsJSObjectOrJump(value_id, is_not_jsobject);
82     assembler_->Goto(FromId(is_jsobject));
83 
84     SetLabel(is_not_jsobject);
85     assembler_->DebugBreak();
86     assembler_->Goto(FromId(is_jsobject));
87 
88     SetLabel(is_jsobject);
89   }
90 
91   Node* result = assembler_->LoadObjectField(
92       FromId(value_id), JSObject::kHeaderSize + kPointerSize * field_no);
93 
94   return FromRaw(result);
95 }
96 
LoadValue(ValueId value_id,int offset)97 FastAccessorAssembler::ValueId FastAccessorAssembler::LoadValue(
98     ValueId value_id, int offset) {
99   CHECK_EQ(kBuilding, state_);
100   return FromRaw(assembler_->LoadBufferObject(FromId(value_id), offset,
101                                               MachineType::IntPtr()));
102 }
103 
LoadObject(ValueId value_id,int offset)104 FastAccessorAssembler::ValueId FastAccessorAssembler::LoadObject(
105     ValueId value_id, int offset) {
106   CHECK_EQ(kBuilding, state_);
107   return FromRaw(assembler_->LoadBufferObject(
108       assembler_->LoadBufferObject(FromId(value_id), offset), 0,
109       MachineType::AnyTagged()));
110 }
111 
ToSmi(ValueId value_id)112 FastAccessorAssembler::ValueId FastAccessorAssembler::ToSmi(ValueId value_id) {
113   CHECK_EQ(kBuilding, state_);
114   return FromRaw(assembler_->SmiTag(FromId(value_id)));
115 }
116 
ReturnValue(ValueId value_id)117 void FastAccessorAssembler::ReturnValue(ValueId value_id) {
118   CHECK_EQ(kBuilding, state_);
119   assembler_->Return(FromId(value_id));
120 }
121 
CheckFlagSetOrReturnNull(ValueId value_id,int mask)122 void FastAccessorAssembler::CheckFlagSetOrReturnNull(ValueId value_id,
123                                                      int mask) {
124   CHECK_EQ(kBuilding, state_);
125   CodeAssemblerLabel pass(assembler_.get());
126   CodeAssemblerLabel fail(assembler_.get());
127   Node* value = FromId(value_id);
128   assembler_->Branch(
129       assembler_->IsSetWord(assembler_->BitcastTaggedToWord(value), mask),
130       &pass, &fail);
131   assembler_->Bind(&fail);
132   assembler_->Return(assembler_->NullConstant());
133   assembler_->Bind(&pass);
134 }
135 
CheckNotZeroOrReturnNull(ValueId value_id)136 void FastAccessorAssembler::CheckNotZeroOrReturnNull(ValueId value_id) {
137   CHECK_EQ(kBuilding, state_);
138   CodeAssemblerLabel is_null(assembler_.get());
139   CodeAssemblerLabel not_null(assembler_.get());
140   assembler_->Branch(
141       assembler_->WordEqual(FromId(value_id), assembler_->SmiConstant(0)),
142       &is_null, &not_null);
143   assembler_->Bind(&is_null);
144   assembler_->Return(assembler_->NullConstant());
145   assembler_->Bind(&not_null);
146 }
147 
MakeLabel()148 FastAccessorAssembler::LabelId FastAccessorAssembler::MakeLabel() {
149   CHECK_EQ(kBuilding, state_);
150   return FromRaw(new CodeAssemblerLabel(assembler_.get()));
151 }
152 
SetLabel(LabelId label_id)153 void FastAccessorAssembler::SetLabel(LabelId label_id) {
154   CHECK_EQ(kBuilding, state_);
155   assembler_->Bind(FromId(label_id));
156 }
157 
Goto(LabelId label_id)158 void FastAccessorAssembler::Goto(LabelId label_id) {
159   CHECK_EQ(kBuilding, state_);
160   assembler_->Goto(FromId(label_id));
161 }
162 
CheckNotZeroOrJump(ValueId value_id,LabelId label_id)163 void FastAccessorAssembler::CheckNotZeroOrJump(ValueId value_id,
164                                                LabelId label_id) {
165   CHECK_EQ(kBuilding, state_);
166   CodeAssemblerLabel pass(assembler_.get());
167   assembler_->Branch(
168       assembler_->WordEqual(FromId(value_id), assembler_->SmiConstant(0)),
169       FromId(label_id), &pass);
170   assembler_->Bind(&pass);
171 }
172 
Call(FunctionCallback callback_function,ValueId arg)173 FastAccessorAssembler::ValueId FastAccessorAssembler::Call(
174     FunctionCallback callback_function, ValueId arg) {
175   CHECK_EQ(kBuilding, state_);
176 
177   // Wrap the FunctionCallback in an ExternalReference.
178   ApiFunction callback_api_function(FUNCTION_ADDR(callback_function));
179   ExternalReference callback(&callback_api_function,
180                              ExternalReference::DIRECT_API_CALL, isolate());
181 
182   // Create & call API callback via stub.
183   const int kJSParameterCount = 1;
184   CallApiCallbackStub stub(isolate(), kJSParameterCount, true, true);
185   CallInterfaceDescriptor descriptor = stub.GetCallInterfaceDescriptor();
186   DCHECK_EQ(4, descriptor.GetParameterCount());
187   DCHECK_EQ(0, descriptor.GetStackParameterCount());
188   Node* context = assembler_->GetJSContextParameter();
189   Node* target = assembler_->HeapConstant(stub.GetCode());
190 
191   Node* call = assembler_->CallStub(
192       descriptor, target, context,
193       assembler_->UndefinedConstant(),  // callee (there's no JSFunction)
194       assembler_->UndefinedConstant(),  // call_data (undefined)
195       assembler_->Parameter(0),  // receiver (same as holder in this case)
196       assembler_->ExternalConstant(callback),  // API callback function
197       FromId(arg));                            // JS argument, on stack
198   return FromRaw(call);
199 }
200 
CheckIsJSObjectOrJump(ValueId value_id,LabelId label_id)201 void FastAccessorAssembler::CheckIsJSObjectOrJump(ValueId value_id,
202                                                   LabelId label_id) {
203   CHECK_EQ(kBuilding, state_);
204 
205   // Determine the 'value' object's instance type.
206   Node* instance_type = assembler_->LoadInstanceType(FromId(value_id));
207 
208   CodeAssemblerLabel is_jsobject(assembler_.get());
209 
210   // Check whether we have a proper JSObject.
211   assembler_->GotoIf(
212       assembler_->Word32Equal(
213           instance_type, assembler_->Int32Constant(Internals::kJSObjectType)),
214       &is_jsobject);
215 
216   // JSApiObject?.
217   assembler_->GotoIfNot(
218       assembler_->Word32Equal(instance_type, assembler_->Int32Constant(
219                                                  Internals::kJSApiObjectType)),
220       FromId(label_id));
221 
222   // Continue.
223   assembler_->Goto(&is_jsobject);
224   assembler_->Bind(&is_jsobject);
225 }
226 
Build()227 MaybeHandle<Code> FastAccessorAssembler::Build() {
228   CHECK_EQ(kBuilding, state_);
229   Handle<Code> code =
230       compiler::CodeAssembler::GenerateCode(assembler_state_.get());
231   state_ = !code.is_null() ? kBuilt : kError;
232   Clear();
233   return code;
234 }
235 
FromRaw(Node * node)236 FastAccessorAssembler::ValueId FastAccessorAssembler::FromRaw(Node* node) {
237   nodes_.push_back(node);
238   ValueId value_id = {nodes_.size() - 1};
239   return value_id;
240 }
241 
FromRaw(CodeAssemblerLabel * label)242 FastAccessorAssembler::LabelId FastAccessorAssembler::FromRaw(
243     CodeAssemblerLabel* label) {
244   labels_.push_back(label);
245   LabelId label_id = {labels_.size() - 1};
246   return label_id;
247 }
248 
FromId(ValueId value) const249 Node* FastAccessorAssembler::FromId(ValueId value) const {
250   CHECK_LT(value.value_id, nodes_.size());
251   CHECK_NOT_NULL(nodes_.at(value.value_id));
252   return nodes_.at(value.value_id);
253 }
254 
FromId(LabelId label) const255 CodeAssemblerLabel* FastAccessorAssembler::FromId(LabelId label) const {
256   CHECK_LT(label.label_id, labels_.size());
257   CHECK_NOT_NULL(labels_.at(label.label_id));
258   return labels_.at(label.label_id);
259 }
260 
Clear()261 void FastAccessorAssembler::Clear() {
262   for (auto label : labels_) {
263     delete label;
264   }
265   nodes_.clear();
266   labels_.clear();
267 }
268 
269 }  // namespace internal
270 }  // namespace v8
271