about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-09-17 01:00:11 +0000
committerbors <bors@rust-lang.org>2021-09-17 01:00:11 +0000
commit78a46efff06558674b51c10d8d81758285746ab5 (patch)
tree74bcd0d53e31967dc52657f64da245acdd61f102
parente36621057d9f497c822eb800934b5933c10510cf (diff)
parent79bc53870f9fb2b25abffedaa3a4823b974fe69f (diff)
downloadrust-78a46efff06558674b51c10d8d81758285746ab5.tar.gz
rust-78a46efff06558674b51c10d8d81758285746ab5.zip
Auto merge of #88832 - pcwalton:debug-unit-variant-fast-path, r=oli-obk
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.

r? `@oli-obk`
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/debug.rs20
-rw-r--r--compiler/rustc_span/src/symbol.rs1
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 14506f296bf..ecf70da6d96 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 d14feecbbdf..760357644a5 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1417,6 +1417,7 @@ symbols! {
         wrapping_sub,
         wreg,
         write_bytes,
+        write_str,
         x87_reg,
         xer,
         xmm_reg,