[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.
This commit is contained in:
Christian Kamm
2008-06-25 20:39:09 +02:00
parent 37a74d1975
commit 7b37093cff
7 changed files with 175 additions and 98 deletions

View File

@@ -794,14 +794,17 @@ struct AsmStatement : Statement
// 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; }

View File

@@ -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 @@ assert(0);
AsmBlockStatement::AsmBlockStatement(Loc loc, Statements* s)
: CompoundStatement(loc, s)
{
enclosingtryfinally = NULL;
}
// rewrite argument indices to the block scope indices
@@ -464,10 +466,10 @@ static void remap_outargs(std::string& insnt, size_t nargs, size_t& idx)
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 @@ static void remap_inargs(std::string& insnt, size_t nargs, size_t& idx)
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 @@ void AsmBlockStatement::toIR(IRState* p)
// 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;
size_t n = asmblock->s.size();
for(size_t i=0; i<n; ++i)
{
IRAsmStmt* a = asmblock->s[i];
// 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;
// skip non-branch statements
if(!a->isBranchToLabel)
continue;
int n_goto = 1;
// 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;
size_t n = asmblock->s.size();
for(size_t i=0; i<n; ++i)
{
IRAsmStmt* a = asmblock->s[i];
// 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;
// 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->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->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 @@ void AsmBlockStatement::toIR(IRState* p)
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 @@ void AsmBlockStatement::toIR(IRState* p)
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 @@ Statement *AsmBlockStatement::syntaxCopy()
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);
}

View File

@@ -1915,7 +1915,7 @@ struct AsmProcessor
if (! lbl->asmLabelNum)
lbl->asmLabelNum = ++d_priv_asm_label_serial;
stmt->isBranchToLabel = lbl->ident;
stmt->isBranchToLabel = lbl;
use_star = false;
addLabel(lbl->ident->toChars());

View File

@@ -73,8 +73,8 @@ struct IRAsmStmt
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

View File

@@ -150,6 +150,54 @@ void DtoAssert(Loc* loc, DValue* msg)
gIR->ir->CreateUnreachable();
}
/****************************************************************************************/
/*////////////////////////////////////////////////////////////////////////////////////////
// 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

View File

@@ -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 @@ void DtoDeleteArray(DValue* arr);
// 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);

View File

@@ -44,27 +44,6 @@ void CompoundStatement::toIR(IRState* p)
}
}
//////////////////////////////////////////////////////////////////////////////
// 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 @@ void ReturnStatement::toIR(IRState* p)
if (!e->inPlace())
DtoAssign(rvar, e);
emit_finallyblocks(p, enclosingtryfinally, NULL);
DtoFinallyBlocks(enclosingtryfinally, NULL);
if (f->inVolatile) {
// store-load barrier
@@ -116,7 +95,7 @@ void ReturnStatement::toIR(IRState* p)
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 @@ void ReturnStatement::toIR(IRState* p)
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 @@ void BreakStatement::toIR(IRState* p)
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 @@ void BreakStatement::toIR(IRState* p)
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 @@ void ContinueStatement::toIR(IRState* p)
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 @@ void ContinueStatement::toIR(IRState* p)
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 @@ void GotoStatement::toIR(IRState* p)
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 @@ void GotoDefaultStatement::toIR(IRState* p)
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 @@ void GotoCaseStatement::toIR(IRState* p)
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);