mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-01 07:30:43 +03:00
Refactoring: move ABI-specific variadic stuff to TargetABI type.
This commit is contained in:
parent
a41a31d535
commit
aa38fe06ec
6 changed files with 85 additions and 86 deletions
|
@ -440,7 +440,13 @@ struct X86_64TargetABI : TargetABI {
|
||||||
|
|
||||||
void rewriteArgument(IrFuncTyArg& arg);
|
void rewriteArgument(IrFuncTyArg& arg);
|
||||||
|
|
||||||
|
LLValue* prepareVaStart(LLValue* addressOfAp);
|
||||||
|
|
||||||
|
void vaCopy(LLValue* addressOfDest, LLValue* src);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
LLType* getValistType();
|
||||||
|
|
||||||
// Rewrite structs and static arrays <= 64 bit and of a size that is a power of 2
|
// Rewrite structs and static arrays <= 64 bit and of a size that is a power of 2
|
||||||
// to an integer of the same size.
|
// to an integer of the same size.
|
||||||
bool canRewriteAsInt(Type* t) {
|
bool canRewriteAsInt(Type* t) {
|
||||||
|
@ -559,12 +565,16 @@ void X86_64TargetABI::rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty) {
|
||||||
fty.reverseParams = true;
|
fty.reverseParams = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSystemVAMD64Target() {
|
|
||||||
return global.params.targetTriple.getArch() == llvm::Triple::x86_64
|
|
||||||
&& !global.params.targetTriple.isOSWindows();
|
|
||||||
}
|
|
||||||
|
|
||||||
LLType* getSystemVAMD64NativeValistType() {
|
/**
|
||||||
|
* The System V AMD64 ABI uses a special native va_list type - a 24-bytes struct passed by
|
||||||
|
* reference.
|
||||||
|
* In druntime, the struct is defined as core.stdc.stdarg.__va_list; the actually used
|
||||||
|
* core.stdc.stdarg.va_list type is a raw char* pointer though to achieve byref semantics.
|
||||||
|
* This requires a little bit of compiler magic in the following implementations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
LLType* X86_64TargetABI::getValistType() {
|
||||||
LLType* uintType = LLType::getInt32Ty(gIR->context());
|
LLType* uintType = LLType::getInt32Ty(gIR->context());
|
||||||
LLType* voidPointerType = getVoidPtrType();
|
LLType* voidPointerType = getVoidPtrType();
|
||||||
|
|
||||||
|
@ -574,5 +584,27 @@ LLType* getSystemVAMD64NativeValistType() {
|
||||||
parts.push_back(voidPointerType); // void* overflow_arg_area;
|
parts.push_back(voidPointerType); // void* overflow_arg_area;
|
||||||
parts.push_back(voidPointerType); // void* reg_save_area; }
|
parts.push_back(voidPointerType); // void* reg_save_area; }
|
||||||
|
|
||||||
return LLStructType::get(gIR->context(), parts, "__va_list");
|
return LLStructType::get(gIR->context(), parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLValue* X86_64TargetABI::prepareVaStart(LLValue* pAp) {
|
||||||
|
// Since the user only created a char* pointer (ap) on the stack before invoking va_start,
|
||||||
|
// we first need to allocate the actual __va_list struct and set 'ap' to its address.
|
||||||
|
LLValue* valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem");
|
||||||
|
valistmem = DtoBitCast(valistmem, getVoidPtrType());
|
||||||
|
DtoStore(valistmem, pAp); // ap = (void*)__va_list_mem
|
||||||
|
|
||||||
|
// pass a void* pointer to the actual struct to LLVM's va_start intrinsic
|
||||||
|
return valistmem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void X86_64TargetABI::vaCopy(LLValue* pDest, LLValue* src) {
|
||||||
|
// Analog to va_start, we need to allocate a __va_list struct on the stack first
|
||||||
|
// and set the passed 'dest' char* pointer to its address.
|
||||||
|
LLValue* valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem");
|
||||||
|
DtoStore(DtoBitCast(valistmem, getVoidPtrType()), pDest);
|
||||||
|
|
||||||
|
// Now bitcopy the source struct over the destination struct.
|
||||||
|
src = DtoBitCast(src, valistmem->getType());
|
||||||
|
DtoStore(DtoLoad(src), valistmem); // *(__va_list*)dest = *(__va_list*)src
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,4 @@ namespace llvm { class Type; }
|
||||||
|
|
||||||
TargetABI* getX86_64TargetABI();
|
TargetABI* getX86_64TargetABI();
|
||||||
|
|
||||||
bool isSystemVAMD64Target();
|
|
||||||
|
|
||||||
llvm::Type* getSystemVAMD64NativeValistType();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
18
gen/abi.cpp
18
gen/abi.cpp
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void ABIRewrite::getL(Type* dty, DValue* v, llvm::Value* lval)
|
void ABIRewrite::getL(Type* dty, DValue* v, LLValue* lval)
|
||||||
{
|
{
|
||||||
LLValue* rval = get(dty, v);
|
LLValue* rval = get(dty, v);
|
||||||
assert(rval->getType() == lval->getType()->getContainedType(0));
|
assert(rval->getType() == lval->getType()->getContainedType(0));
|
||||||
|
@ -35,6 +35,22 @@ void ABIRewrite::getL(Type* dty, DValue* v, llvm::Value* lval)
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
LLValue* TargetABI::prepareVaStart(LLValue* pAp)
|
||||||
|
{
|
||||||
|
// pass a void* pointer to ap to LLVM's va_start intrinsic
|
||||||
|
return DtoBitCast(pAp, getVoidPtrType());
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void TargetABI::vaCopy(LLValue* pDest, LLValue* src)
|
||||||
|
{
|
||||||
|
// simply bitcopy src over dest
|
||||||
|
DtoStore(src, pDest);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Some reasonable defaults for when we don't know what ABI to use.
|
// Some reasonable defaults for when we don't know what ABI to use.
|
||||||
struct UnknownTargetABI : TargetABI
|
struct UnknownTargetABI : TargetABI
|
||||||
{
|
{
|
||||||
|
|
|
@ -91,6 +91,15 @@ struct TargetABI
|
||||||
virtual void rewriteFunctionType(TypeFunction* t, IrFuncTy &fty) = 0;
|
virtual void rewriteFunctionType(TypeFunction* t, IrFuncTy &fty) = 0;
|
||||||
|
|
||||||
virtual void rewriteArgument(IrFuncTyArg& arg) {}
|
virtual void rewriteArgument(IrFuncTyArg& arg) {}
|
||||||
|
|
||||||
|
// Prepares a va_start intrinsic call.
|
||||||
|
// Input: pointer to passed ap argument (va_list*)
|
||||||
|
// Output: value to be passed to LLVM's va_start intrinsic (void*)
|
||||||
|
virtual llvm::Value* prepareVaStart(llvm::Value* pAp);
|
||||||
|
|
||||||
|
// Implements the va_copy intrinsic.
|
||||||
|
// Input: pointer to dest argument (va_list*) and src argument (va_list)
|
||||||
|
virtual void vaCopy(llvm::Value* pDest, llvm::Value* src);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "statement.h"
|
#include "statement.h"
|
||||||
#include "template.h"
|
#include "template.h"
|
||||||
#include "gen/abi.h"
|
#include "gen/abi.h"
|
||||||
#include "gen/abi-x86-64.h"
|
|
||||||
#include "gen/arrays.h"
|
#include "gen/arrays.h"
|
||||||
#include "gen/classes.h"
|
#include "gen/classes.h"
|
||||||
#include "gen/dvalue.h"
|
#include "gen/dvalue.h"
|
||||||
|
@ -1179,30 +1178,17 @@ void DtoDefineFunction(FuncDeclaration* fd)
|
||||||
if (f->linkage == LINKd && f->varargs == 1)
|
if (f->linkage == LINKd && f->varargs == 1)
|
||||||
{
|
{
|
||||||
// allocate _argptr (of type core.stdc.stdarg.va_list)
|
// allocate _argptr (of type core.stdc.stdarg.va_list)
|
||||||
LLValue* argptrmem = DtoAlloca(Type::tvalist, "_argptr_mem"); // _argptr_mem = new va_list [most likely char**]
|
LLValue* argptrmem = DtoAlloca(Type::tvalist, "_argptr_mem");
|
||||||
irFunc->_argptr = argptrmem;
|
irFunc->_argptr = argptrmem;
|
||||||
|
|
||||||
// initialize _argptr
|
// initialize _argptr with a call to the va_start intrinsic
|
||||||
if (isSystemVAMD64Target()) { // System V AMD64 ABI:
|
LLValue* vaStartArg = gABI->prepareVaStart(argptrmem);
|
||||||
LLType* nativeValistType = getSystemVAMD64NativeValistType();
|
llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), vaStartArg, "", gIR->scopebb());
|
||||||
LLValue* valistmem = DtoRawAlloca(nativeValistType,
|
|
||||||
0, "__va_list_mem"); // __va_list_mem = new __va_list
|
|
||||||
valistmem = DtoBitCast(valistmem, getVoidPtrType()); // valistmem = (char*)__va_list_mem
|
|
||||||
DtoStore(valistmem, argptrmem); // *argptrmem = valistmem
|
|
||||||
// => _argptr = (char*)__va_list_mem
|
|
||||||
llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), // llvm.va_start(valistmem)
|
|
||||||
valistmem, "", gIR->scopebb()); // => llvm.va_start(_argptr)
|
|
||||||
} else { // all other ABIs:
|
|
||||||
llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), // llvm.va_start((char*)&_argptr)
|
|
||||||
DtoBitCast(argptrmem, getVoidPtrType()), "", gIR->scopebb());
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy _arguments to a memory location
|
// copy _arguments to a memory location
|
||||||
LLType* argumentsType = irFunc->_arguments->getType();
|
LLType* argumentsType = irFunc->_arguments->getType();
|
||||||
LLValue* argumentsmem = DtoRawAlloca(argumentsType,
|
LLValue* argumentsmem = DtoRawAlloca(argumentsType, 0, "_arguments_mem");
|
||||||
0, "_arguments_mem"); // _arguments_mem = new TypeInfo[]
|
new llvm::StoreInst(irFunc->_arguments, argumentsmem, gIR->scopebb());
|
||||||
new llvm::StoreInst(irFunc->_arguments, argumentsmem, // *_arguments_mem = <passed _arguments>
|
|
||||||
gIR->scopebb());
|
|
||||||
irFunc->_arguments = argumentsmem;
|
irFunc->_arguments = argumentsmem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
70
gen/toir.cpp
70
gen/toir.cpp
|
@ -19,7 +19,6 @@
|
||||||
#include "template.h"
|
#include "template.h"
|
||||||
#include "gen/aa.h"
|
#include "gen/aa.h"
|
||||||
#include "gen/abi.h"
|
#include "gen/abi.h"
|
||||||
#include "gen/abi-x86-64.h"
|
|
||||||
#include "gen/arrays.h"
|
#include "gen/arrays.h"
|
||||||
#include "gen/classes.h"
|
#include "gen/classes.h"
|
||||||
#include "gen/complex.h"
|
#include "gen/complex.h"
|
||||||
|
@ -828,16 +827,6 @@ public:
|
||||||
{
|
{
|
||||||
FuncDeclaration* fndecl = dfnval->func;
|
FuncDeclaration* fndecl = dfnval->func;
|
||||||
|
|
||||||
// The System V AMD64 ABI used for all x86-64 targets except for Windows
|
|
||||||
// uses a special native va_list type - a 24-bytes struct passed by reference.
|
|
||||||
// In druntime, the struct is defined as core.stdc.stdarg.__va_list;
|
|
||||||
// the actually used core.stdc.stdarg.va_list type is a raw char* pointer
|
|
||||||
// though to achieve byref semantics.
|
|
||||||
// This requires a little bit of compiler magic here in the following
|
|
||||||
// implementations of LDC's va_start and va_copy intrinsics.
|
|
||||||
const bool isSystemVTarget = isSystemVAMD64Target();
|
|
||||||
LLType* systemVValistType = getSystemVAMD64NativeValistType();
|
|
||||||
|
|
||||||
// as requested by bearophile, see if it's a C printf call and that it's valid.
|
// as requested by bearophile, see if it's a C printf call and that it's valid.
|
||||||
if (global.params.warnings && checkPrintf)
|
if (global.params.warnings && checkPrintf)
|
||||||
{
|
{
|
||||||
|
@ -849,31 +838,20 @@ public:
|
||||||
|
|
||||||
// va_start instruction
|
// va_start instruction
|
||||||
if (fndecl->llvmInternal == LLVMva_start) {
|
if (fndecl->llvmInternal == LLVMva_start) {
|
||||||
if (e->arguments->dim != 2) {
|
if (e->arguments->dim < 1 || e->arguments->dim > 2) {
|
||||||
e->error("va_start instruction expects 2 arguments");
|
e->error("va_start instruction expects 1 (or 2) arguments");
|
||||||
fatal();
|
fatal();
|
||||||
}
|
}
|
||||||
Expression* exp = (*e->arguments)[0];
|
Expression* exp = (*e->arguments)[0];
|
||||||
LLValue* arg = toElem(exp)->getLVal(); // arg = &<passed ap> [va_list*]
|
LLValue* pAp = toElem(exp)->getLVal(); // va_list*
|
||||||
if (LLValue* argptr = p->func()->_argptr) { // variadic extern(D) function with hidden _argptr:
|
// variadic extern(D) function with implicit _argptr?
|
||||||
DtoStore(DtoLoad(argptr), arg); // *arg = *_argptr_mem
|
if (LLValue* pArgptr = p->func()->_argptr) {
|
||||||
// => <passed ap> = _argptr
|
DtoStore(DtoLoad(pArgptr), pAp); // ap = _argptr
|
||||||
result = new DImValue(e->type, arg);
|
result = new DImValue(e->type, pAp);
|
||||||
} else if (isSystemVTarget) { // System V AMD64 ABI:
|
} else {
|
||||||
// Since the user only created a char* pointer on the stack before invoking va_start, we first
|
LLValue* vaStartArg = gABI->prepareVaStart(pAp);
|
||||||
// need to allocate the actual __va_list struct and set the passed pointer to its address
|
result = new DImValue(e->type, gIR->ir->CreateCall(
|
||||||
// before invoking va_start.
|
GET_INTRINSIC_DECL(vastart), vaStartArg, ""));
|
||||||
LLValue* valistmem = DtoRawAlloca(systemVValistType,
|
|
||||||
0, "__va_list_mem"); // __va_list_mem = new __va_list
|
|
||||||
valistmem = DtoBitCast(valistmem, getVoidPtrType()); // valistmem = (char*)__va_list_mem
|
|
||||||
DtoStore(valistmem, arg); // *arg = valistmem
|
|
||||||
// => <passed ap> = (char*)__va_list_mem
|
|
||||||
result = new DImValue(e->type, gIR->ir->CreateCall( // llvm.va_start(valistmem)
|
|
||||||
GET_INTRINSIC_DECL(vastart), valistmem, "")); // => llvm.va_start(<passed ap>)
|
|
||||||
} else { // all other ABIs:
|
|
||||||
arg = DtoBitCast(arg, getVoidPtrType()); // arg = (char*)arg
|
|
||||||
result = new DImValue(e->type, gIR->ir->CreateCall( // llvm.va_start(arg)
|
|
||||||
GET_INTRINSIC_DECL(vastart), arg, "")); // => llvm.va_start((char*)&<passed ap>)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// va_copy instruction
|
// va_copy instruction
|
||||||
|
@ -882,28 +860,10 @@ public:
|
||||||
e->error("va_copy instruction expects 2 arguments");
|
e->error("va_copy instruction expects 2 arguments");
|
||||||
fatal();
|
fatal();
|
||||||
}
|
}
|
||||||
|
LLValue* pDest = toElem((*e->arguments)[0])->getLVal(); // va_list*
|
||||||
LLValue* arg1 = toElem((*e->arguments)[0])->getLVal(); // arg1 = &<passed dest> [va_list*]
|
LLValue* src = toElem((*e->arguments)[1])->getRVal(); // va_list
|
||||||
LLValue* arg2 = toElem((*e->arguments)[1])->getRVal(); // arg2 = <passed src> [va_list]
|
gABI->vaCopy(pDest, src);
|
||||||
|
result = new DVarValue(e->type, pDest);
|
||||||
if (isSystemVTarget) {
|
|
||||||
// Similar to va_start, we need to allocate a new __va_list struct on the stack first
|
|
||||||
// and set the passed destination char* pointer to its address.
|
|
||||||
LLValue* valistmem = DtoRawAlloca(systemVValistType,
|
|
||||||
0, "__va_list_mem"); // __va_list_mem = new __va_list
|
|
||||||
DtoStore(DtoBitCast(valistmem, getVoidPtrType()), // *arg1 = (char*)__va_list_mem
|
|
||||||
arg1); // => <passed dest> = (char*)__va_list_mem
|
|
||||||
|
|
||||||
// Now simply bitcopy the source struct over the destination struct.
|
|
||||||
arg2 = DtoBitCast(arg2, valistmem->getType()); // arg2 = (__va_list*)arg2
|
|
||||||
DtoStore(DtoLoad(arg2), valistmem); // *__va_list_mem = *arg2
|
|
||||||
// => *(__va_list*)<passed dest> = *(__va_list*)<passed src>
|
|
||||||
} else { // all other ABIs:
|
|
||||||
DtoStore(arg2, arg1); // *arg1 = arg2
|
|
||||||
// => <passed dest> = <passed src>
|
|
||||||
}
|
|
||||||
|
|
||||||
result = new DVarValue(e->type, arg1);
|
|
||||||
}
|
}
|
||||||
// va_arg instruction
|
// va_arg instruction
|
||||||
else if (fndecl->llvmInternal == LLVMva_arg) {
|
else if (fndecl->llvmInternal == LLVMva_arg) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue