Skip to content

Commit 78a46ef

Browse files
committed
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`
2 parents e366210 + 79bc538 commit 78a46ef

File tree

2 files changed

+18
-3
lines changed

2 files changed

+18
-3
lines changed

compiler/rustc_builtin_macros/src/deriving/debug.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,29 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
6565
// We want to make sure we have the ctxt set so that we can use unstable methods
6666
let span = cx.with_def_site_ctxt(span);
6767
let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked));
68+
let fmt = substr.nonself_args[0].clone();
69+
70+
// Special fast path for unit variants. In the common case of an enum that is entirely unit
71+
// variants (i.e. a C-like enum), this fast path allows LLVM to eliminate the entire switch in
72+
// favor of a lookup table.
73+
if let ast::VariantData::Unit(..) = vdata {
74+
let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
75+
let expr = cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]);
76+
let stmts = vec![cx.stmt_expr(expr)];
77+
let block = cx.block(span, stmts);
78+
return cx.expr_block(block);
79+
}
80+
6881
let builder = Ident::new(sym::debug_trait_builder, span);
6982
let builder_expr = cx.expr_ident(span, builder);
7083

71-
let fmt = substr.nonself_args[0].clone();
72-
7384
let mut stmts = Vec::with_capacity(fields.len() + 2);
7485
let fn_path_finish;
7586
match vdata {
76-
ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => {
87+
ast::VariantData::Unit(..) => {
88+
cx.span_bug(span, "unit variants should have been handled above");
89+
}
90+
ast::VariantData::Tuple(..) => {
7791
// tuple struct/"normal" variant
7892
let fn_path_debug_tuple = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_tuple]);
7993
let expr = cx.expr_call_global(span, fn_path_debug_tuple, vec![fmt, name]);

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,7 @@ symbols! {
14171417
wrapping_sub,
14181418
wreg,
14191419
write_bytes,
1420+
write_str,
14201421
x87_reg,
14211422
xer,
14221423
xmm_reg,

0 commit comments

Comments
 (0)