Fix inlining problem: reverse order in which module array is codegenned.

This commit is contained in:
Johan Engelen 2016-06-22 17:06:06 +02:00
parent 2e9b196c09
commit a3f0678283
4 changed files with 67 additions and 53 deletions

View file

@ -1393,7 +1393,15 @@ int cppmain(int argc, char **argv) {
if (global.params.obj && !modules.empty()) {
ldc::CodeGenerator cg(getGlobalContext(), singleObj);
for (unsigned i = 0; i < modules.dim; i++) {
// When inlining is enabled, we are calling semantic3 on function
// declarations, which may _add_ members to the first module in the modules
// array. These added functions must be codegenned, because these functions
// may be "alwaysinline" and linker problems arise otherwise with templates
// that have __FILE__ as parameters (which must be `pragma(inline, true);`)
// Therefore, codegen is done in reverse order with members[0] last, to make
// sure these functions (added to members[0] by members[x>0]) are
// codegenned.
for (d_size_t i = modules.dim; i-- > 0;) {
Module *const m = modules[i];
if (global.params.verbose) {
fprintf(global.stdmsg, "code %s\n", m->toChars());

View file

@ -79,49 +79,69 @@ bool defineAsExternallyAvailable(FuncDeclaration &fdecl) {
IF_LOG Logger::println("Enter defineAsExternallyAvailable");
LOG_SCOPE
// Try to do cheap checks first.
// Implementation note: try to do cheap checks first.
if (fdecl.neverInline || fdecl.inlining == PINLINEnever)
if (fdecl.neverInline || fdecl.inlining == PINLINEnever) {
IF_LOG Logger::println("pragma(inline, false) specified");
return false;
}
// pragma(inline, true) functions will be inlined even at -O0
if (!willInline() && (fdecl.inlining != PINLINEalways))
if (!willInline() && (fdecl.inlining != PINLINEalways)) {
IF_LOG Logger::println("Commandline flags indicate no inlining");
return false;
}
if (fdecl.isUnitTestDeclaration())
if (fdecl.isUnitTestDeclaration()) {
IF_LOG Logger::println("isUnitTestDeclaration() == true");
return false;
if (fdecl.isFuncAliasDeclaration())
}
if (fdecl.isFuncAliasDeclaration()) {
IF_LOG Logger::println("isFuncAliasDeclaration() == true");
return false;
if (!fdecl.fbody)
}
if (!fdecl.fbody) {
IF_LOG Logger::println("No function body available for inlining");
return false;
}
// Disable inlining functions from object.d because of TypeInfo related issue
if (fdecl.getModule()->ident == Id::object)
// Disable inlining functions from object.d because of TypeInfo related
// issue
if (fdecl.getModule()->ident == Id::object) {
IF_LOG Logger::println("Inlining of object.d functions is disabled");
return false;
}
if (alreadyOrWillBeDefined(fdecl))
if (alreadyOrWillBeDefined(fdecl)) {
IF_LOG Logger::println("Function will be defined later.");
return false;
}
// Weak-linkage functions can not be inlined.
if (hasWeakUDA(&fdecl))
if (hasWeakUDA(&fdecl)) {
IF_LOG Logger::println("@weak functions cannot be inlined.");
return false;
}
if (!isInlineCandidate(fdecl))
return false;
IF_LOG Logger::println("Potential inlining candidate");
// If semantic analysis is already complete, the function will be codegenned
// elsewhere.
if (fdecl.semanticRun >= PASSsemantic3)
if (fdecl.semanticRun >= PASSsemantic3) {
// If semantic analysis has come this far, the function will be defined
// elsewhere and should not get the available_externally attribute from
// here.
IF_LOG Logger::println("Semantic analysis already completed");
return false;
}
{
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.
// The inlining is aggressive and may give semantic errors that are
// forward referencing errors. Simply avoid those cases for inlining.
uint errors = global.startGagging();
global.gaggedForInlining = true;
@ -144,9 +164,8 @@ bool defineAsExternallyAvailable(FuncDeclaration &fdecl) {
// For naked functions (inline assembly), we emit the assembly directly as
// globals in the text section. Emitting them during this inline pass will
// therefore result in multiple definitions. Solution: don't try to inline
// them.
// These naked functions don't appear to be inlined anyway, so it is pointless
// at this moment to try.
// them. These naked functions don't appear to be inlined anyway, so it is
// pointless at this moment to try.
// FuncDeclaration::naked is set by the AsmParser during semantic analysis.
if (fdecl.naked) {
IF_LOG Logger::println("Naked asm functions cannot be inlined.");
@ -912,22 +931,11 @@ void DtoDefineFunction(FuncDeclaration *fd, bool availableExternally) {
}
if (!availableExternally) {
// Check whether the frontend knows that the function is already defined
// in some other module (see DMD's FuncDeclaration::toObjFile).
for (FuncDeclaration *f = fd; f;) {
if (!f->isInstantiated() && f->inNonRoot()) {
if (!alreadyOrWillBeDefined(*fd)) {
IF_LOG Logger::println("Skipping '%s'.", fd->toPrettyChars());
// TODO: Emit as available_externally for inlining purposes instead
// (see #673).
fd->ir->setDefined();
return;
}
if (f->isNested()) {
f = f->toParent2()->isFuncDeclaration();
} else {
break;
}
}
}
DtoDeclareFunction(fd);

View file

@ -3,9 +3,14 @@
// RUN: %ldc %s -I%S -c -output-ll -release -enable-inlining -O0 -of=%t.O0.ll && FileCheck %s < %t.O0.ll
// Test linking too (separate compilation)
// RUN: %ldc -singleobj %S/inputs/inlinables.d %s -I%S -c -output-ll -release -enable-inlining -O0 -of=%t.singleobj.O0.ll && FileCheck %s < %t.singleobj.O0.ll
// Test linking too.
// Separate compilation
// RUN: %ldc -c -enable-inlining %S/inputs/inlinables.d -of=%t.inlinables%obj \
// RUN: && %ldc -I%S -enable-inlining %t.inlinables%obj %s -of=%t%exe
// Singleobj compilation
// RUN: %ldc -I%S -enable-inlining -singleobj %S/inputs/inlinables.d %s -of=%t2%exe
import inputs.inlinables;
import std.stdio;
@ -15,9 +20,6 @@ int foo(int i)
{
return call_template_foo(i);
}
// CHECK-NOT: declare{{.*}}D6inputs10inlinables20__T12template_fooTiZ12template_fooUNaNbNiNfiZi
// CHECK: define{{.*}}D6inputs10inlinables20__T12template_fooTiZ12template_fooUNaNbNiNfiZi
// CHECK-SAME: #[[ATTR1:[0-9]+]]
// stdio.File.flush contains a call to errnoException, which contains __FILE__ as default template parameter.
// Make sure the symbol is inlined/defined and not declared (which will lead to linker errors if the location
@ -27,18 +29,15 @@ void ggg(ref File f)
f.flush();
}
// CHECK-NOT: declare{{.*}}D3std9exception{{[0-9]+}}__T12errnoEnforce
// CHECK: define{{.*}}D3std9exception{{[0-9]+}}__T12errnoEnforce
// CHECK-SAME: #[[ATTR2:[0-9]+]]
void hhh()
{
auto f = File("filename","r");
}
void main()
{
}
// CHECK-NOT: declare{{.*}}D6inputs10inlinables20__T12template_fooTiZ12template_foo
// CHECK-NOT: declare{{.*}}D3std9exception{{[0-9]+}}__T12errnoEnforce
// CHECK-DAG: define{{.*}}D6inputs10inlinables20__T12template_fooTiZ12template_foo{{.*}}) #[[ATTR1:[0-9]+]]
// CHECK-DAG: define{{.*}}D3std9exception{{[0-9]+}}__T12errnoEnforce{{.*}}) #[[ATTR2:[0-9]+]]
// CHECK-DAG: attributes #[[ATTR1]] ={{.*}} alwaysinline
// CHECK-DAG: attributes #[[ATTR2]] ={{.*}} alwaysinline

View file

@ -102,13 +102,12 @@ pragma(inline, true) extern (C) void naked_asm_func()
}
}
pragma(inline, true)
int call_template_foo(int i) {
pragma(inline, true) int call_template_foo(int i)
{
return template_foo(i);
}
pragma(inline, true)
T template_foo(T)(T i)
pragma(inline, true) T template_foo(T)(T i)
{
return i;
}