Skip to content

Commit fdc8737

Browse files
eckertliamclaude
andcommitted
Implement Display trait for all types to produce valid LLVM IR text (#46)
Add Display impls for Module, Function, FunctionDeclaration, BasicBlock, GlobalVariable, GlobalAlias, GlobalIFunc, Parameter, and all supporting enums (Linkage, Visibility, CallingConvention, FunctionAttribute, etc.). Module::to_string() now produces valid .ll files that LLVM can re-parse. Also fix existing Display impls for instructions, terminators, and operands to emit correct LLVM IR syntax (binary op second operand without type, phi values without types in brackets, call/invoke with return type, GEP with source element type, switch with newlines). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 39269a1 commit fdc8737

10 files changed

Lines changed: 1142 additions & 123 deletions

File tree

src/basicblock.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,21 @@ pub struct BasicBlock {
1212
pub term: Terminator,
1313
}
1414

15+
impl std::fmt::Display for BasicBlock {
16+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
17+
// label: the name without the % prefix
18+
match &self.name {
19+
Name::Name(n) => writeln!(f, "{}:", n)?,
20+
Name::Number(n) => writeln!(f, "{}:", n)?,
21+
}
22+
for instr in &self.instrs {
23+
writeln!(f, " {}", instr)?;
24+
}
25+
writeln!(f, " {}", self.term)?;
26+
Ok(())
27+
}
28+
}
29+
1530
impl BasicBlock {
1631
/// A `BasicBlock` instance with no instructions and an `Unreachable` terminator
1732
pub fn new(name: Name) -> Self {

src/function.rs

Lines changed: 353 additions & 0 deletions
Large diffs are not rendered by default.

src/instruction.rs

Lines changed: 79 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use crate::constant::ConstantRef;
1+
use crate::constant::{Constant, ConstantRef};
22
use crate::debugloc::{DebugLoc, HasDebugLoc};
33
use crate::function::{CallingConvention, FunctionAttribute, ParameterAttribute};
44
use crate::name::Name;
5-
use crate::operand::Operand;
5+
use crate::operand::{fmt_operand_value, fmt_operand_with_attrs, Operand};
66
use crate::predicates::*;
77
#[cfg(feature = "llvm-14-or-lower")]
88
use crate::types::NamedStructDef;
@@ -631,11 +631,8 @@ macro_rules! binop_display {
631631
($inst:ty, $dispname:expr) => {
632632
impl Display for $inst {
633633
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
634-
write!(
635-
f,
636-
"{} = {} {}, {}",
637-
&self.dest, $dispname, &self.operand0, &self.operand1,
638-
)?;
634+
write!(f, "{} = {} {}, ", &self.dest, $dispname, &self.operand0)?;
635+
fmt_operand_value(&self.operand1, f)?;
639636
if self.debugloc.is_some() {
640637
write!(f, " (with debugloc)")?;
641638
}
@@ -662,11 +659,8 @@ macro_rules! binop_display_with_flags {
662659
// Write any flags we may have
663660
$( #[cfg(feature = $required_feature)] if self.$flag_field { write!(f, " {}", $flag_display)?; })*
664661

665-
write!(
666-
f,
667-
" {}, {}",
668-
&self.operand0, &self.operand1,
669-
)?;
662+
write!(f, " {}, ", &self.operand0)?;
663+
fmt_operand_value(&self.operand1, f)?;
670664

671665
if self.debugloc.is_some() {
672666
write!(f, " (with debugloc)")?;
@@ -1711,14 +1705,14 @@ fn gep_type<'o>(
17111705

17121706
impl Display for GetElementPtr {
17131707
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1714-
// Like for `Load` (see notes there), we differ from the LLVM IR text
1715-
// syntax here because we don't include the destination type (that's a
1716-
// little hard to get for us here, and it's derivable from the other
1717-
// information anyway)
17181708
write!(f, "{} = getelementptr ", &self.dest)?;
17191709
if self.in_bounds {
17201710
write!(f, "inbounds ")?;
17211711
}
1712+
#[cfg(feature = "llvm-14-or-greater")]
1713+
{
1714+
write!(f, "{}, ", &self.source_element_type)?;
1715+
}
17221716
write!(f, "{}", &self.address)?;
17231717
for idx in &self.indices {
17241718
write!(f, ", {}", idx)?;
@@ -1951,11 +1945,8 @@ impl Typed for ICmp {
19511945

19521946
impl Display for ICmp {
19531947
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1954-
write!(
1955-
f,
1956-
"{} = icmp {} {}, {}",
1957-
&self.dest, &self.predicate, &self.operand0, &self.operand1,
1958-
)?;
1948+
write!(f, "{} = icmp {} {}, ", &self.dest, &self.predicate, &self.operand0)?;
1949+
fmt_operand_value(&self.operand1, f)?;
19591950
if self.debugloc.is_some() {
19601951
write!(f, " (with debugloc)")?;
19611952
}
@@ -1998,11 +1989,8 @@ impl Typed for FCmp {
19981989

19991990
impl Display for FCmp {
20001991
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2001-
write!(
2002-
f,
2003-
"{} = fcmp {} {}, {}",
2004-
&self.dest, &self.predicate, &self.operand0, &self.operand1,
2005-
)?;
1992+
write!(f, "{} = fcmp {} {}, ", &self.dest, &self.predicate, &self.operand0)?;
1993+
fmt_operand_value(&self.operand1, f)?;
20061994
if self.debugloc.is_some() {
20071995
write!(f, " (with debugloc)")?;
20081996
}
@@ -2030,13 +2018,13 @@ impl Display for Phi {
20302018
.incoming_values
20312019
.get(0)
20322020
.expect("Phi with no incoming values");
2033-
write!(
2034-
f,
2035-
"{} = phi {} [ {}, {} ]",
2036-
&self.dest, &self.to_type, first_val, first_label,
2037-
)?;
2021+
write!(f, "{} = phi {} [ ", &self.dest, &self.to_type)?;
2022+
fmt_operand_value(first_val, f)?;
2023+
write!(f, ", {} ]", first_label)?;
20382024
for (val, label) in &self.incoming_values[1 ..] {
2039-
write!(f, ", [ {}, {} ]", val, label)?;
2025+
write!(f, ", [ ")?;
2026+
fmt_operand_value(val, f)?;
2027+
write!(f, ", {} ]", label)?;
20402028
}
20412029
if self.debugloc.is_some() {
20422030
write!(f, " (with debugloc)")?;
@@ -2141,30 +2129,75 @@ impl Typed for Call {
21412129

21422130
impl Display for Call {
21432131
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2144-
// We choose not to include all the detailed information available in
2145-
// the `Call` struct in this `Display` impl
21462132
if let Some(dest) = &self.dest {
21472133
write!(f, "{} = ", dest)?;
21482134
}
21492135
if self.is_tail_call {
21502136
write!(f, "tail ")?;
21512137
}
2152-
write!(
2153-
f,
2154-
"call {}(",
2155-
match &self.function {
2156-
Either::Left(_) => "<inline assembly>".into(),
2157-
Either::Right(op) => format!("{}", op),
2138+
write!(f, "call ")?;
2139+
let cc = self.calling_convention.to_string();
2140+
if !cc.is_empty() {
2141+
write!(f, "{} ", cc)?;
2142+
}
2143+
for attr in &self.return_attributes {
2144+
let s = attr.to_string();
2145+
if !s.is_empty() {
2146+
write!(f, "{} ", s)?;
21582147
}
2159-
)?;
2160-
for (i, (arg, _)) in self.arguments.iter().enumerate() {
2161-
if i == self.arguments.len() - 1 {
2162-
write!(f, "{}", arg)?;
2163-
} else {
2164-
write!(f, "{}, ", arg)?;
2148+
}
2149+
// For LLVM 15+, emit the function type (or return type for simple calls)
2150+
// For LLVM 14 and lower, the pointer type includes the pointee (function) type
2151+
#[cfg(feature = "llvm-15-or-greater")]
2152+
{
2153+
match self.function_ty.as_ref() {
2154+
Type::FuncType { result_type, is_var_arg, .. } if *is_var_arg => {
2155+
// varargs: must emit the full function type
2156+
write!(f, "{} ", self.function_ty)?;
2157+
},
2158+
Type::FuncType { result_type, .. } => {
2159+
write!(f, "{} ", result_type)?;
2160+
},
2161+
_ => {
2162+
write!(f, "{} ", self.function_ty)?;
2163+
},
21652164
}
21662165
}
2166+
// Write the callee
2167+
match &self.function {
2168+
Either::Left(_) => write!(f, "<inline assembly>")?,
2169+
Either::Right(Operand::ConstantOperand(cref)) => {
2170+
match cref.as_ref() {
2171+
Constant::GlobalReference { name, .. } => {
2172+
match name {
2173+
Name::Name(n) => write!(f, "@{}", n)?,
2174+
Name::Number(n) => write!(f, "@{}", n)?,
2175+
}
2176+
},
2177+
_ => write!(f, "{}", cref)?,
2178+
}
2179+
},
2180+
#[cfg(feature = "llvm-14-or-lower")]
2181+
Either::Right(op) => write!(f, "{}", op)?,
2182+
#[cfg(feature = "llvm-15-or-greater")]
2183+
Either::Right(Operand::LocalOperand { name, .. }) => write!(f, "{}", name)?,
2184+
#[cfg(feature = "llvm-15-or-greater")]
2185+
Either::Right(op) => write!(f, "{}", op)?,
2186+
}
2187+
write!(f, "(")?;
2188+
for (i, (arg, attrs)) in self.arguments.iter().enumerate() {
2189+
if i > 0 {
2190+
write!(f, ", ")?;
2191+
}
2192+
fmt_operand_with_attrs(arg, attrs, f)?;
2193+
}
21672194
write!(f, ")")?;
2195+
for attr in &self.function_attributes {
2196+
let s = attr.to_string();
2197+
if !s.is_empty() {
2198+
write!(f, " {}", s)?;
2199+
}
2200+
}
21682201
if self.debugloc.is_some() {
21692202
write!(f, " (with debugloc)")?;
21702203
}
@@ -2503,7 +2536,6 @@ pub struct LandingPadClause {}
25032536
// from_llvm //
25042537
// ********* //
25052538

2506-
use crate::constant::Constant;
25072539
use crate::from_llvm::*;
25082540
use crate::function::FunctionContext;
25092541
use crate::llvm_sys::*;

0 commit comments

Comments
 (0)