Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use discriminant_value intrinsic for derive(PartialOrd) #24270

Merged
merged 10 commits into from
Apr 11, 2015

Conversation

pnkfelix
Copy link
Member

Use discriminant_value intrinsic for derive(PartialOrd)

[breaking-change]

This is a [breaking-change] because it can change the result of comparison operators when enum discriminants have been explicitly assigned. Notably in a case like:

#[derive(PartialOrd)]
enum E { A = 2, B = 1}

Under the old deriving, A < B held, because A came before B in the order of declaration. But now we use the ordering according to the provided values, and thus A > B. (However, this change is very unlikely to break much, if any, code, since the orderings themselves should all remain well-defined, total, etc.)

Fix #15523

Aatch and others added 4 commits April 10, 2015 12:23
Implements an intrinsic for extracting the value of the discriminant
enum variant values. For non-enum types, this returns zero, otherwise it
returns the value we use for discriminant comparisons. This means that
enum types that do not have a discriminant will also work in this
arrangement.

This is (at least part of) the work on Issue rust-lang#24263
@rust-highfive
Copy link
Collaborator

r? @brson

(rust_highfive has picked a reviewer for you, use r? to override)

@pnkfelix
Copy link
Member Author

r? @huonw

@rust-highfive rust-highfive assigned huonw and unassigned brson Apr 10, 2015
@pnkfelix pnkfelix force-pushed the use-disr-val-for-derive-ord branch from 3519318 to afb7acf Compare April 10, 2015 14:32
rules: ast::UnsafeBlock(ast::CompilerGenerated),
span: sp }));

// FIXME: This unconditionally casts to `isize`. However:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops this FIXME is now out-of-date. Will fix and push -f.

@pnkfelix pnkfelix force-pushed the use-disr-val-for-derive-ord branch from e9dbfae to 47016f9 Compare April 10, 2015 17:11
ast::TupleVariantKind(..) => ast::PatEnum(path, None),
ast::StructVariantKind(..) => ast::PatStruct(path, Vec::new(), true),
})
fn find_repr_type_name(diagnostic: &SpanHandler,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like there's a chance that this could miss a repr attribute that is applied by a later pass of expansion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very interesting, I'm not even sure how we can handle that ... without some somewhat serious revisions to the expansion infrastructure (e.g. some way to schedule an expansion "at the end" ...)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The most conservative solution would be to change the intrinsic to return (sign, abs-value) to avoid having to figure out the repr at all, but that does make the comparisons a bit less efficient depending on how smart LLVM can be.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a simpler option. The intrinsic could be:

  • compare_variant_order<T:Reflect>(t: &T, u: &T)

which would yield a -1,0,1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(well, we may still want the discriminant_value intrinsic itself independently, but I am inclined to agree that this is an elegant way to resolve all of the issues here.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yeah, that's a good call.

@brson
Copy link
Contributor

brson commented Apr 10, 2015

@bors r+ p=1

Though I agree about @sfackler's point I'm assuming it's not a show-stopper for the entire feature, and don't think it's going to be fixed right now.

@bors
Copy link
Contributor

bors commented Apr 10, 2015

📌 Commit 847a897 has been approved by brson

@bors
Copy link
Contributor

bors commented Apr 10, 2015

⌛ Testing commit 847a897 with merge c35ffe7...

@bors
Copy link
Contributor

bors commented Apr 10, 2015

⛄ The build was interrupted to prioritize another pull request.

@bors
Copy link
Contributor

bors commented Apr 10, 2015

⌛ Testing commit 847a897 with merge 0877916...

bors added a commit that referenced this pull request Apr 10, 2015
Use `discriminant_value` intrinsic for `derive(PartialOrd)`

[breaking-change]

This is a [breaking-change] because it can change the result of comparison operators when enum discriminants have been explicitly assigned.  Notably in a case like:
```rust
#[derive(PartialOrd)]
enum E { A = 2, B = 1}
```

Under the old deriving, `A < B` held, because `A` came before `B` in the order of declaration.  But now we use the ordering according to the provided values, and thus `A > B`.  (However, this change is very unlikely to break much, if any, code, since the orderings themselves should all remain well-defined, total, etc.)

Fix #15523
@bors
Copy link
Contributor

bors commented Apr 10, 2015

💔 Test failed - auto-mac-32-opt

@pnkfelix
Copy link
Member Author

@bors r=brson 0877916

@bors
Copy link
Contributor

bors commented Apr 10, 2015

🙀 0877916 is not a valid commit SHA. Please try again with 05aaad1.

@pnkfelix
Copy link
Member Author

@bors r=brson 05aaad1 p=1

bors added a commit that referenced this pull request Apr 10, 2015
Use `discriminant_value` intrinsic for `derive(PartialOrd)`

[breaking-change]

This is a [breaking-change] because it can change the result of comparison operators when enum discriminants have been explicitly assigned.  Notably in a case like:
```rust
#[derive(PartialOrd)]
enum E { A = 2, B = 1}
```

Under the old deriving, `A < B` held, because `A` came before `B` in the order of declaration.  But now we use the ordering according to the provided values, and thus `A > B`.  (However, this change is very unlikely to break much, if any, code, since the orderings themselves should all remain well-defined, total, etc.)

Fix #15523
@bors
Copy link
Contributor

bors commented Apr 10, 2015

⌛ Testing commit 05aaad1 with merge 93f7fe3...

@bors bors merged commit 05aaad1 into rust-lang:master Apr 11, 2015
@pnkfelix
Copy link
Member Author

Filed #24320 as a follow-on issue to @sfackler 's note about the fragility of the lookup here.

bors added a commit that referenced this pull request Apr 14, 2015
r? @steveklabnik

Should land this, #24270, and #24245 before rolling another beta.
@ranma42
Copy link
Contributor

ranma42 commented Sep 1, 2015

Mentioning #24263 so that the two issues link to each other.
Does this close #13623, #15620 and #20856 ?

@dotdash
Copy link
Contributor

dotdash commented Sep 1, 2015

No, at least the PartialEq issues are still a problem

@ranma42
Copy link
Contributor

ranma42 commented Sep 1, 2015

I am unable to reproduce issues with the quality of optimised LLVM IR code. The --pretty expanded output can still be very redundant, both for PartialEq and PartialOrd. In particular it looks like it would be possible to remove the nullary enum variants from the match. This might also help with reducing the automatically-generated intermediate code for huge C enums, which might be good to reduce the load on the LLVM optimiser (although hopefully we don't spend much time compiling derived code anyway).
If there is any interest in this, I should be able to write a PR for it.

@dotdash
Copy link
Contributor

dotdash commented Sep 1, 2015

Consider this:

#[derive(PartialEq)]
enum E {
  A(i32),
  B(i32),
  C(i32),
  D(i32),
}

Ideally, eq would just do a single compare (on 64bit) or two compares (on 32bit). But instead we get this:

_ZN23E...std..cmp..PartialEq2eq20hd7e6941040691683taaE:
    .cfi_startproc
    movl    (%rdi), %eax
    cmpl    (%rsi), %eax
    jne .LBB0_1
    cmpl    $1, %eax
    je  .LBB0_5
    cmpl    $2, %eax
    je  .LBB0_5
    cmpl    $3, %eax
.LBB0_5:
    movl    4(%rdi), %eax
    cmpl    4(%rsi), %eax
    sete    %al
    retq
.LBB0_1:
    xorl    %eax, %eax
    retq

And if you add a few more variants, we end up with a jump table.

@ranma42
Copy link
Contributor

ranma42 commented Sep 1, 2015

AFAICT none of the issues I mentioned is about this kind of problem.

It looks like you're suggesting that we should try to unify the code paths of all of the variants that share the same signature (or possibly even just the same signature prefix?). This is a (strong) generalisation of what I was suggesting, as nullary signatures are always matching. Several additional complications some to my mind, though, as this seems to interact with the memory layout of the enum (is there any guarantee that the i32 values from your example are always stored in the same position?).
Also, what about different types which might have the same size?

#[derive(PartialEq)]
enum E {
  A(i32),
  B(isize),
}

Should the Rust compiler be able to unify the comparison assuming that the on the target arch isize is 32 bits?

I believe that such optimisations might be more appropriate in LLVM than in the Rust compiler. As a matter of fact, although the LLVM IR code keeps them duplicated, upon translation to assembly LLVM is already able to unify the comparisons of the four cases. It is pretty weird that it cannot optimise the jump table correctly. Apparently it gets rid of the last je (which would be pointing to the next instruction), but then it does not realise that the result of cmpl $3, %eax is unused and so on.
Maybe we could ask for advice to the LLVM developers.

Should a new issue be filed to track it or an existing one be updated to make this specific problem more prominent?

@dotdash
Copy link
Contributor

dotdash commented Sep 1, 2015

#13623 was meant to be about all enums, not just C-like ones. I'll add the above example to it to clarify that.

That LLVM should do most optimizations is right, but in some cases we should also generate code that is easier for LLVM to optimize. Also, while the final asm has the code unified, the IR is still large, which means more work for the optimizer and possibly missed opportunities for inlining.

durka added a commit to durka/rust that referenced this pull request Mar 10, 2016
This is the same approach taken in rust-lang#24270, except that this
should not be a breaking change because it only changes the output
of hash functions, which nobody should be relying on.
durka added a commit to durka/rust that referenced this pull request Mar 10, 2016
This is the same approach taken in rust-lang#24270, except that this
should not be a breaking change because it only changes the output
of hash functions, which nobody should be relying on.
durka added a commit to durka/rust that referenced this pull request Mar 13, 2016
This is the same approach taken in rust-lang#24270, except that this
should not be a breaking change because it only changes the output
of hash functions, which nobody should be relying on.
durka added a commit to durka/rust that referenced this pull request Mar 22, 2016
This is the same approach taken in rust-lang#24270, except that this
should not be a breaking change because it only changes the output
of hash functions, which nobody should be relying on.
durka added a commit to durka/rust that referenced this pull request Mar 27, 2016
This is the same approach taken in rust-lang#24270, except that this
should not be a breaking change because it only changes the output
of hash functions, which nobody should be relying on.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
10 participants