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-inl.h" | 8 #include "src/compiler/node-properties-inl.h" |
9 #include "src/compiler/simplified-node-factory.h" | 9 #include "src/compiler/simplified-node-factory.h" |
10 #include "src/compiler/source-position.h" | 10 #include "src/compiler/source-position.h" |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
48 | 48 |
49 | 49 |
50 TEST(ReduceJSLoadContext) { | 50 TEST(ReduceJSLoadContext) { |
51 ContextSpecializationTester t; | 51 ContextSpecializationTester t; |
52 | 52 |
53 Node* start = t.NewNode(t.common()->Start(0)); | 53 Node* start = t.NewNode(t.common()->Start(0)); |
54 t.graph()->SetStart(start); | 54 t.graph()->SetStart(start); |
55 | 55 |
56 // Make a context and initialize it a bit for this test. | 56 // Make a context and initialize it a bit for this test. |
57 Handle<Context> native = t.factory()->NewNativeContext(); | 57 Handle<Context> native = t.factory()->NewNativeContext(); |
58 Handle<Context> ctx1 = t.factory()->NewNativeContext(); | 58 Handle<Context> subcontext1 = t.factory()->NewNativeContext(); |
59 Handle<Context> ctx2 = t.factory()->NewNativeContext(); | 59 Handle<Context> subcontext2 = t.factory()->NewNativeContext(); |
60 ctx2->set_previous(*ctx1); | 60 subcontext2->set_previous(*subcontext1); |
61 ctx1->set_previous(*native); | 61 subcontext1->set_previous(*native); |
62 Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!"); | 62 Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!"); |
63 const int slot = Context::GLOBAL_OBJECT_INDEX; | 63 const int slot = Context::GLOBAL_OBJECT_INDEX; |
64 native->set(slot, *expected); | 64 native->set(slot, *expected); |
65 | 65 |
66 Node* const_context = t.jsgraph()->Constant(native); | 66 Node* const_context = t.jsgraph()->Constant(native); |
| 67 Node* deep_const_context = t.jsgraph()->Constant(subcontext2); |
67 Node* param_context = t.NewNode(t.common()->Parameter(0), start); | 68 Node* param_context = t.NewNode(t.common()->Parameter(0), start); |
68 JSContextSpecializer spec(t.info(), t.jsgraph(), const_context); | 69 JSContextSpecializer spec(t.info(), t.jsgraph(), const_context); |
69 | 70 |
70 { | 71 { |
71 // Mutable slot, constant context, depth = 0 => do nothing. | 72 // Mutable slot, constant context, depth = 0 => do nothing. |
72 t.info()->SetContext(native); | |
73 Node* load = t.NewNode(t.javascript()->LoadContext(0, 0, false), | 73 Node* load = t.NewNode(t.javascript()->LoadContext(0, 0, false), |
74 const_context, start, start); | 74 const_context, const_context, start); |
75 Reduction r = spec.ReduceJSLoadContext(load); | 75 Reduction r = spec.ReduceJSLoadContext(load); |
76 CHECK(!r.Changed()); | 76 CHECK(!r.Changed()); |
77 } | 77 } |
78 | 78 |
79 { | 79 { |
80 // Mutable slot, non-constant context, depth = 0 => do nothing. | 80 // Mutable slot, non-constant context, depth = 0 => do nothing. |
81 t.info()->SetContext(native); | |
82 Node* load = t.NewNode(t.javascript()->LoadContext(0, 0, false), | 81 Node* load = t.NewNode(t.javascript()->LoadContext(0, 0, false), |
83 param_context, start, start); | 82 param_context, param_context, start); |
84 Reduction r = spec.ReduceJSLoadContext(load); | 83 Reduction r = spec.ReduceJSLoadContext(load); |
85 CHECK(!r.Changed()); | 84 CHECK(!r.Changed()); |
86 } | 85 } |
87 | 86 |
88 { | 87 { |
89 // Mutable slot, non-constant context, depth > 0 => fold-in parent context. | 88 // Mutable slot, constant context, depth > 0 => fold-in parent context. |
90 t.info()->SetContext(ctx2); | |
91 Node* load = t.NewNode( | 89 Node* load = t.NewNode( |
92 t.javascript()->LoadContext(2, Context::GLOBAL_EVAL_FUN_INDEX, false), | 90 t.javascript()->LoadContext(2, Context::GLOBAL_EVAL_FUN_INDEX, false), |
93 param_context, start, start); | 91 deep_const_context, deep_const_context, start); |
94 Reduction r = spec.ReduceJSLoadContext(load); | 92 Reduction r = spec.ReduceJSLoadContext(load); |
95 CHECK(r.Changed()); | 93 CHECK(r.Changed()); |
96 CHECK_EQ(IrOpcode::kHeapConstant, r.replacement()->InputAt(0)->opcode()); | 94 Node* new_context_input = NodeProperties::GetValueInput(r.replacement(), 0); |
97 ValueMatcher<Handle<Context> > match(r.replacement()->InputAt(0)); | 95 CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode()); |
| 96 ValueMatcher<Handle<Context> > match(new_context_input); |
98 CHECK_EQ(*native, *match.Value()); | 97 CHECK_EQ(*native, *match.Value()); |
99 ContextAccess access = static_cast<Operator1<ContextAccess>*>( | 98 ContextAccess access = static_cast<Operator1<ContextAccess>*>( |
100 r.replacement()->op())->parameter(); | 99 r.replacement()->op())->parameter(); |
101 CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, access.index()); | 100 CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, access.index()); |
102 CHECK_EQ(0, access.depth()); | 101 CHECK_EQ(0, access.depth()); |
103 CHECK_EQ(false, access.immutable()); | 102 CHECK_EQ(false, access.immutable()); |
104 } | 103 } |
105 | 104 |
106 { | 105 { |
107 // Immutable slot, constant context => specialize. | 106 // Immutable slot, constant context, depth = 0 => specialize. |
108 t.info()->SetContext(native); | |
109 Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true), | 107 Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true), |
110 const_context, start, start); | 108 const_context, const_context, start); |
111 Reduction r = spec.ReduceJSLoadContext(load); | 109 Reduction r = spec.ReduceJSLoadContext(load); |
112 CHECK(r.Changed()); | 110 CHECK(r.Changed()); |
113 CHECK(r.replacement() != load); | 111 CHECK(r.replacement() != load); |
114 | 112 |
115 ValueMatcher<Handle<Object> > match(r.replacement()); | 113 ValueMatcher<Handle<Object> > match(r.replacement()); |
116 CHECK(match.HasValue()); | 114 CHECK(match.HasValue()); |
117 CHECK_EQ(*expected, *match.Value()); | 115 CHECK_EQ(*expected, *match.Value()); |
118 } | 116 } |
119 | 117 |
120 { | |
121 // Immutable slot, non-constant context => specialize. | |
122 t.info()->SetContext(native); | |
123 Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true), | |
124 param_context, start, start); | |
125 Reduction r = spec.ReduceJSLoadContext(load); | |
126 CHECK(r.Changed()); | |
127 CHECK(r.replacement() != load); | |
128 | |
129 ValueMatcher<Handle<Object> > match(r.replacement()); | |
130 CHECK(match.HasValue()); | |
131 CHECK_EQ(*expected, *match.Value()); | |
132 } | |
133 | |
134 // TODO(titzer): test with other kinds of contexts, e.g. a function context. | 118 // TODO(titzer): test with other kinds of contexts, e.g. a function context. |
135 // TODO(sigurds): test that loads below create context are not optimized | 119 // TODO(sigurds): test that loads below create context are not optimized |
136 } | 120 } |
137 | 121 |
138 | 122 |
| 123 TEST(ReduceJSStoreContext) { |
| 124 ContextSpecializationTester t; |
| 125 |
| 126 Node* start = t.NewNode(t.common()->Start(0)); |
| 127 t.graph()->SetStart(start); |
| 128 |
| 129 // Make a context and initialize it a bit for this test. |
| 130 Handle<Context> native = t.factory()->NewNativeContext(); |
| 131 Handle<Context> subcontext1 = t.factory()->NewNativeContext(); |
| 132 Handle<Context> subcontext2 = t.factory()->NewNativeContext(); |
| 133 subcontext2->set_previous(*subcontext1); |
| 134 subcontext1->set_previous(*native); |
| 135 Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!"); |
| 136 const int slot = Context::GLOBAL_OBJECT_INDEX; |
| 137 native->set(slot, *expected); |
| 138 |
| 139 Node* const_context = t.jsgraph()->Constant(native); |
| 140 Node* deep_const_context = t.jsgraph()->Constant(subcontext2); |
| 141 Node* param_context = t.NewNode(t.common()->Parameter(0), start); |
| 142 JSContextSpecializer spec(t.info(), t.jsgraph(), const_context); |
| 143 |
| 144 { |
| 145 // Mutable slot, constant context, depth = 0 => do nothing. |
| 146 Node* load = t.NewNode(t.javascript()->StoreContext(0, 0), const_context, |
| 147 const_context, start); |
| 148 Reduction r = spec.ReduceJSStoreContext(load); |
| 149 CHECK(!r.Changed()); |
| 150 } |
| 151 |
| 152 { |
| 153 // Mutable slot, non-constant context, depth = 0 => do nothing. |
| 154 Node* load = t.NewNode(t.javascript()->StoreContext(0, 0), param_context, |
| 155 param_context, start); |
| 156 Reduction r = spec.ReduceJSStoreContext(load); |
| 157 CHECK(!r.Changed()); |
| 158 } |
| 159 |
| 160 { |
| 161 // Immutable slot, constant context, depth = 0 => do nothing. |
| 162 Node* load = t.NewNode(t.javascript()->StoreContext(0, slot), const_context, |
| 163 const_context, start); |
| 164 Reduction r = spec.ReduceJSStoreContext(load); |
| 165 CHECK(!r.Changed()); |
| 166 } |
| 167 |
| 168 { |
| 169 // Mutable slot, constant context, depth > 0 => fold-in parent context. |
| 170 Node* load = t.NewNode( |
| 171 t.javascript()->StoreContext(2, Context::GLOBAL_EVAL_FUN_INDEX), |
| 172 deep_const_context, deep_const_context, start); |
| 173 Reduction r = spec.ReduceJSStoreContext(load); |
| 174 CHECK(r.Changed()); |
| 175 Node* new_context_input = NodeProperties::GetValueInput(r.replacement(), 0); |
| 176 CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode()); |
| 177 ValueMatcher<Handle<Context> > match(new_context_input); |
| 178 CHECK_EQ(*native, *match.Value()); |
| 179 ContextAccess access = static_cast<Operator1<ContextAccess>*>( |
| 180 r.replacement()->op())->parameter(); |
| 181 CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, access.index()); |
| 182 CHECK_EQ(0, access.depth()); |
| 183 CHECK_EQ(false, access.immutable()); |
| 184 } |
| 185 } |
| 186 |
| 187 |
139 // TODO(titzer): factor out common code with effects checking in typed lowering. | 188 // TODO(titzer): factor out common code with effects checking in typed lowering. |
140 static void CheckEffectInput(Node* effect, Node* use) { | 189 static void CheckEffectInput(Node* effect, Node* use) { |
141 CHECK_EQ(effect, NodeProperties::GetEffectInput(use)); | 190 CHECK_EQ(effect, NodeProperties::GetEffectInput(use)); |
142 } | 191 } |
143 | 192 |
144 | 193 |
145 TEST(SpecializeToContext) { | 194 TEST(SpecializeToContext) { |
146 ContextSpecializationTester t; | 195 ContextSpecializationTester t; |
147 | 196 |
148 Node* start = t.NewNode(t.common()->Start(0)); | 197 Node* start = t.NewNode(t.common()->Start(0)); |
149 t.graph()->SetStart(start); | 198 t.graph()->SetStart(start); |
150 | 199 |
151 // Make a context and initialize it a bit for this test. | 200 // Make a context and initialize it a bit for this test. |
152 Handle<Context> native = t.factory()->NewNativeContext(); | 201 Handle<Context> native = t.factory()->NewNativeContext(); |
153 Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!"); | 202 Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!"); |
154 const int slot = Context::GLOBAL_OBJECT_INDEX; | 203 const int slot = Context::GLOBAL_OBJECT_INDEX; |
155 native->set(slot, *expected); | 204 native->set(slot, *expected); |
156 t.info()->SetContext(native); | 205 t.info()->SetContext(native); |
157 | 206 |
158 Node* const_context = t.jsgraph()->Constant(native); | 207 Node* const_context = t.jsgraph()->Constant(native); |
159 Node* param_context = t.NewNode(t.common()->Parameter(0), start); | 208 Node* param_context = t.NewNode(t.common()->Parameter(0), start); |
160 JSContextSpecializer spec(t.info(), t.jsgraph(), const_context); | 209 JSContextSpecializer spec(t.info(), t.jsgraph(), const_context); |
161 | 210 |
162 { | 211 { |
163 // Check that SpecializeToContext() replaces values and forwards effects | 212 // Check that SpecializeToContext() replaces values and forwards effects |
164 // correctly, and folds values from constant and non-constant contexts | 213 // correctly, and folds values from constant and non-constant contexts |
165 Node* effect_in = t.NewNode(t.common()->Start(0)); | 214 Node* effect_in = start; |
166 Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true), | 215 Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true), |
167 const_context, const_context, effect_in, start); | 216 const_context, const_context, effect_in); |
168 | 217 |
169 | 218 |
170 Node* value_use = t.ChangeTaggedToInt32(load); | 219 Node* value_use = t.ChangeTaggedToInt32(load); |
171 Node* other_load = t.NewNode(t.javascript()->LoadContext(0, slot, true), | 220 Node* other_load = t.NewNode(t.javascript()->LoadContext(0, slot, true), |
172 param_context, param_context, load, start); | 221 param_context, param_context, load); |
173 Node* effect_use = other_load; | 222 Node* effect_use = other_load; |
174 Node* other_use = t.ChangeTaggedToInt32(other_load); | 223 Node* other_use = t.ChangeTaggedToInt32(other_load); |
175 | 224 |
| 225 Node* add = t.NewNode(t.javascript()->Add(), value_use, other_use, |
| 226 param_context, other_load, start); |
| 227 |
| 228 Node* ret = t.NewNode(t.common()->Return(), add, effect_use, start); |
| 229 Node* end = t.NewNode(t.common()->End(), ret); |
| 230 USE(end); |
| 231 t.graph()->SetEnd(end); |
| 232 |
176 // Double check the above graph is what we expect, or the test is broken. | 233 // Double check the above graph is what we expect, or the test is broken. |
177 CheckEffectInput(effect_in, load); | 234 CheckEffectInput(effect_in, load); |
178 CheckEffectInput(load, effect_use); | 235 CheckEffectInput(load, effect_use); |
179 | 236 |
180 // Perform the substitution on the entire graph. | 237 // Perform the substitution on the entire graph. |
181 spec.SpecializeToContext(); | 238 spec.SpecializeToContext(); |
182 | 239 |
183 // Effects should have been forwarded (not replaced with a value). | 240 // Effects should have been forwarded (not replaced with a value). |
184 CheckEffectInput(effect_in, effect_use); | 241 CheckEffectInput(effect_in, effect_use); |
185 | 242 |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
243 { | 300 { |
244 FunctionTester T( | 301 FunctionTester T( |
245 "(function() { if (false) { var x = 1; } function inc(a)" | 302 "(function() { if (false) { var x = 1; } function inc(a)" |
246 " { return a + x; } return inc; })()"); // x is undefined! | 303 " { return a + x; } return inc; })()"); // x is undefined! |
247 | 304 |
248 CHECK(T.Call(T.Val(0.0), T.Val(0.0)).ToHandleChecked()->IsNaN()); | 305 CHECK(T.Call(T.Val(0.0), T.Val(0.0)).ToHandleChecked()->IsNaN()); |
249 CHECK(T.Call(T.Val(2.0), T.Val(0.0)).ToHandleChecked()->IsNaN()); | 306 CHECK(T.Call(T.Val(2.0), T.Val(0.0)).ToHandleChecked()->IsNaN()); |
250 CHECK(T.Call(T.Val(-2.1), T.Val(0.0)).ToHandleChecked()->IsNaN()); | 307 CHECK(T.Call(T.Val(-2.1), T.Val(0.0)).ToHandleChecked()->IsNaN()); |
251 } | 308 } |
252 } | 309 } |
OLD | NEW |