Skip to content

Commit 13b300d

Browse files
authored
riscv64: Implement near relocations (#11585)
* riscv64: Implement near relocations This is the same as #11570 but for the riscv64 backend. The intention is to support "near" relocations which don't require `Abs8` relocations for upcoming use in Wasmtime. The same design as #11570 is used here. * Review comments
1 parent 6227674 commit 13b300d

File tree

19 files changed

+457
-155
lines changed

19 files changed

+457
-155
lines changed

cranelift/codegen/src/binemit/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ pub enum Reloc {
120120
/// <https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses>
121121
RiscvGotHi20,
122122

123+
/// High 20 bits of a 32-bit PC-relative offset relocation
124+
///
125+
/// This is the `R_RISCV_PCREL_HI20` relocation from the RISC-V ELF psABI document.
126+
/// <https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses>
127+
RiscvPCRelHi20,
128+
123129
/// s390x TLS GD64 - 64-bit offset of tls_index for GD symbol in GOT
124130
S390xTlsGd64,
125131
/// s390x TLS GDCall - marker to enable optimization of TLS calls
@@ -152,6 +158,7 @@ impl fmt::Display for Reloc {
152158
Self::RiscvCallPlt => write!(f, "RiscvCallPlt"),
153159
Self::RiscvTlsGdHi20 => write!(f, "RiscvTlsGdHi20"),
154160
Self::RiscvGotHi20 => write!(f, "RiscvGotHi20"),
161+
Self::RiscvPCRelHi20 => write!(f, "RiscvPCRelHi20"),
155162
Self::RiscvPCRelLo12I => write!(f, "RiscvPCRelLo12I"),
156163
Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"),
157164
Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"),

cranelift/codegen/src/isa/riscv64/inst.isle

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,14 @@
139139
(kind IntegerCompare))
140140

141141
;; Load an inline symbol reference.
142-
(LoadExtName
142+
(LoadExtNameGot
143+
(rd WritableReg)
144+
(name BoxExternalName))
145+
(LoadExtNameNear
146+
(rd WritableReg)
147+
(name BoxExternalName)
148+
(offset i64))
149+
(LoadExtNameFar
143150
(rd WritableReg)
144151
(name BoxExternalName)
145152
(offset i64))
@@ -2750,8 +2757,43 @@
27502757

27512758

27522759
;;;; load extern name
2753-
(decl load_ext_name (ExternalName i64) Reg)
2754-
(extern constructor load_ext_name load_ext_name)
2760+
(decl load_ext_name (ExternalName i64 RelocDistance) Reg)
2761+
(rule (load_ext_name name offset _dist)
2762+
(if-let true (is_pic))
2763+
(rv_add (load_ext_name_got name) (imm $I64 (i64_cast_unsigned offset))))
2764+
(rule 1 (load_ext_name name 0 _dist)
2765+
(if-let true (is_pic))
2766+
(load_ext_name_got name))
2767+
(rule (load_ext_name name offset (RelocDistance.Near))
2768+
(if-let false (is_pic))
2769+
(load_ext_name_near name offset))
2770+
(rule (load_ext_name name offset (RelocDistance.Far))
2771+
(if-let false (is_pic))
2772+
(load_ext_name_far name offset))
2773+
2774+
(decl pure is_pic () bool)
2775+
(extern constructor is_pic is_pic)
2776+
2777+
;; Helper for emitting `MInst.LoadExtNameGot` instructions.
2778+
(decl load_ext_name_got (BoxExternalName) Reg)
2779+
(rule (load_ext_name_got extname)
2780+
(let ((dst WritableReg (temp_writable_reg $I64))
2781+
(_ Unit (emit (MInst.LoadExtNameGot dst extname))))
2782+
dst))
2783+
2784+
;; Helper for emitting `MInst.LoadExtNameNear` instructions.
2785+
(decl load_ext_name_near (BoxExternalName i64) Reg)
2786+
(rule (load_ext_name_near extname offset)
2787+
(let ((dst WritableReg (temp_writable_reg $I64))
2788+
(_ Unit (emit (MInst.LoadExtNameNear dst extname offset))))
2789+
dst))
2790+
2791+
;; Helper for emitting `MInst.LoadExtNameFar` instructions.
2792+
(decl load_ext_name_far (BoxExternalName i64) Reg)
2793+
(rule (load_ext_name_far extname offset)
2794+
(let ((dst WritableReg (temp_writable_reg $I64))
2795+
(_ Unit (emit (MInst.LoadExtNameFar dst extname offset))))
2796+
dst))
27552797

27562798
(decl elf_tls_get_addr (ExternalName) Reg)
27572799
(rule (elf_tls_get_addr name)

cranelift/codegen/src/isa/riscv64/inst/emit.rs

Lines changed: 97 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::isa::riscv64::lower::isle::generated_code::{
88
use cranelift_control::ControlPlane;
99

1010
pub struct EmitInfo {
11+
#[expect(dead_code, reason = "may want to be used in the future")]
1112
shared_flag: settings::Flags,
1213
isa_flags: super::super::riscv_settings::Flags,
1314
}
@@ -170,7 +171,9 @@ impl Inst {
170171
| Inst::ReturnCallInd { .. }
171172
| Inst::Jal { .. }
172173
| Inst::CondBr { .. }
173-
| Inst::LoadExtName { .. }
174+
| Inst::LoadExtNameGot { .. }
175+
| Inst::LoadExtNameNear { .. }
176+
| Inst::LoadExtNameFar { .. }
174177
| Inst::ElfTlsGetAddr { .. }
175178
| Inst::LoadAddr { .. }
176179
| Inst::Mov { .. }
@@ -1999,76 +2002,108 @@ impl Inst {
19992002
.emit(sink, emit_info, state);
20002003
}
20012004

2002-
&Inst::LoadExtName {
2005+
&Inst::LoadExtNameGot { rd, ref name } => {
2006+
// Load a PC-relative address into a register.
2007+
// RISC-V does this slightly differently from other arches. We emit a relocation
2008+
// with a label, instead of the symbol itself.
2009+
//
2010+
// See: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses
2011+
//
2012+
// Emit the following code:
2013+
// label:
2014+
// auipc rd, 0 # R_RISCV_GOT_HI20 (symbol_name)
2015+
// ld rd, rd, 0 # R_RISCV_PCREL_LO12_I (label)
2016+
2017+
// Create the label that is going to be published to the final binary object.
2018+
let auipc_label = sink.get_label();
2019+
sink.bind_label(auipc_label, &mut state.ctrl_plane);
2020+
2021+
// Get the current PC.
2022+
sink.add_reloc(Reloc::RiscvGotHi20, &**name, 0);
2023+
Inst::Auipc {
2024+
rd,
2025+
imm: Imm20::from_i32(0),
2026+
}
2027+
.emit_uncompressed(sink, emit_info, state, start_off);
2028+
2029+
// The `ld` here, points to the `auipc` label instead of directly to the symbol.
2030+
sink.add_reloc(Reloc::RiscvPCRelLo12I, &auipc_label, 0);
2031+
Inst::Load {
2032+
rd,
2033+
op: LoadOP::Ld,
2034+
flags: MemFlags::trusted(),
2035+
from: AMode::RegOffset(rd.to_reg(), 0),
2036+
}
2037+
.emit_uncompressed(sink, emit_info, state, start_off);
2038+
}
2039+
2040+
&Inst::LoadExtNameFar {
20032041
rd,
20042042
ref name,
20052043
offset,
20062044
} => {
2007-
if emit_info.shared_flag.is_pic() {
2008-
// Load a PC-relative address into a register.
2009-
// RISC-V does this slightly differently from other arches. We emit a relocation
2010-
// with a label, instead of the symbol itself.
2011-
//
2012-
// See: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses
2013-
//
2014-
// Emit the following code:
2015-
// label:
2016-
// auipc rd, 0 # R_RISCV_GOT_HI20 (symbol_name)
2017-
// ld rd, rd, 0 # R_RISCV_PCREL_LO12_I (label)
2018-
2019-
// Create the label that is going to be published to the final binary object.
2020-
let auipc_label = sink.get_label();
2021-
sink.bind_label(auipc_label, &mut state.ctrl_plane);
2022-
2023-
// Get the current PC.
2024-
sink.add_reloc(Reloc::RiscvGotHi20, &**name, 0);
2025-
Inst::Auipc {
2026-
rd,
2027-
imm: Imm20::from_i32(0),
2028-
}
2029-
.emit_uncompressed(sink, emit_info, state, start_off);
2045+
// In the non PIC sequence we relocate the absolute address into
2046+
// a preallocated space, load it into a register and jump over
2047+
// it.
2048+
//
2049+
// Emit the following code:
2050+
// ld rd, label_data
2051+
// j label_end
2052+
// label_data:
2053+
// <8 byte space> # ABS8
2054+
// label_end:
20302055

2031-
// The `ld` here, points to the `auipc` label instead of directly to the symbol.
2032-
sink.add_reloc(Reloc::RiscvPCRelLo12I, &auipc_label, 0);
2033-
Inst::Load {
2034-
rd,
2035-
op: LoadOP::Ld,
2036-
flags: MemFlags::trusted(),
2037-
from: AMode::RegOffset(rd.to_reg(), 0),
2038-
}
2039-
.emit_uncompressed(sink, emit_info, state, start_off);
2040-
} else {
2041-
// In the non PIC sequence we relocate the absolute address into
2042-
// a prealocatted space, load it into a register and jump over it.
2043-
//
2044-
// Emit the following code:
2045-
// ld rd, label_data
2046-
// j label_end
2047-
// label_data:
2048-
// <8 byte space> # ABS8
2049-
// label_end:
2050-
2051-
let label_data = sink.get_label();
2052-
let label_end = sink.get_label();
2053-
2054-
// Load the value from a label
2055-
Inst::Load {
2056-
rd,
2057-
op: LoadOP::Ld,
2058-
flags: MemFlags::trusted(),
2059-
from: AMode::Label(label_data),
2060-
}
2061-
.emit(sink, emit_info, state);
2056+
let label_data = sink.get_label();
2057+
let label_end = sink.get_label();
20622058

2063-
// Jump over the data
2064-
Inst::gen_jump(label_end).emit(sink, emit_info, state);
2059+
// Load the value from a label
2060+
Inst::Load {
2061+
rd,
2062+
op: LoadOP::Ld,
2063+
flags: MemFlags::trusted(),
2064+
from: AMode::Label(label_data),
2065+
}
2066+
.emit(sink, emit_info, state);
20652067

2066-
sink.bind_label(label_data, &mut state.ctrl_plane);
2067-
sink.add_reloc(Reloc::Abs8, name.as_ref(), offset);
2068-
sink.put8(0);
2068+
// Jump over the data
2069+
Inst::gen_jump(label_end).emit(sink, emit_info, state);
20692070

2070-
sink.bind_label(label_end, &mut state.ctrl_plane);
2071+
sink.bind_label(label_data, &mut state.ctrl_plane);
2072+
sink.add_reloc(Reloc::Abs8, name.as_ref(), offset);
2073+
sink.put8(0);
2074+
2075+
sink.bind_label(label_end, &mut state.ctrl_plane);
2076+
}
2077+
2078+
&Inst::LoadExtNameNear {
2079+
rd,
2080+
ref name,
2081+
offset,
2082+
} => {
2083+
// Emit the following code:
2084+
// label:
2085+
// auipc rd, 0 # R_RISCV_PCREL_HI20 (symbol_name)
2086+
// ld rd, rd, 0 # R_RISCV_PCREL_LO12_I (label)
2087+
2088+
let auipc_label = sink.get_label();
2089+
sink.bind_label(auipc_label, &mut state.ctrl_plane);
2090+
2091+
// Get the current PC.
2092+
sink.add_reloc(Reloc::RiscvPCRelHi20, &**name, offset);
2093+
Inst::Auipc {
2094+
rd,
2095+
imm: Imm20::from_i32(0),
2096+
}
2097+
.emit_uncompressed(sink, emit_info, state, start_off);
2098+
2099+
sink.add_reloc(Reloc::RiscvPCRelLo12I, &auipc_label, 0);
2100+
Inst::AluRRImm12 {
2101+
alu_op: AluOPRRI::Addi,
2102+
rd,
2103+
rs: rd.to_reg(),
2104+
imm12: Imm12::ZERO,
20712105
}
2106+
.emit_uncompressed(sink, emit_info, state, start_off);
20722107
}
20732108

20742109
&Inst::ElfTlsGetAddr { rd, ref name } => {

cranelift/codegen/src/isa/riscv64/inst/mod.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,9 @@ fn riscv64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) {
390390
collector.reg_use(rs1);
391391
collector.reg_use(rs2);
392392
}
393-
Inst::LoadExtName { rd, .. } => {
393+
Inst::LoadExtNameGot { rd, .. }
394+
| Inst::LoadExtNameNear { rd, .. }
395+
| Inst::LoadExtNameFar { rd, .. } => {
394396
collector.reg_def(rd);
395397
}
396398
Inst::ElfTlsGetAddr { rd, .. } => {
@@ -1411,13 +1413,25 @@ impl Inst {
14111413
format!("{op_name} {rd},{src},({addr})")
14121414
}
14131415
}
1414-
&MInst::LoadExtName {
1416+
&MInst::LoadExtNameGot { rd, ref name } => {
1417+
let rd = format_reg(rd.to_reg());
1418+
format!("load_ext_name_got {rd},{}", name.display(None))
1419+
}
1420+
&MInst::LoadExtNameNear {
1421+
rd,
1422+
ref name,
1423+
offset,
1424+
} => {
1425+
let rd = format_reg(rd.to_reg());
1426+
format!("load_ext_name_near {rd},{}{offset:+}", name.display(None))
1427+
}
1428+
&MInst::LoadExtNameFar {
14151429
rd,
14161430
ref name,
14171431
offset,
14181432
} => {
14191433
let rd = format_reg(rd.to_reg());
1420-
format!("load_sym {},{}{:+}", rd, name.display(None), offset)
1434+
format!("load_ext_name_far {rd},{}{offset:+}", name.display(None))
14211435
}
14221436
&Inst::ElfTlsGetAddr { rd, ref name } => {
14231437
let rd = format_reg(rd.to_reg());

cranelift/codegen/src/isa/riscv64/lower.isle

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2379,8 +2379,8 @@
23792379

23802380
;;;;; Rules for `func_addr`;;;;;;;;;
23812381
(rule
2382-
(lower (func_addr (func_ref_data _ name _)))
2383-
(load_ext_name name 0))
2382+
(lower (func_addr (func_ref_data _ name dist)))
2383+
(load_ext_name name 0 dist))
23842384

23852385
;;;;; Rules for `fcvt_to_uint`;;;;;;;;;
23862386

@@ -2538,8 +2538,8 @@
25382538

25392539
;;;;; Rules for `symbol_value`;;;;;;;;;
25402540
(rule
2541-
(lower (symbol_value (symbol_value_data name _ offset)))
2542-
(load_ext_name name offset))
2541+
(lower (symbol_value (symbol_value_data name dist offset)))
2542+
(load_ext_name name offset dist))
25432543

25442544
;;;;; Rules for `tls_value` ;;;;;;;;;;;;;;
25452545

@@ -2679,12 +2679,12 @@
26792679
output))
26802680

26812681
;; Direct call to an out-of-range function (implicitly via pointer).
2682-
(rule (lower (call (func_ref_data sig_ref name _) args))
2682+
(rule (lower (call (func_ref_data sig_ref name dist) args))
26832683
(let ((output ValueRegsVec (gen_call_output sig_ref))
26842684
(abi Sig (abi_sig sig_ref))
26852685
(uses CallArgList (gen_call_args abi args))
26862686
(defs CallRetList (gen_call_rets abi output))
2687-
(target Reg (load_ext_name name 0))
2687+
(target Reg (load_ext_name name 0 dist))
26882688
(info BoxCallIndInfo (gen_call_ind_info abi target uses defs (try_call_none)))
26892689
(_ Unit (emit_side_effect (call_ind_impl info))))
26902690
output))
@@ -2712,12 +2712,12 @@
27122712
(emit_side_effect (call_impl info))))
27132713

27142714
;; Direct call to an out-of-range function (implicitly via pointer).
2715-
(rule (lower_branch (try_call (func_ref_data sig_ref name _) args et) targets)
2715+
(rule (lower_branch (try_call (func_ref_data sig_ref name dist) args et) targets)
27162716
(let ((abi Sig (abi_sig sig_ref))
27172717
(trycall OptionTryCallInfo (try_call_info et targets))
27182718
(uses CallArgList (gen_call_args abi args))
27192719
(defs CallRetList (gen_try_call_rets abi))
2720-
(target Reg (load_ext_name name 0))
2720+
(target Reg (load_ext_name name 0 dist))
27212721
(info BoxCallIndInfo (gen_call_ind_info abi target uses defs trycall)))
27222722
(emit_side_effect (call_ind_impl info))))
27232723

@@ -2742,10 +2742,10 @@
27422742
(side_effect (return_call_impl info))))
27432743

27442744
;; Direct call to an out-of-range function (implicitly via pointer).
2745-
(rule (lower (return_call (func_ref_data sig_ref name _) args))
2745+
(rule (lower (return_call (func_ref_data sig_ref name dist) args))
27462746
(let ((abi Sig (abi_sig sig_ref))
27472747
(uses CallArgList (gen_return_call_args abi args))
2748-
(target Reg (load_ext_name name 0))
2748+
(target Reg (load_ext_name name 0 dist))
27492749
(info BoxReturnCallIndInfo (gen_return_call_ind_info abi target uses)))
27502750
(side_effect (return_call_ind_impl info))))
27512751

0 commit comments

Comments
 (0)