Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: retrieve immediates in new backend #2251

Merged
merged 4 commits into from
Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 185 additions & 0 deletions cranelift/codegen/src/data_value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
//! This module gives users to instantiate values that Cranelift understands. These values are used,
//! for example, during interpretation and for wrapping immediates.
use crate::ir::immediates::{Ieee32, Ieee64, Imm64, Offset32};
use crate::ir::{types, ConstantData, Type};
use core::convert::TryInto;
use core::fmt::{self, Display, Formatter};
use thiserror::Error;

/// Represent a data value. Where [Value] is an SSA reference, [DataValue] is the type + value
/// that would be referred to by a [Value].
///
/// [Value]: crate::ir::Value
#[allow(missing_docs)]
#[derive(Clone, Debug, PartialEq)]
pub enum DataValue {
B(bool),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
F32(Ieee32),
F64(Ieee64),
V128([u8; 16]),
}

impl DataValue {
/// Try to cast an immediate integer ([Imm64]) to the given Cranelift [Type].
pub fn from_integer(imm: Imm64, ty: Type) -> Result<DataValue, DataValueCastFailure> {
match ty {
types::I8 => Ok(DataValue::I8(imm.bits() as i8)),
types::I16 => Ok(DataValue::I16(imm.bits() as i16)),
types::I32 => Ok(DataValue::I32(imm.bits() as i32)),
types::I64 => Ok(DataValue::I64(imm.bits())),
_ => Err(DataValueCastFailure::FromImm64(imm, ty)),
}
}

/// Return the Cranelift IR [Type] for this [DataValue].
pub fn ty(&self) -> Type {
match self {
DataValue::B(_) => types::B8, // A default type.
DataValue::I8(_) => types::I8,
DataValue::I16(_) => types::I16,
DataValue::I32(_) => types::I32,
DataValue::I64(_) => types::I64,
DataValue::F32(_) => types::F32,
DataValue::F64(_) => types::F64,
DataValue::V128(_) => types::I8X16, // A default type.
}
}

/// Return true if the value is a vector (i.e. `DataValue::V128`).
pub fn is_vector(&self) -> bool {
match self {
DataValue::V128(_) => true,
_ => false,
}
}
}

/// Record failures to cast [DataValue].
#[derive(Error, Debug, PartialEq)]
#[allow(missing_docs)]
pub enum DataValueCastFailure {
#[error("unable to cast data value of type {0} to type {1}")]
TryInto(Type, Type),
#[error("unable to cast Imm64({0}) to a data value of type {1}")]
FromImm64(Imm64, Type),
}

/// Helper for creating conversion implementations for [DataValue].
macro_rules! build_conversion_impl {
( $rust_ty:ty, $data_value_ty:ident, $cranelift_ty:ident ) => {
impl From<$rust_ty> for DataValue {
fn from(data: $rust_ty) -> Self {
DataValue::$data_value_ty(data)
}
}

impl TryInto<$rust_ty> for DataValue {
type Error = DataValueCastFailure;
fn try_into(self) -> Result<$rust_ty, Self::Error> {
if let DataValue::$data_value_ty(v) = self {
Ok(v)
} else {
Err(DataValueCastFailure::TryInto(
self.ty(),
types::$cranelift_ty,
))
}
}
}
};
}
build_conversion_impl!(bool, B, B8);
build_conversion_impl!(i8, I8, I8);
build_conversion_impl!(i16, I16, I16);
build_conversion_impl!(i32, I32, I32);
build_conversion_impl!(i64, I64, I64);
build_conversion_impl!(Ieee32, F32, F32);
build_conversion_impl!(Ieee64, F64, F64);
build_conversion_impl!([u8; 16], V128, I8X16);
impl From<Offset32> for DataValue {
fn from(o: Offset32) -> Self {
DataValue::from(Into::<i32>::into(o))
}
}

impl Display for DataValue {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
DataValue::B(dv) => write!(f, "{}", dv),
DataValue::I8(dv) => write!(f, "{}", dv),
DataValue::I16(dv) => write!(f, "{}", dv),
DataValue::I32(dv) => write!(f, "{}", dv),
DataValue::I64(dv) => write!(f, "{}", dv),
// The Ieee* wrappers here print the expected syntax.
DataValue::F32(dv) => write!(f, "{}", dv),
DataValue::F64(dv) => write!(f, "{}", dv),
// Again, for syntax consistency, use ConstantData, which in this case displays as hex.
DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])),
}
}
}

/// Helper structure for printing bracket-enclosed vectors of [DataValue]s.
/// - for empty vectors, display `[]`
/// - for single item vectors, display `42`, e.g.
/// - for multiple item vectors, display `[42, 43, 44]`, e.g.
pub struct DisplayDataValues<'a>(pub &'a [DataValue]);

impl<'a> Display for DisplayDataValues<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self.0.len() == 1 {
write!(f, "{}", self.0[0])
} else {
write!(f, "[")?;
write_data_value_list(f, &self.0)?;
write!(f, "]")
}
}
}

/// Helper function for displaying `Vec<DataValue>`.
pub fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt::Result {
match list.len() {
0 => Ok(()),
1 => write!(f, "{}", list[0]),
_ => {
write!(f, "{}", list[0])?;
for dv in list.iter().skip(1) {
write!(f, ", {}", dv)?;
}
Ok(())
}
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn type_conversions() {
assert_eq!(DataValue::B(true).ty(), types::B8);
assert_eq!(
TryInto::<bool>::try_into(DataValue::B(false)).unwrap(),
false
);
assert_eq!(
TryInto::<i32>::try_into(DataValue::B(false)).unwrap_err(),
DataValueCastFailure::TryInto(types::B8, types::I32)
);

assert_eq!(DataValue::V128([0; 16]).ty(), types::I8X16);
assert_eq!(
TryInto::<[u8; 16]>::try_into(DataValue::V128([0; 16])).unwrap(),
[0; 16]
);
assert_eq!(
TryInto::<i32>::try_into(DataValue::V128([0; 16])).unwrap_err(),
DataValueCastFailure::TryInto(types::I8X16, types::I32)
);
}
}
5 changes: 5 additions & 0 deletions cranelift/codegen/src/ir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ impl ConstantData {
self.0.is_empty()
}

/// Return the data as a slice.
pub fn as_slice(&self) -> &[u8] {
self.0.as_slice()
}

/// Convert the data to a vector.
pub fn into_vec(self) -> Vec<u8> {
self.0
Expand Down
2 changes: 2 additions & 0 deletions cranelift/codegen/src/ir/immediates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,13 +450,15 @@ impl FromStr for Offset32 {
///
/// All bit patterns are allowed.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[repr(C)]
pub struct Ieee32(u32);

/// An IEEE binary64 immediate floating point value, represented as a u64
/// containing the bit pattern.
///
/// All bit patterns are allowed.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[repr(C)]
pub struct Ieee64(u64);

/// Format a floating point number in a way that is reasonably human-readable, and that can be
Expand Down
36 changes: 36 additions & 0 deletions cranelift/codegen/src/ir/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::ir::{self, trapcode::TrapCode, types, Block, FuncRef, JumpTable, SigR
use crate::isa;

use crate::bitset::BitSet;
use crate::data_value::DataValue;
use crate::entity;
use ir::condcodes::{FloatCC, IntCC};

Expand Down Expand Up @@ -284,6 +285,41 @@ impl InstructionData {
}
}

/// Return the value of an immediate if the instruction has one or `None` otherwise. Only
/// immediate values are considered, not global values, constant handles, condition codes, etc.
pub fn imm_value(&self) -> Option<DataValue> {
match self {
&InstructionData::UnaryBool { imm, .. } => Some(DataValue::from(imm)),
// 8-bit.
&InstructionData::BinaryImm8 { imm, .. }
| &InstructionData::BranchTableEntry { imm, .. } => Some(DataValue::from(imm as i8)), // Note the switch from unsigned to signed.
// 32-bit
&InstructionData::UnaryIeee32 { imm, .. } => Some(DataValue::from(imm)),
&InstructionData::HeapAddr { imm, .. } => {
let imm: u32 = imm.into();
Some(DataValue::from(imm as i32)) // Note the switch from unsigned to signed.
}
&InstructionData::Load { offset, .. }
| &InstructionData::LoadComplex { offset, .. }
| &InstructionData::Store { offset, .. }
| &InstructionData::StoreComplex { offset, .. }
| &InstructionData::StackLoad { offset, .. }
| &InstructionData::StackStore { offset, .. }
| &InstructionData::TableAddr { offset, .. } => Some(DataValue::from(offset)),
// 64-bit.
&InstructionData::UnaryImm { imm, .. }
| &InstructionData::BinaryImm64 { imm, .. }
| &InstructionData::IntCompareImm { imm, .. } => Some(DataValue::from(imm.bits())),
&InstructionData::UnaryIeee64 { imm, .. } => Some(DataValue::from(imm)),
// 128-bit; though these immediates are present logically in the IR they are not
// included in the `InstructionData` for memory-size reasons. This case, returning
// `None`, is left here to alert users of this method that they should retrieve the
// value using the `DataFlowGraph`.
&InstructionData::Shuffle { mask: _, .. } => None,
_ => None,
}
}

/// If this is a trapping instruction, get its trap code. Otherwise, return
/// `None`.
pub fn trap_code(&self) -> Option<TrapCode> {
Expand Down
22 changes: 5 additions & 17 deletions cranelift/codegen/src/isa/aarch64/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use crate::ir::condcodes::{FloatCC, IntCC};
use crate::ir::types::*;
use crate::ir::Inst as IRInst;
use crate::ir::{InstructionData, Opcode, Type};
use crate::ir::{Opcode, Type};
use crate::machinst::lower::*;
use crate::machinst::*;
use crate::CodegenResult;
Expand All @@ -20,6 +20,7 @@ use crate::isa::aarch64::AArch64Backend;

use super::lower_inst;

use crate::data_value::DataValue;
use log::{debug, trace};
use regalloc::{Reg, RegClass, Writable};
use smallvec::SmallVec;
Expand Down Expand Up @@ -126,22 +127,9 @@ pub(crate) fn const_param_to_u128<C: LowerCtx<I = Inst>>(
ctx: &mut C,
inst: IRInst,
) -> Option<u128> {
let data = match ctx.data(inst) {
&InstructionData::Shuffle { mask, .. } => ctx.get_immediate(mask),
&InstructionData::UnaryConst {
constant_handle, ..
} => ctx.get_constant_data(constant_handle),
_ => return None,
};
let data = data.clone().into_vec();

if data.len() == 16 {
let mut bytes = [0u8; 16];

bytes.copy_from_slice(&data);
Some(u128::from_le_bytes(bytes))
} else {
None
match ctx.get_immediate(inst) {
Some(DataValue::V128(bytes)) => Some(u128::from_le_bytes(bytes)),
_ => None,
}
}

Expand Down
8 changes: 4 additions & 4 deletions cranelift/codegen/src/isa/x64/lower.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Lowering rules for X64.

use crate::data_value::DataValue;
use crate::ir::{
condcodes::FloatCC, condcodes::IntCC, types, AbiParam, ArgumentPurpose, ExternalName,
Inst as IRInst, InstructionData, LibCall, Opcode, Signature, Type,
Expand Down Expand Up @@ -2985,10 +2986,9 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let lhs_ty = ctx.input_ty(insn, 0);
let lhs = put_input_in_reg(ctx, inputs[0]);
let rhs = put_input_in_reg(ctx, inputs[1]);
let mask = if let &InstructionData::Shuffle { mask, .. } = ctx.data(insn) {
ctx.get_immediate(mask).clone()
} else {
unreachable!("shuffle should always have the shuffle format")
let mask = match ctx.get_immediate(insn) {
Some(DataValue::V128(bytes)) => bytes.to_vec(),
_ => unreachable!("shuffle should always have a 16-byte immediate"),
};

// A mask-building helper: in 128-bit SIMD, 0-15 indicate which lane to read from and a
Expand Down
1 change: 1 addition & 0 deletions cranelift/codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub use cranelift_entity as entity;
pub mod binemit;
pub mod cfg_printer;
pub mod cursor;
pub mod data_value;
pub mod dbg;
pub mod dominator_tree;
pub mod flowgraph;
Expand Down
31 changes: 24 additions & 7 deletions cranelift/codegen/src/machinst/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ use crate::inst_predicates::{has_lowering_side_effect, is_constant_64bit};
use crate::ir::instructions::BranchInfo;
use crate::ir::types::I64;
use crate::ir::{
ArgumentPurpose, Block, Constant, ConstantData, ExternalName, Function, GlobalValueData,
Immediate, Inst, InstructionData, MemFlags, Opcode, Signature, SourceLoc, Type, Value,
ValueDef,
ArgumentPurpose, Block, Constant, ConstantData, ExternalName, Function, GlobalValueData, Inst,
InstructionData, MemFlags, Opcode, Signature, SourceLoc, Type, Value, ValueDef,
};
use crate::machinst::{
ABICallee, BlockIndex, BlockLoweringOrder, LoweredBlock, MachLabel, VCode, VCodeBuilder,
Expand All @@ -20,8 +19,10 @@ use crate::CodegenResult;

use regalloc::{Reg, RegClass, StackmapRequestInfo, VirtualReg, Writable};

use crate::data_value::DataValue;
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::convert::TryInto;
use log::debug;
use smallvec::SmallVec;

Expand Down Expand Up @@ -161,8 +162,9 @@ pub trait LowerCtx {
fn is_reg_needed(&self, ir_inst: Inst, reg: Reg) -> bool;
/// Retrieve constant data given a handle.
fn get_constant_data(&self, constant_handle: Constant) -> &ConstantData;
/// Retrieve an immediate given a reference.
fn get_immediate(&self, imm: Immediate) -> &ConstantData;
/// Retrieve the value immediate from an instruction. This will perform necessary lookups on the
/// `DataFlowGraph` to retrieve even large immediates.
fn get_immediate(&self, ir_inst: Inst) -> Option<DataValue>;
/// Cause the value in `reg` to be in a virtual reg, by copying it into a new virtual reg
/// if `reg` is a real reg. `ty` describes the type of the value in `reg`.
fn ensure_in_vreg(&mut self, reg: Reg, ty: Type) -> Reg;
Expand Down Expand Up @@ -1007,8 +1009,23 @@ impl<'func, I: VCodeInst> LowerCtx for Lower<'func, I> {
self.f.dfg.constants.get(constant_handle)
}

fn get_immediate(&self, imm: Immediate) -> &ConstantData {
self.f.dfg.immediates.get(imm).unwrap()
fn get_immediate(&self, ir_inst: Inst) -> Option<DataValue> {
let inst_data = self.data(ir_inst);
match inst_data {
InstructionData::Shuffle { mask, .. } => {
let buffer = self.f.dfg.immediates.get(mask.clone()).unwrap().as_slice();
let value = DataValue::V128(buffer.try_into().expect("a 16-byte data buffer"));
Some(value)
}
InstructionData::UnaryConst {
constant_handle, ..
} => {
let buffer = self.f.dfg.constants.get(constant_handle.clone()).as_slice();
let value = DataValue::V128(buffer.try_into().expect("a 16-byte data buffer"));
Some(value)
}
_ => inst_data.imm_value(),
}
}

fn ensure_in_vreg(&mut self, reg: Reg, ty: Type) -> Reg {
Expand Down
Loading