Refactor and fix intrinsics for variadic functions

I.e., va_start(), va_copy() and va_arg().
This commit is contained in:
Martin 2016-06-27 22:40:50 +02:00
parent dfbb6c5b08
commit 82005009e7
9 changed files with 92 additions and 99 deletions

View file

@ -116,34 +116,32 @@ struct AArch64TargetABI : TargetABI {
return LLStructType::get(gIR->context(), parts);
}
LLValue *prepareVaStart(LLValue *pAp) override {
LLValue *prepareVaStart(DLValue *ap) override {
// 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.
// and set `ap` to its address.
LLValue *valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem");
valistmem = DtoBitCast(valistmem, getVoidPtrType());
DtoStore(valistmem, DtoBitCast(pAp, getPtrToType(getVoidPtrType())));
// pass a void* pointer to the actual struct to LLVM's va_start intrinsic
return valistmem;
DtoStore(valistmem,
DtoBitCast(DtoLVal(ap), getPtrToType(valistmem->getType())));
// Pass a i8* pointer to the actual struct to LLVM's va_start intrinsic.
return DtoBitCast(valistmem, getVoidPtrType());
}
void vaCopy(LLValue *pDest, LLValue *src) override {
// Analog to va_start, we need to allocate a new __va_list struct on the
// stack, fill it with a bitcopy of the source struct...
src = DtoLoad(
DtoBitCast(src, getValistType()->getPointerTo())); // *(__va_list*)src
LLValue *valistmem = DtoAllocaDump(src, 0, "__va_list_mem");
// ... and finally set the passed 'dest' char* pointer to the new struct's
// address.
DtoStore(DtoBitCast(valistmem, getVoidPtrType()),
DtoBitCast(pDest, getPtrToType(getVoidPtrType())));
void vaCopy(DLValue *dest, DValue *src) override {
// Analog to va_start, we first need to allocate a new __va_list struct on
// the stack and set `dest` to its address.
LLValue *valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem");
DtoStore(valistmem,
DtoBitCast(DtoLVal(dest), getPtrToType(valistmem->getType())));
// Then fill the new struct with a bitcopy of the source struct.
// `src` is a char* pointer to the source struct.
DtoMemCpy(valistmem, DtoRVal(src));
}
LLValue *prepareVaArg(LLValue *pAp) override {
// pass a void* pointer to the actual __va_list struct to LLVM's va_arg
// intrinsic
return DtoLoad(pAp);
LLValue *prepareVaArg(DLValue *ap) override {
// Pass a i8* pointer to the actual __va_list struct to LLVM's va_arg
// intrinsic.
return DtoRVal(ap);
}
Type *vaListType() override {

View file

@ -119,12 +119,6 @@ struct ArmTargetABI : TargetABI {
}
}
void vaCopy(LLValue *pDest, LLValue *src) override {
// simply bitcopy src over dest. src is __va_list*, so need load
auto srcval = DtoLoad(src);
DtoStore(srcval, pDest);
}
Type *vaListType() override {
// We need to pass the actual va_list type for correct mangling. Simply
// using TypeIdentifier here is a bit wonky but works, as long as the name

View file

@ -214,11 +214,11 @@ struct X86_64TargetABI : TargetABI {
void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override;
void rewriteArgument(IrFuncTyArg &arg, RegCount &regCount);
LLValue *prepareVaStart(LLValue *pAp) override;
LLValue *prepareVaStart(DLValue *ap) override;
void vaCopy(LLValue *pDest, LLValue *src) override;
void vaCopy(DLValue *dest, DValue *src) override;
LLValue *prepareVaArg(LLValue *pAp) override;
LLValue *prepareVaArg(DLValue *ap) override;
Type *vaListType() override;
@ -371,34 +371,32 @@ LLType *X86_64TargetABI::getValistType() {
return LLStructType::get(gIR->context(), parts);
}
LLValue *X86_64TargetABI::prepareVaStart(LLValue *pAp) {
LLValue *X86_64TargetABI::prepareVaStart(DLValue *ap) {
// 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.
// and set `ap` to its address.
LLValue *valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem");
valistmem = DtoBitCast(valistmem, getVoidPtrType());
DtoStore(valistmem, DtoBitCast(pAp, getPtrToType(getVoidPtrType())));
// pass a void* pointer to the actual struct to LLVM's va_start intrinsic
return valistmem;
DtoStore(valistmem,
DtoBitCast(DtoLVal(ap), getPtrToType(valistmem->getType())));
// Pass a i8* pointer to the actual struct to LLVM's va_start intrinsic.
return DtoBitCast(valistmem, getVoidPtrType());
}
void X86_64TargetABI::vaCopy(LLValue *pDest, LLValue *src) {
// Analog to va_start, we need to allocate a new __va_list struct on the
// stack, fill it with a bitcopy of the source struct...
src = DtoLoad(
DtoBitCast(src, getValistType()->getPointerTo())); // *(__va_list*)src
LLValue *valistmem = DtoAllocaDump(src, 0, "__va_list_mem");
// ... and finally set the passed 'dest' char* pointer to the new struct's
// address.
DtoStore(DtoBitCast(valistmem, getVoidPtrType()),
DtoBitCast(pDest, getPtrToType(getVoidPtrType())));
void X86_64TargetABI::vaCopy(DLValue *dest, DValue *src) {
// Analog to va_start, we first need to allocate a new __va_list struct on the
// stack and set `dest` to its address.
LLValue *valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem");
DtoStore(valistmem,
DtoBitCast(DtoLVal(dest), getPtrToType(valistmem->getType())));
// Then fill the new struct with a bitcopy of the source struct.
// `src` is a char* pointer to the source struct.
DtoMemCpy(valistmem, DtoRVal(src));
}
LLValue *X86_64TargetABI::prepareVaArg(LLValue *pAp) {
// pass a void* pointer to the actual __va_list struct to LLVM's va_arg
// intrinsic
return DtoLoad(pAp);
LLValue *X86_64TargetABI::prepareVaArg(DLValue *ap) {
// Pass a i8* pointer to the actual __va_list struct to LLVM's va_arg
// intrinsic.
return DtoRVal(ap);
}
Type *X86_64TargetABI::vaListType() {

View file

@ -241,23 +241,27 @@ void TargetABI::rewriteVarargs(IrFuncTy &fty,
//////////////////////////////////////////////////////////////////////////////
LLValue *TargetABI::prepareVaStart(LLValue *pAp) {
// pass a void* pointer to ap to LLVM's va_start intrinsic
return DtoBitCast(pAp, getVoidPtrType());
LLValue *TargetABI::prepareVaStart(DLValue *ap) {
// pass a i8* pointer to ap to LLVM's va_start intrinsic
return DtoBitCast(DtoLVal(ap), getVoidPtrType());
}
//////////////////////////////////////////////////////////////////////////////
void TargetABI::vaCopy(LLValue *pDest, LLValue *src) {
// simply bitcopy src over dest
DtoStore(src, pDest);
void TargetABI::vaCopy(DLValue *dest, DValue *src) {
LLValue *llDest = DtoLVal(dest);
if (src->isLVal()) {
DtoMemCpy(llDest, DtoLVal(src));
} else {
DtoStore(DtoRVal(src), llDest);
}
}
//////////////////////////////////////////////////////////////////////////////
LLValue *TargetABI::prepareVaArg(LLValue *pAp) {
// pass a void* pointer to ap to LLVM's va_arg intrinsic
return DtoBitCast(pAp, getVoidPtrType());
LLValue *TargetABI::prepareVaArg(DLValue *ap) {
// pass a i8* pointer to ap to LLVM's va_arg intrinsic
return DtoBitCast(DtoLVal(ap), getVoidPtrType());
}
//////////////////////////////////////////////////////////////////////////////

View file

@ -18,6 +18,7 @@
#define LDC_GEN_ABI_H
#include "mars.h"
#include "gen/dvalue.h"
#include "llvm/IR/CallingConv.h"
#include <vector>
@ -26,7 +27,6 @@ class TypeFunction;
class TypeStruct;
struct IrFuncTy;
struct IrFuncTyArg;
class DValue;
class FuncDeclaration;
namespace llvm {
@ -136,22 +136,18 @@ struct TargetABI {
virtual void rewriteVarargs(IrFuncTy &fty, std::vector<IrFuncTyArg *> &args);
virtual void rewriteArgument(IrFuncTy &fty, 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);
/// Prepares a va_start intrinsic call by transforming the D argument (of type
/// va_list) to a low-level value (of type i8*) to be passed to LLVM's
/// va_start intrinsic.
virtual llvm::Value *prepareVaStart(DLValue *ap);
/// 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);
virtual void vaCopy(DLValue *dest, DValue *src);
/// Prepares a va_arg intrinsic call.
///
/// Input: pointer to passed ap argument (va_list*)
/// Output: value to be passed to LLVM's va_arg intrinsic (void*)
virtual llvm::Value *prepareVaArg(llvm::Value *pAp);
/// Prepares a va_arg intrinsic call by transforming the D argument (of type
/// va_list) to a low-level value (of type i8*) to be passed to LLVM's
/// va_arg intrinsic.
virtual llvm::Value *prepareVaArg(DLValue *ap);
/// Returns the D type to be used for va_list.
///

View file

@ -971,13 +971,14 @@ void DtoDefineFunction(FuncDeclaration *fd) {
// D varargs: prepare _argptr and _arguments
if (f->linkage == LINKd && f->varargs == 1) {
// allocate _argptr (of type core.stdc.stdarg.va_list)
LLValue *argptrmem = DtoAlloca(
Type::tvalist->semantic(fd->loc, fd->_scope), "_argptr_mem");
irFunc->_argptr = argptrmem;
Type *const argptrType = Type::tvalist->semantic(fd->loc, fd->_scope);
LLValue *argptrMem = DtoAlloca(argptrType, "_argptr_mem");
irFunc->_argptr = argptrMem;
// initialize _argptr with a call to the va_start intrinsic
LLValue *vaStartArg = gABI->prepareVaStart(argptrmem);
llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), vaStartArg, "",
DLValue argptrVal(argptrType, argptrMem);
LLValue *llAp = gABI->prepareVaStart(&argptrVal);
llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), llAp, "",
gIR->scopebb());
// copy _arguments to a memory location

View file

@ -264,17 +264,16 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e,
e->error("va_start instruction expects 1 (or 2) arguments");
fatal();
}
LLValue *pAp = DtoLVal((*e->arguments)[0]); // va_list*
DLValue *ap = toElem((*e->arguments)[0])->isLVal(); // va_list
assert(ap);
// variadic extern(D) function with implicit _argptr?
if (LLValue *pArgptr = p->func()->_argptr) {
DtoMemCpy(pAp, pArgptr); // ap = _argptr
result = new DImValue(e->type, pAp);
if (LLValue *argptrMem = p->func()->_argptr) {
DtoMemCpy(DtoLVal(ap), argptrMem); // ap = _argptr
} else {
LLValue *vaStartArg = gABI->prepareVaStart(pAp);
result =
new DImValue(e->type, p->ir->CreateCall(GET_INTRINSIC_DECL(vastart),
vaStartArg, ""));
LLValue *llAp = gABI->prepareVaStart(ap);
p->ir->CreateCall(GET_INTRINSIC_DECL(vastart), llAp, "");
}
result = nullptr;
return true;
}
@ -284,10 +283,11 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e,
e->error("va_copy instruction expects 2 arguments");
fatal();
}
LLValue *pDest = DtoLVal((*e->arguments)[0]); // va_list*
LLValue *src = DtoRVal((*e->arguments)[1]); // va_list
gABI->vaCopy(pDest, src);
result = new DLValue(e->type, pDest);
DLValue *dest = toElem((*e->arguments)[0])->isLVal(); // va_list
assert(dest);
DValue *src = toElem((*e->arguments)[1]); // va_list
gABI->vaCopy(dest, src);
result = nullptr;
return true;
}
@ -297,13 +297,15 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e,
e->error("va_arg instruction expects 1 argument");
fatal();
}
LLValue *pAp = DtoLVal((*e->arguments)[0]); // va_list*
LLValue *vaArgArg = gABI->prepareVaArg(pAp);
LLType *llType = DtoType(e->type);
if (DtoIsInMemoryOnly(e->type)) {
llType = getPtrToType(llType);
e->error("va_arg instruction does not support structs and static arrays");
fatal();
}
result = new DImValue(e->type, p->ir->CreateVAArg(vaArgArg, llType));
DLValue *ap = toElem((*e->arguments)[0])->isLVal(); // va_list
assert(ap);
LLValue *llAp = gABI->prepareVaArg(ap);
LLType *llType = DtoType(e->type);
result = new DImValue(e->type, p->ir->CreateVAArg(llAp, llType));
return true;
}

View file

@ -1040,7 +1040,7 @@ public:
// handle cast to void (usually created by frontend to avoid "has no effect"
// error)
if (e->to == Type::tvoid) {
result = new DImValue(Type::tvoid,
result = new DConstValue(Type::tvoid,
llvm::UndefValue::get(DtoMemType(Type::tvoid)));
return;
}

@ -1 +1 @@
Subproject commit 6b194394fe7f41071a680b0bc27b19ba04ba545e
Subproject commit de7ca661326cf927075651ab107a55de9c63a2ab