From 79bc53870f9fb2b25abffedaa3a4823b974fe69f Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 10 Sep 2021 12:00:22 -0700 Subject: [PATCH] Introduce a fast path that avoids the `debug_tuple` abstraction when deriving Debug for unit-like enum variants. The intent here is to allow LLVM to remove the switch entirely in favor of an indexed load from a table of constant strings, which is likely what the programmer would write in C. Unfortunately, LLVM currently doesn't perform this optimization due to a bug, but there is [a patch](https://reviews.llvm.org/D109565) that fixes this issue. I've verified that, with that patch applied on top of this commit, Debug for unit-like tuple variants becomes a load, reducing the O(n) code bloat to O(1). Note that inlining `DebugTuple::finish()` wasn't enough to allow LLVM to optimize the code properly; I had to avoid the abstraction entirely. Not using the abstraction is likely better for compile time anyway. Part of #88793. --- .../src/deriving/debug.rs | 20 ++++++++++++++++--- compiler/rustc_span/src/symbol.rs | 1 + 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 14506f296bf95..ecf70da6d96c5 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -65,15 +65,29 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> // We want to make sure we have the ctxt set so that we can use unstable methods let span = cx.with_def_site_ctxt(span); let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked)); + let fmt = substr.nonself_args[0].clone(); + + // Special fast path for unit variants. In the common case of an enum that is entirely unit + // variants (i.e. a C-like enum), this fast path allows LLVM to eliminate the entire switch in + // favor of a lookup table. + if let ast::VariantData::Unit(..) = vdata { + let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]); + let expr = cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]); + let stmts = vec![cx.stmt_expr(expr)]; + let block = cx.block(span, stmts); + return cx.expr_block(block); + } + let builder = Ident::new(sym::debug_trait_builder, span); let builder_expr = cx.expr_ident(span, builder); - let fmt = substr.nonself_args[0].clone(); - let mut stmts = Vec::with_capacity(fields.len() + 2); let fn_path_finish; match vdata { - ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => { + ast::VariantData::Unit(..) => { + cx.span_bug(span, "unit variants should have been handled above"); + } + ast::VariantData::Tuple(..) => { // tuple struct/"normal" variant let fn_path_debug_tuple = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_tuple]); let expr = cx.expr_call_global(span, fn_path_debug_tuple, vec![fmt, name]); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 24023163cc30e..e88f86a164a9b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1418,6 +1418,7 @@ symbols! { wrapping_sub, wreg, write_bytes, + write_str, x87_reg, xer, xmm_reg,