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.
This commit is contained in:
Tomas Lindquist Olsen 2009-02-03 08:54:57 +01:00
parent 8ab98dad49
commit dc5944df99
28 changed files with 491 additions and 153 deletions

View file

@ -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)

View file

@ -274,6 +274,7 @@ struct VarDeclaration : Declaration
// LDC
AnonDeclaration* anonDecl;
unsigned offset2;
bool nakedUse;
};
/**************************************************************/

View file

@ -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);

View file

@ -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 */

View file

@ -1409,11 +1409,56 @@ struct AsmProcessor
}
void addOperand(const char * fmt, AsmArgType type, Expression * e, AsmCode * asmcode, AsmArgMode mode = Mode_Input) {
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) {
assert(!sc->func->naked);
insnTemplate->writestring((char*) fmtpre);
insnTemplate->printf("<<%s%d>>", (mode==Mode_Input)?"in":"out", asmcode->args.dim);
insnTemplate->writestring((char*) fmtpost);
@ -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();

View file

@ -1529,11 +1529,56 @@ struct AsmProcessor
}
void addOperand(const char * fmt, AsmArgType type, Expression * e, AsmCode * asmcode, AsmArgMode mode = Mode_Input) {
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) {
assert(!sc->func->naked);
insnTemplate->writestring((char*) fmtpre);
insnTemplate->printf("<<%s%d>>", (mode==Mode_Input)?"in":"out", asmcode->args.dim);
insnTemplate->writestring((char*) fmtpost);
@ -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();

View file

@ -15,7 +15,6 @@
#include <cassert>
#include <deque>
#include <iostream>
#include <sstream>
#include <cstring>
//#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; i<statements->dim; i++)
{
Statement* s = (Statement*)statements->data[i];
if (s) s->toNakedIR(p);
}
}

View file

@ -1,4 +1,3 @@
#include <sstream>
#include "gen/llvm.h"
#include "mtype.h"

View file

@ -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) &&
if (
(arg->storageClass & (STCref|STCout))
||
((arg->storageClass & STCin) &&
((t->isscalar() && !t->isfloating()) ||
t->ty == Tclass || t->ty == Tsarray || t->ty == Taarray) &&
(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);

View file

@ -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);

View file

@ -3,6 +3,7 @@
#include <vector>
#include <list>
#include <sstream>
#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<LLConstant*> usedArray;
// dwarf dbg stuff
LLGlobalVariable* dwarfCUs;
LLGlobalVariable* dwarfSPs;
LLGlobalVariable* dwarfGVs;

View file

@ -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);
}
//////////////////////////////////////////////////////////////////////////////////////////

View file

@ -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);

140
gen/naked.cpp Normal file
View file

@ -0,0 +1,140 @@
#include "gen/llvm.h"
#include "expression.h"
#include "statement.h"
#include "declaration.h"
#include <cassert>
#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();
}

View file

@ -2,7 +2,6 @@
#include <stdio.h>
#include <math.h>
#include <sstream>
#include <fstream>
#include <iostream>

View file

@ -8,7 +8,6 @@
#include <stdio.h>
#include <math.h>
#include <sstream>
#include <fstream>
#include <iostream>
@ -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));
}
//////////////////////////////////////////////////////////////////////////////////////////

View file

@ -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)

View file

@ -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,9 +934,11 @@ void VarDeclaration::toObjFile(int multiobj)
if (Logger::enabled())
Logger::cout() << *gvar << '\n';
if (static_local)
DtoConstInitGlobal(this);
else
// 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

View file

@ -1,5 +1,3 @@
module tangotests.asm5;
extern(C) int printf(char*, ...);
void main()
@ -24,7 +22,9 @@ int func()
{
asm
{
naked;
movq RAX, 42;
ret;
}
}
}

21
tests/mini/naked_asm2.d Normal file
View file

@ -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*, ...);

21
tests/mini/naked_asm3.d Normal file
View file

@ -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*, ...);

17
tests/mini/naked_asm4.d Normal file
View file

@ -0,0 +1,17 @@
void foo()
{
version(X86)
asm
{
naked;
jmp pass;
hlt;
pass: ret;
}
else static assert(0, "todo");
}
void main()
{
foo();
}

19
tests/mini/naked_asm5.d Normal file
View file

@ -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);
}

18
tests/mini/naked_asm6.d Normal file
View file

@ -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);
}

18
tests/mini/structinit4.d Normal file
View file

@ -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
}

11
tests/mini/structinit5.d Normal file
View file

@ -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
}

View file

@ -1,11 +0,0 @@
module templ1;
T func1(T)(T a)
{
static T b = a;
return b;
}
void main()
{
}

View file

@ -1,7 +0,0 @@
module templ2;
import templ1;
void main()
{
func1(1);
}