From 0672d1dc0fc0aedaf44dba1d0909214ce069bcc7 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2020 14:08:03 -0700 Subject: [PATCH] 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. --- cranelift/codegen/src/ir/constant.rs | 71 ++++++--- cranelift/codegen/src/ir/entities.rs | 9 ++ cranelift/codegen/src/write.rs | 14 +- .../isa/x86/simd-arithmetic-legalize.clif | 19 ++- .../isa/x86/simd-comparison-legalize.clif | 6 +- .../isa/x86/simd-lane-access-legalize.clif | 19 ++- .../isa/x86/simd-logical-legalize.clif | 6 +- .../isa/x86/simd-vconst-binemit.clif | 10 ++ .../isa/x86/simd-vconst-compile.clif | 4 +- .../filetests/isa/x86/simd-vconst-rodata.clif | 10 ++ cranelift/frontend/src/frontend.rs | 6 +- cranelift/reader/src/lexer.rs | 5 +- cranelift/reader/src/parser.rs | 138 ++++++++++++++---- cranelift/reader/src/sourcemap.rs | 12 +- 14 files changed, 255 insertions(+), 74 deletions(-) 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<()> {