Skip to content

[SYCL] Emit an aliased function only if it is used #2430

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 57 additions & 8 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2264,6 +2264,30 @@ void CodeGenModule::EmitDeferred() {
CurDeclsToEmit.swap(DeferredDeclsToEmit);

for (GlobalDecl &D : CurDeclsToEmit) {
const ValueDecl *VD = cast<ValueDecl>(D.getDecl());
// If emitting for SYCL device, emit the deferred alias
// as well as what it aliases.
if (LangOpts.SYCLIsDevice) {
if (AliasAttr *Attr = VD->getAttr<AliasAttr>()) {
StringRef AliaseeName = Attr->getAliasee();
auto DDI = DeferredDecls.find(AliaseeName);
// Emit what is aliased first.
if (DDI != DeferredDecls.end()) {
llvm::GlobalValue *AliaseeGV = dyn_cast<llvm::GlobalValue>(
GetAddrOfGlobal(DDI->second, ForDefinition));
if (!AliaseeGV)
AliaseeGV = GetGlobalValue(getMangledName(DDI->second));
assert(AliaseeGV);
EmitGlobalDefinition(DDI->second, AliaseeGV);
// Remove the entry just added to the DeferredDeclsToEmit
// since we have emitted it.
DeferredDeclsToEmit.pop_back();
}
// Now emit the alias itself.
EmitAliasDefinition(D);
continue;
}
}
// We should call GetAddrOfGlobal with IsForDefinition set to true in order
// to get GlobalValue with exactly the type we need, not something that
// might had been created for another decl with the same mangled name but
Expand Down Expand Up @@ -2296,6 +2320,20 @@ void CodeGenModule::EmitDeferred() {
// Otherwise, emit the definition and move on to the next one.
EmitGlobalDefinition(D, GV);

if (LangOpts.SYCLIsDevice) {
// If there are any aliases deferred for this, emit those now.
for (auto It = DeferredAliases.begin(); It != DeferredAliases.end();
/*no increment*/) {
const ValueDecl *Global = cast<ValueDecl>(It->second.getDecl());
if (It->first == getMangledName(D)) {
EmitAliasDefinition(Global);
It = DeferredAliases.erase(It);
} else {
++It;
}
}
}

// If we found out that we need to emit more decls, do that recursively.
// This has the advantage that the decls are emitted in a DFS and related
// ones are close together, which is convenient for testing.
Expand Down Expand Up @@ -2619,9 +2657,19 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
return;

// If this is an alias definition (which otherwise looks like a declaration)
// emit it now.
if (Global->hasAttr<AliasAttr>())
return EmitAliasDefinition(GD);
// handle it now.
if (AliasAttr *Attr = Global->getAttr<AliasAttr>()) {
// Emit the alias here if it is not SYCL device compilation.
if (!LangOpts.SYCLIsDevice)
return EmitAliasDefinition(GD);
// Defer for SYCL devices, until either the alias or what it aliases
// is used.
StringRef MangledName = getMangledName(GD);
DeferredDecls[MangledName] = GD;
StringRef AliaseeName = Attr->getAliasee();
DeferredAliases[AliaseeName] = GD;
return;
}

// IFunc like an alias whose value is resolved at runtime by calling resolver.
if (Global->hasAttr<IFuncAttr>())
Expand Down Expand Up @@ -4836,20 +4884,21 @@ void CodeGenModule::EmitAliasDefinition(GlobalDecl GD) {
// if a deferred decl.
llvm::Constant *Aliasee;
llvm::GlobalValue::LinkageTypes LT;
unsigned AS;
if (isa<llvm::FunctionType>(DeclTy)) {
Aliasee = GetOrCreateLLVMFunction(AA->getAliasee(), DeclTy, GD,
/*ForVTable=*/false);
LT = getFunctionLinkage(GD);
AS = Aliasee->getType()->getPointerAddressSpace();
} else {
Aliasee = GetOrCreateLLVMGlobal(AA->getAliasee(),
llvm::PointerType::getUnqual(DeclTy),
AS = ArgInfoAddressSpace(GetGlobalVarAddressSpace(/*D=*/nullptr));
Aliasee = GetOrCreateLLVMGlobal(AA->getAliasee(), DeclTy->getPointerTo(AS),
/*D=*/nullptr);
LT = getLLVMLinkageVarDefinition(cast<VarDecl>(GD.getDecl()),
D->getType().isConstQualified());
}

// Create the new alias itself, but don't set a name yet.
unsigned AS = Aliasee->getType()->getPointerAddressSpace();
auto *GA =
llvm::GlobalAlias::create(DeclTy, AS, LT, "", Aliasee, &getModule());

Expand All @@ -4870,8 +4919,8 @@ void CodeGenModule::EmitAliasDefinition(GlobalDecl GD) {
// Remove it and replace uses of it with the alias.
GA->takeName(Entry);

Entry->replaceAllUsesWith(llvm::ConstantExpr::getBitCast(GA,
Entry->getType()));
Entry->replaceAllUsesWith(
llvm::ConstantExpr::getBitCast(GA, Entry->getType()));
Entry->eraseFromParent();
} else {
GA->setName(MangledName);
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,11 @@ class CodeGenModule : public CodeGenTypeCache {
/// yet.
std::map<StringRef, GlobalDecl> DeferredDecls;

/// This contains all the aliases that are deferred for emission until
/// they or what they alias are actually used. Note that the StringRef
/// associated in this map is that of the aliasee.
std::map<StringRef, GlobalDecl> DeferredAliases;

/// This is a list of deferred decls which we have seen that *are* actually
/// referenced. These get code generated when the module is done.
std::vector<GlobalDecl> DeferredDeclsToEmit;
Expand Down
45 changes: 45 additions & 0 deletions clang/test/CodeGenSYCL/sycl-device-alias.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// RUN: %clang_cc1 -fsycl -fsycl-is-device -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s
// Test that aliasing does not force an unused entity to be emitted

// CHECK-NOT: define spir_func void @unused_func()
extern "C" void unused_func() {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like alias attribute can be also applied to a global variable. Could you please add corresponding tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I did this. (I didn't change the title to avoid needing to force-push, but I will do that when there are no further code-review comments.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think test renaming shouldn't require doing force-push. See #2420 for example. It has a commit which just renames a test - 8b24581 . I didn't do any force-pushes there. Also, current title works for me as well.

// CHECK-NOT: @unused_aliaser
extern "C" void unused_aliaser() __attribute__((alias("unused_func")));
// CHECK-NOT: @unused_int
int unused_int = 3;
// CHECK-NOT: @alias_unused_int
extern int alias_unused_int __attribute__((alias("unused_int")));

// CHECK-DAG: define spir_func void @used_func()
extern "C" void used_func() {}
// CHECK-DAG: @aliaser = alias void (), void ()* @used_func
extern "C" void aliaser() __attribute__((alias("used_func")));

// CHECK-DAG: define spir_func void @func()
extern "C" void func() {}
// CHECK-DAG: @used_aliaser = alias void (), void ()* @func
extern "C" void used_aliaser() __attribute__((alias("func")));

// CHECK-DAG: @used_int = addrspace(1) constant i32 5, align 4
extern "C" const int used_int = 5;
// CHECK-DAG: @alias_used_int = alias i32, i32 addrspace(1)* @used_int
extern "C" const int alias_used_int __attribute__((alias("used_int")));
// CHECK-DAG: @vint = addrspace(1) constant i32 7, align 4
extern "C" const int vint = 7;
// CHECK-DAG: @used_alias_used_int = alias i32, i32 addrspace(1)* @vint
extern "C" const int used_alias_used_int __attribute__((alias("vint")));

// CHECK-DAG: define spir_func void @{{.*}}bar{{.*}}
void bar(const int &i) {}

// CHECK-DAG: define spir_func void @{{.*}}foo{{.*}}
void __attribute__((sycl_device)) foo() {
// CHECK-DAG: call spir_func void @{{.*}}bar{{.*}}@used_int
bar(used_int);
// CHECK-DAG: call spir_func void @{{.*}}bar{{.*}}@used_alias_used_int
bar(used_alias_used_int);
// CHECK-DAG: call spir_func void @used_func()
used_func();
// CHECK-DAG: call spir_func void @used_aliaser()
used_aliaser();
}