Declare constants in the function preamble
This allows us to give names to constants in the constant pool and then use these names in the function body. The original behavior, specifiying the constant value as an instruction immediate, is still supported as a shortcut but some filetests had to change since the canonical way of printing the CLIF constants is now in the preamble.
This commit is contained in:
@@ -58,6 +58,11 @@ impl ConstantData {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
/// Check if the constant contains any bytes.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
/// Convert the data to a vector.
|
||||
pub fn into_vec(self) -> Vec<u8> {
|
||||
self.0
|
||||
@@ -92,23 +97,19 @@ impl ConstantData {
|
||||
impl fmt::Display for ConstantData {
|
||||
/// Print the constant data in hexadecimal format, e.g. 0x000102030405060708090a0b0c0d0e0f.
|
||||
/// This function will flip the stored order of bytes--little-endian--to the more readable
|
||||
/// big-endian ordering. Any zero bytes in high-order bytes will be discarded in the formatted
|
||||
/// string.
|
||||
/// big-endian ordering.
|
||||
///
|
||||
/// ```
|
||||
/// use cranelift_codegen::ir::ConstantData;
|
||||
/// let data = ConstantData::from([3, 2, 1, 0, 0].as_ref()); // note the little-endian order
|
||||
/// assert_eq!(data.to_string(), "0x010203");
|
||||
/// assert_eq!(data.to_string(), "0x0000010203");
|
||||
/// ```
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "0x")?;
|
||||
let mut bytes_written = 0;
|
||||
for b in self.0.iter().rev().skip_while(|&&b| b == 0) {
|
||||
write!(f, "{:02x}", b)?;
|
||||
bytes_written += 1;
|
||||
}
|
||||
if bytes_written < 1 {
|
||||
write!(f, "00")?;
|
||||
if !self.is_empty() {
|
||||
write!(f, "0x")?;
|
||||
for b in self.0.iter().rev() {
|
||||
write!(f, "{:02x}", b)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -224,10 +225,7 @@ impl ConstantPool {
|
||||
*self.values_to_handles.get(&constant_value).unwrap()
|
||||
} else {
|
||||
let constant_handle = Constant::new(self.len());
|
||||
self.values_to_handles
|
||||
.insert(constant_value.clone(), constant_handle);
|
||||
self.handles_to_values
|
||||
.insert(constant_handle, ConstantPoolEntry::new(constant_value));
|
||||
self.set(constant_handle, constant_value);
|
||||
constant_handle
|
||||
}
|
||||
}
|
||||
@@ -238,6 +236,25 @@ impl ConstantPool {
|
||||
&self.handles_to_values.get(&constant_handle).unwrap().data
|
||||
}
|
||||
|
||||
/// Link a constant handle to its value. This does not de-duplicate data but does avoid
|
||||
/// replacing any existing constant values. use `set` to tie a specific `const42` to its value;
|
||||
/// use `insert` to add a value and return the next available `const` entity.
|
||||
pub fn set(&mut self, constant_handle: Constant, constant_value: ConstantData) {
|
||||
let replaced = self.handles_to_values.insert(
|
||||
constant_handle,
|
||||
ConstantPoolEntry::new(constant_value.clone()),
|
||||
);
|
||||
assert!(
|
||||
replaced.is_none(),
|
||||
"attempted to overwrite an existing constant {:?}: {:?} => {:?}",
|
||||
constant_handle,
|
||||
&constant_value,
|
||||
replaced.unwrap().data
|
||||
);
|
||||
self.values_to_handles
|
||||
.insert(constant_value, constant_handle);
|
||||
}
|
||||
|
||||
/// Assign an offset to a given constant, where the offset is the number of bytes from the
|
||||
/// beginning of the function to the beginning of the constant data inside the pool.
|
||||
pub fn set_offset(&mut self, constant_handle: Constant, constant_offset: ConstantOffset) {
|
||||
@@ -344,6 +361,24 @@ mod tests {
|
||||
assert_eq!(sut.get(handle), &data.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set() {
|
||||
let mut sut = ConstantPool::new();
|
||||
let handle = Constant::with_number(42).unwrap();
|
||||
let data = vec![1, 2, 3];
|
||||
sut.set(handle, data.clone().into());
|
||||
assert_eq!(sut.get(handle), &data.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn disallow_overwriting_constant() {
|
||||
let mut sut = ConstantPool::new();
|
||||
let handle = Constant::with_number(42).unwrap();
|
||||
sut.set(handle, vec![].into());
|
||||
sut.set(handle, vec![1].into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn get_nonexistent_constant() {
|
||||
@@ -374,7 +409,7 @@ mod tests {
|
||||
assert_eq!(ConstantData::from([42].as_ref()).to_string(), "0x2a");
|
||||
assert_eq!(
|
||||
ConstantData::from([3, 2, 1, 0].as_ref()).to_string(),
|
||||
"0x010203"
|
||||
"0x00010203"
|
||||
);
|
||||
assert_eq!(
|
||||
ConstantData::from(3735928559u32.to_le_bytes().as_ref()).to_string(),
|
||||
@@ -435,12 +470,12 @@ mod tests {
|
||||
}
|
||||
|
||||
parse_ok("0x00", "0x00");
|
||||
parse_ok("0x00000042", "0x42");
|
||||
parse_ok("0x00000042", "0x00000042");
|
||||
parse_ok(
|
||||
"0x0102030405060708090a0b0c0d0e0f00",
|
||||
"0x0102030405060708090a0b0c0d0e0f00",
|
||||
);
|
||||
parse_ok("0x_0000_0043_21", "0x4321");
|
||||
parse_ok("0x_0000_0043_21", "0x0000004321");
|
||||
|
||||
parse_err("", "Expected a hexadecimal string, e.g. 0x1234");
|
||||
parse_err("0x", "Expected a hexadecimal string, e.g. 0x1234");
|
||||
|
||||
@@ -382,6 +382,8 @@ pub enum AnyEntity {
|
||||
GlobalValue(GlobalValue),
|
||||
/// A jump table.
|
||||
JumpTable(JumpTable),
|
||||
/// A constant.
|
||||
Constant(Constant),
|
||||
/// An external function.
|
||||
FuncRef(FuncRef),
|
||||
/// A function call signature.
|
||||
@@ -402,6 +404,7 @@ impl fmt::Display for AnyEntity {
|
||||
Self::StackSlot(r) => r.fmt(f),
|
||||
Self::GlobalValue(r) => r.fmt(f),
|
||||
Self::JumpTable(r) => r.fmt(f),
|
||||
Self::Constant(r) => r.fmt(f),
|
||||
Self::FuncRef(r) => r.fmt(f),
|
||||
Self::SigRef(r) => r.fmt(f),
|
||||
Self::Heap(r) => r.fmt(f),
|
||||
@@ -452,6 +455,12 @@ impl From<JumpTable> for AnyEntity {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Constant> for AnyEntity {
|
||||
fn from(r: Constant) -> Self {
|
||||
Self::Constant(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FuncRef> for AnyEntity {
|
||||
fn from(r: FuncRef) -> Self {
|
||||
Self::FuncRef(r)
|
||||
|
||||
@@ -102,6 +102,11 @@ pub trait FuncWriter {
|
||||
self.write_entity_definition(w, func, jt.into(), jt_data)?;
|
||||
}
|
||||
|
||||
for (&cref, cval) in func.dfg.constants.iter() {
|
||||
any = true;
|
||||
self.write_entity_definition(w, func, cref.into(), cval)?;
|
||||
}
|
||||
|
||||
Ok(any)
|
||||
}
|
||||
|
||||
@@ -494,6 +499,9 @@ pub fn write_operands(
|
||||
UnaryIeee64 { imm, .. } => write!(w, " {}", imm),
|
||||
UnaryBool { imm, .. } => write!(w, " {}", imm),
|
||||
UnaryGlobalValue { global_value, .. } => write!(w, " {}", global_value),
|
||||
UnaryConst {
|
||||
constant_handle, ..
|
||||
} => write!(w, " {}", constant_handle),
|
||||
Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
|
||||
BinaryImm { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
|
||||
Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
|
||||
@@ -507,12 +515,6 @@ pub fn write_operands(
|
||||
NullAry { .. } => write!(w, " "),
|
||||
InsertLane { lane, args, .. } => write!(w, " {}, {}, {}", args[0], lane, args[1]),
|
||||
ExtractLane { lane, arg, .. } => write!(w, " {}, {}", arg, lane),
|
||||
UnaryConst {
|
||||
constant_handle, ..
|
||||
} => {
|
||||
let constant_data = dfg.constants.get(constant_handle);
|
||||
write!(w, " {}", constant_data)
|
||||
}
|
||||
Shuffle { mask, args, .. } => {
|
||||
let data = dfg.immediates.get(mask).expect(
|
||||
"Expected the shuffle mask to already be inserted into the immediates table",
|
||||
|
||||
Reference in New Issue
Block a user