@@ -1992,6 +1992,29 @@ LogicalResult ControlFlowStructurizer::structurize() {
19921992 ArrayRef<Value>(blockArgs));
19931993 }
19941994
1995+ // Values defined inside the selection region that need to be yielded outside
1996+ // the region.
1997+ SmallVector<Value> valuesToYield;
1998+ // Outside uses of values that were sunk into the selection region. Those uses
1999+ // will be replaced with values returned by the SelectionOp.
2000+ SmallVector<Value> outsideUses;
2001+
2002+ // Move block arguments of the original block (`mergeBlock`) into the merge
2003+ // block inside the selection (`body.back()`). Values produced by block
2004+ // arguments will be yielded by the selection region. We do not update uses or
2005+ // erase original block arguments yet. It will be done later in the code.
2006+ if (!isLoop) {
2007+ for (BlockArgument blockArg : mergeBlock->getArguments ()) {
2008+ // Create new block arguments in the last block ("merge block") of the
2009+ // selection region. We create one argument for each argument in
2010+ // `mergeBlock`. This new value will need to be yielded, and the original
2011+ // value replaced, so add them to appropriate vectors.
2012+ body.back ().addArgument (blockArg.getType (), blockArg.getLoc ());
2013+ valuesToYield.push_back (body.back ().getArguments ().back ());
2014+ outsideUses.push_back (blockArg);
2015+ }
2016+ }
2017+
19952018 // All the blocks cloned into the SelectionOp/LoopOp's region can now be
19962019 // cleaned up.
19972020 LLVM_DEBUG (logger.startLine () << " [cf] cleaning up blocks after clone\n " );
@@ -2000,17 +2023,79 @@ LogicalResult ControlFlowStructurizer::structurize() {
20002023 for (auto *block : constructBlocks)
20012024 block->dropAllReferences ();
20022025
2026+ // All internal uses should be removed from original blocks by now, so
2027+ // whatever is left is an outside use and will need to be yielded from
2028+ // the newly created selection region.
2029+ if (!isLoop) {
2030+ for (Block *block : constructBlocks) {
2031+ for (Operation &op : *block) {
2032+ if (!op.use_empty ())
2033+ for (Value result : op.getResults ()) {
2034+ valuesToYield.push_back (mapper.lookupOrNull (result));
2035+ outsideUses.push_back (result);
2036+ }
2037+ }
2038+ for (BlockArgument &arg : block->getArguments ()) {
2039+ if (!arg.use_empty ()) {
2040+ valuesToYield.push_back (mapper.lookupOrNull (arg));
2041+ outsideUses.push_back (arg);
2042+ }
2043+ }
2044+ }
2045+ }
2046+
2047+ assert (valuesToYield.size () == outsideUses.size ());
2048+
2049+ // If we need to yield any values from the selection region we will take
2050+ // care of it here.
2051+ if (!isLoop && !valuesToYield.empty ()) {
2052+ LLVM_DEBUG (logger.startLine ()
2053+ << " [cf] yielding values from the selection region\n " );
2054+
2055+ // Update `mlir.merge` with values to be yield.
2056+ auto mergeOps = body.back ().getOps <spirv::MergeOp>();
2057+ Operation *merge = llvm::getSingleElement (mergeOps);
2058+ assert (merge);
2059+ merge->setOperands (valuesToYield);
2060+
2061+ // MLIR does not allow changing the number of results of an operation, so
2062+ // we create a new SelectionOp with required list of results and move
2063+ // the region from the initial SelectionOp. The initial operation is then
2064+ // removed. Since we move the region to the new op all links between blocks
2065+ // and remapping we have previously done should be preserved.
2066+ builder.setInsertionPoint (&mergeBlock->front ());
2067+ auto selectionOp = builder.create <spirv::SelectionOp>(
2068+ location, TypeRange (outsideUses),
2069+ static_cast <spirv::SelectionControl>(control));
2070+ selectionOp->getRegion (0 ).takeBody (body);
2071+
2072+ // Remove initial op and swap the pointer to the newly created one.
2073+ op->erase ();
2074+ op = selectionOp;
2075+
2076+ // Update all outside uses to use results of the SelectionOp and remove
2077+ // block arguments from the original merge block.
2078+ for (unsigned i = 0 , e = outsideUses.size (); i != e; ++i)
2079+ outsideUses[i].replaceAllUsesWith (selectionOp.getResult (i));
2080+ for (unsigned i = 0 , e = mergeBlock->getNumArguments (); i != e; ++i)
2081+ mergeBlock->eraseArgument (i);
2082+ }
2083+
20032084 // Check that whether some op in the to-be-erased blocks still has uses. Those
20042085 // uses come from blocks that won't be sinked into the SelectionOp/LoopOp's
20052086 // region. We cannot handle such cases given that once a value is sinked into
2006- // the SelectionOp/LoopOp's region, there is no escape for it:
2007- // SelectionOp/LooOp does not support yield values right now.
2087+ // the SelectionOp/LoopOp's region, there is no escape for it.
20082088 for (auto *block : constructBlocks) {
20092089 for (Operation &op : *block)
20102090 if (!op.use_empty ())
2011- return op.emitOpError (
2012- " failed control flow structurization: it has uses outside of the "
2013- " enclosing selection/loop construct" );
2091+ return op.emitOpError (" failed control flow structurization: value has "
2092+ " uses outside of the "
2093+ " enclosing selection/loop construct" );
2094+ for (BlockArgument &arg : block->getArguments ())
2095+ if (!arg.use_empty ())
2096+ return emitError (arg.getLoc (), " failed control flow structurization: "
2097+ " block argument has uses outside of the "
2098+ " enclosing selection/loop construct" );
20142099 }
20152100
20162101 // Then erase all old blocks.
@@ -2236,7 +2321,7 @@ LogicalResult spirv::Deserializer::structurizeControlFlow() {
22362321
22372322 auto *mergeBlock = mergeInfo.mergeBlock ;
22382323 assert (mergeBlock && " merge block cannot be nullptr" );
2239- if (!mergeBlock->args_empty ())
2324+ if (mergeInfo. continueBlock && !mergeBlock->args_empty ())
22402325 return emitError (unknownLoc, " OpPhi in loop merge block unimplemented" );
22412326 LLVM_DEBUG ({
22422327 logger.startLine () << " [cf] merge block " << mergeBlock << " :\n " ;
0 commit comments