diff --git a/cranelift/codegen/src/ir/constant.rs b/cranelift/codegen/src/ir/constant.rs index 2dc95544c8..969c86a327 100644 --- a/cranelift/codegen/src/ir/constant.rs +++ b/cranelift/codegen/src/ir/constant.rs @@ -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 { 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"); diff --git a/cranelift/codegen/src/ir/entities.rs b/cranelift/codegen/src/ir/entities.rs index 57906ab63a..b480722ff4 100644 --- a/cranelift/codegen/src/ir/entities.rs +++ b/cranelift/codegen/src/ir/entities.rs @@ -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 for AnyEntity { } } +impl From for AnyEntity { + fn from(r: Constant) -> Self { + Self::Constant(r) + } +} + impl From for AnyEntity { fn from(r: FuncRef) -> Self { Self::FuncRef(r) diff --git a/cranelift/codegen/src/write.rs b/cranelift/codegen/src/write.rs index 6d109f4c04..1b3f4de550 100644 --- a/cranelift/codegen/src/write.rs +++ b/cranelift/codegen/src/write.rs @@ -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", diff --git a/cranelift/filetests/filetests/isa/x86/simd-arithmetic-legalize.clif b/cranelift/filetests/filetests/isa/x86/simd-arithmetic-legalize.clif index 5211e1d796..c4f7b886dc 100644 --- a/cranelift/filetests/filetests/isa/x86/simd-arithmetic-legalize.clif +++ b/cranelift/filetests/filetests/isa/x86/simd-arithmetic-legalize.clif @@ -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 diff --git a/cranelift/filetests/filetests/isa/x86/simd-comparison-legalize.clif b/cranelift/filetests/filetests/isa/x86/simd-comparison-legalize.clif index 61888ccb6d..27dba2f87a 100644 --- a/cranelift/filetests/filetests/isa/x86/simd-comparison-legalize.clif +++ b/cranelift/filetests/filetests/isa/x86/simd-comparison-legalize.clif @@ -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 } diff --git a/cranelift/filetests/filetests/isa/x86/simd-lane-access-legalize.clif b/cranelift/filetests/filetests/isa/x86/simd-lane-access-legalize.clif index 3c222f1f7f..5480116404 100644 --- a/cranelift/filetests/filetests/isa/x86/simd-lane-access-legalize.clif +++ b/cranelift/filetests/filetests/isa/x86/simd-lane-access-legalize.clif @@ -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 diff --git a/cranelift/filetests/filetests/isa/x86/simd-logical-legalize.clif b/cranelift/filetests/filetests/isa/x86/simd-logical-legalize.clif index a1248c8bba..b3ac8fa60f 100644 --- a/cranelift/filetests/filetests/isa/x86/simd-logical-legalize.clif +++ b/cranelift/filetests/filetests/isa/x86/simd-logical-legalize.clif @@ -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 diff --git a/cranelift/filetests/filetests/isa/x86/simd-vconst-binemit.clif b/cranelift/filetests/filetests/isa/x86/simd-vconst-binemit.clif index 922f110ec3..7ffe0c8cc5 100644 --- a/cranelift/filetests/filetests/isa/x86/simd-vconst-binemit.clif +++ b/cranelift/filetests/filetests/isa/x86/simd-vconst-binemit.clif @@ -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 +} diff --git a/cranelift/filetests/filetests/isa/x86/simd-vconst-compile.clif b/cranelift/filetests/filetests/isa/x86/simd-vconst-compile.clif index d8f9658c78..55fbde0199 100644 --- a/cranelift/filetests/filetests/isa/x86/simd-vconst-compile.clif +++ b/cranelift/filetests/filetests/isa/x86/simd-vconst-compile.clif @@ -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: } diff --git a/cranelift/filetests/filetests/isa/x86/simd-vconst-rodata.clif b/cranelift/filetests/filetests/isa/x86/simd-vconst-rodata.clif index 3488ad06b6..1cffdfe84d 100644 --- a/cranelift/filetests/filetests/isa/x86/simd-vconst-rodata.clif +++ b/cranelift/filetests/filetests/isa/x86/simd-vconst-rodata.clif @@ -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] diff --git a/cranelift/frontend/src/frontend.rs b/cranelift/frontend/src/frontend.rs index 8213850948..df87010ee7 100644 --- a/cranelift/frontend/src/frontend.rs +++ b/cranelift/frontend/src/frontend.rs @@ -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 } diff --git a/cranelift/reader/src/lexer.rs b/cranelift/reader/src/lexer.rs index 20556dc0bd..d1bb835eaa 100644 --- a/cranelift/reader/src/lexer.rs +++ b/cranelift/reader/src/lexer.rs @@ -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)); } diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index 465701155e..172cb2d099 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -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 { 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 { + 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 { 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 { + // Match and consume a sequence of immediate bytes (uimm8); e.g. [0x42 0x99 0x32] + fn match_constant_data(&mut self) -> ParseResult { + 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 { 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" ); } } diff --git a/cranelift/reader/src/sourcemap.rs b/cranelift/reader/src/sourcemap.rs index 126fd219c3..16cd9e5684 100644 --- a/cranelift/reader/src/sourcemap.rs +++ b/cranelift/reader/src/sourcemap.rs @@ -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 { @@ -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<()> {