Auto merge of #26280 - Marwes:deriving_discriminant, r=pcwalton
PR for #26128. Improves codegen in deriving by utilizing the discriminant_value intrinsic. I have a more detailed comment on the changes in a comment on the issue [here](https://github.com/rust-lang/rust/issues/26128#issuecomment-111509863) ### Old ``` running 7 tests test large_c_like ... bench: 2,694,129 ns/iter (+/- 5,170) test large_c_like_ord ... bench: 2,723,521 ns/iter (+/- 9,098) test test1_partial_eq ... bench: 2,439,317 ns/iter (+/- 2,135) test test1_partial_ord ... bench: 2,499,114 ns/iter (+/- 55,766) test test2_partial_eq ... bench: 3,562,815 ns/iter (+/- 45,590) test test2_partial_ord ... bench: 3,398,306 ns/iter (+/- 22,180) test test_match_success ... bench: 1,509,267 ns/iter (+/- 1,982) ``` ### New ``` running 7 tests test large_c_like ... bench: 286,509 ns/iter (+/- 474) test large_c_like_ord ... bench: 286,666 ns/iter (+/- 8,756) test test1_partial_eq ... bench: 286,584 ns/iter (+/- 2,789) test test1_partial_ord ... bench: 286,470 ns/iter (+/- 516) test test2_partial_eq ... bench: 2,228,997 ns/iter (+/- 34,191) test test2_partial_ord ... bench: 1,731,699 ns/iter (+/- 21,756) test test_match_success ... bench: 1,509,630 ns/iter (+[- 3,765) ``` [Benchmark](https://gist.github.com/Marwes/7c0b3468d0cae972a2b4)
This commit is contained in:
commit
014a5c12ac
1 changed files with 87 additions and 39 deletions
|
@ -1042,22 +1042,32 @@ impl<'a> MethodDef<'a> {
|
||||||
/// variants where all of the variants match, and one catch-all for
|
/// variants where all of the variants match, and one catch-all for
|
||||||
/// when one does not match.
|
/// when one does not match.
|
||||||
|
|
||||||
|
/// As an optimization we generate code which checks whether all variants
|
||||||
|
/// match first which makes llvm see that C-like enums can be compiled into
|
||||||
|
/// a simple equality check (for PartialEq).
|
||||||
|
|
||||||
/// The catch-all handler is provided access the variant index values
|
/// The catch-all handler is provided access the variant index values
|
||||||
/// for each of the self-args, carried in precomputed variables. (Nota
|
/// for each of the self-args, carried in precomputed variables.
|
||||||
/// bene: the variant index values are not necessarily the
|
|
||||||
/// discriminant values. See issue #15523.)
|
|
||||||
|
|
||||||
/// ```{.text}
|
/// ```{.text}
|
||||||
/// match (this, that, ...) {
|
/// let __self0_vi = unsafe {
|
||||||
/// (Variant1, Variant1, Variant1) => ... // delegate Matching on Variant1
|
/// std::intrinsics::discriminant_value(&self) } as i32;
|
||||||
/// (Variant2, Variant2, Variant2) => ... // delegate Matching on Variant2
|
/// let __self1_vi = unsafe {
|
||||||
|
/// std::intrinsics::discriminant_value(&__arg1) } as i32;
|
||||||
|
/// let __self2_vi = unsafe {
|
||||||
|
/// std::intrinsics::discriminant_value(&__arg2) } as i32;
|
||||||
|
///
|
||||||
|
/// if __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... {
|
||||||
|
/// match (...) {
|
||||||
|
/// (Variant1, Variant1, ...) => Body1
|
||||||
|
/// (Variant2, Variant2, ...) => Body2,
|
||||||
/// ...
|
/// ...
|
||||||
/// _ => {
|
/// _ => ::core::intrinsics::unreachable()
|
||||||
/// let __this_vi = match this { Variant1 => 0, Variant2 => 1, ... };
|
|
||||||
/// let __that_vi = match that { Variant1 => 0, Variant2 => 1, ... };
|
|
||||||
/// ... // catch-all remainder can inspect above variant index values.
|
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
|
/// else {
|
||||||
|
/// ... // catch-all remainder can inspect above variant index values.
|
||||||
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
fn build_enum_match_tuple<'b>(
|
fn build_enum_match_tuple<'b>(
|
||||||
&self,
|
&self,
|
||||||
|
@ -1187,7 +1197,6 @@ impl<'a> MethodDef<'a> {
|
||||||
|
|
||||||
cx.arm(sp, vec![single_pat], arm_expr)
|
cx.arm(sp, vec![single_pat], arm_expr)
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
// We will usually need the catch-all after matching the
|
// We will usually need the catch-all after matching the
|
||||||
// tuples `(VariantK, VariantK, ...)` for each VariantK of the
|
// tuples `(VariantK, VariantK, ...)` for each VariantK of the
|
||||||
// enum. But:
|
// enum. But:
|
||||||
|
@ -1223,9 +1232,14 @@ impl<'a> MethodDef<'a> {
|
||||||
// ```
|
// ```
|
||||||
let mut index_let_stmts: Vec<P<ast::Stmt>> = Vec::new();
|
let mut index_let_stmts: Vec<P<ast::Stmt>> = Vec::new();
|
||||||
|
|
||||||
|
//We also build an expression which checks whether all discriminants are equal
|
||||||
|
// discriminant_test = __self0_vi == __self1_vi && __self0_vi == __self2_vi && ...
|
||||||
|
let mut discriminant_test = cx.expr_bool(sp, true);
|
||||||
|
|
||||||
let target_type_name =
|
let target_type_name =
|
||||||
find_repr_type_name(&cx.parse_sess.span_diagnostic, type_attrs);
|
find_repr_type_name(&cx.parse_sess.span_diagnostic, type_attrs);
|
||||||
|
|
||||||
|
let mut first_ident = None;
|
||||||
for (&ident, self_arg) in vi_idents.iter().zip(&self_args) {
|
for (&ident, self_arg) in vi_idents.iter().zip(&self_args) {
|
||||||
let path = vec![cx.ident_of_std("core"),
|
let path = vec![cx.ident_of_std("core"),
|
||||||
cx.ident_of("intrinsics"),
|
cx.ident_of("intrinsics"),
|
||||||
|
@ -1243,32 +1257,64 @@ impl<'a> MethodDef<'a> {
|
||||||
let variant_disr = cx.expr_cast(sp, variant_value, target_ty);
|
let variant_disr = cx.expr_cast(sp, variant_value, target_ty);
|
||||||
let let_stmt = cx.stmt_let(sp, false, ident, variant_disr);
|
let let_stmt = cx.stmt_let(sp, false, ident, variant_disr);
|
||||||
index_let_stmts.push(let_stmt);
|
index_let_stmts.push(let_stmt);
|
||||||
|
|
||||||
|
match first_ident {
|
||||||
|
Some(first) => {
|
||||||
|
let first_expr = cx.expr_ident(sp, first);
|
||||||
|
let id = cx.expr_ident(sp, ident);
|
||||||
|
let test = cx.expr_binary(sp, ast::BiEq, first_expr, id);
|
||||||
|
discriminant_test = cx.expr_binary(sp, ast::BiAnd, discriminant_test, test)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
first_ident = Some(ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let arm_expr = self.call_substructure_method(
|
let arm_expr = self.call_substructure_method(
|
||||||
cx, trait_, type_ident, &self_args[..], nonself_args,
|
cx, trait_, type_ident, &self_args[..], nonself_args,
|
||||||
&catch_all_substructure);
|
&catch_all_substructure);
|
||||||
|
|
||||||
// Builds the expression:
|
//Since we know that all the arguments will match if we reach the match expression we
|
||||||
// {
|
//add the unreachable intrinsics as the result of the catch all which should help llvm
|
||||||
// let __self0_vi = ...;
|
//in optimizing it
|
||||||
// let __self1_vi = ...;
|
let path = vec![cx.ident_of_std("core"),
|
||||||
|
cx.ident_of("intrinsics"),
|
||||||
|
cx.ident_of("unreachable")];
|
||||||
|
let call = cx.expr_call_global(
|
||||||
|
sp, path, vec![]);
|
||||||
|
let unreachable = cx.expr_block(P(ast::Block {
|
||||||
|
stmts: vec![],
|
||||||
|
expr: Some(call),
|
||||||
|
id: ast::DUMMY_NODE_ID,
|
||||||
|
rules: ast::UnsafeBlock(ast::CompilerGenerated),
|
||||||
|
span: sp }));
|
||||||
|
match_arms.push(cx.arm(sp, vec![cx.pat_wild(sp)], unreachable));
|
||||||
|
|
||||||
|
// Final wrinkle: the self_args are expressions that deref
|
||||||
|
// down to desired l-values, but we cannot actually deref
|
||||||
|
// them when they are fed as r-values into a tuple
|
||||||
|
// expression; here add a layer of borrowing, turning
|
||||||
|
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
|
||||||
|
let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg));
|
||||||
|
let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));
|
||||||
|
|
||||||
|
//Lastly we create an expression which branches on all discriminants being equal
|
||||||
|
// if discriminant_test {
|
||||||
|
// match (...) {
|
||||||
|
// (Variant1, Variant1, ...) => Body1
|
||||||
|
// (Variant2, Variant2, ...) => Body2,
|
||||||
// ...
|
// ...
|
||||||
|
// _ => ::core::intrinsics::unreachable()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
// <delegated expression referring to __self0_vi, et al.>
|
// <delegated expression referring to __self0_vi, et al.>
|
||||||
// }
|
// }
|
||||||
let arm_expr = cx.expr_block(
|
let all_match = cx.expr_match(sp, match_arg, match_arms);
|
||||||
cx.block_all(sp, index_let_stmts, Some(arm_expr)));
|
let arm_expr = cx.expr_if(sp, discriminant_test, all_match, Some(arm_expr));
|
||||||
|
cx.expr_block(
|
||||||
// Builds arm:
|
cx.block_all(sp, index_let_stmts, Some(arm_expr)))
|
||||||
// _ => { let __self0_vi = ...;
|
|
||||||
// let __self1_vi = ...;
|
|
||||||
// ...
|
|
||||||
// <delegated expression as above> }
|
|
||||||
let catch_all_match_arm =
|
|
||||||
cx.arm(sp, vec![cx.pat_wild(sp)], arm_expr);
|
|
||||||
|
|
||||||
match_arms.push(catch_all_match_arm);
|
|
||||||
|
|
||||||
} else if variants.is_empty() {
|
} else if variants.is_empty() {
|
||||||
// As an additional wrinkle, For a zero-variant enum A,
|
// As an additional wrinkle, For a zero-variant enum A,
|
||||||
// currently the compiler
|
// currently the compiler
|
||||||
|
@ -1319,8 +1365,9 @@ impl<'a> MethodDef<'a> {
|
||||||
// derive Debug on such a type could here generate code
|
// derive Debug on such a type could here generate code
|
||||||
// that needs the feature gate enabled.)
|
// that needs the feature gate enabled.)
|
||||||
|
|
||||||
return cx.expr_unreachable(sp);
|
cx.expr_unreachable(sp)
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
// Final wrinkle: the self_args are expressions that deref
|
// Final wrinkle: the self_args are expressions that deref
|
||||||
// down to desired l-values, but we cannot actually deref
|
// down to desired l-values, but we cannot actually deref
|
||||||
|
@ -1331,6 +1378,7 @@ impl<'a> MethodDef<'a> {
|
||||||
let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));
|
let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));
|
||||||
cx.expr_match(sp, match_arg, match_arms)
|
cx.expr_match(sp, match_arg, match_arms)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn expand_static_enum_method_body(&self,
|
fn expand_static_enum_method_body(&self,
|
||||||
cx: &mut ExtCtxt,
|
cx: &mut ExtCtxt,
|
||||||
|
|
Loading…
Add table
Reference in a new issue