[llvm] [TargetInstrInfo] enable foldMemoryOperand for InlineAsm (PR #70743)

Nick Desaulniers via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 8 16:08:08 PST 2023


================
@@ -565,6 +565,64 @@ static MachineInstr *foldPatchpoint(MachineFunction &MF, MachineInstr &MI,
   return NewMI;
 }
 
+static void foldInlineAsmMemOperand(MachineInstr *MI, unsigned OpNo, int FI,
+                                    const TargetInstrInfo &TII) {
+  MachineOperand &MO = MI->getOperand(OpNo);
+  const VirtRegInfo &RI = AnalyzeVirtRegInBundle(*MI, MO.getReg());
+
+  // If the machine operand is tied, untie it first.
+  if (MO.isTied()) {
+    unsigned TiedTo = MI->findTiedOperandIdx(OpNo);
+    MI->untieRegOperand(OpNo);
----------------
nickdesaulniers wrote:

> Note: A safe temporary solution could be to disallow the folding (i.e., use the m variant only) for tied operands.

No.  Support for tied operands is table stakes for resolving #20571 because clang will lower "+" modifiers ("in+out" variables) to tied operands.  We need to support:
1. inputs
2. outputs
3. in/outs

so 3 is table stakes.

> In practice, I don't believe this works all the time because IIRC, for instance, x86 cannot have a memory operand both as definition and as argument of the same instruction.

If you have tied operands for inline asm, you may have 2 distinct variables (one as input, one as output), but for the duration of the inline asm blob, they have 1 (the same) storage location (register or stack slot).  So in the inline asm, you'd only refer to the 1 storage location (be it register or memory) and not both values.  So it doesn't matter if x86 cannot have a memory def and use as operands on the same instr.  My example above wasn't clear in that regard, perhaps another example:

```c
long foo (long x) {
    long y;
    asm (
        "mov %0, r8\n\t"
        "mov r8, %0"
        :"=r"(y):"0"(x):"r8");
    return y;
}
```
in the case, `%1` is not referenced in the inline asm, only `%0` is.  But if it were, it would have the same value as `%0` because that's what tying _is_ (at least my understanding of it, which may be wrong).

> what I'm saying is tied operands and untied operands don't have the same semantic

That is correct but I think only so for register operands. If you have 2 memory operands which refer to the same stack slot, they are in effect tied.  IIUC, tied operands only matter for the purpose of register allocation.  If an input value is tied to an output value, tieing them just means "put them in the same place (be that a register or memory location)."

So in MIR if we have:

```
INLINEASM &"# $0 $1", 0 /* attdialect */, 1076101130 /* regdef:GR32 spillable */, def %0, 2147483657 /* reguse tiedto:$0 */, %1(tied-def 3)
```
(i.e. `%0` is tied to `%1`) then that means the developer is asking the register allocator "please assign `%0` and `%1` to the same storage location (be that register or stack slot) for the duration of the INLINEASM blob.

Given that example as input, after my change (this change), we first notice that `%0` is tied, so we untie it, and transform the above (the recursive call) to:

```
INLINEASM &"# $0 $1", 0 /* attdialect */, 1076101130 /* regdef:GR32 spillable */, def %0, 262190 /* mem:m */, %stack.0, 1, $noreg, 0, $noreg
```
which replaces `%1` (a tied register def) with a frame index. Then upon return, we continue transforming `%0` replacing that with _the same frame index_ (in this case `%stack.o`):
```
INLINEASM &"# $0 $1", 0 /* attdialect */, 262190 /* mem:m */, %stack.0, 1, $noreg, 0, $noreg, 262190 /* mem:m */, %stack.0, 1, $noreg, 0, $noreg
```
(this is better reflected in the test added in the child patch in #70832)

---

Perhaps I'm way off in my understanding of tied operands for inline asm, but I still don't understand your concern?  Does any of the above clear anything up?

https://github.com/llvm/llvm-project/pull/70743


More information about the llvm-commits mailing list