From 99b863e2b1b8193526fa013d4c442c7e988b98dd Mon Sep 17 00:00:00 2001 From: Tomas Lindquist Olsen Date: Sat, 28 Mar 2009 06:32:06 +0100 Subject: [PATCH] Added initial support for raw LLVM inline asm. --- dmd/attrib.c | 38 +++++++++++++++++ dmd/expression.h | 4 -- dmd/idgen.c | 1 + gen/enums.h | 3 +- gen/functions.cpp | 16 +++++++ gen/llvmhelpers.h | 3 ++ gen/naked.cpp | 78 +++++++++++++++++++++++++++++++++++ gen/toir.cpp | 19 ++++++++- runtime/import/ldc/llvmasm.di | 7 ++++ 9 files changed, 163 insertions(+), 6 deletions(-) create mode 100644 runtime/import/ldc/llvmasm.di diff --git a/dmd/attrib.c b/dmd/attrib.c index fda2f7d7..161f868a 100644 --- a/dmd/attrib.c +++ b/dmd/attrib.c @@ -987,6 +987,17 @@ void PragmaDeclaration::semantic(Scope *sc) } } + // pragma(llvm_inline_asm) { templdecl(s) } + else if (ident == Id::llvm_inline_asm) + { + if (args && args->dim > 0) + { + error("takes no parameters"); + fatal(); + } + llvm_internal = LLVMinline_asm; + } + #endif // LDC else if (ignoreUnsupportedPragmas) @@ -1116,6 +1127,33 @@ void PragmaDeclaration::semantic(Scope *sc) } break; + case LLVMinline_asm: + if (TemplateDeclaration* td = s->isTemplateDeclaration()) + { + if (td->parameters->dim != 0) + { + error("the '%s' pragma template must have exactly zero template parameters", ident->toChars()); + fatal(); + } + else if (!td->onemember) + { + error("the '%s' pragma template must have exactly one member", ident->toChars()); + fatal(); + } + else if (td->overnext || td->overroot) + { + error("the '%s' pragma template must not be overloaded", ident->toChars()); + fatal(); + } + td->llvmInternal = llvm_internal; + } + else + { + error("the '%s' pragma is only allowed on template declarations", ident->toChars()); + fatal(); + } + break; + default: warning("the LDC specific pragma '%s' is not yet implemented, ignoring", ident->toChars()); } diff --git a/dmd/expression.h b/dmd/expression.h index ad7287a5..ce3357d0 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -1863,12 +1863,8 @@ struct GEPExp : UnaExp void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Expression *toLvalue(Scope *sc, Expression *e); -#if IN_DMD - elem *toElem(IRState *irs); -#elif IN_LLVM DValue* toElem(IRState* irs); llvm::Constant *toConstElem(IRState *irs); -#endif }; #endif diff --git a/dmd/idgen.c b/dmd/idgen.c index 99511b85..243686b2 100644 --- a/dmd/idgen.c +++ b/dmd/idgen.c @@ -225,6 +225,7 @@ Msgtable msgtable[] = { "vaarg", "va_arg" }, { "ldc" }, { "allow_inline" }, + { "llvm_inline_asm" }, // For special functions { "tohash", "toHash" }, diff --git a/gen/enums.h b/gen/enums.h index 444ea597..a51b9f5f 100644 --- a/gen/enums.h +++ b/gen/enums.h @@ -9,5 +9,6 @@ enum LLVMva_copy, LLVMva_end, LLVMva_arg, - LLVMldc + LLVMldc, + LLVMinline_asm }; diff --git a/gen/functions.cpp b/gen/functions.cpp index d228f377..55768c63 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -332,6 +332,22 @@ void DtoResolveFunction(FuncDeclaration* fdecl) fdecl->linkage = LINKintrinsic; ((TypeFunction*)fdecl->type)->linkage = LINKintrinsic; } + else if (tempdecl->llvmInternal == LLVMinline_asm) + { + Logger::println("magic inline asm found"); + TypeFunction* tf = (TypeFunction*)fdecl->type; + if (tf->varargs != 1 || (fdecl->parameters && fdecl->parameters->dim != 0)) + { + error("invalid __asm declaration, must be a D style variadic with no explicit parameters"); + fatal(); + } + fdecl->llvmInternal = LLVMinline_asm; + fdecl->ir.resolved = true; + fdecl->ir.declared = true; + fdecl->ir.initialized = true; + fdecl->ir.defined = true; + return; // this gets mapped to a special inline asm call, no point in going on. + } } DtoFunctionType(fdecl); diff --git a/gen/llvmhelpers.h b/gen/llvmhelpers.h index ea2f76f1..b7631a86 100644 --- a/gen/llvmhelpers.h +++ b/gen/llvmhelpers.h @@ -136,6 +136,9 @@ bool needsTemplateLinkage(Dsymbol* s); /// Returns true if there is any unaligned type inside the aggregate. bool hasUnalignedFields(Type* t); +/// +DValue* DtoInlineAsmExpr(Loc loc, FuncDeclaration* fd, Expressions* arguments); + //////////////////////////////////////////// // gen/tocall.cpp stuff below //////////////////////////////////////////// diff --git a/gen/naked.cpp b/gen/naked.cpp index 24832137..af1140d1 100644 --- a/gen/naked.cpp +++ b/gen/naked.cpp @@ -1,8 +1,10 @@ #include "gen/llvm.h" +#include "llvm/InlineAsm.h" #include "expression.h" #include "statement.h" #include "declaration.h" +#include "template.h" #include @@ -10,6 +12,7 @@ #include "gen/irstate.h" #include "gen/llvmhelpers.h" #include "gen/tollvm.h" +#include "gen/dvalue.h" ////////////////////////////////////////////////////////////////////////////////////////// @@ -320,3 +323,78 @@ void emitABIReturnAsmStmt(IRAsmBlock* asmblock, Loc loc, FuncDeclaration* fdecl) // return values always go in the front asmblock->s.push_front(as); } + +////////////////////////////////////////////////////////////////////////////////////////// + +// sort of kinda related to naked ... + +DValue * DtoInlineAsmExpr(Loc loc, FuncDeclaration * fd, Expressions * arguments) +{ + Logger::println("DtoInlineAsmExpr @ %s", loc.toChars()); + LOG_SCOPE; + + TemplateInstance* ti = fd->toParent()->isTemplateInstance(); + assert(ti && "invalid inline __asm expr"); + + assert(arguments->dim >= 2 && "invalid __asm call"); + + // get code param + Expression* e = (Expression*)arguments->data[0]; + Logger::println("code exp: %s", e->toChars()); + StringExp* se = (StringExp*)e; + if (e->op != TOKstring || se->sz != 1) + { + e->error("__asm code argument is not a char[] string literal"); + fatal(); + } + std::string code((char*)se->string, se->len); + + // get constraints param + e = (Expression*)arguments->data[1]; + Logger::println("constraint exp: %s", e->toChars()); + se = (StringExp*)e; + if (e->op != TOKstring || se->sz != 1) + { + e->error("__asm constraints argument is not a char[] string literal"); + fatal(); + } + std::string constraints((char*)se->string, se->len); + + // build runtime arguments + size_t n = arguments->dim; + + LLSmallVector args; + args.reserve(n-2); + std::vector argtypes; + argtypes.reserve(n-2); + + for (size_t i = 2; i < n; i++) + { + e = (Expression*)arguments->data[i]; + args.push_back(e->toElem(gIR)->getRVal()); + argtypes.push_back(args.back()->getType()); + } + + // build asm function type + llvm::FunctionType* FT = llvm::FunctionType::get(llvm::Type::VoidTy, argtypes, false); + + // build asm call + bool sideeffect = true; + llvm::InlineAsm* ia = llvm::InlineAsm::get(FT, code, constraints, sideeffect); + + llvm::Value* v = gIR->ir->CreateCall(ia, args.begin(), args.end(), ""); + + // return NULL for now + return NULL; +} + + + + + + + + + + + diff --git a/gen/toir.cpp b/gen/toir.cpp index df76f6c3..8270fbc0 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -196,7 +196,11 @@ DValue* VarExp::toElem(IRState* p) { Logger::println("FuncDeclaration"); LLValue* func = 0; - if (fdecl->llvmInternal != LLVMva_arg) { + if (fdecl->llvmInternal == LLVMinline_asm) { + error("special ldc inline asm is not a normal function"); + fatal(); + } + else if (fdecl->llvmInternal != LLVMva_arg) { fdecl->codegen(Type::sir); func = fdecl->ir.irFunc->func; } @@ -723,6 +727,19 @@ DValue* CallExp::toElem(IRState* p) Logger::print("CallExp::toElem: %s @ %s\n", toChars(), type->toChars()); LOG_SCOPE; + // handle magic inline asm + if (e1->op == TOKvar) + { + VarExp* ve = (VarExp*)e1; + if (FuncDeclaration* fd = ve->var->isFuncDeclaration()) + { + if (fd->llvmInternal == LLVMinline_asm) + { + return DtoInlineAsmExpr(loc, fd, arguments); + } + } + } + // get the callee value DValue* fnval = e1->toElem(p); diff --git a/runtime/import/ldc/llvmasm.di b/runtime/import/ldc/llvmasm.di new file mode 100644 index 00000000..2aa035d5 --- /dev/null +++ b/runtime/import/ldc/llvmasm.di @@ -0,0 +1,7 @@ +module ldc.llvmasm; + +pragma(llvm_inline_asm) +template __asm() +{ + void __asm(char[] asmcode, char[] constraints, ...); +}