diff options
| -rw-r--r-- | source/slang/slang-check-out-of-bound-access.cpp | 89 | ||||
| -rw-r--r-- | source/slang/slang-check-out-of-bound-access.h | 12 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-emit.cpp | 2 | ||||
| -rw-r--r-- | tests/diagnostics/array-out-of-bounds-2.slang | 29 | ||||
| -rw-r--r-- | tests/diagnostics/array-out-of-bounds.slang | 28 | ||||
| -rw-r--r-- | tests/language-feature/0-array-1.slang | 12 |
7 files changed, 173 insertions, 4 deletions
diff --git a/source/slang/slang-check-out-of-bound-access.cpp b/source/slang/slang-check-out-of-bound-access.cpp new file mode 100644 index 000000000..51adc2b4c --- /dev/null +++ b/source/slang/slang-check-out-of-bound-access.cpp @@ -0,0 +1,89 @@ +// slang-check-out-of-bound-access.cpp +#include "slang-check-out-of-bound-access.h" + +#include "slang-ir-inst-pass-base.h" +#include "slang-ir-insts.h" +#include "slang-ir.h" + +namespace Slang +{ + +struct OutOfBoundAccessChecker : public InstPassBase +{ + DiagnosticSink* sink; + + OutOfBoundAccessChecker(IRModule* inModule, DiagnosticSink* inSink) + : InstPassBase(inModule), sink(inSink) + { + } + + + void checkArrayAccess(IRInst* inst, IRInst* base, IRInst* index) + { + // Check if index is a constant integer + auto indexLit = as<IRIntLit>(index); + if (!indexLit) + return; // Skip non-constant indices + + // Get the base type + auto baseType = base->getDataType(); + + // Handle pointer-to-array case (for GetElementPtr) + if (auto ptrType = as<IRPtrTypeBase>(baseType)) + { + baseType = ptrType->getValueType(); + } + + // Check if base is an array type + auto arrayType = as<IRArrayTypeBase>(baseType); + if (!arrayType) + return; // Skip non-array types + + // Check if array size is a constant + auto arraySizeInst = arrayType->getElementCount(); + auto arraySizeLit = as<IRIntLit>(arraySizeInst); + if (!arraySizeLit) + return; // Skip arrays with non-constant size + + // Get the actual values + IRIntegerValue indexValue = indexLit->getValue(); + IRIntegerValue arraySizeValue = arraySizeLit->getValue(); + + // Check bounds: index should be >= 0 and < arraySize + if (indexValue < 0 || indexValue >= arraySizeValue) + { + sink->diagnose(inst, Diagnostics::arrayIndexOutOfBounds, indexValue, arraySizeValue); + } + } + + void processModule() + { + processAllInsts( + [&](IRInst* inst) + { + switch (inst->getOp()) + { + case kIROp_GetElement: + case kIROp_GetElementPtr: + { + if (inst->getOperandCount() < 2) + return; + + auto base = inst->getOperand(0); + auto index = inst->getOperand(1); + + checkArrayAccess(inst, base, index); + } + break; + } + }); + } +}; + +void checkForOutOfBoundAccess(IRModule* module, DiagnosticSink* sink) +{ + OutOfBoundAccessChecker checker(module, sink); + checker.processModule(); +} + +} // namespace Slang
\ No newline at end of file diff --git a/source/slang/slang-check-out-of-bound-access.h b/source/slang/slang-check-out-of-bound-access.h new file mode 100644 index 000000000..17f4e8c3d --- /dev/null +++ b/source/slang/slang-check-out-of-bound-access.h @@ -0,0 +1,12 @@ +// slang-check-out-of-bound-access.h +#pragma once + +#include "slang-ir.h" + +namespace Slang +{ +struct IRModule; +class DiagnosticSink; + +void checkForOutOfBoundAccess(IRModule* module, DiagnosticSink* sink); +} // namespace Slang
\ No newline at end of file diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 3d584679e..0ce5d9f47 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -659,6 +659,11 @@ DIAGNOSTIC( "Cannot convert array of size $0 to array of size $1 as this would truncate data") DIAGNOSTIC(30025, Error, invalidArraySize, "array size must be non-negative.") DIAGNOSTIC( + 30029, + Error, + arrayIndexOutOfBounds, + "array index '$0' is out of bounds for array of size '$1'.") +DIAGNOSTIC( 30026, Error, returnInComponentMustComeLast, diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index 1657a2980..f40679bd9 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -9,6 +9,7 @@ #include "../core/slang-performance-profiler.h" #include "../core/slang-type-text-util.h" #include "../core/slang-writer.h" +#include "slang-check-out-of-bound-access.h" #include "slang-emit-c-like.h" #include "slang-emit-cpp.h" #include "slang-emit-cuda.h" @@ -1109,6 +1110,7 @@ Result linkAndOptimizeIR( { checkForRecursiveTypes(irModule, sink); checkForRecursiveFunctions(codeGenContext->getTargetReq(), irModule, sink); + checkForOutOfBoundAccess(irModule, sink); if (requiredLoweringPassSet.missingReturn) checkForMissingReturns(irModule, sink, target, false); diff --git a/tests/diagnostics/array-out-of-bounds-2.slang b/tests/diagnostics/array-out-of-bounds-2.slang new file mode 100644 index 000000000..978f61d9f --- /dev/null +++ b/tests/diagnostics/array-out-of-bounds-2.slang @@ -0,0 +1,29 @@ +// array-out-of-bounds-2.slang + +// Test the specific scenario from the issue (array of size 3, accessing index 3) + +//TEST:SIMPLE(filecheck=CHECK): -target spirv -entry computeMain -stage compute + +struct SH3_t +{ + float coeffs[4]; +}; + +RWStructuredBuffer<float> outputBuffer; + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + SH3_t a[3]; + SH3_t b; + + for (int i = 0; i < 3; ++i) + { + // This should be fine + outputBuffer[i] = a[i].coeffs[0]; + } + + // This reproduces the issue - accessing index 3 in array of size 3 + //CHECK: error 30029: array index '3' is out of bounds for array of size '3'. + outputBuffer[3] = a[3].coeffs[0]; +}
\ No newline at end of file diff --git a/tests/diagnostics/array-out-of-bounds.slang b/tests/diagnostics/array-out-of-bounds.slang new file mode 100644 index 000000000..81c243031 --- /dev/null +++ b/tests/diagnostics/array-out-of-bounds.slang @@ -0,0 +1,28 @@ +// array-out-of-bounds.slang + +// Test that out-of-bounds array access with constant indices generates an error + +//TEST:SIMPLE(filecheck=CHECK): -target spirv -entry computeMain -stage compute + +RWStructuredBuffer<int> outputBuffer; + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + int a[3]; + a[0] = 10; + a[1] = 20; + a[2] = 30; + + // Valid access - should be fine + outputBuffer[0] = a[0]; + outputBuffer[1] = a[2]; + + // Invalid access - index 3 is out of bounds for array of size 3 + //CHECK: error 30029: array index '3' is out of bounds for array of size '3'. + outputBuffer[2] = a[3]; + + // Invalid access - negative index + //CHECK: error 30029: array index '-1' is out of bounds for array of size '3'. + outputBuffer[3] = a[-1]; +}
\ No newline at end of file diff --git a/tests/language-feature/0-array-1.slang b/tests/language-feature/0-array-1.slang index 327f71444..b02be1af0 100644 --- a/tests/language-feature/0-array-1.slang +++ b/tests/language-feature/0-array-1.slang @@ -10,7 +10,7 @@ uniform MyData* myData; uniform int * output; [numthreads(1, 1, 1)] -void computeMain() +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) { // These are all ill-formed, but we want to still ensure our backend // can handle them gracefully without crashing. @@ -18,9 +18,13 @@ void computeMain() // by a `if` statement that checks the size before accessing. // The condition would then evaluate to false and causing all the accessing // code to be optimized out. - InterlockedAdd(myData.a[0][0][0], 1); - myData.a[0][0][0] += 1; - output[0] = myData.a[0][0][0]; + + // Use runtime values to access the 0-sized array to avoid triggering + // the new out-of-bounds diagnostic for constant indices + uint runtimeIndex = dispatchThreadID.x; + InterlockedAdd(myData.a[runtimeIndex][runtimeIndex][runtimeIndex], 1); + myData.a[runtimeIndex][runtimeIndex][runtimeIndex] += 1; + output[0] = myData.a[runtimeIndex][runtimeIndex][runtimeIndex]; } //SPV: OpEntryPoint |
