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()) { if (global.params.obj && !modules.empty()) {
ldc::CodeGenerator cg(getGlobalContext(), singleObj); 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]; Module *const m = modules[i];
if (global.params.verbose) { if (global.params.verbose) {
fprintf(global.stdmsg, "code %s\n", m->toChars()); fprintf(global.stdmsg, "code %s\n", m->toChars());

View file

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

View file

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