Skip to content

Commit

Permalink
Auto merge of rust-lang#108440 - Zoxc:encoder-enum, r=cjgillot
Browse files Browse the repository at this point in the history
Emit the enum discriminant separately for the Encodable macro

This changes the `Encodable` proc macro to first emit the discriminant with a separate `match` statement, then emit the fields. LLVM can optimize down the first `match` to just a read of the enum discriminant. This avoids generating a `emit_usize` call per variant and enums with no fields now optimize down to just a single `emit_usize` call.

The no variant case is special cased to avoid warnings about unreachable code.

<table><tr><td rowspan="2">Benchmark</td><td colspan="1"><b>Before</b></th><td colspan="2"><b>After</b></th></tr><tr><td align="right">Time</td><td align="right">Time</td><td align="right">%</th></tr><tr><td>🟣 <b>clap</b>:check:unchanged</td><td align="right">0.4676s</td><td align="right">0.4640s</td><td align="right"> -0.78%</td></tr><tr><td>🟣 <b>hyper</b>:check:unchanged</td><td align="right">0.1348s</td><td align="right">0.1338s</td><td align="right"> -0.75%</td></tr><tr><td>🟣 <b>regex</b>:check:unchanged</td><td align="right">0.3370s</td><td align="right">0.3352s</td><td align="right"> -0.54%</td></tr><tr><td>🟣 <b>syn</b>:check:unchanged</td><td align="right">0.6326s</td><td align="right">0.6281s</td><td align="right"> -0.71%</td></tr><tr><td>🟣 <b>syntex_syntax</b>:check:unchanged</td><td align="right">1.8561s</td><td align="right">1.8589s</td><td align="right"> 0.15%</td></tr><tr><td>Total</td><td align="right">3.4282s</td><td align="right">3.4200s</td><td align="right"> -0.24%</td></tr><tr><td>Summary</td><td align="right">1.0000s</td><td align="right">0.9947s</td><td align="right"> -0.53%</td></tr></table>
  • Loading branch information
bors committed Feb 26, 2023
2 parents d962ea5 + 7de205e commit 58136ff
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 29 deletions.
47 changes: 30 additions & 17 deletions compiler/rustc_macros/src/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ fn decodable_body(
}
let ty_name = s.ast().ident.to_string();
let decode_body = match s.variants() {
[] => {
let message = format!("`{}` has no variants to decode", ty_name);
quote! {
panic!(#message)
}
}
[vi] => vi.construct(|field, _index| decode_field(field)),
variants => {
let match_inner: TokenStream = variants
Expand Down Expand Up @@ -139,6 +145,11 @@ fn encodable_body(
});

let encode_body = match s.variants() {
[] => {
quote! {
match *self {}
}
}
[_] => {
let encode_inner = s.each_variant(|vi| {
vi.bindings()
Expand All @@ -160,6 +171,23 @@ fn encodable_body(
}
}
_ => {
let disc = {
let mut variant_idx = 0usize;
let encode_inner = s.each_variant(|_| {
let result = quote! {
#variant_idx
};
variant_idx += 1;
result
});
quote! {
let disc = match *self {
#encode_inner
};
::rustc_serialize::Encoder::emit_usize(__encoder, disc);
}
};

let mut variant_idx = 0usize;
let encode_inner = s.each_variant(|vi| {
let encode_fields: TokenStream = vi
Expand All @@ -176,26 +204,11 @@ fn encodable_body(
result
})
.collect();

let result = if !vi.bindings().is_empty() {
quote! {
::rustc_serialize::Encoder::emit_enum_variant(
__encoder,
#variant_idx,
|__encoder| { #encode_fields }
)
}
} else {
quote! {
::rustc_serialize::Encoder::emit_fieldless_enum_variant::<#variant_idx>(
__encoder,
)
}
};
variant_idx += 1;
result
encode_fields
});
quote! {
#disc
match *self {
#encode_inner
}
Expand Down
12 changes: 0 additions & 12 deletions compiler/rustc_serialize/src/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,13 @@ pub trait Encoder {
fn emit_str(&mut self, v: &str);
fn emit_raw_bytes(&mut self, s: &[u8]);

// Convenience for the derive macro:
fn emit_enum_variant<F>(&mut self, v_id: usize, f: F)
where
F: FnOnce(&mut Self),
{
self.emit_usize(v_id);
f(self);
}

// We put the field index in a const generic to allow the emit_usize to be
// compiled into a more efficient form. In practice, the variant index is
// known at compile-time, and that knowledge allows much more efficient
// codegen than we'd otherwise get. LLVM isn't always able to make the
// optimization that would otherwise be necessary here, likely due to the
// multiple levels of inlining and const-prop that are needed.
#[inline]
fn emit_fieldless_enum_variant<const ID: usize>(&mut self) {
self.emit_usize(ID)
}
}

// Note: all the methods in this trait are infallible, which may be surprising.
Expand Down

0 comments on commit 58136ff

Please sign in to comment.