Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 10, 2025

The compiler crashed with a dictionary key collision when a continue statement inside a switch inside a while loop targeted the while loop's continue block.

while (bitmap != 0) {
    switch (buffer[bitmap]) {
    case 0:
        if (thread_id.x < 10) {
            continue;  // Compiler crash: dictionary key already exists
        }
        break;
    }
    bitmap ^= bitmap & ~bitmap;
}

Root Cause

For while loops, the continue block typically equals the target block (loop header). The code only added continue blocks to the exit set when they differed from the target block. This allowed child regions (switches) to include the parent's continue block in their block list, causing the same block to be mapped to multiple regions.

Changes

  • populateExitBlocks(): Always add continue block to exit blocks, even when it equals target block
  • collectBreakableRegionBlocks(): Explicitly add target block for loops where continue == target, since it's now in the exit set but must still be part of the loop region
  • Multi-level branch detection: Recognize that branches to continue blocks equaling target blocks don't require transformation—they're direct loop re-entries
  • Branch transformation: Skip transformation for these cases since the branch already targets the correct destination

Test Coverage

Added regression test tests/bugs/gh-while-continue-in-switch.slang covering continue statements in switches nested within while loops.

Original prompt

This section details on the original issue you should resolve

<issue_title>Compiler exception with nested continue inside a switch</issue_title>
<issue_description>I believe nested breaks are not allowed, but this code causes an exception inside Slang::EliminateMultiLevelBreakContext::gatherInfo, leading to

(0): error 99999: Slang compilation aborted due to an exception of N5Slang13InternalErrorE: assert failure: The key already exists in Dictionary.

Minimal-ish reproduction:

[shader("compute")]
[numthreads(8, 8, 1)]
uint main(uniform uint *buffer, uint3 thread_id: SV_DispatchThreadID) {
    var bitmap = buffer[thread_id.z];
    var value = 0;

    while (bitmap != 0) {
        switch (buffer[bitmap]) {
        case 0:
            if (thread_id.x < 10) {
                continue;
            }
            value += 1;
            break;
        }

        bitmap ^= bitmap & ~bitmap;
    }

    return value;
}

Exception backtrace (it would be nice if slang would print the backtrace to stderr when it hits an exception, rather than only printing the exception type and message, so I didn't have to comment out the exception handling or faff around in the debugger to get this output)

shader-slang/slang#8  0x00007ffff70a3256 in Slang::handleSignal (type=Slang::SignalType::AssertFailure, message=0x7ffff795db98 "The key already exists in Dictionary.")
    at /home/josh/Projects/slang/source/core/slang-signal.cpp:69
shader-slang/slang#9  0x00007ffff6a1c656 in Slang::Dictionary<Slang::IRBlock*, Slang::EliminateMultiLevelBreakContext::BreakableRegionInfo*, Slang::Hash<Slang::IRBlock*>, std::equal_to<Slang::IRBlock*> >::add (this=0x7fffffffce98, kvPair=...) at /home/josh/Projects/slang/source/slang/../core/slang-dictionary.h:313
shader-slang/slang#10 0x00007ffff6a1b7e3 in Slang::Dictionary<Slang::IRBlock*, Slang::EliminateMultiLevelBreakContext::BreakableRegionInfo*, Slang::Hash<Slang::IRBlock*>, std::equal_to<Slang::IRBlock*> >::add (this=0x7fffffffce98, key=@0x7fffffffc9f0: 0x55555a1545f8, value=@0x7fffffffc9e0: 0x555559ff9430)
    at /home/josh/Projects/slang/source/slang/../core/slang-dictionary.h:322
shader-slang/slang#11 0x00007ffff6a1947e in Slang::EliminateMultiLevelBreakContext::FuncContext::gatherInfo(Slang::IRGlobalValueWithCode*)::{lambda(Slang::EliminateMultiLevelBreakContext::BreakableRegionInfo*)#1}::operator()(Slang::EliminateMultiLevelBreakContext::BreakableRegionInfo*) const (__closure=0x7fffffffcbc0, 
    region=0x555559ff9430) at /home/josh/Projects/slang/source/slang/slang-ir-eliminate-multilevel-break.cpp:209
shader-slang/slang#12 0x00007ffff6a1b82d in Slang::EliminateMultiLevelBreakContext::BreakableRegionInfo::forEach<Slang::EliminateMultiLevelBreakContext::FuncContext::gatherInfo(Slang::IRGlobalValueWithCode*)::{lambda(Slang::EliminateMultiLevelBreakContext::BreakableRegionInfo*)#1}>(Slang::EliminateMultiLevelBreakContext::FuncContext::gatherInfo(Slang::IRGlobalValueWithCode*)::{lambda(Slang::EliminateMultiLevelBreakContext::BreakableRegionInfo*)#1} const&) (this=0x555559ff9430, 
    f=...) at /home/josh/Projects/slang/source/slang/slang-ir-eliminate-multilevel-break.cpp:92
shader-slang/slang#13 0x00007ffff6a1b88c in Slang::EliminateMultiLevelBreakContext::BreakableRegionInfo::forEach<Slang::EliminateMultiLevelBreakContext::FuncContext::gatherInfo(Slang::IRGlobalValueWithCode*)::{lambda(Slang::EliminateMultiLevelBreakContext::BreakableRegionInfo*)#1}>(Slang::EliminateMultiLevelBreakContext::FuncContext::gatherInfo(Slang::IRGlobalValueWithCode*)::{lambda(Slang::EliminateMultiLevelBreakContext::BreakableRegionInfo*)#1} const&) (this=0x55555a010a80, 
    f=...) at /home/josh/Projects/slang/source/slang/slang-ir-eliminate-multilevel-break.cpp:94
shader-slang/slang#14 0x00007ffff6a197b4 in Slang::EliminateMultiLevelBreakContext::FuncContext::gatherInfo (this=0x7fffffffce00, func=0x55555a151a78)
    at /home/josh/Projects/slang/source/slang/slang-ir-eliminate-multilevel-break.cpp:201
shader-slang/slang#15 0x00007ffff6a1a8fa in Slang::EliminateMultiLevelBreakContext::processFunc (this=0x7fffffffcfb0, func=0x55555a151a78)
    at /home/josh/Projects/slang/source/slang/slang-ir-eliminate-multilevel-break.cpp:384
shader-slang/slang#16 0x00007ffff6a16fe6 in Slang::eliminateMultiLevelBreak (irModule=0x55555a0f30f0)
    at /home/josh/Projects/slang/source/slang/slang-ir-eliminate-multilevel-break.cpp:606
shader-slang/slang#17 0x00007ffff68a3fa7 in Slang::linkAndOptimizeIR (codeGenContext=0x7fffffffd6c0, options=..., outLinkedIR=...)
    at /home/josh/Projects/slang/source/slang/slang-emit.cpp:1897
shader-slang/slang#18 0x00007ffff68a6e80 in Slang::emitSPIRVForEntryPointsDirectly (codeGenContext=0x7fffffffd6c0, outArtifact=...)
    at /home/josh/Projects/slang/source/slang/slang-emit.cpp:2729
shader-slang/slang#19 0x00007ffff6720d89 in Slang::CodeGenContext::_emitEntryPoints (this=0x7fffffffd6c0, outArtifact=...)
    at /home/josh/Projects/slang/source/slang/slang-cod...

</details>

- Fixes shader-slang/slang#8685

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.

When a continue statement inside a switch that is inside a while loop
targets the while loop's continue block, the compiler would crash with
a dictionary key collision error. This occurred because:

1. For while loops, the continue block often equals the target block
2. The previous code only added continue block to exit blocks if different from target
3. This allowed child regions (like switch) to include the continue block in their blocks
4. The same block appeared in both parent and child regions, causing duplicate key error

The fix:
1. Always add continue block to exit blocks, even if it equals target block
2. Special case handling when collecting blocks for loops where continue == target
3. Allow multi-level branches to continue blocks that equal target blocks (no transformation needed)
4. Skip transformation for these branches since they can go directly to loop entry

Co-authored-by: gtong-nv <[email protected]>
Copilot AI changed the title [WIP] Fix compiler exception with nested continue in switch statement Fix dictionary collision in multi-level break elimination for while loops with nested continue Nov 10, 2025
Copilot AI requested a review from gtong-nv November 10, 2025 23:31
Copilot finished work on behalf of gtong-nv November 10, 2025 23:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants