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 15namespace v8 { 16namespace internal { 17namespace compiler { 18 19JSGenericLowering::JSGenericLowering(CompilationInfo* info, JSGraph* jsgraph) 20 : info_(info), 21 jsgraph_(jsgraph), 22 linkage_(new (jsgraph->zone()) Linkage(info)) {} 23 24 25void JSGenericLowering::PatchOperator(Node* node, const Operator* op) { 26 node->set_op(op); 27} 28 29 30void JSGenericLowering::PatchInsertInput(Node* node, int index, Node* input) { 31 node->InsertInput(zone(), index, input); 32} 33 34 35Node* JSGenericLowering::SmiConstant(int32_t immediate) { 36 return jsgraph()->SmiConstant(immediate); 37} 38 39 40Node* JSGenericLowering::Int32Constant(int immediate) { 41 return jsgraph()->Int32Constant(immediate); 42} 43 44 45Node* JSGenericLowering::CodeConstant(Handle<Code> code) { 46 return jsgraph()->HeapConstant(code); 47} 48 49 50Node* JSGenericLowering::FunctionConstant(Handle<JSFunction> function) { 51 return jsgraph()->HeapConstant(function); 52} 53 54 55Node* JSGenericLowering::ExternalConstant(ExternalReference ref) { 56 return jsgraph()->ExternalConstant(ref); 57} 58 59 60Reduction 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 } 82REPLACE_BINARY_OP_IC_CALL(JSBitwiseOr, Token::BIT_OR) 83REPLACE_BINARY_OP_IC_CALL(JSBitwiseXor, Token::BIT_XOR) 84REPLACE_BINARY_OP_IC_CALL(JSBitwiseAnd, Token::BIT_AND) 85REPLACE_BINARY_OP_IC_CALL(JSShiftLeft, Token::SHL) 86REPLACE_BINARY_OP_IC_CALL(JSShiftRight, Token::SAR) 87REPLACE_BINARY_OP_IC_CALL(JSShiftRightLogical, Token::SHR) 88REPLACE_BINARY_OP_IC_CALL(JSAdd, Token::ADD) 89REPLACE_BINARY_OP_IC_CALL(JSSubtract, Token::SUB) 90REPLACE_BINARY_OP_IC_CALL(JSMultiply, Token::MUL) 91REPLACE_BINARY_OP_IC_CALL(JSDivide, Token::DIV) 92REPLACE_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 } 100REPLACE_COMPARE_IC_CALL(JSEqual, Token::EQ, false) 101REPLACE_COMPARE_IC_CALL(JSNotEqual, Token::NE, false) 102REPLACE_COMPARE_IC_CALL(JSStrictEqual, Token::EQ_STRICT, true) 103REPLACE_COMPARE_IC_CALL(JSStrictNotEqual, Token::NE_STRICT, true) 104REPLACE_COMPARE_IC_CALL(JSLessThan, Token::LT, false) 105REPLACE_COMPARE_IC_CALL(JSGreaterThan, Token::GT, false) 106REPLACE_COMPARE_IC_CALL(JSLessThanOrEqual, Token::LTE, false) 107REPLACE_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 } 115REPLACE_RUNTIME_CALL(JSTypeOf, Runtime::kTypeof) 116REPLACE_RUNTIME_CALL(JSCreate, Runtime::kAbort) 117REPLACE_RUNTIME_CALL(JSCreateFunctionContext, Runtime::kNewFunctionContext) 118REPLACE_RUNTIME_CALL(JSCreateCatchContext, Runtime::kPushCatchContext) 119REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext) 120REPLACE_RUNTIME_CALL(JSCreateBlockContext, Runtime::kPushBlockContext) 121REPLACE_RUNTIME_CALL(JSCreateModuleContext, Runtime::kPushModuleContext) 122REPLACE_RUNTIME_CALL(JSCreateGlobalContext, Runtime::kAbort) 123#undef REPLACE_RUNTIME 124 125 126#define REPLACE_UNIMPLEMENTED(op) \ 127 void JSGenericLowering::Lower##op(Node* node) { UNIMPLEMENTED(); } 128REPLACE_UNIMPLEMENTED(JSToName) 129REPLACE_UNIMPLEMENTED(JSYield) 130REPLACE_UNIMPLEMENTED(JSDebugger) 131#undef REPLACE_UNIMPLEMENTED 132 133 134static 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 143void 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 186void 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 196void 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 215void 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 235void 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 247void 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 254void JSGenericLowering::LowerJSToBoolean(Node* node) { 255 Callable callable = 256 CodeFactory::ToBoolean(isolate(), ToBooleanStub::RESULT_AS_ODDBALL); 257 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite); 258} 259 260 261void JSGenericLowering::LowerJSToNumber(Node* node) { 262 Callable callable = CodeFactory::ToNumber(isolate()); 263 ReplaceWithStubCall(node, callable, CallDescriptor::kNoFlags); 264} 265 266 267void JSGenericLowering::LowerJSToString(Node* node) { 268 ReplaceWithBuiltinCall(node, Builtins::TO_STRING, 1); 269} 270 271 272void JSGenericLowering::LowerJSToObject(Node* node) { 273 ReplaceWithBuiltinCall(node, Builtins::TO_OBJECT, 1); 274} 275 276 277void JSGenericLowering::LowerJSLoadProperty(Node* node) { 278 Callable callable = CodeFactory::KeyedLoadIC(isolate()); 279 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite); 280} 281 282 283void 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 291void 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 298void 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 306void 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 313void JSGenericLowering::LowerJSHasProperty(Node* node) { 314 ReplaceWithBuiltinCall(node, Builtins::IN, 2); 315} 316 317 318void 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 331void 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 348void 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 367void 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 383void 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 395void 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