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

View file

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

View file

@ -18,6 +18,7 @@
#define LDC_GEN_ABI_H #define LDC_GEN_ABI_H
#include "mars.h" #include "mars.h"
#include "gen/dvalue.h"
#include "llvm/IR/CallingConv.h" #include "llvm/IR/CallingConv.h"
#include <vector> #include <vector>
@ -26,7 +27,6 @@ class TypeFunction;
class TypeStruct; class TypeStruct;
struct IrFuncTy; struct IrFuncTy;
struct IrFuncTyArg; struct IrFuncTyArg;
class DValue;
class FuncDeclaration; class FuncDeclaration;
namespace llvm { namespace llvm {
@ -136,22 +136,18 @@ struct TargetABI {
virtual void rewriteVarargs(IrFuncTy &fty, std::vector<IrFuncTyArg *> &args); virtual void rewriteVarargs(IrFuncTy &fty, std::vector<IrFuncTyArg *> &args);
virtual void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) {} virtual void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) {}
/// Prepares a va_start intrinsic call. /// 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
/// Input: pointer to passed ap argument (va_list*) /// va_start intrinsic.
/// Output: value to be passed to LLVM's va_start intrinsic (void*) virtual llvm::Value *prepareVaStart(DLValue *ap);
virtual llvm::Value *prepareVaStart(llvm::Value *pAp);
/// Implements the va_copy intrinsic. /// Implements the va_copy intrinsic.
/// virtual void vaCopy(DLValue *dest, DValue *src);
/// Input: pointer to dest argument (va_list*) and src argument (va_list)
virtual void vaCopy(llvm::Value *pDest, llvm::Value *src);
/// Prepares a va_arg intrinsic call. /// 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
/// Input: pointer to passed ap argument (va_list*) /// va_arg intrinsic.
/// Output: value to be passed to LLVM's va_arg intrinsic (void*) virtual llvm::Value *prepareVaArg(DLValue *ap);
virtual llvm::Value *prepareVaArg(llvm::Value *pAp);
/// Returns the D type to be used for va_list. /// 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 // D varargs: prepare _argptr and _arguments
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 *const argptrType = Type::tvalist->semantic(fd->loc, fd->_scope);
Type::tvalist->semantic(fd->loc, fd->_scope), "_argptr_mem"); LLValue *argptrMem = DtoAlloca(argptrType, "_argptr_mem");
irFunc->_argptr = argptrmem; irFunc->_argptr = argptrMem;
// initialize _argptr with a call to the va_start intrinsic // initialize _argptr with a call to the va_start intrinsic
LLValue *vaStartArg = gABI->prepareVaStart(argptrmem); DLValue argptrVal(argptrType, argptrMem);
llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), vaStartArg, "", LLValue *llAp = gABI->prepareVaStart(&argptrVal);
llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), llAp, "",
gIR->scopebb()); gIR->scopebb());
// copy _arguments to a memory location // 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"); e->error("va_start instruction expects 1 (or 2) arguments");
fatal(); 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? // variadic extern(D) function with implicit _argptr?
if (LLValue *pArgptr = p->func()->_argptr) { if (LLValue *argptrMem = p->func()->_argptr) {
DtoMemCpy(pAp, pArgptr); // ap = _argptr DtoMemCpy(DtoLVal(ap), argptrMem); // ap = _argptr
result = new DImValue(e->type, pAp);
} else { } else {
LLValue *vaStartArg = gABI->prepareVaStart(pAp); LLValue *llAp = gABI->prepareVaStart(ap);
result = p->ir->CreateCall(GET_INTRINSIC_DECL(vastart), llAp, "");
new DImValue(e->type, p->ir->CreateCall(GET_INTRINSIC_DECL(vastart),
vaStartArg, ""));
} }
result = nullptr;
return true; return true;
} }
@ -284,10 +283,11 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e,
e->error("va_copy instruction expects 2 arguments"); e->error("va_copy instruction expects 2 arguments");
fatal(); fatal();
} }
LLValue *pDest = DtoLVal((*e->arguments)[0]); // va_list* DLValue *dest = toElem((*e->arguments)[0])->isLVal(); // va_list
LLValue *src = DtoRVal((*e->arguments)[1]); // va_list assert(dest);
gABI->vaCopy(pDest, src); DValue *src = toElem((*e->arguments)[1]); // va_list
result = new DLValue(e->type, pDest); gABI->vaCopy(dest, src);
result = nullptr;
return true; return true;
} }
@ -297,13 +297,15 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e,
e->error("va_arg instruction expects 1 argument"); e->error("va_arg instruction expects 1 argument");
fatal(); fatal();
} }
LLValue *pAp = DtoLVal((*e->arguments)[0]); // va_list*
LLValue *vaArgArg = gABI->prepareVaArg(pAp);
LLType *llType = DtoType(e->type);
if (DtoIsInMemoryOnly(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; return true;
} }

View file

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

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