blob: cfeb6c523f03d5f721d3b260858c82cc6cadc7ed [file] [log] [blame]
// Copyright 2015 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/assembler.h"
#include "src/macro-assembler.h"
#include "src/register-configuration.h"
#include "src/wasm/wasm-module.h"
#include "src/compiler/linkage.h"
#include "src/zone.h"
namespace v8 {
namespace internal {
// TODO(titzer): this should not be in the WASM namespace.
namespace wasm {
using compiler::LocationSignature;
using compiler::CallDescriptor;
using compiler::LinkageLocation;
namespace {
MachineType MachineTypeFor(LocalType type) {
switch (type) {
case kAstI32:
return MachineType::Int32();
case kAstI64:
return MachineType::Int64();
case kAstF64:
return MachineType::Float64();
case kAstF32:
return MachineType::Float32();
case kAstS128:
return MachineType::Simd128();
default:
UNREACHABLE();
return MachineType::AnyTagged();
}
}
// Platform-specific configuration for C calling convention.
LinkageLocation regloc(Register reg) {
return LinkageLocation::ForRegister(reg.code());
}
LinkageLocation regloc(DoubleRegister reg) {
return LinkageLocation::ForRegister(reg.code());
}
LinkageLocation stackloc(int i) {
return LinkageLocation::ForCallerFrameSlot(i);
}
#if V8_TARGET_ARCH_IA32
// ===========================================================================
// == ia32 ===================================================================
// ===========================================================================
#define GP_PARAM_REGISTERS eax, edx, ecx, ebx, esi
#define GP_RETURN_REGISTERS eax, edx
#define FP_PARAM_REGISTERS xmm1, xmm2, xmm3, xmm4, xmm5, xmm6
#define FP_RETURN_REGISTERS xmm1, xmm2
#elif V8_TARGET_ARCH_X64
// ===========================================================================
// == x64 ====================================================================
// ===========================================================================
#define GP_PARAM_REGISTERS rax, rdx, rcx, rbx, rsi, rdi
#define GP_RETURN_REGISTERS rax, rdx
#define FP_PARAM_REGISTERS xmm1, xmm2, xmm3, xmm4, xmm5, xmm6
#define FP_RETURN_REGISTERS xmm1, xmm2
#elif V8_TARGET_ARCH_X87
// ===========================================================================
// == x87 ====================================================================
// ===========================================================================
#define GP_PARAM_REGISTERS eax, edx, ecx, ebx, esi
#define GP_RETURN_REGISTERS eax, edx
#define FP_RETURN_REGISTERS stX_0
#elif V8_TARGET_ARCH_ARM
// ===========================================================================
// == arm ====================================================================
// ===========================================================================
#define GP_PARAM_REGISTERS r0, r1, r2, r3
#define GP_RETURN_REGISTERS r0, r1
#define FP_PARAM_REGISTERS d0, d1, d2, d3, d4, d5, d6, d7
#define FP_RETURN_REGISTERS d0, d1
#elif V8_TARGET_ARCH_ARM64
// ===========================================================================
// == arm64 ====================================================================
// ===========================================================================
#define GP_PARAM_REGISTERS x0, x1, x2, x3, x4, x5, x6, x7
#define GP_RETURN_REGISTERS x0, x1
#define FP_PARAM_REGISTERS d0, d1, d2, d3, d4, d5, d6, d7
#define FP_RETURN_REGISTERS d0, d1
#elif V8_TARGET_ARCH_MIPS
// ===========================================================================
// == mips ===================================================================
// ===========================================================================
#define GP_PARAM_REGISTERS a0, a1, a2, a3
#define GP_RETURN_REGISTERS v0, v1
#define FP_PARAM_REGISTERS f2, f4, f6, f8, f10, f12, f14
#define FP_RETURN_REGISTERS f2, f4
#elif V8_TARGET_ARCH_MIPS64
// ===========================================================================
// == mips64 =================================================================
// ===========================================================================
#define GP_PARAM_REGISTERS a0, a1, a2, a3, a4, a5, a6, a7
#define GP_RETURN_REGISTERS v0, v1
#define FP_PARAM_REGISTERS f2, f4, f6, f8, f10, f12, f14
#define FP_RETURN_REGISTERS f2, f4
#elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64
// ===========================================================================
// == ppc & ppc64 ============================================================
// ===========================================================================
#define GP_PARAM_REGISTERS r3, r4, r5, r6, r7, r8, r9, r10
#define GP_RETURN_REGISTERS r3, r4
#define FP_PARAM_REGISTERS d1, d2, d3, d4, d5, d6, d7, d8
#define FP_RETURN_REGISTERS d1, d2
#elif V8_TARGET_ARCH_S390X
// ===========================================================================
// == s390x ==================================================================
// ===========================================================================
#define GP_PARAM_REGISTERS r2, r3, r4, r5, r6
#define GP_RETURN_REGISTERS r2
#define FP_PARAM_REGISTERS d0, d2, d4, d6
#define FP_RETURN_REGISTERS d0, d2, d4, d6
#elif V8_TARGET_ARCH_S390
// ===========================================================================
// == s390 ===================================================================
// ===========================================================================
#define GP_PARAM_REGISTERS r2, r3, r4, r5, r6
#define GP_RETURN_REGISTERS r2, r3
#define FP_PARAM_REGISTERS d0, d2
#define FP_RETURN_REGISTERS d0, d2
#else
// ===========================================================================
// == unknown ================================================================
// ===========================================================================
// Don't define anything. We'll just always use the stack.
#endif
// Helper for allocating either an GP or FP reg, or the next stack slot.
struct Allocator {
Allocator(const Register* gp, int gpc, const DoubleRegister* fp, int fpc)
: gp_count(gpc),
gp_offset(0),
gp_regs(gp),
fp_count(fpc),
fp_offset(0),
fp_regs(fp),
stack_offset(0) {}
int gp_count;
int gp_offset;
const Register* gp_regs;
int fp_count;
int fp_offset;
const DoubleRegister* fp_regs;
int stack_offset;
LinkageLocation Next(LocalType type) {
if (IsFloatingPoint(type)) {
// Allocate a floating point register/stack location.
if (fp_offset < fp_count) {
DoubleRegister reg = fp_regs[fp_offset++];
#if V8_TARGET_ARCH_ARM
// Allocate floats using a double register, but modify the code to
// reflect how ARM FP registers alias.
// TODO(bbudge) Modify wasm linkage to allow use of all float regs.
if (type == kAstF32) {
int float_reg_code = reg.code() * 2;
DCHECK(float_reg_code < RegisterConfiguration::kMaxFPRegisters);
return regloc(DoubleRegister::from_code(float_reg_code));
}
#endif
return regloc(reg);
} else {
int offset = -1 - stack_offset;
stack_offset += Words(type);
return stackloc(offset);
}
} else {
// Allocate a general purpose register/stack location.
if (gp_offset < gp_count) {
return regloc(gp_regs[gp_offset++]);
} else {
int offset = -1 - stack_offset;
stack_offset += Words(type);
return stackloc(offset);
}
}
}
bool IsFloatingPoint(LocalType type) {
return type == kAstF32 || type == kAstF64;
}
int Words(LocalType type) {
if (kPointerSize < 8 && (type == kAstI64 || type == kAstF64)) {
return 2;
}
return 1;
}
};
} // namespace
static Allocator GetReturnRegisters() {
#ifdef GP_RETURN_REGISTERS
static const Register kGPReturnRegisters[] = {GP_RETURN_REGISTERS};
static const int kGPReturnRegistersCount =
static_cast<int>(arraysize(kGPReturnRegisters));
#else
static const Register* kGPReturnRegisters = nullptr;
static const int kGPReturnRegistersCount = 0;
#endif
#ifdef FP_RETURN_REGISTERS
static const DoubleRegister kFPReturnRegisters[] = {FP_RETURN_REGISTERS};
static const int kFPReturnRegistersCount =
static_cast<int>(arraysize(kFPReturnRegisters));
#else
static const DoubleRegister* kFPReturnRegisters = nullptr;
static const int kFPReturnRegistersCount = 0;
#endif
Allocator rets(kGPReturnRegisters, kGPReturnRegistersCount,
kFPReturnRegisters, kFPReturnRegistersCount);
return rets;
}
static Allocator GetParameterRegisters() {
#ifdef GP_PARAM_REGISTERS
static const Register kGPParamRegisters[] = {GP_PARAM_REGISTERS};
static const int kGPParamRegistersCount =
static_cast<int>(arraysize(kGPParamRegisters));
#else
static const Register* kGPParamRegisters = nullptr;
static const int kGPParamRegistersCount = 0;
#endif
#ifdef FP_PARAM_REGISTERS
static const DoubleRegister kFPParamRegisters[] = {FP_PARAM_REGISTERS};
static const int kFPParamRegistersCount =
static_cast<int>(arraysize(kFPParamRegisters));
#else
static const DoubleRegister* kFPParamRegisters = nullptr;
static const int kFPParamRegistersCount = 0;
#endif
Allocator params(kGPParamRegisters, kGPParamRegistersCount, kFPParamRegisters,
kFPParamRegistersCount);
return params;
}
// General code uses the above configuration data.
CallDescriptor* ModuleEnv::GetWasmCallDescriptor(Zone* zone,
FunctionSig* fsig) {
MachineSignature::Builder msig(zone, fsig->return_count(),
fsig->parameter_count());
LocationSignature::Builder locations(zone, fsig->return_count(),
fsig->parameter_count());
Allocator rets = GetReturnRegisters();
// Add return location(s).
const int return_count = static_cast<int>(locations.return_count_);
for (int i = 0; i < return_count; i++) {
LocalType ret = fsig->GetReturn(i);
msig.AddReturn(MachineTypeFor(ret));
locations.AddReturn(rets.Next(ret));
}
Allocator params = GetParameterRegisters();
// Add register and/or stack parameter(s).
const int parameter_count = static_cast<int>(fsig->parameter_count());
for (int i = 0; i < parameter_count; i++) {
LocalType param = fsig->GetParam(i);
msig.AddParam(MachineTypeFor(param));
locations.AddParam(params.Next(param));
}
const RegList kCalleeSaveRegisters = 0;
const RegList kCalleeSaveFPRegisters = 0;
// The target for WASM calls is always 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
msig.Build(), // machine_sig
locations.Build(), // location_sig
params.stack_offset, // stack_parameter_count
compiler::Operator::kNoProperties, // properties
kCalleeSaveRegisters, // callee-saved registers
kCalleeSaveFPRegisters, // callee-saved fp regs
CallDescriptor::kUseNativeStack, // flags
"wasm-call");
}
CallDescriptor* ModuleEnv::GetI32WasmCallDescriptor(
Zone* zone, CallDescriptor* descriptor) {
const MachineSignature* signature = descriptor->GetMachineSignature();
size_t parameter_count = signature->parameter_count();
size_t return_count = signature->return_count();
for (size_t i = 0; i < signature->parameter_count(); i++) {
if (signature->GetParam(i) == MachineType::Int64()) {
// For each int64 input we get two int32 inputs.
parameter_count++;
}
}
for (size_t i = 0; i < signature->return_count(); i++) {
if (signature->GetReturn(i) == MachineType::Int64()) {
// For each int64 return we get two int32 returns.
return_count++;
}
}
if (parameter_count == signature->parameter_count() &&
return_count == signature->return_count()) {
// If there is no int64 parameter or return value, we can just return the
// original descriptor.
return descriptor;
}
MachineSignature::Builder msig(zone, return_count, parameter_count);
LocationSignature::Builder locations(zone, return_count, parameter_count);
Allocator rets = GetReturnRegisters();
for (size_t i = 0; i < signature->return_count(); i++) {
if (signature->GetReturn(i) == MachineType::Int64()) {
// For each int64 return we get two int32 returns.
msig.AddReturn(MachineType::Int32());
msig.AddReturn(MachineType::Int32());
locations.AddReturn(rets.Next(MachineRepresentation::kWord32));
locations.AddReturn(rets.Next(MachineRepresentation::kWord32));
} else {
msig.AddReturn(signature->GetReturn(i));
locations.AddReturn(rets.Next(signature->GetReturn(i).representation()));
}
}
Allocator params = GetParameterRegisters();
for (size_t i = 0; i < signature->parameter_count(); i++) {
if (signature->GetParam(i) == MachineType::Int64()) {
// For each int64 input we get two int32 inputs.
msig.AddParam(MachineType::Int32());
msig.AddParam(MachineType::Int32());
locations.AddParam(params.Next(MachineRepresentation::kWord32));
locations.AddParam(params.Next(MachineRepresentation::kWord32));
} else {
msig.AddParam(signature->GetParam(i));
locations.AddParam(params.Next(signature->GetParam(i).representation()));
}
}
return new (zone) CallDescriptor( // --
descriptor->kind(), // kind
descriptor->GetInputType(0), // target MachineType
descriptor->GetInputLocation(0), // target location
msig.Build(), // machine_sig
locations.Build(), // location_sig
params.stack_offset, // stack_parameter_count
descriptor->properties(), // properties
descriptor->CalleeSavedRegisters(), // callee-saved registers
descriptor->CalleeSavedFPRegisters(), // callee-saved fp regs
descriptor->flags(), // flags
descriptor->debug_name());
return descriptor;
}
} // namespace wasm
} // namespace internal
} // namespace v8