mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-03 08:30:47 +03:00
Fix: Prevent regular definitions from being weakened to available_externally
This is exactly what happened for the 4 problematic delegate literals for the Phobos unittests - DtoDefineFunction(fd) could result in that definition ending up as available_externally - all done as part of DtoResolveFunction(), which declared it, and DtoDeclareFunction() defined it as available_externally, and the outer DtoDefineFunction() returned early in that case without fixing up the linkage.
This commit is contained in:
parent
7dddf506a7
commit
bc8dfe3f9f
3 changed files with 52 additions and 28 deletions
|
@ -345,7 +345,7 @@ static llvm::Function *DtoDeclareVaFunction(FuncDeclaration *fdecl) {
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void DtoResolveFunction(FuncDeclaration *fdecl) {
|
void DtoResolveFunction(FuncDeclaration *fdecl, const bool willDeclare) {
|
||||||
if ((!global.params.useUnitTests || !fdecl->type) &&
|
if ((!global.params.useUnitTests || !fdecl->type) &&
|
||||||
fdecl->isUnitTestDeclaration()) {
|
fdecl->isUnitTestDeclaration()) {
|
||||||
IF_LOG Logger::println("Ignoring unittest %s", fdecl->toPrettyChars());
|
IF_LOG Logger::println("Ignoring unittest %s", fdecl->toPrettyChars());
|
||||||
|
@ -423,11 +423,15 @@ void DtoResolveFunction(FuncDeclaration *fdecl) {
|
||||||
LOG_SCOPE;
|
LOG_SCOPE;
|
||||||
|
|
||||||
// queue declaration unless the function is abstract without body
|
// queue declaration unless the function is abstract without body
|
||||||
if (!fdecl->isAbstract() || fdecl->fbody) {
|
if (!willDeclare && (!fdecl->isAbstract() || fdecl->fbody)) {
|
||||||
DtoDeclareFunction(fdecl);
|
DtoDeclareFunction(fdecl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DtoResolveFunction(FuncDeclaration *fdecl) {
|
||||||
|
return DtoResolveFunction(fdecl, false);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -539,8 +543,8 @@ void onlyOneMainCheck(FuncDeclaration *fd) {
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void DtoDeclareFunction(FuncDeclaration *fdecl) {
|
void DtoDeclareFunction(FuncDeclaration *fdecl, const bool willDefine) {
|
||||||
DtoResolveFunction(fdecl);
|
DtoResolveFunction(fdecl, /*willDeclare=*/true);
|
||||||
|
|
||||||
if (fdecl->ir->isDeclared()) {
|
if (fdecl->ir->isDeclared()) {
|
||||||
return;
|
return;
|
||||||
|
@ -567,7 +571,7 @@ void DtoDeclareFunction(FuncDeclaration *fdecl) {
|
||||||
// Check if fdecl should be defined too for cross-module inlining.
|
// Check if fdecl should be defined too for cross-module inlining.
|
||||||
// If true, semantic is fully done for fdecl which is needed for some code
|
// If true, semantic is fully done for fdecl which is needed for some code
|
||||||
// below (e.g. code that uses fdecl->vthis).
|
// below (e.g. code that uses fdecl->vthis).
|
||||||
const bool defineAtEnd = defineAsExternallyAvailable(*fdecl);
|
const bool defineAtEnd = !willDefine && defineAsExternallyAvailable(*fdecl);
|
||||||
if (defineAtEnd) {
|
if (defineAtEnd) {
|
||||||
IF_LOG Logger::println(
|
IF_LOG Logger::println(
|
||||||
"Function is an externally_available inline candidate.");
|
"Function is an externally_available inline candidate.");
|
||||||
|
@ -762,10 +766,14 @@ void DtoDeclareFunction(FuncDeclaration *fdecl) {
|
||||||
if (defineAtEnd) {
|
if (defineAtEnd) {
|
||||||
IF_LOG Logger::println(
|
IF_LOG Logger::println(
|
||||||
"Function is an externally_available inline candidate: define it now.");
|
"Function is an externally_available inline candidate: define it now.");
|
||||||
DtoDefineFunction(fdecl, true);
|
DtoDefineFunction(fdecl, /*linkageAvailableExternally=*/true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DtoDeclareFunction(FuncDeclaration *fdecl) {
|
||||||
|
return DtoDeclareFunction(fdecl, false);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static LinkageWithCOMDAT lowerFuncLinkage(FuncDeclaration *fdecl) {
|
static LinkageWithCOMDAT lowerFuncLinkage(FuncDeclaration *fdecl) {
|
||||||
|
@ -1001,12 +1009,19 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
|
||||||
fatal();
|
fatal();
|
||||||
}
|
}
|
||||||
|
|
||||||
DtoResolveFunction(fd);
|
DtoDeclareFunction(fd, /*willDefine=*/true);
|
||||||
|
assert(fd->ir->isDeclared());
|
||||||
|
|
||||||
|
// DtoDeclareFunction might also set the defined flag for functions we
|
||||||
|
// should not touch.
|
||||||
|
if (fd->ir->isDefined()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fd->ir->setDefined();
|
||||||
|
|
||||||
if (fd->isUnitTestDeclaration() && !global.params.useUnitTests) {
|
if (fd->isUnitTestDeclaration() && !global.params.useUnitTests) {
|
||||||
IF_LOG Logger::println("No code generation for unit test declaration %s",
|
IF_LOG Logger::println("No code generation for unit test declaration %s",
|
||||||
fd->toChars());
|
fd->toChars());
|
||||||
fd->ir->setDefined();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1016,27 +1031,15 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
|
||||||
IF_LOG Logger::println(
|
IF_LOG Logger::println(
|
||||||
"No code generation for typeinfo member %s in @compute code",
|
"No code generation for typeinfo member %s in @compute code",
|
||||||
fd->toChars());
|
fd->toChars());
|
||||||
fd->ir->setDefined();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!linkageAvailableExternally && !alreadyOrWillBeDefined(*fd)) {
|
if (!linkageAvailableExternally && !alreadyOrWillBeDefined(*fd)) {
|
||||||
IF_LOG Logger::println("Skipping '%s'.", fd->toPrettyChars());
|
IF_LOG Logger::println("Skipping '%s'.", fd->toPrettyChars());
|
||||||
fd->ir->setDefined();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DtoDeclareFunction(fd);
|
|
||||||
assert(fd->ir->isDeclared());
|
|
||||||
|
|
||||||
// DtoResolveFunction might also set the defined flag for functions we
|
|
||||||
// should not touch.
|
|
||||||
if (fd->ir->isDefined()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fd->ir->setDefined();
|
|
||||||
|
|
||||||
// We cannot emit nested functions with parents that have not gone through
|
// We cannot emit nested functions with parents that have not gone through
|
||||||
// semantic analysis. This can happen as DMD leaks some template instances
|
// semantic analysis. This can happen as DMD leaks some template instances
|
||||||
// from constraints into the module member list. DMD gets away with being
|
// from constraints into the module member list. DMD gets away with being
|
||||||
|
|
|
@ -215,8 +215,11 @@ LLValue *DtoDelegateEquals(TOK op, LLValue *lhs, LLValue *rhs) {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
LinkageWithCOMDAT DtoLinkage(Dsymbol *sym) {
|
LinkageWithCOMDAT DtoLinkage(Dsymbol *sym) {
|
||||||
auto linkage = (DtoIsTemplateInstance(sym) ? templateLinkage
|
// Function (incl. delegate) literals are emitted into each referencing
|
||||||
: LLGlobalValue::ExternalLinkage);
|
// compilation unit; use template linkage to prevent conflicts.
|
||||||
|
auto linkage = (sym->isFuncLiteralDeclaration() || DtoIsTemplateInstance(sym))
|
||||||
|
? templateLinkage
|
||||||
|
: LLGlobalValue::ExternalLinkage;
|
||||||
|
|
||||||
// If @(ldc.attributes.weak) is applied, override the linkage to WeakAny
|
// If @(ldc.attributes.weak) is applied, override the linkage to WeakAny
|
||||||
if (hasWeakUDA(sym)) {
|
if (hasWeakUDA(sym)) {
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
// Tests that functions are cross-module inlined when emitting multiple object
|
// Tests that functions are cross-module inlined when emitting multiple object
|
||||||
// files.
|
// files.
|
||||||
|
|
||||||
// Generate unoptimized IR for 2 source files as separate compilation units.
|
// Generate unoptimized IR for 2 source files as separate compilation units (in both orders).
|
||||||
// RUN: %ldc -c -output-ll %s %S/inputs/inlinables.d -od=%t
|
// RUN: %ldc -c -output-ll %s %S/inputs/inlinables.d -od=%t && FileCheck %s < %t/inlining_gh3126.ll
|
||||||
// RUN: FileCheck %s < %t/inlining_gh3126.ll
|
// RUN: %ldc -c -output-ll %S/inputs/inlinables.d %s -od=%t && FileCheck %s < %t/inlining_gh3126.ll
|
||||||
|
|
||||||
|
// Now test with another source file making use of inputs.inlinables instead of compiling that module directly.
|
||||||
|
// RUN: %ldc -c -output-ll -I%S %s %S/inlining_imports_pragma.d -od=%t && FileCheck %s < %t/inlining_gh3126.ll
|
||||||
|
// RUN: %ldc -c -output-ll -I%S %S/inlining_imports_pragma.d %s -od=%t && FileCheck %s < %t/inlining_gh3126.ll
|
||||||
|
|
||||||
import inputs.inlinables;
|
import inputs.inlinables;
|
||||||
|
|
||||||
// no other function definitions (always_inline_chain*)
|
// no other function definitions (always_inline_chain*, call_template_foo);
|
||||||
// CHECK-NOT: define
|
// allow template_foo to be instantiated in here though
|
||||||
|
// CHECK-NOT: always_inline_chain
|
||||||
|
// CHECK-NOT: call_template_foo
|
||||||
|
|
||||||
// CHECK: define {{.*}}_D15inlining_gh31263fooFZi
|
// CHECK: define {{.*}}_D15inlining_gh31263fooFZi
|
||||||
int foo()
|
int foo()
|
||||||
|
@ -17,4 +23,16 @@ int foo()
|
||||||
return always_inline_chain0();
|
return always_inline_chain0();
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK-NOT: define
|
// CHECK-NOT: always_inline_chain
|
||||||
|
// CHECK-NOT: call_template_foo
|
||||||
|
|
||||||
|
// CHECK: define {{.*}}_D15inlining_gh31263barFZi
|
||||||
|
int bar()
|
||||||
|
{
|
||||||
|
// no calls to [call_]template_foo
|
||||||
|
// CHECK-NOT: call {{.*}}template_foo
|
||||||
|
return call_template_foo(123);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-NOT: always_inline_chain
|
||||||
|
// CHECK-NOT: call_template_foo
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue