<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/202160>202160</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            [RISC-V][lld] ld.lld TLS le relaxation missing (before LLVM 15)
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            lld
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          XiaobingHou1219
      </td>
    </tr>
</table>

<pre>
    I ran into this while reducing a RISC-V linker testcase. The reproducer is small, and I have been seeing the same result consistently across three reruns.

### Summary
ld.lld 14.0.6 leaves the relaxable TPREL_HI20/TPREL_ADD/TPREL_LO12 sequences unrelaxed as `lui/add/addi` and `lui/add/sw`, while 15.0.7 and 22.1.0 rewrite the same legal input to `addi tp,imm` and `sw offset(tp)`.

### Expected behavior
Link a small RV64 local-exec TLS sample with two in-range TPREL address materializations and one boundary case.

### Environment
- linker route: llvm-mc 22 plus ld.lld version replay
- march: rv64i
- mabi: lp64
- first failing stage: link
- local stability check: True

### Reduced testcase
These are the reduced input files from the minimized reproducer I used locally:

#### `case.s`
```asm
# REQUIRES: riscv

## Additionally test that (a) -no-pie/-pie have the same behavior
## (b) --no-relax/--relax have the same behavior when R_RISCV_RELAX is suppressed.
# RUN: llvm-mc -filetype=obj -triple=riscv32 %s -o %t.32.o
# RUN: ld.lld --relax %t.32.o -o %t.32
# RUN: llvm-nm -p %t.32 | FileCheck --check-prefixes=NM %s
# RUN: llvm-objdump -d --no-show-raw-insn %t.32 | FileCheck --check-prefixes=LE %s
# RUN: ld.lld -pie --no-relax %t.32.o -o %t.32
# RUN: llvm-objdump -d --no-show-raw-insn %t.32 | FileCheck --check-prefixes=LE %s

# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -o %t.64.o
# RUN: ld.lld --no-relax %t.64.o -o %t.64
# RUN: llvm-objdump -d --no-show-raw-insn %t.64 | FileCheck --check-prefixes=LE %s
# RUN: ld.lld -pie --no-relax %t.64.o -o %t.64
# RUN: llvm-objdump -d --no-show-raw-insn %t.64 | FileCheck --check-prefixes=LE %s
# RUN: ld.lld %t.64.o -o %t.64.relax
# RUN: llvm-objdump -d --no-show-raw-insn %t.64.relax | FileCheck --check-prefixes=LE-RELAX %s

# RUN: not ld.lld -shared %t.32.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR --implicit-check-not=error:

# ERR: error: relocation R_RISCV_TPREL_HI20 against .LANCHOR0 cannot be used with -shared
# ERR: error: relocation R_RISCV_TPREL_LO12_I against .LANCHOR0 cannot be used with -shared
# ERR: error: relocation R_RISCV_TPREL_HI20 against a cannot be used with -shared
# ERR: error: relocation R_RISCV_TPREL_LO12_I against a cannot be used with -shared
# ERR: error: relocation R_RISCV_TPREL_HI20 against a cannot be used with -shared
# ERR: error: relocation R_RISCV_TPREL_LO12_S against a cannot be used with -shared
# ERR: error: relocation R_RISCV_TPREL_HI20 against a cannot be used with -shared
# ERR: error: relocation R_RISCV_TPREL_LO12_S against a cannot be used with -shared

# NM: {{0*}}00000008 b .LANCHOR0
# NM: {{0*}}00000800 B a

## .LANCHOR0@tprel = 8
## a@tprel = 12
# LE:      lui a1, 0x0
# LE-NEXT: add a1, a1, tp
# LE-NEXT: addi a1, a1, 0x8
# LE-NEXT: lui a2, 0x0
# LE-NEXT: add a2, a2, tp
# LE-NEXT: addi a2, a2, 0x7fc
# LE-NEXT: lui a3, 0x0
# LE-NEXT: addi a0, a0, 0x1
# LE-NEXT: add a3, a3, tp
# LE-NEXT: addi a0, a0, 0x2
# LE-NEXT: sw a0, 0x7fc(a3)
# LE-NEXT: lui a4, 0x1
# LE-NEXT: add a4, a4, tp
# LE-NEXT: sw a0, -0x800(a4)
# LE-EMPTY:

# LE-RELAX: <.text>:
# LE-RELAX-NEXT:   addi a1, tp, 0x8
# LE-RELAX-NEXT:   addi a2, tp, 0x7fc
# LE-RELAX-NEXT:   addi a0, a0, 0x1
# LE-RELAX-NEXT: addi a0, a0, 0x2
# LE-RELAX-NEXT:   sw a0, 0x7fc(tp)
# LE-RELAX-NEXT: lui a4, 0x1
# LE-RELAX-NEXT:   add a4, a4, tp
# LE-RELAX-NEXT:   sw a0, -0x800(a4)
# LE-RELAX-EMPTY:

lui a1, %tprel_hi(.LANCHOR0)
add a1, a1, tp, %tprel_add(.LANCHOR0)
addi a1, a1, %tprel_lo(.LANCHOR0)

## hi20(a-4) = hi20(0x7fc) = 0. relaxable
lui a2, %tprel_hi(a-4)
add a2, a2, tp, %tprel_add(a-4)
addi a2, a2, %tprel_lo(a-4)

## hi20(a-4) = hi20(0x7fc) = 0. relaxable
## Test non-adjacent instructions.
lui a3, %tprel_hi(a-4)
addi a0, a0, 0x1
add a3, a3, tp, %tprel_add(a-4)
addi a0, a0, 0x2
sw a0, %tprel_lo(a-4)(a3)

## hi20(a) = hi20(0x800) = 1. not relaxable
lui a4, %tprel_hi(a)
add a4, a4, tp, %tprel_add(a)
sw a0, %tprel_lo(a)(a4)

.section .tbss
.space 8
.LANCHOR0:
.space 0x800-8
.globl a
a:
.zero 4
```

### Reproduction notes
- This packaged root does not have a single canonical `run.ps1` wrapper.
- Use the reduced inputs under `case/` and follow the commands documented in `case/README.md`.
- Stable witness outputs, when present, are preserved under `verify/run1..run3/`.

### What I checked
- Reduced inputs are preserved under case/.
- Stable witness outputs are preserved under verify/run1..run3/.
- The strict recheck says stable normalized run signatures across three runs: True.
- Tracker guidance link: https://llvm.org/docs/HowToSubmitABug.html
- evidence summary: 3 clean reproductions under hunt/verify/lld_tls_le_relaxation_missing_before_15/run1..run3 show stable assembly success for main64.o, stable 14.0.6 outputs whose relax64.14.objdump.txt keep `lui/add/addi` and `lui/add/sw` sequences, and stable 15.0.7/22.1.0 outputs whose objdump files rewrite the same sites to `addi a1,tp,0x8`, `addi a2,tp,0x7fc`, and `sw a0,0x7fc(tp)`.

### Notes
upstream////llvm-project//lld//test//ELF//riscv-tls-le.s

Root key: `lld.riscv.tls_le_relaxation_missing_before_15`
Case id: `20260527-lld-tls-le-relaxation-missing`

</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzkWU1v4zjS_jXKpSBBpmzZPuTgxDY6gDszr5Ppd_YUUFLZYocitSRlJ_PrFyQlfyRy0tOYBhbYIJBtsr6rWCQfUa3ZViBeB6ObYDS_oo0ppbr-k1GZMbH9IpsBGUyvMlm8Xt-BogKYMBJMyTTsS8YRFBZNzsQWKKzvHm7Db8CZeEYFBrXJqcYIHktLVitZNDkqYBp0RTkPyC1QUcAdlHSHkCEK0IhWlikRNK0sm264gVwKzbRBYfgr0FxJrcGUCi2BaoSOgngexLP2SRL_Dw9NVVH16od5EXFewGAYxVEKHOkOtVOkkNMXmnGEx9_Xi9XTlzsSB2Tpf8zm88P31W8DAhr_3aDIUUMjHCcWQDUEacwbFpAlLQr_ZEEaO__eTOl9kMbWdx-_wSiKo7EjJCQaRDEo3Ctm8BgEjlvKgYm6MWCklWfFg6kDcsuq6kSP3oPcbDSagEzs9DRI4wuxWbzUmBssIMOS7phUnmDFxDNQnyBYf0uHwGVOeYgvmMPj6sFaVHOEPTMlmL0EJkJFxbYNHtCiUKg1VNSgYpSzv6hhUmhnoRQImWxEQdUruNq4YJvYMSVFhcL4qbArKiUbg0EyA853VVjlQAjUvNHQZneHSjMpbLlx-toxV1TlpeVSu3TIjqMZc6LqdNiNbZjSBjaUcVuG2tCt18bE88ESGw87lTHOzCvkJebPluhRNdjvz9quESwOS8LPP5aoEajCtgo9jU_zhnHUsFGycpMVE6xif2Fxuo7uoNFYeHv4a5DMepVb_UEau2hrW3l-Oo39P9XVgQHWi__74269eHChYjrfvZcIs6JgNqNWp3MITEkNBGRCAzKFUMiwZhiQpf3wK_tQyOel1hlHJpnjtKxuRVlm_-0CP-xLFLB-sg3n29N6sZr96bpKU9e2-LCITnz64_60XkIbWfNaY5DMZfYdQqNYze0v53BCICAjDaG0nyZKSCTfy_K11tl4IDzh6tcvKgjrjgSC8S0sGcdbW0AQhq6Qwlrhhr2gDpL5_VdnTL8smX0vmqqGsPCR06Xch4ruQya0-HEdq8UFHa2PNonHzPwNX3-JfT-Z1XQIYUWNUUEyD8jNwZVjntPhR3k-d9_SnjD-tPvp8Nek57_Ivj57Ir_Gf96qqPX1c9tC3xo-KCAhzSGWuqQKi7clvixwF5ClaDgHEiSLgKSDN7p9JZ0ZECTzxXoNYciqmrOcmXZWSBMkc1RKqvcNGxbrtTWqm7YnE5m7_fPQ7I5HFKBbyoQ2EK1m97dfflvHkFNhHcrQ7wxuj27d-gkl9rjzdPfL1Zz5Qn-tD_-0-F9v-8P_jO0HJfdfrehgfBOMb-KAzILxPBjPY_83gexYiT_EM4ljuAHac5Q5yhnGplbIIUjmMDmjoWdzg5PNbrWwKt0fbxjQgT3Txy_xKUV4v_jz0ZLRomgp_NPUl8jYGV38MukldBrJpxodhX9-qPGELn4Zb_LLOpNPdDKg7nLjn_HL4LJxTpR_fmjcmTzSS6j3BwJrPZlYsdPLXgw_Nc5R-OcF4w46w_hlEsdW6fCt0sXX3x__1dPou63JFW1yGxl8MXZvSXpoDgrhtDzc5e9defQzkFOGt9ntZ_kgh-cMn6borfx3qfL31MsslxPWY_pHibtoyQcJ9Dx9aTyueXtgsB3iqWQBmRybSieqZ-2fcjlUoJ_tvBccOLjsZTjrWyUjzqPQuuR6VzvSht2PxdER_Thxi7x3yws6cei8tbx36A3DeZM5d-WU9B9xomV_tLdTIUVIi-80R2HAbkaqyR0iEZ04nHzq8IU10dPIfiQUfevlUI69wTlvaX1BehciV9N-bBC5k25froc9rp9n-nxF9bh3IL_kQuvA2yxHGl0qIDKZ1t1YTXPsNuFjkSezs3nnXdhRbbnMeLfF0yPtX6gkDN8gHpcgGo-rOHuENKg7vOexZBpqmj_TLRagpDRQSNQuoA6eoKCZ2HK0ZxwpWE45BGmsGhHVehCkMewVrWtUUSfxD90D-WhoRIGqw2oCsuxwvY3kXO4dRy6riopCQyHzpkJhHPcJz3oxm39dRFVxBP5CeDAO3dwzI1BrkI2x-jwEiQJqhRqFcVlW6H-qHRZHg3ao2OY1IEvViEEUqUYk3r4L-N3_l9TAnUfGuvNdeEDBWnf7dLVefGJ5L2u_jdExiwjaKJbbVeAMA01ftcPxOIKQqqLcI2yNAM22gppGoX6DNjdCd1DfUbSi-TMq2DasoCJHjxYmMyiNqS25jRVZ2gtuJNXWXillrgOy_CL3j_KhySpmZjfNNipNxTuhuGMFWmG6xbCTGSSQc6TigAF6YNW7XzY2g8tDFDgvngzXTxyf_Kq3xE8V07ZWnzLcSIVPg9FZvMBet7uIUK2xyvgr6CbPbfA3UkFFmXBQCbnt6Fo8vUvNvpS6hdTTYTQYRu2VPjIvBp4R67-LlB8B9-59QafYAecBWbaw-bkFHZLggdR3kLpmBvUpmO62WNfe7JnKA_SHOXKcs_uOnz1C7q7nnR9nLi6N-2NraWptFNLKl8exSMJaye-Ym26k8F8M6nZosWqpHbgVGq5DjtEZxLG2beoZXdnYqPIicsTRj9RE1yNvqUZgRSuDxCSNR2Qccl60OsOjmLAVc2C-Kq6TYppM6RVeD8aTeJKk49H4qrzGYZrk42KQbXAzjePhho4xTofZOM6n03yaXbFrpyqNxwMyIsk4osN4Gk9jMiCbNE9ie13DijIedUvqimndoGUbpPEVpxly7d5oEeLCR4LR_Epdu-BmzVYHw5gzbfRRgGGGu5dg_h1WMJoHoxvLO5p3-NDj6gF497bIbROtyw7BdsGD1erbV7CranrVKH593gC2zJRNFuWyahP9Pt_OD9saWld21-Q_AQAA___WfRkU">