Fix long-range (non-colocated) aarch64 calls to not use Arm64Call reloc, and fix simplejit to use it.

Previously, every call was lowered on AArch64 to a `call` instruction, which
takes a signed 26-bit PC-relative offset. Including the 2-bit left shift, this
gives a range of +/- 128 MB. Longer-distance offsets would cause an impossible
relocation record to be emitted (or rather, a record that a more sophisticated
linker would fix up by inserting a shim/veneer).

This commit adds a notion of "relocation distance" in the MachInst backends,
and provides this information for every call target and symbol reference. The
intent is that backends on architectures like AArch64, where there are different
offset sizes / addressing strategies to choose from, can either emit a regular
call or a load-64-bit-constant / call-indirect sequence, as necessary. This
avoids the need to implement complex linking behavior.

The MachInst driver code provides this information based on the "colocated" bit
in the CLIF symbol references, which appears to have been designed for this
purpose, or at least a similar one. Combined with the `use_colocated_libcalls`
setting, this allows client code to ensure that library calls can link to
library code at any location in the address space.

Separately, the `simplejit` example did not handle `Arm64Call`; rather than doing
so, it appears all that is necessary to get its tests to pass is to set the
`use_colocated_libcalls` flag to false, to make use of the above change. This
fixes the `libcall_function` unit-test in this crate.
This commit is contained in:
Chris Fallin
2020-04-21 12:23:10 -07:00
parent 6ef106fee9
commit e39b4aba1c
10 changed files with 114 additions and 23 deletions

View File

@@ -7,6 +7,7 @@
use crate::ir::{ArgumentLoc, ExternalName, SigRef, Type};
use crate::isa::{CallConv, RegInfo, RegUnit};
use crate::machinst::RelocDistance;
use alloc::vec::Vec;
use core::fmt;
use core::str::FromStr;
@@ -366,6 +367,16 @@ pub struct ExtFuncData {
/// Will this function be defined nearby, such that it will always be a certain distance away,
/// after linking? If so, references to it can avoid going through a GOT or PLT. Note that
/// symbols meant to be preemptible cannot be considered colocated.
///
/// If `true`, some backends may use relocation forms that have limited range. The exact
/// distance depends on the code model in use. Currently on AArch64, for example, Cranelift
/// uses a custom code model supporting up to +/- 128MB displacements. If it is unknown how
/// far away the target will be, it is best not to set the `colocated` flag; in general, this
/// flag is best used when the target is known to be in the same unit of code generation, such
/// as a Wasm module.
///
/// See the documentation for [`RelocDistance`](machinst::RelocDistance) for more details. A
/// `colocated` flag value of `true` implies `RelocDistance::Near`.
pub colocated: bool,
}
@@ -378,6 +389,17 @@ impl fmt::Display for ExtFuncData {
}
}
impl ExtFuncData {
/// Return an estimate of the distance to the referred-to function symbol.
pub fn reloc_distance(&self) -> RelocDistance {
if self.colocated {
RelocDistance::Near
} else {
RelocDistance::Far
}
}
}
#[cfg(test)]
mod tests {
use super::*;