From bae87afb20f95f9f27c64c4955bbc4464c576509 Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 6 Feb 2025 22:02:43 -0800 Subject: Support stage_switch. (#6311) * Support stage_switch. * Update proposal status. * Fix gl_InstanceID. * Fix. --- source/slang/slang-lower-to-ir.cpp | 96 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) (limited to 'source/slang/slang-lower-to-ir.cpp') 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 } } + 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 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()) -- cgit v1.2.3