- 
                Notifications
    You must be signed in to change notification settings 
- Fork 13.9k
          Make i*::signum a const fn.
          #61635
        
          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
  
    Make i*::signum a const fn.
  
  #61635
              
            Conversation
| r? @bluss (rust_highfive has picked a reviewer for you, use r? to override) | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r? @oli-obk
This uses a well-known branchless implementation of `signum`. Its `const`-ness is unstable and requires `#![feature(const_int_sign)]`.
e080fac    to
    bd899d0      
    Compare
  
    | can you also add some tests using this inside a constant and comparing the result with expected values? There should already be tests for other const fns for integer types, you can add the new test to them. | 
d79e75d    to
    1bf37ae      
    Compare
  
    1bf37ae    to
    7ec54f9      
    Compare
  
    | The job  Click to expand the log.I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact  | 
7ec54f9    to
    c2d8841      
    Compare
  
    c2d8841    to
    f6611db      
    Compare
  
    | @oli-obk I think this is ready to go now. | 
| @bors r+ | 
| 📌 Commit f6611db has been approved by  | 
Make `i*::signum` a `const fn`. Ticks a box in #53718. This uses a well-known branchless implementation of `signum`: `(n > 0) as i32 - (n < 0) as i32`. Here's a [playground](https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=747cf191c4974bf66c9d75e509ae6e6e) comparing the two techniques. On x86 in release mode, the branchless implementation is able to replace a `mov` and `cmov` with a `sar` and `add`, so this should be a bit faster as well. ~~This is marked as a draft since I think I'll need to add `#[rustc_const_unstable]` somewhere. Perhaps the reviewer can point me in the right direction.~~
| ☀️ Test successful - checks-travis, status-appveyor | 
Tested on commit rust-lang/rust@7f90abe. Direct link to PR: <rust-lang/rust#61635> 🎉 rls on windows: test-fail → test-pass (cc @Xanewok, @rust-lang/infra).
| For what it's worth, regarding 'branchless' in the commit message,  this code was/is branchless and more or less as efficient before and after this commit.  Example on ARM64: Note though that the old form also informed the compiler that the result was either -1, 0 or +1. The new form looks to the compiler like the result might be any integer value. That might have side effects on optimization opportunities in inlined contexts. | 
| @bjacob, I compared the generated assembly (albeit only on x86) in the PR message, so I'm aware that the optimized version of the old implementation is branchless. I've reproduced the full output below for you. playground::signum: # @playground::signum
# %bb.0:
	xorl	%eax, %eax
	testl	%edi, %edi
	setg	%al
	sarl	$31, %edi
	addl	%edi, %eax
	retq
                                        # -- End function
playground::signum_branch: # @playground::signum_branch
# %bb.0:
	xorl	%ecx, %ecx
	testl	%edi, %edi
	setne	%cl
	movl	$-1, %eax
	cmovnsl	%ecx, %eax
	retq
                                        # -- End functionThe main benefit of this PR is that  Your last point is an interesting one, although I'm not aware of a set of optimizations that would make use this information. Perhaps you have an idea? | 
| right, sorry I had not followed the link in the commit message. | 
| On the other hand, I'm impressed that even in the new form, the compiler was able to deduce that the result could only be -1, 0 or 1 so it still elided the default: case. | 
| Ah, I didn't consider branching on the result of  I don't know how common code like this is, since match n {
    n if n > 0 => {}
    0 => {}
    _ => {}
}is much more straightforward (I only use  | 
| @Centril This should get  | 
Clean up const-hack PRs now that const if / match exist. Closes rust-lang#67627. Cleans up these merged PRs tagged with `const-hack`: - rust-lang#63810 - rust-lang#63786 - rust-lang#61635 - rust-lang#58044 reverting their contents to have the match or if expressions they originally contained. r? @oli-obk There's one more PR in those tagged with `const-hack` that originally wasn't merged (rust-lang#65107). Reading the thread, it looks like it was originally closed because the `const-hack` for the checked arithmetic non-negligibly hurt performance, and because there was no way to manipulate the returned Option at compile time anyway (with neither const if nor const match). Would you like me to add these changes to the changes from this PR here too, now that we have the necessary features?
Ticks a box in #53718.
This uses a well-known branchless implementation of
signum:(n > 0) as i32 - (n < 0) as i32.Here's a playground comparing the two techniques. On x86 in release mode, the branchless implementation is able to replace a
movandcmovwith asarandadd, so this should be a bit faster as well.This is marked as a draft since I think I'll need to add#[rustc_const_unstable]somewhere. Perhaps the reviewer can point me in the right direction.