Skip to content

Commit

Permalink
Auto merge of #131663 - veera-sivarajan:fix-128709-final, r=<try>
Browse files Browse the repository at this point in the history
Evaluate `std::fmt::Arguments::new_const()` during Compile Time

Fixes #128709

This PR aims to optimize calls to string formating macros without any arguments by evaluating `std::fmt::Arguments::new_const()` in a const context.

Currently,
`println!("hola")` compiles to `std::io::_print(std::fmt::Arguments::new_const(&["hola\n"]))`.

With this PR,
`println!("hola")` compiles to `std::io::_print(const { std::fmt::Arguments::new_const(&["hola\n"]) })`.

This is accomplished in two steps:

1.  Const stabilize `std::fmt::Arguments::new_const()`.
2.  Wrap calls to `std::fmt::Arguments::new_const()` in an inline const block when lowering the AST to HIR.

This reduces the generated code to a `memcpy` instead of multiple `getelementptr` and `store` instructions even with `-C no-prepopulate-passes -C opt-level=0`. Godbolt for code comparison: https://rust.godbolt.org/z/P7Px7de6c

This is a safe and sound transformation because `std::fmt::Arguments::new_const()` is a trivial constructor function taking a slice containing a `'static` string literal as input.

CC #99012
  • Loading branch information
bors committed Oct 14, 2024
2 parents 17a19e6 + 95b1d81 commit 814ddfe
Show file tree
Hide file tree
Showing 22 changed files with 152 additions and 124 deletions.
27 changes: 25 additions & 2 deletions compiler/rustc_ast_lowering/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use rustc_ast::visit::Visitor;
use rustc_ast::*;
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_session::config::FmtDebug;
use rustc_span::symbol::{Ident, kw};
use rustc_span::{Span, Symbol, sym};
Expand All @@ -24,6 +25,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
expand_format_args(self, sp, &fmt, allow_const)
}

/// Wraps given `ExprKind` in an inline const block.
///
/// Caller must ensure it's safe and sound to do so.
fn wrap_in_const_context(
&mut self,
sp: Span,
kind: hir::ExprKind<'hir>,
) -> hir::ExprKind<'hir> {
let expr = hir::Expr { hir_id: self.next_id(), kind, span: self.lower_span(sp) };
let const_node_id = self.next_node_id();
let parent_def_id = self.current_def_id_parent;
let def_id =
self.create_def(parent_def_id, const_node_id, kw::Empty, DefKind::InlineConst, sp);
let hir_id = self.lower_node_id(const_node_id);
let const_block = self.with_new_scopes(sp, |this| hir::ConstBlock {
def_id,
hir_id,
body: this.with_def_id_parent(def_id, |this| this.lower_body(|_| (&[], expr))),
});
hir::ExprKind::ConstBlock(const_block)
}

/// Try to convert a literal into an interned string
fn try_inline_lit(&self, lit: token::Lit) -> Option<Symbol> {
match LitKind::from_token_lit(lit) {
Expand Down Expand Up @@ -464,14 +487,14 @@ fn expand_format_args<'hir>(

if allow_const && arguments.is_empty() && argmap.is_empty() {
// Generate:
// <core::fmt::Arguments>::new_const(lit_pieces)
// const { <core::fmt::Arguments>::new_const(lit_pieces) }
let new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
macsp,
hir::LangItem::FormatArguments,
sym::new_const,
));
let new_args = ctx.arena.alloc_from_iter([lit_pieces]);
return hir::ExprKind::Call(new, new_args);
return ctx.wrap_in_const_context(macsp, hir::ExprKind::Call(new, new_args));
}

// If the args array contains exactly all the original arguments once,
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ pub struct Arguments<'a> {
#[unstable(feature = "fmt_internals", issue = "none")]
impl<'a> Arguments<'a> {
#[inline]
#[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")]
#[rustc_const_stable(feature = "const_fmt_arguments_new", since = "CURRENT_RUSTC_VERSION")]
pub const fn new_const<const N: usize>(pieces: &'a [&'static str; N]) -> Self {
const { assert!(N <= 1) };
Arguments { pieces, fmt: None, args: &[] }
Expand Down
1 change: 0 additions & 1 deletion library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@
#![feature(const_char_encode_utf16)]
#![feature(const_eval_select)]
#![feature(const_exact_div)]
#![feature(const_fmt_arguments_new)]
#![feature(const_hash)]
#![feature(const_heap)]
#![feature(const_index_range_slice_index)]
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/macros/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1038,7 +1038,7 @@ pub(crate) mod builtin {
///
/// This macro will be removed once `format_args` is allowed in const contexts.
#[unstable(feature = "const_format_args", issue = "none")]
#[allow_internal_unstable(fmt_internals, const_fmt_arguments_new)]
#[allow_internal_unstable(fmt_internals)]
#[rustc_builtin_macro]
#[macro_export]
macro_rules! const_format_args {
Expand Down
17 changes: 17 additions & 0 deletions tests/codegen/issues/issue-128709-format-without-args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//@ compile-flags: -C no-prepopulate-passes -C opt-level=0

#![crate_type = "lib"]

// String formating macros without any arguments should compile
// to a `memcpy` followed by a call to a library function.

#[no_mangle]
pub fn code() {
// CHECK-LABEL: @code
// CHECK-NOT: getelementptr
// CHECK-NOT: store
// CHECK-NOT: ; call core::fmt::Arguments::new_const
// CHECK: call void @llvm.memcpy
// CHECK-NEXT: ; call std::io::stdio::_print
println!("hello world");
}
13 changes: 5 additions & 8 deletions tests/coverage/assert.cov-map
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,12 @@ Number of file 0 mappings: 9
Highest counter ID seen: c4

Function name: assert::might_fail_assert
Raw bytes (21): 0x[01, 01, 01, 01, 05, 03, 01, 04, 01, 02, 0f, 02, 02, 25, 00, 3d, 05, 01, 01, 00, 02]
Raw bytes (14): 0x[01, 01, 00, 02, 01, 04, 01, 02, 3e, 05, 03, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
Number of file 0 mappings: 3
- Code(Counter(0)) at (prev + 4, 1) to (start + 2, 15)
- Code(Expression(0, Sub)) at (prev + 2, 37) to (start + 0, 61)
= (c0 - c1)
- Code(Counter(1)) at (prev + 1, 1) to (start + 0, 2)
Number of expressions: 0
Number of file 0 mappings: 2
- Code(Counter(0)) at (prev + 4, 1) to (start + 2, 62)
- Code(Counter(1)) at (prev + 3, 1) to (start + 0, 2)
Highest counter ID seen: c1

42 changes: 18 additions & 24 deletions tests/coverage/bad_counter_ids.cov-map
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,13 @@ Number of file 0 mappings: 2
Highest counter ID seen: c0

Function name: bad_counter_ids::eq_bad_message
Raw bytes (21): 0x[01, 01, 01, 01, 00, 03, 01, 29, 01, 02, 0f, 02, 02, 20, 00, 2b, 00, 01, 01, 00, 02]
Raw bytes (14): 0x[01, 01, 00, 02, 01, 29, 01, 02, 2c, 00, 03, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
- expression 0 operands: lhs = Counter(0), rhs = Zero
Number of file 0 mappings: 3
- Code(Counter(0)) at (prev + 41, 1) to (start + 2, 15)
- Code(Expression(0, Sub)) at (prev + 2, 32) to (start + 0, 43)
= (c0 - Zero)
- Code(Zero) at (prev + 1, 1) to (start + 0, 2)
Number of expressions: 0
Number of file 0 mappings: 2
- Code(Counter(0)) at (prev + 41, 1) to (start + 2, 44)
- Code(Zero) at (prev + 3, 1) to (start + 0, 2)
Highest counter ID seen: c0

Function name: bad_counter_ids::eq_good
Expand All @@ -32,14 +29,13 @@ Number of file 0 mappings: 2
Highest counter ID seen: c1

Function name: bad_counter_ids::eq_good_message
Raw bytes (19): 0x[01, 01, 00, 03, 01, 15, 01, 02, 0f, 00, 02, 20, 00, 2b, 05, 01, 01, 00, 02]
Raw bytes (14): 0x[01, 01, 00, 02, 01, 15, 01, 02, 2c, 05, 03, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 3
- Code(Counter(0)) at (prev + 21, 1) to (start + 2, 15)
- Code(Zero) at (prev + 2, 32) to (start + 0, 43)
- Code(Counter(1)) at (prev + 1, 1) to (start + 0, 2)
Number of file 0 mappings: 2
- Code(Counter(0)) at (prev + 21, 1) to (start + 2, 44)
- Code(Counter(1)) at (prev + 3, 1) to (start + 0, 2)
Highest counter ID seen: c1

Function name: bad_counter_ids::ne_bad
Expand All @@ -53,15 +49,14 @@ Number of file 0 mappings: 2
Highest counter ID seen: c0

Function name: bad_counter_ids::ne_bad_message
Raw bytes (19): 0x[01, 01, 00, 03, 01, 33, 01, 02, 0f, 05, 02, 20, 00, 2b, 00, 01, 01, 00, 02]
Raw bytes (14): 0x[01, 01, 00, 02, 01, 33, 01, 02, 2c, 00, 03, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 3
- Code(Counter(0)) at (prev + 51, 1) to (start + 2, 15)
- Code(Counter(1)) at (prev + 2, 32) to (start + 0, 43)
- Code(Zero) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c1
Number of file 0 mappings: 2
- Code(Counter(0)) at (prev + 51, 1) to (start + 2, 44)
- Code(Zero) at (prev + 3, 1) to (start + 0, 2)
Highest counter ID seen: c0

Function name: bad_counter_ids::ne_good
Raw bytes (16): 0x[01, 01, 01, 01, 00, 02, 01, 1a, 01, 02, 1f, 02, 03, 01, 00, 02]
Expand All @@ -76,15 +71,14 @@ Number of file 0 mappings: 2
Highest counter ID seen: c0

Function name: bad_counter_ids::ne_good_message
Raw bytes (21): 0x[01, 01, 01, 01, 00, 03, 01, 1f, 01, 02, 0f, 00, 02, 20, 00, 2b, 02, 01, 01, 00, 02]
Raw bytes (16): 0x[01, 01, 01, 01, 00, 02, 01, 1f, 01, 02, 2c, 02, 03, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
- expression 0 operands: lhs = Counter(0), rhs = Zero
Number of file 0 mappings: 3
- Code(Counter(0)) at (prev + 31, 1) to (start + 2, 15)
- Code(Zero) at (prev + 2, 32) to (start + 0, 43)
- Code(Expression(0, Sub)) at (prev + 1, 1) to (start + 0, 2)
Number of file 0 mappings: 2
- Code(Counter(0)) at (prev + 31, 1) to (start + 2, 44)
- Code(Expression(0, Sub)) at (prev + 3, 1) to (start + 0, 2)
= (c0 - Zero)
Highest counter ID seen: c0

9 changes: 4 additions & 5 deletions tests/coverage/closure_macro.cov-map
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,19 @@ Number of file 0 mappings: 6
Highest counter ID seen: c1

Function name: closure_macro::main::{closure#0}
Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 0d, 05, 01, 10, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 0d, 00, 17, 00, 1e, 07, 02, 09, 00, 0a]
Raw bytes (30): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 0d, 04, 01, 10, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 1f, 07, 02, 09, 00, 0a]
Number of files: 1
- file 0 => global file 1
Number of expressions: 3
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
- expression 1 operands: lhs = Counter(1), rhs = Expression(2, Add)
- expression 2 operands: lhs = Counter(2), rhs = Counter(3)
Number of file 0 mappings: 5
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 16, 28) to (start + 3, 33)
- Code(Counter(1)) at (prev + 4, 17) to (start + 1, 39)
- Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 22)
- Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 31)
= (c0 - c1)
- Code(Counter(3)) at (prev + 0, 23) to (start + 0, 30)
- Code(Expression(1, Add)) at (prev + 2, 9) to (start + 0, 10)
= (c1 + (c2 + c3))
Highest counter ID seen: c3
Highest counter ID seen: c1

9 changes: 4 additions & 5 deletions tests/coverage/closure_macro_async.cov-map
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,19 @@ Number of file 0 mappings: 6
Highest counter ID seen: c1

Function name: closure_macro_async::test::{closure#0}::{closure#0}
Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 0d, 05, 01, 15, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 0d, 00, 17, 00, 1e, 07, 02, 09, 00, 0a]
Raw bytes (30): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 0d, 04, 01, 15, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 1f, 07, 02, 09, 00, 0a]
Number of files: 1
- file 0 => global file 1
Number of expressions: 3
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
- expression 1 operands: lhs = Counter(1), rhs = Expression(2, Add)
- expression 2 operands: lhs = Counter(2), rhs = Counter(3)
Number of file 0 mappings: 5
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 21, 28) to (start + 3, 33)
- Code(Counter(1)) at (prev + 4, 17) to (start + 1, 39)
- Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 22)
- Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 31)
= (c0 - c1)
- Code(Counter(3)) at (prev + 0, 23) to (start + 0, 30)
- Code(Expression(1, Add)) at (prev + 2, 9) to (start + 0, 10)
= (c1 + (c2 + c3))
Highest counter ID seen: c3
Highest counter ID seen: c1

Loading

0 comments on commit 814ddfe

Please sign in to comment.