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" 6 #include "src/compiler/js-graph.h" 7 #include "src/compiler/js-operator.h" 8 #include "src/compiler/node-matchers.h" 9 #include "src/compiler/node-properties.h" 10 #include "src/compiler/source-position.h" 11 #include "test/cctest/cctest.h" 12 #include "test/cctest/compiler/function-tester.h" 13 #include "test/cctest/compiler/graph-builder-tester.h" 14 15 namespace v8 { 16 namespace internal { 17 namespace compiler { 18 19 class ContextSpecializationTester : public HandleAndZoneScope { 20 public: 21 ContextSpecializationTester() 22 : graph_(new (main_zone()) Graph(main_zone())), 23 common_(main_zone()), 24 javascript_(main_zone()), 25 machine_(main_zone()), 26 simplified_(main_zone()), 27 jsgraph_(main_isolate(), graph(), common(), &javascript_, &simplified_, 28 &machine_), 29 reducer_(main_zone(), graph()), 30 spec_(&reducer_, jsgraph(), MaybeHandle<Context>()) {} 31 32 JSContextSpecialization* spec() { return &spec_; } 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_; } 38 Graph* graph() { return graph_; } 39 40 private: 41 Graph* graph_; 42 CommonOperatorBuilder common_; 43 JSOperatorBuilder javascript_; 44 MachineOperatorBuilder machine_; 45 SimplifiedOperatorBuilder simplified_; 46 JSGraph jsgraph_; 47 GraphReducer reducer_; 48 JSContextSpecialization spec_; 49 }; 50 51 52 TEST(ReduceJSLoadContext) { 53 ContextSpecializationTester t; 54 55 Node* start = t.graph()->NewNode(t.common()->Start(0)); 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!"); 65 const int slot = Context::NATIVE_CONTEXT_INDEX; 66 native->set(slot, *expected); 67 68 Node* const_context = t.jsgraph()->Constant(native); 69 Node* deep_const_context = t.jsgraph()->Constant(subcontext2); 70 Node* param_context = t.graph()->NewNode(t.common()->Parameter(0), start); 71 72 { 73 // Mutable slot, constant context, depth = 0 => do nothing. 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); 77 CHECK(!r.Changed()); 78 } 79 80 { 81 // Mutable slot, non-constant context, depth = 0 => do nothing. 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); 85 CHECK(!r.Changed()); 86 } 87 88 { 89 // Mutable slot, constant context, depth > 0 => fold-in parent context. 90 Node* load = t.graph()->NewNode( 91 t.javascript()->LoadContext(2, Context::GLOBAL_EVAL_FUN_INDEX, false), 92 deep_const_context, deep_const_context, start); 93 Reduction r = t.spec()->Reduce(load); 94 CHECK(r.Changed()); 95 Node* new_context_input = NodeProperties::GetValueInput(r.replacement(), 0); 96 CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode()); 97 HeapObjectMatcher match(new_context_input); 98 CHECK_EQ(*native, *match.Value()); 99 ContextAccess access = OpParameter<ContextAccess>(r.replacement()); 100 CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index())); 101 CHECK_EQ(0, static_cast<int>(access.depth())); 102 CHECK_EQ(false, access.immutable()); 103 } 104 105 { 106 // Immutable slot, constant context, depth = 0 => specialize. 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); 110 CHECK(r.Changed()); 111 CHECK(r.replacement() != load); 112 113 HeapObjectMatcher match(r.replacement()); 114 CHECK(match.HasValue()); 115 CHECK_EQ(*expected, *match.Value()); 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 126 Node* start = t.graph()->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::NATIVE_CONTEXT_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.graph()->NewNode(t.common()->Parameter(0), start); 142 143 { 144 // Mutable slot, constant context, depth = 0 => do nothing. 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); 149 CHECK(!r.Changed()); 150 } 151 152 { 153 // Mutable slot, non-constant context, depth = 0 => do nothing. 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); 158 CHECK(!r.Changed()); 159 } 160 161 { 162 // Immutable slot, constant context, depth = 0 => do nothing. 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); 167 CHECK(!r.Changed()); 168 } 169 170 { 171 // Mutable slot, constant context, depth > 0 => fold-in parent context. 172 Node* load = t.graph()->NewNode( 173 t.javascript()->StoreContext(2, Context::GLOBAL_EVAL_FUN_INDEX), 174 deep_const_context, deep_const_context, const_context, start, start); 175 Reduction r = t.spec()->Reduce(load); 176 CHECK(r.Changed()); 177 Node* new_context_input = NodeProperties::GetValueInput(r.replacement(), 0); 178 CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode()); 179 HeapObjectMatcher match(new_context_input); 180 CHECK_EQ(*native, *match.Value()); 181 ContextAccess access = OpParameter<ContextAccess>(r.replacement()); 182 CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index())); 183 CHECK_EQ(0, static_cast<int>(access.depth())); 184 CHECK_EQ(false, access.immutable()); 185 } 186 } 187 188 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! 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)); 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 } 248 249 } // namespace compiler 250 } // namespace internal 251 } // namespace v8 252