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",
|
||||
|
||||
@@ -3,10 +3,12 @@ set enable_simd
|
||||
target x86_64 skylake
|
||||
|
||||
function %ineg_i32x4() -> b1 {
|
||||
; check: const0 = 0x00000001000000010000000100000001
|
||||
; nextln: const1 = 0x00000000000000000000000000000000
|
||||
block0:
|
||||
v0 = vconst.i32x4 [1 1 1 1]
|
||||
v2 = ineg v0
|
||||
; check: v5 = vconst.i32x4 0x00
|
||||
; check: v5 = vconst.i32x4 const1
|
||||
; nextln: v2 = isub v5, v0
|
||||
|
||||
v3 = extractlane v2, 0
|
||||
@@ -16,37 +18,39 @@ block0:
|
||||
}
|
||||
|
||||
function %ineg_legalized() {
|
||||
; check: const0 = 0x00000000000000000000000000000000
|
||||
block0:
|
||||
v0 = vconst.i8x16 0x00
|
||||
v1 = ineg v0
|
||||
; check: v6 = vconst.i8x16 0x00
|
||||
; check: v6 = vconst.i8x16 const0
|
||||
; nextln: v1 = isub v6, v0
|
||||
|
||||
v2 = raw_bitcast.i16x8 v0
|
||||
v3 = ineg v2
|
||||
; check: v7 = vconst.i16x8 0x00
|
||||
; check: v7 = vconst.i16x8 const0
|
||||
; nextln: v3 = isub v7, v2
|
||||
|
||||
v4 = raw_bitcast.i64x2 v0
|
||||
v5 = ineg v4
|
||||
; check: v8 = vconst.i64x2 0x00
|
||||
; check: v8 = vconst.i64x2 const0
|
||||
; nextln: v5 = isub v8, v4
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
function %fneg_legalized() {
|
||||
; check: const2 = 0xffffffffffffffffffffffffffffffff
|
||||
block0:
|
||||
v0 = vconst.f32x4 [0x1.0 0x2.0 0x3.0 0x4.0]
|
||||
v1 = fneg v0
|
||||
; check: v4 = vconst.i32x4 0xffffffffffffffffffffffffffffffff
|
||||
; check: v4 = vconst.i32x4 const2
|
||||
; nextln: v5 = ishl_imm v4, 31
|
||||
; nextln: v6 = raw_bitcast.f32x4 v5
|
||||
; nextln: v1 = bxor v0, v6
|
||||
|
||||
v2 = vconst.f64x2 [0x1.0 0x2.0]
|
||||
v3 = fneg v2
|
||||
; check: v7 = vconst.i64x2 0xffffffffffffffffffffffffffffffff
|
||||
; check: v7 = vconst.i64x2 const2
|
||||
; nextln: v8 = ishl_imm v7, 63
|
||||
; nextln: v9 = raw_bitcast.f64x2 v8
|
||||
; nextln: v3 = bxor v2, v9
|
||||
@@ -55,10 +59,11 @@ block0:
|
||||
}
|
||||
|
||||
function %fabs_legalized() {
|
||||
; check: const1 = 0xffffffffffffffffffffffffffffffff
|
||||
block0:
|
||||
v0 = vconst.f64x2 [0x1.0 -0x2.0]
|
||||
v1 = fabs v0
|
||||
; check: v2 = vconst.i64x2 0xffffffffffffffffffffffffffffffff
|
||||
; check: v2 = vconst.i64x2 const1
|
||||
; nextln: v3 = ushr_imm v2, 1
|
||||
; nextln: v4 = raw_bitcast.f64x2 v3
|
||||
; nextln: v1 = band v0, v4
|
||||
|
||||
@@ -3,20 +3,22 @@ set enable_simd
|
||||
target x86_64 skylake
|
||||
|
||||
function %icmp_ne_32x4(i32x4, i32x4) -> b32x4 {
|
||||
; check: const0 = 0xffffffffffffffffffffffffffffffff
|
||||
block0(v0: i32x4, v1: i32x4):
|
||||
v2 = icmp ne v0, v1
|
||||
; check: v3 = icmp eq v0, v1
|
||||
; nextln: v4 = vconst.b32x4 0xffffffffffffffffffffffffffffffff
|
||||
; nextln: v4 = vconst.b32x4 const0
|
||||
; nextln: v2 = bxor v4, v3
|
||||
return v2
|
||||
}
|
||||
|
||||
function %icmp_ugt_i32x4(i32x4, i32x4) -> b32x4 {
|
||||
; check: const0 = 0xffffffffffffffffffffffffffffffff
|
||||
block0(v0: i32x4, v1: i32x4):
|
||||
v2 = icmp ugt v0, v1
|
||||
; check: v3 = x86_pmaxu v0, v1
|
||||
; nextln: v4 = icmp eq v3, v1
|
||||
; nextln: v5 = vconst.b32x4 0xffffffffffffffffffffffffffffffff
|
||||
; nextln: v5 = vconst.b32x4 const0
|
||||
; nextln: v2 = bxor v5, v4
|
||||
return v2
|
||||
}
|
||||
|
||||
@@ -5,27 +5,30 @@ target x86_64 skylake
|
||||
;; shuffle
|
||||
|
||||
function %shuffle_different_ssa_values() -> i8x16 {
|
||||
; check: const2 = 0x80000000000000000000000000000000
|
||||
; nextln: const3 = 0x01808080808080808080808080808080
|
||||
block0:
|
||||
v0 = vconst.i8x16 0x00
|
||||
v1 = vconst.i8x16 0x01
|
||||
v2 = shuffle v0, v1, 0x11000000000000000000000000000000 ; pick the second lane of v1, the rest use the first lane of v0
|
||||
return v2
|
||||
}
|
||||
; check: v1 = vconst.i8x16 0x01
|
||||
; nextln: v3 = vconst.i8x16 0x80000000000000000000000000000000
|
||||
; check: v1 = vconst.i8x16 const1
|
||||
; nextln: v3 = vconst.i8x16 const2
|
||||
; nextln: v4 = x86_pshufb v0, v3
|
||||
; nextln: v5 = vconst.i8x16 0x01808080808080808080808080808080
|
||||
; nextln: v5 = vconst.i8x16 const3
|
||||
; nextln: v6 = x86_pshufb v1, v5
|
||||
; nextln: v2 = bor v4, v6
|
||||
|
||||
function %shuffle_same_ssa_value() -> i8x16 {
|
||||
; check: const1 = 0x03000000000000000000000000000000
|
||||
block0:
|
||||
v1 = vconst.i8x16 0x01
|
||||
v2 = shuffle v1, v1, 0x13000000000000000000000000000000 ; pick the fourth lane of v1 and the rest from the first lane of v1
|
||||
return v2
|
||||
}
|
||||
; check: v1 = vconst.i8x16 0x01
|
||||
; nextln: v3 = vconst.i8x16 0x03000000000000000000000000000000
|
||||
; check: v1 = vconst.i8x16 const0
|
||||
; nextln: v3 = vconst.i8x16 const1
|
||||
; nextln: v2 = x86_pshufb v1, v3
|
||||
|
||||
;; splat
|
||||
@@ -71,6 +74,7 @@ block0:
|
||||
; nextln: return v1
|
||||
|
||||
function %splat_i8() -> i8x16 {
|
||||
; check: const0 = 0x00000000000000000000000000000000
|
||||
block0:
|
||||
v0 = iconst.i8 42
|
||||
v1 = splat.i8x16 v0
|
||||
@@ -80,16 +84,17 @@ block0:
|
||||
; nextln: v2 = iconst.i32 42
|
||||
; nextln: v0 = ireduce.i8 v2
|
||||
; nextln: v3 = scalar_to_vector.i8x16 v0
|
||||
; nextln: v4 = vconst.i8x16 0x00
|
||||
; nextln: v4 = vconst.i8x16 const0
|
||||
; nextln: v1 = x86_pshufb v3, v4
|
||||
; nextln: return v1
|
||||
|
||||
function %swizzle() -> i8x16 {
|
||||
; check: const1 = 0x70707070707070707070707070707070
|
||||
block0:
|
||||
v0 = vconst.i8x16 [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
|
||||
v1 = vconst.i8x16 [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
|
||||
v2 = swizzle.i8x16 v0, v1
|
||||
; check: v3 = vconst.i8x16 0x70707070707070707070707070707070
|
||||
; check: v3 = vconst.i8x16 const1
|
||||
; nextln: v4 = uadd_sat v1, v3
|
||||
; nextln: v2 = x86_pshufb v0, v4
|
||||
return v2
|
||||
|
||||
@@ -3,9 +3,10 @@ set enable_simd
|
||||
target x86_64 skylake
|
||||
|
||||
function %bnot_b32x4(b32x4) -> b32x4 {
|
||||
; check: const0 = 0xffffffffffffffffffffffffffffffff
|
||||
block0(v0: b32x4):
|
||||
v1 = bnot v0
|
||||
; check: v2 = vconst.b32x4 0xffffffffffffffffffffffffffffffff
|
||||
; check: v2 = vconst.b32x4 const0
|
||||
; nextln: v1 = bxor v2, v0
|
||||
return v1
|
||||
}
|
||||
@@ -19,9 +20,10 @@ block0(v0: b32x4):
|
||||
}
|
||||
|
||||
function %vall_true_i64x2(i64x2) -> b1 {
|
||||
; check: const0 = 0x00000000000000000000000000000000
|
||||
block0(v0: i64x2):
|
||||
v1 = vall_true v0
|
||||
; check: v2 = vconst.i64x2 0x00
|
||||
; check: v2 = vconst.i64x2 const0
|
||||
; nextln: v3 = icmp eq v0, v2
|
||||
; nextln: v4 = x86_ptest v3, v3
|
||||
; nextln: v1 = trueif eq v4
|
||||
|
||||
@@ -9,3 +9,13 @@ block0:
|
||||
[-, %xmm3] v1 = vconst.b8x16 0x02 ; bin: 0f 10 1d 00000011 PCRelRodata4(31)
|
||||
return
|
||||
}
|
||||
|
||||
function %vconst_with_preamble() {
|
||||
const42 = i32x4 [1 0 0 0]
|
||||
const43 = i32x4 [2 0 0 0]
|
||||
|
||||
block0:
|
||||
[-, %xmm2] v0 = vconst.i32x4 const42 ; bin: 0f 10 15 00000008 PCRelRodata4(15)
|
||||
[-, %xmm3] v1 = vconst.i32x4 const43 ; bin: 0f 10 1d 00000011 PCRelRodata4(31)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ block0:
|
||||
v0 = vconst.i32x4 0x1234
|
||||
return v0
|
||||
}
|
||||
|
||||
; check: const0 = 0x00000000000000000000000000001234
|
||||
; check: block0:
|
||||
; nextln: v0 = vconst.i32x4 0x1234
|
||||
; nextln: v0 = vconst.i32x4 const0
|
||||
; nextln: return v0
|
||||
; nextln: }
|
||||
|
||||
@@ -37,3 +37,13 @@ block11:
|
||||
}
|
||||
|
||||
; sameln: [1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10]
|
||||
|
||||
function %vconst_preamble() -> b16x8 {
|
||||
const42 = i32x4 [0 1 2 3]
|
||||
const43 = i32x4 [4 5 6 7]
|
||||
block0:
|
||||
v0 = vconst.b16x8 const42
|
||||
return v0
|
||||
}
|
||||
|
||||
; sameln: [0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0]
|
||||
|
||||
@@ -1262,13 +1262,15 @@ block0:
|
||||
assert_eq!(
|
||||
func.display(None).to_string(),
|
||||
"function %sample() -> i8x16, b8x16, f32x4 system_v {
|
||||
const0 = 0x00000000000000000000000000000000
|
||||
|
||||
block0:
|
||||
v5 = f32const 0.0
|
||||
v6 = splat.f32x4 v5
|
||||
v2 -> v6
|
||||
v4 = vconst.b8x16 0x00
|
||||
v4 = vconst.b8x16 const0
|
||||
v1 -> v4
|
||||
v3 = vconst.i8x16 0x00
|
||||
v3 = vconst.i8x16 const0
|
||||
v0 -> v3
|
||||
return v0, v1, v2
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ pub enum Token<'a> {
|
||||
Heap(u32), // heap2
|
||||
Table(u32), // table2
|
||||
JumpTable(u32), // jt2
|
||||
Constant(u32), // const2
|
||||
FuncRef(u32), // fn2
|
||||
SigRef(u32), // sig2
|
||||
UserRef(u32), // u345
|
||||
@@ -345,6 +346,7 @@ impl<'a> Lexer<'a> {
|
||||
"heap" => Some(Token::Heap(number)),
|
||||
"table" => Some(Token::Table(number)),
|
||||
"jt" => Some(Token::JumpTable(number)),
|
||||
"const" => Some(Token::Constant(number)),
|
||||
"fn" => Some(Token::FuncRef(number)),
|
||||
"sig" => Some(Token::SigRef(number)),
|
||||
"u" => Some(Token::UserRef(number)),
|
||||
@@ -656,7 +658,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn lex_names() {
|
||||
let mut lex = Lexer::new("%0 %x3 %function %123_abc %ss0 %v3 %block11 %_");
|
||||
let mut lex = Lexer::new("%0 %x3 %function %123_abc %ss0 %v3 %block11 %const42 %_");
|
||||
|
||||
assert_eq!(lex.next(), token(Token::Name("0"), 1));
|
||||
assert_eq!(lex.next(), token(Token::Name("x3"), 1));
|
||||
@@ -665,6 +667,7 @@ mod tests {
|
||||
assert_eq!(lex.next(), token(Token::Name("ss0"), 1));
|
||||
assert_eq!(lex.next(), token(Token::Name("v3"), 1));
|
||||
assert_eq!(lex.next(), token(Token::Name("block11"), 1));
|
||||
assert_eq!(lex.next(), token(Token::Name("const42"), 1));
|
||||
assert_eq!(lex.next(), token(Token::Name("_"), 1));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@ use cranelift_codegen::ir::instructions::{InstructionData, InstructionFormat, Va
|
||||
use cranelift_codegen::ir::types::INVALID;
|
||||
use cranelift_codegen::ir::types::*;
|
||||
use cranelift_codegen::ir::{
|
||||
AbiParam, ArgumentExtension, ArgumentLoc, Block, ConstantData, ExtFuncData, ExternalName,
|
||||
FuncRef, Function, GlobalValue, GlobalValueData, Heap, HeapData, HeapStyle, JumpTable,
|
||||
JumpTableData, MemFlags, Opcode, SigRef, Signature, StackSlot, StackSlotData, StackSlotKind,
|
||||
Table, TableData, Type, Value, ValueLoc,
|
||||
AbiParam, ArgumentExtension, ArgumentLoc, Block, Constant, ConstantData, ExtFuncData,
|
||||
ExternalName, FuncRef, Function, GlobalValue, GlobalValueData, Heap, HeapData, HeapStyle,
|
||||
JumpTable, JumpTableData, MemFlags, Opcode, SigRef, Signature, StackSlot, StackSlotData,
|
||||
StackSlotKind, Table, TableData, Type, Value, ValueLoc,
|
||||
};
|
||||
use cranelift_codegen::isa::{self, CallConv, Encoding, RegUnit, TargetIsa};
|
||||
use cranelift_codegen::packed_option::ReservedValue;
|
||||
@@ -346,6 +346,27 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a new constant.
|
||||
fn add_constant(
|
||||
&mut self,
|
||||
constant: Constant,
|
||||
data: ConstantData,
|
||||
loc: Location,
|
||||
) -> ParseResult<()> {
|
||||
self.map.def_constant(constant, loc)?;
|
||||
self.function.dfg.constants.set(constant, data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Resolve a reference to a constant.
|
||||
fn check_constant(&self, c: Constant, loc: Location) -> ParseResult<()> {
|
||||
if !self.map.contains_constant(c) {
|
||||
err!(loc, "undefined constant {}", c)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a new block.
|
||||
fn add_block(&mut self, block: Block, loc: Location) -> ParseResult<Block> {
|
||||
self.map.def_block(block, loc)?;
|
||||
@@ -566,6 +587,17 @@ impl<'a> Parser<'a> {
|
||||
err!(self.loc, "expected jump table number: jt«n»")
|
||||
}
|
||||
|
||||
// Match and consume a constant reference.
|
||||
fn match_constant(&mut self) -> ParseResult<Constant> {
|
||||
if let Some(Token::Constant(c)) = self.token() {
|
||||
self.consume();
|
||||
if let Some(c) = Constant::with_number(c) {
|
||||
return Ok(c);
|
||||
}
|
||||
}
|
||||
err!(self.loc, "expected constant number: const«n»")
|
||||
}
|
||||
|
||||
// Match and consume a block reference.
|
||||
fn match_block(&mut self, err_msg: &str) -> ParseResult<Block> {
|
||||
if let Some(Token::Block(block)) = self.token() {
|
||||
@@ -621,8 +653,20 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// Match and consume either a hexadecimal Uimm128 immediate (e.g. 0x000102...) or its literal list form (e.g. [0 1 2...])
|
||||
fn match_constant_data(&mut self, controlling_type: Type) -> ParseResult<ConstantData> {
|
||||
// Match and consume a sequence of immediate bytes (uimm8); e.g. [0x42 0x99 0x32]
|
||||
fn match_constant_data(&mut self) -> ParseResult<ConstantData> {
|
||||
self.match_token(Token::LBracket, "expected an opening left bracket")?;
|
||||
let mut data = ConstantData::default();
|
||||
while !self.optional(Token::RBracket) {
|
||||
data = data.append(self.match_uimm8("expected a sequence of bytes (uimm8)")?);
|
||||
}
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
// Match and consume either a hexadecimal Uimm128 immediate (e.g. 0x000102...) or its literal
|
||||
// list form (e.g. [0 1 2...]). For convenience, since uimm128 values are stored in the
|
||||
// `ConstantPool`, this returns `ConstantData`.
|
||||
fn match_uimm128(&mut self, controlling_type: Type) -> ParseResult<ConstantData> {
|
||||
let expected_size = controlling_type.bytes() as usize;
|
||||
let constant_data = if self.optional(Token::LBracket) {
|
||||
// parse using a list of values, e.g. vconst.i32x4 [0 1 2 3]
|
||||
@@ -1454,6 +1498,11 @@ impl<'a> Parser<'a> {
|
||||
self.parse_jump_table_decl()
|
||||
.and_then(|(jt, dat)| ctx.add_jt(jt, dat, self.loc))
|
||||
}
|
||||
Some(Token::Constant(..)) => {
|
||||
self.start_gathering_comments();
|
||||
self.parse_constant_decl()
|
||||
.and_then(|(c, v)| ctx.add_constant(c, v, self.loc))
|
||||
}
|
||||
// More to come..
|
||||
_ => return Ok(()),
|
||||
}?;
|
||||
@@ -1838,6 +1887,26 @@ impl<'a> Parser<'a> {
|
||||
Ok((jt, data))
|
||||
}
|
||||
|
||||
// Parse a constant decl.
|
||||
//
|
||||
// constant-decl ::= * Constant(c) "=" ty? "[" literal {"," literal} "]"
|
||||
fn parse_constant_decl(&mut self) -> ParseResult<(Constant, ConstantData)> {
|
||||
let name = self.match_constant()?;
|
||||
self.match_token(Token::Equal, "expected '=' in constant decl")?;
|
||||
let data = if let Some(Token::Type(_)) = self.token() {
|
||||
let ty = self.match_type("expected type of constant")?;
|
||||
self.match_uimm128(ty)
|
||||
} else {
|
||||
self.match_constant_data()
|
||||
}?;
|
||||
|
||||
// Collect any trailing comments.
|
||||
self.token();
|
||||
self.claim_gathered_comments(name);
|
||||
|
||||
Ok((name, data))
|
||||
}
|
||||
|
||||
// Parse a function body, add contents to `ctx`.
|
||||
//
|
||||
// function-body ::= * { extended-basic-block }
|
||||
@@ -2537,7 +2606,7 @@ impl<'a> Parser<'a> {
|
||||
F32 => DataValue::from(f32::from_bits(self.match_ieee32("expected an f32")?.bits())),
|
||||
F64 => DataValue::from(f64::from_bits(self.match_ieee64("expected an f64")?.bits())),
|
||||
_ if ty.is_vector() => {
|
||||
let as_vec = self.match_constant_data(ty)?.into_vec();
|
||||
let as_vec = self.match_uimm128(ty)?.into_vec();
|
||||
if as_vec.len() == 16 {
|
||||
let mut as_array = [0; 16];
|
||||
as_array.copy_from_slice(&as_vec[..16]);
|
||||
@@ -2583,6 +2652,28 @@ impl<'a> Parser<'a> {
|
||||
opcode,
|
||||
imm: self.match_bool("expected immediate boolean operand")?,
|
||||
},
|
||||
InstructionFormat::UnaryConst => {
|
||||
let constant_handle = if let Some(Token::Constant(_)) = self.token() {
|
||||
// If handed a `const?`, use that.
|
||||
let c = self.match_constant()?;
|
||||
ctx.check_constant(c, self.loc)?;
|
||||
c
|
||||
} else if let Some(controlling_type) = explicit_control_type {
|
||||
// If an explicit control type is present, we expect a sized value and insert
|
||||
// it in the constant pool.
|
||||
let uimm128 = self.match_uimm128(controlling_type)?;
|
||||
ctx.function.dfg.constants.insert(uimm128)
|
||||
} else {
|
||||
return err!(
|
||||
self.loc,
|
||||
"Expected either a const entity or a typed value, e.g. inst.i32x4 [...]"
|
||||
);
|
||||
};
|
||||
InstructionData::UnaryConst {
|
||||
opcode,
|
||||
constant_handle,
|
||||
}
|
||||
}
|
||||
InstructionFormat::UnaryGlobalValue => {
|
||||
let gv = self.match_gv("expected global value")?;
|
||||
ctx.check_gv(gv, self.loc)?;
|
||||
@@ -2753,29 +2844,12 @@ impl<'a> Parser<'a> {
|
||||
let lane = self.match_uimm8("expected lane number")?;
|
||||
InstructionData::ExtractLane { opcode, lane, arg }
|
||||
}
|
||||
InstructionFormat::UnaryConst => match explicit_control_type {
|
||||
None => {
|
||||
return err!(
|
||||
self.loc,
|
||||
"Expected {:?} to have a controlling type variable, e.g. inst.i32x4",
|
||||
opcode
|
||||
)
|
||||
}
|
||||
Some(controlling_type) => {
|
||||
let uimm128 = self.match_constant_data(controlling_type)?;
|
||||
let constant_handle = ctx.function.dfg.constants.insert(uimm128);
|
||||
InstructionData::UnaryConst {
|
||||
opcode,
|
||||
constant_handle,
|
||||
}
|
||||
}
|
||||
},
|
||||
InstructionFormat::Shuffle => {
|
||||
let a = self.match_value("expected SSA value first operand")?;
|
||||
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||
let b = self.match_value("expected SSA value second operand")?;
|
||||
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||
let uimm128 = self.match_constant_data(I8X16)?;
|
||||
let uimm128 = self.match_uimm128(I8X16)?;
|
||||
let mask = ctx.function.dfg.immediates.push(uimm128);
|
||||
InstructionData::Shuffle {
|
||||
opcode,
|
||||
@@ -3692,6 +3766,18 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_unbounded_constants() {
|
||||
// Unlike match_uimm128, match_constant_data can parse byte sequences of any size:
|
||||
assert_eq!(
|
||||
Parser::new("[0 1]").match_constant_data().unwrap(),
|
||||
vec![0, 1].into()
|
||||
);
|
||||
|
||||
// Only parse byte literals:
|
||||
assert!(Parser::new("[256]").match_constant_data().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_run_commands() {
|
||||
// Helper for creating signatures.
|
||||
@@ -3760,7 +3846,7 @@ mod tests {
|
||||
assert_eq!(parse("false", B64).to_string(), "false");
|
||||
assert_eq!(
|
||||
parse("[0 1 2 3]", I32X4).to_string(),
|
||||
"0x03000000020000000100000000"
|
||||
"0x00000003000000020000000100000000"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::error::{Location, ParseResult};
|
||||
use crate::lexer::split_entity_name;
|
||||
use cranelift_codegen::ir::entities::AnyEntity;
|
||||
use cranelift_codegen::ir::{
|
||||
Block, FuncRef, GlobalValue, Heap, JumpTable, SigRef, StackSlot, Table, Value,
|
||||
Block, Constant, FuncRef, GlobalValue, Heap, JumpTable, SigRef, StackSlot, Table, Value,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -68,6 +68,11 @@ impl SourceMap {
|
||||
self.locations.contains_key(&jt.into())
|
||||
}
|
||||
|
||||
/// Look up a constant entity.
|
||||
pub fn contains_constant(&self, c: Constant) -> bool {
|
||||
self.locations.contains_key(&c.into())
|
||||
}
|
||||
|
||||
/// Look up an entity by source name.
|
||||
/// Returns the entity reference corresponding to `name`, if it exists.
|
||||
pub fn lookup_str(&self, name: &str) -> Option<AnyEntity> {
|
||||
@@ -198,6 +203,11 @@ impl SourceMap {
|
||||
self.def_entity(entity.into(), loc)
|
||||
}
|
||||
|
||||
/// Define the jump table `entity`.
|
||||
pub fn def_constant(&mut self, entity: Constant, loc: Location) -> ParseResult<()> {
|
||||
self.def_entity(entity.into(), loc)
|
||||
}
|
||||
|
||||
/// Define an entity. This can be used for instructions whose numbers never
|
||||
/// appear in source, or implicitly defined signatures.
|
||||
pub fn def_entity(&mut self, entity: AnyEntity, loc: Location) -> ParseResult<()> {
|
||||
|
||||
Reference in New Issue
Block a user