// Copyright 2014 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/ast/scopes.h" #include "src/code-stubs.h" #include "src/compiler.h" #include "src/compiler/common-operator.h" #include "src/compiler/frame.h" #include "src/compiler/linkage.h" #include "src/compiler/node.h" #include "src/compiler/osr.h" #include "src/compiler/pipeline.h" namespace v8 { namespace internal { namespace compiler { namespace { LinkageLocation regloc(Register reg) { return LinkageLocation::ForRegister(reg.code()); } MachineType reptyp(Representation representation) { switch (representation.kind()) { case Representation::kInteger8: return MachineType::Int8(); case Representation::kUInteger8: return MachineType::Uint8(); case Representation::kInteger16: return MachineType::Int16(); case Representation::kUInteger16: return MachineType::Uint16(); case Representation::kInteger32: return MachineType::Int32(); case Representation::kSmi: case Representation::kTagged: case Representation::kHeapObject: return MachineType::AnyTagged(); case Representation::kDouble: return MachineType::Float64(); case Representation::kExternal: return MachineType::Pointer(); case Representation::kNone: case Representation::kNumRepresentations: break; } UNREACHABLE(); return MachineType::None(); } } // namespace std::ostream& operator<<(std::ostream& os, const CallDescriptor::Kind& k) { switch (k) { case CallDescriptor::kCallCodeObject: os << "Code"; break; case CallDescriptor::kCallJSFunction: os << "JS"; break; case CallDescriptor::kCallAddress: os << "Addr"; break; case CallDescriptor::kLazyBailout: os << "LazyBail"; break; } return os; } std::ostream& operator<<(std::ostream& os, const CallDescriptor& d) { // TODO(svenpanne) Output properties etc. and be less cryptic. return os << d.kind() << ":" << d.debug_name() << ":r" << d.ReturnCount() << "s" << d.StackParameterCount() << "i" << d.InputCount() << "f" << d.FrameStateCount() << "t" << d.SupportsTailCalls(); } bool CallDescriptor::HasSameReturnLocationsAs( const CallDescriptor* other) const { if (ReturnCount() != other->ReturnCount()) return false; for (size_t i = 0; i < ReturnCount(); ++i) { if (GetReturnLocation(i) != other->GetReturnLocation(i)) return false; } return true; } bool CallDescriptor::CanTailCall(const Node* node, int* stack_param_delta) const { CallDescriptor const* other = OpParameter(node); size_t current_input = 0; size_t other_input = 0; *stack_param_delta = 0; bool more_other = true; bool more_this = true; while (more_other || more_this) { if (other_input < other->InputCount()) { if (!other->GetInputLocation(other_input).IsRegister()) { (*stack_param_delta)--; } } else { more_other = false; } if (current_input < InputCount()) { if (!GetInputLocation(current_input).IsRegister()) { (*stack_param_delta)++; } } else { more_this = false; } ++current_input; ++other_input; } return HasSameReturnLocationsAs(OpParameter(node)); } CallDescriptor* Linkage::ComputeIncoming(Zone* zone, CompilationInfo* info) { if (info->code_stub() != nullptr) { // Use the code stub interface descriptor. CodeStub* stub = info->code_stub(); CallInterfaceDescriptor descriptor = stub->GetCallInterfaceDescriptor(); return GetStubCallDescriptor( info->isolate(), zone, descriptor, stub->GetStackParameterCount(), CallDescriptor::kNoFlags, Operator::kNoProperties); } if (info->has_literal()) { // If we already have the function literal, use the number of parameters // plus the receiver. return GetJSCallDescriptor(zone, info->is_osr(), 1 + info->literal()->parameter_count(), CallDescriptor::kNoFlags); } if (!info->closure().is_null()) { // If we are compiling a JS function, use a JS call descriptor, // plus the receiver. SharedFunctionInfo* shared = info->closure()->shared(); return GetJSCallDescriptor(zone, info->is_osr(), 1 + shared->internal_formal_parameter_count(), CallDescriptor::kNoFlags); } return nullptr; // TODO(titzer): ? } // static int Linkage::FrameStateInputCount(Runtime::FunctionId function) { // Most runtime functions need a FrameState. A few chosen ones that we know // not to call into arbitrary JavaScript, not to throw, and not to deoptimize // are blacklisted here and can be called without a FrameState. switch (function) { case Runtime::kAllocateInTargetSpace: case Runtime::kCreateIterResultObject: case Runtime::kDefineClassMethod: // TODO(jarin): Is it safe? case Runtime::kDefineGetterPropertyUnchecked: // TODO(jarin): Is it safe? case Runtime::kDefineSetterPropertyUnchecked: // TODO(jarin): Is it safe? case Runtime::kFinalizeClassDefinition: // TODO(conradw): Is it safe? case Runtime::kForInDone: case Runtime::kForInStep: case Runtime::kGetSuperConstructor: case Runtime::kNewClosure: case Runtime::kNewClosure_Tenured: case Runtime::kNewFunctionContext: case Runtime::kPushBlockContext: case Runtime::kPushCatchContext: case Runtime::kReThrow: case Runtime::kStringCompare: case Runtime::kStringEquals: case Runtime::kToFastProperties: // TODO(jarin): Is it safe? case Runtime::kTraceEnter: case Runtime::kTraceExit: return 0; case Runtime::kInlineArguments: case Runtime::kInlineArgumentsLength: case Runtime::kInlineGetPrototype: case Runtime::kInlineRegExpConstructResult: case Runtime::kInlineRegExpExec: case Runtime::kInlineSubString: case Runtime::kInlineToInteger: case Runtime::kInlineToLength: case Runtime::kInlineToName: case Runtime::kInlineToNumber: case Runtime::kInlineToObject: case Runtime::kInlineToPrimitive_Number: case Runtime::kInlineToPrimitive_String: case Runtime::kInlineToPrimitive: case Runtime::kInlineToString: return 1; case Runtime::kInlineCall: case Runtime::kInlineTailCall: case Runtime::kInlineDeoptimizeNow: case Runtime::kInlineThrowNotDateError: return 2; default: break; } // Most inlined runtime functions (except the ones listed above) can be called // without a FrameState or will be lowered by JSIntrinsicLowering internally. const Runtime::Function* const f = Runtime::FunctionForId(function); if (f->intrinsic_type == Runtime::IntrinsicType::INLINE) return 0; return 1; } bool CallDescriptor::UsesOnlyRegisters() const { for (size_t i = 0; i < InputCount(); ++i) { if (!GetInputLocation(i).IsRegister()) return false; } for (size_t i = 0; i < ReturnCount(); ++i) { if (!GetReturnLocation(i).IsRegister()) return false; } return true; } CallDescriptor* Linkage::GetRuntimeCallDescriptor( Zone* zone, Runtime::FunctionId function_id, int js_parameter_count, Operator::Properties properties, CallDescriptor::Flags flags) { const size_t function_count = 1; const size_t num_args_count = 1; const size_t context_count = 1; const size_t parameter_count = function_count + static_cast(js_parameter_count) + num_args_count + context_count; const Runtime::Function* function = Runtime::FunctionForId(function_id); const size_t return_count = static_cast(function->result_size); LocationSignature::Builder locations(zone, return_count, parameter_count); MachineSignature::Builder types(zone, return_count, parameter_count); // Add returns. if (locations.return_count_ > 0) { locations.AddReturn(regloc(kReturnRegister0)); } if (locations.return_count_ > 1) { locations.AddReturn(regloc(kReturnRegister1)); } for (size_t i = 0; i < return_count; i++) { types.AddReturn(MachineType::AnyTagged()); } // All parameters to the runtime call go on the stack. for (int i = 0; i < js_parameter_count; i++) { locations.AddParam( LinkageLocation::ForCallerFrameSlot(i - js_parameter_count)); types.AddParam(MachineType::AnyTagged()); } // Add runtime function itself. locations.AddParam(regloc(kRuntimeCallFunctionRegister)); types.AddParam(MachineType::AnyTagged()); // Add runtime call argument count. locations.AddParam(regloc(kRuntimeCallArgCountRegister)); types.AddParam(MachineType::Pointer()); // Add context. locations.AddParam(regloc(kContextRegister)); types.AddParam(MachineType::AnyTagged()); if (Linkage::FrameStateInputCount(function_id) == 0) { flags = static_cast( flags & ~CallDescriptor::kNeedsFrameState); } // The target for runtime calls is a code object. MachineType target_type = MachineType::AnyTagged(); LinkageLocation target_loc = LinkageLocation::ForAnyRegister(); return new (zone) CallDescriptor( // -- CallDescriptor::kCallCodeObject, // kind target_type, // target MachineType target_loc, // target location types.Build(), // machine_sig locations.Build(), // location_sig js_parameter_count, // stack_parameter_count properties, // properties kNoCalleeSaved, // callee-saved kNoCalleeSaved, // callee-saved fp flags, // flags function->name); // debug name } CallDescriptor* Linkage::GetLazyBailoutDescriptor(Zone* zone) { const size_t return_count = 0; const size_t parameter_count = 0; LocationSignature::Builder locations(zone, return_count, parameter_count); MachineSignature::Builder types(zone, return_count, parameter_count); // The target is ignored, but we need to give some values here. MachineType target_type = MachineType::AnyTagged(); LinkageLocation target_loc = regloc(kJSFunctionRegister); return new (zone) CallDescriptor( // -- CallDescriptor::kLazyBailout, // kind target_type, // target MachineType target_loc, // target location types.Build(), // machine_sig locations.Build(), // location_sig 0, // stack_parameter_count Operator::kNoThrow, // properties kNoCalleeSaved, // callee-saved kNoCalleeSaved, // callee-saved fp CallDescriptor::kNeedsFrameState, // flags "lazy-bailout"); } CallDescriptor* Linkage::GetJSCallDescriptor(Zone* zone, bool is_osr, int js_parameter_count, CallDescriptor::Flags flags) { const size_t return_count = 1; const size_t context_count = 1; const size_t new_target_count = 1; const size_t num_args_count = 1; const size_t parameter_count = js_parameter_count + new_target_count + num_args_count + context_count; LocationSignature::Builder locations(zone, return_count, parameter_count); MachineSignature::Builder types(zone, return_count, parameter_count); // All JS calls have exactly one return value. locations.AddReturn(regloc(kReturnRegister0)); types.AddReturn(MachineType::AnyTagged()); // All parameters to JS calls go on the stack. for (int i = 0; i < js_parameter_count; i++) { int spill_slot_index = i - js_parameter_count; locations.AddParam(LinkageLocation::ForCallerFrameSlot(spill_slot_index)); types.AddParam(MachineType::AnyTagged()); } // Add JavaScript call new target value. locations.AddParam(regloc(kJavaScriptCallNewTargetRegister)); types.AddParam(MachineType::AnyTagged()); // Add JavaScript call argument count. locations.AddParam(regloc(kJavaScriptCallArgCountRegister)); types.AddParam(MachineType::Int32()); // Add context. locations.AddParam(regloc(kContextRegister)); types.AddParam(MachineType::AnyTagged()); // The target for JS function calls is the JSFunction object. MachineType target_type = MachineType::AnyTagged(); // TODO(titzer): When entering into an OSR function from unoptimized code, // the JSFunction is not in a register, but it is on the stack in an // unaddressable spill slot. We hack this in the OSR prologue. Fix. LinkageLocation target_loc = regloc(kJSFunctionRegister); return new (zone) CallDescriptor( // -- CallDescriptor::kCallJSFunction, // kind target_type, // target MachineType target_loc, // target location types.Build(), // machine_sig locations.Build(), // location_sig js_parameter_count, // stack_parameter_count Operator::kNoProperties, // properties kNoCalleeSaved, // callee-saved kNoCalleeSaved, // callee-saved fp CallDescriptor::kCanUseRoots | // flags flags, // flags "js-call"); } CallDescriptor* Linkage::GetInterpreterDispatchDescriptor(Zone* zone) { MachineSignature::Builder types(zone, 0, 6); LocationSignature::Builder locations(zone, 0, 6); // Add registers for fixed parameters passed via interpreter dispatch. STATIC_ASSERT(0 == Linkage::kInterpreterAccumulatorParameter); types.AddParam(MachineType::AnyTagged()); locations.AddParam(regloc(kInterpreterAccumulatorRegister)); STATIC_ASSERT(1 == Linkage::kInterpreterRegisterFileParameter); types.AddParam(MachineType::Pointer()); locations.AddParam(regloc(kInterpreterRegisterFileRegister)); STATIC_ASSERT(2 == Linkage::kInterpreterBytecodeOffsetParameter); types.AddParam(MachineType::IntPtr()); locations.AddParam(regloc(kInterpreterBytecodeOffsetRegister)); STATIC_ASSERT(3 == Linkage::kInterpreterBytecodeArrayParameter); types.AddParam(MachineType::AnyTagged()); locations.AddParam(regloc(kInterpreterBytecodeArrayRegister)); STATIC_ASSERT(4 == Linkage::kInterpreterDispatchTableParameter); types.AddParam(MachineType::Pointer()); #if defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_X87) // TODO(rmcilroy): Make the context param the one spilled to the stack once // Turbofan supports modified stack arguments in tail calls. locations.AddParam( LinkageLocation::ForCallerFrameSlot(kInterpreterDispatchTableSpillSlot)); #else locations.AddParam(regloc(kInterpreterDispatchTableRegister)); #endif STATIC_ASSERT(5 == Linkage::kInterpreterContextParameter); types.AddParam(MachineType::AnyTagged()); locations.AddParam(regloc(kContextRegister)); LinkageLocation target_loc = LinkageLocation::ForAnyRegister(); return new (zone) CallDescriptor( // -- CallDescriptor::kCallCodeObject, // kind MachineType::None(), // target MachineType target_loc, // target location types.Build(), // machine_sig locations.Build(), // location_sig 0, // stack_parameter_count Operator::kNoProperties, // properties kNoCalleeSaved, // callee-saved registers kNoCalleeSaved, // callee-saved fp regs CallDescriptor::kSupportsTailCalls | // flags CallDescriptor::kCanUseRoots, // flags "interpreter-dispatch"); } // TODO(all): Add support for return representations/locations to // CallInterfaceDescriptor. // TODO(turbofan): cache call descriptors for code stub calls. CallDescriptor* Linkage::GetStubCallDescriptor( Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor, int stack_parameter_count, CallDescriptor::Flags flags, Operator::Properties properties, MachineType return_type, size_t return_count) { const int register_parameter_count = descriptor.GetRegisterParameterCount(); const int js_parameter_count = register_parameter_count + stack_parameter_count; const int context_count = 1; const size_t parameter_count = static_cast(js_parameter_count + context_count); LocationSignature::Builder locations(zone, return_count, parameter_count); MachineSignature::Builder types(zone, return_count, parameter_count); // Add returns. if (locations.return_count_ > 0) { locations.AddReturn(regloc(kReturnRegister0)); } if (locations.return_count_ > 1) { locations.AddReturn(regloc(kReturnRegister1)); } for (size_t i = 0; i < return_count; i++) { types.AddReturn(return_type); } // Add parameters in registers and on the stack. for (int i = 0; i < js_parameter_count; i++) { if (i < register_parameter_count) { // The first parameters go in registers. Register reg = descriptor.GetRegisterParameter(i); Representation rep = RepresentationFromType(descriptor.GetParameterType(i)); locations.AddParam(regloc(reg)); types.AddParam(reptyp(rep)); } else { // The rest of the parameters go on the stack. int stack_slot = i - register_parameter_count - stack_parameter_count; locations.AddParam(LinkageLocation::ForCallerFrameSlot(stack_slot)); types.AddParam(MachineType::AnyTagged()); } } // Add context. locations.AddParam(regloc(kContextRegister)); types.AddParam(MachineType::AnyTagged()); // The target for stub calls is a code object. MachineType target_type = MachineType::AnyTagged(); LinkageLocation target_loc = LinkageLocation::ForAnyRegister(); return new (zone) CallDescriptor( // -- CallDescriptor::kCallCodeObject, // kind target_type, // target MachineType target_loc, // target location types.Build(), // machine_sig locations.Build(), // location_sig stack_parameter_count, // stack_parameter_count properties, // properties kNoCalleeSaved, // callee-saved registers kNoCalleeSaved, // callee-saved fp flags, // flags descriptor.DebugName(isolate)); } LinkageLocation Linkage::GetOsrValueLocation(int index) const { CHECK(incoming_->IsJSFunctionCall()); int parameter_count = static_cast(incoming_->JSParameterCount() - 1); int first_stack_slot = OsrHelper::FirstStackSlotIndex(parameter_count); if (index == kOsrContextSpillSlotIndex) { // Context. Use the parameter location of the context spill slot. // Parameter (arity + 2) is special for the context of the function frame. // >> context_index = target + receiver + params + new_target + #args int context_index = 1 + 1 + parameter_count + 1 + 1; return incoming_->GetInputLocation(context_index); } else if (index >= first_stack_slot) { // Local variable stored in this (callee) stack. int spill_index = index - first_stack_slot + StandardFrameConstants::kFixedSlotCount; return LinkageLocation::ForCalleeFrameSlot(spill_index); } else { // Parameter. Use the assigned location from the incoming call descriptor. int parameter_index = 1 + index; // skip index 0, which is the target. return incoming_->GetInputLocation(parameter_index); } } bool Linkage::ParameterHasSecondaryLocation(int index) const { if (incoming_->kind() != CallDescriptor::kCallJSFunction) return false; LinkageLocation loc = GetParameterLocation(index); return (loc == regloc(kJSFunctionRegister) || loc == regloc(kContextRegister)); } LinkageLocation Linkage::GetParameterSecondaryLocation(int index) const { DCHECK(ParameterHasSecondaryLocation(index)); LinkageLocation loc = GetParameterLocation(index); if (loc == regloc(kJSFunctionRegister)) { return LinkageLocation::ForCalleeFrameSlot(Frame::kJSFunctionSlot); } else { DCHECK(loc == regloc(kContextRegister)); return LinkageLocation::ForCalleeFrameSlot(Frame::kContextSlot); } } } // namespace compiler } // namespace internal } // namespace v8