1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
//===-- function-inlining.cpp ---------------------------------------------===//
//
// LDC – the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#include "gen/function-inlining.h"
#include "dmd/declaration.h"
#include "dmd/errors.h"
#include "dmd/expression.h"
#include "dmd/globals.h"
#include "dmd/id.h"
#include "dmd/module.h"
#include "dmd/statement.h"
#include "dmd/template.h"
#include "gen/irstate.h"
#include "gen/logger.h"
#include "gen/optimizer.h"
#include "gen/recursivevisitor.h"
#include "gen/uda.h"
namespace {
/// An ASTVisitor that checks whether the number of statements is larger than a
/// certain number.
struct MoreThanXStatements : public StoppableVisitor {
/// Are there more or fewer statements than `threshold`?.
unsigned threshold;
/// The statement count.
unsigned count;
explicit MoreThanXStatements(unsigned X) : threshold(X), count(0) {}
using StoppableVisitor::visit;
void visit(Statement *stmt) override {
count++;
if (count > threshold)
stop = true;
}
void visit(Expression *exp) override {}
void visit(Declaration *decl) override {}
void visit(Initializer *init) override {}
void visit(Dsymbol *) override {}
};
// Use a heuristic to determine if it could make sense to inline this fdecl.
// Note: isInlineCandidate is called _before_ semantic3 analysis of fdecl.
bool isInlineCandidate(FuncDeclaration &fdecl) {
// Giving maximum inlining potential to LLVM should be possible, but we
// restrict it to save some compile time.
// return true;
// TODO: make the heuristic more sophisticated?
// In the end, LLVM will make the decision whether to _actually_ inline.
// The statement count threshold is completely arbitrary. Also, all
// statements are weighed the same.
unsigned statementThreshold = 10;
MoreThanXStatements statementCounter(statementThreshold);
RecursiveWalker walker(&statementCounter, false);
fdecl.fbody->accept(&walker);
IF_LOG Logger::println("Contains %u statements or more (threshold = %u).",
statementCounter.count, statementThreshold);
return statementCounter.count <= statementThreshold;
}
} // end anonymous namespace
bool skipCodegen(FuncDeclaration &fdecl) {
if (fdecl.isFuncLiteralDeclaration()) // emitted into each referencing CU
return false;
for (FuncDeclaration *f = &fdecl; f;) {
if (f->inNonRoot()) { // false if instantiated
return true;
}
if (f->isNested()) {
f = f->toParent2()->isFuncDeclaration();
} else {
break;
}
}
return false;
}
bool defineAsExternallyAvailable(FuncDeclaration &fdecl) {
IF_LOG Logger::println("Enter defineAsExternallyAvailable");
LOG_SCOPE
// Implementation note: try to do cheap checks first.
if (fdecl.neverInline || fdecl.inlining == PINLINE::never) {
IF_LOG Logger::println("pragma(inline, false) specified");
return false;
}
// pragma(inline, true) functions will be inlined even at -O0
if (fdecl.inlining == PINLINE::always) {
IF_LOG Logger::println(
"pragma(inline, true) specified, overrides cmdline flags");
} else if (!willCrossModuleInline()) {
IF_LOG Logger::println("Commandline flags indicate no inlining");
return false;
}
if (fdecl.isFuncLiteralDeclaration()) {
// defined as discardable linkonce_odr in each referencing CU
IF_LOG Logger::println("isFuncLiteralDeclaration() == true");
return false;
}
if (fdecl.isUnitTestDeclaration()) {
IF_LOG Logger::println("isUnitTestDeclaration() == true");
return false;
}
if (fdecl.isFuncAliasDeclaration()) {
IF_LOG Logger::println("isFuncAliasDeclaration() == true");
return false;
}
if (!fdecl.fbody) {
IF_LOG Logger::println("No function body available for inlining");
return false;
}
// Because the frontend names `__invariant*` functions differently depending
// on the compilation order, we cannot emit the `__invariant` wrapper that
// calls the `__invariant*` functions.
// This is a workaround, the frontend needs to be changed such that the
// __invariant* names no longer depend on semantic analysis order.
// See https://github.com/ldc-developers/ldc/issues/1678
if (fdecl.isInvariantDeclaration()) {
IF_LOG Logger::println("__invariant cannot be emitted.");
return false;
}
Module *module = fdecl.getModule();
if (module == gIR->dmodule || (module->isRoot() && global.params.oneobj)) {
IF_LOG Logger::println(
"Function will be regularly defined later in this compilation unit.");
return false;
}
// Weak-linkage functions can not be inlined.
if (hasWeakUDA(&fdecl)) {
IF_LOG Logger::println("@weak functions cannot be inlined.");
return false;
}
if (fdecl.inlining != PINLINE::always && !isInlineCandidate(fdecl))
return false;
IF_LOG Logger::println("Potential inlining candidate");
if (fdecl.semanticRun < PASS::semantic3) {
IF_LOG Logger::println("Do semantic analysis");
LOG_SCOPE
// The inlining is aggressive and may give semantic errors that are
// forward referencing errors. Simply avoid those cases for inlining.
unsigned errors = global.startGagging();
global.gaggedForInlining = true;
bool semantic_error = false;
if (fdecl.functionSemantic3()) {
Module::runDeferredSemantic3();
} else {
IF_LOG Logger::println("Failed functionSemantic3.");
semantic_error = true;
}
global.gaggedForInlining = false;
if (global.endGagging(errors) || semantic_error) {
IF_LOG Logger::println("Errors occured during semantic analysis.");
return false;
}
assert(fdecl.semanticRun >= PASS::semantic3done);
}
// FuncDeclaration::naked is set by the AsmParser during semantic3 analysis,
// and so this check can only be done at this late point.
if (fdecl.isNaked()) {
IF_LOG Logger::println("Naked asm functions cannot be inlined.");
return false;
}
IF_LOG Logger::println("defineAsExternallyAvailable? Yes.");
return true;
}
|