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,
|
Notrap,
|
||||||
Aligned,
|
Aligned,
|
||||||
Readonly,
|
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.
|
/// Flags for memory operations like load/store.
|
||||||
///
|
///
|
||||||
/// Each of these flags introduce a limited form of undefined behavior. The flags each enable
|
/// 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
|
/// 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.
|
/// 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)]
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct MemFlags {
|
pub struct MemFlags {
|
||||||
bits: u8,
|
bits: u8,
|
||||||
@@ -48,16 +64,48 @@ impl MemFlags {
|
|||||||
/// Set a flag bit by name.
|
/// Set a flag bit by name.
|
||||||
///
|
///
|
||||||
/// Returns true if the flag was found and set, false for an unknown flag 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 {
|
pub fn set_by_name(&mut self, name: &str) -> bool {
|
||||||
match NAMES.iter().position(|&s| s == name) {
|
match NAMES.iter().position(|&s| s == name) {
|
||||||
Some(bit) => {
|
Some(bit) => {
|
||||||
self.bits |= 1 << bit;
|
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
|
true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
None => false,
|
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.
|
/// Test if the `notrap` flag is set.
|
||||||
///
|
///
|
||||||
/// Normally, trapping is part of the semantics of a load/store operation. If the platform
|
/// 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::jumptable::JumpTableData;
|
||||||
pub use crate::ir::layout::Layout;
|
pub use crate::ir::layout::Layout;
|
||||||
pub use crate::ir::libcall::{get_probestack_funcref, LibCall};
|
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::progpoint::{ExpandedProgramPoint, ProgramOrder, ProgramPoint};
|
||||||
pub use crate::ir::sourceloc::SourceLoc;
|
pub use crate::ir::sourceloc::SourceLoc;
|
||||||
pub use crate::ir::stackslot::{StackLayoutInfo, StackSlotData, StackSlotKind, StackSlots};
|
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())
|
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.
|
/// Get the pointer type of this ISA.
|
||||||
fn pointer_type(&self) -> ir::Type {
|
fn pointer_type(&self) -> ir::Type {
|
||||||
ir::Type::int(u16::from(self.pointer_bits())).unwrap()
|
ir::Type::int(u16::from(self.pointer_bits())).unwrap()
|
||||||
|
|||||||
@@ -659,7 +659,7 @@ fn narrow_load(
|
|||||||
inst: ir::Inst,
|
inst: ir::Inst,
|
||||||
func: &mut ir::Function,
|
func: &mut ir::Function,
|
||||||
_cfg: &mut ControlFlowGraph,
|
_cfg: &mut ControlFlowGraph,
|
||||||
_isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
) {
|
) {
|
||||||
let mut pos = FuncCursor::new(func).at_inst(inst);
|
let mut pos = FuncCursor::new(func).at_inst(inst);
|
||||||
pos.use_srcloc(inst);
|
pos.use_srcloc(inst);
|
||||||
@@ -684,6 +684,10 @@ fn narrow_load(
|
|||||||
ptr,
|
ptr,
|
||||||
offset.try_add_i64(8).expect("load offset overflow"),
|
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);
|
pos.func.dfg.replace(inst).iconcat(al, ah);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -692,7 +696,7 @@ fn narrow_store(
|
|||||||
inst: ir::Inst,
|
inst: ir::Inst,
|
||||||
func: &mut ir::Function,
|
func: &mut ir::Function,
|
||||||
_cfg: &mut ControlFlowGraph,
|
_cfg: &mut ControlFlowGraph,
|
||||||
_isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
) {
|
) {
|
||||||
let mut pos = FuncCursor::new(func).at_inst(inst);
|
let mut pos = FuncCursor::new(func).at_inst(inst);
|
||||||
pos.use_srcloc(inst);
|
pos.use_srcloc(inst);
|
||||||
@@ -708,6 +712,10 @@ fn narrow_store(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (al, ah) = pos.ins().isplit(val);
|
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, al, ptr, offset);
|
||||||
pos.ins().store(
|
pos.ins().store(
|
||||||
flags,
|
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
|
// 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
|
// alignment immediate says it's aligned, because WebAssembly's immediate
|
||||||
// field is just a hint, while Cranelift's aligned flag needs a guarantee.
|
// 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()))
|
Ok((flags, base, offset.into()))
|
||||||
}
|
}
|
||||||
@@ -2103,7 +2105,8 @@ fn translate_store<FE: FuncEnvironment + ?Sized>(
|
|||||||
builder,
|
builder,
|
||||||
);
|
);
|
||||||
// See the comments in `prepare_load` about the flags.
|
// 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
|
builder
|
||||||
.ins()
|
.ins()
|
||||||
.Store(opcode, val_ty, flags, offset.into(), val, base);
|
.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)?;
|
finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
|
||||||
|
|
||||||
// See the comments in `prepare_load` about the flags.
|
// 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
|
let mut res = builder
|
||||||
.ins()
|
.ins()
|
||||||
.atomic_rmw(access_ty, flags, op, final_effective_address, arg2);
|
.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)?;
|
finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
|
||||||
|
|
||||||
// See the comments in `prepare_load` about the flags.
|
// 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
|
let mut res = builder
|
||||||
.ins()
|
.ins()
|
||||||
.atomic_cas(flags, final_effective_address, expected, replacement);
|
.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)?;
|
finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
|
||||||
|
|
||||||
// See the comments in `prepare_load` about the flags.
|
// 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
|
let mut res = builder
|
||||||
.ins()
|
.ins()
|
||||||
.atomic_load(access_ty, flags, final_effective_address);
|
.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)?;
|
finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
|
||||||
|
|
||||||
// See the comments in `prepare_load` about the flags.
|
// 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
|
builder
|
||||||
.ins()
|
.ins()
|
||||||
.atomic_store(flags, data, final_effective_address);
|
.atomic_store(flags, data, final_effective_address);
|
||||||
|
|||||||
Reference in New Issue
Block a user