mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-01 07:30:43 +03:00
Re-implement __va_list struct allocation for System V AMD64 varargs ABI.
This commit is contained in:
parent
5e3785159f
commit
e5deefb1dd
5 changed files with 108 additions and 12 deletions
|
@ -320,3 +320,21 @@ void X86_64TargetABI::rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty) {
|
|||
if (tf->linkage == LINKd && tf->varargs != 1 && fty.args.size() > 1)
|
||||
fty.reverseParams = true;
|
||||
}
|
||||
|
||||
bool isSystemVAMD64Target() {
|
||||
return global.params.targetTriple.getArch() == llvm::Triple::x86_64
|
||||
&& !global.params.targetTriple.isOSWindows();
|
||||
}
|
||||
|
||||
LLType* getSystemVAMD64NativeValistType() {
|
||||
LLType* uintType = LLType::getInt32Ty(gIR->context());
|
||||
LLType* voidPointerType = getVoidPtrType();
|
||||
|
||||
std::vector<LLType*> parts; // struct __va_list {
|
||||
parts.push_back(uintType); // uint gp_offset;
|
||||
parts.push_back(uintType); // uint fp_offset;
|
||||
parts.push_back(voidPointerType); // void* overflow_arg_area;
|
||||
parts.push_back(voidPointerType); // void* reg_save_area; }
|
||||
|
||||
return LLStructType::get(gIR->context(), parts, "__va_list");
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The ABI implementation used for 64 bit x86 (i.e. x86_64/AMD64/x64) targets.
|
||||
// The System V AMD64 ABI implementation used on all x86-64 platforms except
|
||||
// for Windows.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
@ -15,7 +16,12 @@
|
|||
#define LDC_GEN_ABI_X86_64_H
|
||||
|
||||
struct TargetABI;
|
||||
namespace llvm { class Type; }
|
||||
|
||||
TargetABI* getX86_64TargetABI();
|
||||
|
||||
bool isSystemVAMD64Target();
|
||||
|
||||
llvm::Type* getSystemVAMD64NativeValistType();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#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"
|
||||
|
@ -1182,14 +1183,31 @@ void DtoDefineFunction(FuncDeclaration* fd)
|
|||
// D varargs: prepare _argptr and _arguments
|
||||
if (f->linkage == LINKd && f->varargs == 1)
|
||||
{
|
||||
// allocate _argptr and initialize it by calling the LLVM va_start intrinsic
|
||||
LLValue* argptrmem = DtoAlloca(Type::tvoidptr, "_argptr_mem");
|
||||
llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), DtoBitCast(argptrmem, getVoidPtrType()), "", gIR->scopebb());
|
||||
// allocate _argptr (of type core.stdc.stdarg.va_list)
|
||||
LLValue* argptrmem = DtoAlloca(Type::tvalist, "_argptr_mem"); // _argptr_mem = new va_list [most likely char**]
|
||||
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());
|
||||
}
|
||||
|
||||
// copy _arguments to a memory location
|
||||
LLValue* argumentsmem = DtoRawAlloca(irFunc->_arguments->getType(), 0, "_arguments_mem");
|
||||
new llvm::StoreInst(irFunc->_arguments, argumentsmem, gIR->scopebb());
|
||||
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());
|
||||
irFunc->_arguments = argumentsmem;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,7 @@ DValue* DtoVaArg(Loc& loc, Type* type, Expression* valistArg)
|
|||
llt = getPtrToType(llt);
|
||||
// issue a warning for broken va_arg instruction.
|
||||
if (global.params.targetTriple.getArch() != llvm::Triple::x86
|
||||
&& global.params.targetTriple.getArch() != llvm::Triple::x86_64
|
||||
&& global.params.targetTriple.getArch() != llvm::Triple::ppc64
|
||||
#if LDC_LLVM_VER >= 305
|
||||
&& global.params.targetTriple.getArch() != llvm::Triple::ppc64le
|
||||
|
|
65
gen/toir.cpp
65
gen/toir.cpp
|
@ -19,6 +19,7 @@
|
|||
#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"
|
||||
|
@ -827,6 +828,16 @@ 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)
|
||||
{
|
||||
|
@ -843,15 +854,57 @@ public:
|
|||
fatal();
|
||||
}
|
||||
Expression* exp = (*e->arguments)[0];
|
||||
LLValue* arg = toElem(exp)->getLVal();
|
||||
if (LLValue *argptr = p->func()->_argptr) {
|
||||
DtoStore(DtoLoad(argptr), DtoBitCast(arg, getPtrToType(getVoidPtrType())));
|
||||
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 {
|
||||
arg = DtoBitCast(arg, getVoidPtrType());
|
||||
result = new DImValue(e->type, gIR->ir->CreateCall(GET_INTRINSIC_DECL(vastart), 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>)
|
||||
}
|
||||
}
|
||||
// va_copy instruction
|
||||
else if (fndecl->llvmInternal == LLVMva_copy) {
|
||||
if (e->arguments->dim != 2) {
|
||||
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);
|
||||
}
|
||||
// va_arg instruction
|
||||
else if (fndecl->llvmInternal == LLVMva_arg) {
|
||||
if (e->arguments->dim != 1) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue