Skip to content

Commit 8588ecb

Browse files
adam-yangGreg Roth
authored andcommitted
Turn off structurize-returns when cleanup blocks are present. (#4927)
structurize-returns uses scope-end blocks recorded during codegen to transform the control flow. When "cleanup" blocks are generated (for example for lifetime-markers), structurize-returns as is cannot transform the control flow safely. This change disables structurize-returns and emits a warning when cleanup blocks are detected in the affected function. (cherry picked from commit 93f43ec)
1 parent bac7aa7 commit 8588ecb

8 files changed

Lines changed: 101 additions & 5 deletions

File tree

tools/clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,4 +798,5 @@ def HLSLPayloadAccessQualifer: DiagGroup<"payload-access-qualifier", [
798798
HLSLPayloadAccessQualiferCall
799799
]>;
800800
def HLSLSemanticIdentifierCollision : DiagGroup<"semantic-identifier-collision">;
801+
def HLSLStructurizeExitsLifetimeMarkersConflict: DiagGroup<"structurize-exits-lifetime-markers-conflict">;
801802
// HLSL Change Ends

tools/clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7754,6 +7754,9 @@ def err_hlsl_logical_binop_scalar : Error<
77547754
"operands for short-circuiting logical binary operator must be scalar, for non-scalar types use '%select{and|or}0'">;
77557755
def err_hlsl_ternary_scalar : Error<
77567756
"condition for short-circuiting ternary operator must be scalar, for non-scalar types use 'select'">;
7757+
def warn_hlsl_structurize_exits_lifetime_markers_conflict : Warning <
7758+
"structurize-returns skipped function '%0' due to incompatibility with lifetime markers. Use -disable-lifetime-markers to enable structurize-exits on this function.">,
7759+
InGroup< HLSLStructurizeExitsLifetimeMarkersConflict >;
77577760
// HLSL Change Ends
77587761

77597762
// SPIRV Change Starts

tools/clang/lib/CodeGen/CGCleanup.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "CGCleanup.h"
2121
#include "CodeGenFunction.h"
22+
#include "CGHLSLRuntime.h" // HLSL Change
2223

2324
using namespace clang;
2425
using namespace CodeGen;
@@ -435,6 +436,7 @@ static llvm::BasicBlock *CreateNormalEntry(CodeGenFunction &CGF,
435436
if (!Entry) {
436437
Entry = CGF.createBasicBlock("cleanup");
437438
Scope.setNormalBlock(Entry);
439+
CGF.CGM.getHLSLRuntime().MarkCleanupBlock(CGF, Entry); // HLSL Change
438440
}
439441
return Entry;
440442
}

tools/clang/lib/CodeGen/CGHLSLMS.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ class CGMSHLSLRuntime : public CGHLSLRuntime {
304304
void MarkSwitchStmt(CodeGenFunction &CGF, SwitchInst *switchInst,
305305
BasicBlock *endSwitch) override;
306306
void MarkReturnStmt(CodeGenFunction &CGF, BasicBlock *bbWithRet) override;
307+
void MarkCleanupBlock(CodeGenFunction &CGF, llvm::BasicBlock *cleanupBB) override;
307308
void MarkLoopStmt(CodeGenFunction &CGF, BasicBlock *loopContinue,
308309
BasicBlock *loopExit) override;
309310
CGHLSLMSHelper::Scope* MarkScopeEnd(CodeGenFunction &CGF) override;
@@ -2393,7 +2394,7 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
23932394
F->addFnAttr(Twine("exp-", Attr->getName()).str(), Attr->getValue());
23942395
}
23952396

2396-
m_ScopeMap[F] = ScopeInfo(F);
2397+
m_ScopeMap[F] = ScopeInfo(F, FD->getLocation());
23972398
}
23982399

23992400
void CGMSHLSLRuntime::RemapObsoleteSemantic(DxilParameterAnnotation &paramInfo, bool isPatchConstantFunction) {
@@ -6233,6 +6234,10 @@ void CGMSHLSLRuntime::MarkIfStmt(CodeGenFunction &CGF, BasicBlock *endIfBB) {
62336234
Scope->AddIf(endIfBB);
62346235
}
62356236

6237+
void CGMSHLSLRuntime::MarkCleanupBlock(CodeGenFunction &CGF, llvm::BasicBlock *cleanupBB) {
6238+
if (ScopeInfo *Scope = GetScopeInfo(CGF.CurFn))
6239+
Scope->AddCleanupBB(cleanupBB);
6240+
}
62366241

62376242
void CGMSHLSLRuntime::MarkSwitchStmt(CodeGenFunction &CGF,
62386243
SwitchInst *switchInst,

tools/clang/lib/CodeGen/CGHLSLMSFinishCodeGen.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "clang/Basic/LangOptions.h"
3030
#include "clang/Frontend/CodeGenOptions.h"
3131
#include "clang/Parse/ParseHLSL.h" // root sig would be in Parser if part of lang
32+
#include "clang/Sema/SemaDiagnostic.h"
3233
#include "CodeGenFunction.h"
3334

3435
#include "dxc/DXIL/DxilConstants.h"
@@ -3309,7 +3310,7 @@ void AddDxBreak(Module &M,
33093310

33103311
namespace CGHLSLMSHelper {
33113312

3312-
ScopeInfo::ScopeInfo(Function *F) : maxRetLevel(0), bAllReturnsInIf(true) {
3313+
ScopeInfo::ScopeInfo(Function *F, clang::SourceLocation loc) : maxRetLevel(0), bAllReturnsInIf(true), sourceLoc(loc) {
33133314
Scope FuncScope;
33143315
FuncScope.kind = Scope::ScopeKind::FunctionScope;
33153316
FuncScope.EndScopeBB = nullptr;
@@ -3549,12 +3550,21 @@ static void ChangePredBranch(BasicBlock *BB, BasicBlock *NewBB) {
35493550
// }
35503551
// return vRet;
35513552
// }
3552-
void StructurizeMultiRetFunction(Function *F, ScopeInfo &ScopeInfo,
3553+
void StructurizeMultiRetFunction(Function *F, clang::DiagnosticsEngine &Diags, ScopeInfo &ScopeInfo,
35533554
bool bWaveEnabledStage,
35543555
SmallVector<BranchInst *, 16> &DxBreaks) {
35553556

35563557
if (ScopeInfo.CanSkipStructurize())
35573558
return;
3559+
3560+
// If there are cleanup blocks generated for lifetime markers, do
3561+
// not structurize returns. The scope info recorded is no longer correct.
3562+
if (ScopeInfo.HasCleanupBlocks()) {
3563+
Diags.Report(ScopeInfo.GetSourceLocation(), clang::diag::warn_hlsl_structurize_exits_lifetime_markers_conflict)
3564+
<< F->getName();
3565+
return;
3566+
}
3567+
35583568
// Get bbWithRets.
35593569
auto &rets = ScopeInfo.GetRetScopes();
35603570

@@ -3722,7 +3732,8 @@ void StructurizeMultiRet(Module &M, clang::CodeGen::CodeGenModule &CGM,
37223732
auto it = ScopeMap.find(&F);
37233733
if (it == ScopeMap.end())
37243734
continue;
3725-
StructurizeMultiRetFunction(&F, it->second, bWaveEnabledStage, DxBreaks);
3735+
3736+
StructurizeMultiRetFunction(&F, CGM.getDiags(), it->second, bWaveEnabledStage, DxBreaks);
37263737
}
37273738
}
37283739

tools/clang/lib/CodeGen/CGHLSLMSHelper.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,27 +122,32 @@ struct Scope {
122122
class ScopeInfo {
123123
public:
124124
ScopeInfo(){}
125-
ScopeInfo(llvm::Function *F);
125+
ScopeInfo(llvm::Function *F, clang::SourceLocation loc);
126126
void AddIf(llvm::BasicBlock *endIfBB);
127127
void AddSwitch(llvm::BasicBlock *endSwitchBB);
128128
void AddLoop(llvm::BasicBlock *loopContinue, llvm::BasicBlock *endLoopBB);
129129
void AddRet(llvm::BasicBlock *bbWithRet);
130+
void AddCleanupBB(llvm::BasicBlock *cleanupBB) { hasCleanupBlocks = true; }
130131
Scope &EndScope(bool bScopeFinishedWithRet);
131132
Scope &GetScope(unsigned i);
132133
const llvm::SmallVector<unsigned, 2> &GetRetScopes() { return rets; }
133134
void LegalizeWholeReturnedScope();
134135
llvm::SmallVector<Scope, 16> &GetScopes() { return scopes; }
135136
bool CanSkipStructurize();
136137
void dump();
138+
bool HasCleanupBlocks() const { return hasCleanupBlocks; }
139+
clang::SourceLocation GetSourceLocation() const { return sourceLoc; }
137140

138141
private:
139142
void AddScope(Scope::ScopeKind k, llvm::BasicBlock *endScopeBB);
143+
bool hasCleanupBlocks = false;
140144
llvm::SmallVector<unsigned, 2> rets;
141145
unsigned maxRetLevel;
142146
bool bAllReturnsInIf;
143147
llvm::SmallVector<unsigned, 8> scopeStack;
144148
// save all scopes.
145149
llvm::SmallVector<Scope, 16> scopes;
150+
clang::SourceLocation sourceLoc;
146151
};
147152

148153
// Map from value to resource properties.

tools/clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ class CGHLSLRuntime {
140140
clang::QualType FnRetTy,
141141
const std::function<void(const VarDecl *, llvm::Value *)> &TmpArgMap) = 0;
142142
virtual void MarkIfStmt(CodeGenFunction &CGF, llvm::BasicBlock *endIfBB) = 0;
143+
virtual void MarkCleanupBlock(CodeGenFunction &CGF, llvm::BasicBlock *cleanupBB) = 0;
143144
virtual void MarkSwitchStmt(CodeGenFunction &CGF,
144145
llvm::SwitchInst *switchInst,
145146
llvm::BasicBlock *endSwitch) = 0;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// RUN: %dxc -fdisable-loc-tracking -E main -opt-enable structurize-returns -T cs_6_0 -enable-lifetime-markers -fcgl %s | FileCheck %s -check-prefix=FCGL
2+
// RUN: %dxc -fdisable-loc-tracking -E main -opt-enable structurize-returns -T cs_6_0 -enable-lifetime-markers %s | FileCheck %s
3+
// RUN: %dxc -fdisable-loc-tracking -E main -opt-enable structurize-returns -T cs_6_0 -enable-lifetime-markers %s | FileCheck %s -check-prefix=WARNING -input-file=stderr
4+
// RUN: %dxc -fdisable-loc-tracking -E main -opt-enable structurize-returns -T cs_6_0 -disable-lifetime-markers -fcgl %s | FileCheck %s -check-prefix=NO-LIFETIME
5+
6+
// Regression test for a bug where program structure is completely messed up when lifetime-markers are enabled and
7+
// -opt-enable structurize-returns is on. The scope information recorded during codegen that structurize-returns uses
8+
// to modify the control flow is incorrect if lifetime-markers are enabled. This test checks that
9+
10+
//=================================
11+
// The fcgl test checks the return condition alloca bReturn is not generated and the cleanup code for lifetime-markers
12+
// is present.
13+
// FCGL-NOT: bReturned
14+
// FCGL: %cleanup.dest
15+
16+
//=================================
17+
// The non-fcgl test checks the shader is compiled correctly (the bug causes irreducible flow)
18+
// CHECK-DAG: @main
19+
20+
//=================================
21+
// Check a warning was emitted.
22+
// WARNING: structurize-returns skipped function 'main' due to incompatibility with lifetime markers. Use -disable-lifetime-markers to enable structurize-exits on this function.
23+
24+
//=================================
25+
// The last test makes sure structurize-returns runs as expected
26+
// NO-LIFETIME: @main
27+
// NO-LIFETIME: %bReturned = alloca
28+
29+
struct D {
30+
float3 d_member;
31+
};
32+
33+
struct A {
34+
float4 a_member;
35+
};
36+
37+
struct B {
38+
uint flags;
39+
} ;
40+
41+
struct C {
42+
uint c_member;
43+
};
44+
45+
StructuredBuffer <D> srv0 : register(t0) ;
46+
StructuredBuffer <B> srv1 : register(t1) ;
47+
RWStructuredBuffer <C> uav0 : register(u0) ;
48+
49+
[RootSignature("DescriptorTable(SRV(t0,numDescriptors=10)),DescriptorTable(UAV(u0,numDescriptors=10))")]
50+
[numthreads (64, 1, 1)]
51+
void main(uint3 dtid : SV_DispatchThreadID) {
52+
if (dtid.x < 10) {
53+
A decal = (A)0;
54+
{
55+
const D d = srv0[0];
56+
if (!d.d_member.x)
57+
return;
58+
}
59+
B b = srv1[0];
60+
if (b.flags & 1) {
61+
InterlockedMax(uav0[0].c_member, 10) ;
62+
return;
63+
}
64+
65+
InterlockedMax(uav0[0].c_member, 20) ;
66+
}
67+
InterlockedMax(uav0[0].c_member, 30) ;
68+
}

0 commit comments

Comments
 (0)