llvm / llvm/llvm-project

[Xtensa] Missing .literal section emission causes l32r misaligned literal target link errors

Open
#190,204 0 comments 0 reactions 0 assignees View on GitHub
backend:Xtensa
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

This issue was isolated and reduced by Claude, but I had trouble creating a solid reproducible demonstration.
I hope this is clear, and the issue is identifiable.

## Summary

The upstream LLVM Xtensa backend embeds literal pool data inline within `.text` sections instead of emitting separate `.literal` sections. When per-function sections are used (e.g. `-ffunction-sections`), functions with code sizes that are not multiples of 4 bytes cause subsequent inline literal data to land at non-4-byte-aligned offsets after the linker concatenates sections. The Xtensa `l32r` instruction requires its literal target to be 4-byte aligned, so GNU ld rejects the link with "dangerous relocation: l32r: misaligned literal target".

Espressif's LLVM fork (esp-clang 20.1.1) correctly emits separate `.literal.funcname` sections with proper 4-byte alignment. The upstream LLVM 21 backend is missing this literal section splitting.

## Reproducer

The following LLVM IR, when compiled with the upstream Xtensa backend and linked with GNU ld, triggers the error:

```llvm
; xtensa_literal_bug.ll
target datalayout = "e-m:e-p:32:32-i64:64-i128:128-n32"
target triple = "xtensa-none-elf"

define i32 @func_a(i32 %x) #0 {
%1 = alloca i32, align 4
store i32 %x, ptr %1, align 4
%2 = load i32, ptr %1, align 4
%3 = icmp sgt i32 %2, 0
br i1 %3, label %then, label %else
then:
%4 = load i32, ptr %1, align 4
%5 = add i32 %4, 1
ret i32 %5
else:
%6 = load i32, ptr %1, align 4
%7 = sub i32 %6, 1
ret i32 %7
}

define i32 @func_b() #0 {
%fp = alloca ptr, align 4
store ptr @func_a, ptr %fp, align 4
%1 = load ptr, ptr %fp, align 4
%2 = call i32 %1(i32 42)
ret i32 %2
}

attributes #0 = { "frame-pointer"="all" "target-cpu"="generic" "target-features"="+density,+threadptr,+windowed,+mul16,+mul32,+mul32high,+div32,+sext,+nsa,+clamps,+minmax,+bool,+fp,+loop" }
```

```bash
# Compile IR to object (using llc from LLVM 21):
llc -mtriple=xtensa-none-elf -function-sections -o xtensa_literal_bug.o xtensa_literal_bug.ll

# Inspect: no .literal sections, only .text sections
readelf -S xtensa_literal_bug.o | grep -E 'text\.|literal'
# .text.func_a PROGBITS size=0x26 (38 bytes -- NOT 4-byte aligned)
# .text.func_b PROGBITS size=0x22 (34 bytes)
# No .literal.func_a or .literal.func_b sections

# Link:
xtensa-esp32s3-elf-gcc -nostdlib -e func_b xtensa_literal_bug.o -o test
# Error: dangerous relocation: l32r: misaligned literal target: (.text.func_a+0x22)
```

### Verified output

Object compiled from the above IR via LDC (which uses stock LLVM 21.1.8):

```
$ readelf -S repro.o | grep -E 'text\.|literal'
[ 3] .text.func_a PROGBITS 00000000 000034 000026 00 AX 0 0 4
[ 5] .text.func_b PROGBITS 00000000 00005c 000022 00 AX 0 0 4
# Note: NO .literal sections at all

$ xtensa-esp32s3-elf-gcc -nostdlib -e func_b repro.o -o test
(.text.func_b+0x6): dangerous relocation: l32r: misaligned literal target: (.text.func_a+0x22)
```

### Comparison with Espressif's fork

The same C equivalent compiled with esp-clang (Espressif LLVM 20.1.1):

```
$ readelf -S espclang.o | grep -E 'text\.|literal'
[ 3] .text.func_a PROGBITS 00000000 000034 00000b 00 AX 0 0 4
[ 4] .literal.func_b PROGBITS 00000000 000040 000004 00 AX 0 0 4
[ 6] .text.func_b PROGBITS 00000000 000044 000020 00 AX 0 0 4
# .literal.func_b is emitted separately with 4-byte alignment

$ xtensa-esp32s3-elf-gcc -nostdlib -e func_b espclang.o -o test
# Links successfully
```

## Root Cause

On Xtensa, the `l32r` instruction loads a 32-bit literal from a 4-byte-aligned address within a 256KB range before the instruction. GCC and Espressif's LLVM fork handle this by emitting literal pool entries in separate `.literal` or `.literal.funcname` sections. The Xtensa linker script convention places `.literal.funcname` immediately before `.text.funcname`, ensuring proper alignment.

The upstream LLVM Xtensa backend does not emit separate `.literal` sections. Literal pool data is embedded inline at the beginning of each `.text` function section. When the linker concatenates `.text.func_a` (38 bytes, 0x26) followed by the literal data for `func_b` (embedded at the end of `.text.func_a`), the literal data lands at offset 0x22 (34 bytes from func_a's start, within func_a's 38-byte section). Since 0x22 % 4 = 2, the `l32r` target is not 4-byte aligned.

## Expected Behavior

The LLVM Xtensa backend should emit `.literal` (or `.literal.funcname` with `-function-sections`) sections for literal pool entries, matching Espressif's fork and GCC behavior. The Espressif fork's implementation can be found in their `llvm-project` repository.

## Environment

- LLVM: 21.1.8 (stock upstream, via LDC 1.42.0) -- affected
- Espressif LLVM 20.1.1 (esp-clang/llc) -- NOT affected (emits .literal sections)
- GCC xtensa-esp-elf 15.2.0 -- NOT affected (emits .literal sections)
- Linker: GNU ld (binutils) 2.45 from xtensa-esp-elf toolchain (esp-15.2.0)

The IR was generated by LDC 1.42.0 and verified to produce the link failure. The same IR compiled with Espressif's `llc` (which has the .literal section emission patch) links successfully. To reproduce from the IR directly, use upstream `llc` from LLVM 21+ with `-mtriple=xtensa-none-elf -function-sections`.

## Workaround

No known workaround for the upstream backend. `--align-all-functions=2` does not fix this as it only affects function entry alignment, not inline literal pool placement.

Contributor guide

Open the contributing guide

Research direction

Start by running the provided llc command on xtensa_literal_bug.ll, then inspect the generated sections with readelf and reproduce the GNU ld failure. Compare the upstream Xtensa backend's literal-pool emission with the Espressif fork; done means separate, aligned .literal sections are emitted under -function-sections and the reproducer links successfully.

Written by the indexing model from the issue text.

Assessment

Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.