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

Missed optimization to elide bounds check #73827

Closed
a2aaron opened this issue Jun 28, 2020 · 8 comments · Fixed by #78046
Closed

Missed optimization to elide bounds check #73827

a2aaron opened this issue Jun 28, 2020 · 8 comments · Fixed by #78046
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. I-slow Issue: Problems and improvements with respect to performance of generated code. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@a2aaron
Copy link
Contributor

a2aaron commented Jun 28, 2020

Context: this bug was reduced from indexing into a nested array, but this shows the same problem.

pub fn get(array: &[u8; 8], x: usize, y: usize) -> u8 {
    if x > 7 || y > 7 {
        0
    } else {
        array[y]
    }
}

(Playground link)

Rust doesn't elide the bounds check on array[y], even though it's impossible for y to be out of range due to the if condition. Rust does elide the bounds check when the if condition is just if y > 7, however.

Here is the generated ASM for the above code:

playground::get:
	pushq	%rax
	orq	%rdx, %rsi
	cmpq	$7, %rsi
	jbe	.LBB0_2
	xorl	%eax, %eax
	popq	%rcx
	retq

.LBB0_2:
	cmpq	$7, %rdx
	ja	.LBB0_5
	movb	(%rdi,%rdx), %al
	popq	%rcx
	retq

.LBB0_5:
	leaq	.Lanon.357673f8919c00a2ec2e627b7663c19f.1(%rip), %rax
	movl	$8, %esi
	movq	%rdx, %rdi
	movq	%rax, %rdx
	callq	*core::panicking::panic_bounds_check@GOTPCREL(%rip)
	ud2

.Lanon.357673f8919c00a2ec2e627b7663c19f.0:
	.ascii	"src/lib.rs"

.Lanon.357673f8919c00a2ec2e627b7663c19f.1:
	.quad	.Lanon.357673f8919c00a2ec2e627b7663c19f.0
	.asciz	"\n\000\000\000\000\000\000\000\005\000\000\000\t\000\000"

and here is the generated ASM when we change the if to just be if y > 7

playground::get:
	cmpq	$7, %rdx
	jbe	.LBB0_2
	xorl	%eax, %eax
	retq

.LBB0_2:
	movb	(%rdi,%rdx), %al
	retq

Meta

This occurs in both the latest versions of stable (1.44.1) and nightly (1.46.0-nightly (2020-06-26 7750c3d))

@a2aaron a2aaron added the C-bug Category: This is a bug. label Jun 28, 2020
@a2aaron
Copy link
Contributor Author

a2aaron commented Jun 28, 2020

It seems that the bounds check does get elided if I split up the if expression.

pub fn get(board: &[u8; 8], x: usize, y: usize) -> u8 {
    if x > 7 {
        0
    } else if y > 7 {
        0
    } else {
        board[y]
    }
}

This generates

playground::get:
	orq	%rdx, %rsi
	cmpq	$7, %rsi
	jbe	.LBB0_2
	xorl	%eax, %eax
	retq

.LBB0_2:
	movb	(%rdi,%rdx), %al
	retq

@leonardo-m
Copy link

And it seems it needs two unlikely to produce the right happy path:

pub fn get4(array: &[u8; 8], x: usize, y: usize) -> u8 {
    if unlikely(x > 7) {
        0
    } else if unlikely(y > 7) {
        0
    } else {
        array[y]
    }
}
get4:
        or      rsi, rdx
        cmp     rsi, 7
        ja      .LBB1_1
        mov     al, byte ptr [rdi + rdx]
        ret
.LBB1_1:
        xor     eax, eax
        ret

@bugadani
Copy link
Contributor

The assume intrinsic also seems to not work if a logical expression is used, maybe it's related?

I.e. assume(0<a && a < 42) does not make subsequent bounds checks go away but 2 assumes do.

@nikic
Copy link
Contributor

nikic commented Jun 28, 2020

Godbolt: https://rust.godbolt.org/z/xYEszA

The problem is that the condition is converted to (x | y) > 7, at which point implication no longer works. It should be straightforward to teach LVI about this special case.

@jonas-schievink jonas-schievink added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. I-slow Issue: Problems and improvements with respect to performance of generated code. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jun 28, 2020
@nikic
Copy link
Contributor

nikic commented Jun 28, 2020

https://reviews.llvm.org/D82715

@nikic nikic self-assigned this Jun 28, 2020
nikic added a commit to llvm/llvm-project that referenced this issue Jul 1, 2020
InstCombine may convert conditions like (x < C) && (y < C) into
(x | y) < C (for some C). This patch teaches LVI to recognize that
in this case, it can infer either x < C or y < C along the edge.

This fixes the issue reported at
rust-lang/rust#73827.

Differential Revision: https://reviews.llvm.org/D82715
@nikic
Copy link
Contributor

nikic commented Jul 1, 2020

Fix has landed upstream, we'll pull it in with the LLVM 11 upgrade.

chelini pushed a commit to LoopTactics/mlir that referenced this issue Jul 6, 2020
InstCombine may convert conditions like (x < C) && (y < C) into
(x | y) < C (for some C). This patch teaches LVI to recognize that
in this case, it can infer either x < C or y < C along the edge.

This fixes the issue reported at
rust-lang/rust#73827.

Differential Revision: https://reviews.llvm.org/D82715
arichardson pushed a commit to arichardson/llvm-project that referenced this issue Jul 8, 2020
InstCombine may convert conditions like (x < C) && (y < C) into
(x | y) < C (for some C). This patch teaches LVI to recognize that
in this case, it can infer either x < C or y < C along the edge.

This fixes the issue reported at
rust-lang/rust#73827.

Differential Revision: https://reviews.llvm.org/D82715
@nikic nikic removed their assignment Oct 17, 2020
@nikic nikic added the E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. label Oct 17, 2020
@nikic
Copy link
Contributor

nikic commented Oct 17, 2020

This if fixed in 1.47 with the LLVM 11 upgrade. It's probably worthwhile to add a codegen test, as bounds check optimization tends to be a bit fragile.

@bugadani
Copy link
Contributor

Great, thanks! I can handle the test

bugadani added a commit to bugadani/rust that referenced this issue Oct 17, 2020
GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this issue Oct 20, 2020
bors added a commit to rust-lang-ci/rust that referenced this issue Oct 20, 2020
…laumeGomez

Rollup of 9 pull requests

Successful merges:

 - rust-lang#78046 (Add codegen test for issue rust-lang#73827)
 - rust-lang#78061 (Optimize const value interning for ZST types)
 - rust-lang#78070 (we can test std and core panic macros together)
 - rust-lang#78076 (Move orphan module-name/mod.rs files into module-name.rs files)
 - rust-lang#78129 (Wrapping intrinsics doc links update.)
 - rust-lang#78133 (Add some MIR-related regression tests)
 - rust-lang#78144 (Don't update `entries` in `TypedArena` if T does not need drop)
 - rust-lang#78145 (Drop unneeded `mut`)
 - rust-lang#78157 (Remove unused type from librustdoc)

Failed merges:

r? `@ghost`
@bors bors closed this as completed in 2c1de08 Oct 20, 2020
mem-frob pushed a commit to draperlaboratory/hope-llvm-project that referenced this issue Oct 7, 2022
InstCombine may convert conditions like (x < C) && (y < C) into
(x | y) < C (for some C). This patch teaches LVI to recognize that
in this case, it can infer either x < C or y < C along the edge.

This fixes the issue reported at
rust-lang/rust#73827.

Differential Revision: https://reviews.llvm.org/D82715
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. I-slow Issue: Problems and improvements with respect to performance of generated code. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants