Merge pull request #2492 from uweigand/endian-memory-v5
Support explicit endianness in Cranelift IR MemFlags
This commit is contained in:
@@ -6,15 +6,31 @@ enum FlagBit {
|
||||
Notrap,
|
||||
Aligned,
|
||||
Readonly,
|
||||
LittleEndian,
|
||||
BigEndian,
|
||||
}
|
||||
|
||||
const NAMES: [&str; 3] = ["notrap", "aligned", "readonly"];
|
||||
const NAMES: [&str; 5] = ["notrap", "aligned", "readonly", "little", "big"];
|
||||
|
||||
/// Endianness of a memory access.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum Endianness {
|
||||
/// Little-endian
|
||||
Little,
|
||||
/// Big-endian
|
||||
Big,
|
||||
}
|
||||
|
||||
/// Flags for memory operations like load/store.
|
||||
///
|
||||
/// Each of these flags introduce a limited form of undefined behavior. The flags each enable
|
||||
/// certain optimizations that need to make additional assumptions. Generally, the semantics of a
|
||||
/// program does not change when a flag is removed, but adding a flag will.
|
||||
///
|
||||
/// In addition, the flags determine the endianness of the memory access. By default,
|
||||
/// any memory access uses the native endianness determined by the target ISA. This can
|
||||
/// be overridden for individual accesses by explicitly specifying little- or big-endian
|
||||
/// semantics via the flags.
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct MemFlags {
|
||||
bits: u8,
|
||||
@@ -48,16 +64,48 @@ impl MemFlags {
|
||||
/// Set a flag bit by name.
|
||||
///
|
||||
/// Returns true if the flag was found and set, false for an unknown flag name.
|
||||
/// Will also return false when trying to set inconsistent endianness flags.
|
||||
pub fn set_by_name(&mut self, name: &str) -> bool {
|
||||
match NAMES.iter().position(|&s| s == name) {
|
||||
Some(bit) => {
|
||||
self.bits |= 1 << bit;
|
||||
true
|
||||
let bits = self.bits | 1 << bit;
|
||||
if (bits & (1 << FlagBit::LittleEndian as usize)) != 0
|
||||
&& (bits & (1 << FlagBit::BigEndian as usize)) != 0
|
||||
{
|
||||
false
|
||||
} else {
|
||||
self.bits = bits;
|
||||
true
|
||||
}
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return endianness of the memory access. This will return the endianness
|
||||
/// explicitly specified by the flags if any, and will default to the native
|
||||
/// endianness otherwise. The native endianness has to be provided by the
|
||||
/// caller since it is not explicitly encoded in CLIF IR -- this allows a
|
||||
/// front end to create IR without having to know the target endianness.
|
||||
pub fn endianness(self, native_endianness: Endianness) -> Endianness {
|
||||
if self.read(FlagBit::LittleEndian) {
|
||||
Endianness::Little
|
||||
} else if self.read(FlagBit::BigEndian) {
|
||||
Endianness::Big
|
||||
} else {
|
||||
native_endianness
|
||||
}
|
||||
}
|
||||
|
||||
/// Set endianness of the memory access.
|
||||
pub fn set_endianness(&mut self, endianness: Endianness) {
|
||||
match endianness {
|
||||
Endianness::Little => self.set(FlagBit::LittleEndian),
|
||||
Endianness::Big => self.set(FlagBit::BigEndian),
|
||||
};
|
||||
assert!(!(self.read(FlagBit::LittleEndian) && self.read(FlagBit::BigEndian)));
|
||||
}
|
||||
|
||||
/// Test if the `notrap` flag is set.
|
||||
///
|
||||
/// Normally, trapping is part of the semantics of a load/store operation. If the platform
|
||||
|
||||
@@ -50,7 +50,7 @@ pub use crate::ir::instructions::{
|
||||
pub use crate::ir::jumptable::JumpTableData;
|
||||
pub use crate::ir::layout::Layout;
|
||||
pub use crate::ir::libcall::{get_probestack_funcref, LibCall};
|
||||
pub use crate::ir::memflags::MemFlags;
|
||||
pub use crate::ir::memflags::{Endianness, MemFlags};
|
||||
pub use crate::ir::progpoint::{ExpandedProgramPoint, ProgramOrder, ProgramPoint};
|
||||
pub use crate::ir::sourceloc::SourceLoc;
|
||||
pub use crate::ir::stackslot::{StackLayoutInfo, StackSlotData, StackSlotKind, StackSlots};
|
||||
|
||||
@@ -235,6 +235,14 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
|
||||
CallConv::triple_default(self.triple())
|
||||
}
|
||||
|
||||
/// Get the endianness of this ISA.
|
||||
fn endianness(&self) -> ir::Endianness {
|
||||
match self.triple().endianness().unwrap() {
|
||||
target_lexicon::Endianness::Little => ir::Endianness::Little,
|
||||
target_lexicon::Endianness::Big => ir::Endianness::Big,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the pointer type of this ISA.
|
||||
fn pointer_type(&self) -> ir::Type {
|
||||
ir::Type::int(u16::from(self.pointer_bits())).unwrap()
|
||||
|
||||
@@ -659,7 +659,7 @@ fn narrow_load(
|
||||
inst: ir::Inst,
|
||||
func: &mut ir::Function,
|
||||
_cfg: &mut ControlFlowGraph,
|
||||
_isa: &dyn TargetIsa,
|
||||
isa: &dyn TargetIsa,
|
||||
) {
|
||||
let mut pos = FuncCursor::new(func).at_inst(inst);
|
||||
pos.use_srcloc(inst);
|
||||
@@ -684,6 +684,10 @@ fn narrow_load(
|
||||
ptr,
|
||||
offset.try_add_i64(8).expect("load offset overflow"),
|
||||
);
|
||||
let (al, ah) = match flags.endianness(isa.endianness()) {
|
||||
ir::Endianness::Little => (al, ah),
|
||||
ir::Endianness::Big => (ah, al),
|
||||
};
|
||||
pos.func.dfg.replace(inst).iconcat(al, ah);
|
||||
}
|
||||
|
||||
@@ -692,7 +696,7 @@ fn narrow_store(
|
||||
inst: ir::Inst,
|
||||
func: &mut ir::Function,
|
||||
_cfg: &mut ControlFlowGraph,
|
||||
_isa: &dyn TargetIsa,
|
||||
isa: &dyn TargetIsa,
|
||||
) {
|
||||
let mut pos = FuncCursor::new(func).at_inst(inst);
|
||||
pos.use_srcloc(inst);
|
||||
@@ -708,6 +712,10 @@ fn narrow_store(
|
||||
};
|
||||
|
||||
let (al, ah) = pos.ins().isplit(val);
|
||||
let (al, ah) = match flags.endianness(isa.endianness()) {
|
||||
ir::Endianness::Little => (al, ah),
|
||||
ir::Endianness::Big => (ah, al),
|
||||
};
|
||||
pos.ins().store(flags, al, ptr, offset);
|
||||
pos.ins().store(
|
||||
flags,
|
||||
|
||||
@@ -2056,7 +2056,9 @@ fn prepare_load<FE: FuncEnvironment + ?Sized>(
|
||||
// Note that we don't set `is_aligned` here, even if the load instruction's
|
||||
// alignment immediate says it's aligned, because WebAssembly's immediate
|
||||
// field is just a hint, while Cranelift's aligned flag needs a guarantee.
|
||||
let flags = MemFlags::new();
|
||||
// WebAssembly memory accesses are always little-endian.
|
||||
let mut flags = MemFlags::new();
|
||||
flags.set_endianness(ir::Endianness::Little);
|
||||
|
||||
Ok((flags, base, offset.into()))
|
||||
}
|
||||
@@ -2103,7 +2105,8 @@ fn translate_store<FE: FuncEnvironment + ?Sized>(
|
||||
builder,
|
||||
);
|
||||
// See the comments in `prepare_load` about the flags.
|
||||
let flags = MemFlags::new();
|
||||
let mut flags = MemFlags::new();
|
||||
flags.set_endianness(ir::Endianness::Little);
|
||||
builder
|
||||
.ins()
|
||||
.Store(opcode, val_ty, flags, offset.into(), val, base);
|
||||
@@ -2207,7 +2210,8 @@ fn translate_atomic_rmw<FE: FuncEnvironment + ?Sized>(
|
||||
finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
|
||||
|
||||
// See the comments in `prepare_load` about the flags.
|
||||
let flags = MemFlags::new();
|
||||
let mut flags = MemFlags::new();
|
||||
flags.set_endianness(ir::Endianness::Little);
|
||||
let mut res = builder
|
||||
.ins()
|
||||
.atomic_rmw(access_ty, flags, op, final_effective_address, arg2);
|
||||
@@ -2260,7 +2264,8 @@ fn translate_atomic_cas<FE: FuncEnvironment + ?Sized>(
|
||||
finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
|
||||
|
||||
// See the comments in `prepare_load` about the flags.
|
||||
let flags = MemFlags::new();
|
||||
let mut flags = MemFlags::new();
|
||||
flags.set_endianness(ir::Endianness::Little);
|
||||
let mut res = builder
|
||||
.ins()
|
||||
.atomic_cas(flags, final_effective_address, expected, replacement);
|
||||
@@ -2302,7 +2307,8 @@ fn translate_atomic_load<FE: FuncEnvironment + ?Sized>(
|
||||
finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
|
||||
|
||||
// See the comments in `prepare_load` about the flags.
|
||||
let flags = MemFlags::new();
|
||||
let mut flags = MemFlags::new();
|
||||
flags.set_endianness(ir::Endianness::Little);
|
||||
let mut res = builder
|
||||
.ins()
|
||||
.atomic_load(access_ty, flags, final_effective_address);
|
||||
@@ -2348,7 +2354,8 @@ fn translate_atomic_store<FE: FuncEnvironment + ?Sized>(
|
||||
finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
|
||||
|
||||
// See the comments in `prepare_load` about the flags.
|
||||
let flags = MemFlags::new();
|
||||
let mut flags = MemFlags::new();
|
||||
flags.set_endianness(ir::Endianness::Little);
|
||||
builder
|
||||
.ins()
|
||||
.atomic_store(flags, data, final_effective_address);
|
||||
|
||||
Reference in New Issue
Block a user