Skip to content

Commit 020fbdf

Browse files
authored
[SPIR-V] Fixed a crash if encounter constant buffer fields with overlapping register assignments (#7636)
The issue: simple vertex shader like so ``` uniform float4x4 gMVP : register(c0); uniform float4 gFoo : register(c5); uniform float4 gBar : register(c5); float4 main(float4 pos : POSITION) : SV_Position { return mul(gMVP, pos * gFoo + gBar); } ``` will result in an internal crash ``` dxc.exe -spirv -T vs_6_2 -E main test.hlsl -Fo test.spirv Internal compiler error: access violation. Attempted to read from address 0x0000000000000000 ``` Due to `LowerTypeVisitor` trying to assign offsets to fields without explicit locations. It'll sort fields first, which will fill the map with the fields first. And since it's using `std::map` - if there's fields with the same `register` number - it'll only insert first, other will be left out, resulting nullptrs in the output vector. We read the content of the vector down the road crashing. My change fixes the crash and tries to output somewhat useful info about compilation fail. I hope this helps you in fixing it properly, or you can take it as it is.
1 parent 162bf4e commit 020fbdf

File tree

3 files changed

+55
-28
lines changed

3 files changed

+55
-28
lines changed

tools/clang/lib/SPIRV/LowerTypeVisitor.cpp

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -37,33 +37,6 @@ inline uint32_t roundToPow2(uint32_t val, uint32_t pow2) {
3737

3838
} // end anonymous namespace
3939

40-
// This method sorts a field list in the following order:
41-
// - fields with register annotation first, sorted by register index.
42-
// - then fields without annotation, in order of declaration.
43-
static std::vector<const HybridStructType::FieldInfo *>
44-
sortFields(llvm::ArrayRef<HybridStructType::FieldInfo> fields) {
45-
std::vector<const HybridStructType::FieldInfo *> output;
46-
output.resize(fields.size());
47-
48-
auto back_inserter = output.rbegin();
49-
std::map<uint32_t, const HybridStructType::FieldInfo *> fixed_fields;
50-
for (auto it = fields.rbegin(); it < fields.rend(); it++) {
51-
if (it->registerC) {
52-
fixed_fields.insert({it->registerC->RegisterNumber, &*it});
53-
} else {
54-
*back_inserter = &*it;
55-
back_inserter++;
56-
}
57-
}
58-
59-
auto front_inserter = output.begin();
60-
for (const auto &item : fixed_fields) {
61-
*front_inserter = item.second;
62-
front_inserter++;
63-
}
64-
return output;
65-
}
66-
6740
static void setDefaultFieldSize(const AlignmentSizeCalculator &alignmentCalc,
6841
const SpirvLayoutRule rule,
6942
const HybridStructType::FieldInfo *currentField,
@@ -292,6 +265,37 @@ bool LowerTypeVisitor::visitInstruction(SpirvInstruction *instr) {
292265
return true;
293266
}
294267

268+
std::vector<const HybridStructType::FieldInfo *> LowerTypeVisitor::sortFields(
269+
llvm::ArrayRef<HybridStructType::FieldInfo> fields) {
270+
std::vector<const HybridStructType::FieldInfo *> output;
271+
output.resize(fields.size());
272+
273+
auto back_inserter = output.rbegin();
274+
std::map<uint32_t, const HybridStructType::FieldInfo *> fixed_fields;
275+
for (auto it = fields.rbegin(); it < fields.rend(); it++) {
276+
if (it->registerC) {
277+
auto insertionResult =
278+
fixed_fields.insert({it->registerC->RegisterNumber, &*it});
279+
if (!insertionResult.second) {
280+
emitError(
281+
"field \"%0\" at register(c%1) overlaps with previous members",
282+
it->registerC->Loc)
283+
<< it->name << it->registerC->RegisterNumber;
284+
}
285+
} else {
286+
*back_inserter = &*it;
287+
back_inserter++;
288+
}
289+
}
290+
291+
auto front_inserter = output.begin();
292+
for (const auto &item : fixed_fields) {
293+
*front_inserter = item.second;
294+
front_inserter++;
295+
}
296+
return output;
297+
}
298+
295299
const SpirvType *LowerTypeVisitor::lowerType(const SpirvType *type,
296300
SpirvLayoutRule rule,
297301
SourceLocation loc) {
@@ -1378,12 +1382,19 @@ LowerTypeVisitor::populateLayoutInformation(
13781382
llvm::SmallVector<StructType::FieldInfo, 4> loweredFields;
13791383
llvm::DenseMap<const HybridStructType::FieldInfo *, uint32_t> fieldToIndexMap;
13801384

1385+
llvm::SmallVector<StructType::FieldInfo, 4> result;
1386+
13811387
// This stores the index of the field in the actual SPIR-V construct.
13821388
// When bitfields are merged, this index will be the same for merged fields.
13831389
uint32_t fieldIndexInConstruct = 0;
13841390
for (size_t i = 0, iPrevious = -1; i < sortedFields.size(); iPrevious = i++) {
13851391
const size_t fieldIndexForMap = loweredFields.size();
13861392

1393+
// Can happen if sortFields runs over fields with the same register(c#)
1394+
if (!sortedFields[i]) {
1395+
return result;
1396+
}
1397+
13871398
loweredFields.emplace_back(fieldVisitor(
13881399
(iPrevious < loweredFields.size() ? &loweredFields[iPrevious]
13891400
: nullptr),
@@ -1397,7 +1408,6 @@ LowerTypeVisitor::populateLayoutInformation(
13971408
}
13981409

13991410
// Re-order the sorted fields back to their original order.
1400-
llvm::SmallVector<StructType::FieldInfo, 4> result;
14011411
for (const auto &field : fields)
14021412
result.push_back(loweredFields[fieldToIndexMap[&field]]);
14031413
return result;

tools/clang/lib/SPIRV/LowerTypeVisitor.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ class LowerTypeVisitor : public Visitor {
6262
return astContext.getDiagnostics().Report(srcLoc, diagId);
6363
}
6464

65+
// This method sorts a field list in the following order:
66+
// - fields with register annotation first, sorted by register index.
67+
// - then fields without annotation, in order of declaration.
68+
std::vector<const HybridStructType::FieldInfo *>
69+
sortFields(llvm::ArrayRef<HybridStructType::FieldInfo> fields);
70+
6571
/// Lowers the given Hybrid type into a SPIR-V type.
6672
///
6773
/// Uses the above lowerType method to lower the QualType components of hybrid
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: not %dxc -T vs_6_2 -E main -fcgl %s -spirv 2>&1 | FileCheck %s
2+
3+
// CHECK: error: field "gFoo" at register(c5) overlaps with previous members
4+
5+
uniform float4x4 gMVP : register(c0);
6+
uniform float4 gFoo : register(c5);
7+
uniform float4 gBar : register(c5);
8+
9+
float4 main(float4 pos : POSITION) : SV_Position {
10+
return mul(gMVP, pos * gFoo + gBar);
11+
}

0 commit comments

Comments
 (0)