KhronosGroup / KhronosGroup/glslang

Emit `OpInBoundsAccessChain` for GLSL shaders reading arrays/etc

Open
#4,206 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
3.6k
Forks
989
Avg merge
1d 2h
Merged PRs (30d)
31

Description

`createAccessChain` builds a `Op::OpAccessChain` even though it appears to be used only in cases where, at least for GLSL/ESSL shaders, it could build an `Op::OpInBoundsAccessChain` (array, vector, and matrix indexing with out-of-bounds indices are undefined behavior as far as I can tell). This, in turn, results in a few additional (though not that many) optimizations downstream (eg in mesa). Claude seems to think its as easy as the following (plus regenerating some test output), which appears to generate valid GLSL -> SPIR-V at least for the shaders I'm looking at:

```diff
diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp
index b65ec306..400a32e9 100644
--- a/SPIRV/SpvBuilder.cpp
+++ b/SPIRV/SpvBuilder.cpp
@@ -3215,8 +3215,11 @@ Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vec
Id typeId = getResultingAccessChainType();
typeId = makePointer(storageClass, typeId);

- // Make the instruction
- Instruction* chain = new Instruction(getUniqueId(), typeId, Op::OpAccessChain);
+ // GLSL and ESSL define out-of-bounds indexing as undefined behavior,
+ // so the compiler may assume all access chain indices are in bounds.
+ Op op = (sourceLang == SourceLanguage::GLSL || sourceLang == SourceLanguage::ESSL)
+ ? Op::OpInBoundsAccessChain : Op::OpAccessChain;
+ Instruction* chain = new Instruction(getUniqueId(), typeId, op);
chain->reserveOperands(offsets.size() + 1);
chain->addIdOperand(base);
for (int i = 0; i < (int)offsets.size(); ++i)
diff --git a/SPIRV/SpvPostProcess.cpp b/SPIRV/SpvPostProcess.cpp
index 2d5c3209..2faf6c9a 100644
--- a/SPIRV/SpvPostProcess.cpp
+++ b/SPIRV/SpvPostProcess.cpp
@@ -177,6 +177,7 @@ void Builder::postProcessType(const Instruction& inst, Id typeId)
}
break;
case Op::OpAccessChain:
+ case Op::OpInBoundsAccessChain:
case Op::OpPtrAccessChain:
if (isPointerType(typeId))
break;
@@ -283,7 +284,8 @@ void Builder::postProcess(Instruction& inst)
// the reference type and any scalar component selection in the accesschain,
// and this function computes the rest from the SPIR-V Offset decorations.
Instruction *accessChain = module.getInstruction(inst.getIdOperand(0));
- if (accessChain->getOpCode() == Op::OpAccessChain) {
+ if (accessChain->getOpCode() == Op::OpAccessChain ||
+ accessChain->getOpCode() == Op::OpInBoundsAccessChain) {
const Instruction* base = module.getInstruction(accessChain->getIdOperand(0));
// Get the type of the base of the access chain. It must be a pointer type.
Id typeId = base->getTypeId();
```

Contributor guide

Open the contributing guide

Research direction

Start in SPIRV/SpvBuilder.cpp at Builder::createAccessChain, then inspect the related opcode handling in SPIRV/SpvPostProcess.cpp. Verify that GLSL and ESSL array, vector, and matrix accesses emit OpInBoundsAccessChain while other source languages retain their existing behavior. Regenerate the affected test output and confirm the generated GLSL-to-SPIR-V cases remain valid.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.