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/ast/scopes.h" 6 #include "src/code-stubs.h" 7 #include "src/compiler.h" 8 #include "src/compiler/common-operator.h" 9 #include "src/compiler/frame.h" 10 #include "src/compiler/linkage.h" 11 #include "src/compiler/node.h" 12 #include "src/compiler/osr.h" 13 #include "src/compiler/pipeline.h" 14 15 namespace v8 { 16 namespace internal { 17 namespace compiler { 18 19 namespace { 20 LinkageLocation regloc(Register reg) { 21 return LinkageLocation::ForRegister(reg.code()); 22 } 23 24 25 MachineType reptyp(Representation representation) { 26 switch (representation.kind()) { 27 case Representation::kInteger8: 28 return MachineType::Int8(); 29 case Representation::kUInteger8: 30 return MachineType::Uint8(); 31 case Representation::kInteger16: 32 return MachineType::Int16(); 33 case Representation::kUInteger16: 34 return MachineType::Uint16(); 35 case Representation::kInteger32: 36 return MachineType::Int32(); 37 case Representation::kSmi: 38 case Representation::kTagged: 39 case Representation::kHeapObject: 40 return MachineType::AnyTagged(); 41 case Representation::kDouble: 42 return MachineType::Float64(); 43 case Representation::kExternal: 44 return MachineType::Pointer(); 45 case Representation::kNone: 46 case Representation::kNumRepresentations: 47 break; 48 } 49 UNREACHABLE(); 50 return MachineType::None(); 51 } 52 } // namespace 53 54 55 std::ostream& operator<<(std::ostream& os, const CallDescriptor::Kind& k) { 56 switch (k) { 57 case CallDescriptor::kCallCodeObject: 58 os << "Code"; 59 break; 60 case CallDescriptor::kCallJSFunction: 61 os << "JS"; 62 break; 63 case CallDescriptor::kCallAddress: 64 os << "Addr"; 65 break; 66 case CallDescriptor::kLazyBailout: 67 os << "LazyBail"; 68 break; 69 } 70 return os; 71 } 72 73 74 std::ostream& operator<<(std::ostream& os, const CallDescriptor& d) { 75 // TODO(svenpanne) Output properties etc. and be less cryptic. 76 return os << d.kind() << ":" << d.debug_name() << ":r" << d.ReturnCount() 77 << "s" << d.StackParameterCount() << "i" << d.InputCount() << "f" 78 << d.FrameStateCount() << "t" << d.SupportsTailCalls(); 79 } 80 81 82 bool CallDescriptor::HasSameReturnLocationsAs( 83 const CallDescriptor* other) const { 84 if (ReturnCount() != other->ReturnCount()) return false; 85 for (size_t i = 0; i < ReturnCount(); ++i) { 86 if (GetReturnLocation(i) != other->GetReturnLocation(i)) return false; 87 } 88 return true; 89 } 90 91 92 bool CallDescriptor::CanTailCall(const Node* node, 93 int* stack_param_delta) const { 94 CallDescriptor const* other = OpParameter<CallDescriptor const*>(node); 95 size_t current_input = 0; 96 size_t other_input = 0; 97 *stack_param_delta = 0; 98 bool more_other = true; 99 bool more_this = true; 100 while (more_other || more_this) { 101 if (other_input < other->InputCount()) { 102 if (!other->GetInputLocation(other_input).IsRegister()) { 103 (*stack_param_delta)--; 104 } 105 } else { 106 more_other = false; 107 } 108 if (current_input < InputCount()) { 109 if (!GetInputLocation(current_input).IsRegister()) { 110 (*stack_param_delta)++; 111 } 112 } else { 113 more_this = false; 114 } 115 ++current_input; 116 ++other_input; 117 } 118 return HasSameReturnLocationsAs(OpParameter<CallDescriptor const*>(node)); 119 } 120 121 122 CallDescriptor* Linkage::ComputeIncoming(Zone* zone, CompilationInfo* info) { 123 if (info->code_stub() != nullptr) { 124 // Use the code stub interface descriptor. 125 CodeStub* stub = info->code_stub(); 126 CallInterfaceDescriptor descriptor = stub->GetCallInterfaceDescriptor(); 127 return GetStubCallDescriptor( 128 info->isolate(), zone, descriptor, stub->GetStackParameterCount(), 129 CallDescriptor::kNoFlags, Operator::kNoProperties); 130 } 131 if (info->has_literal()) { 132 // If we already have the function literal, use the number of parameters 133 // plus the receiver. 134 return GetJSCallDescriptor(zone, info->is_osr(), 135 1 + info->literal()->parameter_count(), 136 CallDescriptor::kNoFlags); 137 } 138 if (!info->closure().is_null()) { 139 // If we are compiling a JS function, use a JS call descriptor, 140 // plus the receiver. 141 SharedFunctionInfo* shared = info->closure()->shared(); 142 return GetJSCallDescriptor(zone, info->is_osr(), 143 1 + shared->internal_formal_parameter_count(), 144 CallDescriptor::kNoFlags); 145 } 146 return nullptr; // TODO(titzer): ? 147 } 148 149 150 // static 151 int Linkage::FrameStateInputCount(Runtime::FunctionId function) { 152 // Most runtime functions need a FrameState. A few chosen ones that we know 153 // not to call into arbitrary JavaScript, not to throw, and not to deoptimize 154 // are blacklisted here and can be called without a FrameState. 155 switch (function) { 156 case Runtime::kAllocateInTargetSpace: 157 case Runtime::kCreateIterResultObject: 158 case Runtime::kDefineClassMethod: // TODO(jarin): Is it safe? 159 case Runtime::kDefineGetterPropertyUnchecked: // TODO(jarin): Is it safe? 160 case Runtime::kDefineSetterPropertyUnchecked: // TODO(jarin): Is it safe? 161 case Runtime::kFinalizeClassDefinition: // TODO(conradw): Is it safe? 162 case Runtime::kForInDone: 163 case Runtime::kForInStep: 164 case Runtime::kGetSuperConstructor: 165 case Runtime::kNewClosure: 166 case Runtime::kNewClosure_Tenured: 167 case Runtime::kNewFunctionContext: 168 case Runtime::kPushBlockContext: 169 case Runtime::kPushCatchContext: 170 case Runtime::kReThrow: 171 case Runtime::kStringCompare: 172 case Runtime::kStringEquals: 173 case Runtime::kToFastProperties: // TODO(jarin): Is it safe? 174 case Runtime::kTraceEnter: 175 case Runtime::kTraceExit: 176 return 0; 177 case Runtime::kInlineArguments: 178 case Runtime::kInlineArgumentsLength: 179 case Runtime::kInlineGetPrototype: 180 case Runtime::kInlineRegExpConstructResult: 181 case Runtime::kInlineRegExpExec: 182 case Runtime::kInlineSubString: 183 case Runtime::kInlineToInteger: 184 case Runtime::kInlineToLength: 185 case Runtime::kInlineToName: 186 case Runtime::kInlineToNumber: 187 case Runtime::kInlineToObject: 188 case Runtime::kInlineToPrimitive_Number: 189 case Runtime::kInlineToPrimitive_String: 190 case Runtime::kInlineToPrimitive: 191 case Runtime::kInlineToString: 192 return 1; 193 case Runtime::kInlineCall: 194 case Runtime::kInlineTailCall: 195 case Runtime::kInlineDeoptimizeNow: 196 case Runtime::kInlineThrowNotDateError: 197 return 2; 198 default: 199 break; 200 } 201 202 // Most inlined runtime functions (except the ones listed above) can be called 203 // without a FrameState or will be lowered by JSIntrinsicLowering internally. 204 const Runtime::Function* const f = Runtime::FunctionForId(function); 205 if (f->intrinsic_type == Runtime::IntrinsicType::INLINE) return 0; 206 207 return 1; 208 } 209 210 211 bool CallDescriptor::UsesOnlyRegisters() const { 212 for (size_t i = 0; i < InputCount(); ++i) { 213 if (!GetInputLocation(i).IsRegister()) return false; 214 } 215 for (size_t i = 0; i < ReturnCount(); ++i) { 216 if (!GetReturnLocation(i).IsRegister()) return false; 217 } 218 return true; 219 } 220 221 222 CallDescriptor* Linkage::GetRuntimeCallDescriptor( 223 Zone* zone, Runtime::FunctionId function_id, int js_parameter_count, 224 Operator::Properties properties, CallDescriptor::Flags flags) { 225 const size_t function_count = 1; 226 const size_t num_args_count = 1; 227 const size_t context_count = 1; 228 const size_t parameter_count = function_count + 229 static_cast<size_t>(js_parameter_count) + 230 num_args_count + context_count; 231 232 const Runtime::Function* function = Runtime::FunctionForId(function_id); 233 const size_t return_count = static_cast<size_t>(function->result_size); 234 235 LocationSignature::Builder locations(zone, return_count, parameter_count); 236 MachineSignature::Builder types(zone, return_count, parameter_count); 237 238 // Add returns. 239 if (locations.return_count_ > 0) { 240 locations.AddReturn(regloc(kReturnRegister0)); 241 } 242 if (locations.return_count_ > 1) { 243 locations.AddReturn(regloc(kReturnRegister1)); 244 } 245 for (size_t i = 0; i < return_count; i++) { 246 types.AddReturn(MachineType::AnyTagged()); 247 } 248 249 // All parameters to the runtime call go on the stack. 250 for (int i = 0; i < js_parameter_count; i++) { 251 locations.AddParam( 252 LinkageLocation::ForCallerFrameSlot(i - js_parameter_count)); 253 types.AddParam(MachineType::AnyTagged()); 254 } 255 // Add runtime function itself. 256 locations.AddParam(regloc(kRuntimeCallFunctionRegister)); 257 types.AddParam(MachineType::AnyTagged()); 258 259 // Add runtime call argument count. 260 locations.AddParam(regloc(kRuntimeCallArgCountRegister)); 261 types.AddParam(MachineType::Pointer()); 262 263 // Add context. 264 locations.AddParam(regloc(kContextRegister)); 265 types.AddParam(MachineType::AnyTagged()); 266 267 if (Linkage::FrameStateInputCount(function_id) == 0) { 268 flags = static_cast<CallDescriptor::Flags>( 269 flags & ~CallDescriptor::kNeedsFrameState); 270 } 271 272 // The target for runtime calls is a code object. 273 MachineType target_type = MachineType::AnyTagged(); 274 LinkageLocation target_loc = LinkageLocation::ForAnyRegister(); 275 return new (zone) CallDescriptor( // -- 276 CallDescriptor::kCallCodeObject, // kind 277 target_type, // target MachineType 278 target_loc, // target location 279 types.Build(), // machine_sig 280 locations.Build(), // location_sig 281 js_parameter_count, // stack_parameter_count 282 properties, // properties 283 kNoCalleeSaved, // callee-saved 284 kNoCalleeSaved, // callee-saved fp 285 flags, // flags 286 function->name); // debug name 287 } 288 289 290 CallDescriptor* Linkage::GetLazyBailoutDescriptor(Zone* zone) { 291 const size_t return_count = 0; 292 const size_t parameter_count = 0; 293 294 LocationSignature::Builder locations(zone, return_count, parameter_count); 295 MachineSignature::Builder types(zone, return_count, parameter_count); 296 297 // The target is ignored, but we need to give some values here. 298 MachineType target_type = MachineType::AnyTagged(); 299 LinkageLocation target_loc = regloc(kJSFunctionRegister); 300 return new (zone) CallDescriptor( // -- 301 CallDescriptor::kLazyBailout, // kind 302 target_type, // target MachineType 303 target_loc, // target location 304 types.Build(), // machine_sig 305 locations.Build(), // location_sig 306 0, // stack_parameter_count 307 Operator::kNoThrow, // properties 308 kNoCalleeSaved, // callee-saved 309 kNoCalleeSaved, // callee-saved fp 310 CallDescriptor::kNeedsFrameState, // flags 311 "lazy-bailout"); 312 } 313 314 315 CallDescriptor* Linkage::GetJSCallDescriptor(Zone* zone, bool is_osr, 316 int js_parameter_count, 317 CallDescriptor::Flags flags) { 318 const size_t return_count = 1; 319 const size_t context_count = 1; 320 const size_t new_target_count = 1; 321 const size_t num_args_count = 1; 322 const size_t parameter_count = 323 js_parameter_count + new_target_count + num_args_count + context_count; 324 325 LocationSignature::Builder locations(zone, return_count, parameter_count); 326 MachineSignature::Builder types(zone, return_count, parameter_count); 327 328 // All JS calls have exactly one return value. 329 locations.AddReturn(regloc(kReturnRegister0)); 330 types.AddReturn(MachineType::AnyTagged()); 331 332 // All parameters to JS calls go on the stack. 333 for (int i = 0; i < js_parameter_count; i++) { 334 int spill_slot_index = i - js_parameter_count; 335 locations.AddParam(LinkageLocation::ForCallerFrameSlot(spill_slot_index)); 336 types.AddParam(MachineType::AnyTagged()); 337 } 338 339 // Add JavaScript call new target value. 340 locations.AddParam(regloc(kJavaScriptCallNewTargetRegister)); 341 types.AddParam(MachineType::AnyTagged()); 342 343 // Add JavaScript call argument count. 344 locations.AddParam(regloc(kJavaScriptCallArgCountRegister)); 345 types.AddParam(MachineType::Int32()); 346 347 // Add context. 348 locations.AddParam(regloc(kContextRegister)); 349 types.AddParam(MachineType::AnyTagged()); 350 351 // The target for JS function calls is the JSFunction object. 352 MachineType target_type = MachineType::AnyTagged(); 353 // TODO(titzer): When entering into an OSR function from unoptimized code, 354 // the JSFunction is not in a register, but it is on the stack in an 355 // unaddressable spill slot. We hack this in the OSR prologue. Fix. 356 LinkageLocation target_loc = regloc(kJSFunctionRegister); 357 return new (zone) CallDescriptor( // -- 358 CallDescriptor::kCallJSFunction, // kind 359 target_type, // target MachineType 360 target_loc, // target location 361 types.Build(), // machine_sig 362 locations.Build(), // location_sig 363 js_parameter_count, // stack_parameter_count 364 Operator::kNoProperties, // properties 365 kNoCalleeSaved, // callee-saved 366 kNoCalleeSaved, // callee-saved fp 367 CallDescriptor::kCanUseRoots | // flags 368 flags, // flags 369 "js-call"); 370 } 371 372 373 CallDescriptor* Linkage::GetInterpreterDispatchDescriptor(Zone* zone) { 374 MachineSignature::Builder types(zone, 0, 6); 375 LocationSignature::Builder locations(zone, 0, 6); 376 377 // Add registers for fixed parameters passed via interpreter dispatch. 378 STATIC_ASSERT(0 == Linkage::kInterpreterAccumulatorParameter); 379 types.AddParam(MachineType::AnyTagged()); 380 locations.AddParam(regloc(kInterpreterAccumulatorRegister)); 381 382 STATIC_ASSERT(1 == Linkage::kInterpreterRegisterFileParameter); 383 types.AddParam(MachineType::Pointer()); 384 locations.AddParam(regloc(kInterpreterRegisterFileRegister)); 385 386 STATIC_ASSERT(2 == Linkage::kInterpreterBytecodeOffsetParameter); 387 types.AddParam(MachineType::IntPtr()); 388 locations.AddParam(regloc(kInterpreterBytecodeOffsetRegister)); 389 390 STATIC_ASSERT(3 == Linkage::kInterpreterBytecodeArrayParameter); 391 types.AddParam(MachineType::AnyTagged()); 392 locations.AddParam(regloc(kInterpreterBytecodeArrayRegister)); 393 394 STATIC_ASSERT(4 == Linkage::kInterpreterDispatchTableParameter); 395 types.AddParam(MachineType::Pointer()); 396 #if defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_X87) 397 // TODO(rmcilroy): Make the context param the one spilled to the stack once 398 // Turbofan supports modified stack arguments in tail calls. 399 locations.AddParam( 400 LinkageLocation::ForCallerFrameSlot(kInterpreterDispatchTableSpillSlot)); 401 #else 402 locations.AddParam(regloc(kInterpreterDispatchTableRegister)); 403 #endif 404 405 STATIC_ASSERT(5 == Linkage::kInterpreterContextParameter); 406 types.AddParam(MachineType::AnyTagged()); 407 locations.AddParam(regloc(kContextRegister)); 408 409 LinkageLocation target_loc = LinkageLocation::ForAnyRegister(); 410 return new (zone) CallDescriptor( // -- 411 CallDescriptor::kCallCodeObject, // kind 412 MachineType::None(), // target MachineType 413 target_loc, // target location 414 types.Build(), // machine_sig 415 locations.Build(), // location_sig 416 0, // stack_parameter_count 417 Operator::kNoProperties, // properties 418 kNoCalleeSaved, // callee-saved registers 419 kNoCalleeSaved, // callee-saved fp regs 420 CallDescriptor::kSupportsTailCalls | // flags 421 CallDescriptor::kCanUseRoots, // flags 422 "interpreter-dispatch"); 423 } 424 425 426 // TODO(all): Add support for return representations/locations to 427 // CallInterfaceDescriptor. 428 // TODO(turbofan): cache call descriptors for code stub calls. 429 CallDescriptor* Linkage::GetStubCallDescriptor( 430 Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor, 431 int stack_parameter_count, CallDescriptor::Flags flags, 432 Operator::Properties properties, MachineType return_type, 433 size_t return_count) { 434 const int register_parameter_count = descriptor.GetRegisterParameterCount(); 435 const int js_parameter_count = 436 register_parameter_count + stack_parameter_count; 437 const int context_count = 1; 438 const size_t parameter_count = 439 static_cast<size_t>(js_parameter_count + context_count); 440 441 LocationSignature::Builder locations(zone, return_count, parameter_count); 442 MachineSignature::Builder types(zone, return_count, parameter_count); 443 444 // Add returns. 445 if (locations.return_count_ > 0) { 446 locations.AddReturn(regloc(kReturnRegister0)); 447 } 448 if (locations.return_count_ > 1) { 449 locations.AddReturn(regloc(kReturnRegister1)); 450 } 451 for (size_t i = 0; i < return_count; i++) { 452 types.AddReturn(return_type); 453 } 454 455 // Add parameters in registers and on the stack. 456 for (int i = 0; i < js_parameter_count; i++) { 457 if (i < register_parameter_count) { 458 // The first parameters go in registers. 459 Register reg = descriptor.GetRegisterParameter(i); 460 Representation rep = 461 RepresentationFromType(descriptor.GetParameterType(i)); 462 locations.AddParam(regloc(reg)); 463 types.AddParam(reptyp(rep)); 464 } else { 465 // The rest of the parameters go on the stack. 466 int stack_slot = i - register_parameter_count - stack_parameter_count; 467 locations.AddParam(LinkageLocation::ForCallerFrameSlot(stack_slot)); 468 types.AddParam(MachineType::AnyTagged()); 469 } 470 } 471 // Add context. 472 locations.AddParam(regloc(kContextRegister)); 473 types.AddParam(MachineType::AnyTagged()); 474 475 // The target for stub calls is a code object. 476 MachineType target_type = MachineType::AnyTagged(); 477 LinkageLocation target_loc = LinkageLocation::ForAnyRegister(); 478 return new (zone) CallDescriptor( // -- 479 CallDescriptor::kCallCodeObject, // kind 480 target_type, // target MachineType 481 target_loc, // target location 482 types.Build(), // machine_sig 483 locations.Build(), // location_sig 484 stack_parameter_count, // stack_parameter_count 485 properties, // properties 486 kNoCalleeSaved, // callee-saved registers 487 kNoCalleeSaved, // callee-saved fp 488 flags, // flags 489 descriptor.DebugName(isolate)); 490 } 491 492 493 LinkageLocation Linkage::GetOsrValueLocation(int index) const { 494 CHECK(incoming_->IsJSFunctionCall()); 495 int parameter_count = static_cast<int>(incoming_->JSParameterCount() - 1); 496 int first_stack_slot = OsrHelper::FirstStackSlotIndex(parameter_count); 497 498 if (index == kOsrContextSpillSlotIndex) { 499 // Context. Use the parameter location of the context spill slot. 500 // Parameter (arity + 2) is special for the context of the function frame. 501 // >> context_index = target + receiver + params + new_target + #args 502 int context_index = 1 + 1 + parameter_count + 1 + 1; 503 return incoming_->GetInputLocation(context_index); 504 } else if (index >= first_stack_slot) { 505 // Local variable stored in this (callee) stack. 506 int spill_index = 507 index - first_stack_slot + StandardFrameConstants::kFixedSlotCount; 508 return LinkageLocation::ForCalleeFrameSlot(spill_index); 509 } else { 510 // Parameter. Use the assigned location from the incoming call descriptor. 511 int parameter_index = 1 + index; // skip index 0, which is the target. 512 return incoming_->GetInputLocation(parameter_index); 513 } 514 } 515 516 517 bool Linkage::ParameterHasSecondaryLocation(int index) const { 518 if (incoming_->kind() != CallDescriptor::kCallJSFunction) return false; 519 LinkageLocation loc = GetParameterLocation(index); 520 return (loc == regloc(kJSFunctionRegister) || 521 loc == regloc(kContextRegister)); 522 } 523 524 LinkageLocation Linkage::GetParameterSecondaryLocation(int index) const { 525 DCHECK(ParameterHasSecondaryLocation(index)); 526 LinkageLocation loc = GetParameterLocation(index); 527 528 if (loc == regloc(kJSFunctionRegister)) { 529 return LinkageLocation::ForCalleeFrameSlot(Frame::kJSFunctionSlot); 530 } else { 531 DCHECK(loc == regloc(kContextRegister)); 532 return LinkageLocation::ForCalleeFrameSlot(Frame::kContextSlot); 533 } 534 } 535 536 537 } // namespace compiler 538 } // namespace internal 539 } // namespace v8 540