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:
bors 2015-06-16 22:20:15 +00:00
commit 014a5c12ac
1 changed files with 87 additions and 39 deletions

View File

@ -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,