From 7bbe7826156a71c25c6c0240aca87c92ea3234db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jernej=20Krempu=C5=A1?= Date: Mon, 22 Oct 2012 02:25:56 +0200 Subject: [PATCH] Pragma llvm_inline_ir Adding pragma llvm_inline_ir. Improved the error messages. Append "ret void" when the return type is void Improved the error message in case when the string passed as llvm inline ir isn't valid llvm assembly language. LLVM 3.2 fix. Add attribute AlwaysInline inside DtoInlineIRFunction. Always generate a body for llvm_inline_ir Also, always make llvm_inline_ir functions linkonce_odr. Because the body is always generated when a module uses a llvm_inline_ir function, the fact that the linker removes the function shouldn't cause problems. --- CMakeLists.txt | 2 +- dmd/idgen.c | 1 + dmd2/idgen.c | 1 + gen/functions.cpp | 98 ++++++++++++++++++++++++++++++++++++++++++++++- gen/pragma.cpp | 54 ++++++++++++++++++++++++++ gen/pragma.h | 1 + 6 files changed, 154 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e7aad73..e69ca14e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ endif(MSVC) # We need to find exactly the right LLVM version, our code usually does not # work across LLVM »minor« releases. find_package(LLVM 3.0 REQUIRED - all-targets bitwriter linker ipo instrumentation backend support tablegen ${EXTRA_LLVM_MODULES}) + all-targets bitwriter linker ipo instrumentation backend support tablegen asmparser ${EXTRA_LLVM_MODULES}) math(EXPR LDC_LLVM_VER ${LLVM_VERSION_MAJOR}*100+${LLVM_VERSION_MINOR}) # diff --git a/dmd/idgen.c b/dmd/idgen.c index 12d11463..49166c26 100644 --- a/dmd/idgen.c +++ b/dmd/idgen.c @@ -239,6 +239,7 @@ Msgtable msgtable[] = { "ldc" }, { "allow_inline" }, { "llvm_inline_asm" }, + { "llvm_inline_ir" }, { "fence" }, { "atomic_load" }, { "atomic_store" }, diff --git a/dmd2/idgen.c b/dmd2/idgen.c index 5334221f..dbb2ef93 100644 --- a/dmd2/idgen.c +++ b/dmd2/idgen.c @@ -283,6 +283,7 @@ Msgtable msgtable[] = { "ldc" }, { "allow_inline" }, { "llvm_inline_asm" }, + { "llvm_inline_ir" }, { "fence" }, { "atomic_load" }, { "atomic_store" }, diff --git a/gen/functions.cpp b/gen/functions.cpp index 269fd87f..6b3e42e5 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -257,6 +257,83 @@ llvm::FunctionType* DtoFunctionType(Type* type, Type* thistype, Type* nesttype, ////////////////////////////////////////////////////////////////////////////////////////// +#include +#include "llvm/Support/SourceMgr.h" +#include "llvm/Assembly/Parser.h" + +LLFunction* DtoInlineIRFunction(FuncDeclaration* fdecl) +{ + const char* mangled_name = fdecl->mangle(); + TemplateInstance* tinst = fdecl->parent->isTemplateInstance(); + assert(tinst); + + Objects& objs = tinst->tdtypes; + assert(objs.dim == 3); + + Expression* a0 = isExpression(objs[0]); + assert(a0); + StringExp* strexp = a0->toString(); + assert(strexp); + assert(strexp->sz == 1); + std::string code(static_cast(strexp->string), strexp->len); + + Type* ret = isType(objs[1]); + assert(ret); + + Tuple* a2 = isTuple(objs[2]); + assert(a2); + Objects& arg_types = a2->objects; + + std::string str; + llvm::raw_string_ostream stream(str); + stream << "define " << *DtoType(ret) << " @" << mangled_name << "("; + + for(size_t i = 0; ;) + { + Type* ty = isType(arg_types[i]); + //assert(ty); + if(!ty) + { + error(tinst->loc, + "All parameters of a template defined with pragma llvm_inline_ir, except for the first one, should be types"); + fatal(); + } + stream << *DtoType(ty); + + i++; + if(i >= arg_types.dim) + break; + + stream << ", "; + } + + if(ret->ty == Tvoid) + code.append("\nret void"); + + stream << ")\n{\n" << code << "\n}"; + + llvm::SMDiagnostic err; + llvm::ParseAssemblyString(stream.str().c_str(), gIR->module, err, gIR->context()); + std::string errstr = err.getMessage(); + if(errstr != "") + error(tinst->loc, + "can't parse inline LLVM IR:\n%s\n%s\n%s\nThe input string was: \n%s", + err.getLineContents().c_str(), + (std::string(err.getColumnNo(), ' ') + '^').c_str(), + errstr.c_str(), stream.str().c_str()); + + LLFunction* fun = gIR->module->getFunction(mangled_name); + fun->setLinkage(llvm::GlobalValue::LinkOnceODRLinkage); +#if LDC_LLVM_VER >= 302 + fun->addFnAttr(llvm::Attributes::AlwaysInline); +#else + fun->addFnAttr(AlwaysInline); +#endif + return fun; +} + +////////////////////////////////////////////////////////////////////////////////////////// + static llvm::FunctionType* DtoVaFunctionType(FuncDeclaration* fdecl) { TypeFunction* f = static_cast(fdecl->type); @@ -407,6 +484,15 @@ void DtoResolveFunction(FuncDeclaration* fdecl) fdecl->ir.defined = true; return; // this gets mapped to a special inline asm call, no point in going on. } + else if (tempdecl->llvmInternal == LLVMinline_ir) + { + fdecl->llvmInternal = LLVMinline_ir; + fdecl->linkage = LINKc; + fdecl->ir.defined = true; + Type* type = fdecl->type; + assert(type->ty == Tfunction); + static_cast(type)->linkage = LINKc; + } } DtoType(fdecl->type); @@ -488,8 +574,13 @@ static void set_param_attrs(TypeFunction* f, llvm::Function* func, FuncDeclarati bool found = false; for (size_t j = 0; j < newSize; ++j) { if (attrs[j].Index == curr.Index) { - // TODO: LLVM 3.2. +#if LDC_LLVM_VER >= 302 + attrs[j].Attrs = llvm::Attributes::get( + gIR->context(), + llvm::AttrBuilder(attrs[j].Attrs).addAttributes(curr.Attrs)); +#else attrs[j].Attrs |= curr.Attrs; +#endif found = true; break; } @@ -556,7 +647,10 @@ void DtoDeclareFunction(FuncDeclaration* fdecl) LLFunctionType* functype = DtoFunctionType(fdecl); LLFunction* func = vafunc ? vafunc : gIR->module->getFunction(mangled_name); if (!func) { - func = LLFunction::Create(functype, DtoLinkage(fdecl), mangled_name, gIR->module); + if(fdecl->llvmInternal == LLVMinline_ir) + func = DtoInlineIRFunction(fdecl); + else + func = LLFunction::Create(functype, DtoLinkage(fdecl), mangled_name, gIR->module); } else if (func->getFunctionType() != functype) { error(fdecl->loc, "Function type does not match previously declared function with the same mangled name: %s", fdecl->mangle()); } diff --git a/gen/pragma.cpp b/gen/pragma.cpp index 4118d4d9..3969f38a 100644 --- a/gen/pragma.cpp +++ b/gen/pragma.cpp @@ -257,6 +257,17 @@ Pragma DtoGetPragma(Scope *sc, PragmaDeclaration *decl, std::string &arg1str) return LLVMinline_asm; } + // pragma(llvm_inline_ir) { templdecl(s) } + else if (ident == Id::llvm_inline_ir) + { + if (args && args->dim > 0) + { + error("takes no parameters"); + fatal(); + } + return LLVMinline_ir; + } + return LLVMnone; } @@ -421,6 +432,49 @@ void DtoCheckPragma(PragmaDeclaration *decl, Dsymbol *s, } break; + case LLVMinline_ir: + if (TemplateDeclaration* td = s->isTemplateDeclaration()) + { + Dsymbol* member = td->onemember; + if (!member) + { + error("the '%s' pragma template must have exactly one member", ident->toChars()); + fatal(); + } + FuncDeclaration* fun = member->isFuncDeclaration(); + if (!fun) + { + error("the '%s' pragma template's member must be a function declaration", ident->toChars()); + fatal(); + } + + TemplateParameters& params = *td->parameters; + bool valid_params = + params.dim == 3 && params[1]->isTemplateTypeParameter() && + params[2]->isTemplateTupleParameter(); + + if(valid_params) + { + TemplateValueParameter* p0 = params[0]->isTemplateValueParameter(); + valid_params = valid_params && p0 && p0->valType == Type::tstring; + } + + if(!valid_params) + { + error("the '%s' pragma template must have exactly three parameters: a string, a type and a type tuple", + ident->toChars()); + fatal(); + } + + td->llvmInternal = llvm_internal; + } + else + { + error("the '%s' pragma is only allowed on template declarations", ident->toChars()); + fatal(); + } + break; + default: warning(Loc(), "the LDC specific pragma '%s' is not yet implemented, ignoring", ident->toChars()); } diff --git a/gen/pragma.h b/gen/pragma.h index 9895bc2d..fb28b9d9 100644 --- a/gen/pragma.h +++ b/gen/pragma.h @@ -22,6 +22,7 @@ enum Pragma LLVMva_end, LLVMva_arg, LLVMinline_asm, + LLVMinline_ir, LLVMfence, LLVMatomic_store, LLVMatomic_load,