# HG changeset patch # User ChristianK # Date 1214419149 -7200 # Node ID bef811104734532ab035219fc7af9d89368c2ec1 # Parent f42a1090e895a76f7479c00160038afa28019ecf [svn r323] Branching out of inline asm works. Renamed emit_finallyblocks to DtoFinallyBlocks and moved to llvmhelpers. Added enclosingtryfinally to AsmBlockStatement, so branches out of asm blocks respect finallys. Refactored some GotoStatement code into DtoGoto. diff -r f42a1090e895 -r bef811104734 dmd/statement.h --- a/dmd/statement.h Tue Jun 24 22:48:33 2008 +0200 +++ b/dmd/statement.h Wed Jun 25 20:39:09 2008 +0200 @@ -794,14 +794,17 @@ // LLVMDC // non-zero if this is a branch, contains the target - Identifier* isBranchToLabel; + LabelDsymbol* isBranchToLabel; }; struct AsmBlockStatement : CompoundStatement { + TryFinallyStatement *enclosingtryfinally; + AsmBlockStatement(Loc loc, Statements *s); Statements *flatten(Scope *sc); Statement *syntaxCopy(); + Statement *semantic(Scope *sc); CompoundStatement *isCompoundStatement() { return NULL; } AsmBlockStatement *isAsmBlockStatement() { return this; } diff -r f42a1090e895 -r bef811104734 gen/asmstmt.cpp --- a/gen/asmstmt.cpp Tue Jun 24 22:48:33 2008 +0200 +++ b/gen/asmstmt.cpp Wed Jun 25 20:39:09 2008 +0200 @@ -26,6 +26,7 @@ #include "gen/tollvm.h" #include "gen/logger.h" #include "gen/todebug.h" +#include "gen/llvmhelpers.h" typedef enum { Arg_Integer, @@ -444,6 +445,7 @@ AsmBlockStatement::AsmBlockStatement(Loc loc, Statements* s) : CompoundStatement(loc, s) { + enclosingtryfinally = NULL; } // rewrite argument indices to the block scope indices @@ -464,10 +466,10 @@ for (unsigned i = 0; i < nargs; i++) { needle = prefix + digits[i] + suffix; size_t pos = insnt.find(needle); - if(pos != std::string::npos) { + if(std::string::npos != pos) sprintf(buf, "%u", idx++); + while(std::string::npos != (pos = insnt.find(needle))) insnt.replace(pos, needle.size(), buf); - } } } @@ -489,10 +491,10 @@ for (unsigned i = 0; i < nargs; i++) { needle = prefix + digits[i] + suffix; size_t pos = insnt.find(needle); - if(pos != std::string::npos) { + if(std::string::npos != pos) sprintf(buf, "%u", idx++); + while(std::string::npos != (pos = insnt.find(needle))) insnt.replace(pos, needle.size(), buf); - } } } @@ -522,54 +524,73 @@ // to a unique value that will identify the jump target in // a post-asm switch - // create storage for and initialize the temporary - llvm::AllocaInst* jump_target = new llvm::AllocaInst(llvm::IntegerType::get(32), "__llvm_jump_target", p->topallocapoint()); - gIR->ir->CreateStore(llvm::ConstantInt::get(llvm::IntegerType::get(32), 0), jump_target); + // maps each special value to a goto destination + std::map valToGoto; - IRAsmStmt* outSetterStmt = new IRAsmStmt; - std::string asmGotoEnd = "jmp __llvm_asm_end ; "; - outSetterStmt->code = asmGotoEnd; - outSetterStmt->out_c = "=*m,"; - outSetterStmt->out.push_back(jump_target); + // location of the value containing the index into the valToGoto map + // will be set if post-asm dispatcher block is needed + llvm::AllocaInst* jump_target; - int n_goto = 1; + { + // initialize the setter statement we're going to build + IRAsmStmt* outSetterStmt = new IRAsmStmt; + std::string asmGotoEnd = "jmp __llvm_asm_end ; "; + std::ostringstream code; + code << asmGotoEnd; + + int n_goto = 1; - size_t n = asmblock->s.size(); - for(size_t i=0; is[i]; + size_t n = asmblock->s.size(); + for(size_t i=0; is[i]; - // skip non-branch statements - if(!a->isBranchToLabel) - continue; + // skip non-branch statements + if(!a->isBranchToLabel) + continue; - // if internal, no special handling is necessary, skip - std::vector::const_iterator it, end; - end = asmblock->internalLabels.end(); - bool skip = false; - for(it = asmblock->internalLabels.begin(); it != end; ++it) - if((*it)->equals(a->isBranchToLabel)) - skip = true; - if(skip) - continue; + // if internal, no special handling is necessary, skip + std::vector::const_iterator it, end; + end = asmblock->internalLabels.end(); + bool skip = false; + for(it = asmblock->internalLabels.begin(); it != end; ++it) + if((*it)->equals(a->isBranchToLabel->ident)) + skip = true; + if(skip) + continue; + + // record that the jump needs to be handled in the post-asm dispatcher + valToGoto[n_goto] = a->isBranchToLabel; - // provide an in-asm target for the branch and set value - Logger::println("statement '%s' references outer label '%s': creating forwarder", a->code.c_str(), a->isBranchToLabel->string); - outSetterStmt->code += a->isBranchToLabel->string; - outSetterStmt->code += ": ; "; - outSetterStmt->code += "movl $<>, $<> ; "; - //FIXME: Store the value -> label mapping somewhere, so it can be referenced later - outSetterStmt->in.push_back(llvm::ConstantInt::get(llvm::IntegerType::get(32), n_goto++)); - outSetterStmt->in_c += "i,"; - outSetterStmt->code += asmGotoEnd; + // provide an in-asm target for the branch and set value + Logger::println("statement '%s' references outer label '%s': creating forwarder", a->code.c_str(), a->isBranchToLabel->ident->string); + code << a->isBranchToLabel->ident->string << ": ; "; + code << "movl $<>, $<> ; "; + //FIXME: Store the value -> label mapping somewhere, so it can be referenced later + outSetterStmt->in.push_back(llvm::ConstantInt::get(llvm::IntegerType::get(32), n_goto)); + outSetterStmt->in_c += "i,"; + code << asmGotoEnd; + + ++n_goto; + } + if(code.str() != asmGotoEnd) + { + // finalize code + outSetterStmt->code = code.str(); + outSetterStmt->code += "__llvm_asm_end: ; "; + + // create storage for and initialize the temporary + jump_target = new llvm::AllocaInst(llvm::IntegerType::get(32), "__llvm_jump_target", p->topallocapoint()); + gIR->ir->CreateStore(llvm::ConstantInt::get(llvm::IntegerType::get(32), 0), jump_target); + // setup variable for output from asm + outSetterStmt->out_c = "=*m,"; + outSetterStmt->out.push_back(jump_target); + + asmblock->s.push_back(outSetterStmt); + } + else + delete outSetterStmt; } - if(outSetterStmt->code != asmGotoEnd) - { - outSetterStmt->code += "__llvm_asm_end: ; "; - asmblock->s.push_back(outSetterStmt); - } - else - delete outSetterStmt; // build asm block @@ -583,7 +604,7 @@ std::string code; size_t asmIdx = 0; - n = asmblock->s.size(); + size_t n = asmblock->s.size(); for (size_t i=0; is[i]; @@ -653,7 +674,31 @@ p->asmBlock = NULL; Logger::println("END ASM"); - //FIXME: Emit goto forwarder code here + // if asm contained external branches, emit goto forwarder code + if(!valToGoto.empty()) + { + assert(jump_target); + + // make new blocks + llvm::BasicBlock* oldend = gIR->scopeend(); + llvm::BasicBlock* bb = llvm::BasicBlock::Create("afterasmgotoforwarder", p->topfunc(), oldend); + + llvm::LoadInst* val = p->ir->CreateLoad(jump_target, "__llvm_jump_target_value"); + llvm::SwitchInst* sw = p->ir->CreateSwitch(val, bb, valToGoto.size()); + + // add all cases + std::map::iterator it, end = valToGoto.end(); + for(it = valToGoto.begin(); it != end; ++it) + { + llvm::BasicBlock* casebb = llvm::BasicBlock::Create("case", p->topfunc(), bb); + sw->addCase(llvm::ConstantInt::get(llvm::IntegerType::get(32), it->first), casebb); + + p->scope() = IRScope(casebb,bb); + DtoGoto(&loc, it->second, enclosingtryfinally); + } + + p->scope() = IRScope(bb,oldend); + } } // the whole idea of this statement is to avoid the flattening @@ -676,3 +721,11 @@ AsmBlockStatement *cs = new AsmBlockStatement(loc, a); return cs; } + +// necessary for in-asm branches +Statement *AsmBlockStatement::semantic(Scope *sc) +{ + enclosingtryfinally = sc->tfOfTry; + + return CompoundStatement::semantic(sc); +} diff -r f42a1090e895 -r bef811104734 gen/d-asm-i386.h --- a/gen/d-asm-i386.h Tue Jun 24 22:48:33 2008 +0200 +++ b/gen/d-asm-i386.h Wed Jun 25 20:39:09 2008 +0200 @@ -1915,7 +1915,7 @@ if (! lbl->asmLabelNum) lbl->asmLabelNum = ++d_priv_asm_label_serial; - stmt->isBranchToLabel = lbl->ident; + stmt->isBranchToLabel = lbl; use_star = false; addLabel(lbl->ident->toChars()); diff -r f42a1090e895 -r bef811104734 gen/irstate.h --- a/gen/irstate.h Tue Jun 24 22:48:33 2008 +0200 +++ b/gen/irstate.h Wed Jun 25 20:39:09 2008 +0200 @@ -73,8 +73,8 @@ std::vector out; std::vector in; - // if this is nonzero, it contains the target ident - Identifier* isBranchToLabel; + // if this is nonzero, it contains the target label + LabelDsymbol* isBranchToLabel; }; struct IRAsmBlock diff -r f42a1090e895 -r bef811104734 gen/llvmhelpers.cpp --- a/gen/llvmhelpers.cpp Tue Jun 24 22:48:33 2008 +0200 +++ b/gen/llvmhelpers.cpp Wed Jun 25 20:39:09 2008 +0200 @@ -152,6 +152,54 @@ /****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// +// GOTO HELPER +////////////////////////////////////////////////////////////////////////////////////////*/ +void DtoGoto(Loc* loc, LabelDsymbol* target, TryFinallyStatement* enclosingtryfinally) +{ + assert(!gIR->scopereturned()); + + if (target->statement->llvmBB == NULL) + target->statement->llvmBB = llvm::BasicBlock::Create("label", gIR->topfunc()); + + // find finallys between goto and label + TryFinallyStatement* endfinally = enclosingtryfinally; + while(endfinally != NULL && endfinally != target->statement->enclosingtryfinally) { + endfinally = endfinally->enclosingtryfinally; + } + + // error if didn't find tf statement of label + if(endfinally != target->statement->enclosingtryfinally) + error("cannot goto into try block", loc->toChars()); + + // emit code for finallys between goto and label + DtoFinallyBlocks(enclosingtryfinally, endfinally); + + llvm::BranchInst::Create(target->statement->llvmBB, gIR->scopebb()); +} + +/****************************************************************************************/ +/*//////////////////////////////////////////////////////////////////////////////////////// +// TRY FINALLY HELPER +////////////////////////////////////////////////////////////////////////////////////////*/ +void DtoFinallyBlocks(TryFinallyStatement* start, TryFinallyStatement* end) +{ + // verify that end encloses start + TryFinallyStatement* endfinally = start; + while(endfinally != NULL && endfinally != end) { + endfinally = endfinally->enclosingtryfinally; + } + assert(endfinally == end); + + // emit code for finallys between start and end + TryFinallyStatement* tf = start; + while(tf != end) { + tf->finalbody->toIR(gIR); + tf = tf->enclosingtryfinally; + } +} + +/****************************************************************************************/ +/*//////////////////////////////////////////////////////////////////////////////////////// // NESTED VARIABLE HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ diff -r f42a1090e895 -r bef811104734 gen/llvmhelpers.h --- a/gen/llvmhelpers.h Tue Jun 24 22:48:33 2008 +0200 +++ b/gen/llvmhelpers.h Wed Jun 25 20:39:09 2008 +0200 @@ -1,6 +1,8 @@ #ifndef LLVMDC_GEN_LLVMHELPERS_H #define LLVMDC_GEN_LLVMHELPERS_H +#include "statement.h" + // dynamic memory helpers LLValue* DtoNew(Type* newtype); void DtoDeleteMemory(LLValue* ptr); @@ -11,6 +13,14 @@ // assertion generator void DtoAssert(Loc* loc, DValue* msg); +// emit goto +void DtoGoto(Loc* loc, LabelDsymbol* target, TryFinallyStatement* enclosingtryfinally); + +// generates IR for finally blocks between the 'start' and 'end' statements +// will begin with the finally block belonging to 'start' and does not include +// the finally block of 'end' +void DtoFinallyBlocks(TryFinallyStatement* start, TryFinallyStatement* end); + // nested variable/class helpers LLValue* DtoNestedContext(FuncDeclaration* func); LLValue* DtoNestedVariable(VarDeclaration* vd); diff -r f42a1090e895 -r bef811104734 gen/statements.cpp --- a/gen/statements.cpp Tue Jun 24 22:48:33 2008 +0200 +++ b/gen/statements.cpp Wed Jun 25 20:39:09 2008 +0200 @@ -44,27 +44,6 @@ } } -////////////////////////////////////////////////////////////////////////////// - -// generates IR for finally blocks between the 'start' and 'end' statements -// will begin with the finally block belonging to 'start' and does not include -// the finally block of 'end' -void emit_finallyblocks(IRState* p, TryFinallyStatement* start, TryFinallyStatement* end) -{ - // verify that end encloses start - TryFinallyStatement* endfinally = start; - while(endfinally != NULL && endfinally != end) { - endfinally = endfinally->enclosingtryfinally; - } - assert(endfinally == end); - - // emit code for finallys between start and end - TryFinallyStatement* tf = start; - while(tf != end) { - tf->finalbody->toIR(p); - tf = tf->enclosingtryfinally; - } -} ////////////////////////////////////////////////////////////////////////////// @@ -91,7 +70,7 @@ if (!e->inPlace()) DtoAssign(rvar, e); - emit_finallyblocks(p, enclosingtryfinally, NULL); + DtoFinallyBlocks(enclosingtryfinally, NULL); if (f->inVolatile) { // store-load barrier @@ -116,7 +95,7 @@ Logger::cout() << "return value after cast: " << *v << '\n'; } - emit_finallyblocks(p, enclosingtryfinally, NULL); + DtoFinallyBlocks(enclosingtryfinally, NULL); if (gIR->func()->inVolatile) { // store-load barrier @@ -130,7 +109,7 @@ else { assert(p->topfunc()->getReturnType() == LLType::VoidTy); - emit_finallyblocks(p, enclosingtryfinally, NULL); + DtoFinallyBlocks(enclosingtryfinally, NULL); if (gIR->func()->inVolatile) { // store-load barrier @@ -431,7 +410,7 @@ if (ident != 0) { Logger::println("ident = %s", ident->toChars()); - emit_finallyblocks(p, enclosingtryfinally, target->enclosingtryfinally); + DtoFinallyBlocks(enclosingtryfinally, target->enclosingtryfinally); // get the loop statement the label refers to Statement* targetLoopStatement = target->statement; @@ -452,7 +431,7 @@ assert(found); } else { - emit_finallyblocks(p, enclosingtryfinally, p->loopbbs.back().enclosingtryfinally); + DtoFinallyBlocks(enclosingtryfinally, p->loopbbs.back().enclosingtryfinally); llvm::BranchInst::Create(p->loopbbs.back().end, p->scopebb()); } @@ -475,7 +454,7 @@ if (ident != 0) { Logger::println("ident = %s", ident->toChars()); - emit_finallyblocks(p, enclosingtryfinally, target->enclosingtryfinally); + DtoFinallyBlocks(enclosingtryfinally, target->enclosingtryfinally); // get the loop statement the label refers to Statement* targetLoopStatement = target->statement; @@ -494,7 +473,7 @@ assert(0); } else { - emit_finallyblocks(p, enclosingtryfinally, gIR->loopbbs.back().enclosingtryfinally); + DtoFinallyBlocks(enclosingtryfinally, gIR->loopbbs.back().enclosingtryfinally); llvm::BranchInst::Create(gIR->loopbbs.back().begin, gIR->scopebb()); } } @@ -1108,24 +1087,8 @@ llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* bb = llvm::BasicBlock::Create("aftergoto", p->topfunc(), oldend); - if (label->statement->llvmBB == NULL) - label->statement->llvmBB = llvm::BasicBlock::Create("label", p->topfunc()); - assert(!p->scopereturned()); + DtoGoto(&loc, label, enclosingtryfinally); - // find finallys between goto and label - TryFinallyStatement* endfinally = enclosingtryfinally; - while(endfinally != NULL && endfinally != label->statement->enclosingtryfinally) { - endfinally = endfinally->enclosingtryfinally; - } - - // error if didn't find tf statement of label - if(endfinally != label->statement->enclosingtryfinally) - error("cannot goto into try block", loc.toChars()); - - // emit code for finallys between goto and label - emit_finallyblocks(p, enclosingtryfinally, endfinally); - - llvm::BranchInst::Create(label->statement->llvmBB, p->scopebb()); p->scope() = IRScope(bb,oldend); } @@ -1145,7 +1108,7 @@ assert(!p->scopereturned()); assert(sw->sdefault->bodyBB); - emit_finallyblocks(p, enclosingtryfinally, sw->enclosingtryfinally); + DtoFinallyBlocks(enclosingtryfinally, sw->enclosingtryfinally); llvm::BranchInst::Create(sw->sdefault->bodyBB, p->scopebb()); p->scope() = IRScope(bb,oldend); @@ -1170,7 +1133,7 @@ cs->bodyBB = llvm::BasicBlock::Create("goto_case", p->topfunc(), p->scopeend()); } - emit_finallyblocks(p, enclosingtryfinally, sw->enclosingtryfinally); + DtoFinallyBlocks(enclosingtryfinally, sw->enclosingtryfinally); llvm::BranchInst::Create(cs->bodyBB, p->scopebb()); p->scope() = IRScope(bb,oldend);