Commit 0c68bef8 by David Sehr

Merge lowerCall and lowerRet between x86 and x64

BUG= R=jpp@chromium.org, stichnot@chromium.org Review URL: https://codereview.chromium.org/1592033002 .
parent 29d15fd6
...@@ -153,259 +153,46 @@ void TargetX8632::lowerIndirectJump(Variable *JumpTarget) { ...@@ -153,259 +153,46 @@ void TargetX8632::lowerIndirectJump(Variable *JumpTarget) {
_jmp(JumpTarget); _jmp(JumpTarget);
} }
void TargetX8632::lowerCall(const InstCall *Instr) { Inst *TargetX8632::emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) {
// x86-32 calling convention: std::unique_ptr<AutoBundle> Bundle;
// if (NeedSandboxing) {
// * At the point before the call, the stack must be aligned to 16 bytes. if (llvm::isa<Constant>(CallTarget)) {
// Bundle = makeUnique<AutoBundle>(this, InstBundleLock::Opt_AlignToEnd);
// * The first four arguments of vector type, regardless of their position
// relative to the other arguments in the argument list, are placed in
// registers xmm0 - xmm3.
//
// * Other arguments are pushed onto the stack in right-to-left order, such
// that the left-most argument ends up on the top of the stack at the lowest
// memory address.
//
// * Stack arguments of vector type are aligned to start at the next highest
// multiple of 16 bytes. Other stack arguments are aligned to 4 bytes.
//
// This intends to match the section "IA-32 Function Calling Convention" of
// the document "OS X ABI Function Call Guide" by Apple.
NeedsStackAlignment = true;
OperandList XmmArgs;
OperandList StackArgs, StackArgLocations;
int32_t ParameterAreaSizeBytes = 0;
// Classify each argument operand according to the location where the
// argument is passed.
for (SizeT i = 0, NumArgs = Instr->getNumArgs(); i < NumArgs; ++i) {
Operand *Arg = Instr->getArg(i);
Type Ty = Arg->getType();
// The PNaCl ABI requires the width of arguments to be at least 32 bits.
assert(typeWidthInBytes(Ty) >= 4);
if (isVectorType(Ty) && XmmArgs.size() < Traits::X86_MAX_XMM_ARGS) {
XmmArgs.push_back(Arg);
} else {
StackArgs.push_back(Arg);
if (isVectorType(Arg->getType())) {
ParameterAreaSizeBytes =
Traits::applyStackAlignment(ParameterAreaSizeBytes);
}
Variable *esp =
Func->getTarget()->getPhysicalRegister(Traits::RegisterSet::Reg_esp);
Constant *Loc = Ctx->getConstantInt32(ParameterAreaSizeBytes);
auto *Mem = Traits::X86OperandMem::create(Func, Ty, esp, Loc);
StackArgLocations.push_back(Mem);
ParameterAreaSizeBytes += typeWidthInBytesOnStack(Arg->getType());
}
}
// Ensure there is enough space for the fstp/movs for floating returns.
Variable *Dest = Instr->getDest();
if (Dest != nullptr && isScalarFloatingType(Dest->getType())) {
ParameterAreaSizeBytes =
std::max(static_cast<size_t>(ParameterAreaSizeBytes),
typeWidthInBytesOnStack(Dest->getType()));
}
// Adjust the parameter area so that the stack is aligned. It is assumed that
// the stack is already aligned at the start of the calling sequence.
ParameterAreaSizeBytes = Traits::applyStackAlignment(ParameterAreaSizeBytes);
assert(static_cast<uint32_t>(ParameterAreaSizeBytes) <=
maxOutArgsSizeBytes());
// Copy arguments that are passed on the stack to the appropriate stack
// locations.
for (SizeT i = 0, e = StackArgs.size(); i < e; ++i) {
lowerStore(InstStore::create(Func, StackArgs[i], StackArgLocations[i]));
}
// Copy arguments to be passed in registers to the appropriate registers.
// TODO: Investigate the impact of lowering arguments passed in registers
// after lowering stack arguments as opposed to the other way around.
// Lowering register arguments after stack arguments may reduce register
// pressure. On the other hand, lowering register arguments first (before
// stack arguments) may result in more compact code, as the memory operand
// displacements may end up being smaller before any stack adjustment is
// done.
for (SizeT i = 0, NumXmmArgs = XmmArgs.size(); i < NumXmmArgs; ++i) {
Variable *Reg =
legalizeToReg(XmmArgs[i], Traits::RegisterSet::Reg_xmm0 + i);
// Generate a FakeUse of register arguments so that they do not get dead
// code eliminated as a result of the FakeKill of scratch registers after
// the call.
Context.insert<InstFakeUse>(Reg);
}
// Generate the call instruction. Assign its result to a temporary with high
// register allocation weight.
// ReturnReg doubles as ReturnRegLo as necessary.
Variable *ReturnReg = nullptr;
Variable *ReturnRegHi = nullptr;
if (Dest) {
const Type DestTy = Dest->getType();
switch (DestTy) {
case IceType_NUM:
case IceType_void:
case IceType_i1:
case IceType_i8:
case IceType_i16:
llvm::report_fatal_error("Invalid Call dest type");
break;
case IceType_i32:
ReturnReg = makeReg(DestTy, Traits::RegisterSet::Reg_eax);
break;
case IceType_i64:
ReturnReg = makeReg(IceType_i32, Traits::RegisterSet::Reg_eax);
ReturnRegHi = makeReg(IceType_i32, Traits::RegisterSet::Reg_edx);
break;
case IceType_f32:
case IceType_f64:
// Leave ReturnReg==ReturnRegHi==nullptr, and capture the result with the
// fstp instruction.
break;
case IceType_v4i1:
case IceType_v8i1:
case IceType_v16i1:
case IceType_v16i8:
case IceType_v8i16:
case IceType_v4i32:
case IceType_v4f32:
ReturnReg = makeReg(DestTy, Traits::RegisterSet::Reg_xmm0);
break;
}
}
Operand *CallTarget =
legalize(Instr->getCallTarget(), Legal_Reg | Legal_Imm | Legal_AddrAbs);
Traits::Insts::Call *NewCall;
/* AutoBundle scoping */ {
std::unique_ptr<AutoBundle> Bundle;
if (NeedSandboxing) {
if (llvm::isa<Constant>(CallTarget)) {
Bundle = makeUnique<AutoBundle>(this, InstBundleLock::Opt_AlignToEnd);
} else {
Variable *CallTargetVar = nullptr;
_mov(CallTargetVar, CallTarget);
Bundle = makeUnique<AutoBundle>(this, InstBundleLock::Opt_AlignToEnd);
const SizeT BundleSize =
1 << Func->getAssembler<>()->getBundleAlignLog2Bytes();
_and(CallTargetVar, Ctx->getConstantInt32(~(BundleSize - 1)));
CallTarget = CallTargetVar;
}
}
NewCall = Context.insert<Traits::Insts::Call>(ReturnReg, CallTarget);
}
if (ReturnRegHi)
Context.insert<InstFakeDef>(ReturnRegHi);
// Insert a register-kill pseudo instruction.
Context.insert<InstFakeKill>(NewCall);
if (Dest != nullptr && isScalarFloatingType(Dest->getType())) {
// Special treatment for an FP function which returns its result in st(0).
// If Dest ends up being a physical xmm register, the fstp emit code will
// route st(0) through the space reserved in the function argument area
// we allocated.
_fstp(Dest);
// Create a fake use of Dest in case it actually isn't used, because st(0)
// still needs to be popped.
Context.insert<InstFakeUse>(Dest);
}
// Generate a FakeUse to keep the call live if necessary.
if (Instr->hasSideEffects() && ReturnReg) {
Context.insert<InstFakeUse>(ReturnReg);
}
if (!Dest)
return;
// Assign the result of the call to Dest.
if (ReturnReg) {
if (ReturnRegHi) {
auto *Dest64On32 = llvm::cast<Variable64On32>(Dest);
Variable *DestLo = Dest64On32->getLo();
Variable *DestHi = Dest64On32->getHi();
_mov(DestLo, ReturnReg);
_mov(DestHi, ReturnRegHi);
} else { } else {
const Type DestTy = Dest->getType(); Variable *CallTargetVar = nullptr;
assert(DestTy == IceType_i32 || DestTy == IceType_i16 || _mov(CallTargetVar, CallTarget);
DestTy == IceType_i8 || DestTy == IceType_i1 || Bundle = makeUnique<AutoBundle>(this, InstBundleLock::Opt_AlignToEnd);
isVectorType(DestTy)); const SizeT BundleSize =
if (isVectorType(DestTy)) { 1 << Func->getAssembler<>()->getBundleAlignLog2Bytes();
_movp(Dest, ReturnReg); _and(CallTargetVar, Ctx->getConstantInt32(~(BundleSize - 1)));
} else { CallTarget = CallTargetVar;
_mov(Dest, ReturnReg);
}
} }
} }
return Context.insert<Traits::Insts::Call>(ReturnReg, CallTarget);
} }
void TargetX8632::lowerArguments() { Variable *TargetX8632::moveReturnValueToRegister(Operand *Value,
VarList &Args = Func->getArgs(); Type ReturnType) {
// The first four arguments of vector type, regardless of their position if (isVectorType(ReturnType)) {
// relative to the other arguments in the argument list, are passed in return legalizeToReg(Value, Traits::RegisterSet::Reg_xmm0);
// registers xmm0 - xmm3. } else if (isScalarFloatingType(ReturnType)) {
unsigned NumXmmArgs = 0; _fld(Value);
return nullptr;
Context.init(Func->getEntryNode()); } else {
Context.setInsertPoint(Context.getCur()); assert(ReturnType == IceType_i32 || ReturnType == IceType_i64);
if (ReturnType == IceType_i64) {
for (SizeT I = 0, E = Args.size();
I < E && NumXmmArgs < Traits::X86_MAX_XMM_ARGS; ++I) {
Variable *Arg = Args[I];
Type Ty = Arg->getType();
if (!isVectorType(Ty))
continue;
// Replace Arg in the argument list with the home register. Then generate
// an instruction in the prolog to copy the home register to the assigned
// location of Arg.
int32_t RegNum = Traits::RegisterSet::Reg_xmm0 + NumXmmArgs;
++NumXmmArgs;
Variable *RegisterArg = Func->makeVariable(Ty);
if (BuildDefs::dump())
RegisterArg->setName(Func, "home_reg:" + Arg->getName(Func));
RegisterArg->setRegNum(RegNum);
RegisterArg->setIsArg();
Arg->setIsArg(false);
Args[I] = RegisterArg;
Context.insert<InstAssign>(Arg, RegisterArg);
}
}
void TargetX8632::lowerRet(const InstRet *Inst) {
Variable *Reg = nullptr;
if (Inst->hasRetValue()) {
Operand *Src0 = legalize(Inst->getRetValue());
const Type Src0Ty = Src0->getType();
// TODO(jpp): this is not needed.
if (Src0Ty == IceType_i64) {
Variable *eax = Variable *eax =
legalizeToReg(loOperand(Src0), Traits::RegisterSet::Reg_eax); legalizeToReg(loOperand(Value), Traits::RegisterSet::Reg_eax);
Variable *edx = Variable *edx =
legalizeToReg(hiOperand(Src0), Traits::RegisterSet::Reg_edx); legalizeToReg(hiOperand(Value), Traits::RegisterSet::Reg_edx);
Reg = eax;
Context.insert<InstFakeUse>(edx); Context.insert<InstFakeUse>(edx);
} else if (isScalarFloatingType(Src0Ty)) { return eax;
_fld(Src0);
} else if (isVectorType(Src0Ty)) {
Reg = legalizeToReg(Src0, Traits::RegisterSet::Reg_xmm0);
} else { } else {
assert(Src0Ty == IceType_i32); Variable *Reg = nullptr;
_mov(Reg, Src0, Traits::RegisterSet::Reg_eax); _mov(Reg, Value, Traits::RegisterSet::Reg_eax);
return Reg;
} }
} }
// Add a ret instruction even if sandboxing is enabled, because addEpilog
// explicitly looks for a ret instruction as a marker for where to insert the
// frame removal instructions.
_ret(Reg);
// Add a fake use of esp to make sure esp stays alive for the entire
// function. Otherwise post-call esp adjustments get dead-code eliminated.
keepEspLiveAtExit();
} }
void TargetX8632::addProlog(CfgNode *Node) { void TargetX8632::addProlog(CfgNode *Node) {
......
...@@ -58,9 +58,8 @@ protected: ...@@ -58,9 +58,8 @@ protected:
void initSandbox() {} void initSandbox() {}
void lowerIndirectJump(Variable *JumpTarget); void lowerIndirectJump(Variable *JumpTarget);
void lowerCall(const InstCall *Instr) override; Inst *emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) override;
void lowerArguments() override; Variable *moveReturnValueToRegister(Operand *Value, Type ReturnType) override;
void lowerRet(const InstRet *Inst) override;
void addProlog(CfgNode *Node) override; void addProlog(CfgNode *Node) override;
void addEpilog(CfgNode *Node) override; void addEpilog(CfgNode *Node) override;
......
...@@ -68,10 +68,11 @@ struct TargetX8632Traits { ...@@ -68,10 +68,11 @@ struct TargetX8632Traits {
using Cond = ::Ice::CondX86; using Cond = ::Ice::CondX86;
using RegisterSet = ::Ice::RegX8632; using RegisterSet = ::Ice::RegX8632;
static const SizeT StackPtr = RegX8632::Reg_esp; static constexpr SizeT StackPtr = RegX8632::Reg_esp;
static const SizeT FramePtr = RegX8632::Reg_ebp; static constexpr SizeT FramePtr = RegX8632::Reg_ebp;
static const GPRRegister Encoded_Reg_Accumulator = RegX8632::Encoded_Reg_eax; static constexpr GPRRegister Encoded_Reg_Accumulator =
static const GPRRegister Encoded_Reg_Counter = RegX8632::Encoded_Reg_ecx; RegX8632::Encoded_Reg_eax;
static constexpr GPRRegister Encoded_Reg_Counter = RegX8632::Encoded_Reg_ecx;
static constexpr FixupKind FK_PcRel = llvm::ELF::R_386_PC32; static constexpr FixupKind FK_PcRel = llvm::ELF::R_386_PC32;
static constexpr FixupKind FK_Abs = llvm::ELF::R_386_32; static constexpr FixupKind FK_Abs = llvm::ELF::R_386_32;
static constexpr FixupKind FK_Gotoff = llvm::ELF::R_386_GOTOFF; static constexpr FixupKind FK_Gotoff = llvm::ELF::R_386_GOTOFF;
...@@ -658,21 +659,50 @@ public: ...@@ -658,21 +659,50 @@ public:
llvm::report_fatal_error("no rdx in non-64-bit mode."); llvm::report_fatal_error("no rdx in non-64-bit mode.");
} }
// x86-32 calling convention:
//
// * The first four arguments of vector type, regardless of their position
// relative to the other arguments in the argument list, are placed in
// registers xmm0 - xmm3.
//
// This intends to match the section "IA-32 Function Calling Convention" of
// the document "OS X ABI Function Call Guide" by Apple.
/// The maximum number of arguments to pass in XMM registers /// The maximum number of arguments to pass in XMM registers
static const uint32_t X86_MAX_XMM_ARGS = 4; static constexpr uint32_t X86_MAX_XMM_ARGS = 4;
/// The maximum number of arguments to pass in GPR registers /// The maximum number of arguments to pass in GPR registers
static const uint32_t X86_MAX_GPR_ARGS = 0; static constexpr uint32_t X86_MAX_GPR_ARGS = 0;
/// Whether scalar floating point arguments are passed in XMM registers
static constexpr bool X86_PASS_SCALAR_FP_IN_XMM = false;
/// Get the register for a given argument slot in the XMM registers.
static int32_t getRegisterForXmmArgNum(uint32_t ArgNum) {
// TODO(sehr): Change to use the CCArg technique used in ARM32.
static_assert(RegisterSet::Reg_xmm0 + 1 == RegisterSet::Reg_xmm1,
"Inconsistency between XMM register numbers and ordinals");
if (ArgNum >= X86_MAX_XMM_ARGS) {
return Variable::NoRegister;
}
return static_cast<int32_t>(RegisterSet::Reg_xmm0 + ArgNum);
}
/// Get the register for a given argument slot in the GPRs.
static int32_t getRegisterForGprArgNum(Type Ty, uint32_t ArgNum) {
assert(Ty == IceType_i64 || Ty == IceType_i32);
(void)Ty;
(void)ArgNum;
return Variable::NoRegister;
}
/// The number of bits in a byte /// The number of bits in a byte
static const uint32_t X86_CHAR_BIT = 8; static constexpr uint32_t X86_CHAR_BIT = 8;
/// Stack alignment. This is defined in IceTargetLoweringX8632.cpp because it /// Stack alignment. This is defined in IceTargetLoweringX8632.cpp because it
/// is used as an argument to std::max(), and the default std::less<T> has an /// is used as an argument to std::max(), and the default std::less<T> has an
/// operator(T const&, T const&) which requires this member to have an /// operator(T const&, T const&) which requires this member to have an
/// address. /// address.
static const uint32_t X86_STACK_ALIGNMENT_BYTES; static const uint32_t X86_STACK_ALIGNMENT_BYTES;
/// Size of the return address on the stack /// Size of the return address on the stack
static const uint32_t X86_RET_IP_SIZE_BYTES = 4; static constexpr uint32_t X86_RET_IP_SIZE_BYTES = 4;
/// The number of different NOP instructions /// The number of different NOP instructions
static const uint32_t X86_NUM_NOP_VARIANTS = 5; static constexpr uint32_t X86_NUM_NOP_VARIANTS = 5;
/// \name Limits for unrolling memory intrinsics. /// \name Limits for unrolling memory intrinsics.
/// @{ /// @{
......
...@@ -384,209 +384,11 @@ void TargetX8664::lowerIndirectJump(Variable *JumpTarget) { ...@@ -384,209 +384,11 @@ void TargetX8664::lowerIndirectJump(Variable *JumpTarget) {
_jmp(JumpTarget); _jmp(JumpTarget);
} }
namespace { Inst *TargetX8664::emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) {
static inline TargetX8664::Traits::RegisterSet::AllRegisters
getRegisterForXmmArgNum(uint32_t ArgNum) {
assert(ArgNum < TargetX8664::Traits::X86_MAX_XMM_ARGS);
return static_cast<TargetX8664::Traits::RegisterSet::AllRegisters>(
TargetX8664::Traits::RegisterSet::Reg_xmm0 + ArgNum);
}
static inline TargetX8664::Traits::RegisterSet::AllRegisters
getRegisterForGprArgNum(Type Ty, uint32_t ArgNum) {
assert(ArgNum < TargetX8664::Traits::X86_MAX_GPR_ARGS);
static const TargetX8664::Traits::RegisterSet::AllRegisters GprForArgNum[] = {
TargetX8664::Traits::RegisterSet::Reg_rdi,
TargetX8664::Traits::RegisterSet::Reg_rsi,
TargetX8664::Traits::RegisterSet::Reg_rdx,
TargetX8664::Traits::RegisterSet::Reg_rcx,
TargetX8664::Traits::RegisterSet::Reg_r8,
TargetX8664::Traits::RegisterSet::Reg_r9,
};
static_assert(llvm::array_lengthof(GprForArgNum) ==
TargetX8664::TargetX8664::Traits::X86_MAX_GPR_ARGS,
"Mismatch between MAX_GPR_ARGS and GprForArgNum.");
assert(Ty == IceType_i64 || Ty == IceType_i32);
return static_cast<TargetX8664::Traits::RegisterSet::AllRegisters>(
TargetX8664::Traits::getGprForType(Ty, GprForArgNum[ArgNum]));
}
// constexprMax returns a (constexpr) max(S0, S1), and it is used for defining
// OperandList in lowerCall. std::max() is supposed to work, but it doesn't.
constexpr SizeT constexprMax(SizeT S0, SizeT S1) { return S0 < S1 ? S1 : S0; }
} // end of anonymous namespace
void TargetX8664::lowerCall(const InstCall *Instr) {
// x86-64 calling convention:
//
// * At the point before the call, the stack must be aligned to 16 bytes.
//
// * The first eight arguments of vector/fp type, regardless of their
// position relative to the other arguments in the argument list, are placed
// in registers %xmm0 - %xmm7.
//
// * The first six arguments of integer types, regardless of their position
// relative to the other arguments in the argument list, are placed in
// registers %rdi, %rsi, %rdx, %rcx, %r8, and %r9.
//
// * Other arguments are pushed onto the stack in right-to-left order, such
// that the left-most argument ends up on the top of the stack at the lowest
// memory address.
//
// * Stack arguments of vector type are aligned to start at the next highest
// multiple of 16 bytes. Other stack arguments are aligned to 8 bytes.
//
// This intends to match the section "Function Calling Sequence" of the
// document "System V Application Binary Interface."
NeedsStackAlignment = true;
using OperandList =
llvm::SmallVector<Operand *, constexprMax(Traits::X86_MAX_XMM_ARGS,
Traits::X86_MAX_GPR_ARGS)>;
OperandList XmmArgs;
CfgVector<std::pair<const Type, Operand *>> GprArgs;
OperandList StackArgs, StackArgLocations;
int32_t ParameterAreaSizeBytes = 0;
// Classify each argument operand according to the location where the
// argument is passed.
for (SizeT i = 0, NumArgs = Instr->getNumArgs(); i < NumArgs; ++i) {
Operand *Arg = Instr->getArg(i);
Type Ty = Arg->getType();
// The PNaCl ABI requires the width of arguments to be at least 32 bits.
assert(typeWidthInBytes(Ty) >= 4);
if (isVectorType(Ty) && XmmArgs.size() < Traits::X86_MAX_XMM_ARGS) {
XmmArgs.push_back(Arg);
} else if (isScalarFloatingType(Ty) &&
XmmArgs.size() < Traits::X86_MAX_XMM_ARGS) {
XmmArgs.push_back(Arg);
} else if (isScalarIntegerType(Ty) &&
GprArgs.size() < Traits::X86_MAX_GPR_ARGS) {
GprArgs.emplace_back(Ty, Arg);
} else {
StackArgs.push_back(Arg);
if (isVectorType(Arg->getType())) {
ParameterAreaSizeBytes =
Traits::applyStackAlignment(ParameterAreaSizeBytes);
}
Variable *esp =
getPhysicalRegister(Traits::RegisterSet::Reg_rsp, IceType_i64);
Constant *Loc = Ctx->getConstantInt32(ParameterAreaSizeBytes);
StackArgLocations.push_back(
Traits::X86OperandMem::create(Func, Ty, esp, Loc));
ParameterAreaSizeBytes += typeWidthInBytesOnStack(Arg->getType());
}
}
// Adjust the parameter area so that the stack is aligned. It is assumed that
// the stack is already aligned at the start of the calling sequence.
ParameterAreaSizeBytes = Traits::applyStackAlignment(ParameterAreaSizeBytes);
assert(static_cast<uint32_t>(ParameterAreaSizeBytes) <=
maxOutArgsSizeBytes());
// Copy arguments that are passed on the stack to the appropriate stack
// locations.
for (SizeT i = 0, e = StackArgs.size(); i < e; ++i) {
lowerStore(InstStore::create(Func, StackArgs[i], StackArgLocations[i]));
}
// Copy arguments to be passed in registers to the appropriate registers.
// TODO: Investigate the impact of lowering arguments passed in registers
// after lowering stack arguments as opposed to the other way around.
// Lowering register arguments after stack arguments may reduce register
// pressure. On the other hand, lowering register arguments first (before
// stack arguments) may result in more compact code, as the memory operand
// displacements may end up being smaller before any stack adjustment is
// done.
for (SizeT i = 0, NumXmmArgs = XmmArgs.size(); i < NumXmmArgs; ++i) {
Variable *Reg = legalizeToReg(XmmArgs[i], getRegisterForXmmArgNum(i));
// Generate a FakeUse of register arguments so that they do not get dead
// code eliminated as a result of the FakeKill of scratch registers after
// the call.
Context.insert<InstFakeUse>(Reg);
}
for (SizeT i = 0, NumGprArgs = GprArgs.size(); i < NumGprArgs; ++i) {
const Type SignatureTy = GprArgs[i].first;
Operand *Arg = GprArgs[i].second;
Variable *Reg =
legalizeToReg(Arg, getRegisterForGprArgNum(Arg->getType(), i));
assert(SignatureTy == IceType_i64 || SignatureTy == IceType_i32);
if (SignatureTy != Arg->getType()) {
if (SignatureTy == IceType_i32) {
assert(Arg->getType() == IceType_i64);
Variable *T = makeReg(
IceType_i32, Traits::getGprForType(IceType_i32, Reg->getRegNum()));
_mov(T, Reg);
Reg = T;
} else {
// This branch has never been reached, so we leave the assert(false)
// here until we figure out how to exercise it.
assert(false);
assert(Arg->getType() == IceType_i32);
Variable *T = makeReg(
IceType_i64, Traits::getGprForType(IceType_i64, Reg->getRegNum()));
_movzx(T, Reg);
Reg = T;
}
}
Context.insert<InstFakeUse>(Reg);
}
// Generate the call instruction. Assign its result to a temporary with high
// register allocation weight.
Variable *Dest = Instr->getDest();
// ReturnReg doubles as ReturnRegLo as necessary.
Variable *ReturnReg = nullptr;
if (Dest) {
switch (Dest->getType()) {
case IceType_NUM:
case IceType_void:
llvm::report_fatal_error("Invalid Call dest type");
break;
case IceType_i1:
case IceType_i8:
case IceType_i16:
// The bitcode should never return an i1, i8, or i16.
assert(false);
// Fallthrough intended.
case IceType_i32:
ReturnReg = makeReg(Dest->getType(), Traits::RegisterSet::Reg_eax);
break;
case IceType_i64:
ReturnReg = makeReg(Dest->getType(), Traits::RegisterSet::Reg_rax);
break;
case IceType_f32:
case IceType_f64:
case IceType_v4i1:
case IceType_v8i1:
case IceType_v16i1:
case IceType_v16i8:
case IceType_v8i16:
case IceType_v4i32:
case IceType_v4f32:
ReturnReg = makeReg(Dest->getType(), Traits::RegisterSet::Reg_xmm0);
break;
}
}
InstX86Label *ReturnAddress = nullptr;
Operand *CallTarget =
legalize(Instr->getCallTarget(), Legal_Reg | Legal_Imm | Legal_AddrAbs);
auto *CallTargetR = llvm::dyn_cast<Variable>(CallTarget);
Inst *NewCall = nullptr; Inst *NewCall = nullptr;
if (!NeedSandboxing) { auto *CallTargetR = llvm::dyn_cast<Variable>(CallTarget);
if (CallTargetR != nullptr) { if (NeedSandboxing) {
// x86-64 in Subzero is ILP32. Therefore, CallTarget is i32, but the InstX86Label *ReturnAddress = InstX86Label::create(Func, this);
// emitted call needs a i64 register (for textual asm.)
Variable *T = makeReg(IceType_i64);
_movzx(T, CallTargetR);
CallTarget = T;
}
NewCall = Context.insert<Traits::Insts::Call>(ReturnReg, CallTarget);
} else {
ReturnAddress = InstX86Label::create(Func, this);
ReturnAddress->setIsReturnLocation(true); ReturnAddress->setIsReturnLocation(true);
constexpr bool SuppressMangling = true; constexpr bool SuppressMangling = true;
/* AutoBundle scoping */ { /* AutoBundle scoping */ {
...@@ -620,102 +422,30 @@ void TargetX8664::lowerCall(const InstCall *Instr) { ...@@ -620,102 +422,30 @@ void TargetX8664::lowerCall(const InstCall *Instr) {
} }
Context.insert(ReturnAddress); Context.insert(ReturnAddress);
}
// Insert a register-kill pseudo instruction.
Context.insert<InstFakeKill>(NewCall);
// Generate a FakeUse to keep the call live if necessary.
if (Instr->hasSideEffects() && ReturnReg) {
Context.insert<InstFakeUse>(ReturnReg);
}
if (!Dest)
return;
assert(ReturnReg && "x86-64 always returns value on registers.");
if (isVectorType(Dest->getType())) {
_movp(Dest, ReturnReg);
} else { } else {
assert(isScalarFloatingType(Dest->getType()) || if (CallTargetR != nullptr) {
isScalarIntegerType(Dest->getType())); // x86-64 in Subzero is ILP32. Therefore, CallTarget is i32, but the
_mov(Dest, ReturnReg); // emitted call needs a i64 register (for textual asm.)
} Variable *T = makeReg(IceType_i64);
} _movzx(T, CallTargetR);
CallTarget = T;
void TargetX8664::lowerArguments() {
VarList &Args = Func->getArgs();
// The first eight vector typed arguments (as well as fp arguments) are
// passed in %xmm0 through %xmm7 regardless of their position in the argument
// list.
unsigned NumXmmArgs = 0;
// The first six integer typed arguments are passed in %rdi, %rsi, %rdx,
// %rcx, %r8, and %r9 regardless of their position in the argument list.
unsigned NumGprArgs = 0;
Context.init(Func->getEntryNode());
Context.setInsertPoint(Context.getCur());
for (SizeT i = 0, End = Args.size();
i < End && (NumXmmArgs < Traits::X86_MAX_XMM_ARGS ||
NumGprArgs < Traits::X86_MAX_XMM_ARGS);
++i) {
Variable *Arg = Args[i];
Type Ty = Arg->getType();
Variable *RegisterArg = nullptr;
int32_t RegNum = Variable::NoRegister;
if ((isVectorType(Ty) || isScalarFloatingType(Ty))) {
if (NumXmmArgs >= Traits::X86_MAX_XMM_ARGS) {
continue;
}
RegNum = getRegisterForXmmArgNum(NumXmmArgs);
++NumXmmArgs;
RegisterArg = Func->makeVariable(Ty);
} else if (isScalarIntegerType(Ty)) {
if (NumGprArgs >= Traits::X86_MAX_GPR_ARGS) {
continue;
}
RegNum = getRegisterForGprArgNum(Ty, NumGprArgs);
++NumGprArgs;
RegisterArg = Func->makeVariable(Ty);
} }
assert(RegNum != Variable::NoRegister); NewCall = Context.insert<Traits::Insts::Call>(ReturnReg, CallTarget);
assert(RegisterArg != nullptr);
// Replace Arg in the argument list with the home register. Then generate
// an instruction in the prolog to copy the home register to the assigned
// location of Arg.
if (BuildDefs::dump())
RegisterArg->setName(Func, "home_reg:" + Arg->getName(Func));
RegisterArg->setRegNum(RegNum);
RegisterArg->setIsArg();
Arg->setIsArg(false);
Args[i] = RegisterArg;
Context.insert<InstAssign>(Arg, RegisterArg);
} }
return NewCall;
} }
void TargetX8664::lowerRet(const InstRet *Inst) { Variable *TargetX8664::moveReturnValueToRegister(Operand *Value,
Variable *Reg = nullptr; Type ReturnType) {
if (Inst->hasRetValue()) { if (isVectorType(ReturnType) || isScalarFloatingType(ReturnType)) {
Operand *Src0 = legalize(Inst->getRetValue()); return legalizeToReg(Value, Traits::RegisterSet::Reg_xmm0);
const Type Src0Ty = Src0->getType(); } else {
if (isVectorType(Src0Ty) || isScalarFloatingType(Src0Ty)) { assert(ReturnType == IceType_i32 || ReturnType == IceType_i64);
Reg = legalizeToReg(Src0, Traits::RegisterSet::Reg_xmm0); Variable *Reg = nullptr;
} else { _mov(Reg, Value,
assert(Src0Ty == IceType_i32 || Src0Ty == IceType_i64); Traits::getGprForType(ReturnType, Traits::RegisterSet::Reg_rax));
_mov(Reg, Src0, return Reg;
Traits::getGprForType(Src0Ty, Traits::RegisterSet::Reg_rax));
}
} }
// Add a ret instruction even if sandboxing is enabled, because addEpilog
// explicitly looks for a ret instruction as a marker for where to insert the
// frame removal instructions.
_ret(Reg);
// Add a fake use of esp to make sure esp stays alive for the entire
// function. Otherwise post-call esp adjustments get dead-code eliminated.
keepEspLiveAtExit();
} }
void TargetX8664::addProlog(CfgNode *Node) { void TargetX8664::addProlog(CfgNode *Node) {
......
...@@ -60,9 +60,8 @@ protected: ...@@ -60,9 +60,8 @@ protected:
void initSandbox(); void initSandbox();
void lowerIndirectJump(Variable *JumpTarget); void lowerIndirectJump(Variable *JumpTarget);
void lowerCall(const InstCall *Instr) override; Inst *emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) override;
void lowerArguments() override; Variable *moveReturnValueToRegister(Operand *Value, Type ReturnType) override;
void lowerRet(const InstRet *Inst) override;
void addProlog(CfgNode *Node) override; void addProlog(CfgNode *Node) override;
void addEpilog(CfgNode *Node) override; void addEpilog(CfgNode *Node) override;
......
...@@ -68,10 +68,11 @@ struct TargetX8664Traits { ...@@ -68,10 +68,11 @@ struct TargetX8664Traits {
using Cond = ::Ice::CondX8664; using Cond = ::Ice::CondX8664;
using RegisterSet = ::Ice::RegX8664; using RegisterSet = ::Ice::RegX8664;
static const SizeT StackPtr = RegX8664::Reg_rsp; static constexpr SizeT StackPtr = RegX8664::Reg_rsp;
static const SizeT FramePtr = RegX8664::Reg_rbp; static constexpr SizeT FramePtr = RegX8664::Reg_rbp;
static const GPRRegister Encoded_Reg_Accumulator = RegX8664::Encoded_Reg_eax; static constexpr GPRRegister Encoded_Reg_Accumulator =
static const GPRRegister Encoded_Reg_Counter = RegX8664::Encoded_Reg_ecx; RegX8664::Encoded_Reg_eax;
static constexpr GPRRegister Encoded_Reg_Counter = RegX8664::Encoded_Reg_ecx;
static constexpr FixupKind FK_PcRel = llvm::ELF::R_X86_64_PC32; static constexpr FixupKind FK_PcRel = llvm::ELF::R_X86_64_PC32;
static constexpr FixupKind FK_Abs = llvm::ELF::R_X86_64_32; static constexpr FixupKind FK_Abs = llvm::ELF::R_X86_64_32;
static constexpr FixupKind FK_Gotoff = llvm::ELF::R_X86_64_GOTOFF64; static constexpr FixupKind FK_Gotoff = llvm::ELF::R_X86_64_GOTOFF64;
...@@ -715,21 +716,61 @@ public: ...@@ -715,21 +716,61 @@ public:
static int32_t getRdxOrDie() { return RegisterSet::Reg_rdx; } static int32_t getRdxOrDie() { return RegisterSet::Reg_rdx; }
// x86-64 calling convention:
//
// * The first eight arguments of vector/fp type, regardless of their
// position relative to the other arguments in the argument list, are placed
// in registers %xmm0 - %xmm7.
//
// * The first six arguments of integer types, regardless of their position
// relative to the other arguments in the argument list, are placed in
// registers %rdi, %rsi, %rdx, %rcx, %r8, and %r9.
//
// This intends to match the section "Function Calling Sequence" of the
// document "System V Application Binary Interface."
/// The maximum number of arguments to pass in XMM registers /// The maximum number of arguments to pass in XMM registers
static const uint32_t X86_MAX_XMM_ARGS = 8; static constexpr uint32_t X86_MAX_XMM_ARGS = 8;
/// The maximum number of arguments to pass in GPR registers /// The maximum number of arguments to pass in GPR registers
static const uint32_t X86_MAX_GPR_ARGS = 6; static constexpr uint32_t X86_MAX_GPR_ARGS = 6;
/// Whether scalar floating point arguments are passed in XMM registers
static constexpr bool X86_PASS_SCALAR_FP_IN_XMM = true;
/// Get the register for a given argument slot in the XMM registers.
static int32_t getRegisterForXmmArgNum(uint32_t ArgNum) {
// TODO(sehr): Change to use the CCArg technique used in ARM32.
static_assert(RegisterSet::Reg_xmm0 + 1 == RegisterSet::Reg_xmm1,
"Inconsistency between XMM register numbers and ordinals");
if (ArgNum >= X86_MAX_XMM_ARGS) {
return Variable::NoRegister;
}
return static_cast<int32_t>(RegisterSet::Reg_xmm0 + ArgNum);
}
/// Get the register for a given argument slot in the GPRs.
static int32_t getRegisterForGprArgNum(Type Ty, uint32_t ArgNum) {
if (ArgNum >= X86_MAX_GPR_ARGS) {
return Variable::NoRegister;
}
static const RegisterSet::AllRegisters GprForArgNum[] = {
RegisterSet::Reg_rdi, RegisterSet::Reg_rsi, RegisterSet::Reg_rdx,
RegisterSet::Reg_rcx, RegisterSet::Reg_r8, RegisterSet::Reg_r9,
};
static_assert(llvm::array_lengthof(GprForArgNum) == X86_MAX_GPR_ARGS,
"Mismatch between MAX_GPR_ARGS and GprForArgNum.");
assert(Ty == IceType_i64 || Ty == IceType_i32);
return static_cast<int32_t>(getGprForType(Ty, GprForArgNum[ArgNum]));
}
/// The number of bits in a byte /// The number of bits in a byte
static const uint32_t X86_CHAR_BIT = 8; static constexpr uint32_t X86_CHAR_BIT = 8;
/// Stack alignment. This is defined in IceTargetLoweringX8664.cpp because it /// Stack alignment. This is defined in IceTargetLoweringX8664.cpp because it
/// is used as an argument to std::max(), and the default std::less<T> has an /// is used as an argument to std::max(), and the default std::less<T> has an
/// operator(T const&, T const&) which requires this member to have an /// operator(T const&, T const&) which requires this member to have an
/// address. /// address.
static const uint32_t X86_STACK_ALIGNMENT_BYTES; static const uint32_t X86_STACK_ALIGNMENT_BYTES;
/// Size of the return address on the stack /// Size of the return address on the stack
static const uint32_t X86_RET_IP_SIZE_BYTES = 8; static constexpr uint32_t X86_RET_IP_SIZE_BYTES = 8;
/// The number of different NOP instructions /// The number of different NOP instructions
static const uint32_t X86_NUM_NOP_VARIANTS = 5; static constexpr uint32_t X86_NUM_NOP_VARIANTS = 5;
/// \name Limits for unrolling memory intrinsics. /// \name Limits for unrolling memory intrinsics.
/// @{ /// @{
......
...@@ -197,9 +197,11 @@ protected: ...@@ -197,9 +197,11 @@ protected:
} }
void lowerAlloca(const InstAlloca *Inst) override; void lowerAlloca(const InstAlloca *Inst) override;
void lowerArguments() override;
void lowerArithmetic(const InstArithmetic *Inst) override; void lowerArithmetic(const InstArithmetic *Inst) override;
void lowerAssign(const InstAssign *Inst) override; void lowerAssign(const InstAssign *Inst) override;
void lowerBr(const InstBr *Inst) override; void lowerBr(const InstBr *Inst) override;
void lowerCall(const InstCall *Inst) override;
void lowerCast(const InstCast *Inst) override; void lowerCast(const InstCast *Inst) override;
void lowerExtractElement(const InstExtractElement *Inst) override; void lowerExtractElement(const InstExtractElement *Inst) override;
void lowerFcmp(const InstFcmp *Inst) override; void lowerFcmp(const InstFcmp *Inst) override;
...@@ -209,6 +211,7 @@ protected: ...@@ -209,6 +211,7 @@ protected:
void lowerInsertElement(const InstInsertElement *Inst) override; void lowerInsertElement(const InstInsertElement *Inst) override;
void lowerLoad(const InstLoad *Inst) override; void lowerLoad(const InstLoad *Inst) override;
void lowerPhi(const InstPhi *Inst) override; void lowerPhi(const InstPhi *Inst) override;
void lowerRet(const InstRet *Inst) override;
void lowerSelect(const InstSelect *Inst) override; void lowerSelect(const InstSelect *Inst) override;
void lowerStore(const InstStore *Inst) override; void lowerStore(const InstStore *Inst) override;
void lowerSwitch(const InstSwitch *Inst) override; void lowerSwitch(const InstSwitch *Inst) override;
...@@ -279,6 +282,13 @@ protected: ...@@ -279,6 +282,13 @@ protected:
void scalarizeArithmetic(InstArithmetic::OpKind K, Variable *Dest, void scalarizeArithmetic(InstArithmetic::OpKind K, Variable *Dest,
Operand *Src0, Operand *Src1); Operand *Src0, Operand *Src1);
/// Emit just the call instruction (without argument or return variable
/// processing), sandboxing if needed.
virtual Inst *emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) = 0;
/// Materialize the moves needed to return a value of the specified type.
virtual Variable *moveReturnValueToRegister(Operand *Value,
Type ReturnType) = 0;
/// Emit a fake use of esp to make sure esp stays alive for the entire /// Emit a fake use of esp to make sure esp stays alive for the entire
/// function. Otherwise some esp adjustments get dead-code eliminated. /// function. Otherwise some esp adjustments get dead-code eliminated.
void keepEspLiveAtExit() { void keepEspLiveAtExit() {
......
...@@ -1096,6 +1096,67 @@ void TargetX86Base<TraitsType>::lowerAlloca(const InstAlloca *Inst) { ...@@ -1096,6 +1096,67 @@ void TargetX86Base<TraitsType>::lowerAlloca(const InstAlloca *Inst) {
} }
} }
template <typename TraitsType>
void TargetX86Base<TraitsType>::lowerArguments() {
VarList &Args = Func->getArgs();
unsigned NumXmmArgs = 0;
bool XmmSlotsRemain = true;
unsigned NumGprArgs = 0;
bool GprSlotsRemain = true;
Context.init(Func->getEntryNode());
Context.setInsertPoint(Context.getCur());
for (SizeT i = 0, End = Args.size();
i < End && (XmmSlotsRemain || GprSlotsRemain); ++i) {
Variable *Arg = Args[i];
Type Ty = Arg->getType();
Variable *RegisterArg = nullptr;
int32_t RegNum = Variable::NoRegister;
if (isVectorType(Ty)) {
RegNum = Traits::getRegisterForXmmArgNum(NumXmmArgs);
if (RegNum == Variable::NoRegister) {
XmmSlotsRemain = false;
continue;
}
++NumXmmArgs;
RegisterArg = Func->makeVariable(Ty);
} else if (isScalarFloatingType(Ty)) {
if (!Traits::X86_PASS_SCALAR_FP_IN_XMM) {
continue;
}
RegNum = Traits::getRegisterForXmmArgNum(NumXmmArgs);
if (RegNum == Variable::NoRegister) {
XmmSlotsRemain = false;
continue;
}
++NumXmmArgs;
RegisterArg = Func->makeVariable(Ty);
} else if (isScalarIntegerType(Ty)) {
RegNum = Traits::getRegisterForGprArgNum(Ty, NumGprArgs);
if (RegNum == Variable::NoRegister) {
GprSlotsRemain = false;
continue;
}
++NumGprArgs;
RegisterArg = Func->makeVariable(Ty);
}
assert(RegNum != Variable::NoRegister);
assert(RegisterArg != nullptr);
// Replace Arg in the argument list with the home register. Then generate
// an instruction in the prolog to copy the home register to the assigned
// location of Arg.
if (BuildDefs::dump())
RegisterArg->setName(Func, "home_reg:" + Arg->getName(Func));
RegisterArg->setRegNum(RegNum);
RegisterArg->setIsArg();
Arg->setIsArg(false);
Args[i] = RegisterArg;
Context.insert<InstAssign>(Arg, RegisterArg);
}
}
/// Strength-reduce scalar integer multiplication by a constant (for i32 or /// Strength-reduce scalar integer multiplication by a constant (for i32 or
/// narrower) for certain constants. The lea instruction can be used to multiply /// narrower) for certain constants. The lea instruction can be used to multiply
/// by 3, 5, or 9, and the lsh instruction can be used to multiply by powers of /// by 3, 5, or 9, and the lsh instruction can be used to multiply by powers of
...@@ -2028,6 +2089,204 @@ void TargetX86Base<TraitsType>::lowerBr(const InstBr *Br) { ...@@ -2028,6 +2089,204 @@ void TargetX86Base<TraitsType>::lowerBr(const InstBr *Br) {
_br(Traits::Cond::Br_ne, Br->getTargetTrue(), Br->getTargetFalse()); _br(Traits::Cond::Br_ne, Br->getTargetTrue(), Br->getTargetFalse());
} }
// constexprMax returns a (constexpr) max(S0, S1), and it is used for defining
// OperandList in lowerCall. std::max() is supposed to work, but it doesn't.
inline constexpr SizeT constexprMax(SizeT S0, SizeT S1) {
return S0 < S1 ? S1 : S0;
}
template <typename TraitsType>
void TargetX86Base<TraitsType>::lowerCall(const InstCall *Instr) {
// Common x86 calling convention lowering:
//
// * At the point before the call, the stack must be aligned to 16 bytes.
//
// * Non-register arguments are pushed onto the stack in right-to-left order,
// such that the left-most argument ends up on the top of the stack at the
// lowest memory address.
//
// * Stack arguments of vector type are aligned to start at the next highest
// multiple of 16 bytes. Other stack arguments are aligned to the next word
// size boundary (4 or 8 bytes, respectively).
NeedsStackAlignment = true;
using OperandList =
llvm::SmallVector<Operand *, constexprMax(Traits::X86_MAX_XMM_ARGS,
Traits::X86_MAX_GPR_ARGS)>;
OperandList XmmArgs;
CfgVector<std::pair<const Type, Operand *>> GprArgs;
OperandList StackArgs, StackArgLocations;
uint32_t ParameterAreaSizeBytes = 0;
// Classify each argument operand according to the location where the argument
// is passed.
for (SizeT i = 0, NumArgs = Instr->getNumArgs(); i < NumArgs; ++i) {
Operand *Arg = Instr->getArg(i);
const Type Ty = Arg->getType();
// The PNaCl ABI requires the width of arguments to be at least 32 bits.
assert(typeWidthInBytes(Ty) >= 4);
if (isVectorType(Ty) && (Traits::getRegisterForXmmArgNum(XmmArgs.size()) !=
Variable::NoRegister)) {
XmmArgs.push_back(Arg);
} else if (isScalarFloatingType(Ty) && Traits::X86_PASS_SCALAR_FP_IN_XMM &&
(Traits::getRegisterForXmmArgNum(0) != Variable::NoRegister)) {
XmmArgs.push_back(Arg);
} else if (isScalarIntegerType(Ty) &&
(Traits::getRegisterForGprArgNum(Ty, GprArgs.size()) !=
Variable::NoRegister)) {
GprArgs.emplace_back(Ty, Arg);
} else {
// Place on stack.
StackArgs.push_back(Arg);
if (isVectorType(Arg->getType())) {
ParameterAreaSizeBytes =
Traits::applyStackAlignment(ParameterAreaSizeBytes);
}
Variable *esp = getPhysicalRegister(getStackReg(), Traits::WordType);
Constant *Loc = Ctx->getConstantInt32(ParameterAreaSizeBytes);
StackArgLocations.push_back(
Traits::X86OperandMem::create(Func, Ty, esp, Loc));
ParameterAreaSizeBytes += typeWidthInBytesOnStack(Arg->getType());
}
}
// Ensure there is enough space for the fstp/movs for floating returns.
Variable *Dest = Instr->getDest();
const Type DestTy = Dest ? Dest->getType() : IceType_void;
if (Traits::X86_PASS_SCALAR_FP_IN_XMM) {
if (isScalarFloatingType(DestTy)) {
ParameterAreaSizeBytes =
std::max(static_cast<size_t>(ParameterAreaSizeBytes),
typeWidthInBytesOnStack(DestTy));
}
}
// Adjust the parameter area so that the stack is aligned. It is assumed that
// the stack is already aligned at the start of the calling sequence.
ParameterAreaSizeBytes = Traits::applyStackAlignment(ParameterAreaSizeBytes);
assert(ParameterAreaSizeBytes <= maxOutArgsSizeBytes());
// Copy arguments that are passed on the stack to the appropriate stack
// locations.
for (SizeT i = 0, NumStackArgs = StackArgs.size(); i < NumStackArgs; ++i) {
lowerStore(InstStore::create(Func, StackArgs[i], StackArgLocations[i]));
}
// Copy arguments to be passed in registers to the appropriate registers.
for (SizeT i = 0, NumXmmArgs = XmmArgs.size(); i < NumXmmArgs; ++i) {
Variable *Reg =
legalizeToReg(XmmArgs[i], Traits::getRegisterForXmmArgNum(i));
// Generate a FakeUse of register arguments so that they do not get dead
// code eliminated as a result of the FakeKill of scratch registers after
// the call.
Context.insert<InstFakeUse>(Reg);
}
// Materialize moves for arguments passed in GPRs.
for (SizeT i = 0, NumGprArgs = GprArgs.size(); i < NumGprArgs; ++i) {
const Type SignatureTy = GprArgs[i].first;
Operand *Arg = GprArgs[i].second;
Variable *Reg =
legalizeToReg(Arg, Traits::getRegisterForGprArgNum(Arg->getType(), i));
assert(SignatureTy == IceType_i64 || SignatureTy == IceType_i32);
assert(SignatureTy == Arg->getType());
(void)SignatureTy;
Context.insert<InstFakeUse>(Reg);
}
// Generate the call instruction. Assign its result to a temporary with high
// register allocation weight.
// ReturnReg doubles as ReturnRegLo as necessary.
Variable *ReturnReg = nullptr;
Variable *ReturnRegHi = nullptr;
if (Dest) {
switch (DestTy) {
case IceType_NUM:
case IceType_void:
case IceType_i1:
case IceType_i8:
case IceType_i16:
llvm::report_fatal_error("Invalid Call dest type");
break;
case IceType_i32:
ReturnReg = makeReg(DestTy, Traits::RegisterSet::Reg_eax);
break;
case IceType_i64:
if (Traits::Is64Bit) {
ReturnReg = makeReg(
IceType_i64,
Traits::getGprForType(IceType_i64, Traits::RegisterSet::Reg_eax));
} else {
ReturnReg = makeReg(IceType_i32, Traits::RegisterSet::Reg_eax);
ReturnRegHi = makeReg(IceType_i32, Traits::RegisterSet::Reg_edx);
}
break;
case IceType_f32:
case IceType_f64:
if (!Traits::X86_PASS_SCALAR_FP_IN_XMM) {
// Leave ReturnReg==ReturnRegHi==nullptr, and capture the result with
// the fstp instruction.
break;
}
// Fallthrough intended.
case IceType_v4i1:
case IceType_v8i1:
case IceType_v16i1:
case IceType_v16i8:
case IceType_v8i16:
case IceType_v4i32:
case IceType_v4f32:
ReturnReg = makeReg(DestTy, Traits::RegisterSet::Reg_xmm0);
break;
}
}
// Emit the call to the function.
Operand *CallTarget =
legalize(Instr->getCallTarget(), Legal_Reg | Legal_Imm | Legal_AddrAbs);
Inst *NewCall = emitCallToTarget(CallTarget, ReturnReg);
// Keep the upper return register live on 32-bit platform.
if (ReturnRegHi)
Context.insert<InstFakeDef>(ReturnRegHi);
// Mark the call as killing all the caller-save registers.
Context.insert<InstFakeKill>(NewCall);
// Handle x86-32 floating point returns.
if (Dest != nullptr && isScalarFloatingType(Dest->getType()) &&
!Traits::X86_PASS_SCALAR_FP_IN_XMM) {
// Special treatment for an FP function which returns its result in st(0).
// If Dest ends up being a physical xmm register, the fstp emit code will
// route st(0) through the space reserved in the function argument area
// we allocated.
_fstp(Dest);
// Create a fake use of Dest in case it actually isn't used, because st(0)
// still needs to be popped.
Context.insert<InstFakeUse>(Dest);
}
// Generate a FakeUse to keep the call live if necessary.
if (Instr->hasSideEffects() && ReturnReg) {
Context.insert<InstFakeUse>(ReturnReg);
}
// Process the return value, if any.
if (Dest == nullptr)
return;
// Assign the result of the call to Dest.
if (isVectorType(DestTy)) {
assert(ReturnReg && "Vector type requires a return register");
_movp(Dest, ReturnReg);
} else if (isScalarFloatingType(DestTy)) {
if (Traits::X86_PASS_SCALAR_FP_IN_XMM) {
assert(ReturnReg && "FP type requires a return register");
_mov(Dest, ReturnReg);
}
} else {
assert(isScalarIntegerType(DestTy));
assert(ReturnReg && "Integer type requires a return register");
if (DestTy == IceType_i64 && !Traits::Is64Bit) {
assert(ReturnRegHi && "64-bit type requires two return registers");
auto *Dest64On32 = llvm::cast<Variable64On32>(Dest);
Variable *DestLo = Dest64On32->getLo();
Variable *DestHi = Dest64On32->getHi();
_mov(DestLo, ReturnReg);
_mov(DestHi, ReturnRegHi);
} else {
_mov(Dest, ReturnReg);
}
}
}
template <typename TraitsType> template <typename TraitsType>
void TargetX86Base<TraitsType>::lowerCast(const InstCast *Inst) { void TargetX86Base<TraitsType>::lowerCast(const InstCast *Inst) {
// a = cast(b) ==> t=cast(b); a=t; (link t->b, link a->t, no overlap) // a = cast(b) ==> t=cast(b); a=t; (link t->b, link a->t, no overlap)
...@@ -4821,6 +5080,25 @@ void TargetX86Base<TraitsType>::lowerPhi(const InstPhi * /*Inst*/) { ...@@ -4821,6 +5080,25 @@ void TargetX86Base<TraitsType>::lowerPhi(const InstPhi * /*Inst*/) {
} }
template <typename TraitsType> template <typename TraitsType>
void TargetX86Base<TraitsType>::lowerRet(const InstRet *Inst) {
Variable *Reg = nullptr;
if (Inst->hasRetValue()) {
Operand *RetValue = legalize(Inst->getRetValue());
const Type ReturnType = RetValue->getType();
assert(isVectorType(ReturnType) || isScalarFloatingType(ReturnType) ||
(ReturnType == IceType_i32) || (ReturnType == IceType_i64));
Reg = moveReturnValueToRegister(RetValue, ReturnType);
}
// Add a ret instruction even if sandboxing is enabled, because addEpilog
// explicitly looks for a ret instruction as a marker for where to insert the
// frame removal instructions.
_ret(Reg);
// Add a fake use of esp to make sure esp stays alive for the entire
// function. Otherwise post-call esp adjustments get dead-code eliminated.
keepEspLiveAtExit();
}
template <typename TraitsType>
void TargetX86Base<TraitsType>::lowerSelect(const InstSelect *Select) { void TargetX86Base<TraitsType>::lowerSelect(const InstSelect *Select) {
Variable *Dest = Select->getDest(); Variable *Dest = Select->getDest();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment