This series adds a position-independent large code model to GCC for AArch64
and RISCV64, so shared libraries and position-independent executables can place
code, data and the GOT arbitrarily far apart. Upstream GCC rejects
-mcmodel=large together with -fpic, -fPIC, -fpie or -fPIE on both
architectures.
The code sequences and relocations are the ones LLVM 23.1.1 uses for the same model (the companion LLVM series), so objects from GCC and Clang can be mixed in one link, and the result links with ld.bfd, ld.gold and lld.
Neither model is part of a published ABI. AAELF64 defines no large PIC code model, and the RISCV psABI currently forbids the large code model with PIC. Both are extensions that GCC 16.2.0, LLVM 23.1.1 and Binutils 2.46.1 implement the same way.
| Patch | Area | Summary |
|---|---|---|
| 0001 | AArch64 | Large PIC code model: PC-relative (MOVW_PREL_G*) and GOT (MOVW_GOTOFF_G*) addressing, GOT base computed once per function |
| 0002 | AArch64 | Initial-exec TLS through TLSIE_MOVW_GOTTPREL_G1/_G0_NC; general-/local-dynamic TLS rejected with sorry |
| 0003 | AArch64 | Accept -mcmodel=large with PIC on ELF; invoke.texi |
| 0004 | AArch64 | Tests gcc.target/aarch64/large-pic-{1..8}.c |
| 0005 | RISCV | Large PIC model: self-relative literal-pool entries plus .data.rel.ro slots |
| 0006 | RISCV | Accept -mcmodel=large with PIC on RISCV64; invoke.texi |
| 0007 | RISCV | Tests gcc.target/riscv/large-pic-{1..4}.c |
| 0008 | RISCV | 8-byte PC-relative EH pointers (FDE, personality, LSDA) in the large model, PIC and non-PIC |
| 0009 | libgcc | Binary search of DW_EH_PE_datarel|sdata8 .eh_frame_hdr tables |
Apply in order from the top of a GCC 16.2.0 tree:
for p in 00*.patch; do patch -p1 < $p; doneThe patches apply to the pristine gcc-16.2.0.tar.xz with no offsets or fuzz.
The Fedora 44 patch files touch none of the same files.
| Component | AArch64 | RISCV64 |
|---|---|---|
| Assembler | GNU as with the binutils series (:gotoff_gN: operators), or LLVM's integrated assembler |
Any; no new relocations |
| ld.bfd | Binutils series (MOVW_GOTOFF_G*, MOV[NZ]/MOVK handling) |
Binutils series patch 0005 for 8-byte .eh_frame fields; patch 0004 for code or .eh_frame more than 2 GiB from .eh_frame_hdr |
| ld.gold | Binutils series patch 0003 | No RISCV port |
| lld | LLVM series patch 0002 (MOVW_GOTOFF_G*, MOVW_GOTTPREL) |
Stock |
| Unwinder | Stock | libgcc with patch 0009 (or LLVM libunwind) to binary search 64-bit .eh_frame_hdr tables; older libgcc falls back to a linear scan |
Patch 03 changes initialize_aarch64_code_model. When -mcmodel=large is
combined with any PIC or PIE flag on ELF, it selects the new
AARCH64_CMODEL_LARGE_PIC (aarch64-opts.h) instead of reporting sorry.
PE-COFF keeps the sorry, and ILP32 is still rejected. The preprocessor
defines __AARCH64_CMODEL_LARGE__, as for the non-PIC large model.
AAELF64 has no relocation that reaches the GOT with more than ±4 GiB of range.
The GOT base is therefore computed PC-relatively, from an ADR plus a 64-bit
displacement built in four R_AARCH64_MOVW_PREL_G* chunks. This mirrors x86-64
large PIC, which uses leaq .L$pb(%rip) plus movabsq:
.Lpc:
adr xD, .Lpc
movz tmp, #:prel_g3:_GLOBAL_OFFSET_TABLE_+4
movk tmp, #:prel_g2_nc:_GLOBAL_OFFSET_TABLE_+8
movk tmp, #:prel_g1_nc:_GLOBAL_OFFSET_TABLE_+12
movk tmp, #:prel_g0_nc:_GLOBAL_OFFSET_TABLE_+16
add xD, xD, tmpMOVW_PREL_G* resolves as S + A - P, where P is the address of the
instruction being relocated. Each chunk therefore carries an addend equal to
its own distance from the ADR (+4, +8, +12, +16), so every chunk is a slice
of the same value, _GLOBAL_OFFSET_TABLE_ - .Lpc.
Implementation:
- The six instructions are a single insn,
aarch64_load_large_pic_prel(aarch64.md), with amatch_scratchfortmp. They cannot be split, because the addends depend on their relative positions.aarch64_output_large_pic_prelprints the sequence. - The GOT base lives in the pseudo PIC register (
TARGET_USE_PSEUDO_PIC_REGnow returns true for this model).aarch64_init_pic_reg(TARGET_INIT_PIC_REG) computes it once per function on the entry edge, and only if the function uses it, as i386 does for its large PIC model. - The pseudo register exists only before reload, so every large-PIC expansion
path asserts
can_create_pseudo_p (). aarch64_large_pic_got_basereturns a throwaway register when IVOPTs expands addresses for costing before RTL expansion (pic_offset_table_rtxis stillNULL).
aarch64_classify_symbol classifies every symbol in this model, and
aarch64_load_symref_appropriately expands each class:
| Symbol | Class | Sequence |
|---|---|---|
| Preemptible (does not bind locally) | SYMBOL_LARGE_PIC_GOT |
movz tmp, #:gotoff_g3:sym / movk #:gotoff_g2_nc: / movk #:gotoff_g1_nc: / movk #:gotoff_g0_nc: / ldr xD, [gp, tmp] |
| Binds locally, labels, jump tables, string constants | SYMBOL_LARGE_PIC_PREL |
The six-instruction ADR + MOVW_PREL sequence against the symbol; its displacement also absorbs any constant offset |
| Constant-pool entries | SYMBOL_SMALL_ABSOLUTE (or TINY with PC-relative literal loads) |
Per-function literal pool in the function's own section, as in the non-PIC large model |
- The GOT offset pattern is
aarch64_large_pic_movw_gotoff. The offset is built top chunk first, withMOVZcarrying:gotoff_g3:. aarch64_can_use_per_function_literal_pools_preturns true for the model. Symbolic constants are never forced into a pool, so pools carry no relocations.- Calls are unchanged:
BL/BwithR_AARCH64_CALL26, which the linker extends with veneers or the PLT. aarch64_rtx_costscharges four extra instructions for a symbol reference, so the optimizers see the real cost.- Exception tables already use 8-byte PC-relative encodings in the AArch64
large model (
R_AARCH64_PREL64for personality and LSDA).
| Model | Handling |
|---|---|
| Local-exec | Unchanged. The TPREL sequence is relative to the thread pointer and never uses ADRP. |
| Initial-exec | SYMBOL_LARGE_PIC_TLSIE: movz tmp, #:gottprel_g1:sym / movk #:gottprel_g0_nc:sym / ldr tmp, [gp, tmp] / mrs tp, tpidr_el0 / add xD, tp, tmp (pattern aarch64_large_pic_movw_gottprel) |
| General-/local-dynamic | sorry from aarch64_check_large_pic_tls, with a note suggesting -ftls-model=initial-exec or local-exec. TLSDESC and __tls_get_addr have only ADRP-based sequences, which would silently bring back the ±4 GiB limit. |
AAELF64 defines only two GOTTPREL MOVW chunks, so the GOT itself is limited
to 4 GiB, but the image is not. Linkers must not relax this pair from IE to
LE: the LDR that consumes the offset has no relocation and cannot be
rewritten.
R_AARCH64_MOVW_PREL_G0_NC, _G1_NC, _G2_NC, _G3;
R_AARCH64_MOVW_GOTOFF_G0_NC, _G1_NC, _G2_NC, _G3;
R_AARCH64_TLSIE_MOVW_GOTTPREL_G1, _G0_NC; R_AARCH64_PREL64 (EH data);
R_AARCH64_CALL26. _GLOBAL_OFFSET_TABLE_ must be the start of .got, which
holds for ld.bfd and lld. gold biases the symbol by 0x8000 for large GOTs, but
computes GOT offsets from the same biased symbol, so the sequence still works.
gcc.target/aarch64/large-pic-1.c … -8.c check the GOT base sequence and
its addends, local/hidden/string/call-target PC-relative access, a single GOT
base per function, GOT access for preemptible symbols, IE and LE TLS, the
GD/LD sorry, the predefined macro, jump tables without ADRP, and the GOT
and PC-relative forms again at -O0.
The RISCV large model loads addresses from a per-function literal pool in the
function's section, within auipc range of the code. Under PIC, those
absolute addresses would need dynamic relocations, which cannot live in
.text. Moving them to a writable section breaks the range guarantee.
The pool is split in two: the part that needs a dynamic relocation, and the part that must be near the code. A link-time constant connects them, and no new relocation types are needed.
Symbol that binds locally: the pool entry holds the displacement from the
entry itself to the symbol, an R_RISCV_ADD64/R_RISCV_SUB64 pair that the
static linker resolves.
lla a0, .LC0 # a0 = &entry
ld t0, 0(a0) # t0 = sym - &entry
add a0, a0, t0 # a0 = sym
.LC0:
.dword sym-.Preemptible symbol: its address lives in a slot in .data.rel.ro, which
takes the dynamic relocation (R_RISCV_64, or R_RISCV_RELATIVE for hidden
symbols). The pool entry holds the displacement to the slot.
lla a0, .LC1
ld t0, 0(a0)
add a0, a0, t0 # a0 = &slot
ld a0, 0(a0) # a0 = sym
.LC1:
.dword .LC2-.
.section .data.rel.ro
.LC2:
.dword symImplementation in gcc/config/riscv/:
riscv_large_pic_p ()is true forCM_LARGEwithflag_picand gates everything else. Under PIC,riscv_cmodelstaysCM_LARGEinstead of becomingCM_PIC(patch 06).riscv_classify_symbolreturnsSYMBOL_FORCE_TO_MEMfor symbols andSYMBOL_PCRELfor labels and constant-pool entries.riscv_legitimize_large_pic_movebuilds the pool constants. Each is a(const (unspec [...] UNSPEC_LARGE_PIC_DISP)), wrapping a(const (unspec [...] UNSPEC_LARGE_PIC_SLOT))for preemptible symbols. It emitslarge_pic_load_addressorlarge_pic_load_got(riscv.md) with a clobbered scratch register.riscv_output_addr_const_extra(TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA) prints the displacement assym-.and the slot assym.- GCC's constant pool picks a section per entry.
riscv_elf_select_rtx_sectionsends slot entries to the default relocatable read-only section (.data.rel.ro) and keeps displacement entries in the function's section. riscv_cannot_force_const_memrejects the raw symbolic constants, so an absolute address never reaches the per-function pool.riscv_const_insnsreturns 0 for them, so they are never rematerialized after reload.- Thunks are the only code that materializes addresses after reload. They use the prologue temporaries (t0, or t2 when the destination is t0).
- Call targets use the same sequences, since the large model already calls
through a register. Calls to preemptible functions go through the
.data.rel.roslot, so interposition works without a PLT. - TLS keeps the usual GOT-relative PC-relative sequences, which limit the distance between code and GOT to ±2 GiB, as in LLVM.
riscv_option_override no longer reports sorry for -mcmodel=large with
PIC. invoke.texi documents the model and notes that it is outside the psABI.
gcc.target/riscv/large-pic-1.c … -4.c check the local displacement
entry, the .data.rel.ro slot for preemptible data and functions, the
absence of la, GOT and PLT references, and the __riscv_cmodel_large
predefine.
The large model makes no assumption about the distance between code and
.eh_frame, but RISCV always used 4-byte PC-relative EH pointers. This was
worse than a range limit: with code more than 2 GiB away, lld rejects
R_RISCV_32_PCREL, while ld.bfd links without a diagnostic and produces FDEs
that decode to the wrong address, so unwinding fails at run time.
ASM_PREFERRED_EH_DATA_FORMAT(riscv.h) selectsDW_EH_PE_sdata8forCM_LARGE, PIC and non-PIC. There is no 64-bit PC-relative data relocation, so the pointers are emitted asADD64/SUB64pairs, which survive linker relaxation. medlow and medany are unchanged.riscv_option_overridedefaultsflag_dwarf2_cfi_asmto 0 forCM_LARGE, so dwarf2out writes.eh_frameitself: CIE augmentationzR/zPLRwith FDE encoding 0x1c, personality 0x9c and LSDA 0x1c. When gas builds.eh_framefrom.cfi_*directives, it always encodes an FDE's initial location as pcrel|sdata4, whatever the compiler prefers. An explicit-fdwarf2-cfi-asmis honoured, and gives 4-byte FDE pointers.- Tests:
gcc.target/riscv/large-model-eh-1.c(non-PIC) and-2.c(PIC).
The directive default was added while porting to 16.2.0. The 16.0.1 series
originally had only the encoding change, which left FDE pointers 4-byte
whenever GCC used CFI directives, as it does with any assembler that supports
them. That version failed its own tests and failed to unwind with code 3 GiB
from .eh_frame.
.text: R_RISCV_PCREL_HI20/_LO12_I (lla), R_RISCV_ADD64/SUB64
(pool entries). .data.rel.ro: R_RISCV_64. .eh_frame:
R_RISCV_ADD64/SUB64. The code section needs no dynamic relocations.
When code or .eh_frame is more than 2 GiB from .eh_frame_hdr, linkers
write the binary search table with DW_EH_PE_datarel|DW_EH_PE_sdata8 entries
(encoding 0x3c) and eh_frame_ptr as 8 bytes (0x1c). lld does this, and so
does ld.bfd with binutils patch 04. The first version of that patch widened
only the table and silently truncated eh_frame_ptr when .eh_frame itself
was far away, which made libgcc without this patch segfault in its linear
scan; it was fixed on 2026-09-17. _Unwind_Find_FDE
in unwind-dw2-fde-dip.c only binary searched sdata4 tables, and fell back
to a linear scan of .eh_frame on every lookup for anything else.
find_fde_tail now dispatches 0x3c tables on 64-bit targets to
find_fde_sdata8_table. That function binary searches the 16-byte entries,
reading them with memcpy because the table follows a 4-byte count and is
not necessarily 8-byte aligned. It then checks the PC against the FDE's range
(fde_range). Tables with 32-bit entries take the existing path. The change
is not specific to the large model: it helps any binary whose linker emits a
64-bit table.
The companion LLVM (llvm-project-mcmodel-large, patches 0001 - 0004 on
LLVM 23.1.1) implements the same model:
| Aspect | GCC | Clang/LLVM | Compatible |
|---|---|---|---|
| GOT base | ADR + MOVW_PREL_G3..G0_NC with addends +4…+16 |
Same, scratch x17 |
Yes, identical relocations |
| GOT offset | Top chunk first: movz #:gotoff_g3:, then movk down to :gotoff_g0_nc: |
Low chunk first: movz #:gotoff_g0_nc: … movk #:gotoff_g3: |
Yes, with conditions (see below) |
| Local symbols | PC-relative sequence against the symbol | Same | Yes |
| IE TLS | MOVW_GOTTPREL_G1/_G0_NC indexing the GOT base |
Same (MOVN placeholder under :gottprel_g1:) |
Yes, linker picks MOVZ/MOVN by sign |
| GD/LD TLS | sorry |
Error | Neither supports it |
| Pointer authentication (signed GOT) | Not supported | Diagnosed | Neither supports it |
| Assembler operators | :gotoff_g0: … :gotoff_g3: (binutils series) |
Same spellings (LLVM 0001) | GNU as and llvm-mc emit identical relocations for the same source |
Two details make mixed objects work, and any future specification needs to pin them down:
- A MOVK stays a MOVK.
MOVW_GOTOFF_G0..G3andTLSIE_MOVW_GOTTPREL_G1are MOV[NZ]-class relocations: the linker chooses MOVZ or MOVN from the sign of the value. Clang putsGOTOFF_G3on a MOVK. lld, ld.bfd (binutils 01) and ld.gold (binutils 03) all choose by sign unless the instruction is already a MOVK, in which case they patch only the immediate. GCC's top-chunk-first order works under either rule. :gotoff_g3:is accepted on MOVK by GNU as (binutils 02), so textualclang -S/llcoutput assembles with gas. Other MOV[NZ]-class operators stay MOVZ/MOVN-only. Don't move GCC's:gotoff_g3:or other MOV[NZ] operators onto different instructions without checking that gas accepts them.
All three linkers leave the MOVW GOTTPREL pair unrelaxed.
LLVM's RISCV large PIC model is a prototype behind the hidden option
-riscv-large-pic (LLVM series 0002 - 0004). By default the driver rejects
-mcmodel=large with PIC (LLVM 0001), so Clang objects are built with
clang -emit-llvm and then
llc -code-model=large -relocation-model=pic -riscv-large-pic.
| Aspect | GCC | LLVM prototype | Compatible |
|---|---|---|---|
| Local symbols | Per-use pool entry .dword sym-. |
Per-function anchor in .text to a table |
Link-level: both use only ADD64/SUB64 in .text |
| Preemptible symbols | Per-symbol slot in .data.rel.ro, reached through a pool displacement |
Per-function table .Lrvlp_tbl.<fn> in .data.rel.ro, reached through one anchor (auipc/addi, ld, add, then ld per symbol) |
Link-level: both use R_RISCV_64 in .data.rel.ro |
| New relocations | None | None | Yes |
| TLS | GOT-relative, ±2 GiB | Same | Yes |
| EH pointers | pcrel|sdata8 (0x1c/0x9c) as ADD64/SUB64 |
Same (LLVM mcmodel-large-eh-riscv patch; llvm-mc uses sdata8 FDEs only with -large-code-model) |
Yes |
| Jump tables | In the function's section, reached with lla (upstream: JUMP_TABLES_IN_TEXT_SECTION for CM_LARGE) |
Same since LLVM 98677af3cb50 (mcmodel-large-jt-riscv); before it, in .rodata behind a ±2 GiB auipc, which fails to link with data more than 2 GiB away |
Yes, with that commit |
The code sequences differ, but compatibility is a link-level property: no
relocations or conventions cross function boundaries beyond the standard
calling convention, and interposition through .data.rel.ro works with
objects from either compiler.
| Linker | AArch64 | RISCV64 |
|---|---|---|
| lld (LLVM) | MOVW_GOTOFF_G*, MOVW_GOTTPREL pair unrelaxed; writes 64-bit .eh_frame_hdr tables |
Stock lld; writes 64-bit .eh_frame_hdr tables |
| ld.bfd (Binutils) | 01: MOVW_GOTOFF_G*, MOV[NZ]/MOVK rule; 04: 64-bit .eh_frame_hdr (8-byte eh_frame_ptr and table) |
04: 64-bit .eh_frame_hdr (8-byte eh_frame_ptr and table); 05: fixes 8-byte .eh_frame fields after CIE merging (before it, exceptions silently called std::terminate) |
| ld.gold (Binutils) | 03: MOVW_PREL_G*, MOVW_GOTOFF_G*, TLSIE_MOVW_GOTTPREL_* |
No RISCV port |
- AArch64: no general-/local-dynamic TLS, no signed GOT; the GOT is limited to 4 GiB for initial-exec TLS.
- PLT stubs, and lld's AArch64 PIE range-extension thunks, are ADRP-based, so in dynamically linked programs, calls through the PLT still need the PLT within 4 GiB of the caller. This is a linker property, not code generation.
- RISCV TLS is limited to ±2 GiB between code and GOT.
- AArch64 and x86-64 large models emit
.cfi_*directives, so their FDE initial locations stay 4-byte. This is pre-existing upstream behavior, not changed here. LLVM's integrated assembler uses 8-byte ones for its large model, so on AArch64 only Clang objects built by it can place code more than 2 GiB from.eh_frame; mixed objects otherwise link and unwind normally. .eh_framesection flags are not a limit. A correctly configured GCC emits.eh_frameas"a", PIC or not, because every EH pointer here is PC-relative, andllvm-mcassembles the output (checked on GCC 16.2.0 C++ output for both targets). An earlier version of this README said GCC emits"aw"under-fPICand that LLVM's assembler then rejects the output; that came from the original 16.0.1 test compilers.EH_TABLES_CAN_BE_READ_ONLYdefaults to the result of a configure-time linker check (HAVE_LD_RO_RW_SECTION_MIXING, which needs the assembler, linker and objdump), and only when that check fails is.eh_framealways"aw". Seeing"aw"means GCC was configured without working binutils.
Cross compilers for aarch64-linux-gnu and riscv64-linux-gnu were built
from the pristine tarball (baseline) and from the patched tree. They were
tested with the binutils series, the patched clang/lld 23.1.1, glibc sysroots
and qemu-user. All four builds' configure found working binutils:
gcc_cv_as_cfi_directive=yes and gcc_cv_ld_ro_rw_mix=read-write in
gcc/config.log. Check those before trusting results from any GCC build: one
whose probes failed emits .eh_frame as "aw" and probably no .cfi_*
directives, which hides bugs such as the original patch 08's.
| Check | Result |
|---|---|
| New tests, DejaGnu directives emulated (RISCV counted per torture option) | AArch64 8/8, RISCV 29/29; all fail with the baseline |
Existing large-model.c, jump-table-large-code-model.c |
Pass |
| Differential compile, baseline vs patched, same flags | 4140 gcc.target/aarch64, 6539 gcc.target/riscv, about 1615 gcc.c-torture/compile per architecture: assembly identical apart from debug-info paths and LTO bytecode, except the three RISCV -mcmodel=large tests, which change as intended by patch 08 |
Stress: gcc.dg + gcc.c-torture/{compile,execute} with -mcmodel=large -fPIC, -O2/-O0 |
22,240 compilations per architecture, no ICEs. New failures: only the intended GD/LD sorry (AArch64) and gcc.dg/pr119493-2.c (RISCV musttail), which fails identically in the baseline non-PIC large model |
Runtime ABI matrix: -fPIC DSO + -fPIE executable, {GCC, Clang} × {GCC, Clang}, -O2/-O0; interposition, weak undefined, jump tables, computed goto, FP constants, large offsets, IE/LE TLS |
AArch64 24/24 (ld.bfd, ld.gold, lld); RISCV 16/16 (ld.bfd, lld) |
| C++ exceptions through destructors and callbacks across DSO and PIE | AArch64 6/6, RISCV 4/4 |
RISCV: library .text 3 GiB above .eh_frame |
Exceptions caught with lld and ld.bfd; with -fdwarf2-cfi-asm, lld rejects the link and ld.bfd output calls std::terminate |
AArch64 far data layout: freestanding static programs with all data and the GOT 5 GiB above .text, same checks as the RISCV row below |
Large PIC links and runs at -O2 and -O0 with ld.bfd, ld.gold and lld for every {GCC, Clang} × {GCC, Clang} object combination (24/24); the small model fails to link with all three. .text relocations are only MOVW_PREL_G*, MOVW_GOTOFF_G* and CALL26 |
AArch64, Clang textual output through gas: Clang objects built with clang -fno-addrsig -S (output contains movk xN, #:gotoff_g3:sym) and assembled by gas |
Runtime ABI matrix 24/24 and 5 GiB far layout 24/24, both with ld.bfd, ld.gold and lld. Compared with integrated-assembler objects, the code differs only in the MOVZ/MOVN placeholder under :gottprel_g1:, which linkers rewrite by sign. gas gives FDE initial locations PREL32 (from .cfi_*), where the integrated assembler uses PREL64 |
libgcc 64-bit .eh_frame_hdr search (patch 09): a large-PIC DSO with 200 functions 3 GiB above .eh_frame_hdr (RISCV: code 3 GiB above .eh_frame too; AArch64: .eh_frame kept with the code, since its gas-built FDEs are 4-byte), each looked up with _Unwind_Find_FDE |
Linkers write a 0x3c table. Intact table: 200/200 correct, distinct FDEs with patched and baseline libgcc_s. Every table entry's FDE redirected to the first: patched libgcc_s fails 199/200 (it uses the binary search), baseline still succeeds by linear scan. RISCV and AArch64, ld.bfd and lld. (AArch64 with ld.bfd first showed baseline libgcc_s segfaulting, because binutils patch 04 then wrote a truncated 4-byte eh_frame_ptr with .eh_frame 3 GiB from the header; patch 04 now writes 8 bytes, as lld does) |
RISCV far data layout: freestanding static programs with all data, .data.rel.ro slots and constants 3 GiB above .text; preemptible and hidden globals, statics, a large array, FP constants, strings, local and extern function pointers, jump table, computed goto, writes |
Large PIC links and runs with ld.bfd and lld at -O2 and -O0 whenever the objects are GCC-only, or GCC mixed with Clang -riscv-large-pic objects (10/10; the 2 remaining mixed links used a Clang -O0 object whose jump table was out of range, a gap in LLVM's RISCV large model since fixed by LLVM commit 98677af3cb50, after which all {GCC, Clang} × {GCC, Clang} links pass, 16/16); medany fails to link with both linkers. GCC .text relocations are only PCREL_HI20/LO12 to its own literal pool and ADD64/SUB64 displacements |
The same tests at -O1 and -O3 |
Stress (22,240 compilations per architecture): no ICEs; new failures only the GD/LD sorry (AArch64, 34 files) and pr119493-2.c (RISCV musttail, identical in the baseline non-PIC large model). Runtime ABI matrix AArch64 24/24 (also 24/24 with Clang textual output through gas), RISCV 16/16; C++ exceptions (GCC objects only) AArch64 6/6, RISCV 4/4; the RISCV far EH layout and the exception tests with Clang objects (from the original 16.0.1 testing) were not repeated. Far data: AArch64 5 GiB 24/24 (integrated assembler and gas), RISCV 3 GiB 16/16; jump-table switch GCC 8/8 and Clang 8/8 with LLVM 98677af3cb50 (0/8 without); controls fail to link. .eh_frame_hdr search: unchanged, patched libgcc uses the 64-bit table on all four target/linker combinations. New tests: RISCV torture runs at -O1/-O3 -g 12/12; AArch64 with the level appended 7/8, the exception being large-pic-6.c, the deliberate -O0 variant, whose never-written static int folds away (no ADRP in the remaining code) |
| Build | No new warnings in the patched files |
Upstream changes between 16.0.1 and 16.2.0 that could have interacted with the series were reviewed and needed no change:
- RISCV t1 was removed from
SIBCALL_REGS; the thunk path uses t0/t2. - The RISCV stack-protector patterns now use
riscv_output_move. - AArch64 has a new
narrow_gp_writespass, which cannot narrow the large-PIC UNSPEC/MEM loads.
See the README.MCMODEL-LARGE for more details.
| Series | Location |
|---|---|
| Binutils 2.46.1 | GNU Binutils 2.46.1: (0001 bfd/ld GOTOFF relocations, 0002 gas operators, 0003 gold, 0004 64-bit .eh_frame_hdr with 8-byte eh_frame_ptr, 0005 RISCV .eh_frame fix.) |
| LLVM AArch64 | LLVM 23.1.1: (MC operators, lld relocations, codegen, driver.) |
| LLVM RISCV | LLVM 23.1.1: (prototype behind -riscv-large-pic; DESIGN.md), mcmodel-large-eh-riscv (8-byte EH encodings) and mcmodel-large-jt-riscv (jump tables in the function's section under the large model) |
| GCC 16.2.0 | GCC 16.2.0 contains the patch 0008 fix. |
- Tested on RISCV64 with Fedora 42 RISCV64 Server running in an
x86_64Fedora 41 KVM with the system-providedqemu. - Tested on AArch64 with Fedora 44 AArch64 Server running in an
x86_64Fedora 41 KVM with the system-providedqemu.
Needless to mention, testing this way is extremely slow. I do not have access to real hardware for these two ISA's of interest, so my testing capabilities are limited.
For fun, research, learning, and in the hope that someone might find it useful or interesting.