diff gen/asmstmt.cpp @ 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
line wrap: on
line diff
--- 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);
+}