Skip to content

Commit

Permalink
x64: lower LoadExtName to a RIP-relative LEA (bytecodealliance#6402)
Browse files Browse the repository at this point in the history
This change intends to add a new lowering for `MachInst::LoadExtName`
(the pseudo-instruction behind CLIF's `func_addr` and `symbol_addr`)
that uses RIP-relative addressing when the name is local to the module
(`colocated`). The existing behavior is retained, which currently loads
from the global offset table when the `is_pic` flag is set (a
`X86GOTPCRel4` relocation) or loads a relocated immediate (an `Abs8`
relocation). After this change, a `lea` instruction will calculate a
RIP-relative address if the name in question is marked with CLIF's
`colocated` flag.

Why is this necessary? Though some users of Cranelift, like
`cranelift-jit`, understand how to relocate the existing `Abs8`
relocations (see [`CompiledBlob::perform_relocations`]), Wasmtime does
not. Though technically Wasmtime does apply relocations (see
[`CodeMemory::apply_relocations`]), these are only for libcalls.
Wasmtime will attempt to resolve module-local functions, the ones
targeted by `func_addr`, via some internal relocation patching in
[`ModuleTextBuilder::append_func`]. When it realizes it cannot patch one
of these functions, it panics because [`LabelUse::from_reloc`] only
understands the `X86CallPCRel4` relocation. This prevents the use of
`func_addr` when translating Wasm code. Since no current Wasmtime
translation uses `func_addr` directly, this change is not technically
required, but it does feel like the right option to add, normalizing
`func_addr`'s behavior for local functions to `MachInst::CallKnown`'s
behavior and making it more useful for other Cranelift users in the
future.

[`CompiledBlob::perform_relocations`]: https://github.com/bytecodealliance/wasmtime/blob/752c7ea4dd83f9cb247e00cadf9d1958c0bdf9b6/cranelift/jit/src/compiled_blob.rs#L48
[`CodeMemory::apply_relocations`]: https://github.com/bytecodealliance/wasmtime/blob/752c7ea4dd83f9cb247e00cadf9d1958c0bdf9b6/crates/jit/src/code_memory.rs#L269
[`ModuleTextBuilder::append_func`]: https://github.com/bytecodealliance/wasmtime/blob/752c7ea4dd83f9cb247e00cadf9d1958c0bdf9b6/crates/cranelift-shared/src/obj.rs#L145
[`LabelUse::from_reloc`]: https://github.com/bytecodealliance/wasmtime/blob/752c7ea4dd83f9cb247e00cadf9d1958c0bdf9b6/cranelift/codegen/src/isa/x64/inst/mod.rs#L2759
  • Loading branch information
abrown authored May 17, 2023
1 parent c1d0508 commit 8756fcf
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 9 deletions.
2 changes: 2 additions & 0 deletions cranelift/codegen/src/isa/x64/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@ impl ABIMachineSpec for X64ABIMachineSpec {
dst: tmp,
name: Box::new(name.clone()),
offset: 0,
distance: RelocDistance::Far,
});
insts.push(Inst::call_unknown(
RegMem::reg(tmp.to_reg()),
Expand Down Expand Up @@ -647,6 +648,7 @@ impl ABIMachineSpec for X64ABIMachineSpec {
dst: temp2,
name: Box::new(ExternalName::LibCall(LibCall::Memcpy)),
offset: 0,
distance: RelocDistance::Far,
});
insts.push(Inst::call_unknown(
RegMem::reg(temp2.to_reg()),
Expand Down
10 changes: 6 additions & 4 deletions cranelift/codegen/src/isa/x64/inst.isle
Original file line number Diff line number Diff line change
Expand Up @@ -551,10 +551,12 @@
;; Loads an external symbol in a register, with a relocation:
;;
;; movq $name@GOTPCREL(%rip), dst if PIC is enabled, or
;; lea $name($rip), dst if distance is near, or
;; movabsq $name, dst otherwise.
(LoadExtName (dst WritableReg)
(name BoxExternalName)
(offset i64))
(offset i64)
(distance RelocDistance))

;; =========================================
;; Instructions pertaining to atomic memory accesses.
Expand Down Expand Up @@ -1978,10 +1980,10 @@
;;;; Helpers for Emitting Loads ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Helper for constructing a LoadExtName instruction.
(decl load_ext_name (ExternalName i64) Reg)
(rule (load_ext_name extname offset)
(decl load_ext_name (ExternalName i64 RelocDistance) Reg)
(rule (load_ext_name extname offset distance)
(let ((dst WritableGpr (temp_writable_gpr))
(_ Unit (emit (MInst.LoadExtName dst extname offset))))
(_ Unit (emit (MInst.LoadExtName dst extname offset distance))))
dst))

;; Load a value into a register.
Expand Down
17 changes: 16 additions & 1 deletion cranelift/codegen/src/isa/x64/inst/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3525,7 +3525,12 @@ pub(crate) fn emit(
sink.bind_label(done, &mut state.ctrl_plane);
}

Inst::LoadExtName { dst, name, offset } => {
Inst::LoadExtName {
dst,
name,
offset,
distance,
} => {
let dst = allocs.next(dst.to_reg());

if info.flags.is_pic() {
Expand All @@ -3551,6 +3556,16 @@ pub(crate) fn emit(
sink.put1(0xc0 | (enc_dst & 7));
sink.put4(*offset as u32);
}
} else if distance == &RelocDistance::Near {
// If we know the distance to the name is within 2GB (e.g., a module-local function),
// we can generate a RIP-relative address, with a relocation.
// Generates: lea $name(%rip), $dst
let enc_dst = int_reg_enc(dst);
sink.put1(0x48 | ((enc_dst >> 3) & 1) << 2);
sink.put1(0x8D);
sink.put1(0x05 | ((enc_dst & 7) << 3));
emit_reloc(sink, Reloc::X86CallPCRel4, name, -4);
sink.put4(0);
} else {
// The full address can be encoded in the register, with a relocation.
// Generates: movabsq $name, %dst
Expand Down
3 changes: 3 additions & 0 deletions cranelift/codegen/src/isa/x64/inst/emit_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4383,6 +4383,7 @@ fn test_x64_emit() {
dst: Writable::from_reg(r11),
name: Box::new(ExternalName::User(UserExternalNameRef::new(0))),
offset: 0,
distance: RelocDistance::Far,
},
"4C8B1D00000000",
"load_ext_name userextname0+0, %r11",
Expand All @@ -4392,6 +4393,7 @@ fn test_x64_emit() {
dst: Writable::from_reg(r11),
name: Box::new(ExternalName::User(UserExternalNameRef::new(0))),
offset: 0x12345678,
distance: RelocDistance::Far,
},
"4C8B1D000000004981C378563412",
"load_ext_name userextname0+305419896, %r11",
Expand All @@ -4401,6 +4403,7 @@ fn test_x64_emit() {
dst: Writable::from_reg(r11),
name: Box::new(ExternalName::User(UserExternalNameRef::new(0))),
offset: -0x12345678,
distance: RelocDistance::Far,
},
"4C8B1D000000004981EB78563412",
"load_ext_name userextname0+-305419896, %r11",
Expand Down
8 changes: 4 additions & 4 deletions cranelift/codegen/src/isa/x64/lower.isle
Original file line number Diff line number Diff line change
Expand Up @@ -3039,13 +3039,13 @@

;; Rules for `func_addr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(rule (lower (func_addr (func_ref_data _ extname _)))
(load_ext_name extname 0))
(rule (lower (func_addr (func_ref_data _ extname dist)))
(load_ext_name extname 0 dist))

;; Rules for `symbol_value` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(rule (lower (symbol_value (symbol_value_data extname _ offset)))
(load_ext_name extname offset))
(rule (lower (symbol_value (symbol_value_data extname dist offset)))
(load_ext_name extname offset dist))

;; Rules for `atomic_load` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Expand Down
27 changes: 27 additions & 0 deletions cranelift/filetests/filetests/isa/x64/symbols.clif
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,33 @@ block0:
; popq %rbp
; retq

function %colocated_func_addr() -> i64 {
fn0 = colocated %func0(i64) -> i64

block0:
v0 = func_addr.i64 fn0
return v0
}

; VCode:
; pushq %rbp
; movq %rsp, %rbp
; block0:
; load_ext_name %func0+0, %rax
; movq %rbp, %rsp
; popq %rbp
; ret
;
; Disassembled:
; block0: ; offset 0x0
; pushq %rbp
; movq %rsp, %rbp
; block1: ; offset 0x4
; leaq (%rip), %rax ; reloc_external CallPCRel4 %func0 -4
; movq %rbp, %rsp
; popq %rbp
; retq

function %symbol_value() -> i64 {
gv0 = symbol %global0

Expand Down

0 comments on commit 8756fcf

Please sign in to comment.