1 //! Test command for verifying the rodata emitted after each function
2 //!
3 //! The `rodata` test command runs each function through the full code generator pipeline
4
5 use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
6 use cranelift_codegen;
7 use cranelift_codegen::binemit::{self, CodeInfo};
8 use cranelift_codegen::ir;
9 use cranelift_codegen::ir::{Function, Value};
10 use cranelift_codegen::isa::TargetIsa;
11 use cranelift_codegen::print_errors::pretty_error;
12 use cranelift_reader::TestCommand;
13 use log::info;
14 use std::borrow::Cow;
15
16 struct TestRodata;
17
subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>>18 pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
19 assert_eq!(parsed.command, "rodata");
20 if !parsed.options.is_empty() {
21 Err(format!("No options allowed on {}", parsed))
22 } else {
23 Ok(Box::new(TestRodata))
24 }
25 }
26
27 impl SubTest for TestRodata {
name(&self) -> &'static str28 fn name(&self) -> &'static str {
29 "rodata"
30 }
31
is_mutating(&self) -> bool32 fn is_mutating(&self) -> bool {
33 true
34 }
35
needs_isa(&self) -> bool36 fn needs_isa(&self) -> bool {
37 true
38 }
39
run(&self, func: Cow<ir::Function>, context: &Context) -> SubtestResult<()>40 fn run(&self, func: Cow<ir::Function>, context: &Context) -> SubtestResult<()> {
41 let isa = context.isa.expect("rodata needs an ISA");
42 let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
43
44 let CodeInfo { total_size, .. } = comp_ctx
45 .compile(isa)
46 .map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?;
47
48 info!(
49 "Generated {} bytes of code:\n{}",
50 total_size,
51 comp_ctx.func.display(isa)
52 );
53
54 // Verify that the returned code size matches the emitted bytes.
55 let mut sink = RodataSink::default();
56 binemit::emit_function(
57 &comp_ctx.func,
58 |func, inst, div, sink, isa| isa.emit_inst(func, inst, div, sink),
59 &mut sink,
60 isa,
61 );
62
63 // Run final code through filecheck.
64 let text = format!("{:X?}", sink.rodata);
65 info!("Found rodata: {}", text);
66 run_filecheck(&text, context)
67 }
68 }
69
70 /// Code sink that only captures emitted rodata
71 #[derive(Default)]
72 struct RodataSink {
73 offset: usize,
74 rodata: Vec<u8>,
75 in_rodata: bool,
76 }
77
78 impl binemit::CodeSink for RodataSink {
offset(&self) -> binemit::CodeOffset79 fn offset(&self) -> binemit::CodeOffset {
80 self.offset as u32
81 }
82
put1(&mut self, byte: u8)83 fn put1(&mut self, byte: u8) {
84 self.offset += 1;
85 if self.in_rodata {
86 self.rodata.push(byte);
87 }
88 }
89
put2(&mut self, bytes: u16)90 fn put2(&mut self, bytes: u16) {
91 self.offset += 2;
92 if self.in_rodata {
93 self.rodata.extend_from_slice(&bytes.to_be_bytes());
94 }
95 }
96
put4(&mut self, bytes: u32)97 fn put4(&mut self, bytes: u32) {
98 self.offset += 4;
99 if self.in_rodata {
100 self.rodata.extend_from_slice(&bytes.to_be_bytes());
101 }
102 }
103
put8(&mut self, bytes: u64)104 fn put8(&mut self, bytes: u64) {
105 self.offset += 8;
106 if self.in_rodata {
107 self.rodata.extend_from_slice(&bytes.to_be_bytes());
108 }
109 }
110
reloc_block(&mut self, _reloc: binemit::Reloc, _block_offset: binemit::CodeOffset)111 fn reloc_block(&mut self, _reloc: binemit::Reloc, _block_offset: binemit::CodeOffset) {}
reloc_external( &mut self, _: ir::SourceLoc, _: binemit::Reloc, _: &ir::ExternalName, _: binemit::Addend, )112 fn reloc_external(
113 &mut self,
114 _: ir::SourceLoc,
115 _: binemit::Reloc,
116 _: &ir::ExternalName,
117 _: binemit::Addend,
118 ) {
119 }
reloc_constant(&mut self, _: binemit::Reloc, _: ir::ConstantOffset)120 fn reloc_constant(&mut self, _: binemit::Reloc, _: ir::ConstantOffset) {}
reloc_jt(&mut self, _reloc: binemit::Reloc, _jt: ir::JumpTable)121 fn reloc_jt(&mut self, _reloc: binemit::Reloc, _jt: ir::JumpTable) {}
trap(&mut self, _code: ir::TrapCode, _srcloc: ir::SourceLoc)122 fn trap(&mut self, _code: ir::TrapCode, _srcloc: ir::SourceLoc) {}
begin_jumptables(&mut self)123 fn begin_jumptables(&mut self) {
124 assert!(!self.in_rodata, "Jump tables must be emitted before rodata");
125 }
begin_rodata(&mut self)126 fn begin_rodata(&mut self) {
127 self.in_rodata = true;
128 }
end_codegen(&mut self)129 fn end_codegen(&mut self) {
130 assert!(
131 self.in_rodata,
132 "Expected rodata to be emitted before the end of codegen"
133 );
134 }
add_stackmap(&mut self, _: &[Value], _: &Function, _: &dyn TargetIsa)135 fn add_stackmap(&mut self, _: &[Value], _: &Function, _: &dyn TargetIsa) {}
136 }
137