Skip to content

Commit 13a6342

Browse files
authored
[DirectX] Fix the writing of ConstantExpr GEPs to DXIL bitcode (#154446)
Fixes #153304 Changes: - When writing `ConstantExpr` GEPs to DXIL bitcode, the bitcode writer will use the old Constant Code `CST_CODE_CE_GEP_OLD = 12` instead of the newer `CST_CODE_CE_GEP = 32` which is interpreted as an undef in DXIL. Additional context: [CST_CODE_CE_GEP = 12 in DXC](https://github.com/microsoft/DirectXShaderCompiler/blob/0c9e75e7e91bb18fab101abc81d399a0296f499e/include/llvm/Bitcode/LLVMBitCodes.h#L187) while the same constant code is labeled [CST_CODE_CE_GEP_OLD in LLVM](https://github.com/llvm/llvm-project/blob/65de318d186c815f43b892aa20b98c50f22ab6fe/llvm/include/llvm/Bitcode/LLVMBitCodes.h#L411) - Modifies the `PointerTypeAnalysis` to be able to analyze pointer-typed constants that appear in the operands of instructions so that the correct type of the `ConstantExpr` GEP is determined and written into the DXIL bitcode. - Adds a `PointerTypeAnalysis` test and dxil-dis test to ensure that the pointer type of `ConstantExpr` GEPs are resolved and `ConstantExpr` GEPs are written to DXIL bitcode correctly In addition, this PR also adds a missing call to `GV.removeDeadConstantUsers()` in the DXILFinalizeLinkage pass, and removes an unnecessary manual removal of a ConstantExpr in the DXILFlattenArrays pass.
1 parent 74a4d81 commit 13a6342

File tree

6 files changed

+90
-10
lines changed

6 files changed

+90
-10
lines changed

llvm/lib/Target/DirectX/DXILDataScalarization.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,9 +343,7 @@ bool DataScalarizerVisitor::visitGetElementPtrInst(GetElementPtrInst &GEPI) {
343343

344344
GOp->replaceAllUsesWith(NewGEP);
345345

346-
if (auto *CE = dyn_cast<ConstantExpr>(GOp))
347-
CE->destroyConstant();
348-
else if (auto *OldGEPI = dyn_cast<GetElementPtrInst>(GOp))
346+
if (auto *OldGEPI = dyn_cast<GetElementPtrInst>(GOp))
349347
OldGEPI->eraseFromParent();
350348

351349
return true;

llvm/lib/Target/DirectX/DXILFinalizeLinkage.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ static bool finalizeLinkage(Module &M) {
2222

2323
// Convert private globals and external globals with no usage to internal
2424
// linkage.
25-
for (GlobalVariable &GV : M.globals())
25+
for (GlobalVariable &GV : M.globals()) {
26+
GV.removeDeadConstantUsers();
2627
if (GV.hasPrivateLinkage() || (GV.hasExternalLinkage() && GV.use_empty())) {
2728
GV.setLinkage(GlobalValue::InternalLinkage);
2829
MadeChange = true;
2930
}
31+
}
3032

3133
SmallVector<Function *> Funcs;
3234

llvm/lib/Target/DirectX/DXILWriter/DXILBitcodeWriter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2113,7 +2113,7 @@ void DXILBitcodeWriter::writeConstants(unsigned FirstVal, unsigned LastVal,
21132113
}
21142114
break;
21152115
case Instruction::GetElementPtr: {
2116-
Code = bitc::CST_CODE_CE_GEP;
2116+
Code = bitc::CST_CODE_CE_GEP_OLD;
21172117
const auto *GO = cast<GEPOperator>(C);
21182118
if (GO->isInBounds())
21192119
Code = bitc::CST_CODE_CE_INBOUNDS_GEP;

llvm/lib/Target/DirectX/DirectXIRPasses/PointerTypeAnalysis.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,39 @@
1515
#include "llvm/IR/GlobalVariable.h"
1616
#include "llvm/IR/Instructions.h"
1717
#include "llvm/IR/Module.h"
18+
#include "llvm/IR/Operator.h"
1819

1920
using namespace llvm;
2021
using namespace llvm::dxil;
2122

2223
namespace {
2324

25+
Type *classifyFunctionType(const Function &F, PointerTypeMap &Map);
26+
2427
// Classifies the type of the value passed in by walking the value's users to
2528
// find a typed instruction to materialize a type from.
2629
Type *classifyPointerType(const Value *V, PointerTypeMap &Map) {
2730
assert(V->getType()->isPointerTy() &&
2831
"classifyPointerType called with non-pointer");
32+
33+
// A CallInst will trigger this case, and we want to classify its Function
34+
// operand as a Function rather than a generic Value.
35+
if (const Function *F = dyn_cast<Function>(V))
36+
return classifyFunctionType(*F, Map);
37+
38+
// There can potentially be dead constants hanging off of the globals we do
39+
// not want to deal with. So we remove them here.
40+
if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(V))
41+
GV->removeDeadConstantUsers();
42+
2943
auto It = Map.find(V);
3044
if (It != Map.end())
3145
return It->second;
3246

3347
Type *PointeeTy = nullptr;
34-
if (auto *Inst = dyn_cast<GetElementPtrInst>(V)) {
35-
if (!Inst->getResultElementType()->isPointerTy())
36-
PointeeTy = Inst->getResultElementType();
48+
if (auto *GEP = dyn_cast<GEPOperator>(V)) {
49+
if (!GEP->getResultElementType()->isPointerTy())
50+
PointeeTy = GEP->getResultElementType();
3751
} else if (auto *Inst = dyn_cast<AllocaInst>(V)) {
3852
PointeeTy = Inst->getAllocatedType();
3953
} else if (auto *GV = dyn_cast<GlobalVariable>(V)) {
@@ -49,8 +63,8 @@ Type *classifyPointerType(const Value *V, PointerTypeMap &Map) {
4963
// When store value is ptr type, cannot get more type info.
5064
if (NewPointeeTy->isPointerTy())
5165
continue;
52-
} else if (const auto *Inst = dyn_cast<GetElementPtrInst>(User)) {
53-
NewPointeeTy = Inst->getSourceElementType();
66+
} else if (const auto *GEP = dyn_cast<GEPOperator>(User)) {
67+
NewPointeeTy = GEP->getSourceElementType();
5468
}
5569
if (NewPointeeTy) {
5670
// HLSL doesn't support pointers, so it is unlikely to get more than one
@@ -204,6 +218,9 @@ PointerTypeMap PointerTypeAnalysis::run(const Module &M) {
204218
for (const auto &I : B) {
205219
if (I.getType()->isPointerTy())
206220
classifyPointerType(&I, Map);
221+
for (const auto &O : I.operands())
222+
if (O.get()->getType()->isPointerTy())
223+
classifyPointerType(O.get(), Map);
207224
}
208225
}
209226
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
; RUN: llc --filetype=obj %s -o - | dxil-dis -o - | FileCheck %s
2+
target triple = "dxil-unknown-shadermodel6.7-library"
3+
4+
; CHECK: [[GLOBAL:@.*]] = unnamed_addr addrspace(3) global [10 x i32] zeroinitializer, align 4
5+
@g = local_unnamed_addr addrspace(3) global [10 x i32] zeroinitializer, align 4
6+
7+
define i32 @fn() #0 {
8+
; CHECK-LABEL: define i32 @fn()
9+
; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32 addrspace(3)* getelementptr inbounds ([10 x i32], [10 x i32] addrspace(3)* [[GLOBAL]], i32 0, i32 1), align 4
10+
; CHECK-NEXT: ret i32 [[LOAD]]
11+
;
12+
%gep = getelementptr [10 x i32], ptr addrspace(3) @g, i32 0, i32 1
13+
%ld = load i32, ptr addrspace(3) %gep, align 4
14+
ret i32 %ld
15+
}
16+
17+
define i32 @fn2() #0 {
18+
; CHECK-LABEL: define i32 @fn2()
19+
; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32 addrspace(3)* getelementptr inbounds ([10 x i32], [10 x i32] addrspace(3)* [[GLOBAL]], i32 0, i32 2), align 4
20+
; CHECK-NEXT: ret i32 [[LOAD]]
21+
;
22+
%ld = load i32, ptr addrspace(3) getelementptr ([10 x i32], ptr addrspace(3) @g, i32 0, i32 2), align 4
23+
ret i32 %ld
24+
}
25+
26+
define i32 @fn3() #0 {
27+
; CHECK-LABEL: define i32 @fn3()
28+
; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32 addrspace(3)* getelementptr inbounds ([10 x i32], [10 x i32] addrspace(3)* [[GLOBAL]], i32 0, i32 3), align 4
29+
; CHECK-NEXT: ret i32 [[LOAD]]
30+
;
31+
%ld = load i32, ptr addrspace(3) getelementptr (i8, ptr addrspace(3) @g, i32 12), align 4
32+
ret i32 %ld
33+
}
34+
35+
attributes #0 = { "hlsl.export" }

llvm/unittests/Target/DirectX/PointerTypeAnalysisTests.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "DirectXIRPasses/PointerTypeAnalysis.h"
1010
#include "llvm/AsmParser/Parser.h"
11+
#include "llvm/IR/Constants.h"
1112
#include "llvm/IR/Instructions.h"
1213
#include "llvm/IR/LLVMContext.h"
1314
#include "llvm/IR/Module.h"
@@ -123,6 +124,33 @@ TEST(PointerTypeAnalysis, DiscoverGEP) {
123124
EXPECT_THAT(Map, Contains(Pair(IsA<GetElementPtrInst>(), I64Ptr)));
124125
}
125126

127+
TEST(PointerTypeAnalysis, DiscoverConstantExprGEP) {
128+
StringRef Assembly = R"(
129+
@g = internal global [10 x i32] zeroinitializer
130+
define i32 @test() {
131+
%i = load i32, ptr getelementptr ([10 x i32], ptr @g, i64 0, i64 1)
132+
ret i32 %i
133+
}
134+
)";
135+
136+
LLVMContext Context;
137+
SMDiagnostic Error;
138+
auto M = parseAssemblyString(Assembly, Error, Context);
139+
ASSERT_TRUE(M) << "Bad assembly?";
140+
141+
PointerTypeMap Map = PointerTypeAnalysis::run(*M);
142+
ASSERT_EQ(Map.size(), 3u);
143+
Type *I32Ty = Type::getInt32Ty(Context);
144+
Type *I32Ptr = TypedPointerType::get(I32Ty, 0);
145+
Type *I32ArrPtr = TypedPointerType::get(ArrayType::get(I32Ty, 10), 0);
146+
Type *FnTy = FunctionType::get(I32Ty, {}, false);
147+
148+
EXPECT_THAT(Map, Contains(Pair(IsA<GlobalVariable>(), I32ArrPtr)));
149+
EXPECT_THAT(Map,
150+
Contains(Pair(IsA<Function>(), TypedPointerType::get(FnTy, 0))));
151+
EXPECT_THAT(Map, Contains(Pair(IsA<ConstantExpr>(), I32Ptr)));
152+
}
153+
126154
TEST(PointerTypeAnalysis, TraceIndirect) {
127155
StringRef Assembly = R"(
128156
define i64 @test(ptr %p) {

0 commit comments

Comments
 (0)