Line data Source code
1 : // Copyright 2017 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/builtins/builtins-utils-gen.h"
6 : #include "src/builtins/builtins.h"
7 : #include "src/code-stub-assembler.h"
8 : #include "src/macro-assembler.h"
9 : #include "src/runtime/runtime.h"
10 :
11 : namespace v8 {
12 : namespace internal {
13 :
14 : // -----------------------------------------------------------------------------
15 : // Interrupt and stack checks.
16 :
17 43 : void Builtins::Generate_InterruptCheck(MacroAssembler* masm) {
18 43 : masm->TailCallRuntime(Runtime::kInterrupt);
19 43 : }
20 :
21 43 : void Builtins::Generate_StackCheck(MacroAssembler* masm) {
22 43 : masm->TailCallRuntime(Runtime::kStackGuard);
23 43 : }
24 :
25 : // -----------------------------------------------------------------------------
26 : // TurboFan support builtins.
27 :
28 129 : TF_BUILTIN(CopyFastSmiOrObjectElements, CodeStubAssembler) {
29 : Node* object = Parameter(Descriptor::kObject);
30 :
31 : // Load the {object}s elements.
32 43 : Node* source = LoadObjectField(object, JSObject::kElementsOffset);
33 :
34 : ParameterMode mode = OptimalParameterMode();
35 43 : Node* length = TaggedToParameter(LoadFixedArrayBaseLength(source), mode);
36 :
37 : // Check if we can allocate in new space.
38 : ElementsKind kind = FAST_ELEMENTS;
39 43 : int max_elements = FixedArrayBase::GetMaxLengthForNewSpaceAllocation(kind);
40 43 : Label if_newspace(this), if_oldspace(this);
41 : Branch(UintPtrOrSmiLessThan(length, IntPtrOrSmiConstant(max_elements, mode),
42 : mode),
43 43 : &if_newspace, &if_oldspace);
44 :
45 43 : BIND(&if_newspace);
46 : {
47 43 : Node* target = AllocateFixedArray(kind, length, mode);
48 : CopyFixedArrayElements(kind, source, target, length, SKIP_WRITE_BARRIER,
49 : mode);
50 43 : StoreObjectField(object, JSObject::kElementsOffset, target);
51 43 : Return(target);
52 : }
53 :
54 43 : BIND(&if_oldspace);
55 : {
56 43 : Node* target = AllocateFixedArray(kind, length, mode, kPretenured);
57 : CopyFixedArrayElements(kind, source, target, length, UPDATE_WRITE_BARRIER,
58 : mode);
59 43 : StoreObjectField(object, JSObject::kElementsOffset, target);
60 43 : Return(target);
61 43 : }
62 43 : }
63 :
64 129 : TF_BUILTIN(GrowFastDoubleElements, CodeStubAssembler) {
65 : Node* object = Parameter(Descriptor::kObject);
66 : Node* key = Parameter(Descriptor::kKey);
67 : Node* context = Parameter(Descriptor::kContext);
68 :
69 : Label runtime(this, Label::kDeferred);
70 43 : Node* elements = LoadElements(object);
71 : elements = TryGrowElementsCapacity(object, elements, FAST_DOUBLE_ELEMENTS,
72 43 : key, &runtime);
73 43 : Return(elements);
74 :
75 43 : BIND(&runtime);
76 43 : TailCallRuntime(Runtime::kGrowArrayElements, context, object, key);
77 43 : }
78 :
79 129 : TF_BUILTIN(GrowFastSmiOrObjectElements, CodeStubAssembler) {
80 : Node* object = Parameter(Descriptor::kObject);
81 : Node* key = Parameter(Descriptor::kKey);
82 : Node* context = Parameter(Descriptor::kContext);
83 :
84 : Label runtime(this, Label::kDeferred);
85 43 : Node* elements = LoadElements(object);
86 : elements =
87 43 : TryGrowElementsCapacity(object, elements, FAST_ELEMENTS, key, &runtime);
88 43 : Return(elements);
89 :
90 43 : BIND(&runtime);
91 43 : TailCallRuntime(Runtime::kGrowArrayElements, context, object, key);
92 43 : }
93 :
94 129 : TF_BUILTIN(NewUnmappedArgumentsElements, CodeStubAssembler) {
95 : Node* frame = Parameter(Descriptor::kFrame);
96 43 : Node* length = SmiToWord(Parameter(Descriptor::kLength));
97 :
98 : // Check if we can allocate in new space.
99 : ElementsKind kind = FAST_ELEMENTS;
100 43 : int max_elements = FixedArray::GetMaxLengthForNewSpaceAllocation(kind);
101 43 : Label if_newspace(this), if_oldspace(this, Label::kDeferred);
102 : Branch(IntPtrLessThan(length, IntPtrConstant(max_elements)), &if_newspace,
103 43 : &if_oldspace);
104 :
105 43 : BIND(&if_newspace);
106 : {
107 : // Prefer EmptyFixedArray in case of non-positive {length} (the {length}
108 : // can be negative here for rest parameters).
109 43 : Label if_empty(this), if_notempty(this);
110 : Branch(IntPtrLessThanOrEqual(length, IntPtrConstant(0)), &if_empty,
111 43 : &if_notempty);
112 :
113 43 : BIND(&if_empty);
114 43 : Return(EmptyFixedArrayConstant());
115 :
116 43 : BIND(&if_notempty);
117 : {
118 : // Allocate a FixedArray in new space.
119 43 : Node* result = AllocateFixedArray(kind, length);
120 :
121 : // Compute the effective {offset} into the {frame}.
122 43 : Node* offset = IntPtrAdd(length, IntPtrConstant(1));
123 :
124 : // Copy the parameters from {frame} (starting at {offset}) to {result}.
125 43 : VARIABLE(var_index, MachineType::PointerRepresentation());
126 43 : Label loop(this, &var_index), done_loop(this);
127 43 : var_index.Bind(IntPtrConstant(0));
128 43 : Goto(&loop);
129 43 : BIND(&loop);
130 : {
131 : // Load the current {index}.
132 43 : Node* index = var_index.value();
133 :
134 : // Check if we are done.
135 43 : GotoIf(WordEqual(index, length), &done_loop);
136 :
137 : // Load the parameter at the given {index}.
138 : Node* value = Load(MachineType::AnyTagged(), frame,
139 : WordShl(IntPtrSub(offset, index),
140 43 : IntPtrConstant(kPointerSizeLog2)));
141 :
142 : // Store the {value} into the {result}.
143 43 : StoreFixedArrayElement(result, index, value, SKIP_WRITE_BARRIER);
144 :
145 : // Continue with next {index}.
146 43 : var_index.Bind(IntPtrAdd(index, IntPtrConstant(1)));
147 43 : Goto(&loop);
148 : }
149 :
150 43 : BIND(&done_loop);
151 86 : Return(result);
152 43 : }
153 : }
154 :
155 43 : BIND(&if_oldspace);
156 : {
157 : // Allocate in old space (or large object space).
158 : TailCallRuntime(Runtime::kNewArgumentsElements, NoContextConstant(),
159 43 : BitcastWordToTagged(frame), SmiFromWord(length));
160 43 : }
161 43 : }
162 :
163 129 : TF_BUILTIN(ReturnReceiver, CodeStubAssembler) {
164 43 : Return(Parameter(Descriptor::kReceiver));
165 43 : }
166 :
167 : class DeletePropertyBaseAssembler : public CodeStubAssembler {
168 : public:
169 : explicit DeletePropertyBaseAssembler(compiler::CodeAssemblerState* state)
170 43 : : CodeStubAssembler(state) {}
171 :
172 43 : void DeleteFastProperty(Node* receiver, Node* receiver_map, Node* properties,
173 : Node* name, Label* dont_delete, Label* not_found,
174 : Label* slow) {
175 : // This builtin implements a special case for fast property deletion:
176 : // when the last property in an object is deleted, then instead of
177 : // normalizing the properties, we can undo the last map transition,
178 : // with a few prerequisites:
179 : // (1) The current map must not be marked stable. Otherwise there could
180 : // be optimized code that depends on the assumption that no object that
181 : // reached this map transitions away from it (without triggering the
182 : // "deoptimize dependent code" mechanism).
183 43 : Node* bitfield3 = LoadMapBitField3(receiver_map);
184 43 : GotoIfNot(IsSetWord32<Map::IsUnstable>(bitfield3), slow);
185 : // (2) The property to be deleted must be the last property.
186 43 : Node* descriptors = LoadMapDescriptors(receiver_map);
187 : Node* nof = DecodeWord32<Map::NumberOfOwnDescriptorsBits>(bitfield3);
188 43 : GotoIf(Word32Equal(nof, Int32Constant(0)), not_found);
189 43 : Node* descriptor_number = Int32Sub(nof, Int32Constant(1));
190 43 : Node* key_index = DescriptorArrayToKeyIndex(descriptor_number);
191 43 : Node* actual_key = LoadFixedArrayElement(descriptors, key_index);
192 : // TODO(jkummerow): We could implement full descriptor search in order
193 : // to avoid the runtime call for deleting nonexistent properties, but
194 : // that's probably a rare case.
195 43 : GotoIf(WordNotEqual(actual_key, name), slow);
196 : // (3) The property to be deleted must be deletable.
197 : Node* details =
198 : LoadDetailsByKeyIndex<DescriptorArray>(descriptors, key_index);
199 : GotoIf(IsSetWord32(details, PropertyDetails::kAttributesDontDeleteMask),
200 43 : dont_delete);
201 : // (4) The map must have a back pointer.
202 : Node* backpointer =
203 43 : LoadObjectField(receiver_map, Map::kConstructorOrBackPointerOffset);
204 43 : GotoIfNot(IsMap(backpointer), slow);
205 : // (5) The last transition must have been caused by adding a property
206 : // (and not any kind of special transition).
207 : Node* previous_nof = DecodeWord32<Map::NumberOfOwnDescriptorsBits>(
208 43 : LoadMapBitField3(backpointer));
209 43 : GotoIfNot(Word32Equal(previous_nof, descriptor_number), slow);
210 :
211 : // Preconditions successful, perform the map rollback!
212 : // Zap the property to avoid keeping objects alive.
213 : // Zapping is not necessary for properties stored in the descriptor array.
214 : Label zapping_done(this);
215 : GotoIf(Word32NotEqual(DecodeWord32<PropertyDetails::LocationField>(details),
216 : Int32Constant(kField)),
217 86 : &zapping_done);
218 : Node* field_index =
219 43 : DecodeWordFromWord32<PropertyDetails::FieldIndexField>(details);
220 43 : Node* inobject_properties = LoadMapInobjectProperties(receiver_map);
221 43 : Label inobject(this), backing_store(this);
222 : // Due to inobject slack tracking, a field currently within the object
223 : // could later be between objects. Use the one pointer filler map for
224 : // zapping the deleted field to make this safe.
225 43 : Node* filler = LoadRoot(Heap::kOnePointerFillerMapRootIndex);
226 : DCHECK(Heap::RootIsImmortalImmovable(Heap::kOnePointerFillerMapRootIndex));
227 : Branch(UintPtrLessThan(field_index, inobject_properties), &inobject,
228 43 : &backing_store);
229 43 : BIND(&inobject);
230 : {
231 : Node* field_offset =
232 : IntPtrMul(IntPtrSub(LoadMapInstanceSize(receiver_map),
233 : IntPtrSub(inobject_properties, field_index)),
234 43 : IntPtrConstant(kPointerSize));
235 43 : StoreObjectFieldNoWriteBarrier(receiver, field_offset, filler);
236 43 : Goto(&zapping_done);
237 : }
238 43 : BIND(&backing_store);
239 : {
240 43 : Node* backing_store_index = IntPtrSub(field_index, inobject_properties);
241 : StoreFixedArrayElement(properties, backing_store_index, filler,
242 43 : SKIP_WRITE_BARRIER);
243 43 : Goto(&zapping_done);
244 : }
245 43 : BIND(&zapping_done);
246 43 : StoreMap(receiver, backpointer);
247 86 : Return(TrueConstant());
248 43 : }
249 :
250 43 : void DeleteDictionaryProperty(Node* receiver, Node* properties, Node* name,
251 : Node* context, Label* dont_delete,
252 : Label* notfound) {
253 43 : VARIABLE(var_name_index, MachineType::PointerRepresentation());
254 43 : Label dictionary_found(this, &var_name_index);
255 : NameDictionaryLookup<NameDictionary>(properties, name, &dictionary_found,
256 43 : &var_name_index, notfound);
257 :
258 43 : BIND(&dictionary_found);
259 43 : Node* key_index = var_name_index.value();
260 : Node* details =
261 : LoadDetailsByKeyIndex<NameDictionary>(properties, key_index);
262 : GotoIf(IsSetWord32(details, PropertyDetails::kAttributesDontDeleteMask),
263 43 : dont_delete);
264 : // Overwrite the entry itself (see NameDictionary::SetEntry).
265 43 : Node* filler = TheHoleConstant();
266 : DCHECK(Heap::RootIsImmortalImmovable(Heap::kTheHoleValueRootIndex));
267 43 : StoreFixedArrayElement(properties, key_index, filler, SKIP_WRITE_BARRIER);
268 : StoreValueByKeyIndex<NameDictionary>(properties, key_index, filler,
269 : SKIP_WRITE_BARRIER);
270 : StoreDetailsByKeyIndex<NameDictionary>(properties, key_index,
271 43 : SmiConstant(Smi::kZero));
272 :
273 : // Update bookkeeping information (see NameDictionary::ElementRemoved).
274 : Node* nof = GetNumberOfElements<NameDictionary>(properties);
275 43 : Node* new_nof = SmiSub(nof, SmiConstant(1));
276 : SetNumberOfElements<NameDictionary>(properties, new_nof);
277 : Node* num_deleted = GetNumberOfDeletedElements<NameDictionary>(properties);
278 43 : Node* new_deleted = SmiAdd(num_deleted, SmiConstant(1));
279 : SetNumberOfDeletedElements<NameDictionary>(properties, new_deleted);
280 :
281 : // Shrink the dictionary if necessary (see NameDictionary::Shrink).
282 43 : Label shrinking_done(this);
283 : Node* capacity = GetCapacity<NameDictionary>(properties);
284 43 : GotoIf(SmiGreaterThan(new_nof, SmiShr(capacity, 2)), &shrinking_done);
285 43 : GotoIf(SmiLessThan(new_nof, SmiConstant(16)), &shrinking_done);
286 43 : CallRuntime(Runtime::kShrinkPropertyDictionary, context, receiver, name);
287 43 : Goto(&shrinking_done);
288 43 : BIND(&shrinking_done);
289 :
290 86 : Return(TrueConstant());
291 43 : }
292 : };
293 :
294 172 : TF_BUILTIN(DeleteProperty, DeletePropertyBaseAssembler) {
295 : Node* receiver = Parameter(Descriptor::kObject);
296 : Node* key = Parameter(Descriptor::kKey);
297 : Node* language_mode = Parameter(Descriptor::kLanguageMode);
298 : Node* context = Parameter(Descriptor::kContext);
299 :
300 43 : VARIABLE(var_index, MachineType::PointerRepresentation());
301 86 : VARIABLE(var_unique, MachineRepresentation::kTagged, key);
302 43 : Label if_index(this), if_unique_name(this), if_notunique(this),
303 43 : if_notfound(this), slow(this);
304 :
305 43 : GotoIf(TaggedIsSmi(receiver), &slow);
306 43 : Node* receiver_map = LoadMap(receiver);
307 43 : Node* instance_type = LoadMapInstanceType(receiver_map);
308 : GotoIf(Int32LessThanOrEqual(instance_type,
309 : Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
310 43 : &slow);
311 : TryToName(key, &if_index, &var_index, &if_unique_name, &var_unique, &slow,
312 43 : &if_notunique);
313 :
314 43 : BIND(&if_index);
315 : {
316 43 : Comment("integer index");
317 43 : Goto(&slow); // TODO(jkummerow): Implement more smarts here.
318 : }
319 :
320 43 : BIND(&if_unique_name);
321 : {
322 43 : Comment("key is unique name");
323 43 : Node* unique = var_unique.value();
324 43 : CheckForAssociatedProtector(unique, &slow);
325 :
326 43 : Label dictionary(this), dont_delete(this);
327 43 : Node* properties = LoadProperties(receiver);
328 43 : Node* properties_map = LoadMap(properties);
329 : GotoIf(WordEqual(properties_map, LoadRoot(Heap::kHashTableMapRootIndex)),
330 43 : &dictionary);
331 : DeleteFastProperty(receiver, receiver_map, properties, unique, &dont_delete,
332 43 : &if_notfound, &slow);
333 :
334 43 : BIND(&dictionary);
335 : {
336 : DeleteDictionaryProperty(receiver, properties, unique, context,
337 43 : &dont_delete, &if_notfound);
338 : }
339 :
340 43 : BIND(&dont_delete);
341 : {
342 : STATIC_ASSERT(LANGUAGE_END == 2);
343 43 : GotoIf(SmiNotEqual(language_mode, SmiConstant(SLOPPY)), &slow);
344 43 : Return(FalseConstant());
345 43 : }
346 : }
347 :
348 43 : BIND(&if_notunique);
349 : {
350 : // If the string was not found in the string table, then no object can
351 : // have a property with that name.
352 : TryInternalizeString(key, &if_index, &var_index, &if_unique_name,
353 43 : &var_unique, &if_notfound, &slow);
354 : }
355 :
356 43 : BIND(&if_notfound);
357 43 : Return(TrueConstant());
358 :
359 43 : BIND(&slow);
360 : {
361 : TailCallRuntime(Runtime::kDeleteProperty, context, receiver, key,
362 43 : language_mode);
363 43 : }
364 43 : }
365 :
366 : } // namespace internal
367 : } // namespace v8
|