Implement jump tables (#453)
* Add 'jump_table_entry' and 'indirect_jump' instructions. * Update CodeSink to keep track of code size. Pretty up clif-util's disassembly output. * Only disassemble the machine portion of output. Pretty print the read-only data after it. * Update switch frontend code to use new br_table instruction w/ default.
This commit is contained in:
committed by
Dan Gohman
parent
de1d82b4ba
commit
79cea5e18b
@@ -1402,3 +1402,41 @@ ebb0:
|
||||
|
||||
trap user0 ; bin: user0 0f 0b
|
||||
}
|
||||
|
||||
; Tests for i64 jump table instructions.
|
||||
function %I64_JT(i64 [%rdi]) {
|
||||
jt0 = jump_table ebb1, ebb2, ebb3
|
||||
|
||||
ebb0(v0: i64 [%rdi]):
|
||||
; Note: The next two lines will need to change whenever instructions are
|
||||
; added or removed from this test.
|
||||
[-, %rax] v1 = jump_table_base.i64 jt0 ; bin: 48 8d 05 00000039
|
||||
[-, %r10] v2 = jump_table_base.i64 jt0 ; bin: 4c 8d 15 00000032
|
||||
|
||||
[-, %rbx] v10 = iconst.i64 1
|
||||
[-, %r13] v11 = iconst.i64 2
|
||||
|
||||
[-, %rax] v20 = jump_table_entry.i64 v10, v1, 4, jt0 ; bin: 48 63 04 98
|
||||
[-, %rax] v21 = jump_table_entry.i64 v10, v2, 4, jt0 ; bin: 49 63 04 9a
|
||||
[-, %rax] v22 = jump_table_entry.i64 v11, v1, 4, jt0 ; bin: 4a 63 04 a8
|
||||
[-, %rax] v23 = jump_table_entry.i64 v11, v2, 4, jt0 ; bin: 4b 63 04 aa
|
||||
|
||||
[-, %r10] v30 = jump_table_entry.i64 v10, v1, 4, jt0 ; bin: 4c 63 14 98
|
||||
[-, %r10] v31 = jump_table_entry.i64 v10, v2, 4, jt0 ; bin: 4d 63 14 9a
|
||||
[-, %r10] v32 = jump_table_entry.i64 v11, v1, 4, jt0 ; bin: 4e 63 14 a8
|
||||
[-, %r10] v33 = jump_table_entry.i64 v11, v2, 4, jt0 ; bin: 4f 63 14 aa
|
||||
|
||||
fallthrough ebb10
|
||||
|
||||
ebb10:
|
||||
indirect_jump_table_br v10, jt0 ; bin: ff e3
|
||||
ebb11:
|
||||
indirect_jump_table_br v11, jt0 ; bin: 41 ff e5
|
||||
|
||||
ebb1:
|
||||
fallthrough ebb2
|
||||
ebb2:
|
||||
fallthrough ebb3
|
||||
ebb3:
|
||||
trap user0
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ function u0:0(i64) system_v {
|
||||
ebb0(v0: i64):
|
||||
v1 = stack_addr.i64 ss0
|
||||
v2 = load.i8 v1
|
||||
br_table v2, jt0
|
||||
br_table v2, ebb2, jt0
|
||||
|
||||
ebb2:
|
||||
jump ebb1
|
||||
|
||||
ebb1:
|
||||
|
||||
@@ -85,21 +85,22 @@ function %jumptable(i32) {
|
||||
jt2 = jump_table 0, 0, ebb10, ebb40, ebb20, ebb30
|
||||
|
||||
ebb10(v3: i32):
|
||||
br_table v3, jt2
|
||||
trap user1
|
||||
br_table v3, ebb50, jt2
|
||||
|
||||
ebb20:
|
||||
trap user2
|
||||
ebb30:
|
||||
trap user3
|
||||
ebb40:
|
||||
trap user4
|
||||
ebb50:
|
||||
trap user1
|
||||
}
|
||||
; sameln: function %jumptable(i32) fast {
|
||||
; check: jt2 = jump_table 0, 0, ebb10, ebb40, ebb20, ebb30
|
||||
; check: jt200 = jump_table 0
|
||||
; check: ebb10(v3: i32):
|
||||
; nextln: br_table v3, jt2
|
||||
; nextln: trap user1
|
||||
; nextln: br_table v3, ebb50, jt2
|
||||
; nextln:
|
||||
; nextln: ebb20:
|
||||
; nextln: trap user2
|
||||
@@ -109,4 +110,7 @@ ebb40:
|
||||
; nextln:
|
||||
; nextln: ebb40:
|
||||
; nextln: trap user4
|
||||
; nextln:
|
||||
; nextln: ebb50:
|
||||
; nextln: trap user1
|
||||
; nextln: }
|
||||
|
||||
@@ -71,10 +71,12 @@ function %jump_table_args() {
|
||||
jt1 = jump_table ebb1
|
||||
ebb0:
|
||||
v0 = iconst.i32 0
|
||||
br_table v0, jt1 ; error: takes no arguments, but had target ebb1 with 1 arguments
|
||||
return
|
||||
br_table v0, ebb2, jt1 ; error: takes no arguments, but had target ebb1 with 1 arguments
|
||||
|
||||
ebb1(v5: i32):
|
||||
return
|
||||
ebb2:
|
||||
return
|
||||
}
|
||||
|
||||
function %jump_args() {
|
||||
|
||||
@@ -51,7 +51,9 @@ function %br_table(i32) {
|
||||
jt0 = jump_table ebb3, ebb1, 0, ebb2
|
||||
|
||||
ebb0(v0: i32):
|
||||
br_table v0, jt0
|
||||
br_table v0, ebb4, jt0
|
||||
|
||||
ebb4:
|
||||
trap oob
|
||||
|
||||
ebb1:
|
||||
|
||||
@@ -97,12 +97,20 @@ fn handle_module(
|
||||
context.func = func;
|
||||
|
||||
// Compile and encode the result to machine code.
|
||||
let total_size = context
|
||||
.compile(isa)
|
||||
.map_err(|err| pretty_error(&context.func, Some(isa), err))?;
|
||||
|
||||
let mut mem = Vec::new();
|
||||
mem.resize(total_size as usize, 0);
|
||||
|
||||
let mut relocs = PrintRelocs { flag_print };
|
||||
let mut traps = PrintTraps { flag_print };
|
||||
context
|
||||
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps)
|
||||
.map_err(|err| pretty_error(&context.func, Some(isa), err))?;
|
||||
let mut code_sink: binemit::MemoryCodeSink;
|
||||
unsafe {
|
||||
code_sink = binemit::MemoryCodeSink::new(mem.as_mut_ptr(), &mut relocs, &mut traps);
|
||||
}
|
||||
isa.emit_function_to_memory(&context.func, &mut code_sink);
|
||||
|
||||
if flag_print {
|
||||
println!("{}", context.func.display(isa));
|
||||
@@ -121,17 +129,41 @@ fn handle_module(
|
||||
}
|
||||
|
||||
println!();
|
||||
print_disassembly(isa, &mem)?;
|
||||
print_disassembly(isa, &mem[0..code_sink.code_size as usize])?;
|
||||
print_readonly_data(&mem[code_sink.code_size as usize..total_size as usize]);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_readonly_data(mem: &[u8]) {
|
||||
if mem.len() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("\nFollowed by {} bytes of read-only data:", mem.len());
|
||||
|
||||
for (i, byte) in mem.iter().enumerate() {
|
||||
if i % 16 == 0 {
|
||||
if i != 0 {
|
||||
println!();
|
||||
}
|
||||
print!("{:4}: ", i);
|
||||
}
|
||||
if i % 4 == 0 {
|
||||
print!(" ");
|
||||
}
|
||||
print!("{:02x} ", byte);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "disas")] {
|
||||
use capstone::prelude::*;
|
||||
use target_lexicon::Architecture;
|
||||
use std::fmt::Write;
|
||||
|
||||
fn get_disassembler(isa: &TargetIsa) -> Result<Capstone, String> {
|
||||
let cs = match isa.triple().architecture {
|
||||
@@ -168,10 +200,28 @@ cfg_if! {
|
||||
fn print_disassembly(isa: &TargetIsa, mem: &[u8]) -> Result<(), String> {
|
||||
let mut cs = get_disassembler(isa)?;
|
||||
|
||||
println!("\nDisassembly:");
|
||||
println!("\nDisassembly of {} bytes:", mem.len());
|
||||
let insns = cs.disasm_all(&mem, 0x0).unwrap();
|
||||
for i in insns.iter() {
|
||||
println!("{}", i);
|
||||
let mut line = String::new();
|
||||
|
||||
write!(&mut line, "{:4x}:\t", i.address()).unwrap();
|
||||
|
||||
let mut bytes_str = String::new();
|
||||
for b in i.bytes() {
|
||||
write!(&mut bytes_str, "{:02x} ", b).unwrap();
|
||||
}
|
||||
write!(&mut line, "{:21}\t", bytes_str).unwrap();
|
||||
|
||||
if let Some(s) = i.mnemonic() {
|
||||
write!(&mut line, "{}\t", s).unwrap();
|
||||
}
|
||||
|
||||
if let Some(s) = i.op_str() {
|
||||
write!(&mut line, "{}", s).unwrap();
|
||||
}
|
||||
|
||||
println!("{}", line);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user