Skip to content

Commit 325ae8e

Browse files
authored
Fixed a crash when linker erases functions that still have users. (#5437)
When linking to library with a list of `-exports`, the linker erases functions that are not exported assuming they are no longer used. This assumption is not correct when the function is a constructor for a global variable. This change fixes the crash by first checking whether the function still has uses before erasing. This change adds a regression test with a non-trivially removable global variable constructor that would crash without this change.
1 parent cdee446 commit 325ae8e

2 files changed

Lines changed: 30 additions & 1 deletion

File tree

lib/HLSL/DxilLinker.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,11 @@ DxilLinkJob::LinkToLib(const ShaderModel *pSM) {
959959
if (!m_exportMap.ProcessFunction(F, true)) {
960960
// Remove Function not in exportMap.
961961
DM.RemoveFunction(F);
962-
F->eraseFromParent();
962+
963+
// Only erase function if user is empty. The function can still be
964+
// used by @llvm.global_ctors
965+
if (F->user_empty())
966+
F->eraseFromParent();
963967
}
964968
}
965969

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %dxc %s -T lib_6_3 -Fo lib0
2+
// RUN: %dxl %s -T lib_6_3 lib0 -exports foo | FileCheck %s
3+
4+
// CHECK: @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @{{.+}}, i8* null }]
5+
6+
// This is a regression test for a crash in the linker where when:
7+
// 1. There are constructors for global variables
8+
// 2. There are exports
9+
//
10+
// The bug assumes that functions not on the export list must have no users. This is not the case when
11+
// the functions are constructors for global variables.
12+
13+
Texture1D<float> t0 : register(t0);
14+
static float get_val(int i) {
15+
return t0[i];
16+
}
17+
struct My_Glob {
18+
float a, b, c;
19+
};
20+
// This constructor has real code that has to run and cannot be removed.
21+
static My_Glob glob = { get_val(0), get_val(1), get_val(2) };
22+
23+
export float foo() {
24+
return (++glob.a) + (++glob.b) + (++glob.c);
25+
}

0 commit comments

Comments
 (0)