Repeated assignments to the same SSA value in optimized MLIR program

Hi everyone!

I built a small compilation pass that performs a transformation essentially amounting to repeating every instruction in a program + its ‘inverse’ (also called its adjoint) a bunch of times.

As a simple example, here is a small program:

module {
  func.func @main(%arg0: !ensemble.qubit, %arg1: !ensemble.qubit) -> !ensemble.qubit {
    %0 = ensemble.gate1q "H" %arg0 : (!ensemble.qubit) -> (!ensemble.qubit)
    %1 = ensemble.gate1q "H" %arg1 : (!ensemble.qubit) -> (!ensemble.qubit)

    %2 = ensemble.gate1q "X" %0 : (!ensemble.qubit) -> (!ensemble.qubit)
    %3 = ensemble.gate1q "X" %1 : (!ensemble.qubit) -> (!ensemble.qubit)

    return %3: !ensemble.qubit
  }
}

And here is the output from the compiler pass I built (using a custom version of mlir-opt that I registered my dialect and pass with):

module {
  func.func @main(%arg0: !ensemble.qubit, %arg1: !ensemble.qubit) -> !ensemble.qubit {
    %0 = ensemble.gate1q "H" %arg0 {"zne-applied"} : (!ensemble.qubit) -> (!ensemble.qubit)
    affine.for %arg2 = 0 to 10 {
      %4 = ensemble.gate1q "H" %0 {"zne-applied"} : (!ensemble.qubit) -> (!ensemble.qubit)
      %5 = ensemble.gate1q "H" %4 {adjoint, "zne-applied"} : (!ensemble.qubit) -> (!ensemble.qubit)
    }
    %1 = ensemble.gate1q "H" %arg1 {"zne-applied"} : (!ensemble.qubit) -> (!ensemble.qubit)
    affine.for %arg2 = 0 to 10 {
      %4 = ensemble.gate1q "H" %1 {"zne-applied"} : (!ensemble.qubit) -> (!ensemble.qubit)
      %5 = ensemble.gate1q "H" %4 {adjoint, "zne-applied"} : (!ensemble.qubit) -> (!ensemble.qubit)
    }
    %2 = ensemble.gate1q "X" %0 {"zne-applied"} : (!ensemble.qubit) -> (!ensemble.qubit)
    affine.for %arg2 = 0 to 10 {
      %4 = ensemble.gate1q "X" %2 {"zne-applied"} : (!ensemble.qubit) -> (!ensemble.qubit)
      %5 = ensemble.gate1q "X" %4 {adjoint, "zne-applied"} : (!ensemble.qubit) -> (!ensemble.qubit)
    }
    %3 = ensemble.gate1q "X" %1 {"zne-applied"} : (!ensemble.qubit) -> (!ensemble.qubit)
    affine.for %arg2 = 0 to 10 {
      %4 = ensemble.gate1q "X" %3 {"zne-applied"} : (!ensemble.qubit) -> (!ensemble.qubit)
      %5 = ensemble.gate1q "X" %4 {adjoint, "zne-applied"} : (!ensemble.qubit) -> (!ensemble.qubit)
    }
    return %3 : !ensemble.qubit
  }
}

Something that I noticed is that the names of the SSA values being assigned inside the for loops are repeated (%4 and %5).

Is there any way that I could change the names of these variables (programmatically)? I’m also not sure how the generated IR included multiple assignments to the same values - am I wrong in thinking that MLIR handles SSA value naming automatically to ensure that no value is assigned twice? Or do I need to take specific steps in my compiler passes to ensure this?

Thank you in advance!

You can’t really control a value name, it does not exist outside of the text format. But you can have unique names by passing the --mlir-print-unique-ssa-ids command line option.

These are different values. Think of it as you write in C:

if (...) {
  int foo = ...;
  ...
}
if (...) {
  int foo = ...;
  ...
}

The two variables foo have the same identifier, but are unrelated and don’t conflict because they have different scope.

That makes sense, thank you so much!