view gen/asmstmt.cpp @ 1117:4c20fcc4252b

Fun with parameter attributes: For several of the "synthetic" parameters added to D functions, we can apply noalias and nocapture. They are sret parameters, 'nest' pointers passed to nested functions, and _argptr: Nocapture: - Sret and nest are nocapture because they don't represent D-level variables, and thus the callee can't (validly) obtain a pointer to them, let alone keep it around after it returns. - _argptr is nocapture because although the callee has access to it as a pointer, that pointer is invalidated when it returns. All three are noalias because they're function-local variables - Sret and _argptr are noalias because they're freshly alloca'd memory only used for a single function call that's not allowed to keep an aliasing pointer to it around (since the parameter is nocapture). - 'Nest' is noalias because the callee only ever has access to one such pointer per parent function, and every parent function has a different one. This commit also ensures attributes set on sret, _arguments and _argptr are propagated to calls to such functions. It also adds one exception to the general rule that attributes on function types should propagate to calls: the type of a delegate's function pointer has a 'nest' parameter, but this can either be a true 'nest' (for delegates to nested functions) or a 'this' (for delegates to member functions). Since 'this' is neither noalias nor nocapture, and there's generally no way to tell which one it is, we remove these attributes at the call site if the callee is a delegate.
author Frits van Bommel <fvbommel wxs.nl>
date Sat, 14 Mar 2009 22:15:31 +0100
parents b30fe7e1dbb9
children f99a3b393c03
line wrap: on
line source

// Taken from GDC source tree. Original by David Friedman.
// Released under the Artistic License found in dmd/artistic.txt

#include "gen/llvm.h"
#include "llvm/InlineAsm.h"

//#include "d-gcc-includes.h"
//#include "total.h"
#include "mars.h"
#include "statement.h"
#include "scope.h"
#include "declaration.h"
#include "dsymbol.h"

#include <cassert>
#include <deque>
#include <cstring>
#include <string>
#include <sstream>

//#include "d-lang.h"
//#include "d-codegen.h"

#include "gen/irstate.h"
#include "gen/dvalue.h"
#include "gen/tollvm.h"
#include "gen/logger.h"
#include "gen/todebug.h"
#include "gen/llvmhelpers.h"
#include "gen/functions.h"

typedef enum {
    Arg_Integer,
    Arg_Pointer,
    Arg_Memory,
    Arg_FrameRelative,
    Arg_LocalSize,
    Arg_Dollar
} AsmArgType;

typedef enum {
    Mode_Input,
    Mode_Output,
    Mode_Update
} AsmArgMode;

struct AsmArg {
    Expression * expr;
    AsmArgType   type;
    AsmArgMode   mode;
    AsmArg(AsmArgType type, Expression * expr, AsmArgMode mode) {
	this->type = type;
	this->expr = expr;
	this->mode = mode;
    }
};

struct AsmCode {
    std::string insnTemplate;
    std::vector<AsmArg> args;
    std::vector<bool> regs;
    unsigned dollarLabel;
    int      clobbersMemory;
    AsmCode(int n_regs) {
	regs.resize(n_regs, false);
	dollarLabel = 0;
	clobbersMemory = 0;
    }
};

AsmStatement::AsmStatement(Loc loc, Token *tokens) :
    Statement(loc)
{
    this->tokens = tokens; // Do I need to copy these?
    asmcode = 0;
    asmalign = 0;
    refparam = 0;
    naked = 0;

    isBranchToLabel = NULL;
}

Statement *AsmStatement::syntaxCopy()
{
    // copy tokens? copy 'code'?
    AsmStatement * a_s = new AsmStatement(loc,tokens);
    a_s->asmcode = asmcode;
    a_s->refparam = refparam;
    a_s->naked = naked;
    return a_s;
}

void AsmStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    bool sep = 0, nsep = 0;
    buf->writestring("asm { ");
    
    for (Token * t = tokens; t; t = t->next) {	
	switch (t->value) {
	case TOKlparen:
	case TOKrparen:
	case TOKlbracket:
	case TOKrbracket:
	case TOKcolon:
	case TOKsemicolon:
	case TOKcomma:
	case TOKstring:
	case TOKcharv:
	case TOKwcharv:
	case TOKdcharv:
	    nsep = 0;
	    break;
	default:
	    nsep = 1;
	}
	if (sep + nsep == 2)
    		buf->writeByte(' ');
	sep = nsep;
	buf->writestring(t->toChars());
    }
    buf->writestring("; }");
    buf->writenl();
}

int AsmStatement::comeFrom()
{
    return FALSE;
}

struct AsmParserCommon
{
    virtual void run(Scope* sc, AsmStatement* asmst) = 0;
    virtual std::string getRegName(int i) = 0;
};
AsmParserCommon* asmparser = NULL;

#include "asm-x86-32.h"
#include "asm-x86-64.h"

bool d_have_inline_asm() { return true; }

Statement *AsmStatement::semantic(Scope *sc)
{
    bool err = false;
    if ((global.params.cpu != ARCHx86) && (global.params.cpu != ARCHx86_64))
    {
        error("inline asm is not supported for the \"%s\" architecture", global.params.llvmArch);
        err = true;
    }
    if (!global.params.useInlineAsm)
    {
        error("inline asm is not allowed when the -noasm switch is used");
        err = true;
    }
    if (err)
        fatal();

    //puts(toChars());

    sc->func->inlineAsm = 1;
    sc->func->inlineStatus = ILSno; // %% not sure
    // %% need to set DECL_UNINLINABLE too?
    sc->func->hasReturnExp = 1; // %% DMD does this, apparently...
    
    // empty statement -- still do the above things because they might be expected?
    if (! tokens)
	return this;
    
    if (!asmparser)
        if (global.params.cpu == ARCHx86)
            asmparser = new AsmParserx8632::AsmParser;
        else if (global.params.cpu == ARCHx86_64)
            asmparser = new AsmParserx8664::AsmParser;

    asmparser->run(sc, this);

    return this;
}

int AsmStatement::blockExit()
{
    //printf("AsmStatement::blockExit(%p)\n", this);
    return BEfallthru | BEreturn | BEgoto | BEhalt;
}

void
AsmStatement::toIR(IRState * irs)
{
    Logger::println("AsmStatement::toIR(): %s", loc.toChars());
    LOG_SCOPE;

    // sanity check
    assert(irs->func()->decl->inlineAsm);

    // get asm block
    IRAsmBlock* asmblock = irs->asmBlock;
    assert(asmblock);

    // debug info
    if (global.params.symdebug)
        DtoDwarfStopPoint(loc.linnum);

    if (! asmcode)
    return;

    static std::string i_cns = "i";
    static std::string p_cns = "i";
    static std::string m_cns = "*m";
    static std::string mw_cns = "=*m";
    static std::string mrw_cns = "+*m";
    static std::string memory_name = "memory";

    AsmCode * code = (AsmCode *) asmcode;
    std::vector<LLValue*> input_values;
    std::vector<std::string> input_constraints;
    std::vector<LLValue*> output_values;
    std::vector<std::string> output_constraints;
    std::vector<std::string> clobbers;

// FIXME
    //#define HOST_WIDE_INT long
    //HOST_WIDE_INT var_frame_offset; // "frame_offset" is a macro
    bool clobbers_mem = code->clobbersMemory;
    int input_idx = 0;
    int n_outputs = 0;
    int arg_map[10];

    assert(code->args.size() <= 10);

    std::vector<AsmArg>::iterator arg = code->args.begin();
    for (unsigned i = 0; i < code->args.size(); i++, ++arg) {
	bool is_input = true;
	LLValue* arg_val = 0;
	std::string cns;

	switch (arg->type) {
	case Arg_Integer:
	    arg_val = arg->expr->toElem(irs)->getRVal();
	do_integer:
	    cns = i_cns;
	    break;
	case Arg_Pointer:
        assert(arg->expr->op == TOKvar);
        arg_val = arg->expr->toElem(irs)->getRVal();
        cns = p_cns;

	    break;
	case Arg_Memory:
        arg_val = arg->expr->toElem(irs)->getRVal();

	    switch (arg->mode) {
	    case Mode_Input:  cns = m_cns; break;
	    case Mode_Output: cns = mw_cns;  is_input = false; break;
	    case Mode_Update: cns = mrw_cns; is_input = false; break;
	    default: assert(0); break;
	    }
	    break;
	case Arg_FrameRelative:
// FIXME
llvm::cout << "asm fixme Arg_FrameRelative" << std::endl;
assert(0);
/*	    if (arg->expr->op == TOKvar)
		arg_val = ((VarExp *) arg->expr)->var->toSymbol()->Stree;
	    else
		assert(0);
	    if ( getFrameRelativeValue(arg_val, & var_frame_offset) ) {
//		arg_val = irs->integerConstant(var_frame_offset);
		cns = i_cns;
	    } else {
		this->error("%s", "argument not frame relative");
		return;
	    }
	    if (arg->mode != Mode_Input)
		clobbers_mem = true;
	    break;*/
	case Arg_LocalSize:
// FIXME
llvm::cout << "asm fixme Arg_LocalSize" << std::endl;
assert(0);
/*	    var_frame_offset = cfun->x_frame_offset;
	    if (var_frame_offset < 0)
		var_frame_offset = - var_frame_offset;
	    arg_val = irs->integerConstant( var_frame_offset );*/
	    goto do_integer;
	default:
	    assert(0);
	}

	if (is_input) {
	    arg_map[i] = --input_idx;
	    input_values.push_back(arg_val);
	    input_constraints.push_back(cns);
	} else {
	    arg_map[i] = n_outputs++;
	    output_values.push_back(arg_val);
	    output_constraints.push_back(cns);
	}
    }

    // Telling GCC that callee-saved registers are clobbered makes it preserve
    // those registers.   This changes the stack from what a naked function
    // expects.
    
// FIXME
//    if (! irs->func->naked) {
        assert(asmparser);
	for (int i = 0; i < code->regs.size(); i++) {
	    if (code->regs[i]) {
		clobbers.push_back(asmparser->getRegName(i));
	    }
	}
	if (clobbers_mem)
	    clobbers.push_back(memory_name);
//    }

    // Remap argument numbers
    for (unsigned i = 0; i < code->args.size(); i++) {
	if (arg_map[i] < 0)
	    arg_map[i] = -arg_map[i] - 1 + n_outputs;
    }
    
    bool pct = false;
    std::string::iterator
        p = code->insnTemplate.begin(),
        q = code->insnTemplate.end();
    //printf("start: %.*s\n", code->insnTemplateLen, code->insnTemplate);
    while (p < q) {
	if (pct) {
	    if (*p >= '0' && *p <= '9') {
		// %% doesn't check against nargs
		*p = '0' + arg_map[*p - '0'];
		pct = false;
	    } else if (*p == '$') {
		pct = false;
	    }
	    //assert(*p == '%');// could be 'a', etc. so forget it..
	} else if (*p == '$')
	    pct = true;
	++p;
    }

    typedef std::vector<std::string>::iterator It;
    if (Logger::enabled()) {
        Logger::cout() << "final asm: " << code->insnTemplate << '\n';
        std::ostringstream ss;
        
        ss << "GCC-style output constraints: {";
        for (It i = output_constraints.begin(), e = output_constraints.end(); i != e; ++i) {
            ss << " " << *i;
        }
        ss << " }";
        Logger::println("%s", ss.str().c_str());
        
        ss.str("");
        ss << "GCC-style input constraints: {";
        for (It i = input_constraints.begin(), e = input_constraints.end(); i != e; ++i) {
            ss << " " << *i;
        }
        ss << " }";
        Logger::println("%s", ss.str().c_str());
        
        ss.str("");
        ss << "GCC-style clobbers: {";
        for (It i = clobbers.begin(), e = clobbers.end(); i != e; ++i) {
            ss << " " << *i;
        }
        ss << " }";
        Logger::println("%s", ss.str().c_str());
    }

    // rewrite GCC-style constraints to LLVM-style constraints
    std::string llvmOutConstraints;
    std::string llvmInConstraints;
    int n = 0;
    for(It i = output_constraints.begin(), e = output_constraints.end(); i != e; ++i, ++n) {
        // rewrite update constraint to in and out constraints
        if((*i)[0] == '+') {
            assert(*i == mrw_cns && "What else are we updating except memory?");
            /* LLVM doesn't support updating operands, so split into an input
             * and an output operand.
             */
            
            // Change update operand to pure output operand.
            *i = mw_cns;
            
            // Add input operand with same value, with original as "matching output".
            std::ostringstream ss;
            ss << '*' << (n + asmblock->outputcount);
            // Must be at the back; unused operands before used ones screw up numbering.
            input_constraints.push_back(ss.str());
            input_values.push_back(output_values[n]);
        }
        llvmOutConstraints += *i;
        llvmOutConstraints += ",";
    }
    asmblock->outputcount += n;

    for(It i = input_constraints.begin(), e = input_constraints.end(); i != e; ++i) {
        llvmInConstraints += *i;
        llvmInConstraints += ",";
    }

    std::string clobstr;
    for(It i = clobbers.begin(), e = clobbers.end(); i != e; ++i) {
        clobstr = "~{" + *i + "},";
        asmblock->clobs.insert(clobstr);
    }

    if (Logger::enabled()) {
        typedef std::vector<LLValue*>::iterator It;
        {
            Logger::println("Output values:");
            LOG_SCOPE
            size_t i = 0;
            for (It I = output_values.begin(), E = output_values.end(); I != E; ++I) {
                Logger::cout() << "Out " << i++ << " = " << **I << '\n';
            }
        }
        {
            Logger::println("Input values:");
            LOG_SCOPE
            size_t i = 0;
            for (It I = input_values.begin(), E = input_values.end(); I != E; ++I) {
                Logger::cout() << "In  " << i++ << " = " << **I << '\n';
            }
        }
    }

    // excessive commas are removed later...

    // push asm statement
    IRAsmStmt* asmStmt = new IRAsmStmt;
    asmStmt->code = code->insnTemplate;
    asmStmt->out_c = llvmOutConstraints;
    asmStmt->in_c = llvmInConstraints;
    asmStmt->out.insert(asmStmt->out.begin(), output_values.begin(), output_values.end());
    asmStmt->in.insert(asmStmt->in.begin(), input_values.begin(), input_values.end());
    asmStmt->isBranchToLabel = isBranchToLabel;
    asmblock->s.push_back(asmStmt);
}

//////////////////////////////////////////////////////////////////////////////

AsmBlockStatement::AsmBlockStatement(Loc loc, Statements* s)
:   CompoundStatement(loc, s)
{
    enclosinghandler = NULL;
    tf = NULL;

    abiret = NULL;
}

// rewrite argument indices to the block scope indices
static void remap_outargs(std::string& insnt, size_t nargs, size_t idx)
{
    static const std::string digits[10] =
    {
        "0","1","2","3","4",
        "5","6","7","8","9"
    };
    assert(nargs <= 10);

    static const std::string prefix("<<out");
    static const std::string suffix(">>");
    std::string argnum;
    std::string needle;
    char buf[10];
    for (unsigned i = 0; i < nargs; i++) {
        needle = prefix + digits[i] + suffix;
        size_t pos = insnt.find(needle);
        if(std::string::npos != pos)
            sprintf(buf, "%lu", idx++);
        while(std::string::npos != (pos = insnt.find(needle)))
            insnt.replace(pos, needle.size(), buf);
    }
}

// rewrite argument indices to the block scope indices
static void remap_inargs(std::string& insnt, size_t nargs, size_t idx)
{
    static const std::string digits[10] =
    {
        "0","1","2","3","4",
        "5","6","7","8","9"
    };
    assert(nargs <= 10);

    static const std::string prefix("<<in");
    static const std::string suffix(">>");
    std::string argnum;
    std::string needle;
    char buf[10];
    for (unsigned i = 0; i < nargs; i++) {
        needle = prefix + digits[i] + suffix;
        size_t pos = insnt.find(needle);
        if(std::string::npos != pos)
            sprintf(buf, "%lu", idx++);
        while(std::string::npos != (pos = insnt.find(needle)))
            insnt.replace(pos, needle.size(), buf);
    }
}

LLValue* DtoAggrPairSwap(LLValue* aggr);

void AsmBlockStatement::toIR(IRState* p)
{
    Logger::println("AsmBlockStatement::toIR(): %s", loc.toChars());
    LOG_SCOPE;
    Logger::println("BEGIN ASM");

    // disable inlining by default
    if (!p->func()->decl->allowInlining)
        p->func()->setNeverInline();

    // create asm block structure
    assert(!p->asmBlock);
    IRAsmBlock* asmblock = new IRAsmBlock(this);
    assert(asmblock);
    p->asmBlock = asmblock;

    // do asm statements
    for (int i=0; i<statements->dim; i++)
    {
        Statement* s = (Statement*)statements->data[i];
        if (s) {
            s->toIR(p);
        }
    }

    // build forwarder for in-asm branches to external labels
    // this additional asm code sets the __llvm_jump_target variable
    // to a unique value that will identify the jump target in
    // a post-asm switch

    // maps each goto destination to its special value
    std::map<Identifier*, int> gotoToVal;

    // location of the special value determining the goto label
    // will be set if post-asm dispatcher block is needed
    llvm::AllocaInst* jump_target;

    {
        FuncDeclaration* fd = gIR->func()->decl;
        char* fdmangle = fd->mangle();

        // we use a simple static counter to make sure the new end labels are unique
        static size_t uniqueLabelsId = 0;
        std::ostringstream asmGotoEndLabel;
        asmGotoEndLabel << "." << fdmangle << "__llvm_asm_end" << uniqueLabelsId++;

        // initialize the setter statement we're going to build
        IRAsmStmt* outSetterStmt = new IRAsmStmt;
        std::string asmGotoEnd = "\n\tjmp "+asmGotoEndLabel.str()+"\n";
        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];

            // 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 we already set things up for this branch target, skip
            if(gotoToVal.find(a->isBranchToLabel) != gotoToVal.end())
                continue;

            // record that the jump needs to be handled in the post-asm dispatcher
            gotoToVal[a->isBranchToLabel] = n_goto;

            // 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);
            code << fdmangle << '_' << a->isBranchToLabel->string << ":\n\t";
            code << "movl $<<in" << n_goto << ">>, $<<out0>>\n";
            //FIXME: Store the value -> label mapping somewhere, so it can be referenced later
            outSetterStmt->in.push_back(DtoConstUint(n_goto));
            outSetterStmt->in_c += "i,";
            code << asmGotoEnd;

            ++n_goto;
        }
        if(code.str() != asmGotoEnd)
        {
            // finalize code
            outSetterStmt->code = code.str();
            outSetterStmt->code += asmGotoEndLabel.str()+":\n";

            // create storage for and initialize the temporary
            jump_target = DtoAlloca(LLType::Int32Ty, "__llvm_jump_target");
            gIR->ir->CreateStore(DtoConstUint(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;
    }


    // build a fall-off-end-properly asm statement

    FuncDeclaration* thisfunc = p->func()->decl;
    bool useabiret = false;
    p->asmBlock->asmBlock->abiret = NULL;
    if (thisfunc->fbody->endsWithAsm() == this && thisfunc->type->nextOf()->ty != Tvoid)
    {
        // there can't be goto forwarders in this case
        assert(gotoToVal.empty());
        emitABIReturnAsmStmt(asmblock, loc, thisfunc);
        useabiret = true;
    }


    // build asm block
    std::vector<LLValue*> outargs;
    std::vector<LLValue*> inargs;
    std::vector<const LLType*> outtypes;
    std::vector<const LLType*> intypes;
    std::string out_c;
    std::string in_c;
    std::string clobbers;
    std::string code;
    size_t asmIdx = asmblock->retn;

    Logger::println("do outputs");
    size_t n = asmblock->s.size();
    for (size_t i=0; i<n; ++i)
    {
        IRAsmStmt* a = asmblock->s[i];
        assert(a);
        size_t onn = a->out.size();
        for (size_t j=0; j<onn; ++j)
        {
            outargs.push_back(a->out[j]);
            outtypes.push_back(a->out[j]->getType());
        }
        if (!a->out_c.empty())
        {
            out_c += a->out_c;
        }
        remap_outargs(a->code, onn+a->in.size(), asmIdx);
        asmIdx += onn;
    }

    Logger::println("do inputs");
    for (size_t i=0; i<n; ++i)
    {
        IRAsmStmt* a = asmblock->s[i];
        assert(a);
        size_t inn = a->in.size();
        for (size_t j=0; j<inn; ++j)
        {
            inargs.push_back(a->in[j]);
            intypes.push_back(a->in[j]->getType());
        }
        if (!a->in_c.empty())
        {
            in_c += a->in_c;
        }
        remap_inargs(a->code, inn+a->out.size(), asmIdx);
        asmIdx += inn;
        if (!code.empty())
            code += "\n\t";
        code += a->code;
    }
    asmblock->s.clear();

    // append inputs
    out_c += in_c;

    // append clobbers
    typedef std::set<std::string>::iterator clobs_it;
    for (clobs_it i=asmblock->clobs.begin(); i!=asmblock->clobs.end(); ++i)
    {
        out_c += *i;
    }

    // remove excessive comma
    if (!out_c.empty())
        out_c.resize(out_c.size()-1);

    Logger::println("code = \"%s\"", code.c_str());
    Logger::println("constraints = \"%s\"", out_c.c_str());

    // build return types
    const LLType* retty;
    if (asmblock->retn)
        retty = asmblock->retty;
    else
        retty = llvm::Type::VoidTy;

    // build argument types
    std::vector<const LLType*> types;
    types.insert(types.end(), outtypes.begin(), outtypes.end());
    types.insert(types.end(), intypes.begin(), intypes.end());
    llvm::FunctionType* fty = llvm::FunctionType::get(retty, types, false);
    if (Logger::enabled())
        Logger::cout() << "function type = " << *fty << '\n';

    std::vector<LLValue*> args;
    args.insert(args.end(), outargs.begin(), outargs.end());
    args.insert(args.end(), inargs.begin(), inargs.end());

    if (Logger::enabled()) {
        Logger::cout() << "Arguments:" << '\n';
        Logger::indent();
        for (std::vector<LLValue*>::iterator b = args.begin(), i = b, e = args.end(); i != e; ++i) {
            llvm::OStream cout = Logger::cout();
            cout << '$' << (i - b) << " ==> " << **i;
            if (!llvm::isa<llvm::Instruction>(*i) && !llvm::isa<LLGlobalValue>(*i))
                cout << '\n';
        }
        Logger::undent();
    }

    llvm::InlineAsm* ia = llvm::InlineAsm::get(fty, code, out_c, true);

    llvm::CallInst* call = p->ir->CreateCall(ia, args.begin(), args.end(),
        retty == LLType::VoidTy ? "" : "asm");

    if (Logger::enabled())
        Logger::cout() << "Complete asm statement: " << *call << '\n';

    // capture abi return value
    if (useabiret)
    {
        IRAsmBlock* block = p->asmBlock;
        if (block->retfixup)
            block->asmBlock->abiret = (*block->retfixup)(p->ir, call);
        else if (p->asmBlock->retemu)
            block->asmBlock->abiret = DtoLoad(block->asmBlock->abiret);
        else
            block->asmBlock->abiret = call;
    }

    p->asmBlock = NULL;
    Logger::println("END ASM");

    // if asm contained external branches, emit goto forwarder code
    if(!gotoToVal.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, gotoToVal.size());

        // add all cases
        std::map<Identifier*, int>::iterator it, end = gotoToVal.end();
        for(it = gotoToVal.begin(); it != end; ++it)
        {
            llvm::BasicBlock* casebb = llvm::BasicBlock::Create("case", p->topfunc(), bb);
            sw->addCase(llvm::ConstantInt::get(llvm::IntegerType::get(32), it->second), casebb);

            p->scope() = IRScope(casebb,bb);
            DtoGoto(&loc, it->first, enclosinghandler, tf);
        }

        p->scope() = IRScope(bb,oldend);
    }
}

// the whole idea of this statement is to avoid the flattening
Statements* AsmBlockStatement::flatten(Scope* sc)
{
    return NULL;
}

Statement *AsmBlockStatement::syntaxCopy()
{
    Statements *a = new Statements();
    a->setDim(statements->dim);
    for (size_t i = 0; i < statements->dim; i++)
    {
        Statement *s = (Statement *)statements->data[i];
        if (s)
            s = s->syntaxCopy();
        a->data[i] = s;
    }
    AsmBlockStatement *cs = new AsmBlockStatement(loc, a);
    return cs;
}

// necessary for in-asm branches
Statement *AsmBlockStatement::semantic(Scope *sc)
{
    enclosinghandler = sc->tfOfTry;
    tf = sc->tf;

    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
    p->nakedAsm << "\t" << code->insnTemplate << 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);
    }
}