Esempio n. 1
0
void AsmBlockStatement::toIR(IRState* p)
{
    Logger::println("AsmBlockStatement::toIR(): %s", loc.toChars());
    LOG_SCOPE;

    // 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 (unsigned i=0; i<statements->dim; i++)
    {
        Statement* s = static_cast<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 = 0;

    {
        FuncDeclaration* fd = gIR->func()->decl;
        const 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;
        printLabelName(asmGotoEndLabel, fdmangle, "_llvm_asm_end");
        asmGotoEndLabel << 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);
            printLabelName(code, fdmangle, a->isBranchToLabel->string);
            code << ":\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(Type::tint32, "__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<LLType*> outtypes;
    std::vector<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
    LLType* retty;
    if (asmblock->retn)
        retty = asmblock->retty;
    else
        retty = llvm::Type::getVoidTy(gIR->context());

    // build argument types
    std::vector<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) {
            Stream 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,
        retty == LLType::getVoidTy(gIR->context()) ? "" : "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;

    // 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(gIR->context(), "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(gIR->context(), "case", p->topfunc(), bb);
            sw->addCase(LLConstantInt::get(llvm::IntegerType::get(gIR->context(), 32), it->second), casebb);

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

        p->scope() = IRScope(bb,oldend);
    }
}
Esempio n. 2
0
llvm::GlobalVariable * IrAggr::getInterfaceVtbl(BaseClass * b, bool new_instance, size_t interfaces_index)
{
    ClassGlobalMap::iterator it = interfaceVtblMap.find(b->base);
    if (it != interfaceVtblMap.end())
        return it->second;

    IF_LOG Logger::println("Building vtbl for implementation of interface %s in class %s",
        b->base->toPrettyChars(), aggrdecl->toPrettyChars());
    LOG_SCOPE;

    ClassDeclaration* cd = aggrdecl->isClassDeclaration();
    assert(cd && "not a class aggregate");

    FuncDeclarations vtbl_array;
    b->fillVtbl(cd, &vtbl_array, new_instance);

    std::vector<llvm::Constant*> constants;
    constants.reserve(vtbl_array.dim);

    if (!b->base->isCPPinterface()) { // skip interface info for CPP interfaces
        // start with the interface info
        VarDeclarationIter interfaces_idx(ClassDeclaration::classinfo->fields, 3);

        // index into the interfaces array
        llvm::Constant* idxs[2] = {
            DtoConstSize_t(0),
            DtoConstSize_t(interfaces_index)
        };

        llvm::Constant* c = llvm::ConstantExpr::getGetElementPtr(
            getInterfaceArraySymbol(), idxs, true);

        constants.push_back(c);
    }

    // add virtual function pointers
    size_t n = vtbl_array.dim;
    for (size_t i = b->base->vtblOffset(); i < n; i++)
    {
        Dsymbol* dsym = static_cast<Dsymbol*>(vtbl_array.data[i]);
        if (dsym == NULL)
        {
            // FIXME
            // why is this null?
            // happens for mini/s.d
            constants.push_back(getNullValue(getVoidPtrType()));
            continue;
        }

        FuncDeclaration* fd = dsym->isFuncDeclaration();
        assert(fd && "vtbl entry not a function");

        assert((!fd->isAbstract() || fd->fbody) &&
            "null symbol in interface implementation vtable");

        fd->codegen(Type::sir);
        assert(fd->ir.irFunc && "invalid vtbl function");

        LLFunction *fn = fd->ir.irFunc->func;

        // If the base is a cpp interface, 'this' parameter is a pointer to
        // the interface not the underlying object as expected. Instead of
        // the function, we place into the vtable a small wrapper, called thunk,
        // that casts 'this' to the object and then pass it to the real function.
        if (b->base->isCPPinterface()) {
            TypeFunction *f = (TypeFunction*)fd->type->toBasetype();
            assert(f->fty.arg_this);

            // create the thunk function
            OutBuffer name;
            name.writestring("Th");
            name.printf("%i", b->offset);
            name.writestring(fd->mangle());
            LLFunction *thunk = LLFunction::Create(isaFunction(fn->getType()->getContainedType(0)),
                                                 DtoLinkage(fd), name.toChars(), gIR->module);

            // create entry and end blocks
            llvm::BasicBlock* beginbb = llvm::BasicBlock::Create(gIR->context(), "entry", thunk);
            llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endentry", thunk);
            gIR->scopes.push_back(IRScope(beginbb, endbb));

            // copy the function parameters, so later we can pass them to the real function
            std::vector<LLValue*> args;
            llvm::Function::arg_iterator iarg = thunk->arg_begin();
            for (; iarg != thunk->arg_end(); ++iarg)
                args.push_back(iarg);

            // cast 'this' to Object
            LLValue* &thisArg = args[(f->fty.arg_sret == 0) ? 0 : 1];
            LLType* thisType = thisArg->getType();
            thisArg = DtoBitCast(thisArg, getVoidPtrType());
            thisArg = DtoGEP1(thisArg, DtoConstInt(-b->offset));
            thisArg = DtoBitCast(thisArg, thisType);

            // call the real vtbl function.
            LLValue *retVal = gIR->ir->CreateCall(fn, args);

            // return from the thunk
            if (thunk->getReturnType() == LLType::getVoidTy(gIR->context()))
                llvm::ReturnInst::Create(gIR->context(), beginbb);
            else
                llvm::ReturnInst::Create(gIR->context(), retVal, beginbb);

            // clean up
            gIR->scopes.pop_back();
            thunk->getBasicBlockList().pop_back();

            fn = thunk;
        }

        constants.push_back(fn);
    }

    // build the vtbl constant
    llvm::Constant* vtbl_constant = LLConstantStruct::getAnon(gIR->context(), constants, false);

    // create the global variable to hold it
    llvm::GlobalValue::LinkageTypes _linkage = DtoExternalLinkage(aggrdecl);

    std::string mangle("_D");
    mangle.append(cd->mangle());
    mangle.append("11__interface");
    mangle.append(b->base->mangle());
    mangle.append("6__vtblZ");

    llvm::GlobalVariable* GV = getOrCreateGlobal(cd->loc,
        *gIR->module,
        vtbl_constant->getType(),
        true,
        _linkage,
        vtbl_constant,
        mangle
    );

    // insert into the vtbl map
    interfaceVtblMap.insert(std::make_pair(b->base, GV));

    return GV;
}