summaryrefslogtreecommitdiff
path: root/source/slang/slang-lower-to-ir.cpp
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2025-02-06 22:02:43 -0800
committerGitHub <noreply@github.com>2025-02-06 22:02:43 -0800
commitbae87afb20f95f9f27c64c4955bbc4464c576509 (patch)
tree44d079bd76002d69be20efdbd03ac6ff62ef8caf /source/slang/slang-lower-to-ir.cpp
parent075b10e69055acc6536d74c1cb3399e0fe75338d (diff)
Support stage_switch. (#6311)
* Support stage_switch. * Update proposal status. * Fix gl_InstanceID. * Fix.
Diffstat (limited to 'source/slang/slang-lower-to-ir.cpp')
-rw-r--r--source/slang/slang-lower-to-ir.cpp96
1 files changed, 96 insertions, 0 deletions
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index 36003dcb2..06cdf430b 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -6719,6 +6719,102 @@ struct StmtLoweringVisitor : StmtVisitor<StmtLoweringVisitor>
}
}
+ void visitStageSwitchStmt(StageSwitchStmt* stmt)
+ {
+ if (!stmt->targetCases.getCount())
+ return;
+
+ // We will lower stage switch as a normal switch statement, so they can participate in all
+ // optimizations.
+ auto builder = getBuilder();
+ startBlockIfNeeded(stmt);
+
+ // First emit code to get the current stage to switch on:
+ auto conditionVal = builder->emitGetCurrentStage();
+
+ // Remember the initial block so that we can add to it
+ // after we've collected all the `case`s
+ auto initialBlock = builder->getBlock();
+
+ // Next, create a block to use as the target for any `break` statements
+ auto breakLabel = createBlock();
+
+ // Register the `break` label so
+ // that we can find it for nested statements.
+ context->shared->breakLabels.add(stmt, breakLabel);
+
+ builder->setInsertInto(initialBlock->getParent());
+
+ // Iterate over the body of the statement, looking
+ // for `case` or `default` statements:
+ SwitchStmtInfo info;
+ info.initialBlock = initialBlock;
+ info.defaultLabel = nullptr;
+
+ Dictionary<Stmt*, IRBlock*> mapCaseStmtToBlock;
+ for (auto targetCase : stmt->targetCases)
+ {
+ IRBlock* caseBlock = nullptr;
+ if (!mapCaseStmtToBlock.tryGetValue(targetCase->body, caseBlock))
+ {
+ caseBlock = builder->emitBlock();
+ lowerStmt(context, targetCase->body);
+ mapCaseStmtToBlock.add(targetCase->body, caseBlock);
+ if (!builder->getBlock()->getTerminator())
+ builder->emitBranch(breakLabel);
+ }
+ if (targetCase->capability == 0)
+ {
+ info.defaultLabel = caseBlock;
+ }
+ else
+ {
+ auto stage = getStageFromAtom((CapabilityAtom)targetCase->capability);
+ info.cases.add(builder->getIntValue(builder->getIntType(), (IRIntegerValue)stage));
+ info.cases.add(caseBlock);
+ }
+ }
+
+ // If the current block (the end of the last
+ // `case`) is not terminated, then terminate with a
+ // `break` operation.
+ //
+ // Double check that we aren't in the initial
+ // block, so we don't get tripped up on an
+ // empty `switch`.
+ auto curBlock = builder->getBlock();
+ if (curBlock != initialBlock)
+ {
+ // Is the block already terminated?
+ if (!curBlock->getTerminator())
+ {
+ // Not terminated, so add one.
+ builder->emitBreak(breakLabel);
+ }
+ }
+
+ // If there was no `default` statement, then the
+ // default case will just branch directly to the end.
+ auto defaultLabel = info.defaultLabel ? info.defaultLabel : breakLabel;
+
+ // Now that we've collected the cases, we are
+ // prepared to emit the `switch` instruction
+ // itself.
+ builder->setInsertInto(initialBlock);
+ builder->emitSwitch(
+ conditionVal,
+ breakLabel,
+ defaultLabel,
+ info.cases.getCount(),
+ info.cases.getBuffer());
+
+ // Finally we insert the label that a `break` will jump to
+ // (and that control flow will fall through to otherwise).
+ // This is the block that subsequent code will go into.
+ insertBlock(breakLabel);
+ context->shared->breakLabels.remove(stmt);
+ }
+
void visitTargetSwitchStmt(TargetSwitchStmt* stmt)
{
if (!stmt->targetCases.getCount())