From db279c72f2fea755c8e66cc1f0a69954764e9284 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Fri, 16 Aug 2024 10:32:10 -0700 Subject: [PATCH] [HLSL] Change default linkage of HLSL functions to internal (#95331) An HLSL function has internal linkage by default unless it is: 1. shader entry point function 2. marked with the `export` keyword (https://github.com/llvm/llvm-project/issues/92812) 3. patch constant function (not implemented yet) This PR adds a link-time pass `DXILFinalizeLinkage` that updates the linkage of functions to make sure only shader entry points and exported functions are visible from the module (have _program linkage_). All other functions will be updated to have internal linkage. Related spec update: microsoft/hlsl-specs#295 Fixes #llvm/llvm-project#92071 --- llvm/lib/Target/DirectX/CMakeLists.txt | 1 + .../Target/DirectX/DXILFinalizeLinkage.cpp | 60 +++++++++++++++++ llvm/lib/Target/DirectX/DXILFinalizeLinkage.h | 39 +++++++++++ llvm/lib/Target/DirectX/DirectX.h | 7 ++ .../Target/DirectX/DirectXTargetMachine.cpp | 2 + .../DirectX/conflicting-bitcast-insert.ll | 4 +- llvm/test/CodeGen/DirectX/finalize_linkage.ll | 64 +++++++++++++++++++ .../CodeGen/DirectX/omit-bitcast-insert.ll | 6 +- 8 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 llvm/lib/Target/DirectX/DXILFinalizeLinkage.cpp create mode 100644 llvm/lib/Target/DirectX/DXILFinalizeLinkage.h create mode 100644 llvm/test/CodeGen/DirectX/finalize_linkage.ll diff --git a/llvm/lib/Target/DirectX/CMakeLists.txt b/llvm/lib/Target/DirectX/CMakeLists.txt index 4c70b3f9230edb..f7ae09957996b5 100644 --- a/llvm/lib/Target/DirectX/CMakeLists.txt +++ b/llvm/lib/Target/DirectX/CMakeLists.txt @@ -19,6 +19,7 @@ add_llvm_target(DirectXCodeGen DirectXSubtarget.cpp DirectXTargetMachine.cpp DXContainerGlobals.cpp + DXILFinalizeLinkage.cpp DXILIntrinsicExpansion.cpp DXILMetadata.cpp DXILOpBuilder.cpp diff --git a/llvm/lib/Target/DirectX/DXILFinalizeLinkage.cpp b/llvm/lib/Target/DirectX/DXILFinalizeLinkage.cpp new file mode 100644 index 00000000000000..c02eb768cdf49b --- /dev/null +++ b/llvm/lib/Target/DirectX/DXILFinalizeLinkage.cpp @@ -0,0 +1,60 @@ +//===- DXILFinalizeLinkage.cpp - Finalize linkage of functions ------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DXILFinalizeLinkage.h" +#include "DirectX.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" + +#define DEBUG_TYPE "dxil-finalize-linkage" + +using namespace llvm; + +static bool finalizeLinkage(Module &M) { + SmallPtrSet EntriesAndExports; + + // Find all entry points and export functions + for (Function &EF : M.functions()) { + if (!EF.hasFnAttribute("hlsl.shader") && !EF.hasFnAttribute("hlsl.export")) + continue; + EntriesAndExports.insert(&EF); + } + + for (Function &F : M.functions()) { + if (F.getLinkage() == GlobalValue::ExternalLinkage && + !EntriesAndExports.contains(&F)) { + F.setLinkage(GlobalValue::InternalLinkage); + } + } + + return false; +} + +PreservedAnalyses DXILFinalizeLinkage::run(Module &M, + ModuleAnalysisManager &AM) { + if (finalizeLinkage(M)) + return PreservedAnalyses::none(); + return PreservedAnalyses::all(); +} + +bool DXILFinalizeLinkageLegacy::runOnModule(Module &M) { + return finalizeLinkage(M); +} + +char DXILFinalizeLinkageLegacy::ID = 0; + +INITIALIZE_PASS_BEGIN(DXILFinalizeLinkageLegacy, DEBUG_TYPE, + "DXIL Finalize Linkage", false, false) +INITIALIZE_PASS_END(DXILFinalizeLinkageLegacy, DEBUG_TYPE, + "DXIL Finalize Linkage", false, false) + +ModulePass *llvm::createDXILFinalizeLinkageLegacyPass() { + return new DXILFinalizeLinkageLegacy(); +} diff --git a/llvm/lib/Target/DirectX/DXILFinalizeLinkage.h b/llvm/lib/Target/DirectX/DXILFinalizeLinkage.h new file mode 100644 index 00000000000000..aab1bc3f7a28e2 --- /dev/null +++ b/llvm/lib/Target/DirectX/DXILFinalizeLinkage.h @@ -0,0 +1,39 @@ +//===- DXILFinalizeLinkage.h - Finalize linkage of functions --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// DXILFinalizeLinkage pass updates the linkage of functions to make sure only +/// shader entry points and exported functions are visible from the module (have +/// program linkage). All other functions will be updated to have internal +/// linkage. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TARGET_DIRECTX_DXILFINALIZELINKAGE_H +#define LLVM_TARGET_DIRECTX_DXILFINALIZELINKAGE_H + +#include "llvm/IR/PassManager.h" +#include "llvm/Pass.h" + +namespace llvm { + +class DXILFinalizeLinkage : public PassInfoMixin { +public: + PreservedAnalyses run(Module &M, ModuleAnalysisManager &); + static bool isRequired() { return true; } +}; + +class DXILFinalizeLinkageLegacy : public ModulePass { +public: + DXILFinalizeLinkageLegacy() : ModulePass(ID) {} + bool runOnModule(Module &M) override; + + static char ID; // Pass identification. +}; +} // namespace llvm + +#endif // LLVM_TARGET_DIRECTX_DXILFINALIZELINKAGE_H diff --git a/llvm/lib/Target/DirectX/DirectX.h b/llvm/lib/Target/DirectX/DirectX.h index d056ae2bc488e7..de6c6f2db5b244 100644 --- a/llvm/lib/Target/DirectX/DirectX.h +++ b/llvm/lib/Target/DirectX/DirectX.h @@ -63,6 +63,13 @@ void initializeDXContainerGlobalsPass(PassRegistry &); /// Pass for generating DXContainer part globals. ModulePass *createDXContainerGlobalsPass(); + +/// Initializer for DXILFinalizeLinkage pass. +void initializeDXILFinalizeLinkageLegacyPass(PassRegistry &); + +/// Pass to finalize linkage of functions. +ModulePass *createDXILFinalizeLinkageLegacyPass(); + } // namespace llvm #endif // LLVM_LIB_TARGET_DIRECTX_DIRECTX_H diff --git a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp index 92bd69b69684f0..2c2b86e7193382 100644 --- a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp +++ b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp @@ -48,6 +48,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeDirectXTarget() { initializeDXILTranslateMetadataPass(*PR); initializeDXILResourceMDWrapperPass(*PR); initializeShaderFlagsAnalysisWrapperPass(*PR); + initializeDXILFinalizeLinkageLegacyPass(*PR); } class DXILTargetObjectFile : public TargetLoweringObjectFile { @@ -79,6 +80,7 @@ class DirectXPassConfig : public TargetPassConfig { void addCodeGenPrepare() override { addPass(createDXILIntrinsicExpansionLegacyPass()); addPass(createDXILOpLoweringLegacyPass()); + addPass(createDXILFinalizeLinkageLegacyPass()); addPass(createDXILTranslateMetadataPass()); addPass(createDXILPrepareModulePass()); } diff --git a/llvm/test/CodeGen/DirectX/conflicting-bitcast-insert.ll b/llvm/test/CodeGen/DirectX/conflicting-bitcast-insert.ll index 01084f285cd72a..8f5d3ae8641717 100644 --- a/llvm/test/CodeGen/DirectX/conflicting-bitcast-insert.ll +++ b/llvm/test/CodeGen/DirectX/conflicting-bitcast-insert.ll @@ -7,7 +7,7 @@ define i64 @test(ptr %p) { ret i64 %v } -; CHECK: define i64 @test(ptr %p) { +; CHECK: define internal i64 @test(ptr %p) { ; CHECK-NEXT: %1 = bitcast ptr %p to ptr ; CHECK-NEXT: store i32 0, ptr %1, align 4 ; CHECK-NEXT: %2 = bitcast ptr %p to ptr @@ -19,7 +19,7 @@ define i64 @testGEP(ptr %p) { ret i64 %val } -; CHECK: define i64 @testGEP(ptr %p) { +; CHECK: define internal i64 @testGEP(ptr %p) { ; CHECK-NEXT: %1 = bitcast ptr %p to ptr ; CHECK-NEXT: %ptr = getelementptr i32, ptr %1, i32 4 ; CHECK-NEXT: %2 = bitcast ptr %p to ptr diff --git a/llvm/test/CodeGen/DirectX/finalize_linkage.ll b/llvm/test/CodeGen/DirectX/finalize_linkage.ll new file mode 100644 index 00000000000000..0ee8a5f44593ba --- /dev/null +++ b/llvm/test/CodeGen/DirectX/finalize_linkage.ll @@ -0,0 +1,64 @@ +; RUN: opt -S -dxil-finalize-linkage -mtriple=dxil-unknown-shadermodel6.5-compute %s | FileCheck %s +; RUN: llc %s --filetype=asm -o - | FileCheck %s --check-prefixes=CHECK-LLC + +target triple = "dxilv1.5-pc-shadermodel6.5-compute" + +; DXILFinalizeLinkage changes linkage of all functions that are not +; entry points or exported function to internal. + +; CHECK: define internal void @"?f1@@YAXXZ"() +define void @"?f1@@YAXXZ"() #0 { +entry: + ret void +} + +; CHECK: define internal void @"?f2@@YAXXZ"() +define void @"?f2@@YAXXZ"() #0 { +entry: + ret void +} + +; CHECK: define internal void @"?f3@@YAXXZ"() +define void @"?f3@@YAXXZ"() #0 { +entry: + ret void +} + +; CHECK: define internal void @"?foo@@YAXXZ"() +define void @"?foo@@YAXXZ"() #0 { +entry: + call void @"?f2@@YAXXZ"() #3 + ret void +} + +; Exported function - do not change linkage +; CHECK: define void @"?bar@@YAXXZ"() +define void @"?bar@@YAXXZ"() #1 { +entry: + call void @"?f3@@YAXXZ"() #3 + ret void +} + +; CHECK: define internal void @"?main@@YAXXZ"() #0 +define internal void @"?main@@YAXXZ"() #0 { +entry: + call void @"?foo@@YAXXZ"() #3 + call void @"?bar@@YAXXZ"() #3 + ret void +} + +; Entry point function - do not change linkage +; CHECK: define void @main() #2 +define void @main() #2 { +entry: + call void @"?main@@YAXXZ"() + ret void +} + +attributes #0 = { convergent noinline nounwind optnone} +attributes #1 = { convergent noinline nounwind optnone "hlsl.export"} +attributes #2 = { convergent "hlsl.numthreads"="4,1,1" "hlsl.shader"="compute"} +attributes #3 = { convergent } + +; Make sure "hlsl.export" attribute is stripped by llc +; CHECK-LLC-NOT: "hlsl.export" diff --git a/llvm/test/CodeGen/DirectX/omit-bitcast-insert.ll b/llvm/test/CodeGen/DirectX/omit-bitcast-insert.ll index 73c33e8cfdd864..6066a0033e45de 100644 --- a/llvm/test/CodeGen/DirectX/omit-bitcast-insert.ll +++ b/llvm/test/CodeGen/DirectX/omit-bitcast-insert.ll @@ -6,7 +6,7 @@ define i64 @test(ptr %p) { ret i64 %v } -; CHECK: define i64 @test(ptr %p) { +; CHECK: define internal i64 @test(ptr %p) { ; CHECK-NEXT: %v = load i64, ptr %p, align 8 ; CHECK-NEXT: ret i64 %v @@ -16,7 +16,7 @@ define i64 @test2(ptr %p) { ret i64 %v } -; CHECK: define i64 @test2(ptr %p) { +; CHECK: define internal i64 @test2(ptr %p) { ; CHECK-NEXT: store i64 0, ptr %p ; CHECK-NEXT: %v = load i64, ptr %p, align 8 ; CHECK-NEXT: ret i64 %v @@ -27,6 +27,6 @@ define i32 @test3(ptr %0) { ret i32 %3 } -; CHECK: define i32 @test3(ptr %0) { +; CHECK: define internal i32 @test3(ptr %0) { ; CHECK-NEXT: %2 = getelementptr i32, ptr %0, i32 4 ; CHECK-NEXT: %3 = load i32, ptr %2