Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cranelift/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ harness = false

[dependencies]
cfg-if = "1.0"
cranelift-codegen = { workspace = true }
cranelift-codegen = { workspace = true, features = ["disas"] }
cranelift-entity = { workspace = true }
cranelift-interpreter = { workspace = true }
cranelift-reader = { workspace = true }
Expand Down
6 changes: 6 additions & 0 deletions cranelift/codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ edition.workspace = true

[dependencies]
arrayvec = "0.7"
anyhow = { workspace = true, optional = true }
bumpalo = "3"
capstone = { workspace = true, optional = true }
cranelift-codegen-shared = { path = "./shared", version = "0.94.0" }
cranelift-entity = { workspace = true }
cranelift-bforest = { workspace = true }
Expand Down Expand Up @@ -55,6 +57,10 @@ std = []
# compatibility as a no-op.
core = []

# Enable the `to_capstone` method on TargetIsa, for constructing a Capstone
# context, and the `disassemble` method on `MachBufferFinalized`.
disas = ["anyhow", "capstone"]

# This enables some additional functions useful for writing tests, but which
# can significantly increase the size of the library.
testing_hooks = []
Expand Down
18 changes: 17 additions & 1 deletion cranelift/codegen/src/isa/aarch64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl TargetIsa for AArch64Backend {
Ok(CompiledCodeStencil {
buffer,
frame_size,
disasm: emit_result.disasm,
vcode: emit_result.disasm,
value_labels_ranges,
sized_stackslot_offsets,
dynamic_stackslot_offsets,
Expand Down Expand Up @@ -195,6 +195,22 @@ impl TargetIsa for AArch64Backend {
// 4-byte alignment.
32
}

#[cfg(feature = "disas")]
fn to_capstone(&self) -> Result<capstone::Capstone, capstone::Error> {
use capstone::prelude::*;
let mut cs = Capstone::new()
.arm64()
.mode(arch::arm64::ArchMode::Arm)
.build()?;
// AArch64 uses inline constants rather than a separate constant pool right now.
// Without this option, Capstone will stop disassembling as soon as it sees
// an inline constant that is not also a valid instruction. With this option,
// Capstone will print a `.byte` directive with the bytes of the inline constant
// and continue to the next instruction.
cs.set_skipdata(true)?;
Ok(cs)
}
}

impl fmt::Display for AArch64Backend {
Expand Down
6 changes: 6 additions & 0 deletions cranelift/codegen/src/isa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,12 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
{
Arc::new(self)
}

/// Generate a `Capstone` context for disassembling bytecode for this architecture.
#[cfg(feature = "disas")]
fn to_capstone(&self) -> Result<capstone::Capstone, capstone::Error> {
Err(capstone::Error::UnsupportedArch)
}
}

/// Methods implemented for free for target ISA!
Expand Down
16 changes: 15 additions & 1 deletion cranelift/codegen/src/isa/riscv64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ impl TargetIsa for Riscv64Backend {
Ok(CompiledCodeStencil {
buffer,
frame_size,
disasm: emit_result.disasm,
vcode: emit_result.disasm,
value_labels_ranges,
sized_stackslot_offsets,
dynamic_stackslot_offsets,
Expand Down Expand Up @@ -169,6 +169,20 @@ impl TargetIsa for Riscv64Backend {
fn function_alignment(&self) -> u32 {
4
}

#[cfg(feature = "disas")]
fn to_capstone(&self) -> Result<capstone::Capstone, capstone::Error> {
use capstone::prelude::*;
let mut cs = Capstone::new()
.riscv()
.mode(arch::riscv::ArchMode::RiscV64)
.build()?;
// Similar to AArch64, RISC-V uses inline constants rather than a separate
// constant pool. We want to skip dissasembly over inline constants instead
// of stopping on invalid bytes.
cs.set_skipdata(true)?;
Ok(cs)
}
}

impl fmt::Display for Riscv64Backend {
Expand Down
15 changes: 14 additions & 1 deletion cranelift/codegen/src/isa/s390x/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl TargetIsa for S390xBackend {
Ok(CompiledCodeStencil {
buffer,
frame_size,
disasm: emit_result.disasm,
vcode: emit_result.disasm,
value_labels_ranges,
sized_stackslot_offsets,
dynamic_stackslot_offsets,
Expand Down Expand Up @@ -170,6 +170,19 @@ impl TargetIsa for S390xBackend {
fn function_alignment(&self) -> u32 {
4
}

#[cfg(feature = "disas")]
fn to_capstone(&self) -> Result<capstone::Capstone, capstone::Error> {
use capstone::prelude::*;
let mut cs = Capstone::new()
.sysz()
.mode(arch::sysz::ArchMode::Default)
.build()?;

cs.set_skipdata(true)?;

Ok(cs)
}
}

impl fmt::Display for S390xBackend {
Expand Down
12 changes: 11 additions & 1 deletion cranelift/codegen/src/isa/x64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl TargetIsa for X64Backend {
Ok(CompiledCodeStencil {
buffer,
frame_size,
disasm: emit_result.disasm,
vcode: emit_result.disasm,
value_labels_ranges,
sized_stackslot_offsets,
dynamic_stackslot_offsets,
Expand Down Expand Up @@ -171,6 +171,16 @@ impl TargetIsa for X64Backend {
fn function_alignment(&self) -> u32 {
16
}

#[cfg(feature = "disas")]
fn to_capstone(&self) -> Result<capstone::Capstone, capstone::Error> {
use capstone::prelude::*;
Capstone::new()
.x86()
.mode(arch::x86::ArchMode::Mode64)
.syntax(arch::x86::ArchSyntax::Att)
.build()
}
}

impl fmt::Display for X64Backend {
Expand Down
69 changes: 67 additions & 2 deletions cranelift/codegen/src/machinst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ pub struct CompiledCodeBase<T: CompilePhase> {
/// Size of stack frame, in bytes.
pub frame_size: u32,
/// Disassembly, if requested.
pub disasm: Option<String>,
pub vcode: Option<String>,
/// Debug info: value labels to registers/stackslots at code offsets.
pub value_labels_ranges: ValueLabelsRanges,
/// Debug info: stackslots to stack pointer offsets.
Expand Down Expand Up @@ -317,7 +317,7 @@ impl CompiledCodeStencil {
CompiledCode {
buffer: self.buffer.apply_base_srcloc(params.base_srcloc()),
frame_size: self.frame_size,
disasm: self.disasm,
vcode: self.vcode,
value_labels_ranges: self.value_labels_ranges,
sized_stackslot_offsets: self.sized_stackslot_offsets,
dynamic_stackslot_offsets: self.dynamic_stackslot_offsets,
Expand All @@ -340,6 +340,71 @@ impl<T: CompilePhase> CompiledCodeBase<T> {
pub fn code_buffer(&self) -> &[u8] {
self.buffer.data()
}

/// Get the disassembly of the buffer, using the given capstone context.
#[cfg(feature = "disas")]
pub fn disassemble(
&self,
params: Option<&crate::ir::function::FunctionParameters>,
cs: &capstone::Capstone,
) -> Result<String, anyhow::Error> {
use std::fmt::Write;

let mut buf = String::new();

let relocs = self.buffer.relocs();
let traps = self.buffer.traps();
let labels = self.bb_starts.as_slice();

let insns = cs.disasm_all(self.buffer.data(), 0x0).map_err(map_caperr)?;
for i in insns.iter() {
if let Some((n, off)) = labels
.iter()
.copied()
.enumerate()
.find(|(_, val)| *val == i.address() as u32)
{
writeln!(buf, "block{}: ; offset 0x{:x}", n, off)?;
}

write!(buf, " ")?;

let op_str = i.op_str().unwrap_or("");
if let Some(s) = i.mnemonic() {
write!(buf, "{}", s)?;
if !op_str.is_empty() {
write!(buf, " ")?;
}
}

write!(buf, "{}", op_str)?;

let end = i.address() + i.bytes().len() as u64;
let contains = |off| i.address() <= off && off < end;

if let Some(reloc) = relocs.iter().find(|reloc| contains(reloc.offset as u64)) {
write!(
buf,
" ; reloc_external {} {} {}",
reloc.kind,
reloc.name.display(params),
reloc.addend,
)?;
}

if let Some(trap) = traps.iter().find(|trap| contains(trap.offset as u64)) {
write!(buf, " ; trap: {}", trap.code)?;
}

writeln!(buf)?;
}

return Ok(buf);

fn map_caperr(err: capstone::Error) -> anyhow::Error {
anyhow::format_err!("{}", err)
}
}
}

/// Result of compiling a `FunctionStencil`, before applying `FunctionParameters` onto it.
Expand Down
2 changes: 1 addition & 1 deletion cranelift/filetests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ publish = false
edition.workspace = true

[dependencies]
cranelift-codegen = { workspace = true, features = ["testing_hooks"] }
cranelift-codegen = { workspace = true, features = ["testing_hooks", "disas"] }
cranelift-frontend = { workspace = true }
cranelift-interpreter = { workspace = true }
cranelift-native = { workspace = true }
Expand Down
11 changes: 11 additions & 0 deletions cranelift/filetests/filetests/egraph/multivalue.clif
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
test compile precise-output
set opt_level=speed
set use_egraphs=true
set machine_code_cfg_info=true
target x86_64

;; We want to make sure that this compiles successfully, so we are properly
Expand All @@ -15,11 +16,21 @@ function u0:359(i64) -> i8, i8 system_v {
return v3, v4
}

; VCode:
; pushq %rbp
; movq %rsp, %rbp
; block0:
; call User(userextname0)
; movq %rbp, %rsp
; popq %rbp
; ret
;
; Disassembled:
; pushq %rbp
; movq %rsp, %rbp
; block0: ; offset 0x4
; callq 9 ; reloc_external CallPCRel4 u0:521 -4
; movq %rbp, %rsp
; popq %rbp
; retq

15 changes: 15 additions & 0 deletions cranelift/filetests/filetests/egraph/not_a_load.clif
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ function u0:1302(i64) -> i64 system_v {
return v0
}

; VCode:
; pushq %rbp
; movq %rsp, %rbp
; block0:
Expand All @@ -21,4 +22,18 @@ function u0:1302(i64) -> i64 system_v {
; movq %rbp, %rsp
; popq %rbp
; ret
;
; Disassembled:
; pushq %rbp
; movq %rsp, %rbp
; block0: ; offset 0x4
; movq (%rdi), %rax ; trap: heap_oob
; movq %rax, %rcx
; addq %rdi, %rcx
; lock cmpxchgq %rcx, (%rdi) ; trap: heap_oob
; jne 7
; movq %rdi, %rax
; movq %rbp, %rsp
; popq %rbp
; retq

Loading