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/linkage.h" 6 7 #include "src/ast/scopes.h" 8 #include "src/code-stubs.h" 9 #include "src/compilation-info.h" 10 #include "src/compiler/common-operator.h" 11 #include "src/compiler/frame.h" 12 #include "src/compiler/node.h" 13 #include "src/compiler/osr.h" 14 #include "src/compiler/pipeline.h" 15 #include "src/objects-inl.h" 16 17 namespace v8 { 18 namespace internal { 19 namespace compiler { 20 21 namespace { 22 23 LinkageLocation regloc(Register reg, MachineType type) { 24 return LinkageLocation::ForRegister(reg.code(), type); 25 } 26 27 } // namespace 28 29 30 std::ostream& operator<<(std::ostream& os, const CallDescriptor::Kind& k) { 31 switch (k) { 32 case CallDescriptor::kCallCodeObject: 33 os << "Code"; 34 break; 35 case CallDescriptor::kCallJSFunction: 36 os << "JS"; 37 break; 38 case CallDescriptor::kCallAddress: 39 os << "Addr"; 40 break; 41 } 42 return os; 43 } 44 45 46 std::ostream& operator<<(std::ostream& os, const CallDescriptor& d) { 47 // TODO(svenpanne) Output properties etc. and be less cryptic. 48 return os << d.kind() << ":" << d.debug_name() << ":r" << d.ReturnCount() 49 << "s" << d.StackParameterCount() << "i" << d.InputCount() << "f" 50 << d.FrameStateCount() << "t" << d.SupportsTailCalls(); 51 } 52 53 MachineSignature* CallDescriptor::GetMachineSignature(Zone* zone) const { 54 size_t param_count = ParameterCount(); 55 size_t return_count = ReturnCount(); 56 MachineType* types = zone->NewArray<MachineType>(param_count + return_count); 57 int current = 0; 58 for (size_t i = 0; i < return_count; ++i) { 59 types[current++] = GetReturnType(i); 60 } 61 for (size_t i = 0; i < param_count; ++i) { 62 types[current++] = GetParameterType(i); 63 } 64 return new (zone) MachineSignature(return_count, param_count, types); 65 } 66 67 bool CallDescriptor::HasSameReturnLocationsAs( 68 const CallDescriptor* other) const { 69 if (ReturnCount() != other->ReturnCount()) return false; 70 for (size_t i = 0; i < ReturnCount(); ++i) { 71 if (GetReturnLocation(i) != other->GetReturnLocation(i)) return false; 72 } 73 return true; 74 } 75 76 int CallDescriptor::GetStackParameterDelta( 77 CallDescriptor const* tail_caller) const { 78 int callee_slots_above_sp = 0; 79 for (size_t i = 0; i < InputCount(); ++i) { 80 LinkageLocation operand = GetInputLocation(i); 81 if (!operand.IsRegister()) { 82 int new_candidate = 83 -operand.GetLocation() + operand.GetSizeInPointers() - 1; 84 if (new_candidate > callee_slots_above_sp) { 85 callee_slots_above_sp = new_candidate; 86 } 87 } 88 } 89 int tail_caller_slots_above_sp = 0; 90 if (tail_caller != nullptr) { 91 for (size_t i = 0; i < tail_caller->InputCount(); ++i) { 92 LinkageLocation operand = tail_caller->GetInputLocation(i); 93 if (!operand.IsRegister()) { 94 int new_candidate = 95 -operand.GetLocation() + operand.GetSizeInPointers() - 1; 96 if (new_candidate > tail_caller_slots_above_sp) { 97 tail_caller_slots_above_sp = new_candidate; 98 } 99 } 100 } 101 } 102 return callee_slots_above_sp - tail_caller_slots_above_sp; 103 } 104 105 bool CallDescriptor::CanTailCall(const Node* node) const { 106 return HasSameReturnLocationsAs(CallDescriptorOf(node->op())); 107 } 108 109 int CallDescriptor::CalculateFixedFrameSize() const { 110 switch (kind_) { 111 case kCallJSFunction: 112 return PushArgumentCount() 113 ? OptimizedBuiltinFrameConstants::kFixedSlotCount 114 : StandardFrameConstants::kFixedSlotCount; 115 break; 116 case kCallAddress: 117 return CommonFrameConstants::kFixedSlotCountAboveFp + 118 CommonFrameConstants::kCPSlotCount; 119 break; 120 case kCallCodeObject: 121 return TypedFrameConstants::kFixedSlotCount; 122 } 123 UNREACHABLE(); 124 return 0; 125 } 126 127 CallDescriptor* Linkage::ComputeIncoming(Zone* zone, CompilationInfo* info) { 128 DCHECK(!info->IsStub()); 129 if (!info->closure().is_null()) { 130 // If we are compiling a JS function, use a JS call descriptor, 131 // plus the receiver. 132 SharedFunctionInfo* shared = info->closure()->shared(); 133 return GetJSCallDescriptor(zone, info->is_osr(), 134 1 + shared->internal_formal_parameter_count(), 135 CallDescriptor::kNoFlags); 136 } 137 return nullptr; // TODO(titzer): ? 138 } 139 140 141 // static 142 bool Linkage::NeedsFrameStateInput(Runtime::FunctionId function) { 143 switch (function) { 144 // Most runtime functions need a FrameState. A few chosen ones that we know 145 // not to call into arbitrary JavaScript, not to throw, and not to lazily 146 // deoptimize are whitelisted here and can be called without a FrameState. 147 case Runtime::kAbort: 148 case Runtime::kAllocateInTargetSpace: 149 case Runtime::kConvertReceiver: 150 case Runtime::kCreateIterResultObject: 151 case Runtime::kDefineGetterPropertyUnchecked: // TODO(jarin): Is it safe? 152 case Runtime::kDefineSetterPropertyUnchecked: // TODO(jarin): Is it safe? 153 case Runtime::kGeneratorGetContinuation: 154 case Runtime::kIsFunction: 155 case Runtime::kNewClosure: 156 case Runtime::kNewClosure_Tenured: 157 case Runtime::kNewFunctionContext: 158 case Runtime::kPushBlockContext: 159 case Runtime::kPushCatchContext: 160 case Runtime::kReThrow: 161 case Runtime::kStringCompare: 162 case Runtime::kStringEqual: 163 case Runtime::kStringNotEqual: 164 case Runtime::kStringLessThan: 165 case Runtime::kStringLessThanOrEqual: 166 case Runtime::kStringGreaterThan: 167 case Runtime::kStringGreaterThanOrEqual: 168 case Runtime::kToFastProperties: // TODO(conradw): Is it safe? 169 case Runtime::kTraceEnter: 170 case Runtime::kTraceExit: 171 return false; 172 173 // Some inline intrinsics are also safe to call without a FrameState. 174 case Runtime::kInlineClassOf: 175 case Runtime::kInlineCreateIterResultObject: 176 case Runtime::kInlineFixedArrayGet: 177 case Runtime::kInlineFixedArraySet: 178 case Runtime::kInlineGeneratorClose: 179 case Runtime::kInlineGeneratorGetInputOrDebugPos: 180 case Runtime::kInlineGeneratorGetResumeMode: 181 case Runtime::kInlineIsArray: 182 case Runtime::kInlineIsJSReceiver: 183 case Runtime::kInlineIsRegExp: 184 case Runtime::kInlineIsSmi: 185 case Runtime::kInlineIsTypedArray: 186 return false; 187 188 default: 189 break; 190 } 191 192 // For safety, default to needing a FrameState unless whitelisted. 193 return true; 194 } 195 196 197 bool CallDescriptor::UsesOnlyRegisters() const { 198 for (size_t i = 0; i < InputCount(); ++i) { 199 if (!GetInputLocation(i).IsRegister()) return false; 200 } 201 for (size_t i = 0; i < ReturnCount(); ++i) { 202 if (!GetReturnLocation(i).IsRegister()) return false; 203 } 204 return true; 205 } 206 207 208 CallDescriptor* Linkage::GetRuntimeCallDescriptor( 209 Zone* zone, Runtime::FunctionId function_id, int js_parameter_count, 210 Operator::Properties properties, CallDescriptor::Flags flags) { 211 const Runtime::Function* function = Runtime::FunctionForId(function_id); 212 const int return_count = function->result_size; 213 const char* debug_name = function->name; 214 215 if (!Linkage::NeedsFrameStateInput(function_id)) { 216 flags = static_cast<CallDescriptor::Flags>( 217 flags & ~CallDescriptor::kNeedsFrameState); 218 } 219 220 return GetCEntryStubCallDescriptor(zone, return_count, js_parameter_count, 221 debug_name, properties, flags); 222 } 223 224 CallDescriptor* Linkage::GetCEntryStubCallDescriptor( 225 Zone* zone, int return_count, int js_parameter_count, 226 const char* debug_name, Operator::Properties properties, 227 CallDescriptor::Flags flags) { 228 const size_t function_count = 1; 229 const size_t num_args_count = 1; 230 const size_t context_count = 1; 231 const size_t parameter_count = function_count + 232 static_cast<size_t>(js_parameter_count) + 233 num_args_count + context_count; 234 235 LocationSignature::Builder locations(zone, static_cast<size_t>(return_count), 236 static_cast<size_t>(parameter_count)); 237 238 // Add returns. 239 if (locations.return_count_ > 0) { 240 locations.AddReturn(regloc(kReturnRegister0, MachineType::AnyTagged())); 241 } 242 if (locations.return_count_ > 1) { 243 locations.AddReturn(regloc(kReturnRegister1, MachineType::AnyTagged())); 244 } 245 if (locations.return_count_ > 2) { 246 locations.AddReturn(regloc(kReturnRegister2, 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(LinkageLocation::ForCallerFrameSlot( 252 i - js_parameter_count, MachineType::AnyTagged())); 253 } 254 // Add runtime function itself. 255 locations.AddParam( 256 regloc(kRuntimeCallFunctionRegister, MachineType::Pointer())); 257 258 // Add runtime call argument count. 259 locations.AddParam( 260 regloc(kRuntimeCallArgCountRegister, MachineType::Int32())); 261 262 // Add context. 263 locations.AddParam(regloc(kContextRegister, MachineType::AnyTagged())); 264 265 // The target for runtime calls is a code object. 266 MachineType target_type = MachineType::AnyTagged(); 267 LinkageLocation target_loc = 268 LinkageLocation::ForAnyRegister(MachineType::AnyTagged()); 269 return new (zone) CallDescriptor( // -- 270 CallDescriptor::kCallCodeObject, // kind 271 target_type, // target MachineType 272 target_loc, // target location 273 locations.Build(), // location_sig 274 js_parameter_count, // stack_parameter_count 275 properties, // properties 276 kNoCalleeSaved, // callee-saved 277 kNoCalleeSaved, // callee-saved fp 278 flags, // flags 279 debug_name); // debug name 280 } 281 282 CallDescriptor* Linkage::GetJSCallDescriptor(Zone* zone, bool is_osr, 283 int js_parameter_count, 284 CallDescriptor::Flags flags) { 285 const size_t return_count = 1; 286 const size_t context_count = 1; 287 const size_t new_target_count = 1; 288 const size_t num_args_count = 1; 289 const size_t parameter_count = 290 js_parameter_count + new_target_count + num_args_count + context_count; 291 292 LocationSignature::Builder locations(zone, return_count, parameter_count); 293 294 // All JS calls have exactly one return value. 295 locations.AddReturn(regloc(kReturnRegister0, MachineType::AnyTagged())); 296 297 // All parameters to JS calls go on the stack. 298 for (int i = 0; i < js_parameter_count; i++) { 299 int spill_slot_index = i - js_parameter_count; 300 locations.AddParam(LinkageLocation::ForCallerFrameSlot( 301 spill_slot_index, MachineType::AnyTagged())); 302 } 303 304 // Add JavaScript call new target value. 305 locations.AddParam( 306 regloc(kJavaScriptCallNewTargetRegister, MachineType::AnyTagged())); 307 308 // Add JavaScript call argument count. 309 locations.AddParam( 310 regloc(kJavaScriptCallArgCountRegister, MachineType::Int32())); 311 312 // Add context. 313 locations.AddParam(regloc(kContextRegister, MachineType::AnyTagged())); 314 315 // The target for JS function calls is the JSFunction object. 316 MachineType target_type = MachineType::AnyTagged(); 317 // When entering into an OSR function from unoptimized code the JSFunction 318 // is not in a register, but it is on the stack in the marker spill slot. 319 LinkageLocation target_loc = 320 is_osr ? LinkageLocation::ForSavedCallerFunction() 321 : regloc(kJSFunctionRegister, MachineType::AnyTagged()); 322 return new (zone) CallDescriptor( // -- 323 CallDescriptor::kCallJSFunction, // kind 324 target_type, // target MachineType 325 target_loc, // target location 326 locations.Build(), // location_sig 327 js_parameter_count, // stack_parameter_count 328 Operator::kNoProperties, // properties 329 kNoCalleeSaved, // callee-saved 330 kNoCalleeSaved, // callee-saved fp 331 CallDescriptor::kCanUseRoots | // flags 332 flags, // flags 333 "js-call"); 334 } 335 336 // TODO(all): Add support for return representations/locations to 337 // CallInterfaceDescriptor. 338 // TODO(turbofan): cache call descriptors for code stub calls. 339 CallDescriptor* Linkage::GetStubCallDescriptor( 340 Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor, 341 int stack_parameter_count, CallDescriptor::Flags flags, 342 Operator::Properties properties, MachineType return_type, 343 size_t return_count) { 344 const int register_parameter_count = descriptor.GetRegisterParameterCount(); 345 const int js_parameter_count = 346 register_parameter_count + stack_parameter_count; 347 const int context_count = 1; 348 const size_t parameter_count = 349 static_cast<size_t>(js_parameter_count + context_count); 350 351 LocationSignature::Builder locations(zone, return_count, parameter_count); 352 353 // Add returns. 354 if (locations.return_count_ > 0) { 355 locations.AddReturn(regloc(kReturnRegister0, return_type)); 356 } 357 if (locations.return_count_ > 1) { 358 locations.AddReturn(regloc(kReturnRegister1, return_type)); 359 } 360 if (locations.return_count_ > 2) { 361 locations.AddReturn(regloc(kReturnRegister2, return_type)); 362 } 363 364 // Add parameters in registers and on the stack. 365 for (int i = 0; i < js_parameter_count; i++) { 366 if (i < register_parameter_count) { 367 // The first parameters go in registers. 368 Register reg = descriptor.GetRegisterParameter(i); 369 MachineType type = descriptor.GetParameterType(i); 370 locations.AddParam(regloc(reg, type)); 371 } else { 372 // The rest of the parameters go on the stack. 373 int stack_slot = i - register_parameter_count - stack_parameter_count; 374 locations.AddParam(LinkageLocation::ForCallerFrameSlot( 375 stack_slot, MachineType::AnyTagged())); 376 } 377 } 378 // Add context. 379 locations.AddParam(regloc(kContextRegister, MachineType::AnyTagged())); 380 381 // The target for stub calls is a code object. 382 MachineType target_type = MachineType::AnyTagged(); 383 LinkageLocation target_loc = 384 LinkageLocation::ForAnyRegister(MachineType::AnyTagged()); 385 return new (zone) CallDescriptor( // -- 386 CallDescriptor::kCallCodeObject, // kind 387 target_type, // target MachineType 388 target_loc, // target location 389 locations.Build(), // location_sig 390 stack_parameter_count, // stack_parameter_count 391 properties, // properties 392 kNoCalleeSaved, // callee-saved registers 393 kNoCalleeSaved, // callee-saved fp 394 CallDescriptor::kCanUseRoots | // flags 395 flags, // flags 396 descriptor.DebugName(isolate)); 397 } 398 399 // static 400 CallDescriptor* Linkage::GetAllocateCallDescriptor(Zone* zone) { 401 LocationSignature::Builder locations(zone, 1, 1); 402 403 locations.AddParam(regloc(kAllocateSizeRegister, MachineType::Int32())); 404 405 locations.AddReturn(regloc(kReturnRegister0, MachineType::AnyTagged())); 406 407 // The target for allocate calls is a code object. 408 MachineType target_type = MachineType::AnyTagged(); 409 LinkageLocation target_loc = 410 LinkageLocation::ForAnyRegister(MachineType::AnyTagged()); 411 return new (zone) CallDescriptor( // -- 412 CallDescriptor::kCallCodeObject, // kind 413 target_type, // target MachineType 414 target_loc, // target location 415 locations.Build(), // location_sig 416 0, // stack_parameter_count 417 Operator::kNoThrow, // properties 418 kNoCalleeSaved, // callee-saved registers 419 kNoCalleeSaved, // callee-saved fp 420 CallDescriptor::kCanUseRoots, // flags 421 "Allocate"); 422 } 423 424 // static 425 CallDescriptor* Linkage::GetBytecodeDispatchCallDescriptor( 426 Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor, 427 int stack_parameter_count) { 428 const int register_parameter_count = descriptor.GetRegisterParameterCount(); 429 const int parameter_count = register_parameter_count + stack_parameter_count; 430 431 LocationSignature::Builder locations(zone, 0, parameter_count); 432 433 // Add parameters in registers and on the stack. 434 for (int i = 0; i < parameter_count; i++) { 435 if (i < register_parameter_count) { 436 // The first parameters go in registers. 437 Register reg = descriptor.GetRegisterParameter(i); 438 MachineType type = descriptor.GetParameterType(i); 439 locations.AddParam(regloc(reg, type)); 440 } else { 441 // The rest of the parameters go on the stack. 442 int stack_slot = i - register_parameter_count - stack_parameter_count; 443 locations.AddParam(LinkageLocation::ForCallerFrameSlot( 444 stack_slot, MachineType::AnyTagged())); 445 } 446 } 447 448 // The target for interpreter dispatches is a code entry address. 449 MachineType target_type = MachineType::Pointer(); 450 LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type); 451 return new (zone) CallDescriptor( // -- 452 CallDescriptor::kCallAddress, // kind 453 target_type, // target MachineType 454 target_loc, // target location 455 locations.Build(), // location_sig 456 stack_parameter_count, // stack_parameter_count 457 Operator::kNoProperties, // properties 458 kNoCalleeSaved, // callee-saved registers 459 kNoCalleeSaved, // callee-saved fp 460 CallDescriptor::kCanUseRoots | // flags 461 CallDescriptor::kSupportsTailCalls, // flags 462 descriptor.DebugName(isolate)); 463 } 464 465 LinkageLocation Linkage::GetOsrValueLocation(int index) const { 466 CHECK(incoming_->IsJSFunctionCall()); 467 int parameter_count = static_cast<int>(incoming_->JSParameterCount() - 1); 468 int first_stack_slot = OsrHelper::FirstStackSlotIndex(parameter_count); 469 470 if (index == kOsrContextSpillSlotIndex) { 471 // Context. Use the parameter location of the context spill slot. 472 // Parameter (arity + 2) is special for the context of the function frame. 473 // >> context_index = target + receiver + params + new_target + #args 474 int context_index = 1 + 1 + parameter_count + 1 + 1; 475 return incoming_->GetInputLocation(context_index); 476 } else if (index >= first_stack_slot) { 477 // Local variable stored in this (callee) stack. 478 int spill_index = 479 index - first_stack_slot + StandardFrameConstants::kFixedSlotCount; 480 return LinkageLocation::ForCalleeFrameSlot(spill_index, 481 MachineType::AnyTagged()); 482 } else { 483 // Parameter. Use the assigned location from the incoming call descriptor. 484 int parameter_index = 1 + index; // skip index 0, which is the target. 485 return incoming_->GetInputLocation(parameter_index); 486 } 487 } 488 489 490 bool Linkage::ParameterHasSecondaryLocation(int index) const { 491 if (!incoming_->IsJSFunctionCall()) return false; 492 LinkageLocation loc = GetParameterLocation(index); 493 return (loc == regloc(kJSFunctionRegister, MachineType::AnyTagged()) || 494 loc == regloc(kContextRegister, MachineType::AnyTagged())); 495 } 496 497 LinkageLocation Linkage::GetParameterSecondaryLocation(int index) const { 498 DCHECK(ParameterHasSecondaryLocation(index)); 499 LinkageLocation loc = GetParameterLocation(index); 500 501 if (loc == regloc(kJSFunctionRegister, MachineType::AnyTagged())) { 502 return LinkageLocation::ForCalleeFrameSlot(Frame::kJSFunctionSlot, 503 MachineType::AnyTagged()); 504 } else { 505 DCHECK(loc == regloc(kContextRegister, MachineType::AnyTagged())); 506 return LinkageLocation::ForCalleeFrameSlot(Frame::kContextSlot, 507 MachineType::AnyTagged()); 508 } 509 } 510 511 512 } // namespace compiler 513 } // namespace internal 514 } // namespace v8 515