diff --git a/cranelift/filetests/filetests/isa/x86/rodata-vconst.clif b/cranelift/filetests/filetests/isa/x86/rodata-vconst.clif new file mode 100644 index 0000000000..772cf3b03c --- /dev/null +++ b/cranelift/filetests/filetests/isa/x86/rodata-vconst.clif @@ -0,0 +1,13 @@ +test rodata +set enable_simd=true +set probestack_enabled=false +target x86_64 haswell + +; use baldrdash calling convention here for simplicity (avoids prologue, epilogue) +function %test_vconst_i32() -> i32x4 baldrdash_system_v { +ebb0: + v0 = vconst.i32x4 0x1234 + return v0 +} + +; sameln: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 34] diff --git a/cranelift/filetests/src/lib.rs b/cranelift/filetests/src/lib.rs index 08f02afbf5..073fc24492 100644 --- a/cranelift/filetests/src/lib.rs +++ b/cranelift/filetests/src/lib.rs @@ -48,6 +48,7 @@ mod test_postopt; mod test_preopt; mod test_print_cfg; mod test_regalloc; +mod test_rodata; mod test_run; mod test_safepoint; mod test_shrink; @@ -119,6 +120,7 @@ fn new_subtest(parsed: &TestCommand) -> subtest::SubtestResult test_binemit::subtest(parsed), "cat" => test_cat::subtest(parsed), "compile" => test_compile::subtest(parsed), + "rodata" => test_rodata::subtest(parsed), "dce" => test_dce::subtest(parsed), "domtree" => test_domtree::subtest(parsed), "legalizer" => test_legalizer::subtest(parsed), diff --git a/cranelift/filetests/src/test_rodata.rs b/cranelift/filetests/src/test_rodata.rs new file mode 100644 index 0000000000..412fa55ff0 --- /dev/null +++ b/cranelift/filetests/src/test_rodata.rs @@ -0,0 +1,123 @@ +//! Test command for verifying the rodata emitted after each function +//! +//! The `rodata` test command runs each function through the full code generator pipeline + +use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult}; +use cranelift_codegen; +use cranelift_codegen::binemit::{self, CodeInfo}; +use cranelift_codegen::ir; +use cranelift_codegen::ir::{Function, Value}; +use cranelift_codegen::isa::TargetIsa; +use cranelift_codegen::print_errors::pretty_error; +use cranelift_reader::TestCommand; +use log::info; +use std::borrow::Cow; + +struct TestRodata; + +pub fn subtest(parsed: &TestCommand) -> SubtestResult> { + assert_eq!(parsed.command, "rodata"); + if !parsed.options.is_empty() { + Err(format!("No options allowed on {}", parsed)) + } else { + Ok(Box::new(TestRodata)) + } +} + +impl SubTest for TestRodata { + fn name(&self) -> &'static str { + "rodata" + } + + fn is_mutating(&self) -> bool { + true + } + + fn needs_isa(&self) -> bool { + true + } + + fn run(&self, func: Cow, context: &Context) -> SubtestResult<()> { + let isa = context.isa.expect("rodata needs an ISA"); + let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned()); + + let CodeInfo { total_size, .. } = comp_ctx + .compile(isa) + .map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?; + + info!( + "Generated {} bytes of code:\n{}", + total_size, + comp_ctx.func.display(isa) + ); + + // Verify that the returned code size matches the emitted bytes. + let mut sink = RodataSink { + rodata: Vec::new(), + in_rodata: false, + }; + binemit::emit_function( + &comp_ctx.func, + |func, inst, div, sink, isa| isa.emit_inst(func, inst, div, sink), + &mut sink, + isa, + ); + + // Run final code through filecheck. + let text = format!("{:X?}", sink.rodata); + info!("Found rodata: {}", text); + run_filecheck(&text, context) + } +} + +/// Code sink that only captures emitted rodata +struct RodataSink { + in_rodata: bool, + rodata: Vec, +} + +impl binemit::CodeSink for RodataSink { + fn offset(&self) -> binemit::CodeOffset { + 0 + } + + fn put1(&mut self, byte: u8) { + if self.in_rodata { + self.rodata.push(byte); + } + } + + fn put2(&mut self, bytes: u16) { + if self.in_rodata { + self.rodata.extend_from_slice(&bytes.to_be_bytes()); + } + } + + fn put4(&mut self, bytes: u32) { + if self.in_rodata { + self.rodata.extend_from_slice(&bytes.to_be_bytes()); + } + } + + fn put8(&mut self, bytes: u64) { + if self.in_rodata { + self.rodata.extend_from_slice(&bytes.to_be_bytes()); + } + } + + fn reloc_ebb(&mut self, _reloc: binemit::Reloc, _ebb_offset: binemit::CodeOffset) {} + fn reloc_external(&mut self, _: binemit::Reloc, _: &ir::ExternalName, _: binemit::Addend) {} + fn reloc_constant(&mut self, _: binemit::Reloc, _: ir::ConstantOffset) {} + fn reloc_jt(&mut self, _reloc: binemit::Reloc, _jt: ir::JumpTable) {} + fn trap(&mut self, _code: ir::TrapCode, _srcloc: ir::SourceLoc) {} + fn begin_jumptables(&mut self) { + assert!(!self.in_rodata); + } + fn begin_rodata(&mut self) { + self.in_rodata = true; + } + fn end_codegen(&mut self) { + assert!(self.in_rodata); + } + fn add_stackmap(&mut self, _: &[Value], _: &Function, _: &dyn TargetIsa) {} +}