[llvm] [MC] Fuse relaxation and layout into a single forward pass (PR #184544)

Hans Wennborg via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 30 04:45:29 PDT 2026


zmodem wrote:

I think I'm starting to see what the problem is.

Here's an asm based reproducer: [a.s.gz](https://github.com/user-attachments/files/26348152/a.s.gz)

```
$ build/bin/llvm-mc -filetype=obj -o /tmp/a.bad.o -triple=thumbv7-linux-androideabi29 /tmp/a.s
/tmp/a.s:43:10: error: out of range pc-relative fixup value
        cbz     r0, .LBB0_11
                    ^
```

If we comment out the error message we can see what code it's trying to emit:

```
$ git diff
diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
index cc21844b2635..be985bbd3468 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
@@ -747,7 +747,7 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCAssembler &Asm,
     // so ensure that the raw value LSB is zero and it lies in [2, 130].
     // An offset of 2 will be relaxed to a NOP.
     if ((int64_t)Value < 2 || Value > 0x82 || Value & 1) {
-      Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value");
+      llvm::errs() << "FIXUP VALUE: " << Value << "\n";
       return 0;
     }
     // Offset by 4 and don't encode the lower bit, which is always 0.
```

```
$ build/bin/llvm-mc -filetype=obj -o /tmp/a.bad.o -triple=thumbv7-linux-androideabi29 /tmp/a.s
$ build/bin/llvm-objdump -d /tmp/a.bad.o --triple=thumbv7m-arm-none-eabi | tee /tmp/bad.txt
```

and compare it with the output before this change:

```
$ build/bin/llvm-mc.good -filetype=obj -o /tmp/a.good.o -triple=thumbv7-linux-androideabi29 /tmp/a.s
$ build/bin/llvm-objdump -d /tmp/a.good.o --triple=thumbv7m-arm-none-eabi | tee /tmp/good.txt
```

Here are the output files for convenience:  [good.txt](https://github.com/user-attachments/files/26348297/good.txt), [bad.txt](https://github.com/user-attachments/files/26348302/bad.txt)

>From good.txt:

```
      2c: b3e8         	cbz	r0, 0xaa <_ZN4fuzz5mojom14FuzzUnion_Data8ValidateEv+0xaa> @ imm = #0x7a
...
      56: 4a17         	ldr	r2, [pc, #0x5c]         @ 0xb4 <_ZN4fuzz5mojom14FuzzUnion_Data8ValidateEv+0xb4>
...
      8a: 4a0d         	ldr	r2, [pc, #0x34]         @ 0xc0 <_ZN4fuzz5mojom14FuzzUnion_Data8ValidateEv+0xc0>
...
      9c: 4a09         	ldr	r2, [pc, #0x24]         @ 0xc4 <_ZN4fuzz5mojom14FuzzUnion_Data8ValidateEv+0xc4>
...
      aa: 4620         	mov	r0, r4
      ac: bdb0         	pop	{r4, r5, r7, pc}
```

>From bad.txt:

```
      2c: b100         	cbz	r0, ...     <--- Trying to branch to B0 but the immediate doesn't fit.
...
      56: f8df 2060    	ldr.w	r2, [pc, #0x60]         @ 0xb8 <_ZN4fuzz5mojom14FuzzUnion_Data8ValidateEv+0xb8>
...
      8c: f8df 2034    	ldr.w	r2, [pc, #0x34]         @ 0xc4 <_ZN4fuzz5mojom14FuzzUnion_Data8ValidateEv+0xc4>
...
      a0: f8df 2024    	ldr.w	r2, [pc, #0x24]         @ 0xc8 <_ZN4fuzz5mojom14FuzzUnion_Data8ValidateEv+0xc8>
...
      b0: 4620         	mov	r0, r4
      b2: bdb0         	pop	{r4, r5, r7, pc}
```

There seems to be two issues:

1. After this PR, the assembler is relaxing a bunch of `LDR` instructions to the wide encoding, `LDR.W`. If I'm reading https://developer.arm.com/documentation/ddi0406/c/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/LDR--literal-?lang=en right, the narrow `LDR` encoding supports offsets from 0--1020, so these relaxations are all unnecessary.

   Regardless of the out-of-range fixup error, this seems like an unfortunate binary size regression.

2. Using the wider `LDR.W` instructions has pushed the `CBZ`'s branch target out of range (the fixup has gone from 126 to 132 bytes), causing the error we hit.

   The `CBZ` instruction is formed by `ARMConstantIslands::optimizeThumb2Branches()`. It relies on the `BBInfoVector`'s offset information, which is ultimately based on `ARMBaseInstrInfo::getInstSizeInBytes()`. That doesn't take into account that these `LDR` instructions (2-byte `tLDRpci`) can get relaxed to 4-byte `t2LDRpci` by the assembler (`ARMAsmBackend::relaxInstruction()`).

   Should `ARMBaseInstrInfo::getInstSizeInBytes()` have special cases for instructions that can get relaxed to wider encodings by the assembler? (Maybe it could call `ARMAsmBackend::getRelaxedOpcode()`?)

+ at smithp35 for Arm expertise.

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


More information about the llvm-commits mailing list