Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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 |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "src/compiler/js-context-specialization.h" |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 6 | #include "src/compiler/js-graph.h" |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 7 | #include "src/compiler/js-operator.h" |
| 8 | #include "src/compiler/node-matchers.h" |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 9 | #include "src/compiler/node-properties.h" |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 10 | #include "src/compiler/source-position.h" |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 11 | #include "test/cctest/cctest.h" |
| 12 | #include "test/cctest/compiler/function-tester.h" |
| 13 | #include "test/cctest/compiler/graph-builder-tester.h" |
| 14 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 15 | namespace v8 { |
| 16 | namespace internal { |
| 17 | namespace compiler { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 18 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 19 | class ContextSpecializationTester : public HandleAndZoneScope { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 20 | public: |
| 21 | ContextSpecializationTester() |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 22 | : graph_(new (main_zone()) Graph(main_zone())), |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 23 | common_(main_zone()), |
| 24 | javascript_(main_zone()), |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 25 | machine_(main_zone()), |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 26 | simplified_(main_zone()), |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 27 | jsgraph_(main_isolate(), graph(), common(), &javascript_, &simplified_, |
| 28 | &machine_), |
| 29 | reducer_(main_zone(), graph()), |
| 30 | spec_(&reducer_, jsgraph(), MaybeHandle<Context>()) {} |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 31 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 32 | JSContextSpecialization* spec() { return &spec_; } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 33 | Factory* factory() { return main_isolate()->factory(); } |
| 34 | CommonOperatorBuilder* common() { return &common_; } |
| 35 | JSOperatorBuilder* javascript() { return &javascript_; } |
| 36 | SimplifiedOperatorBuilder* simplified() { return &simplified_; } |
| 37 | JSGraph* jsgraph() { return &jsgraph_; } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 38 | Graph* graph() { return graph_; } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 39 | |
| 40 | private: |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 41 | Graph* graph_; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 42 | CommonOperatorBuilder common_; |
| 43 | JSOperatorBuilder javascript_; |
| 44 | MachineOperatorBuilder machine_; |
| 45 | SimplifiedOperatorBuilder simplified_; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 46 | JSGraph jsgraph_; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 47 | GraphReducer reducer_; |
| 48 | JSContextSpecialization spec_; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 49 | }; |
| 50 | |
| 51 | |
| 52 | TEST(ReduceJSLoadContext) { |
| 53 | ContextSpecializationTester t; |
| 54 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 55 | Node* start = t.graph()->NewNode(t.common()->Start(0)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 56 | t.graph()->SetStart(start); |
| 57 | |
| 58 | // Make a context and initialize it a bit for this test. |
| 59 | Handle<Context> native = t.factory()->NewNativeContext(); |
| 60 | Handle<Context> subcontext1 = t.factory()->NewNativeContext(); |
| 61 | Handle<Context> subcontext2 = t.factory()->NewNativeContext(); |
| 62 | subcontext2->set_previous(*subcontext1); |
| 63 | subcontext1->set_previous(*native); |
| 64 | Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!"); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 65 | const int slot = Context::NATIVE_CONTEXT_INDEX; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 66 | native->set(slot, *expected); |
| 67 | |
| 68 | Node* const_context = t.jsgraph()->Constant(native); |
| 69 | Node* deep_const_context = t.jsgraph()->Constant(subcontext2); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 70 | Node* param_context = t.graph()->NewNode(t.common()->Parameter(0), start); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 71 | |
| 72 | { |
| 73 | // Mutable slot, constant context, depth = 0 => do nothing. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 74 | Node* load = t.graph()->NewNode(t.javascript()->LoadContext(0, 0, false), |
| 75 | const_context, const_context, start); |
| 76 | Reduction r = t.spec()->Reduce(load); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 77 | CHECK(!r.Changed()); |
| 78 | } |
| 79 | |
| 80 | { |
| 81 | // Mutable slot, non-constant context, depth = 0 => do nothing. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 82 | Node* load = t.graph()->NewNode(t.javascript()->LoadContext(0, 0, false), |
| 83 | param_context, param_context, start); |
| 84 | Reduction r = t.spec()->Reduce(load); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 85 | CHECK(!r.Changed()); |
| 86 | } |
| 87 | |
| 88 | { |
| 89 | // Mutable slot, constant context, depth > 0 => fold-in parent context. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 90 | Node* load = t.graph()->NewNode( |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 91 | t.javascript()->LoadContext(2, Context::GLOBAL_EVAL_FUN_INDEX, false), |
| 92 | deep_const_context, deep_const_context, start); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 93 | Reduction r = t.spec()->Reduce(load); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 94 | CHECK(r.Changed()); |
| 95 | Node* new_context_input = NodeProperties::GetValueInput(r.replacement(), 0); |
| 96 | CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 97 | HeapObjectMatcher match(new_context_input); |
| 98 | CHECK_EQ(*native, *match.Value()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 99 | ContextAccess access = OpParameter<ContextAccess>(r.replacement()); |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 100 | CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index())); |
| 101 | CHECK_EQ(0, static_cast<int>(access.depth())); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 102 | CHECK_EQ(false, access.immutable()); |
| 103 | } |
| 104 | |
| 105 | { |
| 106 | // Immutable slot, constant context, depth = 0 => specialize. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 107 | Node* load = t.graph()->NewNode(t.javascript()->LoadContext(0, slot, true), |
| 108 | const_context, const_context, start); |
| 109 | Reduction r = t.spec()->Reduce(load); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 110 | CHECK(r.Changed()); |
| 111 | CHECK(r.replacement() != load); |
| 112 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 113 | HeapObjectMatcher match(r.replacement()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 114 | CHECK(match.HasValue()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 115 | CHECK_EQ(*expected, *match.Value()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 116 | } |
| 117 | |
| 118 | // TODO(titzer): test with other kinds of contexts, e.g. a function context. |
| 119 | // TODO(sigurds): test that loads below create context are not optimized |
| 120 | } |
| 121 | |
| 122 | |
| 123 | TEST(ReduceJSStoreContext) { |
| 124 | ContextSpecializationTester t; |
| 125 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 126 | Node* start = t.graph()->NewNode(t.common()->Start(0)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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!"); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 136 | const int slot = Context::NATIVE_CONTEXT_INDEX; |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 137 | native->set(slot, *expected); |
| 138 | |
| 139 | Node* const_context = t.jsgraph()->Constant(native); |
| 140 | Node* deep_const_context = t.jsgraph()->Constant(subcontext2); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 141 | Node* param_context = t.graph()->NewNode(t.common()->Parameter(0), start); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 142 | |
| 143 | { |
| 144 | // Mutable slot, constant context, depth = 0 => do nothing. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 145 | Node* load = |
| 146 | t.graph()->NewNode(t.javascript()->StoreContext(0, 0), const_context, |
| 147 | const_context, const_context, start, start); |
| 148 | Reduction r = t.spec()->Reduce(load); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 149 | CHECK(!r.Changed()); |
| 150 | } |
| 151 | |
| 152 | { |
| 153 | // Mutable slot, non-constant context, depth = 0 => do nothing. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 154 | Node* load = |
| 155 | t.graph()->NewNode(t.javascript()->StoreContext(0, 0), param_context, |
| 156 | param_context, const_context, start, start); |
| 157 | Reduction r = t.spec()->Reduce(load); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 158 | CHECK(!r.Changed()); |
| 159 | } |
| 160 | |
| 161 | { |
| 162 | // Immutable slot, constant context, depth = 0 => do nothing. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 163 | Node* load = |
| 164 | t.graph()->NewNode(t.javascript()->StoreContext(0, slot), const_context, |
| 165 | const_context, const_context, start, start); |
| 166 | Reduction r = t.spec()->Reduce(load); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 167 | CHECK(!r.Changed()); |
| 168 | } |
| 169 | |
| 170 | { |
| 171 | // Mutable slot, constant context, depth > 0 => fold-in parent context. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 172 | Node* load = t.graph()->NewNode( |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 173 | t.javascript()->StoreContext(2, Context::GLOBAL_EVAL_FUN_INDEX), |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 174 | deep_const_context, deep_const_context, const_context, start, start); |
| 175 | Reduction r = t.spec()->Reduce(load); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 176 | CHECK(r.Changed()); |
| 177 | Node* new_context_input = NodeProperties::GetValueInput(r.replacement(), 0); |
| 178 | CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 179 | HeapObjectMatcher match(new_context_input); |
| 180 | CHECK_EQ(*native, *match.Value()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 181 | ContextAccess access = OpParameter<ContextAccess>(r.replacement()); |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 182 | CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index())); |
| 183 | CHECK_EQ(0, static_cast<int>(access.depth())); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 184 | CHECK_EQ(false, access.immutable()); |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 189 | TEST(SpecializeJSFunction_ToConstant1) { |
| 190 | FunctionTester T( |
| 191 | "(function() { var x = 1; function inc(a)" |
| 192 | " { return a + x; } return inc; })()"); |
| 193 | |
| 194 | T.CheckCall(1.0, 0.0, 0.0); |
| 195 | T.CheckCall(2.0, 1.0, 0.0); |
| 196 | T.CheckCall(2.1, 1.1, 0.0); |
| 197 | } |
| 198 | |
| 199 | |
| 200 | TEST(SpecializeJSFunction_ToConstant2) { |
| 201 | FunctionTester T( |
| 202 | "(function() { var x = 1.5; var y = 2.25; var z = 3.75;" |
| 203 | " function f(a) { return a - x + y - z; } return f; })()"); |
| 204 | |
| 205 | T.CheckCall(-3.0, 0.0, 0.0); |
| 206 | T.CheckCall(-2.0, 1.0, 0.0); |
| 207 | T.CheckCall(-1.9, 1.1, 0.0); |
| 208 | } |
| 209 | |
| 210 | |
| 211 | TEST(SpecializeJSFunction_ToConstant3) { |
| 212 | FunctionTester T( |
| 213 | "(function() { var x = -11.5; function inc()" |
| 214 | " { return (function(a) { return a + x; }); }" |
| 215 | " return inc(); })()"); |
| 216 | |
| 217 | T.CheckCall(-11.5, 0.0, 0.0); |
| 218 | T.CheckCall(-10.5, 1.0, 0.0); |
| 219 | T.CheckCall(-10.4, 1.1, 0.0); |
| 220 | } |
| 221 | |
| 222 | |
| 223 | TEST(SpecializeJSFunction_ToConstant_uninit) { |
| 224 | { |
| 225 | FunctionTester T( |
| 226 | "(function() { if (false) { var x = 1; } function inc(a)" |
| 227 | " { return x; } return inc; })()"); // x is undefined! |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 228 | i::Isolate* isolate = CcTest::i_isolate(); |
| 229 | CHECK( |
| 230 | T.Call(T.Val(0.0), T.Val(0.0)).ToHandleChecked()->IsUndefined(isolate)); |
| 231 | CHECK( |
| 232 | T.Call(T.Val(2.0), T.Val(0.0)).ToHandleChecked()->IsUndefined(isolate)); |
| 233 | CHECK(T.Call(T.Val(-2.1), T.Val(0.0)) |
| 234 | .ToHandleChecked() |
| 235 | ->IsUndefined(isolate)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 236 | } |
| 237 | |
| 238 | { |
| 239 | FunctionTester T( |
| 240 | "(function() { if (false) { var x = 1; } function inc(a)" |
| 241 | " { return a + x; } return inc; })()"); // x is undefined! |
| 242 | |
| 243 | CHECK(T.Call(T.Val(0.0), T.Val(0.0)).ToHandleChecked()->IsNaN()); |
| 244 | CHECK(T.Call(T.Val(2.0), T.Val(0.0)).ToHandleChecked()->IsNaN()); |
| 245 | CHECK(T.Call(T.Val(-2.1), T.Val(0.0)).ToHandleChecked()->IsNaN()); |
| 246 | } |
| 247 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 248 | |
| 249 | } // namespace compiler |
| 250 | } // namespace internal |
| 251 | } // namespace v8 |