From fc480b7fd8f8cb6dee5e579bda2778049e9f8cd3 Mon Sep 17 00:00:00 2001 From: Tomas Lindquist Olsen Date: Sun, 8 Feb 2009 05:26:54 +0100 Subject: [PATCH] SWITCHED TO LLVM 2.5 ! Applied patch from ticket #129 to compile against latest LLVM. Thanks Frits van Bommel. Fixed implicit return by asm block at the end of a function on x86-32. Other architectures will produce an error at the moment. Adding support for new targets is fairly simple. Fixed return calling convention for complex numbers, ST and ST(1) were switched around. Added some testcases. I've run a dstress test and there are no regressions. However, the runtime does not seem to compile with symbolic debug information. -O3 -release -inline works well and is what I used for the dstress run. Tango does not compile, a small workaround is needed in tango.io.digest.Digest.Digest.hexDigest. See ticket #206 . --- dmd/declaration.h | 3 ++ dmd/func.c | 1 + dmd/idgen.c | 1 + dmd/statement.c | 7 +++ dmd/statement.h | 11 ++-- dmd2/declaration.h | 3 ++ dmd2/func.c | 1 + dmd2/idgen.c | 1 + dmd2/statement.c | 7 +++ dmd2/statement.h | 11 ++-- gen/aa.cpp | 2 +- gen/arrays.cpp | 20 ++++---- gen/asmstmt.cpp | 45 +++++++++++++++-- gen/classes.cpp | 6 +-- gen/functions.cpp | 40 +++++++-------- gen/functions.h | 2 + gen/irstate.h | 9 +++- gen/llvmhelpers.cpp | 1 - gen/naked.cpp | 61 +++++++++++++++++++++++ gen/statements.cpp | 37 ++++++++++++++ gen/structs.cpp | 10 ++-- gen/tocall.cpp | 8 ++- gen/tollvm.cpp | 30 ++++++----- gen/tollvm.h | 3 +- gen/toobj.cpp | 2 +- ir/irstruct.cpp | 2 +- tests/mini/asm3.d | 19 ++++--- tests/mini/asm5.d | 23 +++++++++ tests/mini/asm8.d | 119 ++++++++++++++++++++++++++++++++++++++++++++ 29 files changed, 404 insertions(+), 81 deletions(-) create mode 100644 tests/mini/asm5.d create mode 100644 tests/mini/asm8.d diff --git a/dmd/declaration.h b/dmd/declaration.h index 836c0719..60dd2d96 100644 --- a/dmd/declaration.h +++ b/dmd/declaration.h @@ -663,6 +663,9 @@ struct FuncDeclaration : Declaration // if this is an array operation it gets a little special attention bool isArrayOp; + + // true if overridden with the pragma(allow_inline); stmt + bool allowInlining; }; struct FuncAliasDeclaration : FuncDeclaration diff --git a/dmd/func.c b/dmd/func.c index a18651ea..aaae4424 100644 --- a/dmd/func.c +++ b/dmd/func.c @@ -80,6 +80,7 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, enum STC s // LDC isArrayOp = false; + allowInlining = false; } Dsymbol *FuncDeclaration::syntaxCopy(Dsymbol *s) diff --git a/dmd/idgen.c b/dmd/idgen.c index 661cc681..99511b85 100644 --- a/dmd/idgen.c +++ b/dmd/idgen.c @@ -224,6 +224,7 @@ Msgtable msgtable[] = { "vaend", "va_end" }, { "vaarg", "va_arg" }, { "ldc" }, + { "allow_inline" }, // For special functions { "tohash", "toHash" }, diff --git a/dmd/statement.c b/dmd/statement.c index c8bfe86a..ccddb463 100644 --- a/dmd/statement.c +++ b/dmd/statement.c @@ -2146,6 +2146,13 @@ Statement *PragmaStatement::semantic(Scope *sc) } #endif } + + // LDC + else if (ident == Id::allow_inline) + { + sc->func->allowInlining = true; + } + else error("unrecognized pragma(%s)", ident->toChars()); diff --git a/dmd/statement.h b/dmd/statement.h index 607d4761..f165d12b 100644 --- a/dmd/statement.h +++ b/dmd/statement.h @@ -161,15 +161,16 @@ struct Statement : Object // Back end virtual void toIR(IRState *irs); - // LDC - virtual void toNakedIR(IRState *irs); - // Avoid dynamic_cast virtual DeclarationStatement *isDeclarationStatement() { return NULL; } virtual CompoundStatement *isCompoundStatement() { return NULL; } virtual ReturnStatement *isReturnStatement() { return NULL; } virtual IfStatement *isIfStatement() { return NULL; } virtual CaseStatement* isCaseStatement() { return NULL; } + + // LDC + virtual void toNakedIR(IRState *irs); + virtual AsmBlockStatement* endsWithAsm(); }; struct ExpStatement : Statement @@ -242,6 +243,7 @@ struct CompoundStatement : Statement // LDC virtual void toNakedIR(IRState *irs); + virtual AsmBlockStatement* endsWithAsm(); virtual CompoundStatement *isCompoundStatement() { return this; } }; @@ -905,6 +907,9 @@ struct AsmBlockStatement : CompoundStatement void toIR(IRState *irs); void toNakedIR(IRState *irs); + AsmBlockStatement* endsWithAsm(); + + llvm::Value* abiret; }; #endif /* DMD_STATEMENT_H */ diff --git a/dmd2/declaration.h b/dmd2/declaration.h index 7e26a2c6..441d694d 100644 --- a/dmd2/declaration.h +++ b/dmd2/declaration.h @@ -675,6 +675,9 @@ struct FuncDeclaration : Declaration // if this is an array operation it gets a little special attention bool isArrayOp; + + // true if overridden with the pragma(allow_inline); stmt + bool allowInlining; }; struct FuncAliasDeclaration : FuncDeclaration diff --git a/dmd2/func.c b/dmd2/func.c index 1b468986..0c99cadb 100644 --- a/dmd2/func.c +++ b/dmd2/func.c @@ -80,6 +80,7 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, enum STC s // LDC isArrayOp = false; + allowInlining = false; } Dsymbol *FuncDeclaration::syntaxCopy(Dsymbol *s) diff --git a/dmd2/idgen.c b/dmd2/idgen.c index 2882abf6..cc46b0bb 100644 --- a/dmd2/idgen.c +++ b/dmd2/idgen.c @@ -240,6 +240,7 @@ Msgtable msgtable[] = { "vaend", "va_end" }, { "vaarg", "va_arg" }, { "ldc" }, + { "allow_inline" }, // For special functions { "tohash", "toHash" }, diff --git a/dmd2/statement.c b/dmd2/statement.c index c4ab1bf1..38f06677 100644 --- a/dmd2/statement.c +++ b/dmd2/statement.c @@ -2197,6 +2197,13 @@ Statement *IfStatement::semantic(Scope *sc) condition = new AssignExp(loc, v, condition); condition = condition->semantic(scd); } + + // LDC + else if (ident == Id::allow_inline) + { + sc->func->allowInlining = true; + } + else scd = sc->push(); ifbody = ifbody->semantic(scd); diff --git a/dmd2/statement.h b/dmd2/statement.h index 98221a83..be6b78e2 100644 --- a/dmd2/statement.h +++ b/dmd2/statement.h @@ -162,15 +162,16 @@ struct Statement : Object // Back end virtual void toIR(IRState *irs); - // LDC - virtual void toNakedIR(IRState *irs); - // Avoid dynamic_cast virtual DeclarationStatement *isDeclarationStatement() { return NULL; } virtual CompoundStatement *isCompoundStatement() { return NULL; } virtual ReturnStatement *isReturnStatement() { return NULL; } virtual IfStatement *isIfStatement() { return NULL; } virtual CaseStatement* isCaseStatement() { return NULL; } + + // LDC + virtual void toNakedIR(IRState *irs); + virtual AsmBlockStatement* endsWithAsm(); }; struct ExpStatement : Statement @@ -245,6 +246,7 @@ struct CompoundStatement : Statement // LDC virtual void toNakedIR(IRState *irs); + virtual AsmBlockStatement* endsWithAsm(); virtual CompoundStatement *isCompoundStatement() { return this; } }; @@ -941,6 +943,9 @@ struct AsmBlockStatement : CompoundStatement void toIR(IRState *irs); virtual void toNakedIR(IRState *irs); + AsmBlockStatement* endsWithAsm(); + + llvm::Value* abiret; }; #endif /* DMD_STATEMENT_H */ diff --git a/gen/aa.cpp b/gen/aa.cpp index 484421c0..7d733e58 100644 --- a/gen/aa.cpp +++ b/gen/aa.cpp @@ -77,7 +77,7 @@ DValue* DtoAAIndex(Loc& loc, Type* type, DValue* aa, DValue* key, bool lvalue) keyti = DtoBitCast(keyti, funcTy->getParamType(1)); // valuesize param - LLValue* valsize = DtoConstSize_t(getABITypeSize(DtoType(type))); + LLValue* valsize = DtoConstSize_t(getTypePaddedSize(DtoType(type))); // pkey param LLValue* pkey = to_pkey(loc, key); diff --git a/gen/arrays.cpp b/gen/arrays.cpp index 43645f73..0bcd04a2 100644 --- a/gen/arrays.cpp +++ b/gen/arrays.cpp @@ -100,7 +100,7 @@ void DtoArrayInit(Loc& loc, DValue* array, DValue* value) // this simplifies codegen later on as llvm null's have no address! if (isaConstant(val) && isaConstant(val)->isNullValue()) { - size_t X = getABITypeSize(val->getType()); + size_t X = getTypePaddedSize(val->getType()); LLValue* nbytes = gIR->ir->CreateMul(dim, DtoConstSize_t(X), ".nbytes"); DtoMemSetZero(ptr, nbytes); return; @@ -181,7 +181,7 @@ void DtoArrayInit(Loc& loc, DValue* array, DValue* value) assert(arrayelemty == valuety && "ArrayInit doesn't work on elem-initialized static arrays"); args[0] = DtoBitCast(args[0], getVoidPtrType()); args[2] = DtoBitCast(args[2], getVoidPtrType()); - args.push_back(DtoConstSize_t(getABITypeSize(DtoType(arrayelemty)))); + args.push_back(DtoConstSize_t(getTypePaddedSize(DtoType(arrayelemty)))); break; default: @@ -331,7 +331,7 @@ static LLValue* get_slice_ptr(DSliceValue* e, LLValue*& sz) { assert(e->len != 0); const LLType* t = e->ptr->getType()->getContainedType(0); - sz = gIR->ir->CreateMul(DtoConstSize_t(getABITypeSize(t)), e->len, "tmp"); + sz = gIR->ir->CreateMul(DtoConstSize_t(getTypePaddedSize(t)), e->len, "tmp"); return e->ptr; } @@ -362,7 +362,7 @@ void DtoStaticArrayCopy(LLValue* dst, LLValue* src) { Logger::println("StaticArrayCopy"); - size_t n = getABITypeSize(dst->getType()->getContainedType(0)); + size_t n = getTypePaddedSize(dst->getType()->getContainedType(0)); DtoMemCpy(dst, src, DtoConstSize_t(n)); } @@ -534,7 +534,7 @@ DSliceValue* DtoCatAssignArray(DValue* arr, Expression* exp) src1 = gIR->ir->CreateGEP(src1,len1,"tmp"); // memcpy - LLValue* elemSize = DtoConstSize_t(getABITypeSize(src2->getType()->getContainedType(0))); + LLValue* elemSize = DtoConstSize_t(getTypePaddedSize(src2->getType()->getContainedType(0))); LLValue* bytelen = gIR->ir->CreateMul(len2, elemSize, "tmp"); DtoMemCpy(src1,src2,bytelen); @@ -570,7 +570,7 @@ DSliceValue* DtoCatArrays(Type* type, Expression* exp1, Expression* exp2) src2 = DtoArrayPtr(e2); // first memcpy - LLValue* elemSize = DtoConstSize_t(getABITypeSize(src1->getType()->getContainedType(0))); + LLValue* elemSize = DtoConstSize_t(getTypePaddedSize(src1->getType()->getContainedType(0))); LLValue* bytelen = gIR->ir->CreateMul(len1, elemSize, "tmp"); DtoMemCpy(mem,src1,bytelen); @@ -613,7 +613,7 @@ DSliceValue* DtoCatArrayElement(Type* type, Expression* exp1, Expression* exp2) mem = gIR->ir->CreateGEP(mem,DtoConstSize_t(1),"tmp"); - LLValue* elemSize = DtoConstSize_t(getABITypeSize(src1->getType()->getContainedType(0))); + LLValue* elemSize = DtoConstSize_t(getTypePaddedSize(src1->getType()->getContainedType(0))); LLValue* bytelen = gIR->ir->CreateMul(len1, elemSize, "tmp"); DtoMemCpy(mem,src1,bytelen); @@ -632,7 +632,7 @@ DSliceValue* DtoCatArrayElement(Type* type, Expression* exp1, Expression* exp2) src1 = DtoArrayPtr(e1); - LLValue* elemSize = DtoConstSize_t(getABITypeSize(src1->getType()->getContainedType(0))); + LLValue* elemSize = DtoConstSize_t(getTypePaddedSize(src1->getType()->getContainedType(0))); LLValue* bytelen = gIR->ir->CreateMul(len1, elemSize, "tmp"); DtoMemCpy(mem,src1,bytelen); @@ -769,8 +769,8 @@ LLValue* DtoArrayCastLength(LLValue* len, const LLType* elemty, const LLType* ne assert(elemty); assert(newelemty); - size_t esz = getABITypeSize(elemty); - size_t nsz = getABITypeSize(newelemty); + size_t esz = getTypePaddedSize(elemty); + size_t nsz = getTypePaddedSize(newelemty); if (esz == nsz) return len; diff --git a/gen/asmstmt.cpp b/gen/asmstmt.cpp index db94271a..b1e22f6c 100644 --- a/gen/asmstmt.cpp +++ b/gen/asmstmt.cpp @@ -26,6 +26,7 @@ #include "gen/logger.h" #include "gen/todebug.h" #include "gen/llvmhelpers.h" +#include "gen/functions.h" typedef enum { Arg_Integer, @@ -400,6 +401,8 @@ AsmBlockStatement::AsmBlockStatement(Loc loc, Statements* s) { enclosinghandler = NULL; tf = NULL; + + abiret = NULL; } // rewrite argument indices to the block scope indices @@ -452,18 +455,21 @@ static void remap_inargs(std::string& insnt, size_t nargs, size_t& idx) } } +LLValue* DtoAggrPairSwap(LLValue* aggr); + void AsmBlockStatement::toIR(IRState* p) { Logger::println("AsmBlockStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; Logger::println("BEGIN ASM"); - // disable inlining - gIR->func()->setNeverInline(); + // disable inlining by default + if (!p->func()->decl->allowInlining) + p->func()->setNeverInline(); // create asm block structure assert(!p->asmBlock); - IRAsmBlock* asmblock = new IRAsmBlock; + IRAsmBlock* asmblock = new IRAsmBlock(this); assert(asmblock); p->asmBlock = asmblock; @@ -562,6 +568,20 @@ void AsmBlockStatement::toIR(IRState* p) } + // build a fall-off-end-properly asm statement + + FuncDeclaration* thisfunc = p->func()->decl; + bool useabiret = false; + p->asmBlock->asmBlock->abiret = NULL; + if (thisfunc->fbody->endsWithAsm() == this && thisfunc->type->nextOf()->ty != Tvoid) + { + // there can't be goto forwarders in this case + assert(gotoToVal.empty()); + emitABIReturnAsmStmt(asmblock, loc, thisfunc); + useabiret = true; + } + + // build asm block std::vector outargs; std::vector inargs; @@ -571,8 +591,9 @@ void AsmBlockStatement::toIR(IRState* p) std::string in_c; std::string clobbers; std::string code; - size_t asmIdx = 0; + size_t asmIdx = asmblock->retn; + Logger::println("do outputs"); size_t n = asmblock->s.size(); for (size_t i=0; icode, onn+a->in.size(), asmIdx); } + + Logger::println("do inputs"); for (size_t i=0; is[i]; @@ -628,10 +651,18 @@ void AsmBlockStatement::toIR(IRState* p) Logger::println("code = \"%s\"", code.c_str()); Logger::println("constraints = \"%s\"", out_c.c_str()); + // build return types + const LLType* retty; + if (asmblock->retn) + retty = asmblock->retty; + else + retty = llvm::Type::VoidTy; + + // build argument types std::vector types; types.insert(types.end(), outtypes.begin(), outtypes.end()); types.insert(types.end(), intypes.begin(), intypes.end()); - llvm::FunctionType* fty = llvm::FunctionType::get(llvm::Type::VoidTy, types, false); + llvm::FunctionType* fty = llvm::FunctionType::get(retty, types, false); if (Logger::enabled()) Logger::cout() << "function type = " << *fty << '\n'; llvm::InlineAsm* ia = llvm::InlineAsm::get(fty, code, out_c, true); @@ -640,6 +671,10 @@ void AsmBlockStatement::toIR(IRState* p) args.insert(args.end(), outargs.begin(), outargs.end()); args.insert(args.end(), inargs.begin(), inargs.end()); llvm::CallInst* call = p->ir->CreateCall(ia, args.begin(), args.end(), ""); + if (useabiret) + { + p->asmBlock->asmBlock->abiret = call; + } p->asmBlock = NULL; Logger::println("END ASM"); diff --git a/gen/classes.cpp b/gen/classes.cpp index c37f7ca6..9cfa8ac6 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -973,8 +973,8 @@ void DtoInitClass(TypeClass* tc, LLValue* dst) { DtoForceConstInitDsymbol(tc->sym); - size_t presz = 2*getABITypeSize(DtoSize_t()); - uint64_t n = getABITypeSize(tc->ir.type->get()) - presz; + size_t presz = 2*getTypePaddedSize(DtoSize_t()); + uint64_t n = getTypePaddedSize(tc->ir.type->get()) - presz; // set vtable field seperately, this might give better optimization assert(tc->sym->ir.irStruct->vtbl); @@ -1494,7 +1494,7 @@ void DtoDefineClassInfo(ClassDeclaration* cd) { c = DtoBitCast(ir->init, voidPtr); //Logger::cout() << *ir->constInit->getType() << std::endl; - size_t initsz = getABITypeSize(ir->init->getType()->getContainedType(0)); + size_t initsz = getTypePaddedSize(ir->init->getType()->getContainedType(0)); c = DtoConstSlice(DtoConstSize_t(initsz), c); } inits.push_back(c); diff --git a/gen/functions.cpp b/gen/functions.cpp index aa94dd44..4e2f6e1f 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -907,6 +907,10 @@ void DtoDefineFunction(FuncDeclaration* fd) // output function body fd->fbody->toIR(gIR); + // TODO: clean up this mess + +// std::cout << *func << std::endl; + // llvm requires all basic blocks to end with a TerminatorInst but DMD does not put a return statement // in automatically, so we do it here. if (!gIR->scopereturned()) { @@ -917,12 +921,23 @@ void DtoDefineFunction(FuncDeclaration* fd) } else { if (!fd->isMain()) - llvm::ReturnInst::Create(llvm::UndefValue::get(func->getReturnType()), gIR->scopebb()); + { + AsmBlockStatement* asmb = fd->fbody->endsWithAsm(); + if (asmb) { + assert(asmb->abiret); + llvm::ReturnInst::Create(asmb->abiret, gIR->scopebb()); + } + else { + llvm::ReturnInst::Create(llvm::UndefValue::get(func->getReturnType()), gIR->scopebb()); + } + } else llvm::ReturnInst::Create(llvm::Constant::getNullValue(func->getReturnType()), gIR->scopebb()); } } +// std::cout << *func << std::endl; + // erase alloca point allocaPoint->eraseFromParent(); allocaPoint = 0; @@ -934,28 +949,9 @@ void DtoDefineFunction(FuncDeclaration* fd) assert(!func->getBasicBlockList().empty()); func->getBasicBlockList().pop_back(); - // if the last block is empty now, it must be unreachable or it's a bug somewhere else - // would be nice to figure out how to assert that this is correct - llvm::BasicBlock* lastbb = &func->getBasicBlockList().back(); - if (lastbb->empty()) - { - new llvm::UnreachableInst(lastbb); - } - - // if the last block is not terminated we return a null value or void - // for some unknown reason this is needed when a void main() has a inline asm block ... - // this should be harmless for well formed code! - lastbb = &func->getBasicBlockList().back(); - if (!lastbb->getTerminator()) - { - Logger::println("adding missing return statement"); - if (func->getReturnType() == LLType::VoidTy) - llvm::ReturnInst::Create(lastbb); - else - llvm::ReturnInst::Create(llvm::Constant::getNullValue(func->getReturnType()), lastbb); - } - gIR->functions.pop_back(); + +// std::cout << *func << std::endl; } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gen/functions.h b/gen/functions.h index 1d31c6f4..7c433b96 100644 --- a/gen/functions.h +++ b/gen/functions.h @@ -9,7 +9,9 @@ const llvm::FunctionType* DtoBaseFunctionType(FuncDeclaration* fdecl); void DtoResolveFunction(FuncDeclaration* fdecl); void DtoDeclareFunction(FuncDeclaration* fdecl); void DtoDefineFunction(FuncDeclaration* fd); + void DtoDefineNakedFunction(FuncDeclaration* fd); +void emitABIReturnAsmStmt(IRAsmBlock* asmblock, Loc loc, FuncDeclaration* fdecl); DValue* DtoArgument(Argument* fnarg, Expression* argexp); void DtoVariadicArgument(Expression* argexp, LLValue* dst); diff --git a/gen/irstate.h b/gen/irstate.h index a8b378dd..84af9056 100644 --- a/gen/irstate.h +++ b/gen/irstate.h @@ -2,6 +2,7 @@ #define LDC_GEN_IRSTATE_H #include +#include #include #include @@ -78,11 +79,17 @@ struct IRAsmStmt struct IRAsmBlock { - std::vector s; + std::deque s; std::set clobs; // stores the labels within the asm block std::vector internalLabels; + + AsmBlockStatement* asmBlock; + const LLType* retty; + unsigned retn; + + IRAsmBlock(AsmBlockStatement* b) : asmBlock(b), retty(NULL), retn(0) {} }; // llvm::CallInst and llvm::InvokeInst don't share a common base diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index fe95aaaa..b77e0948 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -1486,7 +1486,6 @@ LLConstant* DtoConstExpInit(Loc loc, Type* type, Expression* exp) return exp->toConstElem(gIR); } - ////////////////////////////////////////////////////////////////////////////////////////// void DtoAnnotation(const char* str) diff --git a/gen/naked.cpp b/gen/naked.cpp index fb75c0e5..ffb967b7 100644 --- a/gen/naked.cpp +++ b/gen/naked.cpp @@ -9,6 +9,7 @@ #include "gen/logger.h" #include "gen/irstate.h" #include "gen/llvmhelpers.h" +#include "gen/tollvm.h" ////////////////////////////////////////////////////////////////////////////////////////// @@ -164,3 +165,63 @@ void DtoDefineNakedFunction(FuncDeclaration* fd) gIR->functions.pop_back(); } + +////////////////////////////////////////////////////////////////////////////////////////// + +void emitABIReturnAsmStmt(IRAsmBlock* asmblock, Loc loc, FuncDeclaration* fdecl) +{ + Logger::println("emitABIReturnAsmStmt(%s)", fdecl->mangle()); + LOG_SCOPE; + + IRAsmStmt* as = new IRAsmStmt; + + const LLType* llretTy = DtoType(fdecl->type->nextOf()); + asmblock->retty = llretTy; + asmblock->retn = 1; + + // x86 + if (global.params.cpu == ARCHx86) + { + LINK l = fdecl->linkage; + assert((l == LINKd || l == LINKc || l == LINKwindows) && "invalid linkage for asm implicit return"); + + Type* rt = fdecl->type->nextOf()->toBasetype(); + if (rt->isintegral() || rt->ty == Tpointer || rt->ty == Tclass || rt->ty == Taarray) + { + if (rt->size() == 8) { + as->out_c = "=A,"; + } else { + as->out_c = "={ax},"; + } + } + else if (rt->isfloating()) + { + if (rt->iscomplex()) { + as->out_c = "={st},={st(1)},"; + asmblock->retn = 2; + } else { + as->out_c = "={st},"; + } + } + else if (rt->ty == Tarray || rt->ty == Tdelegate) + { + as->out_c = "={ax},={dx},"; + asmblock->retn = 2; + } + else + { + error(loc, "unimplemented return type '%s' for implicit abi return", rt->toChars()); + fatal(); + } + } + + // unsupported + else + { + error(loc, "this target (%s) does not implement inline asm falling off the end of the function", global.params.targetTriple); + fatal(); + } + + // return values always go in the front + asmblock->s.push_front(as); +} diff --git a/gen/statements.cpp b/gen/statements.cpp index 7d83a283..eb1676f1 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -81,6 +81,12 @@ void ReturnStatement::toIR(IRState* p) LLValue* v = e->getRVal(); delete e; + // swap real/imag parts on a x87 + if (global.params.cpu == ARCHx86 && exp->type->toBasetype()->iscomplex()) + { + v = DtoAggrPairSwap(v); + } + if (Logger::enabled()) Logger::cout() << "return value is '" <<*v << "'\n"; @@ -1198,6 +1204,9 @@ void LabelStatement::toIR(IRState* p) a->code += ":"; p->asmBlock->s.push_back(a); p->asmBlock->internalLabels.push_back(ident); + + // disable inlining + gIR->func()->setNeverInline(); } else { @@ -1453,3 +1462,31 @@ STUBST(Statement); #if DMDV2 STUBST(PragmaStatement); #endif + +////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////// + +AsmBlockStatement* Statement::endsWithAsm() +{ + // does not end with inline asm + return NULL; +} + +AsmBlockStatement* CompoundStatement::endsWithAsm() +{ + // make the last inner statement decide + if (statements && statements->dim) + { + unsigned last = statements->dim - 1; + Statement* s = (Statement*)statements->data[last]; + if (s) return s->endsWithAsm(); + } + return NULL; +} + +AsmBlockStatement* AsmBlockStatement::endsWithAsm() +{ + // yes this is inline asm + return this; +} diff --git a/gen/structs.cpp b/gen/structs.cpp index fbebcbcd..a5e1da47 100644 --- a/gen/structs.cpp +++ b/gen/structs.cpp @@ -239,7 +239,7 @@ Lpadding: } // there might still be padding after the last one, make sure that is defaulted/zeroed as well - size_t structsize = getABITypeSize(structtype); + size_t structsize = getTypePaddedSize(structtype); // if there is space before the next explicit initializer // FIXME: this should be handled in the loop above as well @@ -292,7 +292,7 @@ Lpadding2: { Logger::cout() << "constant struct initializer: " << *c << '\n'; } - assert(getABITypeSize(c->getType()) == structsize); + assert(getTypePaddedSize(c->getType()) == structsize); return c; } @@ -406,7 +406,7 @@ std::vector DtoStructLiteralValues(const StructDeclaration* sd, co // fill out rest with default initializers const LLType* structtype = DtoType(sd->type); - size_t structsize = getABITypeSize(structtype); + size_t structsize = getTypePaddedSize(structtype); // FIXME: this could probably share some code with the above if (structsize > lastoffset+lastsize) @@ -558,7 +558,7 @@ void DtoResolveStruct(StructDeclaration* sd) printf(" index: %u offset: %u\n", v->ir.irField->index, v->ir.irField->unionOffset); } - unsigned llvmSize = (unsigned)getABITypeSize(ST); + unsigned llvmSize = (unsigned)getTypePaddedSize(ST); unsigned dmdSize = (unsigned)sd->type->size(); printf(" llvm size: %u dmd size: %u\n", llvmSize, dmdSize); assert(llvmSize == dmdSize); @@ -685,7 +685,7 @@ LLValue* DtoStructEquals(TOK op, DValue* lhs, DValue* rhs) cmpop = llvm::ICmpInst::ICMP_NE; // call memcmp - size_t sz = getABITypeSize(DtoType(t)); + size_t sz = getTypePaddedSize(DtoType(t)); LLValue* val = DtoMemCmp(lhs->getRVal(), rhs->getRVal(), DtoConstSize_t(sz)); return gIR->ir->CreateICmp(cmpop, val, LLConstantInt::get(val->getType(), 0, false), "tmp"); } diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 6a191d0b..004d5e21 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -136,7 +136,7 @@ void DtoBuildDVarArgList(std::vector& args, std::vectordata[i]; vtypes.push_back(DtoType(argexp->type)); - size_t sz = getABITypeSize(vtypes.back()); + size_t sz = getTypePaddedSize(vtypes.back()); if (sz < PTRSIZE) vtypes.back() = DtoSize_t(); } @@ -463,6 +463,12 @@ DValue* DtoCallFunction(Loc& loc, Type* resulttype, DValue* fnval, Expressions* // get return value LLValue* retllval = (retinptr) ? args[0] : call->get(); + // swap real/imag parts on a x87 + if (global.params.cpu == ARCHx86 && tf->nextOf()->toBasetype()->iscomplex()) + { + retllval = DtoAggrPairSwap(retllval); + } + // repaint the type if necessary if (resulttype) { diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index ddf0fd31..73b61e28 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -419,11 +419,9 @@ void DtoMemSetZero(LLValue* dst, LLValue* nbytes) { dst = DtoBitCast(dst,getVoidPtrType()); - llvm::Function* fn; - if (global.params.is64bit) - fn = GET_INTRINSIC_DECL(memset_i64); - else - fn = GET_INTRINSIC_DECL(memset_i32); + const LLType* intTy = DtoSize_t(); + llvm::Function* fn = llvm::Intrinsic::getDeclaration(gIR->module, + llvm::Intrinsic::memset, &intTy, 1); gIR->ir->CreateCall4(fn, dst, DtoConstUbyte(0), nbytes, DtoConstUint(0), ""); } @@ -435,11 +433,9 @@ void DtoMemCpy(LLValue* dst, LLValue* src, LLValue* nbytes) dst = DtoBitCast(dst,getVoidPtrType()); src = DtoBitCast(src,getVoidPtrType()); - llvm::Function* fn; - if (global.params.is64bit) - fn = GET_INTRINSIC_DECL(memcpy_i64); - else - fn = GET_INTRINSIC_DECL(memcpy_i32); + const LLType* intTy = DtoSize_t(); + llvm::Function* fn = llvm::Intrinsic::getDeclaration(gIR->module, + llvm::Intrinsic::memcpy, &intTy, 1); gIR->ir->CreateCall4(fn, dst, src, nbytes, DtoConstUint(0), ""); } @@ -700,9 +696,9 @@ size_t getTypeStoreSize(const LLType* t) return gTargetData->getTypeStoreSize(t); } -size_t getABITypeSize(const LLType* t) +size_t getTypePaddedSize(const LLType* t) { - size_t sz = gTargetData->getABITypeSize(t); + size_t sz = gTargetData->getTypePaddedSize(t); //Logger::cout() << "abi type size of: " << *t << " == " << sz << '\n'; return sz; } @@ -728,7 +724,7 @@ const LLType* getBiggestType(const LLType** begin, size_t n) { const LLType* T = *begin; - size_t sz = getABITypeSize(T); + size_t sz = getTypePaddedSize(T); size_t ali = getABITypeAlign(T); if (sz > bigSize || (sz == bigSize && ali > bigAlign)) { @@ -881,3 +877,11 @@ LLValue* DtoAggrPaint(LLValue* aggr, const LLType* as) V = DtoBitCast(V, as->getContainedType(1)); return gIR->ir->CreateInsertValue(res, V, 1, "tmp"); } + +LLValue* DtoAggrPairSwap(LLValue* aggr) +{ + Logger::println("swapping aggr pair"); + LLValue* r = gIR->ir->CreateExtractValue(aggr, 0); + LLValue* i = gIR->ir->CreateExtractValue(aggr, 1); + return DtoAggrPair(i, r, "swapped"); +} diff --git a/gen/tollvm.h b/gen/tollvm.h index ad407b86..8da71a39 100644 --- a/gen/tollvm.h +++ b/gen/tollvm.h @@ -92,7 +92,7 @@ LLConstant* getNullValue(const LLType* t); // type sizes size_t getTypeBitSize(const LLType* t); size_t getTypeStoreSize(const LLType* t); -size_t getABITypeSize(const LLType* t); +size_t getTypePaddedSize(const LLType* t); // type alignments unsigned char getABITypeAlign(const LLType* t); @@ -105,6 +105,7 @@ const LLType* getBiggestType(const LLType** begin, size_t n); LLValue* DtoAggrPair(const LLType* type, LLValue* V1, LLValue* V2, const char* name = 0); LLValue* DtoAggrPair(LLValue* V1, LLValue* V2, const char* name = 0); LLValue* DtoAggrPaint(LLValue* aggr, const LLType* as); +LLValue* DtoAggrPairSwap(LLValue* aggr); /** * Generates a call to llvm.memset.i32 (or i64 depending on architecture). diff --git a/gen/toobj.cpp b/gen/toobj.cpp index c6bedfb0..5a3e6e6b 100644 --- a/gen/toobj.cpp +++ b/gen/toobj.cpp @@ -255,7 +255,7 @@ void Module::genobjfile(int multiobj) Logger::println("Writing native asm to: %s\n", spath.c_str()); std::string err; { - llvm::raw_fd_ostream out(spath.c_str(), err); + llvm::raw_fd_ostream out(spath.c_str(), false, err); write_asm_to_file(Target, *ir.module, out); } diff --git a/ir/irstruct.cpp b/ir/irstruct.cpp index e87ee190..709e485d 100644 --- a/ir/irstruct.cpp +++ b/ir/irstruct.cpp @@ -328,7 +328,7 @@ void IrStruct::buildDefaultConstInit(std::vector& inits) // there might still be padding after the last one, make sure that is zeroed as well // is there space in between last last offset and this one? - size_t structsize = getABITypeSize(structtype); + size_t structsize = getTypePaddedSize(structtype); if (structsize > lastoffset+lastsize) { diff --git a/tests/mini/asm3.d b/tests/mini/asm3.d index 517b5e05..c5e8c6d4 100644 --- a/tests/mini/asm3.d +++ b/tests/mini/asm3.d @@ -8,21 +8,20 @@ void main() printf(fmt); version (LLVM_InlineAsm_X86) { - asm - { - push fmt; - call printf; - pop EAX; - } + asm + { + push fmt; + call printf; + pop EAX; + } } else version(LLVM_InlineAsm_X86_64) { asm { - movq RDI, fmt; - xor AL, AL; - call printf; + movq RDI, fmt; + xor AL, AL; + call printf; } } - } diff --git a/tests/mini/asm5.d b/tests/mini/asm5.d new file mode 100644 index 00000000..12b1c06f --- /dev/null +++ b/tests/mini/asm5.d @@ -0,0 +1,23 @@ +int foo() +{ + version(X86) + asm { mov EAX, 42; } + else static assert(0, "todo"); +} + +ulong bar() +{ + version(X86) + asm { mov EAX, 0xFF; mov EDX, 0xAA; } + else static assert(0, "todo"); +} + +void main() +{ + long l = 1; + l = 2; + l = 4; + l = 8; + assert(foo() == 42); + assert(bar() == 0x000000AA000000FF); +} diff --git a/tests/mini/asm8.d b/tests/mini/asm8.d new file mode 100644 index 00000000..f813f288 --- /dev/null +++ b/tests/mini/asm8.d @@ -0,0 +1,119 @@ +int foo() +{ + version(X86) + asm { mov EAX, 42; } + else static assert(0, "todo"); +} + +ulong bar() +{ + version(X86) + asm { mov EDX, 0xAA; mov EAX, 0xFF; } + else static assert(0, "todo"); +} + +float onef() +{ + version(X86) + asm { fld1; } + else static assert(0, "todo"); +} + +double oned() +{ + version(X86) + asm { fld1; } + else static assert(0, "todo"); +} + +real oner() +{ + version(X86) + asm { fld1; } + else static assert(0, "todo"); +} + +real two = 2.0; + +creal cr() +{ + version(X86) + asm { fld1; fld two; } + else static assert(0, "todo"); +} + +creal cr2() +{ + version(X86) + asm + { + naked; + fld1; + fld two; + ret; + } + else static assert(0, "todo"); +} + +void* vp() +{ + version(X86) + asm { mov EAX, 0x80; } + else static assert(0, "todo"); +} + +int[int] gaa; + +int[int] aa() +{ + version(X86) + asm { mov EAX, gaa; } + else static assert(0, "todo"); +} + +Object gobj; + +Object ob() +{ + version(X86) + asm { mov EAX, gobj; } + else static assert(0, "todo"); +} + +char[] ghello = "hello world"; + +char[] str() +{ + version(X86) + asm { lea ECX, ghello; mov EAX, [ECX]; mov EDX, [ECX+4]; } + else static assert(0, "todo"); +} + +char[] delegate() dg() +{ + version(X86) + asm { mov EAX, gobj; lea EDX, Object.toString; } + else static assert(0, "todo"); +} + +void main() +{ + gaa[4] = 5; + gobj = new Object; + auto adg = &gobj.toString; + + assert(foo() == 42); + assert(bar() == 0x000000AA000000FF); + assert(onef() == 1); + assert(oned() == 1); + assert(oner() == 1); + assert(cr() == 1+2i); + assert(cr2() == 1+2i); + assert(vp() == cast(void*)0x80); + assert(aa() is gaa); + assert(ob() is gobj); + assert(str() == "hello world"); + assert(dg()() == "object.Object"); +} + +extern(C) int printf(char*, ...);