Commit ad3e42a4 by Antonio Maiorano

Subzero: add support for variadic calls (System V)

For the System V ABI, variadic calls must store the number of floating point arguments into RAX. This was mostly working by accident for our calls to printf since RAX is used as a scratch register, and was often non-zero, which is all that's really needed for printf with float args to work; however, when RAX would become 0, printf would print the wrong thing. Bug: b/149913889 Change-Id: Id4b519c5416927d537fca078109c0dc850f08359 Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/41668Tested-by: 's avatarAntonio Maiorano <amaiorano@google.com> Kokoro-Presubmit: kokoro <noreply+kokoro@google.com> Reviewed-by: 's avatarNicolas Capens <nicolascapens@google.com>
parent 4b34ee3d
...@@ -145,7 +145,7 @@ Ice::Constant *getConstantPointer(Ice::GlobalContext *context, void const *ptr) ...@@ -145,7 +145,7 @@ Ice::Constant *getConstantPointer(Ice::GlobalContext *context, void const *ptr)
} }
// Wrapper for calls on C functions with Ice types // Wrapper for calls on C functions with Ice types
Ice::Variable *Call(Ice::Cfg *function, Ice::CfgNode *basicBlock, Ice::Type retTy, void const *fptr, const std::vector<Ice::Operand *> &iceArgs) Ice::Variable *Call(Ice::Cfg *function, Ice::CfgNode *basicBlock, Ice::Type retTy, void const *fptr, const std::vector<Ice::Operand *> &iceArgs, bool isVariadic)
{ {
// Subzero doesn't support boolean return values. Replace with an i32. // Subzero doesn't support boolean return values. Replace with an i32.
if(retTy == Ice::IceType_i1) if(retTy == Ice::IceType_i1)
...@@ -159,7 +159,7 @@ Ice::Variable *Call(Ice::Cfg *function, Ice::CfgNode *basicBlock, Ice::Type retT ...@@ -159,7 +159,7 @@ Ice::Variable *Call(Ice::Cfg *function, Ice::CfgNode *basicBlock, Ice::Type retT
ret = function->makeVariable(retTy); ret = function->makeVariable(retTy);
} }
auto call = Ice::InstCall::create(function, iceArgs.size(), ret, getConstantPointer(function->getContext(), fptr), false); auto call = Ice::InstCall::create(function, iceArgs.size(), ret, getConstantPointer(function->getContext(), fptr), false, false, isVariadic);
for(auto arg : iceArgs) for(auto arg : iceArgs)
{ {
call->addArg(arg); call->addArg(arg);
...@@ -175,7 +175,7 @@ Ice::Variable *Call(Ice::Cfg *function, Ice::CfgNode *basicBlock, Return(fptr)(C ...@@ -175,7 +175,7 @@ Ice::Variable *Call(Ice::Cfg *function, Ice::CfgNode *basicBlock, Return(fptr)(C
{ {
Ice::Type retTy = T(rr::CToReactorT<Return>::getType()); Ice::Type retTy = T(rr::CToReactorT<Return>::getType());
std::vector<Ice::Operand *> iceArgs{ std::forward<RArgs>(args)... }; std::vector<Ice::Operand *> iceArgs{ std::forward<RArgs>(args)... };
return Call(function, basicBlock, retTy, reinterpret_cast<void const *>(fptr), iceArgs); return Call(function, basicBlock, retTy, reinterpret_cast<void const *>(fptr), iceArgs, false);
} }
// Returns a non-const variable copy of const v // Returns a non-const variable copy of const v
...@@ -818,7 +818,7 @@ private: ...@@ -818,7 +818,7 @@ private:
#ifdef ENABLE_RR_PRINT #ifdef ENABLE_RR_PRINT
void VPrintf(const std::vector<Value *> &vals) void VPrintf(const std::vector<Value *> &vals)
{ {
sz::Call(::function, ::basicBlock, Ice::IceType_i32, reinterpret_cast<const void *>(::printf), V(vals)); sz::Call(::function, ::basicBlock, Ice::IceType_i32, reinterpret_cast<const void *>(::printf), V(vals), true);
} }
#endif // ENABLE_RR_PRINT #endif // ENABLE_RR_PRINT
......
...@@ -426,7 +426,8 @@ class InstCall : public InstHighLevel { ...@@ -426,7 +426,8 @@ class InstCall : public InstHighLevel {
public: public:
static InstCall *create(Cfg *Func, SizeT NumArgs, Variable *Dest, static InstCall *create(Cfg *Func, SizeT NumArgs, Variable *Dest,
Operand *CallTarget, bool HasTailCall, Operand *CallTarget, bool HasTailCall,
bool IsTargetHelperCall = false) { bool IsTargetHelperCall = false,
bool IsVariadic = false) {
/// Set HasSideEffects to true so that the call instruction can't be /// Set HasSideEffects to true so that the call instruction can't be
/// dead-code eliminated. IntrinsicCalls can override this if the particular /// dead-code eliminated. IntrinsicCalls can override this if the particular
/// intrinsic is deletable and has no side-effects. /// intrinsic is deletable and has no side-effects.
...@@ -434,7 +435,7 @@ public: ...@@ -434,7 +435,7 @@ public:
constexpr InstKind Kind = Inst::Call; constexpr InstKind Kind = Inst::Call;
return new (Func->allocate<InstCall>()) return new (Func->allocate<InstCall>())
InstCall(Func, NumArgs, Dest, CallTarget, HasTailCall, InstCall(Func, NumArgs, Dest, CallTarget, HasTailCall,
IsTargetHelperCall, HasSideEffects, Kind); IsTargetHelperCall, IsVariadic, HasSideEffects, Kind);
} }
void addArg(Operand *Arg) { addSource(Arg); } void addArg(Operand *Arg) { addSource(Arg); }
Operand *getCallTarget() const { return getSrc(0); } Operand *getCallTarget() const { return getSrc(0); }
...@@ -442,6 +443,7 @@ public: ...@@ -442,6 +443,7 @@ public:
SizeT getNumArgs() const { return getSrcSize() - 1; } SizeT getNumArgs() const { return getSrcSize() - 1; }
bool isTailcall() const { return HasTailCall; } bool isTailcall() const { return HasTailCall; }
bool isTargetHelperCall() const { return IsTargetHelperCall; } bool isTargetHelperCall() const { return IsTargetHelperCall; }
bool isVariadic() const { return IsVariadic; }
bool isMemoryWrite() const override { return true; } bool isMemoryWrite() const override { return true; }
void dump(const Cfg *Func) const override; void dump(const Cfg *Func) const override;
static bool classof(const Inst *Instr) { return Instr->getKind() == Call; } static bool classof(const Inst *Instr) { return Instr->getKind() == Call; }
...@@ -449,10 +451,10 @@ public: ...@@ -449,10 +451,10 @@ public:
protected: protected:
InstCall(Cfg *Func, SizeT NumArgs, Variable *Dest, Operand *CallTarget, InstCall(Cfg *Func, SizeT NumArgs, Variable *Dest, Operand *CallTarget,
bool HasTailCall, bool IsTargetHelperCall, bool HasSideEff, bool HasTailCall, bool IsTargetHelperCall, bool IsVariadic,
InstKind Kind) bool HasSideEff, InstKind Kind)
: InstHighLevel(Func, Kind, NumArgs + 1, Dest), HasTailCall(HasTailCall), : InstHighLevel(Func, Kind, NumArgs + 1, Dest), HasTailCall(HasTailCall),
IsTargetHelperCall(IsTargetHelperCall) { IsTargetHelperCall(IsTargetHelperCall), IsVariadic(IsVariadic) {
HasSideEffects = HasSideEff; HasSideEffects = HasSideEff;
addSource(CallTarget); addSource(CallTarget);
} }
...@@ -460,6 +462,7 @@ protected: ...@@ -460,6 +462,7 @@ protected:
private: private:
const bool HasTailCall; const bool HasTailCall;
const bool IsTargetHelperCall; const bool IsTargetHelperCall;
const bool IsVariadic;
}; };
/// Cast instruction (a.k.a. conversion operation). /// Cast instruction (a.k.a. conversion operation).
...@@ -633,7 +636,7 @@ public: ...@@ -633,7 +636,7 @@ public:
private: private:
InstIntrinsicCall(Cfg *Func, SizeT NumArgs, Variable *Dest, InstIntrinsicCall(Cfg *Func, SizeT NumArgs, Variable *Dest,
Operand *CallTarget, const Intrinsics::IntrinsicInfo &Info) Operand *CallTarget, const Intrinsics::IntrinsicInfo &Info)
: InstCall(Func, NumArgs, Dest, CallTarget, false, false, : InstCall(Func, NumArgs, Dest, CallTarget, false, false, false,
Info.HasSideEffects, Inst::IntrinsicCall), Info.HasSideEffects, Inst::IntrinsicCall),
Info(Info) {} Info(Info) {}
......
...@@ -351,7 +351,13 @@ bool TargetX8632::legalizeOptAddrForSandbox(OptAddr *Addr) { ...@@ -351,7 +351,13 @@ bool TargetX8632::legalizeOptAddrForSandbox(OptAddr *Addr) {
return false; return false;
} }
Inst *TargetX8632::emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) { Inst *TargetX8632::emitCallToTarget(Operand *CallTarget, Variable *ReturnReg,
size_t NumVariadicFpArgs) {
(void)NumVariadicFpArgs;
// Note that NumVariadicFpArgs is only used for System V x86-64 variadic
// calls, because floating point arguments are passed via vector registers,
// whereas for x86-32, all args are passed via the stack.
std::unique_ptr<AutoBundle> Bundle; std::unique_ptr<AutoBundle> Bundle;
if (NeedSandboxing) { if (NeedSandboxing) {
if (llvm::isa<Constant>(CallTarget)) { if (llvm::isa<Constant>(CallTarget)) {
......
...@@ -62,7 +62,8 @@ protected: ...@@ -62,7 +62,8 @@ protected:
void emitStackProbe(size_t StackSizeBytes); void emitStackProbe(size_t StackSizeBytes);
void lowerIndirectJump(Variable *JumpTarget); void lowerIndirectJump(Variable *JumpTarget);
void emitGetIP(CfgNode *Node); void emitGetIP(CfgNode *Node);
Inst *emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) override; Inst *emitCallToTarget(Operand *CallTarget, Variable *ReturnReg,
size_t NumVariadicFpArgs = 0) override;
Variable *moveReturnValueToRegister(Operand *Value, Type ReturnType) override; Variable *moveReturnValueToRegister(Operand *Value, Type ReturnType) override;
private: private:
......
...@@ -640,7 +640,8 @@ void TargetX8664::lowerIndirectJump(Variable *JumpTarget) { ...@@ -640,7 +640,8 @@ void TargetX8664::lowerIndirectJump(Variable *JumpTarget) {
_jmp(JumpTarget); _jmp(JumpTarget);
} }
Inst *TargetX8664::emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) { Inst *TargetX8664::emitCallToTarget(Operand *CallTarget, Variable *ReturnReg,
size_t NumVariadicFpArgs) {
Inst *NewCall = nullptr; Inst *NewCall = nullptr;
auto *CallTargetR = llvm::dyn_cast<Variable>(CallTarget); auto *CallTargetR = llvm::dyn_cast<Variable>(CallTarget);
if (NeedSandboxing) { if (NeedSandboxing) {
...@@ -700,7 +701,6 @@ Inst *TargetX8664::emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) { ...@@ -700,7 +701,6 @@ Inst *TargetX8664::emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) {
_add(T64, r15); _add(T64, r15);
CallTarget = T64; CallTarget = T64;
} }
NewCall = Context.insert<Traits::Insts::Jmp>(CallTarget); NewCall = Context.insert<Traits::Insts::Jmp>(CallTarget);
} }
if (ReturnReg != nullptr) { if (ReturnReg != nullptr) {
...@@ -715,14 +715,42 @@ Inst *TargetX8664::emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) { ...@@ -715,14 +715,42 @@ Inst *TargetX8664::emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) {
Variable *T = makeReg(IceType_i64); Variable *T = makeReg(IceType_i64);
_movzx(T, CallTargetR); _movzx(T, CallTargetR);
CallTarget = T; CallTarget = T;
} else if (llvm::isa<Constant>(CallTarget) &&
CallTarget->getType() == IceType_i64) { } else if (CallTarget->getType() == IceType_i64) {
// x86-64 does not support 64-bit direct calls, so write the value // x86-64 does not support 64-bit direct calls, so write the value to a
// to a register and make an indirect call. // register and make an indirect call for Constant call targets.
Variable *T = makeReg(IceType_i64); RegNumT TargetReg = {};
_mov(T, CallTarget);
CallTarget = T; // System V: force r11 when calling a variadic function so that rax isn't
// used, since rax stores the number of FP args (see NumVariadicFpArgs
// usage below).
#if !defined(SUBZERO_USE_MICROSOFT_ABI)
if (NumVariadicFpArgs > 0)
TargetReg = Traits::RegisterSet::Reg_r11;
#endif
if (llvm::isa<Constant>(CallTarget)) {
Variable *T = makeReg(IceType_i64, TargetReg);
_mov(T, CallTarget);
CallTarget = T;
} else if (llvm::isa<Variable>(CallTarget)) {
Operand *T = legalizeToReg(CallTarget, TargetReg);
CallTarget = T;
}
}
// System V: store number of FP args in RAX for variadic calls
#if !defined(SUBZERO_USE_MICROSOFT_ABI)
if (NumVariadicFpArgs > 0) {
// Store number of FP args (stored in XMM registers) in RAX for variadic
// calls
auto *NumFpArgs = Ctx->getConstantInt64(NumVariadicFpArgs);
Variable *NumFpArgsReg =
legalizeToReg(NumFpArgs, Traits::RegisterSet::Reg_rax);
Context.insert<InstFakeUse>(NumFpArgsReg);
} }
#endif
NewCall = Context.insert<Traits::Insts::Call>(ReturnReg, CallTarget); NewCall = Context.insert<Traits::Insts::Call>(ReturnReg, CallTarget);
} }
return NewCall; return NewCall;
......
...@@ -65,7 +65,8 @@ protected: ...@@ -65,7 +65,8 @@ protected:
void emitStackProbe(size_t StackSizeBytes); void emitStackProbe(size_t StackSizeBytes);
void lowerIndirectJump(Variable *JumpTarget); void lowerIndirectJump(Variable *JumpTarget);
void emitGetIP(CfgNode *Node); void emitGetIP(CfgNode *Node);
Inst *emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) override; Inst *emitCallToTarget(Operand *CallTarget, Variable *ReturnReg,
size_t NumVariadicFpArgs = 0) override;
Variable *moveReturnValueToRegister(Operand *Value, Type ReturnType) override; Variable *moveReturnValueToRegister(Operand *Value, Type ReturnType) override;
private: private:
......
...@@ -384,7 +384,8 @@ protected: ...@@ -384,7 +384,8 @@ protected:
/// Emit just the call instruction (without argument or return variable /// Emit just the call instruction (without argument or return variable
/// processing), sandboxing if needed. /// processing), sandboxing if needed.
virtual Inst *emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) = 0; virtual Inst *emitCallToTarget(Operand *CallTarget, Variable *ReturnReg,
size_t NumVariadicFpArgs = 0) = 0;
/// Materialize the moves needed to return a value of the specified type. /// Materialize the moves needed to return a value of the specified type.
virtual Variable *moveReturnValueToRegister(Operand *Value, virtual Variable *moveReturnValueToRegister(Operand *Value,
Type ReturnType) = 0; Type ReturnType) = 0;
......
...@@ -2816,7 +2816,8 @@ void TargetX86Base<TraitsType>::lowerCall(const InstCall *Instr) { ...@@ -2816,7 +2816,8 @@ void TargetX86Base<TraitsType>::lowerCall(const InstCall *Instr) {
// Emit the call to the function. // Emit the call to the function.
Operand *CallTarget = Operand *CallTarget =
legalize(Instr->getCallTarget(), Legal_Reg | Legal_Imm | Legal_AddrAbs); legalize(Instr->getCallTarget(), Legal_Reg | Legal_Imm | Legal_AddrAbs);
Inst *NewCall = emitCallToTarget(CallTarget, ReturnReg); size_t NumVariadicFpArgs = Instr->isVariadic() ? XmmArgs.size() : 0;
Inst *NewCall = emitCallToTarget(CallTarget, ReturnReg, NumVariadicFpArgs);
// Keep the upper return register live on 32-bit platform. // Keep the upper return register live on 32-bit platform.
if (ReturnRegHi) if (ReturnRegHi)
Context.insert<InstFakeDef>(ReturnRegHi); Context.insert<InstFakeDef>(ReturnRegHi);
......
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