@@ -2003,7 +2003,14 @@ LogicalResult ControlFlowStructurizer::structurize() {
20032003 // block inside the selection (`body.back()`). Values produced by block
20042004 // arguments will be yielded by the selection region. We do not update uses or
20052005 // erase original block arguments yet. It will be done later in the code.
2006- if (!isLoop) {
2006+ //
2007+ // Code below is not executed for loops as it would interfere with the logic
2008+ // above. Currently block arguments in the merge block are not supported, but
2009+ // instead, the code above copies those arguments from the header block into
2010+ // the merge block. As such, running the code would yield those copied
2011+ // arguments that is most likely not a desired behaviour. This may need to be
2012+ // revisited in the future.
2013+ if (!isLoop)
20072014 for (BlockArgument blockArg : mergeBlock->getArguments ()) {
20082015 // Create new block arguments in the last block ("merge block") of the
20092016 // selection region. We create one argument for each argument in
@@ -2013,7 +2020,6 @@ LogicalResult ControlFlowStructurizer::structurize() {
20132020 valuesToYield.push_back (body.back ().getArguments ().back ());
20142021 outsideUses.push_back (blockArg);
20152022 }
2016- }
20172023
20182024 // All the blocks cloned into the SelectionOp/LoopOp's region can now be
20192025 // cleaned up.
@@ -2025,32 +2031,30 @@ LogicalResult ControlFlowStructurizer::structurize() {
20252031
20262032 // All internal uses should be removed from original blocks by now, so
20272033 // 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);
2034+ // the newly created selection / loop region.
2035+ for (Block *block : constructBlocks) {
2036+ for (Operation &op : *block) {
2037+ if (!op.use_empty ())
2038+ for (Value result : op.getResults ()) {
2039+ valuesToYield.push_back (mapper.lookupOrNull (result));
2040+ outsideUses.push_back (result);
20422041 }
2042+ }
2043+ for (BlockArgument &arg : block->getArguments ()) {
2044+ if (!arg.use_empty ()) {
2045+ valuesToYield.push_back (mapper.lookupOrNull (arg));
2046+ outsideUses.push_back (arg);
20432047 }
20442048 }
20452049 }
20462050
20472051 assert (valuesToYield.size () == outsideUses.size ());
20482052
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 ()) {
2053+ // If we need to yield any values from the selection / loop region we will
2054+ // take care of it here.
2055+ if (!valuesToYield.empty ()) {
20522056 LLVM_DEBUG (logger.startLine ()
2053- << " [cf] yielding values from the selection region\n " );
2057+ << " [cf] yielding values from the selection / loop region\n " );
20542058
20552059 // Update `mlir.merge` with values to be yield.
20562060 auto mergeOps = body.back ().getOps <spirv::MergeOp>();
@@ -2059,25 +2063,40 @@ LogicalResult ControlFlowStructurizer::structurize() {
20592063 merge->setOperands (valuesToYield);
20602064
20612065 // 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+ // we create a new SelectionOp / LoopOp with required list of results and
2067+ // move the region from the initial SelectionOp / LoopOp. The initial
2068+ // operation is then removed. Since we move the region to the new op all
2069+ // links between blocks and remapping we have previously done should be
2070+ // preserved.
20662071 builder.setInsertionPoint (&mergeBlock->front ());
2067- auto selectionOp = builder.create <spirv::SelectionOp>(
2068- location, TypeRange (ValueRange (outsideUses)),
2069- static_cast <spirv::SelectionControl>(control));
2070- selectionOp->getRegion (0 ).takeBody (body);
2072+
2073+ Operation *newOp = nullptr ;
2074+
2075+ if (isLoop)
2076+ newOp = builder.create <spirv::LoopOp>(
2077+ location, TypeRange (ValueRange (outsideUses)),
2078+ static_cast <spirv::LoopControl>(control));
2079+ else
2080+ newOp = builder.create <spirv::SelectionOp>(
2081+ location, TypeRange (ValueRange (outsideUses)),
2082+ static_cast <spirv::SelectionControl>(control));
2083+
2084+ newOp->getRegion (0 ).takeBody (body);
20712085
20722086 // Remove initial op and swap the pointer to the newly created one.
20732087 op->erase ();
2074- op = selectionOp ;
2088+ op = newOp ;
20752089
2076- // Update all outside uses to use results of the SelectionOp and remove
2077- // block arguments from the original merge block.
2090+ // Update all outside uses to use results of the SelectionOp / LoopOp and
2091+ // remove block arguments from the original merge block.
20782092 for (unsigned i = 0 , e = outsideUses.size (); i != e; ++i)
2079- outsideUses[i].replaceAllUsesWith (selectionOp.getResult (i));
2080- mergeBlock->eraseArguments (0 , mergeBlock->getNumArguments ());
2093+ outsideUses[i].replaceAllUsesWith (op->getResult (i));
2094+
2095+ // We do not support block arguments in loop merge block. Also running this
2096+ // function with loop would break some of the loop specific code above
2097+ // dealing with block arguments.
2098+ if (!isLoop)
2099+ mergeBlock->eraseArguments (0 , mergeBlock->getNumArguments ());
20812100 }
20822101
20832102 // Check that whether some op in the to-be-erased blocks still has uses. Those
0 commit comments