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