mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-03 00:20:40 +03:00

Previously, the transitory state only needed and valid during generation of the LLVM IR for the function body was conflated with the general codegen metadata for the function declaration in IrFunction. There is further potential for cleanup regarding the use of gIR->func() and so on all over the code base, but this is out of scope of this commit, which is only concerned with those IrFunction members moved to FuncGenState. GitHub: Fixes #1661.
1968 lines
65 KiB
C++
1968 lines
65 KiB
C++
//===-- statements.cpp ----------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "init.h"
|
||
#include "mars.h"
|
||
#include "module.h"
|
||
#include "mtype.h"
|
||
#include "port.h"
|
||
#include "gen/abi.h"
|
||
#include "gen/arrays.h"
|
||
#include "gen/classes.h"
|
||
#include "gen/coverage.h"
|
||
#include "gen/dvalue.h"
|
||
#include "gen/funcgenstate.h"
|
||
#include "gen/irstate.h"
|
||
#include "gen/llvm.h"
|
||
#include "gen/llvmhelpers.h"
|
||
#include "gen/logger.h"
|
||
#include "gen/runtime.h"
|
||
#include "gen/tollvm.h"
|
||
#include "gen/ms-cxx-helper.h"
|
||
#include "ir/irfunction.h"
|
||
#include "ir/irmodule.h"
|
||
#include "llvm/IR/CFG.h"
|
||
#include "llvm/IR/InlineAsm.h"
|
||
#include <fstream>
|
||
#include <math.h>
|
||
#include <stdio.h>
|
||
|
||
// Need to include this after the other DMD includes because of missing
|
||
// dependencies.
|
||
#include "hdrgen.h"
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
// FIXME: Integrate these functions
|
||
void AsmStatement_toIR(AsmStatement *stmt, IRState *irs);
|
||
void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
// For sorting string switch cases lexicographically.
|
||
namespace {
|
||
bool compareCaseStrings(CaseStatement *lhs, CaseStatement *rhs) {
|
||
return lhs->exp->compare(rhs->exp) < 0;
|
||
}
|
||
};
|
||
|
||
static LLValue *call_string_switch_runtime(llvm::Value *table, Expression *e) {
|
||
Type *dt = e->type->toBasetype();
|
||
Type *dtnext = dt->nextOf()->toBasetype();
|
||
TY ty = dtnext->ty;
|
||
const char *fname;
|
||
if (ty == Tchar) {
|
||
fname = "_d_switch_string";
|
||
} else if (ty == Twchar) {
|
||
fname = "_d_switch_ustring";
|
||
} else if (ty == Tdchar) {
|
||
fname = "_d_switch_dstring";
|
||
} else {
|
||
llvm_unreachable("not char/wchar/dchar");
|
||
}
|
||
|
||
llvm::Function *fn = getRuntimeFunction(e->loc, gIR->module, fname);
|
||
|
||
IF_LOG {
|
||
Logger::cout() << *table->getType() << '\n';
|
||
Logger::cout() << *fn->getFunctionType()->getParamType(0) << '\n';
|
||
}
|
||
assert(table->getType() == fn->getFunctionType()->getParamType(0));
|
||
|
||
DValue *val = toElemDtor(e);
|
||
LLValue *llval = DtoRVal(val);
|
||
assert(llval->getType() == fn->getFunctionType()->getParamType(1));
|
||
|
||
LLCallSite call = gIR->CreateCallOrInvoke(fn, table, llval);
|
||
|
||
return call.getInstruction();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
class ToIRVisitor : public Visitor {
|
||
IRState *irs;
|
||
|
||
public:
|
||
explicit ToIRVisitor(IRState *irs) : irs(irs) {}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
// Import all functions from class Visitor
|
||
using Visitor::visit;
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(CompoundStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("CompoundStatement::toIR(): %s",
|
||
stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
for (auto s : *stmt->statements) {
|
||
if (s) {
|
||
s->accept(this);
|
||
}
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ReturnStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("ReturnStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
// emit dwarf stop point
|
||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
|
||
// The LLVM value to return, or null for void returns.
|
||
LLValue *returnValue = nullptr;
|
||
|
||
auto &funcGen = irs->funcGen();
|
||
IrFunction *const f = &funcGen.irFunc;
|
||
FuncDeclaration *const fd = f->decl;
|
||
LLFunction *const llFunc = f->func;
|
||
|
||
// is there a return value expression?
|
||
if (stmt->exp || (!stmt->exp && (llFunc == irs->mainFunc))) {
|
||
// if the function's return type is void, it uses sret
|
||
if (llFunc->getReturnType() == LLType::getVoidTy(irs->context())) {
|
||
assert(!f->type->isref);
|
||
|
||
LLValue *sretPointer = getIrFunc(fd)->sretArg;
|
||
assert(sretPointer);
|
||
|
||
assert(!f->irFty.arg_sret->rewrite &&
|
||
"ABI shouldn't have to rewrite sret returns");
|
||
DLValue returnValue(f->type->next, sretPointer);
|
||
|
||
// try to construct the return value in-place
|
||
const auto initialCleanupScope = funcGen.scopes.currentCleanupScope();
|
||
const bool constructed = toInPlaceConstruction(&returnValue, stmt->exp);
|
||
if (constructed) {
|
||
// cleanup manually (otherwise done by toElemDtor())
|
||
if (funcGen.scopes.currentCleanupScope() != initialCleanupScope) {
|
||
auto endbb = llvm::BasicBlock::Create(
|
||
irs->context(), "inPlaceSretConstruct.success", llFunc);
|
||
funcGen.scopes.runCleanups(initialCleanupScope, endbb);
|
||
funcGen.scopes.popCleanups(initialCleanupScope);
|
||
irs->scope() = IRScope(endbb);
|
||
}
|
||
} else {
|
||
DValue *e = toElemDtor(stmt->exp);
|
||
|
||
// store the return value unless NRVO already used the sret pointer
|
||
if (!e->isLVal() || DtoLVal(e) != sretPointer) {
|
||
// call postblit if the expression is a D lvalue
|
||
// exceptions: NRVO and special __result variable (out contracts)
|
||
bool doPostblit = !(fd->nrvo_can && fd->nrvo_var);
|
||
if (doPostblit && stmt->exp->op == TOKvar) {
|
||
auto ve = static_cast<VarExp *>(stmt->exp);
|
||
if (ve->var->isResult())
|
||
doPostblit = false;
|
||
}
|
||
|
||
DtoAssign(stmt->loc, &returnValue, e, TOKblit);
|
||
if (doPostblit)
|
||
callPostblit(stmt->loc, stmt->exp, sretPointer);
|
||
}
|
||
}
|
||
} else {
|
||
// the return type is not void, so this is a normal "register" return
|
||
if (!stmt->exp && (llFunc == irs->mainFunc)) {
|
||
returnValue =
|
||
LLConstant::getNullValue(irs->mainFunc->getReturnType());
|
||
} else {
|
||
if (stmt->exp->op == TOKnull) {
|
||
stmt->exp->type = f->type->next;
|
||
}
|
||
DValue *dval = nullptr;
|
||
// call postblit if necessary
|
||
if (!f->type->isref) {
|
||
dval = toElemDtor(stmt->exp);
|
||
LLValue *vthis =
|
||
(DtoIsInMemoryOnly(dval->type) ? DtoLVal(dval) : DtoRVal(dval));
|
||
callPostblit(stmt->loc, stmt->exp, vthis);
|
||
} else {
|
||
Expression *ae = stmt->exp;
|
||
dval = toElemDtor(ae);
|
||
}
|
||
// do abi specific transformations on the return value
|
||
returnValue = getIrFunc(fd)->irFty.putRet(dval);
|
||
}
|
||
|
||
// Hack around LDC assuming structs and static arrays are in memory:
|
||
// If the function returns a struct or a static array, and the return
|
||
// value is a pointer to a struct or a static array, load from it
|
||
// before returning.
|
||
if (returnValue->getType() != llFunc->getReturnType() &&
|
||
DtoIsInMemoryOnly(f->type->next) &&
|
||
isaPointer(returnValue->getType())) {
|
||
Logger::println("Loading value for return");
|
||
returnValue = DtoLoad(returnValue);
|
||
}
|
||
|
||
// can happen for classes and void main
|
||
if (returnValue->getType() != llFunc->getReturnType()) {
|
||
// for the main function this only happens if it is declared as void
|
||
// and then contains a return (exp); statement. Since the actual
|
||
// return type remains i32, we just throw away the exp value
|
||
// and return 0 instead
|
||
// if we're not in main, just bitcast
|
||
if (llFunc == irs->mainFunc) {
|
||
returnValue =
|
||
LLConstant::getNullValue(irs->mainFunc->getReturnType());
|
||
} else {
|
||
returnValue =
|
||
irs->ir->CreateBitCast(returnValue, llFunc->getReturnType());
|
||
}
|
||
|
||
IF_LOG Logger::cout() << "return value after cast: " << *returnValue
|
||
<< '\n';
|
||
}
|
||
}
|
||
} else {
|
||
// no return value expression means it's a void function.
|
||
assert(llFunc->getReturnType() == LLType::getVoidTy(irs->context()));
|
||
}
|
||
|
||
// If there are no cleanups to run, we try to keep the IR simple and
|
||
// just directly emit the return instruction. If there are cleanups to run
|
||
// first, we need to store the return value to a stack slot, in which case
|
||
// we can use a shared return bb for all these cases.
|
||
const bool useRetValSlot = funcGen.scopes.currentCleanupScope() != 0;
|
||
const bool sharedRetBlockExists = !!funcGen.retBlock;
|
||
if (useRetValSlot) {
|
||
if (!sharedRetBlockExists) {
|
||
funcGen.retBlock =
|
||
llvm::BasicBlock::Create(irs->context(), "return", llFunc);
|
||
if (returnValue) {
|
||
funcGen.retValSlot =
|
||
DtoRawAlloca(returnValue->getType(), 0, "return.slot");
|
||
}
|
||
}
|
||
|
||
// Create the store to the slot at the end of our current basic
|
||
// block, before we run the cleanups.
|
||
if (returnValue) {
|
||
irs->ir->CreateStore(returnValue, funcGen.retValSlot);
|
||
}
|
||
|
||
// Now run the cleanups.
|
||
funcGen.scopes.runAllCleanups(funcGen.retBlock);
|
||
|
||
irs->scope() = IRScope(funcGen.retBlock);
|
||
}
|
||
|
||
// If we need to emit the actual return instruction, do so.
|
||
if (!useRetValSlot || !sharedRetBlockExists) {
|
||
if (returnValue) {
|
||
// Hack: the frontend generates 'return 0;' as last statement of
|
||
// 'void main()'. But the debug location is missing. Use the end
|
||
// of function as debug location.
|
||
if (fd->isMain() && !stmt->loc.linnum) {
|
||
irs->DBuilder.EmitStopPoint(fd->endloc);
|
||
}
|
||
|
||
irs->ir->CreateRet(useRetValSlot ? DtoLoad(funcGen.retValSlot)
|
||
: returnValue);
|
||
} else {
|
||
irs->ir->CreateRetVoid();
|
||
}
|
||
}
|
||
|
||
// Finally, create a new predecessor-less dummy bb as the current IRScope
|
||
// to make sure we do not emit any extra instructions after the terminating
|
||
// instruction (ret or branch to return bb), which would be illegal IR.
|
||
irs->scope() = IRScope(
|
||
llvm::BasicBlock::Create(gIR->context(), "dummy.afterreturn", llFunc));
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ExpStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("ExpStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
// emit dwarf stop point
|
||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
|
||
if (stmt->exp) {
|
||
elem *e;
|
||
// a cast(void) around the expression is allowed, but doesn't require any
|
||
// code
|
||
if (stmt->exp->op == TOKcast && stmt->exp->type == Type::tvoid) {
|
||
CastExp *cexp = static_cast<CastExp *>(stmt->exp);
|
||
e = toElemDtor(cexp->e1);
|
||
} else {
|
||
e = toElemDtor(stmt->exp);
|
||
}
|
||
delete e;
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(IfStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("IfStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
auto truecount = PGO.getRegionCount(stmt);
|
||
auto elsecount = PGO.getCurrentRegionCount() - truecount;
|
||
auto brweights = PGO.createProfileWeights(truecount, elsecount);
|
||
|
||
// start a dwarf lexical block
|
||
irs->DBuilder.EmitBlockStart(stmt->loc);
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
|
||
if (stmt->match) {
|
||
DtoRawVarDeclaration(stmt->match);
|
||
}
|
||
|
||
DValue *cond_e = toElemDtor(stmt->condition);
|
||
LLValue *cond_val = DtoRVal(cond_e);
|
||
|
||
llvm::BasicBlock *ifbb =
|
||
llvm::BasicBlock::Create(irs->context(), "if", irs->topfunc());
|
||
llvm::BasicBlock *endbb =
|
||
llvm::BasicBlock::Create(irs->context(), "endif", irs->topfunc());
|
||
llvm::BasicBlock *elsebb =
|
||
stmt->elsebody ? llvm::BasicBlock::Create(irs->context(), "else",
|
||
irs->topfunc(), endbb)
|
||
: endbb;
|
||
|
||
if (cond_val->getType() != LLType::getInt1Ty(irs->context())) {
|
||
IF_LOG Logger::cout() << "if conditional: " << *cond_val << '\n';
|
||
cond_val = DtoRVal(DtoCast(stmt->loc, cond_e, Type::tbool));
|
||
}
|
||
auto brinstr =
|
||
llvm::BranchInst::Create(ifbb, elsebb, cond_val, irs->scopebb());
|
||
PGO.addBranchWeights(brinstr, brweights);
|
||
|
||
// replace current scope
|
||
irs->scope() = IRScope(ifbb);
|
||
|
||
// do scoped statements
|
||
|
||
if (stmt->ifbody) {
|
||
irs->DBuilder.EmitBlockStart(stmt->ifbody->loc);
|
||
PGO.emitCounterIncrement(stmt);
|
||
stmt->ifbody->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(endbb, irs->scopebb());
|
||
}
|
||
|
||
if (stmt->elsebody) {
|
||
irs->scope() = IRScope(elsebb);
|
||
irs->DBuilder.EmitBlockStart(stmt->elsebody->loc);
|
||
stmt->elsebody->accept(this);
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(endbb, irs->scopebb());
|
||
}
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
// end the dwarf lexical block
|
||
irs->DBuilder.EmitBlockEnd();
|
||
|
||
// rewrite the scope
|
||
irs->scope() = IRScope(endbb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ScopeStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("ScopeStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
if (stmt->statement) {
|
||
irs->DBuilder.EmitBlockStart(stmt->statement->loc);
|
||
stmt->statement->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(WhileStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("WhileStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
// start a dwarf lexical block
|
||
irs->DBuilder.EmitBlockStart(stmt->loc);
|
||
|
||
// create while blocks
|
||
|
||
llvm::BasicBlock *whilebb =
|
||
llvm::BasicBlock::Create(irs->context(), "whilecond", irs->topfunc());
|
||
llvm::BasicBlock *whilebodybb =
|
||
llvm::BasicBlock::Create(irs->context(), "whilebody", irs->topfunc());
|
||
llvm::BasicBlock *endbb =
|
||
llvm::BasicBlock::Create(irs->context(), "endwhile", irs->topfunc());
|
||
|
||
// move into the while block
|
||
irs->ir->CreateBr(whilebb);
|
||
|
||
// replace current scope
|
||
irs->scope() = IRScope(whilebb);
|
||
|
||
// create the condition
|
||
emitCoverageLinecountInc(stmt->condition->loc);
|
||
DValue *cond_e = toElemDtor(stmt->condition);
|
||
LLValue *cond_val = DtoRVal(DtoCast(stmt->loc, cond_e, Type::tbool));
|
||
delete cond_e;
|
||
|
||
// conditional branch
|
||
auto branchinst =
|
||
llvm::BranchInst::Create(whilebodybb, endbb, cond_val, irs->scopebb());
|
||
{
|
||
auto loopcount = PGO.getRegionCount(stmt);
|
||
auto brweights =
|
||
PGO.createProfileWeightsWhileLoop(stmt->condition, loopcount);
|
||
PGO.addBranchWeights(branchinst, brweights);
|
||
}
|
||
|
||
// rewrite scope
|
||
irs->scope() = IRScope(whilebodybb);
|
||
|
||
// while body code
|
||
irs->funcGen().scopes.pushLoopTarget(stmt, whilebb, endbb);
|
||
PGO.emitCounterIncrement(stmt);
|
||
if (stmt->_body) {
|
||
stmt->_body->accept(this);
|
||
}
|
||
irs->funcGen().scopes.popLoopTarget();
|
||
|
||
// loop
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(whilebb, irs->scopebb());
|
||
}
|
||
|
||
// rewrite the scope
|
||
irs->scope() = IRScope(endbb);
|
||
|
||
// end the dwarf lexical block
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(DoStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("DoStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
auto entryCount = PGO.setCurrentStmt(stmt);
|
||
|
||
// start a dwarf lexical block
|
||
irs->DBuilder.EmitBlockStart(stmt->loc);
|
||
|
||
// create while blocks
|
||
llvm::BasicBlock *dowhilebb =
|
||
llvm::BasicBlock::Create(irs->context(), "dowhile", irs->topfunc());
|
||
llvm::BasicBlock *condbb =
|
||
llvm::BasicBlock::Create(irs->context(), "dowhilecond", irs->topfunc());
|
||
llvm::BasicBlock *endbb =
|
||
llvm::BasicBlock::Create(irs->context(), "enddowhile", irs->topfunc());
|
||
|
||
// move into the while block
|
||
assert(!irs->scopereturned());
|
||
llvm::BranchInst::Create(dowhilebb, irs->scopebb());
|
||
|
||
// replace current scope
|
||
irs->scope() = IRScope(dowhilebb);
|
||
|
||
// do-while body code
|
||
irs->funcGen().scopes.pushLoopTarget(stmt, condbb, endbb);
|
||
PGO.emitCounterIncrement(stmt);
|
||
if (stmt->_body) {
|
||
stmt->_body->accept(this);
|
||
}
|
||
irs->funcGen().scopes.popLoopTarget();
|
||
|
||
// branch to condition block
|
||
llvm::BranchInst::Create(condbb, irs->scopebb());
|
||
irs->scope() = IRScope(condbb);
|
||
|
||
// create the condition
|
||
emitCoverageLinecountInc(stmt->condition->loc);
|
||
DValue *cond_e = toElemDtor(stmt->condition);
|
||
LLValue *cond_val = DtoRVal(DtoCast(stmt->loc, cond_e, Type::tbool));
|
||
delete cond_e;
|
||
|
||
// conditional branch
|
||
auto branchinst =
|
||
llvm::BranchInst::Create(dowhilebb, endbb, cond_val, irs->scopebb());
|
||
{
|
||
// The region counter includes fallthrough from the previous statement.
|
||
// Subtract parent count to get the true branch count of the loop
|
||
// conditional.
|
||
auto loopcount = PGO.getRegionCount(stmt) - entryCount;
|
||
auto brweights =
|
||
PGO.createProfileWeightsWhileLoop(stmt->condition, loopcount);
|
||
PGO.addBranchWeights(branchinst, brweights);
|
||
}
|
||
|
||
// Order the blocks in a logical order in IR
|
||
condbb->moveAfter(&irs->topfunc()->back());
|
||
endbb->moveAfter(condbb);
|
||
|
||
// rewrite the scope
|
||
irs->scope() = IRScope(endbb);
|
||
|
||
// end the dwarf lexical block
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ForStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("ForStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
// start new dwarf lexical block
|
||
irs->DBuilder.EmitBlockStart(stmt->loc);
|
||
|
||
// create for blocks
|
||
llvm::BasicBlock *forbb =
|
||
llvm::BasicBlock::Create(irs->context(), "forcond", irs->topfunc());
|
||
llvm::BasicBlock *forbodybb =
|
||
llvm::BasicBlock::Create(irs->context(), "forbody", irs->topfunc());
|
||
llvm::BasicBlock *forincbb =
|
||
llvm::BasicBlock::Create(irs->context(), "forinc", irs->topfunc());
|
||
llvm::BasicBlock *endbb =
|
||
llvm::BasicBlock::Create(irs->context(), "endfor", irs->topfunc());
|
||
|
||
// init
|
||
if (stmt->_init != nullptr) {
|
||
stmt->_init->accept(this);
|
||
}
|
||
|
||
// move into the for condition block, ie. start the loop
|
||
assert(!irs->scopereturned());
|
||
llvm::BranchInst::Create(forbb, irs->scopebb());
|
||
|
||
// In case of loops that have been rewritten to a composite statement
|
||
// containing the initializers and then the actual loop, we need to
|
||
// register the former as target scope start.
|
||
Statement *scopeStart = stmt->getRelatedLabeled();
|
||
while (ScopeStatement *scope = scopeStart->isScopeStatement()) {
|
||
scopeStart = scope->statement;
|
||
}
|
||
irs->funcGen().scopes.pushLoopTarget(scopeStart, forincbb, endbb);
|
||
|
||
// replace current scope
|
||
irs->scope() = IRScope(forbb);
|
||
|
||
// create the condition
|
||
llvm::Value *cond_val;
|
||
if (stmt->condition) {
|
||
emitCoverageLinecountInc(stmt->condition->loc);
|
||
DValue *cond_e = toElemDtor(stmt->condition);
|
||
cond_val = DtoRVal(DtoCast(stmt->loc, cond_e, Type::tbool));
|
||
delete cond_e;
|
||
} else {
|
||
cond_val = DtoConstBool(true);
|
||
}
|
||
|
||
// conditional branch
|
||
assert(!irs->scopereturned());
|
||
auto branchinst =
|
||
llvm::BranchInst::Create(forbodybb, endbb, cond_val, irs->scopebb());
|
||
{
|
||
auto brweights = PGO.createProfileWeightsForLoop(stmt);
|
||
PGO.addBranchWeights(branchinst, brweights);
|
||
}
|
||
|
||
// rewrite scope
|
||
irs->scope() = IRScope(forbodybb);
|
||
|
||
// do for body code
|
||
PGO.emitCounterIncrement(stmt);
|
||
if (stmt->_body) {
|
||
stmt->_body->accept(this);
|
||
}
|
||
|
||
// Order the blocks in a logical order in IR
|
||
forincbb->moveAfter(&irs->topfunc()->back());
|
||
endbb->moveAfter(forincbb);
|
||
|
||
// move into the for increment block
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(forincbb, irs->scopebb());
|
||
}
|
||
irs->scope() = IRScope(forincbb);
|
||
|
||
// increment
|
||
if (stmt->increment) {
|
||
emitCoverageLinecountInc(stmt->increment->loc);
|
||
DValue *inc = toElemDtor(stmt->increment);
|
||
delete inc;
|
||
}
|
||
|
||
// loop
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(forbb, irs->scopebb());
|
||
}
|
||
|
||
irs->funcGen().scopes.popLoopTarget();
|
||
|
||
// rewrite the scope
|
||
irs->scope() = IRScope(endbb);
|
||
|
||
// end the dwarf lexical block
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(BreakStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("BreakStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
// don't emit two terminators in a row
|
||
// happens just before DMD generated default statements if the last case
|
||
// terminates
|
||
if (irs->scopereturned()) {
|
||
return;
|
||
}
|
||
|
||
// emit dwarf stop point
|
||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
|
||
if (stmt->ident) {
|
||
IF_LOG Logger::println("ident = %s", stmt->ident->toChars());
|
||
|
||
// Get the loop or break statement the label refers to
|
||
Statement *targetStatement = stmt->target->statement;
|
||
ScopeStatement *tmp;
|
||
while ((tmp = targetStatement->isScopeStatement())) {
|
||
targetStatement = tmp->statement;
|
||
}
|
||
|
||
irs->funcGen().scopes.breakToStatement(targetStatement);
|
||
} else {
|
||
irs->funcGen().scopes.breakToClosest();
|
||
}
|
||
|
||
// the break terminated this basicblock, start a new one
|
||
llvm::BasicBlock *bb =
|
||
llvm::BasicBlock::Create(irs->context(), "afterbreak", irs->topfunc());
|
||
irs->scope() = IRScope(bb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ContinueStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("ContinueStatement::toIR(): %s",
|
||
stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
// emit dwarf stop point
|
||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
|
||
if (stmt->ident) {
|
||
IF_LOG Logger::println("ident = %s", stmt->ident->toChars());
|
||
|
||
// get the loop statement the label refers to
|
||
Statement *targetLoopStatement = stmt->target->statement;
|
||
ScopeStatement *tmp;
|
||
while ((tmp = targetLoopStatement->isScopeStatement())) {
|
||
targetLoopStatement = tmp->statement;
|
||
}
|
||
|
||
irs->funcGen().scopes.continueWithLoop(targetLoopStatement);
|
||
} else {
|
||
irs->funcGen().scopes.continueWithClosest();
|
||
}
|
||
|
||
// the break terminated this basicblock, start a new one
|
||
llvm::BasicBlock *bb =
|
||
llvm::BasicBlock::Create(irs->context(), "afterbreak", irs->topfunc());
|
||
irs->scope() = IRScope(bb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(OnScopeStatement *stmt) LLVM_OVERRIDE {
|
||
stmt->error("Internal Compiler Error: OnScopeStatement should have been "
|
||
"lowered by frontend.");
|
||
fatal();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(TryFinallyStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("TryFinallyStatement::toIR(): %s",
|
||
stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
/*auto entryCount = */ PGO.setCurrentStmt(stmt);
|
||
|
||
// emit dwarf stop point
|
||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||
|
||
// We only need to consider exception handling/cleanup issues if there
|
||
// is both a try and a finally block. If not, just directly emit what
|
||
// is present.
|
||
if (!stmt->_body || !stmt->finalbody) {
|
||
if (stmt->_body) {
|
||
irs->DBuilder.EmitBlockStart(stmt->_body->loc);
|
||
stmt->_body->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
} else if (stmt->finalbody) {
|
||
irs->DBuilder.EmitBlockStart(stmt->finalbody->loc);
|
||
stmt->finalbody->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
return;
|
||
}
|
||
|
||
// We'll append the "try" part to the current basic block later. No need
|
||
// for an extra one (we'd need to branch to it unconditionally anyway).
|
||
llvm::BasicBlock *trybb = irs->scopebb();
|
||
|
||
// Emit the finally block and set up the cleanup scope for it.
|
||
llvm::BasicBlock *finallybb =
|
||
llvm::BasicBlock::Create(irs->context(), "finally", irs->topfunc());
|
||
irs->scope() = IRScope(finallybb);
|
||
irs->DBuilder.EmitBlockStart(stmt->finalbody->loc);
|
||
stmt->finalbody->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
|
||
CleanupCursor cleanupBefore = irs->funcGen().scopes.currentCleanupScope();
|
||
irs->funcGen().scopes.pushCleanup(finallybb, irs->scopebb());
|
||
|
||
// Emit the try block.
|
||
irs->scope() = IRScope(trybb);
|
||
|
||
assert(stmt->_body);
|
||
irs->DBuilder.EmitBlockStart(stmt->_body->loc);
|
||
stmt->_body->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
|
||
// Create a block to branch to after successfully running the try block
|
||
// and any cleanups.
|
||
if (!irs->scopereturned()) {
|
||
llvm::BasicBlock *successbb = llvm::BasicBlock::Create(
|
||
irs->context(), "try.success", irs->topfunc());
|
||
irs->funcGen().scopes.runCleanups(cleanupBefore, successbb);
|
||
irs->scope() = IRScope(successbb);
|
||
// PGO counter tracks the continuation of the try-finally statement
|
||
PGO.emitCounterIncrement(stmt);
|
||
}
|
||
irs->funcGen().scopes.popCleanups(cleanupBefore);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
#if LDC_LLVM_VER >= 308
|
||
void emitBeginCatchMSVCEH(Catch *ctch, llvm::BasicBlock *endbb,
|
||
llvm::CatchSwitchInst *catchSwitchInst) {
|
||
VarDeclaration *var = ctch->var;
|
||
// The MSVC/x86 build uses C++ exception handling
|
||
// This needs a series of catch pads to match the exception
|
||
// and the catch handler must be terminated by a catch return instruction
|
||
LLValue *exnObj = nullptr;
|
||
LLValue *cpyObj = nullptr;
|
||
LLValue *typeDesc = nullptr;
|
||
LLValue *clssInfo = nullptr;
|
||
if (var) {
|
||
// alloca storage for the variable, it always needs a place on the stack
|
||
// do not initialize, this will be done by the C++ exception handler
|
||
var->_init = nullptr;
|
||
|
||
// redirect scope to avoid the generation of debug info before the
|
||
// catchpad
|
||
IRScope save = irs->scope();
|
||
irs->scope() = IRScope(gIR->topallocapoint()->getParent());
|
||
irs->scope().builder.SetInsertPoint(gIR->topallocapoint());
|
||
DtoDeclarationExp(var);
|
||
|
||
// catch handler will be outlined, so always treat as a nested reference
|
||
exnObj = getIrValue(var);
|
||
|
||
if (var->nestedrefs.dim) {
|
||
// if variable needed in a closure, use a stack temporary and copy it
|
||
// when caught
|
||
cpyObj = exnObj;
|
||
exnObj = DtoAlloca(var->type, "exnObj");
|
||
}
|
||
irs->scope() = save;
|
||
irs->DBuilder.EmitStopPoint(ctch->loc); // re-set debug loc after the
|
||
// SetInsertPoint(allocaInst) call
|
||
} else if (ctch->type) {
|
||
// catch without var
|
||
exnObj = DtoAlloca(ctch->type, "exnObj");
|
||
} else {
|
||
// catch all
|
||
exnObj = LLConstant::getNullValue(getVoidPtrType());
|
||
}
|
||
|
||
if (ctch->type) {
|
||
ClassDeclaration *cd = ctch->type->toBasetype()->isClassHandle();
|
||
typeDesc = getTypeDescriptor(*irs, cd);
|
||
clssInfo = getIrAggr(cd)->getClassInfoSymbol();
|
||
} else {
|
||
// catch all
|
||
typeDesc = LLConstant::getNullValue(getVoidPtrType());
|
||
clssInfo = LLConstant::getNullValue(DtoType(Type::typeinfoclass->type));
|
||
}
|
||
|
||
// "catchpad within %switch [TypeDescriptor, 0, &caughtObject]" must be
|
||
// first instruction
|
||
int flags = var ? 0 : 64; // just mimicking clang here
|
||
LLValue *args[] = {typeDesc, DtoConstUint(flags), exnObj};
|
||
auto catchpad = irs->ir->CreateCatchPad(
|
||
catchSwitchInst, llvm::ArrayRef<LLValue *>(args), "");
|
||
catchSwitchInst->addHandler(irs->scopebb());
|
||
|
||
if (cpyObj) {
|
||
// assign the caught exception to the location in the closure
|
||
auto val = irs->ir->CreateLoad(exnObj);
|
||
irs->ir->CreateStore(val, cpyObj);
|
||
exnObj = cpyObj;
|
||
}
|
||
|
||
// Exceptions are never rethrown by D code (but thrown again), so
|
||
// we can leave the catch handler right away and continue execution
|
||
// outside the catch funclet
|
||
llvm::BasicBlock *catchhandler = llvm::BasicBlock::Create(
|
||
irs->context(), "catchhandler", irs->topfunc());
|
||
llvm::CatchReturnInst::Create(catchpad, catchhandler, irs->scopebb());
|
||
irs->scope() = IRScope(catchhandler);
|
||
auto enterCatchFn =
|
||
getRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch");
|
||
irs->CreateCallOrInvoke(enterCatchFn, DtoBitCast(exnObj, getVoidPtrType()),
|
||
clssInfo);
|
||
}
|
||
#endif
|
||
|
||
void visit(TryCatchStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("TryCatchStatement::toIR(): %s",
|
||
stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
auto entryCount = PGO.setCurrentStmt(stmt);
|
||
|
||
// Emit dwarf stop point
|
||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||
|
||
// We'll append the "try" part to the current basic block later. No need
|
||
// for an extra one (we'd need to branch to it unconditionally anyway).
|
||
llvm::BasicBlock *trybb = irs->scopebb();
|
||
|
||
// Create a basic block to branch to after leaving the try or an
|
||
// associated catch block successfully.
|
||
llvm::BasicBlock *endbb = llvm::BasicBlock::Create(
|
||
irs->context(), "try.success.or.caught", irs->topfunc());
|
||
|
||
assert(stmt->catches);
|
||
|
||
struct CatchBlock {
|
||
ClassDeclaration *classdecl;
|
||
llvm::BasicBlock *BB;
|
||
uint64_t catchcount;
|
||
};
|
||
|
||
llvm::SmallVector<CatchBlock, 6> catchBlocks;
|
||
catchBlocks.reserve(stmt->catches->dim);
|
||
|
||
#if LDC_LLVM_VER >= 308
|
||
if (useMSVCEH()) {
|
||
auto &scopes = irs->funcGen().scopes;
|
||
auto catchSwitchBlock = llvm::BasicBlock::Create(
|
||
irs->context(), "catch.dispatch", irs->topfunc());
|
||
llvm::BasicBlock *unwindto =
|
||
scopes.currentCleanupScope() > 0 || scopes.currentCatchScope() > 0
|
||
? scopes.getLandingPad()
|
||
: nullptr;
|
||
auto funclet = scopes.getFunclet();
|
||
auto catchSwitchInst = llvm::CatchSwitchInst::Create(
|
||
funclet ? funclet : llvm::ConstantTokenNone::get(irs->context()),
|
||
unwindto, stmt->catches->dim, "", catchSwitchBlock);
|
||
|
||
for (auto it = stmt->catches->begin(), end = stmt->catches->end();
|
||
it != end; ++it) {
|
||
llvm::BasicBlock *catchBB = llvm::BasicBlock::Create(
|
||
irs->context(), llvm::Twine("catch.") + (*it)->type->toChars(),
|
||
irs->topfunc(), endbb);
|
||
|
||
irs->scope() = IRScope(catchBB);
|
||
irs->DBuilder.EmitBlockStart((*it)->loc);
|
||
PGO.emitCounterIncrement(*it);
|
||
|
||
emitBeginCatchMSVCEH(*it, endbb, catchSwitchInst);
|
||
|
||
// Emit handler, if there is one. The handler is zero, for instance,
|
||
// when building 'catch { debug foo(); }' in non-debug mode.
|
||
if ((*it)->handler) {
|
||
Statement_toIR((*it)->handler, irs);
|
||
}
|
||
|
||
if (!irs->scopereturned()) {
|
||
irs->ir->CreateBr(endbb);
|
||
}
|
||
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
// TODO: PGO has not yet been implemented for MSVC EH, set catchCount
|
||
// temporarily to 0
|
||
uint64_t catchCount = 0;
|
||
|
||
CatchBlock cb = {nullptr, catchSwitchBlock, catchCount};
|
||
catchBlocks.push_back(cb); // just for cleanup
|
||
scopes.pushCatch(nullptr, catchSwitchBlock);
|
||
|
||
// if no landing pad is created, the catch blocks are unused, but
|
||
// the verifier complains if there are catchpads without personality
|
||
// so we can just set it unconditionally
|
||
if (!irs->func()->func->hasPersonalityFn()) {
|
||
const char *personality = "__CxxFrameHandler3";
|
||
LLFunction *personalityFn =
|
||
getRuntimeFunction(Loc(), irs->module, personality);
|
||
irs->func()->func->setPersonalityFn(personalityFn);
|
||
}
|
||
} else
|
||
#endif
|
||
{
|
||
for (Catches::reverse_iterator it = stmt->catches->rbegin(),
|
||
end = stmt->catches->rend();
|
||
it != end; ++it) {
|
||
llvm::BasicBlock *catchBB = llvm::BasicBlock::Create(
|
||
irs->context(), llvm::Twine("catch.") + (*it)->type->toChars(),
|
||
irs->topfunc(), endbb);
|
||
|
||
irs->scope() = IRScope(catchBB);
|
||
irs->DBuilder.EmitBlockStart((*it)->loc);
|
||
PGO.emitCounterIncrement(*it);
|
||
|
||
const auto enterCatchFn =
|
||
getRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch");
|
||
auto ptr = DtoLoad(irs->funcGen().getOrCreateEhPtrSlot());
|
||
auto throwableObj = irs->ir->CreateCall(enterCatchFn, ptr);
|
||
|
||
// For catches that use the Throwable object, create storage for it.
|
||
// We will set it in the code that branches from the landing pads
|
||
// (there might be more than one) to catchBB.
|
||
auto var = (*it)->var;
|
||
if (var) {
|
||
// This will alloca if we haven't already and take care of nested refs
|
||
// if there are any.
|
||
DtoDeclarationExp(var);
|
||
|
||
// Copy the exception reference over from the _d_eh_enter_catch return
|
||
// value.
|
||
DtoStore(DtoBitCast(throwableObj, DtoType((*it)->var->type)),
|
||
getIrLocal(var)->value);
|
||
}
|
||
|
||
// Emit handler, if there is one. The handler is zero, for instance,
|
||
// when building 'catch { debug foo(); }' in non-debug mode.
|
||
if ((*it)->handler) {
|
||
Statement_toIR((*it)->handler, irs);
|
||
}
|
||
|
||
if (!irs->scopereturned()) {
|
||
irs->ir->CreateBr(endbb);
|
||
}
|
||
|
||
irs->DBuilder.EmitBlockEnd();
|
||
|
||
// PGO information, currently unused
|
||
auto catchCount = PGO.getRegionCount(*it);
|
||
|
||
CatchBlock cb = {(*it)->type->toBasetype()->isClassHandle(), catchBB,
|
||
catchCount};
|
||
catchBlocks.push_back(cb);
|
||
}
|
||
|
||
// Total number of uncaught exceptions is equal to the execution count at
|
||
// the start of the try block minus the one after the continuation.
|
||
// uncaughtCount keeps track of the exception type mismatch count while
|
||
// iterating through the catchBlocks list.
|
||
auto uncaughtCount = entryCount - PGO.getRegionCount(stmt);
|
||
|
||
// Only after emitting all the catch bodies, register the catch scopes.
|
||
// This is so that (re)throwing inside a catch does not match later
|
||
// catches.
|
||
for (const auto &cb : catchBlocks) {
|
||
auto matchWeights =
|
||
PGO.createProfileWeights(cb.catchcount, uncaughtCount);
|
||
// Add this exception type's match count to the uncaughtCount, because
|
||
// these failed to match the exception types of the remaining
|
||
// iterations.
|
||
uncaughtCount += cb.catchcount;
|
||
|
||
DtoResolveClass(cb.classdecl);
|
||
|
||
irs->funcGen().scopes.pushCatch(
|
||
getIrAggr(cb.classdecl)->getClassInfoSymbol(), cb.BB, matchWeights);
|
||
}
|
||
}
|
||
|
||
// Emit the try block.
|
||
irs->scope() = IRScope(trybb);
|
||
|
||
assert(stmt->_body);
|
||
irs->DBuilder.EmitBlockStart(stmt->_body->loc);
|
||
stmt->_body->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(endbb, irs->scopebb());
|
||
}
|
||
|
||
// Now that we have done the try block, remove the catches and continue
|
||
// codegen in the end block the try and all the catches branch to.
|
||
for (size_t i = 0; i < catchBlocks.size(); ++i) {
|
||
irs->funcGen().scopes.popCatch();
|
||
}
|
||
|
||
// Move end block after all generated blocks
|
||
endbb->moveAfter(&irs->topfunc()->back());
|
||
|
||
irs->scope() = IRScope(endbb);
|
||
// PGO counter tracks the continuation of the try statement
|
||
PGO.emitCounterIncrement(stmt);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ThrowStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("ThrowStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
// emit dwarf stop point
|
||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
|
||
assert(stmt->exp);
|
||
DValue *e = toElemDtor(stmt->exp);
|
||
|
||
llvm::Function *fn =
|
||
getRuntimeFunction(stmt->loc, irs->module, "_d_throw_exception");
|
||
LLValue *arg =
|
||
DtoBitCast(DtoRVal(e), fn->getFunctionType()->getParamType(0));
|
||
|
||
irs->CreateCallOrInvoke(fn, arg);
|
||
irs->ir->CreateUnreachable();
|
||
|
||
// TODO: Should not be needed.
|
||
llvm::BasicBlock *bb =
|
||
llvm::BasicBlock::Create(irs->context(), "afterthrow", irs->topfunc());
|
||
irs->scope() = IRScope(bb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(SwitchStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("SwitchStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
const auto incomingPGORegionCount = PGO.getCurrentRegionCount();
|
||
|
||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
llvm::BasicBlock *const oldbb = irs->scopebb();
|
||
|
||
// The cases of the switch statement, in codegen order. For string switches,
|
||
// we reorder them lexicographically later to match what the _d_switch_*
|
||
// druntime dispatch functions expect.
|
||
auto cases = stmt->cases;
|
||
const auto caseCount = cases->dim;
|
||
|
||
// llvm::Values for the case indices. Might not be llvm::Constants for
|
||
// runtime-initialised immutable globals as case indices, in which case we
|
||
// need to emit a `br` chain instead of `switch`.
|
||
llvm::SmallVector<llvm::Value *, 16> indices;
|
||
indices.reserve(caseCount);
|
||
bool useSwitchInst = true;
|
||
|
||
// For string switches, sort the cases and emit the table data.
|
||
llvm::Value *stringTableSlice = nullptr;
|
||
const bool isStringSwitch = !stmt->condition->type->isintegral();
|
||
if (isStringSwitch) {
|
||
Logger::println("is string switch");
|
||
|
||
// Sort the cases, taking care not to modify the original AST.
|
||
cases = cases->copy();
|
||
std::sort(cases->begin(), cases->end(), compareCaseStrings);
|
||
|
||
// Emit constants for the case values.
|
||
llvm::SmallVector<llvm::Constant *, 16> stringConsts;
|
||
stringConsts.reserve(caseCount);
|
||
for (size_t i = 0; i < caseCount; ++i) {
|
||
stringConsts.push_back(toConstElem((*cases)[i]->exp, irs));
|
||
indices.push_back(DtoConstUint(i));
|
||
}
|
||
|
||
// Create internal global with the data table.
|
||
const auto elemTy = DtoType(stmt->condition->type);
|
||
const auto arrTy = llvm::ArrayType::get(elemTy, stringConsts.size());
|
||
const auto arrInit = LLConstantArray::get(arrTy, stringConsts);
|
||
const auto arr = new llvm::GlobalVariable(
|
||
irs->module, arrTy, true, llvm::GlobalValue::InternalLinkage, arrInit,
|
||
".string_switch_table_data");
|
||
|
||
// Create D slice to pass to runtime later.
|
||
const auto arrPtr =
|
||
llvm::ConstantExpr::getBitCast(arr, getPtrToType(elemTy));
|
||
const auto arrLen = DtoConstSize_t(stringConsts.size());
|
||
stringTableSlice = DtoConstSlice(arrLen, arrPtr);
|
||
} else {
|
||
for (auto cs : *cases) {
|
||
if (cs->exp->op == TOKvar) {
|
||
const auto vd =
|
||
static_cast<VarExp *>(cs->exp)->var->isVarDeclaration();
|
||
if (vd && (!vd->_init || !vd->isConst())) {
|
||
indices.push_back(DtoRVal(toElemDtor(cs->exp)));
|
||
useSwitchInst = false;
|
||
continue;
|
||
}
|
||
}
|
||
indices.push_back(toConstElem(cs->exp, irs));
|
||
}
|
||
}
|
||
assert(indices.size() == caseCount);
|
||
|
||
// body block.
|
||
// FIXME: that block is never used
|
||
llvm::BasicBlock *bodybb =
|
||
llvm::BasicBlock::Create(irs->context(), "switchbody", irs->topfunc());
|
||
|
||
// end (break point)
|
||
llvm::BasicBlock *endbb =
|
||
llvm::BasicBlock::Create(irs->context(), "switchend", irs->topfunc());
|
||
// PGO counter tracks exit point of switch statement:
|
||
{
|
||
irs->scope() = IRScope(endbb);
|
||
PGO.emitCounterIncrement(stmt);
|
||
}
|
||
|
||
// default
|
||
auto defaultTargetBB = endbb;
|
||
if (stmt->sdefault) {
|
||
Logger::println("has default");
|
||
defaultTargetBB =
|
||
llvm::BasicBlock::Create(irs->context(), "default", irs->topfunc());
|
||
stmt->sdefault->bodyBB = defaultTargetBB;
|
||
}
|
||
|
||
// do switch body
|
||
assert(stmt->_body);
|
||
irs->scope() = IRScope(bodybb);
|
||
irs->funcGen().scopes.pushBreakTarget(stmt, endbb);
|
||
stmt->_body->accept(this);
|
||
irs->funcGen().scopes.popBreakTarget();
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(endbb, irs->scopebb());
|
||
}
|
||
|
||
irs->scope() = IRScope(oldbb);
|
||
if (useSwitchInst) {
|
||
// The case index value.
|
||
LLValue *condVal;
|
||
if (isStringSwitch) {
|
||
condVal = call_string_switch_runtime(stringTableSlice, stmt->condition);
|
||
} else {
|
||
condVal = DtoRVal(toElemDtor(stmt->condition));
|
||
}
|
||
|
||
// Create switch and add the cases.
|
||
// For PGO instrumentation, we need to add counters /before/ the case
|
||
// statement bodies, because the counters should only count the jumps
|
||
// directly from the switch statement and not "goto default", etc.
|
||
llvm::SwitchInst *si;
|
||
if (!global.params.genInstrProf) {
|
||
si = llvm::SwitchInst::Create(condVal, defaultTargetBB, caseCount,
|
||
irs->scopebb());
|
||
for (size_t i = 0; i < caseCount; ++i) {
|
||
si->addCase(isaConstantInt(indices[i]), (*cases)[i]->bodyBB);
|
||
}
|
||
} else {
|
||
auto switchbb = irs->scopebb();
|
||
// Add PGO instrumentation.
|
||
// Create "default" counter
|
||
{
|
||
llvm::BasicBlock *defaultcntr = llvm::BasicBlock::Create(
|
||
irs->context(), "defaultcntr", irs->topfunc());
|
||
irs->scope() = IRScope(defaultcntr);
|
||
PGO.emitCounterIncrement(stmt->sdefault);
|
||
llvm::BranchInst::Create(defaultTargetBB, irs->scopebb());
|
||
defaultcntr->moveBefore(defaultTargetBB);
|
||
// Create switch
|
||
si = llvm::SwitchInst::Create(condVal, defaultcntr, caseCount,
|
||
switchbb);
|
||
}
|
||
// Create and add case counters
|
||
for (size_t i = 0; i < caseCount; ++i) {
|
||
const auto cs = (*cases)[i];
|
||
llvm::BasicBlock *casecntr = llvm::BasicBlock::Create(
|
||
irs->context(), "casecntr", irs->topfunc());
|
||
irs->scope() = IRScope(casecntr);
|
||
PGO.emitCounterIncrement(cs);
|
||
llvm::BranchInst::Create(cs->bodyBB, irs->scopebb());
|
||
casecntr->moveBefore(cs->bodyBB);
|
||
|
||
si->addCase(isaConstantInt(indices[i]), casecntr);
|
||
}
|
||
}
|
||
|
||
// Put the switchend block after the last block, for a more logical IR
|
||
// layout.
|
||
endbb->moveAfter(&irs->topfunc()->back());
|
||
|
||
// Apply PGO switch branch weights:
|
||
{
|
||
// Get case statements execution counts from profile data.
|
||
std::vector<uint64_t> case_prof_counts;
|
||
case_prof_counts.push_back(
|
||
stmt->sdefault ? PGO.getRegionCount(stmt->sdefault) : 0);
|
||
for (auto cs : *cases) {
|
||
auto w = PGO.getRegionCount(cs);
|
||
case_prof_counts.push_back(w);
|
||
}
|
||
|
||
auto brweights = PGO.createProfileWeights(case_prof_counts);
|
||
PGO.addBranchWeights(si, brweights);
|
||
}
|
||
} else {
|
||
// We can't use switch, so we will use a bunch of br instructions
|
||
// instead.
|
||
|
||
DValue *cond = toElemDtor(stmt->condition);
|
||
LLValue *condVal = DtoRVal(cond);
|
||
|
||
llvm::BasicBlock *nextbb =
|
||
llvm::BasicBlock::Create(irs->context(), "checkcase", irs->topfunc());
|
||
llvm::BranchInst::Create(nextbb, irs->scopebb());
|
||
|
||
if (global.params.genInstrProf) {
|
||
// Prepend extra BB to "default:" to increment profiling counter.
|
||
llvm::BasicBlock *defaultcntr = llvm::BasicBlock::Create(
|
||
irs->context(), "defaultcntr", irs->topfunc());
|
||
irs->scope() = IRScope(defaultcntr);
|
||
PGO.emitCounterIncrement(stmt->sdefault);
|
||
llvm::BranchInst::Create(defaultTargetBB, irs->scopebb());
|
||
defaultcntr->moveBefore(defaultTargetBB);
|
||
defaultTargetBB = defaultcntr;
|
||
}
|
||
|
||
irs->scope() = IRScope(nextbb);
|
||
auto failedCompareCount = incomingPGORegionCount;
|
||
for (size_t i = 0; i < caseCount; ++i) {
|
||
LLValue *cmp = irs->ir->CreateICmp(llvm::ICmpInst::ICMP_EQ, indices[i],
|
||
condVal, "checkcase");
|
||
nextbb = llvm::BasicBlock::Create(irs->context(), "checkcase",
|
||
irs->topfunc());
|
||
|
||
// Add case counters for PGO in front of case body
|
||
const auto cs = (*cases)[i];
|
||
auto casejumptargetbb = cs->bodyBB;
|
||
if (global.params.genInstrProf) {
|
||
llvm::BasicBlock *casecntr = llvm::BasicBlock::Create(
|
||
irs->context(), "casecntr", irs->topfunc());
|
||
auto savedbb = irs->scope();
|
||
irs->scope() = IRScope(casecntr);
|
||
PGO.emitCounterIncrement(cs);
|
||
llvm::BranchInst::Create(casejumptargetbb, irs->scopebb());
|
||
casecntr->moveBefore(casejumptargetbb);
|
||
irs->scope() = savedbb;
|
||
|
||
casejumptargetbb = casecntr;
|
||
}
|
||
|
||
// Create the comparison branch for this case
|
||
auto branchinst = llvm::BranchInst::Create(casejumptargetbb, nextbb,
|
||
cmp, irs->scopebb());
|
||
|
||
// Calculate and apply PGO branch weights
|
||
{
|
||
auto trueCount = PGO.getRegionCount(cs);
|
||
assert(trueCount <= failedCompareCount &&
|
||
"Higher branch count than switch incoming count!");
|
||
failedCompareCount -= trueCount;
|
||
auto brweights =
|
||
PGO.createProfileWeights(trueCount, failedCompareCount);
|
||
PGO.addBranchWeights(branchinst, brweights);
|
||
}
|
||
|
||
irs->scope() = IRScope(nextbb);
|
||
}
|
||
|
||
llvm::BranchInst::Create(defaultTargetBB, irs->scopebb());
|
||
|
||
endbb->moveAfter(nextbb);
|
||
}
|
||
|
||
irs->scope() = IRScope(endbb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(CaseStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("CaseStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
llvm::BasicBlock *nbb =
|
||
llvm::BasicBlock::Create(irs->context(), "case", irs->topfunc());
|
||
if (stmt->bodyBB && !stmt->bodyBB->getTerminator()) {
|
||
llvm::BranchInst::Create(nbb, stmt->bodyBB);
|
||
}
|
||
stmt->bodyBB = nbb;
|
||
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(stmt->bodyBB, irs->scopebb());
|
||
}
|
||
|
||
irs->scope() = IRScope(stmt->bodyBB);
|
||
|
||
assert(stmt->statement);
|
||
irs->DBuilder.EmitBlockStart(stmt->statement->loc);
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
if (stmt->gototarget) {
|
||
PGO.emitCounterIncrement(PGO.getCounterPtr(stmt, 1));
|
||
}
|
||
stmt->statement->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(DefaultStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("DefaultStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
assert(stmt->bodyBB);
|
||
|
||
llvm::BasicBlock *nbb =
|
||
llvm::BasicBlock::Create(irs->context(), "default", irs->topfunc());
|
||
|
||
if (!stmt->bodyBB->getTerminator()) {
|
||
llvm::BranchInst::Create(nbb, stmt->bodyBB);
|
||
}
|
||
stmt->bodyBB = nbb;
|
||
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(stmt->bodyBB, irs->scopebb());
|
||
}
|
||
|
||
irs->scope() = IRScope(stmt->bodyBB);
|
||
|
||
assert(stmt->statement);
|
||
irs->DBuilder.EmitBlockStart(stmt->statement->loc);
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
if (stmt->gototarget) {
|
||
PGO.emitCounterIncrement(PGO.getCounterPtr(stmt, 1));
|
||
}
|
||
stmt->statement->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(UnrolledLoopStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("UnrolledLoopStatement::toIR(): %s",
|
||
stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
// if no statements, there's nothing to do
|
||
if (!stmt->statements || !stmt->statements->dim) {
|
||
return;
|
||
}
|
||
|
||
// start a dwarf lexical block
|
||
irs->DBuilder.EmitBlockStart(stmt->loc);
|
||
|
||
// DMD doesn't fold stuff like continue/break, and since this isn't really a
|
||
// loop we have to keep track of each statement and jump to the next/end
|
||
// on continue/break
|
||
|
||
// create a block for each statement
|
||
size_t nstmt = stmt->statements->dim;
|
||
llvm::SmallVector<llvm::BasicBlock *, 4> blocks(nstmt, nullptr);
|
||
|
||
for (size_t i = 0; i < nstmt; i++) {
|
||
blocks[i] = llvm::BasicBlock::Create(irs->context(), "unrolledstmt",
|
||
irs->topfunc());
|
||
}
|
||
|
||
// create end block
|
||
llvm::BasicBlock *endbb =
|
||
llvm::BasicBlock::Create(irs->context(), "unrolledend", irs->topfunc());
|
||
|
||
// enter first stmt
|
||
if (!irs->scopereturned()) {
|
||
irs->ir->CreateBr(blocks[0]);
|
||
}
|
||
|
||
// do statements
|
||
Statement **stmts = static_cast<Statement **>(stmt->statements->data);
|
||
|
||
for (size_t i = 0; i < nstmt; i++) {
|
||
Statement *s = stmts[i];
|
||
|
||
// get blocks
|
||
llvm::BasicBlock *thisbb = blocks[i];
|
||
llvm::BasicBlock *nextbb = (i + 1 == nstmt) ? endbb : blocks[i + 1];
|
||
|
||
// update scope
|
||
irs->scope() = IRScope(thisbb);
|
||
|
||
// push loop scope
|
||
// continue goes to next statement, break goes to end
|
||
irs->funcGen().scopes.pushLoopTarget(stmt, nextbb, endbb);
|
||
|
||
// do statement
|
||
s->accept(this);
|
||
|
||
// pop loop scope
|
||
irs->funcGen().scopes.popLoopTarget();
|
||
|
||
// next stmt
|
||
if (!irs->scopereturned()) {
|
||
irs->ir->CreateBr(nextbb);
|
||
}
|
||
}
|
||
|
||
// finish scope
|
||
if (!irs->scopereturned()) {
|
||
irs->ir->CreateBr(endbb);
|
||
}
|
||
irs->scope() = IRScope(endbb);
|
||
|
||
// end the dwarf lexical block
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ForeachStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("ForeachStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
// start a dwarf lexical block
|
||
irs->DBuilder.EmitBlockStart(stmt->loc);
|
||
|
||
// assert(arguments->dim == 1);
|
||
assert(stmt->value != 0);
|
||
assert(stmt->aggr != 0);
|
||
assert(stmt->func != 0);
|
||
|
||
// Argument* arg = static_cast<Argument*>(arguments->data[0]);
|
||
// Logger::println("Argument is %s", arg->toChars());
|
||
|
||
IF_LOG Logger::println("aggr = %s", stmt->aggr->toChars());
|
||
|
||
// key
|
||
LLType *keytype = stmt->key ? DtoType(stmt->key->type) : DtoSize_t();
|
||
LLValue *keyvar;
|
||
if (stmt->key) {
|
||
keyvar = DtoRawVarDeclaration(stmt->key);
|
||
} else {
|
||
keyvar = DtoRawAlloca(keytype, 0, "foreachkey");
|
||
}
|
||
LLValue *zerokey = LLConstantInt::get(keytype, 0, false);
|
||
|
||
// value
|
||
IF_LOG Logger::println("value = %s", stmt->value->toPrettyChars());
|
||
LLValue *valvar = nullptr;
|
||
if (!stmt->value->isRef() && !stmt->value->isOut()) {
|
||
// Create a local variable to serve as the value.
|
||
DtoRawVarDeclaration(stmt->value);
|
||
valvar = getIrLocal(stmt->value)->value;
|
||
}
|
||
|
||
// what to iterate
|
||
DValue *aggrval = toElemDtor(stmt->aggr);
|
||
|
||
// get length and pointer
|
||
LLValue *niters = DtoArrayLen(aggrval);
|
||
LLValue *val = DtoArrayPtr(aggrval);
|
||
|
||
if (niters->getType() != keytype) {
|
||
size_t sz1 = getTypeBitSize(niters->getType());
|
||
size_t sz2 = getTypeBitSize(keytype);
|
||
if (sz1 < sz2) {
|
||
niters = irs->ir->CreateZExt(niters, keytype, "foreachtrunckey");
|
||
} else if (sz1 > sz2) {
|
||
niters = irs->ir->CreateTrunc(niters, keytype, "foreachtrunckey");
|
||
} else {
|
||
niters = irs->ir->CreateBitCast(niters, keytype, "foreachtrunckey");
|
||
}
|
||
}
|
||
|
||
if (stmt->op == TOKforeach) {
|
||
new llvm::StoreInst(zerokey, keyvar, irs->scopebb());
|
||
} else {
|
||
new llvm::StoreInst(niters, keyvar, irs->scopebb());
|
||
}
|
||
|
||
llvm::BasicBlock *condbb =
|
||
llvm::BasicBlock::Create(irs->context(), "foreachcond", irs->topfunc());
|
||
llvm::BasicBlock *bodybb =
|
||
llvm::BasicBlock::Create(irs->context(), "foreachbody", irs->topfunc());
|
||
llvm::BasicBlock *nextbb =
|
||
llvm::BasicBlock::Create(irs->context(), "foreachnext", irs->topfunc());
|
||
llvm::BasicBlock *endbb =
|
||
llvm::BasicBlock::Create(irs->context(), "foreachend", irs->topfunc());
|
||
|
||
llvm::BranchInst::Create(condbb, irs->scopebb());
|
||
|
||
// condition
|
||
irs->scope() = IRScope(condbb);
|
||
|
||
LLValue *done = nullptr;
|
||
LLValue *load = DtoLoad(keyvar);
|
||
if (stmt->op == TOKforeach) {
|
||
done = irs->ir->CreateICmpULT(load, niters);
|
||
} else if (stmt->op == TOKforeach_reverse) {
|
||
done = irs->ir->CreateICmpUGT(load, zerokey);
|
||
load = irs->ir->CreateSub(load, LLConstantInt::get(keytype, 1, false));
|
||
DtoStore(load, keyvar);
|
||
}
|
||
auto branchinst =
|
||
llvm::BranchInst::Create(bodybb, endbb, done, irs->scopebb());
|
||
{
|
||
auto brweights = PGO.createProfileWeightsForeach(stmt);
|
||
PGO.addBranchWeights(branchinst, brweights);
|
||
}
|
||
|
||
// init body
|
||
irs->scope() = IRScope(bodybb);
|
||
PGO.emitCounterIncrement(stmt);
|
||
|
||
// get value for this iteration
|
||
LLValue *loadedKey = irs->ir->CreateLoad(keyvar);
|
||
LLValue *gep = DtoGEP1(val, loadedKey, true);
|
||
|
||
if (!stmt->value->isRef() && !stmt->value->isOut()) {
|
||
// Copy value to local variable, and use it as the value variable.
|
||
DLValue dst(stmt->value->type, valvar);
|
||
DLValue src(stmt->value->type, gep);
|
||
DtoAssign(stmt->loc, &dst, &src, TOKassign);
|
||
getIrLocal(stmt->value)->value = valvar;
|
||
} else {
|
||
// Use the GEP as the address of the value variable.
|
||
DtoRawVarDeclaration(stmt->value, gep);
|
||
}
|
||
|
||
// emit body
|
||
irs->funcGen().scopes.pushLoopTarget(stmt, nextbb, endbb);
|
||
if (stmt->_body) {
|
||
stmt->_body->accept(this);
|
||
}
|
||
irs->funcGen().scopes.popLoopTarget();
|
||
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(nextbb, irs->scopebb());
|
||
}
|
||
|
||
// next
|
||
irs->scope() = IRScope(nextbb);
|
||
if (stmt->op == TOKforeach) {
|
||
LLValue *load = DtoLoad(keyvar);
|
||
load = irs->ir->CreateAdd(load, LLConstantInt::get(keytype, 1, false));
|
||
DtoStore(load, keyvar);
|
||
}
|
||
llvm::BranchInst::Create(condbb, irs->scopebb());
|
||
|
||
// end the dwarf lexical block
|
||
irs->DBuilder.EmitBlockEnd();
|
||
|
||
// end
|
||
irs->scope() = IRScope(endbb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ForeachRangeStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("ForeachRangeStatement::toIR(): %s",
|
||
stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
// start a dwarf lexical block
|
||
irs->DBuilder.EmitBlockStart(stmt->loc);
|
||
|
||
// evaluate lwr/upr
|
||
assert(stmt->lwr->type->isintegral());
|
||
LLValue *lower = DtoRVal(toElemDtor(stmt->lwr));
|
||
assert(stmt->upr->type->isintegral());
|
||
LLValue *upper = DtoRVal(toElemDtor(stmt->upr));
|
||
|
||
// handle key
|
||
assert(stmt->key->type->isintegral());
|
||
LLValue *keyval = DtoRawVarDeclaration(stmt->key);
|
||
|
||
// store initial value in key
|
||
if (stmt->op == TOKforeach) {
|
||
DtoStore(lower, keyval);
|
||
} else {
|
||
DtoStore(upper, keyval);
|
||
}
|
||
|
||
// set up the block we'll need
|
||
llvm::BasicBlock *condbb = llvm::BasicBlock::Create(
|
||
irs->context(), "foreachrange_cond", irs->topfunc());
|
||
llvm::BasicBlock *bodybb = llvm::BasicBlock::Create(
|
||
irs->context(), "foreachrange_body", irs->topfunc());
|
||
llvm::BasicBlock *nextbb = llvm::BasicBlock::Create(
|
||
irs->context(), "foreachrange_next", irs->topfunc());
|
||
llvm::BasicBlock *endbb = llvm::BasicBlock::Create(
|
||
irs->context(), "foreachrange_end", irs->topfunc());
|
||
|
||
// jump to condition
|
||
llvm::BranchInst::Create(condbb, irs->scopebb());
|
||
|
||
// CONDITION
|
||
irs->scope() = IRScope(condbb);
|
||
|
||
// first we test that lwr < upr
|
||
lower = DtoLoad(keyval);
|
||
assert(lower->getType() == upper->getType());
|
||
llvm::ICmpInst::Predicate cmpop;
|
||
if (isLLVMUnsigned(stmt->key->type)) {
|
||
cmpop = (stmt->op == TOKforeach) ? llvm::ICmpInst::ICMP_ULT
|
||
: llvm::ICmpInst::ICMP_UGT;
|
||
} else {
|
||
cmpop = (stmt->op == TOKforeach) ? llvm::ICmpInst::ICMP_SLT
|
||
: llvm::ICmpInst::ICMP_SGT;
|
||
}
|
||
LLValue *cond = irs->ir->CreateICmp(cmpop, lower, upper);
|
||
|
||
// jump to the body if range is ok, to the end if not
|
||
auto branchinst =
|
||
llvm::BranchInst::Create(bodybb, endbb, cond, irs->scopebb());
|
||
{
|
||
auto brweights = PGO.createProfileWeightsForeachRange(stmt);
|
||
PGO.addBranchWeights(branchinst, brweights);
|
||
}
|
||
|
||
// BODY
|
||
irs->scope() = IRScope(bodybb);
|
||
PGO.emitCounterIncrement(stmt);
|
||
|
||
// reverse foreach decrements here
|
||
if (stmt->op == TOKforeach_reverse) {
|
||
LLValue *v = DtoLoad(keyval);
|
||
LLValue *one = LLConstantInt::get(v->getType(), 1, false);
|
||
v = irs->ir->CreateSub(v, one);
|
||
DtoStore(v, keyval);
|
||
}
|
||
|
||
// emit body
|
||
irs->funcGen().scopes.pushLoopTarget(stmt, nextbb, endbb);
|
||
if (stmt->_body) {
|
||
stmt->_body->accept(this);
|
||
}
|
||
irs->funcGen().scopes.popLoopTarget();
|
||
|
||
// jump to next iteration
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(nextbb, irs->scopebb());
|
||
}
|
||
|
||
// NEXT
|
||
irs->scope() = IRScope(nextbb);
|
||
|
||
// forward foreach increments here
|
||
if (stmt->op == TOKforeach) {
|
||
LLValue *v = DtoLoad(keyval);
|
||
LLValue *one = LLConstantInt::get(v->getType(), 1, false);
|
||
v = irs->ir->CreateAdd(v, one);
|
||
DtoStore(v, keyval);
|
||
}
|
||
|
||
// jump to condition
|
||
llvm::BranchInst::Create(condbb, irs->scopebb());
|
||
|
||
// end the dwarf lexical block
|
||
irs->DBuilder.EmitBlockEnd();
|
||
|
||
// END
|
||
irs->scope() = IRScope(endbb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(LabelStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("LabelStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
// if it's an inline asm label, we don't create a basicblock, just emit it
|
||
// in the asm
|
||
if (irs->asmBlock) {
|
||
auto a = new IRAsmStmt;
|
||
std::stringstream label;
|
||
printLabelName(label, mangleExact(irs->func()->decl),
|
||
stmt->ident->toChars());
|
||
label << ":";
|
||
a->code = label.str();
|
||
irs->asmBlock->s.push_back(a);
|
||
irs->asmBlock->internalLabels.push_back(stmt->ident);
|
||
|
||
// disable inlining
|
||
irs->func()->setNeverInline();
|
||
} else {
|
||
llvm::BasicBlock *labelBB = llvm::BasicBlock::Create(
|
||
irs->context(), llvm::Twine("label.") + stmt->ident->toChars(),
|
||
irs->topfunc());
|
||
irs->funcGen().scopes.addLabelTarget(stmt->ident, labelBB);
|
||
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(labelBB, irs->scopebb());
|
||
}
|
||
|
||
irs->scope() = IRScope(labelBB);
|
||
}
|
||
|
||
PGO.emitCounterIncrement(stmt);
|
||
// statement == nullptr when the label is at the end of function
|
||
if (stmt->statement) {
|
||
stmt->statement->accept(this);
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(GotoStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("GotoStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
|
||
DtoGoto(stmt->loc, stmt->label);
|
||
|
||
// TODO: Should not be needed.
|
||
llvm::BasicBlock *bb =
|
||
llvm::BasicBlock::Create(irs->context(), "aftergoto", irs->topfunc());
|
||
irs->scope() = IRScope(bb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(GotoDefaultStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("GotoDefaultStatement::toIR(): %s",
|
||
stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
|
||
assert(!irs->scopereturned());
|
||
assert(stmt->sw->sdefault->bodyBB);
|
||
|
||
#if 0
|
||
// TODO: Store switch scopes.
|
||
DtoEnclosingHandlers(stmt->loc, stmt->sw);
|
||
#endif
|
||
|
||
llvm::BranchInst::Create(stmt->sw->sdefault->bodyBB, irs->scopebb());
|
||
|
||
// TODO: Should not be needed.
|
||
llvm::BasicBlock *bb = llvm::BasicBlock::Create(
|
||
irs->context(), "aftergotodefault", irs->topfunc());
|
||
irs->scope() = IRScope(bb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(GotoCaseStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("GotoCaseStatement::toIR(): %s",
|
||
stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
|
||
assert(!irs->scopereturned());
|
||
if (!stmt->cs->bodyBB) {
|
||
stmt->cs->bodyBB =
|
||
llvm::BasicBlock::Create(irs->context(), "goto_case", irs->topfunc());
|
||
}
|
||
|
||
#if 0
|
||
// TODO: Store switch scopes.
|
||
DtoEnclosingHandlers(stmt->loc, stmt->sw);
|
||
#endif
|
||
|
||
llvm::BranchInst::Create(stmt->cs->bodyBB, irs->scopebb());
|
||
|
||
// TODO: Should not be needed.
|
||
llvm::BasicBlock *bb = llvm::BasicBlock::Create(
|
||
irs->context(), "aftergotocase", irs->topfunc());
|
||
irs->scope() = IRScope(bb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(WithStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("WithStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
irs->DBuilder.EmitBlockStart(stmt->loc);
|
||
|
||
assert(stmt->exp);
|
||
|
||
// with(..) can either be used with expressions or with symbols
|
||
// wthis == null indicates the symbol form
|
||
if (stmt->wthis) {
|
||
LLValue *mem = DtoRawVarDeclaration(stmt->wthis);
|
||
DValue *e = toElemDtor(stmt->exp);
|
||
LLValue *val = (DtoIsInMemoryOnly(e->type) ? DtoLVal(e) : DtoRVal(e));
|
||
DtoStore(val, mem);
|
||
}
|
||
|
||
if (stmt->_body) {
|
||
stmt->_body->accept(this);
|
||
}
|
||
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(SwitchErrorStatement *stmt) LLVM_OVERRIDE {
|
||
IF_LOG Logger::println("SwitchErrorStatement::toIR(): %s",
|
||
stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
llvm::Function *fn =
|
||
getRuntimeFunction(stmt->loc, irs->module, "_d_switch_error");
|
||
|
||
LLValue *moduleInfoSymbol =
|
||
getIrModule(irs->func()->decl->getModule())->moduleInfoSymbol();
|
||
LLType *moduleInfoType = DtoType(Module::moduleinfo->type);
|
||
|
||
LLCallSite call = irs->CreateCallOrInvoke(
|
||
fn, DtoBitCast(moduleInfoSymbol, getPtrToType(moduleInfoType)),
|
||
DtoConstUint(stmt->loc.linnum));
|
||
call.setDoesNotReturn();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(AsmStatement *stmt) LLVM_OVERRIDE { AsmStatement_toIR(stmt, irs); }
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(CompoundAsmStatement *stmt) LLVM_OVERRIDE {
|
||
CompoundAsmStatement_toIR(stmt, irs);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ImportStatement *stmt) LLVM_OVERRIDE {
|
||
// Empty.
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(Statement *stmt) LLVM_OVERRIDE {
|
||
error(stmt->loc, "Statement type Statement not implemented: %s",
|
||
stmt->toChars());
|
||
fatal();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(PragmaStatement *stmt) LLVM_OVERRIDE {
|
||
error(stmt->loc, "Statement type PragmaStatement not implemented: %s",
|
||
stmt->toChars());
|
||
fatal();
|
||
}
|
||
};
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
void Statement_toIR(Statement *s, IRState *irs) {
|
||
ToIRVisitor v(irs);
|
||
s->accept(&v);
|
||
}
|