mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-09 12:32:33 +03:00
Refactor and fix intrinsics for variadic functions
I.e., va_start(), va_copy() and va_arg().
This commit is contained in:
parent
dfbb6c5b08
commit
82005009e7
9 changed files with 92 additions and 99 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 ®Count);
|
void rewriteArgument(IrFuncTyArg &arg, RegCount ®Count);
|
||||||
|
|
||||||
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() {
|
||||||
|
|
22
gen/abi.cpp
22
gen/abi.cpp
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
24
gen/abi.h
24
gen/abi.h
|
@ -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.
|
||||||
///
|
///
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1040,7 +1040,7 @@ 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
|
Loading…
Add table
Add a link
Reference in a new issue