[svn r59] Added support for C-style variadic functions. Currently only works on x86, x86-64 va_arg is broken in LLVM 2.1. PPC and PPC64 unknown.

Updates to runtime. Rebuild!
This commit is contained in:
Tomas Lindquist Olsen
2007-10-24 22:18:06 +02:00
parent 039bc0880d
commit 152e229329
13 changed files with 324 additions and 114 deletions

View File

@@ -29,8 +29,10 @@
#include "aggregate.h"
#include "module.h"
#include "parse.h"
#include "template.h"
#include "../gen/enums.h"
#include "../gen/logger.h"
extern void obj_includelib(char *name);
@@ -752,9 +754,9 @@ void PragmaDeclaration::semantic(Scope *sc)
#if IN_LLVM
int llvm_internal = 0;
char* llvm_str1 = NULL;
#endif
//printf("\tPragmaDeclaration::semantic '%s'\n",toChars());
if (ident == Id::msg)
{
@@ -873,6 +875,18 @@ void PragmaDeclaration::semantic(Scope *sc)
llvm_internal = LLVMbind;
assert(args->dim == 2);
}
else if (strcmp(str,"va_start")==0) {
llvm_internal = LLVMva_start;
assert(args->dim == 1);
}
else if (strcmp(str,"va_arg")==0) {
llvm_internal = LLVMva_arg;
assert(args->dim == 1);
}
else if (strcmp(str,"va_intrinsic")==0) {
llvm_internal = LLVMva_intrinsic;
assert(args->dim == 2);
}
else {
error("unknown pragma command: %s", str);
}
@@ -886,6 +900,7 @@ void PragmaDeclaration::semantic(Scope *sc)
case LLVMintrinsic:
case LLVMmangle:
case LLVMbind:
case LLVMva_intrinsic:
e = (Expression *)args->data[1];
e = e->semantic(sc);
e = e->optimize(WANTvalue);
@@ -897,6 +912,8 @@ void PragmaDeclaration::semantic(Scope *sc)
break;
case LLVMnull:
case LLVMva_arg:
case LLVMva_start:
break;
default:
@@ -923,6 +940,7 @@ void PragmaDeclaration::semantic(Scope *sc)
{
case LLVMintrinsic:
case LLVMmangle:
case LLVMva_intrinsic:
if (FuncDeclaration* fd = s->isFuncDeclaration()) {
fd->llvmInternal = llvm_internal;
fd->llvmInternal1 = llvm_str1;
@@ -932,7 +950,7 @@ void PragmaDeclaration::semantic(Scope *sc)
assert(0);
}
break;
case LLVMnull:
if (StaticCtorDeclaration* sd = s->isStaticCtorDeclaration()) {
sd->llvmInternal = llvm_internal;
@@ -942,7 +960,7 @@ void PragmaDeclaration::semantic(Scope *sc)
assert(0);
}
break;
case LLVMbind:
if (VarDeclaration* vd = s->isVarDeclaration()) {
vd->llvmInternal = llvm_internal;
@@ -953,7 +971,23 @@ void PragmaDeclaration::semantic(Scope *sc)
assert(0);
}
break;
case LLVMva_start:
case LLVMva_arg:
if (TemplateDeclaration* td = s->isTemplateDeclaration()) {
td->llvmInternal = llvm_internal;
assert(td->parameters->dim == 1);
assert(!td->overnext);
assert(!td->overroot);
assert(td->onemember);
Logger::println("template->onemember = %s", td->onemember->toChars());
}
else {
error("can only be used on templates");
assert(0);
}
break;
default:
assert(0 && "invalid LLVM_internal pragma got through :/");
}

View File

@@ -47,6 +47,7 @@ Dsymbol::Dsymbol()
this->llvmInternal1 = NULL;
this->llvmInternal2 = NULL;
this->llvmValue = NULL;
this->llvmDModule = NULL;
}
Dsymbol::Dsymbol(Identifier *ident)

View File

@@ -15,6 +15,7 @@
#include <assert.h>
#include <limits.h>
#include <string>
#include <vaarg.h>
#if _WIN32
#include <windows.h>
@@ -667,6 +668,10 @@ int main(int argc, char *argv[])
global.params.llvmArch = const_cast<char*>(e->Name);
if (global.params.verbose || very_verbose)
printf("Default target found: %s\n", global.params.llvmArch);
if (very_verbose) {
int X = sizeof(va_list);
printf("valist.sizeof = %d\n", X);
}
}
}

View File

@@ -5,4 +5,7 @@ enum
LLVMmangle,
LLVMintrinsic,
LLVMbind,
LLVMva_arg,
LLVMva_start,
LLVMva_intrinsic
};

View File

@@ -210,12 +210,12 @@ elem* VarExp::toElem(IRState* p)
else if (FuncDeclaration* fdecl = var->isFuncDeclaration())
{
Logger::println("FuncDeclaration");
if (fdecl->llvmValue == 0) {
if (fdecl->llvmInternal != LLVMva_arg && fdecl->llvmValue == 0)
fdecl->toObjFile();
}
e->val = fdecl->llvmValue;
e->type = elem::FUNC;
e->funcdecl = fdecl;
return e;
}
else if (SymbolDeclaration* sdecl = var->isSymbolDeclaration())
{
@@ -971,21 +971,21 @@ elem* CallExp::toElem(IRState* p)
{
Logger::print("CallExp::toElem: %s\n", toChars());
LOG_SCOPE;
elem* e = new elem;
elem* fn = e1->toElem(p);
LINK dlink = LINKdefault;
TypeFunction* tf = 0;
Type* e1type = LLVM_DtoDType(e1->type);
bool delegateCall = false;
llvm::Value* zero = llvm::ConstantInt::get(llvm::Type::Int32Ty,0,false);
llvm::Value* one = llvm::ConstantInt::get(llvm::Type::Int32Ty,1,false);
LINK dlink = LINKdefault;
// hidden struct return parameter handling
bool retinptr = false;
TypeFunction* tf = 0;
Type* e1type = LLVM_DtoDType(e1->type);
// regular functions
if (e1type->ty == Tfunction) {
tf = (TypeFunction*)e1type;
@@ -1012,7 +1012,38 @@ elem* CallExp::toElem(IRState* p)
assert(tf);
}
// va args
bool va_magic = false;
bool va_intrinsic = false;
if (fn->funcdecl) {
if (fn->funcdecl->llvmInternal == LLVMva_intrinsic) {
va_magic = true;
va_intrinsic = true;
}
else if (fn->funcdecl->llvmInternal == LLVMva_start) {
va_magic = true;
}
else if (fn->funcdecl->llvmInternal == LLVMva_arg) {
Argument* fnarg = Argument::getNth(tf->parameters, 0);
Expression* exp = (Expression*)arguments->data[0];
elem* expelem = exp->toElem(p);
assert(expelem->mem);
elem* e = new elem;
Type* t = LLVM_DtoDType(type);
const llvm::Type* llt = LLVM_DtoType(type);
if (LLVM_DtoIsPassedByRef(t))
llt = llvm::PointerType::get(llt);
e->type = elem::VAL;
e->val = p->ir->CreateVAArg(expelem->mem,llt,"tmp");
delete expelem;
return e;
}
}
// args
size_t n = arguments->dim;
if (fn->funcdecl && fn->funcdecl->llvmInternal == LLVMva_start)
n = 1;
if (fn->arg || delegateCall) n++;
if (retinptr) n++;
@@ -1113,11 +1144,26 @@ elem* CallExp::toElem(IRState* p)
Logger::println("regular arguments");
// va arg function special argument passing
if (va_magic) {
size_t n = va_intrinsic ? arguments->dim : 1;
for (int i=0; i<n; i++,j++)
{
Argument* fnarg = Argument::getNth(tf->parameters, i);
Expression* exp = (Expression*)arguments->data[i];
elem* expelem = exp->toElem(p);
assert(expelem->mem);
llargs[j] = p->ir->CreateBitCast(expelem->mem, llvm::PointerType::get(llvm::Type::Int8Ty), "tmp");
delete expelem;
}
}
// regular arguments
for (int i=0; i<arguments->dim; i++,j++)
{
Argument* fnarg = Argument::getNth(tf->parameters, i);
llargs[j] = LLVM_DtoArgument(llfnty->getParamType(j), fnarg, (Expression*)arguments->data[i]);
else {
for (int i=0; i<arguments->dim; i++,j++)
{
Argument* fnarg = Argument::getNth(tf->parameters, i);
llargs[j] = LLVM_DtoArgument(llfnty->getParamType(j), fnarg, (Expression*)arguments->data[i]);
}
}
// void returns cannot not be named
@@ -1131,7 +1177,7 @@ elem* CallExp::toElem(IRState* p)
Logger::cout() << *llargs[i] << '\n';
}
//Logger::cout() << "Calling: " << *funcval->getType() << '\n';
Logger::cout() << "Calling: " << *funcval->getType() << '\n';
// call the function
llvm::CallInst* call = new llvm::CallInst(funcval, llargs.begin(), llargs.end(), varname, p->scopebb());
@@ -1141,7 +1187,7 @@ elem* CallExp::toElem(IRState* p)
e->val = call;
// set calling convention
if ((fn->funcdecl && (fn->funcdecl->llvmInternal != LLVMintrinsic)) || delegateCall)
if ((fn->funcdecl && (fn->funcdecl->llvmInternal != LLVMintrinsic && fn->funcdecl->llvmInternal != LLVMva_start)) || delegateCall)
call->setCallingConv(LLVM_DtoCallingConv(dlink));
else if (fn->callconv != (unsigned)-1)
call->setCallingConv(fn->callconv);

View File

@@ -221,8 +221,39 @@ const llvm::FunctionType* LLVM_DtoFunctionType(Type* t, const llvm::Type* thispa
//////////////////////////////////////////////////////////////////////////////////////////
static const llvm::FunctionType* LLVM_DtoVaFunctionType(FuncDeclaration* fdecl)
{
TypeFunction* f = (TypeFunction*)fdecl->type;
assert(f != 0);
const llvm::PointerType* i8pty = llvm::PointerType::get(llvm::Type::Int8Ty);
std::vector<const llvm::Type*> args;
if (fdecl->llvmInternal == LLVMva_start) {
args.push_back(i8pty);
}
else if (fdecl->llvmInternal == LLVMva_intrinsic) {
size_t n = Argument::dim(f->parameters);
for (size_t i=0; i<n; ++i) {
args.push_back(i8pty);
}
}
else
assert(0);
const llvm::FunctionType* fty = llvm::FunctionType::get(llvm::Type::VoidTy, args, false);
f->llvmType = fty;
return fty;
}
//////////////////////////////////////////////////////////////////////////////////////////
const llvm::FunctionType* LLVM_DtoFunctionType(FuncDeclaration* fdecl)
{
if ((fdecl->llvmInternal == LLVMva_start) || (fdecl->llvmInternal == LLVMva_intrinsic)) {
return LLVM_DtoVaFunctionType(fdecl);
}
TypeFunction* f = (TypeFunction*)fdecl->type;
assert(f != 0);
@@ -260,73 +291,79 @@ const llvm::FunctionType* LLVM_DtoFunctionType(FuncDeclaration* fdecl)
// parameter types
std::vector<const llvm::Type*> paramvec;
if (retinptr) {
Logger::cout() << "returning through pointer parameter: " << *rettype << '\n';
paramvec.push_back(rettype);
}
if (fdecl->needThis()) {
if (AggregateDeclaration* ad = fdecl->isMember()) {
Logger::print("isMember = this is: %s\n", ad->type->toChars());
const llvm::Type* thisty = LLVM_DtoType(ad->type);
Logger::cout() << "this llvm type: " << *thisty << '\n';
if (llvm::isa<llvm::StructType>(thisty) || thisty == gIR->topstruct().recty.get())
thisty = llvm::PointerType::get(thisty);
paramvec.push_back(thisty);
usesthis = true;
}
else
assert(0);
}
else if (fdecl->isNested()) {
if (fdecl->llvmInternal == LLVMva_start) {
paramvec.push_back(llvm::PointerType::get(llvm::Type::Int8Ty));
usesthis = true;
}
size_t n = Argument::dim(f->parameters);
for (int i=0; i < n; ++i) {
Argument* arg = Argument::getNth(f->parameters, i);
// ensure scalar
Type* argT = LLVM_DtoDType(arg->type);
assert(argT);
if ((arg->storageClass & STCref) || (arg->storageClass & STCout)) {
//assert(arg->vardecl);
//arg->vardecl->refparam = true;
else {
if (retinptr) {
Logger::cout() << "returning through pointer parameter: " << *rettype << '\n';
paramvec.push_back(rettype);
}
else
arg->llvmCopy = true;
const llvm::Type* at = LLVM_DtoType(argT);
if (llvm::isa<llvm::StructType>(at)) {
Logger::println("struct param");
paramvec.push_back(llvm::PointerType::get(at));
}
else if (llvm::isa<llvm::ArrayType>(at)) {
Logger::println("sarray param");
assert(argT->ty == Tsarray);
//paramvec.push_back(llvm::PointerType::get(at->getContainedType(0)));
paramvec.push_back(llvm::PointerType::get(at));
}
else if (llvm::isa<llvm::OpaqueType>(at)) {
Logger::println("opaque param");
if (argT->ty == Tstruct || argT->ty == Tclass)
paramvec.push_back(llvm::PointerType::get(at));
if (fdecl->needThis()) {
if (AggregateDeclaration* ad = fdecl->isMember()) {
Logger::print("isMember = this is: %s\n", ad->type->toChars());
const llvm::Type* thisty = LLVM_DtoType(ad->type);
Logger::cout() << "this llvm type: " << *thisty << '\n';
if (llvm::isa<llvm::StructType>(thisty) || thisty == gIR->topstruct().recty.get())
thisty = llvm::PointerType::get(thisty);
paramvec.push_back(thisty);
usesthis = true;
}
else
assert(0);
}
/*if (llvm::isa<llvm::StructType>(at) || argT->ty == Tstruct || argT->ty == Tsarray) {
paramvec.push_back(llvm::PointerType::get(at));
}*/
else {
if (!arg->llvmCopy) {
Logger::println("ref param");
at = llvm::PointerType::get(at);
else if (fdecl->isNested()) {
paramvec.push_back(llvm::PointerType::get(llvm::Type::Int8Ty));
usesthis = true;
}
size_t n = Argument::dim(f->parameters);
for (int i=0; i < n; ++i) {
Argument* arg = Argument::getNth(f->parameters, i);
// ensure scalar
Type* argT = LLVM_DtoDType(arg->type);
assert(argT);
if ((arg->storageClass & STCref) || (arg->storageClass & STCout)) {
//assert(arg->vardecl);
//arg->vardecl->refparam = true;
}
else
arg->llvmCopy = true;
const llvm::Type* at = LLVM_DtoType(argT);
if (llvm::isa<llvm::StructType>(at)) {
Logger::println("struct param");
paramvec.push_back(llvm::PointerType::get(at));
}
else if (llvm::isa<llvm::ArrayType>(at)) {
Logger::println("sarray param");
assert(argT->ty == Tsarray);
//paramvec.push_back(llvm::PointerType::get(at->getContainedType(0)));
paramvec.push_back(llvm::PointerType::get(at));
}
else if (llvm::isa<llvm::OpaqueType>(at)) {
Logger::println("opaque param");
if (argT->ty == Tstruct || argT->ty == Tclass)
paramvec.push_back(llvm::PointerType::get(at));
else
assert(0);
}
/*if (llvm::isa<llvm::StructType>(at) || argT->ty == Tstruct || argT->ty == Tsarray) {
paramvec.push_back(llvm::PointerType::get(at));
}*/
else {
Logger::println("in param");
if (!arg->llvmCopy) {
Logger::println("ref param");
at = llvm::PointerType::get(at);
}
else {
Logger::println("in param");
}
paramvec.push_back(at);
}
paramvec.push_back(at);
}
}
@@ -931,10 +968,44 @@ llvm::Value* LLVM_DtoGEPi(llvm::Value* ptr, unsigned i0, unsigned i1, const std:
//////////////////////////////////////////////////////////////////////////////////////////
static llvm::Function* LLVM_DtoDeclareVaFunction(FuncDeclaration* fdecl)
{
TypeFunction* f = (TypeFunction*)LLVM_DtoDType(fdecl->type);
const llvm::FunctionType* fty = LLVM_DtoVaFunctionType(fdecl);
llvm::Constant* fn = 0;
if (fdecl->llvmInternal == LLVMva_start) {
fn = gIR->module->getOrInsertFunction("llvm.va_start", fty);
assert(fn);
}
else if (fdecl->llvmInternal == LLVMva_intrinsic) {
fn = gIR->module->getOrInsertFunction(fdecl->llvmInternal1, fty);
assert(fn);
}
else
assert(0);
llvm::Function* func = llvm::cast_or_null<llvm::Function>(fn);
assert(func);
assert(func->isIntrinsic());
fdecl->llvmValue = func;
return func;
}
//////////////////////////////////////////////////////////////////////////////////////////
llvm::Function* LLVM_DtoDeclareFunction(FuncDeclaration* fdecl)
{
if ((fdecl->llvmInternal == LLVMva_start) || (fdecl->llvmInternal == LLVMva_intrinsic)) {
return LLVM_DtoDeclareVaFunction(fdecl);
}
// mangled name
char* mangled_name = (fdecl->llvmInternal == LLVMintrinsic) ? fdecl->llvmInternal1 : fdecl->mangle();
char* mangled_name;
if (fdecl->llvmInternal == LLVMintrinsic)
mangled_name = fdecl->llvmInternal1;
else
mangled_name = fdecl->mangle();
// unit test special handling
if (fdecl->isUnitTestDeclaration())

View File

@@ -646,8 +646,31 @@ void FuncDeclaration::toObjFile()
return;
}
Type* t = LLVM_DtoDType(type);
TypeFunction* f = (TypeFunction*)t;
bool declareOnly = false;
if (TemplateInstance* tinst = parent->isTemplateInstance()) {
TemplateDeclaration* tempdecl = tinst->tempdecl;
if (tempdecl->llvmInternal == LLVMva_start)
{
Logger::println("magic va_start found");
llvmInternal = LLVMva_start;
declareOnly = true;
}
else if (tempdecl->llvmInternal == LLVMva_arg)
{
Logger::println("magic va_arg found");
llvmInternal = LLVMva_arg;
return;
}
}
llvm::Function* func = LLVM_DtoDeclareFunction(this);
if (declareOnly)
return;
if (!gIR->structs.empty() && gIR->topstruct().queueFuncs) {
if (!llvmQueued) {
Logger::println("queueing %s", toChars());
@@ -657,8 +680,6 @@ void FuncDeclaration::toObjFile()
return; // we wait with the definition as they might invoke a virtual method and the vtable is not yet complete
}
Type* t = LLVM_DtoDType(type);
TypeFunction* f = (TypeFunction*)t;
assert(f->llvmType);
const llvm::FunctionType* functype = llvm::cast<llvm::FunctionType>(llvmValue->getType()->getContainedType(0));

View File

@@ -25,6 +25,10 @@ echo "compiling typeinfos"
rebuild typeinfos.d -c -oqobj -dc=llvmdc-posix || exit 1
llvm-link -f -o=../lib/llvmdcore.bc `ls obj/typeinfo.*.bc` ../lib/llvmdcore.bc || exit 1
echo "compiling llvm runtime support"
rebuild llvmsupport.d -c -oqobj -dc=llvmdc-posix || exit
llvm-link -f -o=../lib/llvmdcore.bc `ls obj/llvm.*.bc` ../lib/llvmdcore.bc || exit 1
echo "optimizing"
opt -f -std-compile-opts -o=../lib/llvmdcore.bc ../lib/llvmdcore.bc || exit 1

View File

@@ -1,15 +1,5 @@
module llvm.intrinsic;
// variable argument handling intrinsics
pragma(LLVM_internal, "intrinsic", "llvm.va_start")
void llvm_va_start(void* args);
pragma(LLVM_internal, "intrinsic", "llvm.va_end")
void llvm_va_end(void* args);
pragma(LLVM_internal, "intrinsic", "llvm.va_copy")
void llvm_va_copy(void* dst, void* src);
// code generator intrinsics
/*
pragma(LLVM_internal, "intrinsic", "llvm.returnaddress")

25
lphobos/llvm/va_list.d Normal file
View File

@@ -0,0 +1,25 @@
module llvm.va_list;
alias void* va_list;
/*
version(X86)
{
alias void* va_list;
}
else version(X86_64)
{
struct X86_64_va_list
{
uint gp_offset;
uint fp_offset;
void* overflow_arg_area;
void* reg_save_area;
}
alias X86_64_va_list va_list;
}
else
static assert("only x86 and x86-64 support va_list");
*/

5
lphobos/llvmsupport.d Normal file
View File

@@ -0,0 +1,5 @@
module llvmsupport;
import
llvm.intrinsic,
llvm.va_list;

View File

@@ -1,7 +1,7 @@
/**
* C's &lt;stdarg.h&gt;
* Authors: Hauke Duden and Walter Bright, Digital Mars, www.digitalmars.com
* Authors: Hauke Duden, Walter Bright and Tomas Lindquist Olsen, Digital Mars, www.digitalmars.com
* License: Public Domain
* Macros:
* WIKI=Phobos/StdCStdarg
@@ -11,32 +11,16 @@
module std.c.stdarg;
alias void* va_list;
public import llvm.va_list;
template va_start(T)
{
void va_start(out va_list ap, inout T parmn)
{
ap = cast(va_list)(cast(void*)&parmn + ((T.sizeof + int.sizeof - 1) & ~(int.sizeof - 1)));
}
}
pragma(LLVM_internal, "va_start")
void va_start(T)(va_list ap, ref T);
template va_arg(T)
{
T va_arg(inout va_list ap)
{
T arg = *cast(T*)ap;
ap = cast(va_list)(cast(void*)ap + ((T.sizeof + int.sizeof - 1) & ~(int.sizeof - 1)));
return arg;
}
}
pragma(LLVM_internal, "va_arg")
T va_arg(T)(va_list ap);
void va_end(va_list ap)
{
pragma(LLVM_internal, "va_intrinsic", "llvm.va_end")
void va_end(va_list args);
}
void va_copy(out va_list dest, va_list src)
{
dest = src;
}
pragma(LLVM_internal, "va_intrinsic", "llvm.va_copy")
void va_copy(va_list dst, va_list src);

21
test/vararg1.d Normal file
View File

@@ -0,0 +1,21 @@
module vararg1;
import std.c.stdarg;
extern(C) int add(int n, ...)
{
va_list ap=void;
va_start(ap, n);
int r;
//for (int i=0; i<n; i++)
// r += va_arg!(int)(ap);
r = va_arg!(int)(ap);
va_end(ap);
return r;
}
void main()
{
int i = add(4,1,2,3,4);
assert(i == 10);
}