From dc5944df999efdd1eb5a469bb004ec4fa1b5a949 Mon Sep 17 00:00:00 2001 From: Tomas Lindquist Olsen Date: Tue, 3 Feb 2009 08:54:57 +0100 Subject: [PATCH] Implemented proper support for naked asm using llvm module level asm. Still not 100% complete, but already 1000 times better that what we had before. Don's BignumX86 implementation from Tango (when turned into a standalone unittest) seems to fully work with no changes, and great performance :) Fixed align N; in asm blocks. Fixed inreg parameter passing on x86 for ref/out params. Removed support for lazy initialization of function local static variables, I have no idea why I ever implemented this, it's not in the D spec, and DMD doesn't support it :P Some of the global variable related changes might cause minor regressions, but they should be easily fixable. --- dmd/declaration.c | 1 + dmd/declaration.h | 1 + dmd/mars.c | 2 +- dmd/statement.h | 13 +++ gen/asm-x86-32.h | 74 ++++++++++++--- gen/asm-x86-64.h | 74 ++++++++++++--- gen/asmstmt.cpp | 35 ++++++- gen/classes.cpp | 1 - gen/functions.cpp | 24 +++-- gen/functions.h | 3 +- gen/irstate.h | 5 +- gen/llvmhelpers.cpp | 65 +------------ gen/llvmhelpers.h | 3 - gen/naked.cpp | 140 ++++++++++++++++++++++++++++ gen/statements.cpp | 1 - gen/toir.cpp | 15 +-- gen/tollvm.cpp | 5 +- gen/toobj.cpp | 21 ++--- tests/mini/{asm5.d => naked_asm1.d} | 18 ++-- tests/mini/naked_asm2.d | 21 +++++ tests/mini/naked_asm3.d | 21 +++++ tests/mini/naked_asm4.d | 17 ++++ tests/mini/naked_asm5.d | 19 ++++ tests/mini/naked_asm6.d | 18 ++++ tests/mini/structinit4.d | 18 ++++ tests/mini/structinit5.d | 11 +++ tests/mini/templ1.d | 11 --- tests/mini/templ2.d | 7 -- 28 files changed, 491 insertions(+), 153 deletions(-) create mode 100644 gen/naked.cpp rename tests/mini/{asm5.d => naked_asm1.d} (62%) create mode 100644 tests/mini/naked_asm2.d create mode 100644 tests/mini/naked_asm3.d create mode 100644 tests/mini/naked_asm4.d create mode 100644 tests/mini/naked_asm5.d create mode 100644 tests/mini/naked_asm6.d create mode 100644 tests/mini/structinit4.d create mode 100644 tests/mini/structinit5.d delete mode 100644 tests/mini/templ1.d delete mode 100644 tests/mini/templ2.d diff --git a/dmd/declaration.c b/dmd/declaration.c index 59efda0e..6b72af8b 100644 --- a/dmd/declaration.c +++ b/dmd/declaration.c @@ -625,6 +625,7 @@ VarDeclaration::VarDeclaration(Loc loc, Type *type, Identifier *id, Initializer // LDC anonDecl = NULL; offset2 = 0; + nakedUse = false; } Dsymbol *VarDeclaration::syntaxCopy(Dsymbol *s) diff --git a/dmd/declaration.h b/dmd/declaration.h index b865d450..836c0719 100644 --- a/dmd/declaration.h +++ b/dmd/declaration.h @@ -274,6 +274,7 @@ struct VarDeclaration : Declaration // LDC AnonDeclaration* anonDecl; unsigned offset2; + bool nakedUse; }; /**************************************************************/ diff --git a/dmd/mars.c b/dmd/mars.c index 6f9da47d..1b352ef8 100644 --- a/dmd/mars.c +++ b/dmd/mars.c @@ -888,7 +888,7 @@ int main(int argc, char *argv[]) global.params.cpu = ARCHthumb; } else { - assert(0 && "Invalid arch"); + error("invalid cpu architecture specified: %s", global.params.llvmArch); } assert(global.params.cpu != ARCHinvalid); diff --git a/dmd/statement.h b/dmd/statement.h index c33a37a7..607d4761 100644 --- a/dmd/statement.h +++ b/dmd/statement.h @@ -161,6 +161,9 @@ 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; } @@ -185,6 +188,9 @@ struct ExpStatement : Statement Statement *inlineScan(InlineScanState *iss); void toIR(IRState *irs); + + // LDC + void toNakedIR(IRState *irs); }; struct CompileStatement : Statement @@ -234,6 +240,9 @@ struct CompoundStatement : Statement virtual void toIR(IRState *irs); + // LDC + virtual void toNakedIR(IRState *irs); + virtual CompoundStatement *isCompoundStatement() { return this; } }; @@ -844,6 +853,7 @@ struct LabelStatement : Statement // LDC bool asmLabel; // for labels inside inline assembler + void toNakedIR(IRState *irs); }; struct LabelDsymbol : Dsymbol @@ -876,6 +886,8 @@ struct AsmStatement : Statement // LDC // non-zero if this is a branch, contains the target labels identifier Identifier* isBranchToLabel; + + void toNakedIR(IRState *irs); }; struct AsmBlockStatement : CompoundStatement @@ -892,6 +904,7 @@ struct AsmBlockStatement : CompoundStatement AsmBlockStatement *isAsmBlockStatement() { return this; } void toIR(IRState *irs); + void toNakedIR(IRState *irs); }; #endif /* DMD_STATEMENT_H */ diff --git a/gen/asm-x86-32.h b/gen/asm-x86-32.h index c1f929e0..ecfd380b 100644 --- a/gen/asm-x86-32.h +++ b/gen/asm-x86-32.h @@ -1409,21 +1409,66 @@ struct AsmProcessor } void addOperand(const char * fmt, AsmArgType type, Expression * e, AsmCode * asmcode, AsmArgMode mode = Mode_Input) { - insnTemplate->writestring((char*) fmt); - insnTemplate->printf("<<%s%d>>", (mode==Mode_Input)?"in":"out", asmcode->args.dim); - asmcode->args.push( new AsmArg(type, e, mode) ); + if (sc->func->naked) + { + switch(type) + { + case Arg_Integer: + if (e->type->isunsigned()) + insnTemplate->printf("$%llu", e->toUInteger()); + else + insnTemplate->printf("$%lld", e->toInteger()); + break; + + case Arg_Pointer: + stmt->error("unsupported pointer reference to '%s' in naked asm", e->toChars()); + break; + + case Arg_Memory: + if (e->op == TOKvar) + { + VarExp* v = (VarExp*)e; + if (VarDeclaration* vd = v->var->isVarDeclaration()) + { + if (!vd->isDataseg()) + { + stmt->error("only global variables can be referenced by identifier in naked asm"); + break; + } + + // print out the mangle + insnTemplate->writestring(vd->mangle()); + vd->nakedUse = true; + break; + } + } + stmt->error("unsupported memory reference to '%s' in naked asm", e->toChars()); + break; + + default: + assert(0 && "asm unsupported arg"); + break; + } + } + else + { + insnTemplate->writestring((char*) fmt); + insnTemplate->printf("<<%s%d>>", (mode==Mode_Input)?"in":"out", asmcode->args.dim); + asmcode->args.push( new AsmArg(type, e, mode) ); + } } void addOperand2(const char * fmtpre, const char * fmtpost, AsmArgType type, Expression * e, AsmCode * asmcode, AsmArgMode mode = Mode_Input) { - insnTemplate->writestring((char*) fmtpre); - insnTemplate->printf("<<%s%d>>", (mode==Mode_Input)?"in":"out", asmcode->args.dim); - insnTemplate->writestring((char*) fmtpost); - asmcode->args.push( new AsmArg(type, e, mode) ); + assert(!sc->func->naked); + insnTemplate->writestring((char*) fmtpre); + insnTemplate->printf("<<%s%d>>", (mode==Mode_Input)?"in":"out", asmcode->args.dim); + insnTemplate->writestring((char*) fmtpost); + asmcode->args.push( new AsmArg(type, e, mode) ); } void addLabel(char* id) { - insnTemplate->writestring(sc->func->mangle()); - insnTemplate->writestring("_"); - insnTemplate->writestring(id); + insnTemplate->writestring(sc->func->mangle()); + insnTemplate->writestring("_"); + insnTemplate->writestring(id); } /* Determines whether the operand is a register, memory reference @@ -1916,9 +1961,12 @@ struct AsmProcessor insnTemplate->writebyte('*'); use_star = false; } + + if (!sc->func->naked) { // no addrexp in naked asm please :) Type* tt = e->type->pointerTo(); e = new AddrExp(0, e); e->type = tt; + } addOperand(fmt, Arg_Memory, e, asmcode, mode); } @@ -2515,9 +2563,9 @@ struct AsmProcessor // parse primary: DMD allows 'MyAlign' (const int) but not '2+2' // GAS is padding with NOPs last time I checked. Expression * e = parseAsmExp()->optimize(WANTvalue | WANTinterpret); - integer_t align = e->toInteger(); + uinteger_t align = e->toUInteger(); - if (align & align - 1 == 0) { + if ((align & (align - 1)) == 0) { //FIXME: This printf is not portable. The use of `align` varies from system to system; // on i386 using a.out, .align `n` will align on a 2^`n` boundary instead of an `n` boundary #ifdef HAVE_GAS_BALIGN_AND_P2ALIGN @@ -2526,7 +2574,7 @@ struct AsmProcessor insnTemplate->printf(".align\t%u", (unsigned) align); #endif } else { - stmt->error("alignment must be a power of 2"); + stmt->error("alignment must be a power of 2, not %u", (unsigned) align); } setAsmCode(); diff --git a/gen/asm-x86-64.h b/gen/asm-x86-64.h index 6626243a..a17a7352 100644 --- a/gen/asm-x86-64.h +++ b/gen/asm-x86-64.h @@ -1529,21 +1529,66 @@ struct AsmProcessor } void addOperand(const char * fmt, AsmArgType type, Expression * e, AsmCode * asmcode, AsmArgMode mode = Mode_Input) { - insnTemplate->writestring((char*) fmt); - insnTemplate->printf("<<%s%d>>", (mode==Mode_Input)?"in":"out", asmcode->args.dim); - asmcode->args.push( new AsmArg(type, e, mode) ); + if (sc->func->naked) + { + switch(type) + { + case Arg_Integer: + if (e->type->isunsigned()) + insnTemplate->printf("$%llu", e->toUInteger()); + else + insnTemplate->printf("$%lld", e->toInteger()); + break; + + case Arg_Pointer: + stmt->error("unsupported pointer reference to '%s' in naked asm", e->toChars()); + break; + + case Arg_Memory: + if (e->op == TOKvar) + { + VarExp* v = (VarExp*)e; + if (VarDeclaration* vd = v->var->isVarDeclaration()) + { + if (!vd->isDataseg()) + { + stmt->error("only global variables can be referenced by identifier in naked asm"); + break; + } + + // print out the mangle + insnTemplate->writestring(vd->mangle()); + vd->nakedUse = true; + break; + } + } + stmt->error("unsupported memory reference to '%s' in naked asm", e->toChars()); + break; + + default: + assert(0 && "asm unsupported arg"); + break; + } + } + else + { + insnTemplate->writestring((char*) fmt); + insnTemplate->printf("<<%s%d>>", (mode==Mode_Input)?"in":"out", asmcode->args.dim); + asmcode->args.push( new AsmArg(type, e, mode) ); + } } void addOperand2(const char * fmtpre, const char * fmtpost, AsmArgType type, Expression * e, AsmCode * asmcode, AsmArgMode mode = Mode_Input) { - insnTemplate->writestring((char*) fmtpre); - insnTemplate->printf("<<%s%d>>", (mode==Mode_Input)?"in":"out", asmcode->args.dim); - insnTemplate->writestring((char*) fmtpost); - asmcode->args.push( new AsmArg(type, e, mode) ); + assert(!sc->func->naked); + insnTemplate->writestring((char*) fmtpre); + insnTemplate->printf("<<%s%d>>", (mode==Mode_Input)?"in":"out", asmcode->args.dim); + insnTemplate->writestring((char*) fmtpost); + asmcode->args.push( new AsmArg(type, e, mode) ); } void addLabel(char* id) { - insnTemplate->writestring(sc->func->mangle()); - insnTemplate->writestring("_"); - insnTemplate->writestring(id); + insnTemplate->writestring(sc->func->mangle()); + insnTemplate->writestring("_"); + insnTemplate->writestring(id); } /* Determines whether the operand is a register, memory reference @@ -2037,9 +2082,12 @@ struct AsmProcessor insnTemplate->writebyte('*'); use_star = false; } + + if (!sc->func->naked) { // no addrexp in naked asm please :) Type* tt = e->type->pointerTo(); e = new AddrExp(0, e); e->type = tt; + } addOperand(fmt, Arg_Memory, e, asmcode, mode); } @@ -2636,9 +2684,9 @@ struct AsmProcessor // parse primary: DMD allows 'MyAlign' (const int) but not '2+2' // GAS is padding with NOPs last time I checked. Expression * e = parseAsmExp()->optimize(WANTvalue | WANTinterpret); - integer_t align = e->toInteger(); + uinteger_t align = e->toUInteger(); - if (align & align - 1 == 0) { + if ((align & (align - 1)) == 0) { //FIXME: This printf is not portable. The use of `align` varies from system to system; // on i386 using a.out, .align `n` will align on a 2^`n` boundary instead of an `n` boundary #ifdef HAVE_GAS_BALIGN_AND_P2ALIGN @@ -2647,7 +2695,7 @@ struct AsmProcessor insnTemplate->printf(".align\t%u", (unsigned) align); #endif } else { - stmt->error("alignment must be a power of 2"); + stmt->error("alignment must be a power of 2, not %u", (unsigned) align); } setAsmCode(); diff --git a/gen/asmstmt.cpp b/gen/asmstmt.cpp index 3635cd13..db94271a 100644 --- a/gen/asmstmt.cpp +++ b/gen/asmstmt.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include //#include "d-lang.h" @@ -157,6 +156,8 @@ Statement *AsmStatement::semantic(Scope *sc) if (err) fatal(); + //puts(toChars()); + sc->func->inlineAsm = 1; sc->func->inlineStatus = ILSno; // %% not sure // %% need to set DECL_UNINLINABLE too? @@ -699,3 +700,35 @@ Statement *AsmBlockStatement::semantic(Scope *sc) return CompoundStatement::semantic(sc); } + +////////////////////////////////////////////////////////////////////////////// + +void AsmStatement::toNakedIR(IRState *p) +{ + Logger::println("AsmStatement::toNakedIR(): %s", loc.toChars()); + LOG_SCOPE; + + // is there code? + if (!asmcode) + return; + AsmCode * code = (AsmCode *) asmcode; + + // build asm stmt + std::ostringstream& asmstr = p->nakedAsm; + asmstr << "\t"; + asmstr.write(code->insnTemplate, code->insnTemplateLen); + asmstr << std::endl; +} + +void AsmBlockStatement::toNakedIR(IRState *p) +{ + Logger::println("AsmBlockStatement::toNakedIR(): %s", loc.toChars()); + LOG_SCOPE; + + // do asm statements + for (unsigned i=0; idim; i++) + { + Statement* s = (Statement*)statements->data[i]; + if (s) s->toNakedIR(p); + } +} diff --git a/gen/classes.cpp b/gen/classes.cpp index 043bcd03..7885f96d 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -1,4 +1,3 @@ -#include #include "gen/llvm.h" #include "mtype.h" diff --git a/gen/functions.cpp b/gen/functions.cpp index 94270323..2ac24748 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -209,12 +209,16 @@ const llvm::FunctionType* DtoFunctionType(Type* type, const LLType* thistype, co { Type* t = arg->type->toBasetype(); - // 32bit ints, pointers, classes, static arrays and AAs + // 32bit ints, pointers, classes, static arrays, AAs, ref and out params // are candidate for being passed in EAX - if ((arg->storageClass & STCin) && - ((t->isscalar() && !t->isfloating()) || + if ( + (arg->storageClass & (STCref|STCout)) + || + ((arg->storageClass & STCin) && + ((t->isscalar() && !t->isfloating()) || t->ty == Tclass || t->ty == Tsarray || t->ty == Taarray) && - (t->size() <= PTRSIZE)) + (t->size() <= PTRSIZE)) + ) { arg->llvmAttrs |= llvm::Attribute::InReg; assert((f->thisAttrs & llvm::Attribute::InReg) == 0 && "can't have two inreg args!"); @@ -618,7 +622,7 @@ void DtoDeclareFunction(FuncDeclaration* fdecl) ////////////////////////////////////////////////////////////////////////////////////////// -void DtoDefineFunc(FuncDeclaration* fd) +void DtoDefineFunction(FuncDeclaration* fd) { if (fd->ir.defined) return; fd->ir.defined = true; @@ -628,6 +632,13 @@ void DtoDefineFunc(FuncDeclaration* fd) Logger::println("DtoDefineFunc(%s): %s", fd->toPrettyChars(), fd->loc.toChars()); LOG_SCOPE; + // if this function is naked, we take over right away! no standard processing! + if (fd->naked) + { + DtoDefineNakedFunction(fd); + return; + } + // debug info if (global.params.symdebug) { Module* mo = fd->getModule(); @@ -684,8 +695,7 @@ void DtoDefineFunc(FuncDeclaration* fd) // this hack makes sure the frame pointer elimination optimization is disabled. // this this eliminates a bunch of inline asm related issues. - // naked must always eliminate the framepointer however... - if (fd->inlineAsm && !fd->naked) + if (fd->inlineAsm) { // emit a call to llvm_eh_unwind_init LLFunction* hack = GET_INTRINSIC_DECL(eh_unwind_init); diff --git a/gen/functions.h b/gen/functions.h index 2e202889..1d31c6f4 100644 --- a/gen/functions.h +++ b/gen/functions.h @@ -8,7 +8,8 @@ const llvm::FunctionType* DtoBaseFunctionType(FuncDeclaration* fdecl); void DtoResolveFunction(FuncDeclaration* fdecl); void DtoDeclareFunction(FuncDeclaration* fdecl); -void DtoDefineFunc(FuncDeclaration* fd); +void DtoDefineFunction(FuncDeclaration* fd); +void DtoDefineNakedFunction(FuncDeclaration* fd); DValue* DtoArgument(Argument* fnarg, Expression* argexp); void DtoVariadicArgument(Expression* argexp, LLValue* dst); diff --git a/gen/irstate.h b/gen/irstate.h index c7896fd8..a8b378dd 100644 --- a/gen/irstate.h +++ b/gen/irstate.h @@ -3,6 +3,7 @@ #include #include +#include #include "root.h" #include "aggregate.h" @@ -200,10 +201,12 @@ struct IRState // for inline asm IRAsmBlock* asmBlock; + std::ostringstream nakedAsm; - // dwarf dbg stuff // 'used' array solely for keeping a reference to globals std::vector usedArray; + + // dwarf dbg stuff LLGlobalVariable* dwarfCUs; LLGlobalVariable* dwarfSPs; LLGlobalVariable* dwarfGVs; diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 99195f3a..82f565e1 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -836,36 +836,6 @@ bool DtoIsTemplateInstance(Dsymbol* s) return false; } -/****************************************************************************************/ -/*//////////////////////////////////////////////////////////////////////////////////////// -// LAZY STATIC INIT HELPER -////////////////////////////////////////////////////////////////////////////////////////*/ - -void DtoLazyStaticInit(bool istempl, LLValue* gvar, Initializer* init, Type* t) -{ - // create a flag to make sure initialization only happens once - llvm::GlobalValue::LinkageTypes gflaglink = istempl ? TEMPLATE_LINKAGE_TYPE : llvm::GlobalValue::InternalLinkage; - std::string gflagname(gvar->getName()); - gflagname.append("__initflag"); - llvm::GlobalVariable* gflag = new llvm::GlobalVariable(LLType::Int1Ty,false,gflaglink,DtoConstBool(false),gflagname,gIR->module); - - // check flag and do init if not already done - llvm::BasicBlock* oldend = gIR->scopeend(); - llvm::BasicBlock* initbb = llvm::BasicBlock::Create("ifnotinit",gIR->topfunc(),oldend); - llvm::BasicBlock* endinitbb = llvm::BasicBlock::Create("ifnotinitend",gIR->topfunc(),oldend); - LLValue* cond = gIR->ir->CreateICmpEQ(gIR->ir->CreateLoad(gflag,"tmp"),DtoConstBool(false)); - gIR->ir->CreateCondBr(cond, initbb, endinitbb); - gIR->scope() = IRScope(initbb,endinitbb); - DValue* ie = DtoInitializer(gvar, init); - - DVarValue dst(t, gvar); - DtoAssign(init->loc, &dst, ie); - - gIR->ir->CreateStore(DtoConstBool(true), gflag); - gIR->ir->CreateBr(endinitbb); - gIR->scope() = IRScope(endinitbb,oldend); -} - /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // PROCESSING QUEUE HELPERS @@ -946,7 +916,7 @@ void DtoDefineDsymbol(Dsymbol* dsym) DtoDefineClass(cd); } else if (FuncDeclaration* fd = dsym->isFuncDeclaration()) { - DtoDefineFunc(fd); + DtoDefineFunction(fd); } else if (TypeInfoDeclaration* fd = dsym->isTypeInfoDeclaration()) { DtoDefineTypeInfo(fd); @@ -967,36 +937,10 @@ void DtoConstInitGlobal(VarDeclaration* vd) Logger::println("DtoConstInitGlobal(%s) @ %s", vd->toChars(), vd->locToChars()); LOG_SCOPE; - // if the variable is a function local static variable with a runtime initializer - // we must do lazy initialization, which involves a boolean flag to make sure it happens only once - // FIXME: I don't think it's thread safe ... - - bool doLazyInit = false; Dsymbol* par = vd->toParent(); - if (par && par->isFuncDeclaration() && vd->init) - { - if (ExpInitializer* einit = vd->init->isExpInitializer()) - { - if (!einit->exp->isConst()) - { - // mark as needing lazy now - doLazyInit = true; - } - } - } - - // if we do lazy init, we start out with an undefined initializer - LLConstant* initVal; - if (doLazyInit) - { - initVal = llvm::UndefValue::get(DtoType(vd->type)); - } - // otherwise we build it - else - { - initVal = DtoConstInitializer(vd->loc, vd->type, vd->init); - } + // build the initializer + LLConstant* initVal = DtoConstInitializer(vd->loc, vd->type, vd->init); // set the initializer if appropriate IrGlobal* glob = vd->ir.irGlobal; @@ -1035,9 +979,6 @@ void DtoConstInitGlobal(VarDeclaration* vd) gIR->usedArray.push_back(llvm::ConstantExpr::getBitCast(gv, getVoidPtrType())); } } - - if (doLazyInit) - DtoLazyStaticInit(istempl, gvar, vd->init, vd->type); } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gen/llvmhelpers.h b/gen/llvmhelpers.h index 83e087ef..6764f7a9 100644 --- a/gen/llvmhelpers.h +++ b/gen/llvmhelpers.h @@ -64,9 +64,6 @@ DValue* DtoPaintType(Loc& loc, DValue* val, Type* to); // is template instance check bool DtoIsTemplateInstance(Dsymbol* s); -// generates lazy static initialization code for a global variable -void DtoLazyStaticInit(bool istempl, LLValue* gvar, Initializer* init, Type* t); - // these are all basically drivers for the codegeneration called by the main loop void DtoResolveDsymbol(Dsymbol* dsym); void DtoDeclareDsymbol(Dsymbol* dsym); diff --git a/gen/naked.cpp b/gen/naked.cpp new file mode 100644 index 00000000..b1b4c2b4 --- /dev/null +++ b/gen/naked.cpp @@ -0,0 +1,140 @@ +#include "gen/llvm.h" + +#include "expression.h" +#include "statement.h" +#include "declaration.h" + +#include + +#include "gen/logger.h" +#include "gen/irstate.h" +#include "gen/llvmhelpers.h" + +////////////////////////////////////////////////////////////////////////////////////////// + +void Statement::toNakedIR(IRState *p) +{ + error("not allowed in naked function"); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +void CompoundStatement::toNakedIR(IRState *p) +{ + Logger::println("CompoundStatement::toNakedIR(): %s", loc.toChars()); + LOG_SCOPE; + + if (statements) + for (unsigned i = 0; i < statements->dim; i++) + { + Statement* s = (Statement*)statements->data[i]; + if (s) s->toNakedIR(p); + } +} + +////////////////////////////////////////////////////////////////////////////////////////// + +void ExpStatement::toNakedIR(IRState *p) +{ + Logger::println("ExpStatement::toNakedIR(): %s", loc.toChars()); + LOG_SCOPE; + + // only expstmt supported in declarations + if (exp->op != TOKdeclaration) + { + Statement::toNakedIR(p); + return; + } + + DeclarationExp* d = (DeclarationExp*)exp; + VarDeclaration* vd = d->declaration->isVarDeclaration(); + FuncDeclaration* fd = d->declaration->isFuncDeclaration(); + EnumDeclaration* ed = d->declaration->isEnumDeclaration(); + + // and only static variable/function declaration + // no locals or nested stuffies! + if (!vd && !fd && !ed) + { + Statement::toNakedIR(p); + return; + } + else if (vd && !vd->isDataseg()) + { + error("non-static variable '%s' not allowed in naked function", vd->toChars()); + return; + } + else if (fd && !fd->isStatic()) + { + error("non-static nested function '%s' not allowed in naked function", fd->toChars()); + return; + } + // enum decls should always be safe + + // make sure the symbols gets processed + d->declaration->toObjFile(0); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +void LabelStatement::toNakedIR(IRState *p) +{ + Logger::println("LabelStatement::toNakedIR(): %s", loc.toChars()); + LOG_SCOPE; + + p->nakedAsm << p->func()->decl->mangle() << "_" << ident->toChars() << ":"; + + if (statement) + statement->toNakedIR(p); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +void DtoDefineNakedFunction(FuncDeclaration* fd) +{ + Logger::println("DtoDefineNakedFunction(%s)", fd->mangle()); + LOG_SCOPE; + + assert(fd->ir.irFunc); + gIR->functions.push_back(fd->ir.irFunc); + + // we need to do special processing on the body, since we only want + // to allow actual inline asm blocks to reach the final asm output + + std::ostringstream& asmstr = gIR->nakedAsm; + + // build function header + + // REALLY FIXME: this is most likely extremely platform dependent + + const char* mangle = fd->mangle(); + const char* linkage = "globl"; + std::string section = "text"; + unsigned align = 16; + + std::ostringstream tmpstr; + + if (DtoIsTemplateInstance(fd)) + { + linkage = "weak"; + tmpstr << "section\t.gnu.linkonce.t." << mangle << ",\"ax\",@progbits"; + section = tmpstr.str(); + } + + asmstr << "\t." << section << std::endl; + asmstr << "\t.align\t" << align << std::endl; + asmstr << "\t." << linkage << "\t" << mangle << std::endl; + asmstr << "\t.type\t" << mangle << ",@function" << std::endl; + asmstr << mangle << ":" << std::endl; + + // emit body + fd->fbody->toNakedIR(gIR); + + // emit size after body + // why? dunno, llvm seems to do it by default .. + asmstr << "\t.size\t" << mangle << ", .-" << mangle << std::endl << std::endl; + + gIR->module->appendModuleInlineAsm(asmstr.str()); + asmstr.str(""); + + gIR->functions.pop_back(); +} diff --git a/gen/statements.cpp b/gen/statements.cpp index dc106d64..7d83a283 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include diff --git a/gen/toir.cpp b/gen/toir.cpp index 197e1f79..b3b10392 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include @@ -205,6 +204,7 @@ LLConstant* VarExp::toConstElem(IRState* p) { Logger::print("VarExp::toConstElem: %s | %s\n", toChars(), type->toChars()); LOG_SCOPE; + if (StaticStructInitDeclaration* sdecl = var->isStaticStructInitDeclaration()) { // this seems to be the static initialiser for structs @@ -216,7 +216,8 @@ LLConstant* VarExp::toConstElem(IRState* p) assert(ts->sym->ir.irStruct->constInit); return ts->sym->ir.irStruct->constInit; } - else if (TypeInfoDeclaration* ti = var->isTypeInfoDeclaration()) + + if (TypeInfoDeclaration* ti = var->isTypeInfoDeclaration()) { const LLType* vartype = DtoType(type); LLConstant* m = DtoTypeInfoOf(ti->tinfo, false); @@ -224,15 +225,17 @@ LLConstant* VarExp::toConstElem(IRState* p) m = llvm::ConstantExpr::getBitCast(m, vartype); return m; } - else if (VarDeclaration* vd = var->isVarDeclaration()) + + VarDeclaration* vd = var->isVarDeclaration(); + if (vd && vd->isConst() && vd->init) { // return the initializer - assert(vd->init); return DtoConstInitializer(loc, type, vd->init); } + // fail - assert(0 && "Unsupported const VarExp kind"); - return NULL; + error("non-constant expression %s", toChars()); + return llvm::UndefValue::get(DtoType(type)); } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index d686e0f1..5f5743d6 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -294,8 +294,9 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym) if (fdecl->llvmInternal == LLVMintrinsic) return llvm::GlobalValue::ExternalLinkage; // template instances should have weak linkage - // but only if there's a body, otherwise we make it external - else if (DtoIsTemplateInstance(fdecl) && fdecl->fbody) + // but only if there's a body, and it's not naked + // otherwise we make it external + else if (DtoIsTemplateInstance(fdecl) && fdecl->fbody && !fdecl->naked) return TEMPLATE_LINKAGE_TYPE; // extern(C) functions are always external else if (ft->linkage == LINKc) diff --git a/gen/toobj.cpp b/gen/toobj.cpp index 7e590191..64472570 100644 --- a/gen/toobj.cpp +++ b/gen/toobj.cpp @@ -914,22 +914,13 @@ void VarDeclaration::toObjFile(int multiobj) Logger::println("parent: %s (%s)", parent->toChars(), parent->kind()); - // handle static local variables - bool static_local = false; #if DMDV2 // not sure why this is only needed for d2 bool _isconst = isConst() && init; #else bool _isconst = isConst(); #endif - Dsymbol* par = toParent2(); - if (par && par->isFuncDeclaration()) - { - static_local = true; - if (init && init->isExpInitializer()) { - _isconst = false; - } - } + Logger::println("Creating global variable"); @@ -943,10 +934,12 @@ void VarDeclaration::toObjFile(int multiobj) if (Logger::enabled()) Logger::cout() << *gvar << '\n'; - if (static_local) - DtoConstInitGlobal(this); - else - gIR->constInitList.push_back(this); + // if this global is used from a nested function, this is necessary or + // optimization could potentially remove the global (if it's the only use) + if (nakedUse) + gIR->usedArray.push_back(DtoBitCast(gvar, getVoidPtrType())); + + gIR->constInitList.push_back(this); } else { diff --git a/tests/mini/asm5.d b/tests/mini/naked_asm1.d similarity index 62% rename from tests/mini/asm5.d rename to tests/mini/naked_asm1.d index 4a6d6913..2e8d20b0 100644 --- a/tests/mini/asm5.d +++ b/tests/mini/naked_asm1.d @@ -1,5 +1,3 @@ -module tangotests.asm5; - extern(C) int printf(char*, ...); void main() @@ -13,18 +11,20 @@ int func() { version (LLVM_InlineAsm_X86) { - asm - { - naked; - mov EAX, 42; - ret; - } + asm + { + naked; + mov EAX, 42; + ret; + } } else version(LLVM_InlineAsm_X86_64) { asm { - movq RAX, 42; + naked; + movq RAX, 42; + ret; } } } diff --git a/tests/mini/naked_asm2.d b/tests/mini/naked_asm2.d new file mode 100644 index 00000000..943e94b8 --- /dev/null +++ b/tests/mini/naked_asm2.d @@ -0,0 +1,21 @@ +int foo() +{ + static int fourty2 = 42; + version(X86) + asm + { + naked; + mov EAX, fourty2; + ret; + } + else static assert(0, "todo"); +} + +void main() +{ + int i = foo(); + printf("i == %d\n", i); + assert(i == 42); +} + +extern(C) int printf(char*, ...); diff --git a/tests/mini/naked_asm3.d b/tests/mini/naked_asm3.d new file mode 100644 index 00000000..c21298b8 --- /dev/null +++ b/tests/mini/naked_asm3.d @@ -0,0 +1,21 @@ +int foo() +{ + enum { fourty2 = 42 } + version(X86) + asm + { + naked; + mov EAX, fourty2; + ret; + } + else static assert(0, "todo"); +} + +void main() +{ + int i = foo(); + printf("i == %d\n", i); + assert(i == 42); +} + +extern(C) int printf(char*, ...); diff --git a/tests/mini/naked_asm4.d b/tests/mini/naked_asm4.d new file mode 100644 index 00000000..6e167d93 --- /dev/null +++ b/tests/mini/naked_asm4.d @@ -0,0 +1,17 @@ +void foo() +{ + version(X86) + asm + { + naked; + jmp pass; + hlt; +pass: ret; + } + else static assert(0, "todo"); +} + +void main() +{ + foo(); +} diff --git a/tests/mini/naked_asm5.d b/tests/mini/naked_asm5.d new file mode 100644 index 00000000..2bdb5daf --- /dev/null +++ b/tests/mini/naked_asm5.d @@ -0,0 +1,19 @@ +int foo(int op)(int a, int b) +{ + version(X86) + { + const OP = (op == '+') ? "add" : "sub"; + asm { naked; } + mixin("asm{"~OP~" EAX, [ESP+4];}"); + asm { ret 4; } + } + else static assert(0, "todo"); +} + +void main() +{ + int i = foo!('+')(2, 4); + assert(i == 6); + i = foo!('-')(2, 4); + assert(i == 2); +} diff --git a/tests/mini/naked_asm6.d b/tests/mini/naked_asm6.d new file mode 100644 index 00000000..8a2db5c8 --- /dev/null +++ b/tests/mini/naked_asm6.d @@ -0,0 +1,18 @@ +extern(C) int printf(char*, ...); + +ulong retval() { + asm { naked; mov EAX, 0xff; mov EDX, 0xaa; ret; } +} + +ulong retval2() { + return (cast(ulong)0xaa << 32) | 0xff; +} + +void main() { + ulong a,b; + a = retval(); + b = retval2(); + printf("%llu\n%llu\n", retval(), retval2()); + assert(a == 0x000000aa000000ff); + assert(a == b); +} diff --git a/tests/mini/structinit4.d b/tests/mini/structinit4.d new file mode 100644 index 00000000..d9e23f0b --- /dev/null +++ b/tests/mini/structinit4.d @@ -0,0 +1,18 @@ +// testcase from bug #199 + +struct Color { + uint c; + +} + +struct Vertex { + Color c; +} + +void main() { + Color c = {0xffffffff}; + + auto v = Vertex(c); + + assert(v.c.c == 0xffffffff); // fails in LDC +} diff --git a/tests/mini/structinit5.d b/tests/mini/structinit5.d new file mode 100644 index 00000000..cf344185 --- /dev/null +++ b/tests/mini/structinit5.d @@ -0,0 +1,11 @@ +struct Vertex { + uint[1] c; +} + +void main() { + uint[1] c = 0xffffffff; + + auto v = Vertex(c); + + assert(v.c[0] == 0xffffffff); // fails in LDC +} diff --git a/tests/mini/templ1.d b/tests/mini/templ1.d deleted file mode 100644 index de740fb3..00000000 --- a/tests/mini/templ1.d +++ /dev/null @@ -1,11 +0,0 @@ -module templ1; - -T func1(T)(T a) -{ - static T b = a; - return b; -} - -void main() -{ -} diff --git a/tests/mini/templ2.d b/tests/mini/templ2.d deleted file mode 100644 index 63d5ac7e..00000000 --- a/tests/mini/templ2.d +++ /dev/null @@ -1,7 +0,0 @@ -module templ2; -import templ1; - -void main() -{ - func1(1); -}