Merge pull request #2492 from uweigand/endian-memory-v5

Support explicit endianness in Cranelift IR MemFlags
This commit is contained in:
Chris Fallin
2020-12-14 13:59:08 -08:00
committed by GitHub
5 changed files with 83 additions and 12 deletions

View File

@@ -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

View File

@@ -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};

View File

@@ -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()

View File

@@ -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,

View File

@@ -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);