Refactoring: move ABI-specific variadic stuff to TargetABI type.

This commit is contained in:
Martin 2014-11-17 23:38:21 +01:00
parent a41a31d535
commit aa38fe06ec
6 changed files with 85 additions and 86 deletions

View file

@ -440,7 +440,13 @@ struct X86_64TargetABI : TargetABI {
void rewriteArgument(IrFuncTyArg& arg);
LLValue* prepareVaStart(LLValue* addressOfAp);
void vaCopy(LLValue* addressOfDest, LLValue* src);
private:
LLType* getValistType();
// Rewrite structs and static arrays <= 64 bit and of a size that is a power of 2
// to an integer of the same size.
bool canRewriteAsInt(Type* t) {
@ -559,12 +565,16 @@ void X86_64TargetABI::rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty) {
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* voidPointerType = getVoidPtrType();
@ -574,5 +584,27 @@ LLType* getSystemVAMD64NativeValistType() {
parts.push_back(voidPointerType); // void* overflow_arg_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
}

View file

@ -20,8 +20,4 @@ namespace llvm { class Type; }
TargetABI* getX86_64TargetABI();
bool isSystemVAMD64Target();
llvm::Type* getSystemVAMD64NativeValistType();
#endif

View file

@ -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);
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.
struct UnknownTargetABI : TargetABI
{

View file

@ -91,6 +91,15 @@ struct TargetABI
virtual void rewriteFunctionType(TypeFunction* t, IrFuncTy &fty) = 0;
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

View file

@ -17,7 +17,6 @@
#include "statement.h"
#include "template.h"
#include "gen/abi.h"
#include "gen/abi-x86-64.h"
#include "gen/arrays.h"
#include "gen/classes.h"
#include "gen/dvalue.h"
@ -1179,30 +1178,17 @@ void DtoDefineFunction(FuncDeclaration* fd)
if (f->linkage == LINKd && f->varargs == 1)
{
// 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;
// initialize _argptr
if (isSystemVAMD64Target()) { // System V AMD64 ABI:
LLType* nativeValistType = getSystemVAMD64NativeValistType();
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());
}
// initialize _argptr with a call to the va_start intrinsic
LLValue* vaStartArg = gABI->prepareVaStart(argptrmem);
llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), vaStartArg, "", gIR->scopebb());
// copy _arguments to a memory location
LLType* argumentsType = irFunc->_arguments->getType();
LLValue* argumentsmem = DtoRawAlloca(argumentsType,
0, "_arguments_mem"); // _arguments_mem = new TypeInfo[]
new llvm::StoreInst(irFunc->_arguments, argumentsmem, // *_arguments_mem = <passed _arguments>
gIR->scopebb());
LLValue* argumentsmem = DtoRawAlloca(argumentsType, 0, "_arguments_mem");
new llvm::StoreInst(irFunc->_arguments, argumentsmem, gIR->scopebb());
irFunc->_arguments = argumentsmem;
}

View file

@ -19,7 +19,6 @@
#include "template.h"
#include "gen/aa.h"
#include "gen/abi.h"
#include "gen/abi-x86-64.h"
#include "gen/arrays.h"
#include "gen/classes.h"
#include "gen/complex.h"
@ -828,16 +827,6 @@ public:
{
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.
if (global.params.warnings && checkPrintf)
{
@ -849,31 +838,20 @@ public:
// va_start instruction
if (fndecl->llvmInternal == LLVMva_start) {
if (e->arguments->dim != 2) {
e->error("va_start instruction expects 2 arguments");
if (e->arguments->dim < 1 || e->arguments->dim > 2) {
e->error("va_start instruction expects 1 (or 2) arguments");
fatal();
}
Expression* exp = (*e->arguments)[0];
LLValue* arg = toElem(exp)->getLVal(); // arg = &<passed ap> [va_list*]
if (LLValue* argptr = p->func()->_argptr) { // variadic extern(D) function with hidden _argptr:
DtoStore(DtoLoad(argptr), arg); // *arg = *_argptr_mem
// => <passed ap> = _argptr
result = new DImValue(e->type, arg);
} else if (isSystemVTarget) { // System V AMD64 ABI:
// Since the user only created a char* pointer on the stack before invoking va_start, we first
// need to allocate the actual __va_list struct and set the passed pointer to its address
// before invoking va_start.
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>)
LLValue* pAp = toElem(exp)->getLVal(); // va_list*
// variadic extern(D) function with implicit _argptr?
if (LLValue* pArgptr = p->func()->_argptr) {
DtoStore(DtoLoad(pArgptr), pAp); // ap = _argptr
result = new DImValue(e->type, pAp);
} else {
LLValue* vaStartArg = gABI->prepareVaStart(pAp);
result = new DImValue(e->type, gIR->ir->CreateCall(
GET_INTRINSIC_DECL(vastart), vaStartArg, ""));
}
}
// va_copy instruction
@ -882,28 +860,10 @@ public:
e->error("va_copy instruction expects 2 arguments");
fatal();
}
LLValue* arg1 = toElem((*e->arguments)[0])->getLVal(); // arg1 = &<passed dest> [va_list*]
LLValue* arg2 = toElem((*e->arguments)[1])->getRVal(); // arg2 = <passed src> [va_list]
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);
LLValue* pDest = toElem((*e->arguments)[0])->getLVal(); // va_list*
LLValue* src = toElem((*e->arguments)[1])->getRVal(); // va_list
gABI->vaCopy(pDest, src);
result = new DVarValue(e->type, pDest);
}
// va_arg instruction
else if (fndecl->llvmInternal == LLVMva_arg) {