Line data Source code
1 : // Copyright 2016 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-forin-gen.h"
6 :
7 : #include "src/builtins/builtins-utils-gen.h"
8 : #include "src/builtins/builtins.h"
9 : #include "src/code-factory.h"
10 : #include "src/code-stub-assembler.h"
11 : #include "src/counters.h"
12 : #include "src/keys.h"
13 : #include "src/lookup.h"
14 : #include "src/objects-inl.h"
15 : #include "src/property-descriptor.h"
16 :
17 : namespace v8 {
18 : namespace internal {
19 :
20 : typedef compiler::Node Node;
21 :
22 86 : Node* ForInBuiltinsAssembler::ForInFilter(Node* key, Node* object,
23 : Node* context) {
24 : CSA_ASSERT(this, IsName(key));
25 :
26 86 : VARIABLE(var_result, MachineRepresentation::kTagged, key);
27 :
28 : Node* has_property =
29 86 : HasProperty(object, key, context, Runtime::kForInHasProperty);
30 :
31 86 : Label end(this);
32 86 : GotoIf(WordEqual(has_property, BooleanConstant(true)), &end);
33 :
34 86 : var_result.Bind(UndefinedConstant());
35 86 : Goto(&end);
36 :
37 86 : BIND(&end);
38 172 : return var_result.value();
39 : }
40 :
41 172 : std::tuple<Node*, Node*, Node*> ForInBuiltinsAssembler::EmitForInPrepare(
42 : Node* object, Node* context, Label* call_runtime,
43 : Label* nothing_to_iterate) {
44 172 : Label use_cache(this);
45 : CSA_ASSERT(this, IsJSReceiver(object));
46 :
47 172 : CheckEnumCache(object, &use_cache, nothing_to_iterate, call_runtime);
48 :
49 172 : BIND(&use_cache);
50 172 : Node* map = LoadMap(object);
51 172 : Node* enum_length = EnumLength(map);
52 172 : GotoIf(WordEqual(enum_length, SmiConstant(0)), nothing_to_iterate);
53 172 : Node* descriptors = LoadMapDescriptors(map);
54 : Node* cache_offset =
55 172 : LoadObjectField(descriptors, DescriptorArray::kEnumCacheOffset);
56 : Node* enum_cache = LoadObjectField(
57 172 : cache_offset, DescriptorArray::kEnumCacheBridgeCacheOffset);
58 :
59 172 : return std::make_tuple(map, enum_cache, enum_length);
60 : }
61 :
62 688 : Node* ForInBuiltinsAssembler::EnumLength(Node* map) {
63 : CSA_ASSERT(this, IsMap(map));
64 688 : Node* bitfield_3 = LoadMapBitField3(map);
65 688 : Node* enum_length = DecodeWordFromWord32<Map::EnumLengthBits>(bitfield_3);
66 688 : return SmiTag(enum_length);
67 : }
68 :
69 344 : void ForInBuiltinsAssembler::CheckPrototypeEnumCache(Node* receiver, Node* map,
70 : Label* use_cache,
71 : Label* use_runtime) {
72 344 : VARIABLE(current_js_object, MachineRepresentation::kTagged, receiver);
73 688 : VARIABLE(current_map, MachineRepresentation::kTagged, map);
74 :
75 : // These variables are updated in the loop below.
76 344 : Variable* loop_vars[2] = {¤t_js_object, ¤t_map};
77 688 : Label loop(this, 2, loop_vars), next(this);
78 :
79 344 : Goto(&loop);
80 : // Check that there are no elements. |current_js_object| contains
81 : // the current JS object we've reached through the prototype chain.
82 344 : BIND(&loop);
83 : {
84 344 : Label if_elements(this), if_no_elements(this);
85 344 : Node* elements = LoadElements(current_js_object.value());
86 344 : Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
87 : // Check that there are no elements.
88 : Branch(WordEqual(elements, empty_fixed_array), &if_no_elements,
89 344 : &if_elements);
90 344 : BIND(&if_elements);
91 : {
92 : // Second chance, the object may be using the empty slow element
93 : // dictionary.
94 : Node* slow_empty_dictionary =
95 344 : LoadRoot(Heap::kEmptySlowElementDictionaryRootIndex);
96 : Branch(WordNotEqual(elements, slow_empty_dictionary), use_runtime,
97 344 : &if_no_elements);
98 : }
99 :
100 344 : BIND(&if_no_elements);
101 : {
102 : // Update map prototype.
103 344 : current_js_object.Bind(LoadMapPrototype(current_map.value()));
104 : Branch(WordEqual(current_js_object.value(), NullConstant()), use_cache,
105 344 : &next);
106 344 : }
107 : }
108 :
109 344 : BIND(&next);
110 : {
111 : // For all objects but the receiver, check that the cache is empty.
112 344 : current_map.Bind(LoadMap(current_js_object.value()));
113 344 : Node* enum_length = EnumLength(current_map.value());
114 344 : Node* zero_constant = SmiConstant(Smi::kZero);
115 344 : Branch(WordEqual(enum_length, zero_constant), &loop, use_runtime);
116 344 : }
117 344 : }
118 :
119 172 : void ForInBuiltinsAssembler::CheckEnumCache(Node* receiver, Label* use_cache,
120 : Label* nothing_to_iterate,
121 : Label* use_runtime) {
122 172 : Node* map = LoadMap(receiver);
123 :
124 172 : Label check_empty_prototype(this),
125 172 : check_dict_receiver(this, Label::kDeferred);
126 :
127 : // Check if the enum length field is properly initialized, indicating that
128 : // there is an enum cache.
129 : {
130 : Node* invalid_enum_cache_sentinel =
131 172 : SmiConstant(Smi::FromInt(kInvalidEnumCacheSentinel));
132 172 : Node* enum_length = EnumLength(map);
133 : Branch(WordEqual(enum_length, invalid_enum_cache_sentinel),
134 172 : &check_dict_receiver, &check_empty_prototype);
135 : }
136 :
137 : // Check that there are no elements on the fast |receiver| and its prototype
138 : // chain.
139 172 : BIND(&check_empty_prototype);
140 172 : CheckPrototypeEnumCache(receiver, map, use_cache, use_runtime);
141 :
142 172 : Label dict_loop(this);
143 172 : BIND(&check_dict_receiver);
144 : {
145 : // Avoid runtime-call for empty dictionary receivers.
146 172 : GotoIfNot(IsDictionaryMap(map), use_runtime);
147 172 : Node* properties = LoadProperties(receiver);
148 : Node* length = LoadFixedArrayElement(
149 172 : properties, NameDictionary::kNumberOfElementsIndex);
150 172 : GotoIfNot(WordEqual(length, SmiConstant(0)), use_runtime);
151 : // Check that there are no elements on the |receiver| and its prototype
152 : // chain. Given that we do not create an EnumCache for dict-mode objects,
153 : // directly jump to |nothing_to_iterate| if there are no elements and no
154 : // properties on the |receiver|.
155 172 : CheckPrototypeEnumCache(receiver, map, nothing_to_iterate, use_runtime);
156 172 : }
157 172 : }
158 :
159 172 : TF_BUILTIN(ForInFilter, ForInBuiltinsAssembler) {
160 : Node* key = Parameter(Descriptor::kKey);
161 : Node* object = Parameter(Descriptor::kObject);
162 : Node* context = Parameter(Descriptor::kContext);
163 :
164 43 : Return(ForInFilter(key, object, context));
165 43 : }
166 :
167 215 : TF_BUILTIN(ForInNext, ForInBuiltinsAssembler) {
168 43 : Label filter(this);
169 : Node* object = Parameter(Descriptor::kObject);
170 : Node* cache_array = Parameter(Descriptor::kCacheArray);
171 : Node* cache_type = Parameter(Descriptor::kCacheType);
172 : Node* index = Parameter(Descriptor::kIndex);
173 : Node* context = Parameter(Descriptor::kContext);
174 :
175 43 : Node* key = LoadFixedArrayElement(cache_array, SmiUntag(index));
176 43 : Node* map = LoadMap(object);
177 43 : GotoIfNot(WordEqual(map, cache_type), &filter);
178 43 : Return(key);
179 43 : BIND(&filter);
180 43 : Return(ForInFilter(key, object, context));
181 43 : }
182 :
183 215 : TF_BUILTIN(ForInPrepare, ForInBuiltinsAssembler) {
184 86 : Label call_runtime(this), nothing_to_iterate(this);
185 : Node* object = Parameter(Descriptor::kObject);
186 : Node* context = Parameter(Descriptor::kContext);
187 :
188 : Node* cache_type;
189 : Node* cache_array;
190 : Node* cache_length;
191 86 : std::tie(cache_type, cache_array, cache_length) =
192 : EmitForInPrepare(object, context, &call_runtime, ¬hing_to_iterate);
193 :
194 43 : Return(cache_type, cache_array, cache_length);
195 :
196 43 : BIND(&call_runtime);
197 43 : TailCallRuntime(Runtime::kForInPrepare, context, object);
198 :
199 43 : BIND(¬hing_to_iterate);
200 : {
201 43 : Node* zero = SmiConstant(0);
202 43 : Return(zero, zero, zero);
203 43 : }
204 43 : }
205 : } // namespace internal
206 : } // namespace v8
|