OLD | NEW |
1 // Copyright 2014 the V8 project authors. All rights reserved. | 1 // Copyright 2014 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/compiler/js-context-specialization.h" | 5 #include "src/compiler/js-context-specialization.h" |
6 #include "src/compiler/js-operator.h" | 6 #include "src/compiler/js-operator.h" |
7 #include "src/compiler/node-matchers.h" | 7 #include "src/compiler/node-matchers.h" |
8 #include "src/compiler/node-properties.h" | 8 #include "src/compiler/node-properties.h" |
9 #include "src/compiler/source-position.h" | 9 #include "src/compiler/source-position.h" |
10 #include "test/cctest/cctest.h" | 10 #include "test/cctest/cctest.h" |
11 #include "test/cctest/compiler/function-tester.h" | 11 #include "test/cctest/compiler/function-tester.h" |
12 #include "test/cctest/compiler/graph-builder-tester.h" | 12 #include "test/cctest/compiler/graph-builder-tester.h" |
13 | 13 |
14 using namespace v8::internal; | 14 using namespace v8::internal; |
15 using namespace v8::internal::compiler; | 15 using namespace v8::internal::compiler; |
16 | 16 |
17 class ContextSpecializationTester : public HandleAndZoneScope, | 17 class ContextSpecializationTester : public HandleAndZoneScope { |
18 public DirectGraphBuilder { | |
19 public: | 18 public: |
20 ContextSpecializationTester() | 19 ContextSpecializationTester() |
21 : DirectGraphBuilder(main_isolate(), | 20 : graph_(new (main_zone()) Graph(main_zone())), |
22 new (main_zone()) Graph(main_zone())), | |
23 common_(main_zone()), | 21 common_(main_zone()), |
24 javascript_(main_zone()), | 22 javascript_(main_zone()), |
25 machine_(main_zone()), | 23 machine_(main_zone()), |
26 simplified_(main_zone()), | 24 simplified_(main_zone()), |
27 jsgraph_(main_isolate(), graph(), common(), &javascript_, &machine_) {} | 25 jsgraph_(main_isolate(), graph(), common(), &javascript_, &machine_) {} |
28 | 26 |
29 Factory* factory() { return main_isolate()->factory(); } | 27 Factory* factory() { return main_isolate()->factory(); } |
30 CommonOperatorBuilder* common() { return &common_; } | 28 CommonOperatorBuilder* common() { return &common_; } |
31 JSOperatorBuilder* javascript() { return &javascript_; } | 29 JSOperatorBuilder* javascript() { return &javascript_; } |
32 SimplifiedOperatorBuilder* simplified() { return &simplified_; } | 30 SimplifiedOperatorBuilder* simplified() { return &simplified_; } |
33 JSGraph* jsgraph() { return &jsgraph_; } | 31 JSGraph* jsgraph() { return &jsgraph_; } |
| 32 Graph* graph() { return graph_; } |
34 | 33 |
35 private: | 34 private: |
| 35 Graph* graph_; |
36 CommonOperatorBuilder common_; | 36 CommonOperatorBuilder common_; |
37 JSOperatorBuilder javascript_; | 37 JSOperatorBuilder javascript_; |
38 MachineOperatorBuilder machine_; | 38 MachineOperatorBuilder machine_; |
39 SimplifiedOperatorBuilder simplified_; | 39 SimplifiedOperatorBuilder simplified_; |
40 JSGraph jsgraph_; | 40 JSGraph jsgraph_; |
41 }; | 41 }; |
42 | 42 |
43 | 43 |
44 TEST(ReduceJSLoadContext) { | 44 TEST(ReduceJSLoadContext) { |
45 ContextSpecializationTester t; | 45 ContextSpecializationTester t; |
46 | 46 |
47 Node* start = t.NewNode(t.common()->Start(0)); | 47 Node* start = t.graph()->NewNode(t.common()->Start(0)); |
48 t.graph()->SetStart(start); | 48 t.graph()->SetStart(start); |
49 | 49 |
50 // Make a context and initialize it a bit for this test. | 50 // Make a context and initialize it a bit for this test. |
51 Handle<Context> native = t.factory()->NewNativeContext(); | 51 Handle<Context> native = t.factory()->NewNativeContext(); |
52 Handle<Context> subcontext1 = t.factory()->NewNativeContext(); | 52 Handle<Context> subcontext1 = t.factory()->NewNativeContext(); |
53 Handle<Context> subcontext2 = t.factory()->NewNativeContext(); | 53 Handle<Context> subcontext2 = t.factory()->NewNativeContext(); |
54 subcontext2->set_previous(*subcontext1); | 54 subcontext2->set_previous(*subcontext1); |
55 subcontext1->set_previous(*native); | 55 subcontext1->set_previous(*native); |
56 Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!"); | 56 Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!"); |
57 const int slot = Context::GLOBAL_OBJECT_INDEX; | 57 const int slot = Context::GLOBAL_OBJECT_INDEX; |
58 native->set(slot, *expected); | 58 native->set(slot, *expected); |
59 | 59 |
60 Node* const_context = t.jsgraph()->Constant(native); | 60 Node* const_context = t.jsgraph()->Constant(native); |
61 Node* deep_const_context = t.jsgraph()->Constant(subcontext2); | 61 Node* deep_const_context = t.jsgraph()->Constant(subcontext2); |
62 Node* param_context = t.NewNode(t.common()->Parameter(0), start); | 62 Node* param_context = t.graph()->NewNode(t.common()->Parameter(0), start); |
63 JSContextSpecializer spec(t.jsgraph()); | 63 JSContextSpecializer spec(t.jsgraph()); |
64 | 64 |
65 { | 65 { |
66 // Mutable slot, constant context, depth = 0 => do nothing. | 66 // Mutable slot, constant context, depth = 0 => do nothing. |
67 Node* load = t.NewNode(t.javascript()->LoadContext(0, 0, false), | 67 Node* load = t.graph()->NewNode(t.javascript()->LoadContext(0, 0, false), |
68 const_context, const_context, start); | 68 const_context, const_context, start); |
69 Reduction r = spec.ReduceJSLoadContext(load); | 69 Reduction r = spec.ReduceJSLoadContext(load); |
70 CHECK(!r.Changed()); | 70 CHECK(!r.Changed()); |
71 } | 71 } |
72 | 72 |
73 { | 73 { |
74 // Mutable slot, non-constant context, depth = 0 => do nothing. | 74 // Mutable slot, non-constant context, depth = 0 => do nothing. |
75 Node* load = t.NewNode(t.javascript()->LoadContext(0, 0, false), | 75 Node* load = t.graph()->NewNode(t.javascript()->LoadContext(0, 0, false), |
76 param_context, param_context, start); | 76 param_context, param_context, start); |
77 Reduction r = spec.ReduceJSLoadContext(load); | 77 Reduction r = spec.ReduceJSLoadContext(load); |
78 CHECK(!r.Changed()); | 78 CHECK(!r.Changed()); |
79 } | 79 } |
80 | 80 |
81 { | 81 { |
82 // Mutable slot, constant context, depth > 0 => fold-in parent context. | 82 // Mutable slot, constant context, depth > 0 => fold-in parent context. |
83 Node* load = t.NewNode( | 83 Node* load = t.graph()->NewNode( |
84 t.javascript()->LoadContext(2, Context::GLOBAL_EVAL_FUN_INDEX, false), | 84 t.javascript()->LoadContext(2, Context::GLOBAL_EVAL_FUN_INDEX, false), |
85 deep_const_context, deep_const_context, start); | 85 deep_const_context, deep_const_context, start); |
86 Reduction r = spec.ReduceJSLoadContext(load); | 86 Reduction r = spec.ReduceJSLoadContext(load); |
87 CHECK(r.Changed()); | 87 CHECK(r.Changed()); |
88 Node* new_context_input = NodeProperties::GetValueInput(r.replacement(), 0); | 88 Node* new_context_input = NodeProperties::GetValueInput(r.replacement(), 0); |
89 CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode()); | 89 CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode()); |
90 HeapObjectMatcher<Context> match(new_context_input); | 90 HeapObjectMatcher<Context> match(new_context_input); |
91 CHECK_EQ(*native, *match.Value().handle()); | 91 CHECK_EQ(*native, *match.Value().handle()); |
92 ContextAccess access = OpParameter<ContextAccess>(r.replacement()); | 92 ContextAccess access = OpParameter<ContextAccess>(r.replacement()); |
93 CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index())); | 93 CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index())); |
94 CHECK_EQ(0, static_cast<int>(access.depth())); | 94 CHECK_EQ(0, static_cast<int>(access.depth())); |
95 CHECK_EQ(false, access.immutable()); | 95 CHECK_EQ(false, access.immutable()); |
96 } | 96 } |
97 | 97 |
98 { | 98 { |
99 // Immutable slot, constant context, depth = 0 => specialize. | 99 // Immutable slot, constant context, depth = 0 => specialize. |
100 Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true), | 100 Node* load = t.graph()->NewNode(t.javascript()->LoadContext(0, slot, true), |
101 const_context, const_context, start); | 101 const_context, const_context, start); |
102 Reduction r = spec.ReduceJSLoadContext(load); | 102 Reduction r = spec.ReduceJSLoadContext(load); |
103 CHECK(r.Changed()); | 103 CHECK(r.Changed()); |
104 CHECK(r.replacement() != load); | 104 CHECK(r.replacement() != load); |
105 | 105 |
106 HeapObjectMatcher<Object> match(r.replacement()); | 106 HeapObjectMatcher<Object> match(r.replacement()); |
107 CHECK(match.HasValue()); | 107 CHECK(match.HasValue()); |
108 CHECK_EQ(*expected, *match.Value().handle()); | 108 CHECK_EQ(*expected, *match.Value().handle()); |
109 } | 109 } |
110 | 110 |
111 // TODO(titzer): test with other kinds of contexts, e.g. a function context. | 111 // TODO(titzer): test with other kinds of contexts, e.g. a function context. |
112 // TODO(sigurds): test that loads below create context are not optimized | 112 // TODO(sigurds): test that loads below create context are not optimized |
113 } | 113 } |
114 | 114 |
115 | 115 |
116 TEST(ReduceJSStoreContext) { | 116 TEST(ReduceJSStoreContext) { |
117 ContextSpecializationTester t; | 117 ContextSpecializationTester t; |
118 | 118 |
119 Node* start = t.NewNode(t.common()->Start(0)); | 119 Node* start = t.graph()->NewNode(t.common()->Start(0)); |
120 t.graph()->SetStart(start); | 120 t.graph()->SetStart(start); |
121 | 121 |
122 // Make a context and initialize it a bit for this test. | 122 // Make a context and initialize it a bit for this test. |
123 Handle<Context> native = t.factory()->NewNativeContext(); | 123 Handle<Context> native = t.factory()->NewNativeContext(); |
124 Handle<Context> subcontext1 = t.factory()->NewNativeContext(); | 124 Handle<Context> subcontext1 = t.factory()->NewNativeContext(); |
125 Handle<Context> subcontext2 = t.factory()->NewNativeContext(); | 125 Handle<Context> subcontext2 = t.factory()->NewNativeContext(); |
126 subcontext2->set_previous(*subcontext1); | 126 subcontext2->set_previous(*subcontext1); |
127 subcontext1->set_previous(*native); | 127 subcontext1->set_previous(*native); |
128 Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!"); | 128 Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!"); |
129 const int slot = Context::GLOBAL_OBJECT_INDEX; | 129 const int slot = Context::GLOBAL_OBJECT_INDEX; |
130 native->set(slot, *expected); | 130 native->set(slot, *expected); |
131 | 131 |
132 Node* const_context = t.jsgraph()->Constant(native); | 132 Node* const_context = t.jsgraph()->Constant(native); |
133 Node* deep_const_context = t.jsgraph()->Constant(subcontext2); | 133 Node* deep_const_context = t.jsgraph()->Constant(subcontext2); |
134 Node* param_context = t.NewNode(t.common()->Parameter(0), start); | 134 Node* param_context = t.graph()->NewNode(t.common()->Parameter(0), start); |
135 JSContextSpecializer spec(t.jsgraph()); | 135 JSContextSpecializer spec(t.jsgraph()); |
136 | 136 |
137 { | 137 { |
138 // Mutable slot, constant context, depth = 0 => do nothing. | 138 // Mutable slot, constant context, depth = 0 => do nothing. |
139 Node* load = t.NewNode(t.javascript()->StoreContext(0, 0), const_context, | 139 Node* load = t.graph()->NewNode(t.javascript()->StoreContext(0, 0), |
140 const_context, start); | 140 const_context, const_context, start); |
141 Reduction r = spec.ReduceJSStoreContext(load); | 141 Reduction r = spec.ReduceJSStoreContext(load); |
142 CHECK(!r.Changed()); | 142 CHECK(!r.Changed()); |
143 } | 143 } |
144 | 144 |
145 { | 145 { |
146 // Mutable slot, non-constant context, depth = 0 => do nothing. | 146 // Mutable slot, non-constant context, depth = 0 => do nothing. |
147 Node* load = t.NewNode(t.javascript()->StoreContext(0, 0), param_context, | 147 Node* load = t.graph()->NewNode(t.javascript()->StoreContext(0, 0), |
148 param_context, start); | 148 param_context, param_context, start); |
149 Reduction r = spec.ReduceJSStoreContext(load); | 149 Reduction r = spec.ReduceJSStoreContext(load); |
150 CHECK(!r.Changed()); | 150 CHECK(!r.Changed()); |
151 } | 151 } |
152 | 152 |
153 { | 153 { |
154 // Immutable slot, constant context, depth = 0 => do nothing. | 154 // Immutable slot, constant context, depth = 0 => do nothing. |
155 Node* load = t.NewNode(t.javascript()->StoreContext(0, slot), const_context, | 155 Node* load = t.graph()->NewNode(t.javascript()->StoreContext(0, slot), |
156 const_context, start); | 156 const_context, const_context, start); |
157 Reduction r = spec.ReduceJSStoreContext(load); | 157 Reduction r = spec.ReduceJSStoreContext(load); |
158 CHECK(!r.Changed()); | 158 CHECK(!r.Changed()); |
159 } | 159 } |
160 | 160 |
161 { | 161 { |
162 // Mutable slot, constant context, depth > 0 => fold-in parent context. | 162 // Mutable slot, constant context, depth > 0 => fold-in parent context. |
163 Node* load = t.NewNode( | 163 Node* load = t.graph()->NewNode( |
164 t.javascript()->StoreContext(2, Context::GLOBAL_EVAL_FUN_INDEX), | 164 t.javascript()->StoreContext(2, Context::GLOBAL_EVAL_FUN_INDEX), |
165 deep_const_context, deep_const_context, start); | 165 deep_const_context, deep_const_context, start); |
166 Reduction r = spec.ReduceJSStoreContext(load); | 166 Reduction r = spec.ReduceJSStoreContext(load); |
167 CHECK(r.Changed()); | 167 CHECK(r.Changed()); |
168 Node* new_context_input = NodeProperties::GetValueInput(r.replacement(), 0); | 168 Node* new_context_input = NodeProperties::GetValueInput(r.replacement(), 0); |
169 CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode()); | 169 CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode()); |
170 HeapObjectMatcher<Context> match(new_context_input); | 170 HeapObjectMatcher<Context> match(new_context_input); |
171 CHECK_EQ(*native, *match.Value().handle()); | 171 CHECK_EQ(*native, *match.Value().handle()); |
172 ContextAccess access = OpParameter<ContextAccess>(r.replacement()); | 172 ContextAccess access = OpParameter<ContextAccess>(r.replacement()); |
173 CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index())); | 173 CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index())); |
174 CHECK_EQ(0, static_cast<int>(access.depth())); | 174 CHECK_EQ(0, static_cast<int>(access.depth())); |
175 CHECK_EQ(false, access.immutable()); | 175 CHECK_EQ(false, access.immutable()); |
176 } | 176 } |
177 } | 177 } |
178 | 178 |
179 | 179 |
180 // TODO(titzer): factor out common code with effects checking in typed lowering. | 180 // TODO(titzer): factor out common code with effects checking in typed lowering. |
181 static void CheckEffectInput(Node* effect, Node* use) { | 181 static void CheckEffectInput(Node* effect, Node* use) { |
182 CHECK_EQ(effect, NodeProperties::GetEffectInput(use)); | 182 CHECK_EQ(effect, NodeProperties::GetEffectInput(use)); |
183 } | 183 } |
184 | 184 |
185 | 185 |
186 TEST(SpecializeToContext) { | 186 TEST(SpecializeToContext) { |
187 ContextSpecializationTester t; | 187 ContextSpecializationTester t; |
188 | 188 |
189 Node* start = t.NewNode(t.common()->Start(0)); | 189 Node* start = t.graph()->NewNode(t.common()->Start(0)); |
190 t.graph()->SetStart(start); | 190 t.graph()->SetStart(start); |
191 | 191 |
192 // Make a context and initialize it a bit for this test. | 192 // Make a context and initialize it a bit for this test. |
193 Handle<Context> native = t.factory()->NewNativeContext(); | 193 Handle<Context> native = t.factory()->NewNativeContext(); |
194 Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!"); | 194 Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!"); |
195 const int slot = Context::GLOBAL_OBJECT_INDEX; | 195 const int slot = Context::GLOBAL_OBJECT_INDEX; |
196 native->set(slot, *expected); | 196 native->set(slot, *expected); |
197 | 197 |
198 Node* const_context = t.jsgraph()->Constant(native); | 198 Node* const_context = t.jsgraph()->Constant(native); |
199 Node* param_context = t.NewNode(t.common()->Parameter(0), start); | 199 Node* param_context = t.graph()->NewNode(t.common()->Parameter(0), start); |
200 JSContextSpecializer spec(t.jsgraph()); | 200 JSContextSpecializer spec(t.jsgraph()); |
201 | 201 |
202 { | 202 { |
203 // Check that specialization replaces values and forwards effects | 203 // Check that specialization replaces values and forwards effects |
204 // correctly, and folds values from constant and non-constant contexts | 204 // correctly, and folds values from constant and non-constant contexts |
205 Node* effect_in = start; | 205 Node* effect_in = start; |
206 Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true), | 206 Node* load = t.graph()->NewNode(t.javascript()->LoadContext(0, slot, true), |
207 const_context, const_context, effect_in); | 207 const_context, const_context, effect_in); |
208 | 208 |
209 | 209 |
210 Node* value_use = t.NewNode(t.simplified()->ChangeTaggedToInt32(), load); | 210 Node* value_use = |
211 Node* other_load = t.NewNode(t.javascript()->LoadContext(0, slot, true), | 211 t.graph()->NewNode(t.simplified()->ChangeTaggedToInt32(), load); |
212 param_context, param_context, load); | 212 Node* other_load = |
| 213 t.graph()->NewNode(t.javascript()->LoadContext(0, slot, true), |
| 214 param_context, param_context, load); |
213 Node* effect_use = other_load; | 215 Node* effect_use = other_load; |
214 Node* other_use = | 216 Node* other_use = |
215 t.NewNode(t.simplified()->ChangeTaggedToInt32(), other_load); | 217 t.graph()->NewNode(t.simplified()->ChangeTaggedToInt32(), other_load); |
216 | 218 |
217 Node* add = t.NewNode(t.javascript()->Add(LanguageMode::SLOPPY), value_use, | 219 Node* add = |
218 other_use, param_context, other_load, start); | 220 t.graph()->NewNode(t.javascript()->Add(LanguageMode::SLOPPY), value_use, |
| 221 other_use, param_context, other_load, start); |
219 | 222 |
220 Node* ret = t.NewNode(t.common()->Return(), add, effect_use, start); | 223 Node* ret = |
221 Node* end = t.NewNode(t.common()->End(), ret); | 224 t.graph()->NewNode(t.common()->Return(), add, effect_use, start); |
| 225 Node* end = t.graph()->NewNode(t.common()->End(), ret); |
222 USE(end); | 226 USE(end); |
223 t.graph()->SetEnd(end); | 227 t.graph()->SetEnd(end); |
224 | 228 |
225 // Double check the above graph is what we expect, or the test is broken. | 229 // Double check the above graph is what we expect, or the test is broken. |
226 CheckEffectInput(effect_in, load); | 230 CheckEffectInput(effect_in, load); |
227 CheckEffectInput(load, effect_use); | 231 CheckEffectInput(load, effect_use); |
228 | 232 |
229 // Perform the reduction on the entire graph. | 233 // Perform the reduction on the entire graph. |
230 GraphReducer graph_reducer(t.graph(), t.main_zone()); | 234 GraphReducer graph_reducer(t.graph(), t.main_zone()); |
231 graph_reducer.AddReducer(&spec); | 235 graph_reducer.AddReducer(&spec); |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
294 { | 298 { |
295 FunctionTester T( | 299 FunctionTester T( |
296 "(function() { if (false) { var x = 1; } function inc(a)" | 300 "(function() { if (false) { var x = 1; } function inc(a)" |
297 " { return a + x; } return inc; })()"); // x is undefined! | 301 " { return a + x; } return inc; })()"); // x is undefined! |
298 | 302 |
299 CHECK(T.Call(T.Val(0.0), T.Val(0.0)).ToHandleChecked()->IsNaN()); | 303 CHECK(T.Call(T.Val(0.0), T.Val(0.0)).ToHandleChecked()->IsNaN()); |
300 CHECK(T.Call(T.Val(2.0), T.Val(0.0)).ToHandleChecked()->IsNaN()); | 304 CHECK(T.Call(T.Val(2.0), T.Val(0.0)).ToHandleChecked()->IsNaN()); |
301 CHECK(T.Call(T.Val(-2.1), T.Val(0.0)).ToHandleChecked()->IsNaN()); | 305 CHECK(T.Call(T.Val(-2.1), T.Val(0.0)).ToHandleChecked()->IsNaN()); |
302 } | 306 } |
303 } | 307 } |
OLD | NEW |