From a86e698672a331e9003f1a786e67c5e74fed2acb Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Sat, 8 Aug 2026 16:33:25 +0200 Subject: [PATCH 1/4] perf: cranelift-codegen-meta: optimize llvm-lines in `fmtln!` Formatter --- cranelift/srcgen/src/lib.rs | 39 ++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/cranelift/srcgen/src/lib.rs b/cranelift/srcgen/src/lib.rs index 12e30515c009..7c65296be666 100644 --- a/cranelift/srcgen/src/lib.rs +++ b/cranelift/srcgen/src/lib.rs @@ -5,6 +5,7 @@ use std::cmp; use std::collections::{BTreeMap, BTreeSet}; +use std::fmt::Display; use std::fs; use std::io::Write; @@ -43,11 +44,11 @@ impl core::fmt::Display for FileLocation { #[macro_export] macro_rules! fmtln { ($fmt:ident, $fmtstring:expr, $($fmtargs:expr),*) => { - $fmt.line_with_location(format!($fmtstring, $($fmtargs),*), $crate::loc!()) + $fmt.line_with_location(format_args!($fmtstring, $($fmtargs),*), $crate::loc!()) }; ($fmt:ident, $arg:expr) => { - $fmt.line_with_location(format!($arg), $crate::loc!()) + $fmt.line_with_location(format_args!($arg), $crate::loc!()) }; ($_:tt, $($args:expr),+) => { @@ -131,23 +132,28 @@ impl Formatter { } /// Add an indented line. - pub fn line(&mut self, contents: impl AsRef) { - let indented_line = format!("{}{}\n", self.get_indent(), contents.as_ref()); + pub fn line(&mut self, contents: impl Display) { + let indent = self.get_indent(); + let indented_line = format!("{indent}{contents}\n"); self.lines.push(indented_line); } - /// Add an indented lin with a given a `location` appended as a comment to + /// Add an indented line with a given a `location` appended as a comment to /// the line (this is useful for identifying where a line was generated). - pub fn line_with_location(&mut self, contents: impl AsRef, location: FileLocation) { + pub fn line_with_location(&mut self, contents: impl Display, location: FileLocation) { + use std::fmt::Write; + let indent = self.get_indent(); - let contents = contents.as_ref(); - let indented_line = if self.lang.should_append_location(contents) { + let mut buf = String::new(); + + write!(&mut buf, "{indent}{contents}").unwrap(); + // just after writing contents, check ends_with using should_append_location + if self.lang.should_append_location(&buf) { let comment_token = self.lang.comment_token(); - format!("{indent}{contents} {comment_token} {location}\n") - } else { - format!("{indent}{contents}\n") - }; - self.lines.push(indented_line); + write!(&mut buf, " {comment_token} {location}").unwrap(); + } + buf.push('\n'); + self.lines.push(buf); } /// Pushes an empty line. @@ -161,10 +167,11 @@ impl Formatter { } /// Add a comment line. - pub fn comment(&mut self, s: impl AsRef) { + pub fn comment(&mut self, comment: impl Display) { // Avoid `fmtln!` here: we don't want to append a location comment to a // comment. - self.line(format!("{} {}", self.lang.comment_token(), s.as_ref())); + let comment_token = self.lang.comment_token(); + self.line(format!("{comment_token} {comment}")); } /// Add a (multi-line) documentation comment. @@ -186,7 +193,7 @@ impl Formatter { /// }`. This properly indents the contents of the block. pub fn add_block T>(&mut self, start: &str, f: F) -> T { assert!(matches!(self.lang, Language::Rust)); - self.line(format!("{start} {{")); + self.line(format_args!("{start} {{")); let ret = self.indent(f); self.line("}"); ret From ca1a8519eca34e6666a361c631870799890fdcc2 Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Sat, 8 Aug 2026 16:58:26 +0200 Subject: [PATCH 2/4] perf: use format_args! where applicable --- cranelift/codegen/meta/src/gen_inst.rs | 2 +- cranelift/codegen/meta/src/gen_settings.rs | 2 +- cranelift/srcgen/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cranelift/codegen/meta/src/gen_inst.rs b/cranelift/codegen/meta/src/gen_inst.rs index 30dd6f405912..7acf965885c9 100644 --- a/cranelift/codegen/meta/src/gen_inst.rs +++ b/cranelift/codegen/meta/src/gen_inst.rs @@ -951,7 +951,7 @@ fn gen_type_constraints(all_inst: &AllInstructions, fmt: &mut Formatter) { .collect::>() .join(", "))); if let Some(poly) = &inst.polymorphic_info { - fmt.comment(format!("Polymorphic over {}", typeset_to_string(poly.ctrl_typevar.get_raw_typeset()))); + fmt.comment(format_args!("Polymorphic over {}", typeset_to_string(poly.ctrl_typevar.get_raw_typeset()))); } // Compute the bit field encoding, c.f. instructions.rs. diff --git a/cranelift/codegen/meta/src/gen_settings.rs b/cranelift/codegen/meta/src/gen_settings.rs index fe83ea73705c..2c06e16dbb73 100644 --- a/cranelift/codegen/meta/src/gen_settings.rs +++ b/cranelift/codegen/meta/src/gen_settings.rs @@ -317,7 +317,7 @@ fn gen_descriptors(group: &SettingGroup, fmt: &mut Formatter) { ); fmt.indent(|fmt| { for preset in &group.presets { - fmt.comment(format!( + fmt.comment(format_args!( "{}: {}", preset.name, preset.setting_names(group).collect::>().join(", ") diff --git a/cranelift/srcgen/src/lib.rs b/cranelift/srcgen/src/lib.rs index 7c65296be666..8a570a213d38 100644 --- a/cranelift/srcgen/src/lib.rs +++ b/cranelift/srcgen/src/lib.rs @@ -171,7 +171,7 @@ impl Formatter { // Avoid `fmtln!` here: we don't want to append a location comment to a // comment. let comment_token = self.lang.comment_token(); - self.line(format!("{comment_token} {comment}")); + self.line(format_args!("{comment_token} {comment}")); } /// Add a (multi-line) documentation comment. From 2264ffee7ffe6aa469f8b5c15fe6f9f4dda9a55f Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Sat, 8 Aug 2026 19:50:40 +0200 Subject: [PATCH 3/4] perf: cranelift-srcgen: optimize Formatter buffering and run-time of cranelift-codegen (build-script run) --- cranelift/srcgen/src/lib.rs | 73 +++++++++++++++---------------------- 1 file changed, 29 insertions(+), 44 deletions(-) diff --git a/cranelift/srcgen/src/lib.rs b/cranelift/srcgen/src/lib.rs index 8a570a213d38..a3c1f7be8c24 100644 --- a/cranelift/srcgen/src/lib.rs +++ b/cranelift/srcgen/src/lib.rs @@ -11,8 +11,6 @@ use std::io::Write; pub mod error; -static SHIFTWIDTH: usize = 4; - /// A macro for constructing a [`FileLocation`] at the current location. #[macro_export] macro_rules! loc { @@ -85,10 +83,19 @@ impl Language { } } +static SHIFTWIDTH: usize = 4; + +/// Returns 4 times as many spaces. Max indent is 16, which consists of 64 spaces +fn spaces_for_indent(indent: usize) -> &'static str { + // static str of 64 spaces + static INDENT_STR: &'static str = " "; + &INDENT_STR[..INDENT_STR.len().min(indent * SHIFTWIDTH)] +} + /// Collect source code to be written to a file and keep track of indentation. pub struct Formatter { indent: usize, - lines: Vec, + lines: String, lang: Language, } @@ -98,7 +105,7 @@ impl Formatter { pub fn new(lang: Language) -> Self { Self { indent: 0, - lines: Vec::new(), + lines: String::new(), lang, } } @@ -123,19 +130,16 @@ impl Formatter { } /// Get the current whitespace indentation in the form of a String. - fn get_indent(&self) -> String { - if self.indent == 0 { - String::new() - } else { - format!("{:-1$}", " ", self.indent * SHIFTWIDTH) - } + fn get_indent(&self) -> &'static str { + spaces_for_indent(self.indent) } /// Add an indented line. pub fn line(&mut self, contents: impl Display) { + use std::fmt::Write; + let indent = self.get_indent(); - let indented_line = format!("{indent}{contents}\n"); - self.lines.push(indented_line); + write!(&mut self.lines, "{indent}{contents}\n").unwrap(); } /// Add an indented line with a given a `location` appended as a comment to @@ -144,21 +148,19 @@ impl Formatter { use std::fmt::Write; let indent = self.get_indent(); - let mut buf = String::new(); - write!(&mut buf, "{indent}{contents}").unwrap(); + write!(&mut self.lines, "{indent}{contents}").unwrap(); // just after writing contents, check ends_with using should_append_location - if self.lang.should_append_location(&buf) { + if self.lang.should_append_location(&self.lines) { let comment_token = self.lang.comment_token(); - write!(&mut buf, " {comment_token} {location}").unwrap(); + write!(&mut self.lines, " {comment_token} {location}").unwrap(); } - buf.push('\n'); - self.lines.push(buf); + self.lines.push('\n'); } /// Pushes an empty line. pub fn empty_line(&mut self) { - self.lines.push("\n".to_string()); + self.lines.push('\n'); } /// Add one or more lines after stripping common indentation. @@ -248,30 +250,17 @@ impl Formatter { eprintln!("Writing generated file: {}", path.display()); let mut f = fs::File::create(path)?; - for l in self.lines.iter().map(|l| l.as_bytes()) { - f.write_all(l)?; - } - + f.write_all(self.lines.as_bytes())?; Ok(()) } } -/// Compute the indentation of s, or None of an empty line. -fn _indent(s: &str) -> Option { - if s.is_empty() { - None - } else { - let t = s.trim_start(); - Some(s.len() - t.len()) - } -} - /// Given a multi-line string, split it into a sequence of lines after /// stripping a common indentation. This is useful for strings defined with /// doc strings. fn parse_multiline(s: &str) -> Vec { // Convert tabs into spaces. - let expanded_tab = format!("{:-1$}", " ", SHIFTWIDTH); + let expanded_tab = spaces_for_indent(1); let lines: Vec = s.lines().map(|l| l.replace('\t', &expanded_tab)).collect(); // Determine minimum indentation, ignoring the first line and empty lines. @@ -393,12 +382,8 @@ mod srcgen_tests { use super::Match; use super::parse_multiline; - fn from_raw_string>(s: S) -> Vec { - s.into() - .trim() - .split("\n") - .map(|x| format!("{x}\n")) - .collect() + fn from_raw_string>(s: S) -> String { + s.into().trim().to_string() } #[test] @@ -483,7 +468,7 @@ match x { " // Nested comment\n", "Back home again\n", ]; - assert_eq!(fmt.lines, expected_lines); + assert_eq!(fmt.lines, expected_lines.join("")); } #[test] @@ -509,7 +494,7 @@ match x { fn fmt_can_add_type_to_lines() { let mut fmt = Formatter::new(Language::Rust); fmt.line(format!("pub const {}: Type = Type({:#x});", "example", 0)); - let expected_lines = vec!["pub const example: Type = Type(0x0);\n"]; + let expected_lines = "pub const example: Type = Type(0x0);\n"; assert_eq!(fmt.lines, expected_lines); } @@ -520,7 +505,7 @@ match x { fmt.indent_push(); fmt.line("world"); let expected_lines = vec!["hello\n", " world\n"]; - assert_eq!(fmt.lines, expected_lines); + assert_eq!(fmt.lines, expected_lines.join("")); } #[test] @@ -528,7 +513,7 @@ match x { let mut fmt = Formatter::new(Language::Rust); fmt.doc_comment("documentation\nis\ngood"); let expected_lines = vec!["/// documentation\n", "/// is\n", "/// good\n"]; - assert_eq!(fmt.lines, expected_lines); + assert_eq!(fmt.lines, expected_lines.join("")); } #[test] From ac6344cdae42fd337458b36756dd1edec00c7c37 Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Sat, 8 Aug 2026 20:16:11 +0200 Subject: [PATCH 4/4] cranelift-srcgen: test: assert_eq without .trim() --- cranelift/srcgen/src/lib.rs | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/cranelift/srcgen/src/lib.rs b/cranelift/srcgen/src/lib.rs index a3c1f7be8c24..76b5d80418bb 100644 --- a/cranelift/srcgen/src/lib.rs +++ b/cranelift/srcgen/src/lib.rs @@ -382,10 +382,6 @@ mod srcgen_tests { use super::Match; use super::parse_multiline; - fn from_raw_string>(s: S) -> String { - s.into().trim().to_string() - } - #[test] fn adding_arms_works() { let mut m = Match::new("x"); @@ -398,9 +394,7 @@ mod srcgen_tests { let mut fmt = Formatter::new(Language::Rust); fmt.add_match(m); - let expected_lines = from_raw_string( - r#" -match x { + let expected_lines = r#"match x { Green { a, b } => { different body } @@ -412,8 +406,7 @@ match x { some body } } - "#, - ); +"#; assert_eq!(fmt.lines, expected_lines); } @@ -429,9 +422,7 @@ match x { let mut fmt = Formatter::new(Language::Rust); fmt.add_match(m); - let expected_lines = from_raw_string( - r#" -match x { + let expected_lines = r#"match x { Green { a, b } => { different body } @@ -442,8 +433,7 @@ match x { unreachable!() } } - "#, - ); +"#; assert_eq!(fmt.lines, expected_lines); } @@ -526,13 +516,11 @@ match x { If you stick to writing it. "#, ); - let expected_lines = from_raw_string( - r#" -/// documentation + let expected_lines = r#"/// documentation /// can be really good. /// -/// If you stick to writing it."#, - ); +/// If you stick to writing it. +"#; assert_eq!(fmt.lines, expected_lines); } }