const Operand *Src,
const typename Traits::Assembler::GPREmitterShiftOp &Emitter);
- static X86TargetLowering *getTarget(const Cfg* Func) {
+ static X86TargetLowering *getTarget(const Cfg *Func) {
return static_cast<X86TargetLowering *>(Func->getTarget());
}
InstX86Jmp(Cfg *Func, Operand *Target);
};
-/// AdjustStack instruction - subtracts esp by the given amount and updates the
-/// stack offset during code emission.
+/// AdjustStack instruction - grows the stack (moves esp down) by the given
+/// amount. If the amount is negative, it shrinks the stack (moves esp up).
+/// It also updates the target lowering StackAdjustment during code emission.
template <class Machine>
class InstX86AdjustStack final : public InstX86Base<Machine> {
InstX86AdjustStack() = delete;
InstX86AdjustStack &operator=(const InstX86AdjustStack &) = delete;
public:
- static InstX86AdjustStack *create(Cfg *Func, SizeT Amount, Variable *Esp) {
+ static InstX86AdjustStack *create(Cfg *Func, int32_t Amount, Variable *Esp) {
return new (Func->allocate<InstX86AdjustStack>())
InstX86AdjustStack(Func, Amount, Esp);
}
}
private:
- InstX86AdjustStack(Cfg *Func, SizeT Amount, Variable *Esp);
- SizeT Amount;
+ InstX86AdjustStack(Cfg *Func, int32_t Amount, Variable *Esp);
+ const int32_t Amount;
};
/// Call instruction. Arguments should have already been pushed.
}
template <class Machine>
-InstX86AdjustStack<Machine>::InstX86AdjustStack(Cfg *Func, SizeT Amount,
+InstX86AdjustStack<Machine>::InstX86AdjustStack(Cfg *Func, int32_t Amount,
Variable *Esp)
: InstX86Base<Machine>(Func, InstX86Base<Machine>::Adjuststack, 1, Esp),
Amount(Amount) {
Str << "*";
CallTarget->emit(Func);
}
- Target->resetStackAdjustment();
}
template <class Machine>
} else {
llvm_unreachable("Unexpected operand type");
}
- Target->resetStackAdjustment();
}
template <class Machine>
this->getDest()->getRegNum()),
InstX86Base<Machine>::Traits::getEncodedGPR(SrcVar->getRegNum()));
} else {
- Asm->cmov(
- SrcTy, Condition, InstX86Base<Machine>::Traits::getEncodedGPR(
- this->getDest()->getRegNum()),
- Target->stackVarToAsmOperand(SrcVar));
+ Asm->cmov(SrcTy, Condition, InstX86Base<Machine>::Traits::getEncodedGPR(
+ this->getDest()->getRegNum()),
+ Target->stackVarToAsmOperand(SrcVar));
}
} else if (const auto *Mem = llvm::dyn_cast<
typename InstX86Base<Machine>::Traits::X86OperandMem>(Src)) {
return;
}
Type Ty = this->getDest()->getType();
- size_t Width = typeWidthInBytes(Ty);
if (!this->getDest()->hasReg()) {
Str << "\tfstp" << this->getFldString(Ty) << "\t";
this->getDest()->emit(Func);
return;
}
// Dest is a physical (xmm) register, so st(0) needs to go through memory.
- // Hack this by creating a temporary stack slot, spilling st(0) there,
- // loading it into the xmm register, and deallocating the stack slot.
- Str << "\tsubl\t$" << Width << ", %esp\n";
+ // Hack this by using caller-reserved memory at the top of stack, spilling
+ // st(0) there, and loading it into the xmm register.
Str << "\tfstp" << this->getFldString(Ty) << "\t"
<< "(%esp)\n";
Str << "\tmov" << InstX86Base<Machine>::Traits::TypeAttributes[Ty].SdSsString
<< "(%esp), ";
this->getDest()->emit(Func);
Str << "\n";
- Str << "\taddl\t$" << Width << ", %esp";
}
template <class Machine>
Asm->fstp(Ty, StackAddr);
} else {
// Dest is a physical (xmm) register, so st(0) needs to go through memory.
- // Hack this by creating a temporary stack slot, spilling st(0) there,
- // loading it into the xmm register, and deallocating the stack slot.
- Immediate Width(typeWidthInBytes(Ty));
- Asm->sub(IceType_i32,
- InstX86Base<Machine>::Traits::RegisterSet::Encoded_Reg_esp, Width);
+ // Hack this by using caller-reserved memory at the top of stack, spilling
+ // st(0) there, and loading it into the xmm register.
typename InstX86Base<Machine>::Traits::Address StackSlot =
typename InstX86Base<Machine>::Traits::Address(
InstX86Base<Machine>::Traits::RegisterSet::Encoded_Reg_esp, 0,
Asm->movss(Ty,
InstX86Base<Machine>::Traits::getEncodedXmm(Dest->getRegNum()),
StackSlot);
- Asm->add(IceType_i32,
- InstX86Base<Machine>::Traits::RegisterSet::Encoded_Reg_esp, Width);
}
}
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrEmit();
- Str << "\tsubl\t$" << Amount << ", %esp";
+ if (Amount > 0)
+ Str << "\tsubl\t$" << Amount << ", %esp";
+ else
+ Str << "\taddl\t$" << -Amount << ", %esp";
auto *Target = InstX86Base<Machine>::getTarget(Func);
Target->updateStackAdjustment(Amount);
}
void InstX86AdjustStack<Machine>::emitIAS(const Cfg *Func) const {
typename InstX86Base<Machine>::Traits::Assembler *Asm =
Func->getAssembler<typename InstX86Base<Machine>::Traits::Assembler>();
- Asm->sub(IceType_i32,
- InstX86Base<Machine>::Traits::RegisterSet::Encoded_Reg_esp,
- Immediate(Amount));
+ if (Amount > 0)
+ Asm->sub(IceType_i32,
+ InstX86Base<Machine>::Traits::RegisterSet::Encoded_Reg_esp,
+ Immediate(Amount));
+ else
+ Asm->add(IceType_i32,
+ InstX86Base<Machine>::Traits::RegisterSet::Encoded_Reg_esp,
+ Immediate(-Amount));
auto *Target = InstX86Base<Machine>::getTarget(Func);
Target->updateStackAdjustment(Amount);
}
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrDump();
- Str << "esp = sub.i32 esp, " << Amount;
+ if (Amount > 0)
+ Str << "esp = sub.i32 esp, " << Amount;
+ else
+ Str << "esp = add.i32 esp, " << -Amount;
}
template <class Machine>
OperandList XmmArgs;
OperandList StackArgs, StackArgLocations;
- uint32_t ParameterAreaSizeBytes = 0;
+ int32_t ParameterAreaSizeBytes = 0;
// Classify each argument operand according to the location where the
// argument is passed.
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.
}
// 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;
Variable *ReturnRegHi = nullptr;
if (ReturnRegHi)
Context.insert(InstFakeDef::create(Func, ReturnRegHi));
- // Add the appropriate offset to esp. The call instruction takes care of
- // resetting the stack offset during emission.
- if (ParameterAreaSizeBytes) {
- Variable *esp =
- Func->getTarget()->getPhysicalRegister(Traits::RegisterSet::Reg_esp);
- _add(esp, Ctx->getConstantInt32(ParameterAreaSizeBytes));
- }
-
// Insert a register-kill pseudo instruction.
Context.insert(InstFakeKill::create(Func, 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::create(Func, Dest));
+ }
+
+ // Add the appropriate offset to esp.
+ if (ParameterAreaSizeBytes)
+ _adjust_stack(-ParameterAreaSizeBytes);
+
// Generate a FakeUse to keep the call live if necessary.
if (Instr->hasSideEffects() && ReturnReg) {
Inst *FakeUse = InstFakeUse::create(Func, ReturnReg);
_mov(Dest, ReturnReg);
}
}
- } else if (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 a temporary stack slot.
- _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::create(Func, Dest));
}
}
_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.
- // TODO: Are there more places where the fake use should be inserted? E.g.
- // "void f(int n){while(1) g(n);}" may not have a ret instruction.
- Variable *esp =
- Func->getTarget()->getPhysicalRegister(Traits::RegisterSet::Reg_esp);
- Context.insert(InstFakeUse::create(Func, esp));
+ keepEspLiveAtExit();
}
void TargetX8632::addProlog(CfgNode *Node) {
_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.
- // TODO: Are there more places where the fake use should be inserted? E.g.
- // "void f(int n){while(1) g(n);}" may not have a ret instruction.
- Variable *esp =
- Func->getTarget()->getPhysicalRegister(Traits::RegisterSet::Reg_esp);
- Context.insert(InstFakeUse::create(Func, esp));
+ keepEspLiveAtExit();
}
void TargetX8664::addProlog(CfgNode *Node) {
void scalarizeArithmetic(InstArithmetic::OpKind K, Variable *Dest,
Operand *Src0, Operand *Src1);
+ /// Emit a fake use of esp to make sure esp stays alive for the entire
+ /// function. Otherwise some esp adjustments get dead-code eliminated.
+ void keepEspLiveAtExit() {
+ Variable *esp = Func->getTarget()->getPhysicalRegister(getStackReg());
+ Context.insert(InstFakeUse::create(Func, esp));
+ }
+
/// Operand legalization helpers. To deal with address mode constraints, the
/// helpers will create a new Operand and emit instructions that guarantee
/// that the Operand kind is one of those indicated by the LegalMask (a
void TargetX86Base<Machine>::lowerUnreachable(
const InstUnreachable * /*Inst*/) {
_ud2();
+ // Add a fake use of esp to make sure esp adjustments after the unreachable
+ // do not get dead-code eliminated.
+ keepEspLiveAtExit();
}
template <class Machine>