mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-28 14:10:42 +03:00
1746 lines
56 KiB
C++
1746 lines
56 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 "dmd/errors.h"
|
||
#include "dmd/expression.h"
|
||
#include "dmd/hdrgen.h"
|
||
#include "dmd/id.h"
|
||
#include "dmd/identifier.h"
|
||
#include "dmd/import.h"
|
||
#include "dmd/init.h"
|
||
#include "dmd/mangle.h"
|
||
#include "dmd/module.h"
|
||
#include "dmd/mtype.h"
|
||
#include "dmd/root/port.h"
|
||
#include "gen/abi/abi.h"
|
||
#include "gen/arrays.h"
|
||
#include "gen/classes.h"
|
||
#include "gen/coverage.h"
|
||
#include "gen/dcompute/target.h"
|
||
#include "gen/dvalue.h"
|
||
#include "gen/funcgenstate.h"
|
||
#include "gen/functions.h"
|
||
#include "gen/irstate.h"
|
||
#include "gen/llvm.h"
|
||
#include "gen/llvmhelpers.h"
|
||
#include "gen/logger.h"
|
||
#include "gen/recursivevisitor.h"
|
||
#include "gen/runtime.h"
|
||
#include "gen/tollvm.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>
|
||
|
||
using namespace dmd;
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
// FIXME: Integrate these functions
|
||
void GccAsmStatement_toIR(GccAsmStatement *stmt, IRState *irs);
|
||
void AsmStatement_toIR(InlineAsmStatement *stmt, IRState *irs);
|
||
void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
namespace {
|
||
bool isAssertFalse(Expression *e) {
|
||
return e ? e->type == Type::tnoreturn &&
|
||
(e->op == EXP::halt || e->op == EXP::assert_)
|
||
: false;
|
||
}
|
||
|
||
bool isAssertFalse(Statement *s) {
|
||
if (!s)
|
||
return false;
|
||
if (auto es = s->isExpStatement())
|
||
return isAssertFalse(es->exp);
|
||
else if (auto ss = s->isScopeStatement())
|
||
return isAssertFalse(ss->statement);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
/// Used to check if a control-flow stmt body contains any label. A label
|
||
/// is considered anything that lets us jump inside the body _apart from_
|
||
/// the stmt. That includes case / default statements.
|
||
/// It is a StoppableVisitor that stops when a label is found.
|
||
/// It's to be passed in a ContainsLabelWalker which recursively
|
||
/// walks the tree and updates our `inside_switch` flag accordingly.
|
||
struct ContainsLabelVisitor : public StoppableVisitor {
|
||
// If RecursiveWalker finds a SwitchStatement,
|
||
// `insideSwitch` points to that statement.
|
||
SwitchStatement *insideSwitch = nullptr;
|
||
|
||
using StoppableVisitor::visit;
|
||
|
||
void visit(Statement *stmt) override {}
|
||
|
||
void visit(LabelStatement *stmt) override { stop = true; }
|
||
|
||
void visit(CaseStatement *stmt) override {
|
||
if (insideSwitch == nullptr)
|
||
stop = true;
|
||
}
|
||
|
||
void visit(DefaultStatement *stmt) override {
|
||
if (insideSwitch == nullptr)
|
||
stop = true;
|
||
}
|
||
|
||
bool foundLabel() { return stop; }
|
||
|
||
void visit(Declaration *) override {}
|
||
void visit(Initializer *) override {}
|
||
void visit(Dsymbol *) override {}
|
||
void visit(Expression *) override {}
|
||
};
|
||
|
||
/// As the RecursiveWalker, but it gets a ContainsLabelVisitor
|
||
/// and updates its `insideSwitch` field accordingly.
|
||
class ContainsLabelWalker : public RecursiveWalker {
|
||
public:
|
||
using RecursiveWalker::visit;
|
||
|
||
explicit ContainsLabelWalker(ContainsLabelVisitor *visitor,
|
||
bool _continueAfterStop = true)
|
||
: RecursiveWalker(visitor, _continueAfterStop) {}
|
||
|
||
void visit(SwitchStatement *stmt) override {
|
||
ContainsLabelVisitor *ev = static_cast<ContainsLabelVisitor *>(v);
|
||
SwitchStatement *save = ev->insideSwitch;
|
||
ev->insideSwitch = stmt;
|
||
RecursiveWalker::visit(stmt);
|
||
ev->insideSwitch = save;
|
||
}
|
||
|
||
void visit(Expression *) override {}
|
||
};
|
||
|
||
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) 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) 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;
|
||
llvm::FunctionType *funcType = f->getLLVMFuncType();
|
||
|
||
emitInstrumentationFnLeave(fd);
|
||
|
||
const auto cleanupScopeBeforeExpression =
|
||
funcGen.scopes.currentCleanupScope();
|
||
|
||
// is there a return value expression?
|
||
const bool isMainFunc = isAnyMainFunction(fd);
|
||
if (stmt->exp || isMainFunc) {
|
||
// We clean up manually (*not* using toElemDtor) as the expression might
|
||
// be an lvalue pointing into a temporary, and we may need a load. So we
|
||
// need to make sure to destruct any temporaries after all of that.
|
||
|
||
const auto rt = f->type->next;
|
||
const auto rtb = rt->toBasetype();
|
||
|
||
if (!stmt->exp) {
|
||
// implicitly return 0 for the main function
|
||
returnValue = LLConstant::getNullValue(funcType->getReturnType());
|
||
} else if ((rtb->ty == TY::Tvoid || rtb->ty == TY::Tnoreturn) &&
|
||
!isMainFunc) {
|
||
// evaluate expression for side effects
|
||
assert(stmt->exp->type->toBasetype()->ty == TY::Tvoid ||
|
||
stmt->exp->type->toBasetype()->ty == TY::Tnoreturn);
|
||
toElem(stmt->exp);
|
||
} else if (funcType->getReturnType()->isVoidTy()) {
|
||
// if the IR function's return type is void (but not the D one), it uses
|
||
// sret
|
||
assert(!f->type->isref());
|
||
|
||
LLValue *sretPointer = f->sretArg;
|
||
assert(sretPointer);
|
||
|
||
assert(!f->irFty.arg_sret->rewrite &&
|
||
"ABI shouldn't have to rewrite sret returns");
|
||
DLValue returnValue(rt, sretPointer);
|
||
|
||
// try to construct the return value in-place
|
||
const bool constructed = toInPlaceConstruction(&returnValue, stmt->exp);
|
||
if (!constructed) {
|
||
DValue *e = toElem(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->isNRVO() && fd->nrvo_var);
|
||
if (doPostblit) {
|
||
if (auto ve = stmt->exp->isVarExp())
|
||
if (ve->var->isResult())
|
||
doPostblit = false;
|
||
}
|
||
|
||
DtoAssign(stmt->loc, &returnValue, e, EXP::blit);
|
||
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->op == EXP::null_) {
|
||
stmt->exp->type = rt;
|
||
}
|
||
DValue *dval = nullptr;
|
||
// call postblit if necessary
|
||
if (!f->type->isref()) {
|
||
dval = toElem(stmt->exp);
|
||
LLValue *vthis =
|
||
(DtoIsInMemoryOnly(dval->type) ? DtoLVal(dval) : DtoRVal(dval));
|
||
callPostblit(stmt->loc, stmt->exp, vthis);
|
||
} else {
|
||
Expression *ae = stmt->exp;
|
||
dval = toElem(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() != funcType->getReturnType() &&
|
||
DtoIsInMemoryOnly(rt) && isaPointer(returnValue)) {
|
||
Logger::println("Loading value for return");
|
||
returnValue = DtoLoad(funcType->getReturnType(), returnValue);
|
||
}
|
||
}
|
||
} else {
|
||
// no return value expression means it's a void function.
|
||
assert(funcType->getReturnType()->isVoidTy());
|
||
}
|
||
|
||
// 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 = irs->insertBB("return");
|
||
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.runCleanups(0, funcGen.retBlock);
|
||
// Pop the cleanups pushed during evaluation of the return expression.
|
||
funcGen.scopes.popCleanups(cleanupScopeBeforeExpression);
|
||
|
||
irs->ir->SetInsertPoint(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 (isAnyMainFunction(fd) && !stmt->loc.linnum()) {
|
||
irs->DBuilder.EmitStopPoint(fd->endloc);
|
||
}
|
||
|
||
irs->ir->CreateRet(
|
||
useRetValSlot ? DtoLoad(funcType->getReturnType(), 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->ir->SetInsertPoint(irs->insertBB("dummy.afterreturn"));
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ExpStatement *stmt) 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);
|
||
|
||
if (auto e = stmt->exp) {
|
||
if (e->hasCode() &&
|
||
!isAssertFalse(e)) { // `assert(0)` not meant to be covered
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
}
|
||
|
||
DValue *elem;
|
||
// a cast(void) around the expression is allowed, but doesn't require any
|
||
// code
|
||
if (e->op == EXP::cast_ && e->type == Type::tvoid) {
|
||
elem = toElemDtor(static_cast<CastExp *>(e)->e1);
|
||
} else {
|
||
elem = toElemDtor(e);
|
||
}
|
||
delete elem;
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
bool dcomputeReflectMatches(CallExp *ce) {
|
||
auto arg1 = (DComputeTarget::ID)(*ce->arguments)[0]->toInteger();
|
||
auto arg2 = (*ce->arguments)[1]->toInteger();
|
||
auto dct = irs->dcomputetarget;
|
||
if (!dct) {
|
||
return arg1 == DComputeTarget::ID::Host;
|
||
} else {
|
||
return arg1 == dct->target &&
|
||
(!arg2 || arg2 == static_cast<dinteger_t>(dct->tversion));
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
bool containsLabel(Statement *stmt) {
|
||
if (!stmt)
|
||
return false;
|
||
ContainsLabelVisitor labelChecker;
|
||
ContainsLabelWalker walker(&labelChecker, false);
|
||
stmt->accept(&walker);
|
||
return labelChecker.foundLabel();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(IfStatement *stmt) 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);
|
||
// Open a new scope for the optional condition variable (`if (auto i = ...)`)
|
||
irs->funcGen().localVariableLifetimeAnnotator.pushScope();
|
||
|
||
|
||
// This is a (dirty) hack to get codegen time conditional
|
||
// compilation, on account of the fact that we are trying
|
||
// to target multiple backends "simultaneously" with one
|
||
// pass through the front end, to have a single "static"
|
||
// context.
|
||
if (auto ce = stmt->condition->isCallExp()) {
|
||
if (ce->f && ce->f->ident == Id::dcReflect) {
|
||
if (dcomputeReflectMatches(ce))
|
||
stmt->ifbody->accept(this);
|
||
else if (stmt->elsebody)
|
||
stmt->elsebody->accept(this);
|
||
return;
|
||
}
|
||
}
|
||
DValue *cond_e = toElemDtor(stmt->condition);
|
||
LLValue *cond_val = DtoRVal(cond_e);
|
||
if (!cond_val->getType()->isIntegerTy(1)) {
|
||
IF_LOG Logger::cout() << "if conditional: " << *cond_val << std::endl;
|
||
cond_val = DtoRVal(DtoCast(stmt->loc, cond_e, Type::tbool));
|
||
}
|
||
|
||
// Is it constant, and is its value certainly `false` or `true`?
|
||
// Note: It must be a simple constant for which it is sufficient to
|
||
// determine true/false by calling `isZeroValue()`. This is not the case for
|
||
// complex llvm::Constant such as a CompareConstantExpr where `isZeroValue()
|
||
// == false` does not imply that the value is always `true`. See LDC issue
|
||
// #4556.
|
||
if (llvm::ConstantInt *const_val = llvm::dyn_cast<llvm::ConstantInt>(cond_val)) {
|
||
Statement *executed = stmt->ifbody;
|
||
Statement *skipped = stmt->elsebody;
|
||
if (const_val->isZeroValue()) {
|
||
std::swap(executed, skipped);
|
||
}
|
||
if (!containsLabel(skipped)) {
|
||
IF_LOG Logger::cout() << "Constant true/false condition - elide. (cond_val: " << *cond_val << ')' << std::endl;
|
||
|
||
if (executed) {
|
||
irs->DBuilder.EmitBlockStart(executed->loc);
|
||
}
|
||
// True condition, the branch is taken so emit counter increment.
|
||
if (!const_val->isZeroValue()) {
|
||
PGO.emitCounterIncrement(stmt);
|
||
}
|
||
if (executed) {
|
||
executed->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
// end the dwarf lexical block
|
||
irs->DBuilder.EmitBlockEnd();
|
||
return;
|
||
}
|
||
}
|
||
llvm::BasicBlock *ifbb = irs->insertBB("if");
|
||
llvm::BasicBlock *endbb = irs->insertBBAfter(ifbb, "endif");
|
||
llvm::BasicBlock *elsebb =
|
||
stmt->elsebody ? irs->insertBBAfter(ifbb, "else") : endbb;
|
||
|
||
auto brinstr =
|
||
llvm::BranchInst::Create(ifbb, elsebb, cond_val, irs->scopebb());
|
||
PGO.addBranchWeights(brinstr, brweights);
|
||
|
||
// replace current scope
|
||
irs->ir->SetInsertPoint(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->ir->SetInsertPoint(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->ir->SetInsertPoint(endbb);
|
||
// Close the scope for the optional condition variable. This is suboptimal,
|
||
// because the condition variable is not in scope in the else block.
|
||
irs->funcGen().localVariableLifetimeAnnotator.popScope();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ScopeStatement *stmt) 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->funcGen().localVariableLifetimeAnnotator.pushScope();
|
||
irs->DBuilder.EmitBlockStart(stmt->statement->loc);
|
||
stmt->statement->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
irs->funcGen().localVariableLifetimeAnnotator.popScope();
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(WhileStatement *stmt) 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 = irs->insertBB("whilecond");
|
||
llvm::BasicBlock *whilebodybb = irs->insertBBAfter(whilebb, "whilebody");
|
||
llvm::BasicBlock *endbb = irs->insertBBAfter(whilebodybb, "endwhile");
|
||
|
||
// move into the while block
|
||
irs->ir->CreateBr(whilebb);
|
||
|
||
// replace current scope
|
||
irs->ir->SetInsertPoint(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->ir->SetInsertPoint(whilebodybb);
|
||
|
||
// while body code
|
||
irs->funcGen().jumpTargets.pushLoopTarget(stmt, whilebb, endbb);
|
||
PGO.emitCounterIncrement(stmt);
|
||
if (stmt->_body) {
|
||
stmt->_body->accept(this);
|
||
}
|
||
irs->funcGen().jumpTargets.popLoopTarget();
|
||
|
||
// loop
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(whilebb, irs->scopebb());
|
||
}
|
||
|
||
// rewrite the scope
|
||
irs->ir->SetInsertPoint(endbb);
|
||
|
||
// end the dwarf lexical block
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(DoStatement *stmt) 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 = irs->insertBB("dowhile");
|
||
llvm::BasicBlock *condbb = irs->insertBBAfter(dowhilebb, "dowhilecond");
|
||
llvm::BasicBlock *endbb = irs->insertBBAfter(condbb, "enddowhile");
|
||
|
||
// move into the while block
|
||
assert(!irs->scopereturned());
|
||
llvm::BranchInst::Create(dowhilebb, irs->scopebb());
|
||
|
||
// replace current scope
|
||
irs->ir->SetInsertPoint(dowhilebb);
|
||
|
||
// do-while body code
|
||
irs->funcGen().jumpTargets.pushLoopTarget(stmt, condbb, endbb);
|
||
PGO.emitCounterIncrement(stmt);
|
||
if (stmt->_body) {
|
||
stmt->_body->accept(this);
|
||
}
|
||
irs->funcGen().jumpTargets.popLoopTarget();
|
||
|
||
// branch to condition block
|
||
llvm::BranchInst::Create(condbb, irs->scopebb());
|
||
irs->ir->SetInsertPoint(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);
|
||
}
|
||
|
||
// rewrite the scope
|
||
irs->ir->SetInsertPoint(endbb);
|
||
|
||
// end the dwarf lexical block
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ForStatement *stmt) 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);
|
||
irs->funcGen().localVariableLifetimeAnnotator.pushScope();
|
||
|
||
// create for blocks
|
||
llvm::BasicBlock *forbb = irs->insertBB("forcond");
|
||
llvm::BasicBlock *forbodybb = irs->insertBBAfter(forbb, "forbody");
|
||
llvm::BasicBlock *forincbb = irs->insertBBAfter(forbodybb, "forinc");
|
||
llvm::BasicBlock *endbb = irs->insertBBAfter(forincbb, "endfor");
|
||
|
||
// 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().jumpTargets.pushLoopTarget(scopeStart, forincbb, endbb);
|
||
|
||
// replace current scope
|
||
irs->ir->SetInsertPoint(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->ir->SetInsertPoint(forbodybb);
|
||
|
||
// do for body code
|
||
PGO.emitCounterIncrement(stmt);
|
||
if (stmt->_body) {
|
||
stmt->_body->accept(this);
|
||
}
|
||
|
||
// move into the for increment block
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(forincbb, irs->scopebb());
|
||
}
|
||
irs->ir->SetInsertPoint(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().jumpTargets.popLoopTarget();
|
||
|
||
// rewrite the scope
|
||
irs->ir->SetInsertPoint(endbb);
|
||
|
||
// end the dwarf lexical block
|
||
irs->funcGen().localVariableLifetimeAnnotator.popScope();
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(BreakStatement *stmt) 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().jumpTargets.breakToStatement(targetStatement);
|
||
} else {
|
||
irs->funcGen().jumpTargets.breakToClosest();
|
||
}
|
||
|
||
// the break terminated this basicblock, start a new one
|
||
llvm::BasicBlock *bb = irs->insertBB("afterbreak");
|
||
irs->ir->SetInsertPoint(bb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ContinueStatement *stmt) 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().jumpTargets.continueWithLoop(targetLoopStatement);
|
||
} else {
|
||
irs->funcGen().jumpTargets.continueWithClosest();
|
||
}
|
||
|
||
// the continue terminated this basicblock, start a new one
|
||
llvm::BasicBlock *bb = irs->insertBB("aftercontinue");
|
||
irs->ir->SetInsertPoint(bb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ScopeGuardStatement *stmt) override {
|
||
error(stmt->loc,
|
||
"Internal Compiler Error: ScopeGuardStatement should have been "
|
||
"lowered by frontend.");
|
||
fatal();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(TryFinallyStatement *stmt) 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();
|
||
|
||
llvm::BasicBlock *finallybb = irs->insertBB("finally");
|
||
// Create a block to branch to after successfully running the try block
|
||
// and any cleanups.
|
||
llvm::BasicBlock *successbb =
|
||
irs->scopereturned() ? nullptr
|
||
: irs->insertBBAfter(finallybb, "try.success");
|
||
|
||
// Emit the finally block and set up the cleanup scope for it.
|
||
irs->ir->SetInsertPoint(finallybb);
|
||
irs->DBuilder.EmitBlockStart(stmt->finalbody->loc);
|
||
stmt->finalbody->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
CleanupCursor cleanupBefore;
|
||
|
||
// For @compute code, don't emit any exception handling as there are no
|
||
// exceptions anyway.
|
||
const bool computeCode = !!irs->dcomputetarget;
|
||
if (!computeCode) {
|
||
cleanupBefore = irs->funcGen().scopes.currentCleanupScope();
|
||
irs->funcGen().scopes.pushCleanup(finallybb, irs->scopebb());
|
||
}
|
||
// Emit the try block.
|
||
irs->ir->SetInsertPoint(trybb);
|
||
|
||
assert(stmt->_body);
|
||
irs->DBuilder.EmitBlockStart(stmt->_body->loc);
|
||
stmt->_body->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
|
||
if (successbb) {
|
||
if (!computeCode)
|
||
irs->funcGen().scopes.runCleanups(cleanupBefore, successbb);
|
||
irs->ir->SetInsertPoint(successbb);
|
||
// PGO counter tracks the continuation of the try-finally statement
|
||
PGO.emitCounterIncrement(stmt);
|
||
}
|
||
if (!computeCode)
|
||
irs->funcGen().scopes.popCleanups(cleanupBefore);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(TryCatchStatement *stmt) override {
|
||
IF_LOG Logger::println("TryCatchStatement::toIR(): %s",
|
||
stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
assert(!irs->dcomputetarget);
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
|
||
// 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 = irs->insertBB("try.success.or.caught");
|
||
|
||
irs->funcGen().scopes.pushTryCatch(stmt, endbb);
|
||
|
||
// Emit the try block.
|
||
irs->ir->SetInsertPoint(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());
|
||
|
||
irs->funcGen().scopes.popTryCatch();
|
||
|
||
irs->ir->SetInsertPoint(endbb);
|
||
|
||
// PGO counter tracks the continuation of the try statement
|
||
PGO.emitCounterIncrement(stmt);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ThrowStatement *stmt) override {
|
||
IF_LOG Logger::println("ThrowStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
assert(!irs->dcomputetarget);
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
// emit dwarf stop point
|
||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
|
||
assert(stmt->exp);
|
||
DtoThrow(stmt->loc, toElemDtor(stmt->exp));
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(SwitchStatement *stmt) override {
|
||
IF_LOG Logger::println("SwitchStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &funcGen = irs->funcGen();
|
||
|
||
auto &PGO = 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.
|
||
auto cases = stmt->cases;
|
||
const auto caseCount = cases->length;
|
||
|
||
// 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 (auto cs : *cases) {
|
||
auto ce = cs->exp;
|
||
if (auto ceConst = tryToConstElem(ce, irs)) {
|
||
indices.push_back(ceConst);
|
||
} else {
|
||
indices.push_back(DtoRVal(toElemDtor(ce)));
|
||
useSwitchInst = false;
|
||
}
|
||
}
|
||
assert(indices.size() == caseCount);
|
||
|
||
// body block.
|
||
// FIXME: that block is never used
|
||
llvm::BasicBlock *bodybb = irs->insertBB("switchbody");
|
||
|
||
// end (break point)
|
||
llvm::BasicBlock *endbb = irs->insertBBAfter(bodybb, "switchend");
|
||
|
||
// default
|
||
auto defaultTargetBB = endbb;
|
||
if (stmt->sdefault) {
|
||
Logger::println("has default");
|
||
defaultTargetBB =
|
||
funcGen.switchTargets.getOrCreate(stmt->sdefault, "default", *irs);
|
||
}
|
||
|
||
// do switch body
|
||
assert(stmt->_body);
|
||
irs->ir->SetInsertPoint(bodybb);
|
||
funcGen.jumpTargets.pushBreakTarget(stmt, endbb);
|
||
stmt->_body->accept(this);
|
||
funcGen.jumpTargets.popBreakTarget();
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(endbb, irs->scopebb());
|
||
}
|
||
|
||
irs->ir->SetInsertPoint(oldbb);
|
||
if (useSwitchInst) {
|
||
// The case index value.
|
||
LLValue *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 (!PGO.emitsInstrumentation()) {
|
||
si = llvm::SwitchInst::Create(condVal, defaultTargetBB, caseCount,
|
||
irs->scopebb());
|
||
for (size_t i = 0; i < caseCount; ++i) {
|
||
si->addCase(isaConstantInt(indices[i]),
|
||
funcGen.switchTargets.get((*cases)[i]));
|
||
}
|
||
} else {
|
||
auto switchbb = irs->scopebb();
|
||
// Add PGO instrumentation.
|
||
// Create "default" counter bb.
|
||
{
|
||
llvm::BasicBlock *defaultcntr =
|
||
irs->insertBBBefore(defaultTargetBB, "defaultcntr");
|
||
irs->ir->SetInsertPoint(defaultcntr);
|
||
if (stmt->sdefault)
|
||
PGO.emitCounterIncrement(stmt->sdefault);
|
||
llvm::BranchInst::Create(defaultTargetBB, defaultcntr);
|
||
// Create switch
|
||
si = llvm::SwitchInst::Create(condVal, defaultcntr, caseCount,
|
||
switchbb);
|
||
}
|
||
|
||
// Create and add case counter bbs.
|
||
for (size_t i = 0; i < caseCount; ++i) {
|
||
const auto cs = (*cases)[i];
|
||
const auto body = funcGen.switchTargets.get(cs);
|
||
|
||
auto casecntr = irs->insertBBBefore(body, "casecntr");
|
||
irs->ir->SetInsertPoint(casecntr);
|
||
PGO.emitCounterIncrement(cs);
|
||
llvm::BranchInst::Create(body, casecntr);
|
||
si->addCase(isaConstantInt(indices[i]), casecntr);
|
||
}
|
||
}
|
||
|
||
// 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 = irs->insertBBBefore(endbb, "checkcase");
|
||
llvm::BranchInst::Create(nextbb, irs->scopebb());
|
||
|
||
if (stmt->sdefault && PGO.emitsInstrumentation()) {
|
||
// Prepend extra BB to "default:" to increment profiling counter.
|
||
llvm::BasicBlock *defaultcntr =
|
||
irs->insertBBBefore(defaultTargetBB, "defaultcntr");
|
||
irs->ir->SetInsertPoint(defaultcntr);
|
||
PGO.emitCounterIncrement(stmt->sdefault);
|
||
llvm::BranchInst::Create(defaultTargetBB, defaultcntr);
|
||
defaultTargetBB = defaultcntr;
|
||
}
|
||
|
||
irs->ir->SetInsertPoint(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 = irs->insertBBBefore(endbb, "checkcase");
|
||
|
||
// Add case counters for PGO in front of case body
|
||
const auto cs = (*cases)[i];
|
||
auto casejumptargetbb = funcGen.switchTargets.get(cs);
|
||
if (PGO.emitsInstrumentation()) {
|
||
llvm::BasicBlock *casecntr =
|
||
irs->insertBBBefore(casejumptargetbb, "casecntr");
|
||
const auto savedInsertPoint = irs->saveInsertPoint();
|
||
irs->ir->SetInsertPoint(casecntr);
|
||
PGO.emitCounterIncrement(cs);
|
||
llvm::BranchInst::Create(casejumptargetbb, casecntr);
|
||
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->ir->SetInsertPoint(nextbb);
|
||
}
|
||
|
||
llvm::BranchInst::Create(defaultTargetBB, irs->scopebb());
|
||
}
|
||
|
||
irs->ir->SetInsertPoint(endbb);
|
||
// PGO counter tracks exit point of switch statement:
|
||
PGO.emitCounterIncrement(stmt);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(CaseStatement *stmt) override {
|
||
IF_LOG Logger::println("CaseStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &funcGen = irs->funcGen();
|
||
auto &PGO = funcGen.pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
const auto body = funcGen.switchTargets.getOrCreate(stmt, "case", *irs);
|
||
// The BB may have already been created by a `goto case` statement.
|
||
// Move it after the current scope BB for lexical order.
|
||
body->moveAfter(irs->scopebb());
|
||
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(body, irs->scopebb());
|
||
}
|
||
|
||
irs->ir->SetInsertPoint(body);
|
||
|
||
assert(stmt->statement);
|
||
irs->DBuilder.EmitBlockStart(stmt->statement->loc);
|
||
if (!isAssertFalse(stmt->statement)) {
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
}
|
||
if (stmt->gototarget) {
|
||
PGO.emitCounterIncrement(PGO.getCounterPtr(stmt, 1));
|
||
}
|
||
stmt->statement->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(DefaultStatement *stmt) override {
|
||
IF_LOG Logger::println("DefaultStatement::toIR(): %s", stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &funcGen = irs->funcGen();
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
const auto body = funcGen.switchTargets.getOrCreate(stmt, "default", *irs);
|
||
// The BB may have already been created.
|
||
// Move it after the current scope BB for lexical order.
|
||
body->moveAfter(irs->scopebb());
|
||
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(body, irs->scopebb());
|
||
}
|
||
|
||
irs->ir->SetInsertPoint(body);
|
||
|
||
assert(stmt->statement);
|
||
irs->DBuilder.EmitBlockStart(stmt->statement->loc);
|
||
if (!isAssertFalse(stmt->statement)) {
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
}
|
||
if (stmt->gototarget) {
|
||
PGO.emitCounterIncrement(PGO.getCounterPtr(stmt, 1));
|
||
}
|
||
stmt->statement->accept(this);
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(UnrolledLoopStatement *stmt) 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->length) {
|
||
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 end block
|
||
llvm::BasicBlock *endbb = irs->insertBB("unrolledend");
|
||
|
||
// create a block for each statement
|
||
size_t nstmt = stmt->statements->length;
|
||
llvm::SmallVector<llvm::BasicBlock *, 4> blocks(nstmt, nullptr);
|
||
for (size_t i = 0; i < nstmt; i++)
|
||
blocks[i] = irs->insertBBBefore(endbb, "unrolledstmt");
|
||
|
||
// enter first stmt
|
||
if (!irs->scopereturned()) {
|
||
irs->ir->CreateBr(blocks[0]);
|
||
}
|
||
|
||
// do statements
|
||
Statement **stmts = &(*stmt->statements)[0];
|
||
|
||
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->ir->SetInsertPoint(thisbb);
|
||
|
||
// push loop scope
|
||
// continue goes to next statement, break goes to end
|
||
irs->funcGen().jumpTargets.pushLoopTarget(stmt, nextbb, endbb);
|
||
|
||
PGO.emitCounterIncrement(s);
|
||
|
||
// do statement
|
||
s->accept(this);
|
||
|
||
// pop loop scope
|
||
irs->funcGen().jumpTargets.popLoopTarget();
|
||
|
||
// next stmt
|
||
if (!irs->scopereturned()) {
|
||
irs->ir->CreateBr(nextbb);
|
||
}
|
||
}
|
||
|
||
irs->ir->SetInsertPoint(endbb);
|
||
|
||
// PGO counter tracks the continuation after the loop
|
||
PGO.emitCounterIncrement(stmt);
|
||
|
||
// end the dwarf lexical block
|
||
irs->DBuilder.EmitBlockEnd();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ForeachStatement *stmt) 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->length == 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 == TOK::foreach_) {
|
||
new llvm::StoreInst(zerokey, keyvar, irs->scopebb());
|
||
} else {
|
||
new llvm::StoreInst(niters, keyvar, irs->scopebb());
|
||
}
|
||
|
||
llvm::BasicBlock *condbb = irs->insertBB("foreachcond");
|
||
llvm::BasicBlock *bodybb = irs->insertBBAfter(condbb, "foreachbody");
|
||
llvm::BasicBlock *nextbb = irs->insertBBAfter(bodybb, "foreachnext");
|
||
llvm::BasicBlock *endbb = irs->insertBBAfter(nextbb, "foreachend");
|
||
|
||
llvm::BranchInst::Create(condbb, irs->scopebb());
|
||
|
||
// condition
|
||
irs->ir->SetInsertPoint(condbb);
|
||
|
||
LLValue *done = nullptr;
|
||
LLValue *load = DtoLoad(keytype, keyvar);
|
||
if (stmt->op == TOK::foreach_) {
|
||
done = irs->ir->CreateICmpULT(load, niters);
|
||
} else if (stmt->op == TOK::foreach_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->ir->SetInsertPoint(bodybb);
|
||
PGO.emitCounterIncrement(stmt);
|
||
|
||
// get value for this iteration
|
||
LLValue *loadedKey = DtoLoad(keytype, keyvar);
|
||
LLValue *gep = DtoGEP1(DtoMemType(aggrval->type->nextOf()), val, loadedKey);
|
||
|
||
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, EXP::assign);
|
||
getIrLocal(stmt->value)->value = valvar;
|
||
} else {
|
||
// Use the GEP as the address of the value variable.
|
||
DtoRawVarDeclaration(stmt->value, gep);
|
||
}
|
||
|
||
// emit body
|
||
irs->funcGen().jumpTargets.pushLoopTarget(stmt, nextbb, endbb);
|
||
if (stmt->_body) {
|
||
stmt->_body->accept(this);
|
||
}
|
||
irs->funcGen().jumpTargets.popLoopTarget();
|
||
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(nextbb, irs->scopebb());
|
||
}
|
||
|
||
// next
|
||
irs->ir->SetInsertPoint(nextbb);
|
||
if (stmt->op == TOK::foreach_) {
|
||
LLValue *load = DtoLoad(keytype, 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->ir->SetInsertPoint(endbb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ForeachRangeStatement *stmt) 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);
|
||
LLType *keytype = DtoType(stmt->key->type);
|
||
// store initial value in key
|
||
if (stmt->op == TOK::foreach_) {
|
||
DtoStore(lower, keyval);
|
||
} else {
|
||
DtoStore(upper, keyval);
|
||
}
|
||
|
||
// set up the block we'll need
|
||
llvm::BasicBlock *condbb = irs->insertBB("foreachrange_cond");
|
||
llvm::BasicBlock *bodybb = irs->insertBBAfter(condbb, "foreachrange_body");
|
||
llvm::BasicBlock *nextbb = irs->insertBBAfter(bodybb, "foreachrange_next");
|
||
llvm::BasicBlock *endbb = irs->insertBBAfter(nextbb, "foreachrange_end");
|
||
|
||
// jump to condition
|
||
llvm::BranchInst::Create(condbb, irs->scopebb());
|
||
|
||
// CONDITION
|
||
irs->ir->SetInsertPoint(condbb);
|
||
|
||
// first we test that lwr < upr
|
||
lower = DtoLoad(keytype, keyval);
|
||
assert(lower->getType() == upper->getType());
|
||
llvm::ICmpInst::Predicate cmpop;
|
||
if (isLLVMUnsigned(stmt->key->type)) {
|
||
cmpop = (stmt->op == TOK::foreach_) ? llvm::ICmpInst::ICMP_ULT
|
||
: llvm::ICmpInst::ICMP_UGT;
|
||
} else {
|
||
cmpop = (stmt->op == TOK::foreach_) ? 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->ir->SetInsertPoint(bodybb);
|
||
PGO.emitCounterIncrement(stmt);
|
||
|
||
// reverse foreach decrements here
|
||
if (stmt->op == TOK::foreach_reverse_) {
|
||
LLValue *v = DtoLoad(keytype, keyval);
|
||
LLValue *one = LLConstantInt::get(v->getType(), 1, false);
|
||
v = irs->ir->CreateSub(v, one);
|
||
DtoStore(v, keyval);
|
||
}
|
||
|
||
// emit body
|
||
irs->funcGen().jumpTargets.pushLoopTarget(stmt, nextbb, endbb);
|
||
if (stmt->_body) {
|
||
stmt->_body->accept(this);
|
||
}
|
||
irs->funcGen().jumpTargets.popLoopTarget();
|
||
|
||
// jump to next iteration
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(nextbb, irs->scopebb());
|
||
}
|
||
|
||
// NEXT
|
||
irs->ir->SetInsertPoint(nextbb);
|
||
|
||
// forward foreach increments here
|
||
if (stmt->op == TOK::foreach_) {
|
||
LLValue *v = DtoLoad(keytype, 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->ir->SetInsertPoint(endbb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(LabelStatement *stmt) 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 =
|
||
irs->insertBB(llvm::Twine("label.") + stmt->ident->toChars());
|
||
irs->funcGen().jumpTargets.addLabelTarget(stmt->ident, labelBB);
|
||
|
||
if (!irs->scopereturned()) {
|
||
llvm::BranchInst::Create(labelBB, irs->scopebb());
|
||
}
|
||
|
||
irs->ir->SetInsertPoint(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) 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 = irs->insertBB("aftergoto");
|
||
irs->ir->SetInsertPoint(bb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(GotoDefaultStatement *stmt) override {
|
||
IF_LOG Logger::println("GotoDefaultStatement::toIR(): %s",
|
||
stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &funcGen = irs->funcGen();
|
||
auto &PGO = funcGen.pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
|
||
assert(!irs->scopereturned());
|
||
|
||
const auto defaultBB = funcGen.switchTargets.get(stmt->sw->sdefault);
|
||
llvm::BranchInst::Create(defaultBB, irs->scopebb());
|
||
|
||
// TODO: Should not be needed.
|
||
llvm::BasicBlock *bb = irs->insertBB("aftergotodefault");
|
||
irs->ir->SetInsertPoint(bb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(GotoCaseStatement *stmt) override {
|
||
IF_LOG Logger::println("GotoCaseStatement::toIR(): %s",
|
||
stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
auto &funcGen = irs->funcGen();
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||
|
||
emitCoverageLinecountInc(stmt->loc);
|
||
|
||
assert(!irs->scopereturned());
|
||
|
||
const auto caseBB =
|
||
funcGen.switchTargets.getOrCreate(stmt->cs, "goto_case", *irs);
|
||
llvm::BranchInst::Create(caseBB, irs->scopebb());
|
||
|
||
// TODO: Should not be needed.
|
||
llvm::BasicBlock *bb = irs->insertBB("aftergotocase");
|
||
irs->ir->SetInsertPoint(bb);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(WithStatement *stmt) 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) override {
|
||
IF_LOG Logger::println("SwitchErrorStatement::toIR(): %s",
|
||
stmt->loc.toChars());
|
||
LOG_SCOPE;
|
||
assert(!irs->dcomputetarget);
|
||
|
||
auto &PGO = irs->funcGen().pgo;
|
||
PGO.setCurrentStmt(stmt);
|
||
|
||
if (global.params.checkAction == CHECKACTION_C) {
|
||
auto module = irs->func()->decl->getModule();
|
||
DtoCAssert(module, stmt->loc, DtoConstCString("no switch default"));
|
||
return;
|
||
}
|
||
|
||
// `stmt->exp` is a CallExpression to `object.__switch_error!()`
|
||
assert(stmt->exp);
|
||
toElemDtor(stmt->exp);
|
||
|
||
gIR->ir->CreateUnreachable();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(InlineAsmStatement *stmt) override {
|
||
assert(!irs->dcomputetarget);
|
||
AsmStatement_toIR(stmt, irs);
|
||
}
|
||
|
||
void visit(GccAsmStatement *stmt) override {
|
||
assert(!irs->dcomputetarget);
|
||
GccAsmStatement_toIR(stmt, irs);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(CompoundAsmStatement *stmt) override {
|
||
assert(!irs->dcomputetarget);
|
||
CompoundAsmStatement_toIR(stmt, irs);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ImportStatement *stmt) override {
|
||
for (auto s : *stmt->imports) {
|
||
assert(s->isImport());
|
||
irs->DBuilder.EmitImport(static_cast<Import *>(s));
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(Statement *stmt) override {
|
||
error(stmt->loc, "Statement type Statement not implemented: `%s`",
|
||
stmt->toChars());
|
||
fatal();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(PragmaStatement *stmt) 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);
|
||
}
|