changeset 302:bef811104734 trunk

[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.
author ChristianK
date Wed, 25 Jun 2008 20:39:09 +0200
parents f42a1090e895
children 4aa2b6753059
files dmd/statement.h gen/asmstmt.cpp gen/d-asm-i386.h gen/irstate.h gen/llvmhelpers.cpp gen/llvmhelpers.h gen/statements.cpp
diffstat 7 files changed, 175 insertions(+), 98 deletions(-) [+]
line wrap: on
line diff
--- 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; }
--- 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<int, LabelDsymbol*> 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; i<n; ++i)
-    {
-        IRAsmStmt* a = asmblock->s[i];
+        size_t n = asmblock->s.size();
+        for(size_t i=0; i<n; ++i)
+        {
+            IRAsmStmt* a = asmblock->s[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<Identifier*>::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<Identifier*>::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 $<<in1>>, $<<out0>> ; ";
-        //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 $<<in" << n_goto << ">>, $<<out0>> ; ";
+            //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; i<n; ++i)
     {
         IRAsmStmt* a = asmblock->s[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<int, LabelDsymbol*>::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);
+}
--- 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());
--- 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<LLValue*> out;
     std::vector<LLValue*> 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
--- 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
 ////////////////////////////////////////////////////////////////////////////////////////*/
 
--- 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);
--- 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);