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/code-factory.h" 6 #include "src/code-stubs.h" 7 #include "src/compiler/common-operator.h" 8 #include "src/compiler/graph-inl.h" 9 #include "src/compiler/js-generic-lowering.h" 10 #include "src/compiler/machine-operator.h" 11 #include "src/compiler/node-aux-data-inl.h" 12 #include "src/compiler/node-properties-inl.h" 13 #include "src/unique.h" 14 15 namespace v8 { 16 namespace internal { 17 namespace compiler { 18 19 JSGenericLowering::JSGenericLowering(CompilationInfo* info, JSGraph* jsgraph) 20 : info_(info), 21 jsgraph_(jsgraph), 22 linkage_(new (jsgraph->zone()) Linkage(info)) {} 23 24 25 void JSGenericLowering::PatchOperator(Node* node, const Operator* op) { 26 node->set_op(op); 27 } 28 29 30 void JSGenericLowering::PatchInsertInput(Node* node, int index, Node* input) { 31 node->InsertInput(zone(), index, input); 32 } 33 34 35 Node* JSGenericLowering::SmiConstant(int32_t immediate) { 36 return jsgraph()->SmiConstant(immediate); 37 } 38 39 40 Node* JSGenericLowering::Int32Constant(int immediate) { 41 return jsgraph()->Int32Constant(immediate); 42 } 43 44 45 Node* JSGenericLowering::CodeConstant(Handle<Code> code) { 46 return jsgraph()->HeapConstant(code); 47 } 48 49 50 Node* JSGenericLowering::FunctionConstant(Handle<JSFunction> function) { 51 return jsgraph()->HeapConstant(function); 52 } 53 54 55 Node* JSGenericLowering::ExternalConstant(ExternalReference ref) { 56 return jsgraph()->ExternalConstant(ref); 57 } 58 59 60 Reduction JSGenericLowering::Reduce(Node* node) { 61 switch (node->opcode()) { 62 #define DECLARE_CASE(x) \ 63 case IrOpcode::k##x: \ 64 Lower##x(node); \ 65 break; 66 DECLARE_CASE(Branch) 67 JS_OP_LIST(DECLARE_CASE) 68 #undef DECLARE_CASE 69 default: 70 // Nothing to see. 71 return NoChange(); 72 } 73 return Changed(node); 74 } 75 76 77 #define REPLACE_BINARY_OP_IC_CALL(op, token) \ 78 void JSGenericLowering::Lower##op(Node* node) { \ 79 ReplaceWithStubCall(node, CodeFactory::BinaryOpIC(isolate(), token), \ 80 CallDescriptor::kPatchableCallSiteWithNop); \ 81 } 82 REPLACE_BINARY_OP_IC_CALL(JSBitwiseOr, Token::BIT_OR) 83 REPLACE_BINARY_OP_IC_CALL(JSBitwiseXor, Token::BIT_XOR) 84 REPLACE_BINARY_OP_IC_CALL(JSBitwiseAnd, Token::BIT_AND) 85 REPLACE_BINARY_OP_IC_CALL(JSShiftLeft, Token::SHL) 86 REPLACE_BINARY_OP_IC_CALL(JSShiftRight, Token::SAR) 87 REPLACE_BINARY_OP_IC_CALL(JSShiftRightLogical, Token::SHR) 88 REPLACE_BINARY_OP_IC_CALL(JSAdd, Token::ADD) 89 REPLACE_BINARY_OP_IC_CALL(JSSubtract, Token::SUB) 90 REPLACE_BINARY_OP_IC_CALL(JSMultiply, Token::MUL) 91 REPLACE_BINARY_OP_IC_CALL(JSDivide, Token::DIV) 92 REPLACE_BINARY_OP_IC_CALL(JSModulus, Token::MOD) 93 #undef REPLACE_BINARY_OP_IC_CALL 94 95 96 #define REPLACE_COMPARE_IC_CALL(op, token, pure) \ 97 void JSGenericLowering::Lower##op(Node* node) { \ 98 ReplaceWithCompareIC(node, token, pure); \ 99 } 100 REPLACE_COMPARE_IC_CALL(JSEqual, Token::EQ, false) 101 REPLACE_COMPARE_IC_CALL(JSNotEqual, Token::NE, false) 102 REPLACE_COMPARE_IC_CALL(JSStrictEqual, Token::EQ_STRICT, true) 103 REPLACE_COMPARE_IC_CALL(JSStrictNotEqual, Token::NE_STRICT, true) 104 REPLACE_COMPARE_IC_CALL(JSLessThan, Token::LT, false) 105 REPLACE_COMPARE_IC_CALL(JSGreaterThan, Token::GT, false) 106 REPLACE_COMPARE_IC_CALL(JSLessThanOrEqual, Token::LTE, false) 107 REPLACE_COMPARE_IC_CALL(JSGreaterThanOrEqual, Token::GTE, false) 108 #undef REPLACE_COMPARE_IC_CALL 109 110 111 #define REPLACE_RUNTIME_CALL(op, fun) \ 112 void JSGenericLowering::Lower##op(Node* node) { \ 113 ReplaceWithRuntimeCall(node, fun); \ 114 } 115 REPLACE_RUNTIME_CALL(JSTypeOf, Runtime::kTypeof) 116 REPLACE_RUNTIME_CALL(JSCreate, Runtime::kAbort) 117 REPLACE_RUNTIME_CALL(JSCreateFunctionContext, Runtime::kNewFunctionContext) 118 REPLACE_RUNTIME_CALL(JSCreateCatchContext, Runtime::kPushCatchContext) 119 REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext) 120 REPLACE_RUNTIME_CALL(JSCreateBlockContext, Runtime::kPushBlockContext) 121 REPLACE_RUNTIME_CALL(JSCreateModuleContext, Runtime::kPushModuleContext) 122 REPLACE_RUNTIME_CALL(JSCreateGlobalContext, Runtime::kAbort) 123 #undef REPLACE_RUNTIME 124 125 126 #define REPLACE_UNIMPLEMENTED(op) \ 127 void JSGenericLowering::Lower##op(Node* node) { UNIMPLEMENTED(); } 128 REPLACE_UNIMPLEMENTED(JSToName) 129 REPLACE_UNIMPLEMENTED(JSYield) 130 REPLACE_UNIMPLEMENTED(JSDebugger) 131 #undef REPLACE_UNIMPLEMENTED 132 133 134 static CallDescriptor::Flags FlagsForNode(Node* node) { 135 CallDescriptor::Flags result = CallDescriptor::kNoFlags; 136 if (OperatorProperties::HasFrameStateInput(node->op())) { 137 result |= CallDescriptor::kNeedsFrameState; 138 } 139 return result; 140 } 141 142 143 void JSGenericLowering::ReplaceWithCompareIC(Node* node, Token::Value token, 144 bool pure) { 145 Callable callable = CodeFactory::CompareIC(isolate(), token); 146 bool has_frame_state = OperatorProperties::HasFrameStateInput(node->op()); 147 CallDescriptor* desc_compare = linkage()->GetStubCallDescriptor( 148 callable.descriptor(), 0, 149 CallDescriptor::kPatchableCallSiteWithNop | FlagsForNode(node)); 150 NodeVector inputs(zone()); 151 inputs.reserve(node->InputCount() + 1); 152 inputs.push_back(CodeConstant(callable.code())); 153 inputs.push_back(NodeProperties::GetValueInput(node, 0)); 154 inputs.push_back(NodeProperties::GetValueInput(node, 1)); 155 inputs.push_back(NodeProperties::GetContextInput(node)); 156 if (pure) { 157 // A pure (strict) comparison doesn't have an effect, control or frame 158 // state. But for the graph, we need to add control and effect inputs. 159 DCHECK(!has_frame_state); 160 inputs.push_back(graph()->start()); 161 inputs.push_back(graph()->start()); 162 } else { 163 DCHECK(has_frame_state == FLAG_turbo_deoptimization); 164 if (FLAG_turbo_deoptimization) { 165 inputs.push_back(NodeProperties::GetFrameStateInput(node)); 166 } 167 inputs.push_back(NodeProperties::GetEffectInput(node)); 168 inputs.push_back(NodeProperties::GetControlInput(node)); 169 } 170 Node* compare = 171 graph()->NewNode(common()->Call(desc_compare), 172 static_cast<int>(inputs.size()), &inputs.front()); 173 174 node->ReplaceInput(0, compare); 175 node->ReplaceInput(1, SmiConstant(token)); 176 177 if (has_frame_state) { 178 // Remove the frame state from inputs. 179 node->RemoveInput(NodeProperties::FirstFrameStateIndex(node)); 180 } 181 182 ReplaceWithRuntimeCall(node, Runtime::kBooleanize); 183 } 184 185 186 void JSGenericLowering::ReplaceWithStubCall(Node* node, Callable callable, 187 CallDescriptor::Flags flags) { 188 CallDescriptor* desc = linkage()->GetStubCallDescriptor( 189 callable.descriptor(), 0, flags | FlagsForNode(node)); 190 Node* stub_code = CodeConstant(callable.code()); 191 PatchInsertInput(node, 0, stub_code); 192 PatchOperator(node, common()->Call(desc)); 193 } 194 195 196 void JSGenericLowering::ReplaceWithBuiltinCall(Node* node, 197 Builtins::JavaScript id, 198 int nargs) { 199 Callable callable = 200 CodeFactory::CallFunction(isolate(), nargs - 1, NO_CALL_FUNCTION_FLAGS); 201 CallDescriptor* desc = 202 linkage()->GetStubCallDescriptor(callable.descriptor(), nargs); 203 // TODO(mstarzinger): Accessing the builtins object this way prevents sharing 204 // of code across native contexts. Fix this by loading from given context. 205 Handle<JSFunction> function( 206 JSFunction::cast(info()->context()->builtins()->javascript_builtin(id))); 207 Node* stub_code = CodeConstant(callable.code()); 208 Node* function_node = FunctionConstant(function); 209 PatchInsertInput(node, 0, stub_code); 210 PatchInsertInput(node, 1, function_node); 211 PatchOperator(node, common()->Call(desc)); 212 } 213 214 215 void JSGenericLowering::ReplaceWithRuntimeCall(Node* node, 216 Runtime::FunctionId f, 217 int nargs_override) { 218 Operator::Properties properties = node->op()->properties(); 219 const Runtime::Function* fun = Runtime::FunctionForId(f); 220 int nargs = (nargs_override < 0) ? fun->nargs : nargs_override; 221 CallDescriptor* desc = 222 linkage()->GetRuntimeCallDescriptor(f, nargs, properties); 223 Node* ref = ExternalConstant(ExternalReference(f, isolate())); 224 Node* arity = Int32Constant(nargs); 225 if (!centrystub_constant_.is_set()) { 226 centrystub_constant_.set(CodeConstant(CEntryStub(isolate(), 1).GetCode())); 227 } 228 PatchInsertInput(node, 0, centrystub_constant_.get()); 229 PatchInsertInput(node, nargs + 1, ref); 230 PatchInsertInput(node, nargs + 2, arity); 231 PatchOperator(node, common()->Call(desc)); 232 } 233 234 235 void JSGenericLowering::LowerBranch(Node* node) { 236 if (!info()->is_typing_enabled()) { 237 // TODO(mstarzinger): If typing is enabled then simplified lowering will 238 // have inserted the correct ChangeBoolToBit, otherwise we need to perform 239 // poor-man's representation inference here and insert manual change. 240 Node* test = graph()->NewNode(machine()->WordEqual(), node->InputAt(0), 241 jsgraph()->TrueConstant()); 242 node->ReplaceInput(0, test); 243 } 244 } 245 246 247 void JSGenericLowering::LowerJSUnaryNot(Node* node) { 248 Callable callable = CodeFactory::ToBoolean( 249 isolate(), ToBooleanStub::RESULT_AS_INVERSE_ODDBALL); 250 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite); 251 } 252 253 254 void JSGenericLowering::LowerJSToBoolean(Node* node) { 255 Callable callable = 256 CodeFactory::ToBoolean(isolate(), ToBooleanStub::RESULT_AS_ODDBALL); 257 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite); 258 } 259 260 261 void JSGenericLowering::LowerJSToNumber(Node* node) { 262 Callable callable = CodeFactory::ToNumber(isolate()); 263 ReplaceWithStubCall(node, callable, CallDescriptor::kNoFlags); 264 } 265 266 267 void JSGenericLowering::LowerJSToString(Node* node) { 268 ReplaceWithBuiltinCall(node, Builtins::TO_STRING, 1); 269 } 270 271 272 void JSGenericLowering::LowerJSToObject(Node* node) { 273 ReplaceWithBuiltinCall(node, Builtins::TO_OBJECT, 1); 274 } 275 276 277 void JSGenericLowering::LowerJSLoadProperty(Node* node) { 278 Callable callable = CodeFactory::KeyedLoadIC(isolate()); 279 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite); 280 } 281 282 283 void JSGenericLowering::LowerJSLoadNamed(Node* node) { 284 LoadNamedParameters p = OpParameter<LoadNamedParameters>(node); 285 Callable callable = CodeFactory::LoadIC(isolate(), p.contextual_mode); 286 PatchInsertInput(node, 1, jsgraph()->HeapConstant(p.name)); 287 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite); 288 } 289 290 291 void JSGenericLowering::LowerJSStoreProperty(Node* node) { 292 StrictMode strict_mode = OpParameter<StrictMode>(node); 293 Callable callable = CodeFactory::KeyedStoreIC(isolate(), strict_mode); 294 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite); 295 } 296 297 298 void JSGenericLowering::LowerJSStoreNamed(Node* node) { 299 StoreNamedParameters params = OpParameter<StoreNamedParameters>(node); 300 Callable callable = CodeFactory::StoreIC(isolate(), params.strict_mode); 301 PatchInsertInput(node, 1, jsgraph()->HeapConstant(params.name)); 302 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite); 303 } 304 305 306 void JSGenericLowering::LowerJSDeleteProperty(Node* node) { 307 StrictMode strict_mode = OpParameter<StrictMode>(node); 308 PatchInsertInput(node, 2, SmiConstant(strict_mode)); 309 ReplaceWithBuiltinCall(node, Builtins::DELETE, 3); 310 } 311 312 313 void JSGenericLowering::LowerJSHasProperty(Node* node) { 314 ReplaceWithBuiltinCall(node, Builtins::IN, 2); 315 } 316 317 318 void JSGenericLowering::LowerJSInstanceOf(Node* node) { 319 InstanceofStub::Flags flags = static_cast<InstanceofStub::Flags>( 320 InstanceofStub::kReturnTrueFalseObject | 321 InstanceofStub::kArgsInRegisters); 322 InstanceofStub stub(isolate(), flags); 323 CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor(); 324 CallDescriptor* desc = linkage()->GetStubCallDescriptor(d, 0); 325 Node* stub_code = CodeConstant(stub.GetCode()); 326 PatchInsertInput(node, 0, stub_code); 327 PatchOperator(node, common()->Call(desc)); 328 } 329 330 331 void JSGenericLowering::LowerJSLoadContext(Node* node) { 332 ContextAccess access = OpParameter<ContextAccess>(node); 333 // TODO(mstarzinger): Use simplified operators instead of machine operators 334 // here so that load/store optimization can be applied afterwards. 335 for (int i = 0; i < access.depth(); ++i) { 336 node->ReplaceInput( 337 0, graph()->NewNode( 338 machine()->Load(kMachAnyTagged), 339 NodeProperties::GetValueInput(node, 0), 340 Int32Constant(Context::SlotOffset(Context::PREVIOUS_INDEX)), 341 NodeProperties::GetEffectInput(node))); 342 } 343 node->ReplaceInput(1, Int32Constant(Context::SlotOffset(access.index()))); 344 PatchOperator(node, machine()->Load(kMachAnyTagged)); 345 } 346 347 348 void JSGenericLowering::LowerJSStoreContext(Node* node) { 349 ContextAccess access = OpParameter<ContextAccess>(node); 350 // TODO(mstarzinger): Use simplified operators instead of machine operators 351 // here so that load/store optimization can be applied afterwards. 352 for (int i = 0; i < access.depth(); ++i) { 353 node->ReplaceInput( 354 0, graph()->NewNode( 355 machine()->Load(kMachAnyTagged), 356 NodeProperties::GetValueInput(node, 0), 357 Int32Constant(Context::SlotOffset(Context::PREVIOUS_INDEX)), 358 NodeProperties::GetEffectInput(node))); 359 } 360 node->ReplaceInput(2, NodeProperties::GetValueInput(node, 1)); 361 node->ReplaceInput(1, Int32Constant(Context::SlotOffset(access.index()))); 362 PatchOperator(node, machine()->Store(StoreRepresentation(kMachAnyTagged, 363 kFullWriteBarrier))); 364 } 365 366 367 void JSGenericLowering::LowerJSCallConstruct(Node* node) { 368 int arity = OpParameter<int>(node); 369 CallConstructStub stub(isolate(), NO_CALL_CONSTRUCTOR_FLAGS); 370 CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor(); 371 CallDescriptor* desc = 372 linkage()->GetStubCallDescriptor(d, arity, FlagsForNode(node)); 373 Node* stub_code = CodeConstant(stub.GetCode()); 374 Node* construct = NodeProperties::GetValueInput(node, 0); 375 PatchInsertInput(node, 0, stub_code); 376 PatchInsertInput(node, 1, Int32Constant(arity - 1)); 377 PatchInsertInput(node, 2, construct); 378 PatchInsertInput(node, 3, jsgraph()->UndefinedConstant()); 379 PatchOperator(node, common()->Call(desc)); 380 } 381 382 383 void JSGenericLowering::LowerJSCallFunction(Node* node) { 384 CallParameters p = OpParameter<CallParameters>(node); 385 CallFunctionStub stub(isolate(), p.arity - 2, p.flags); 386 CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor(); 387 CallDescriptor* desc = 388 linkage()->GetStubCallDescriptor(d, p.arity - 1, FlagsForNode(node)); 389 Node* stub_code = CodeConstant(stub.GetCode()); 390 PatchInsertInput(node, 0, stub_code); 391 PatchOperator(node, common()->Call(desc)); 392 } 393 394 395 void JSGenericLowering::LowerJSCallRuntime(Node* node) { 396 Runtime::FunctionId function = OpParameter<Runtime::FunctionId>(node); 397 int arity = OperatorProperties::GetValueInputCount(node->op()); 398 ReplaceWithRuntimeCall(node, function, arity); 399 } 400 401 } // namespace compiler 402 } // namespace internal 403 } // namespace v8 404