Skip to content

Bad native codegen (2017-12-25 Nightly) #47015

Description

@leonardo-m

Given this small program:

#[inline(never)]
fn e97() -> u64 {
    // Input.
    const BASE: f64 = 28_433.0;
    const EXP: u64 = 7_830_457;
    const BIG: f64 = 1e10;

    let mut b = EXP.next_power_of_two();

    let mut n = 1.0;
    while b > 0 {
        if EXP & b != 0 {
            n = (n * n * 2.0) % BIG;
        } else {
            n = (n * n) % BIG;
        }
        b >>= 1;
    }

    ((n * BASE + 1.0) % BIG) as u64
}

fn main() {
    assert_eq!(e97(), 8_739_992_577);
}

I am using:

rustc 1.24.0-nightly (4a7c072fa 2017-12-25)
commit-hash: 4a7c072fa61b42f96d8b75c37fc1edfd71172695
host: x86_64-pc-windows-gnu

If I compile that program with this it works correctly:
rustc -C opt-level=1 test.rs

If I compile it with a native CPU target it asserts:
rustc -C opt-level=1 -C target-cpu=native test.rs

The llvm-ir of the e97 function produced with a default CPU target:

; test::e97
; Function Attrs: noinline norecurse nounwind readnone uwtable
define internal fastcc i64 @_ZN4test3e9717h2a230e2896608cc2E() unnamed_addr #3 {
start:
  br label %bb4

bb3:                                              ; preds = %bb4
  %0 = fmul double %n.1.1, 2.843300e+04
  %1 = fadd double %0, 1.000000e+00
  %2 = frem double %1, 1.000000e+10
  %3 = fptoui double %2 to i64
  ret i64 %3

bb4:                                              ; preds = %bb4, %start
  %b.08 = phi i64 [ 8388608, %start ], [ %12, %bb4 ]
  %n.07 = phi double [ 1.000000e+00, %start ], [ %n.1.1, %bb4 ]
  %4 = and i64 %b.08, 7830457
  %5 = icmp eq i64 %4, 0
  %6 = fmul double %n.07, %n.07
  %7 = fmul double %6, 2.000000e+00
  %n.1.in = select i1 %5, double %6, double %7
  %n.1 = frem double %n.1.in, 1.000000e+10
  %8 = and i64 %b.08, 15660914
  %9 = icmp eq i64 %8, 0
  %10 = fmul double %n.1, %n.1
  %11 = fmul double %10, 2.000000e+00
  %n.1.in.1 = select i1 %9, double %10, double %11
  %n.1.1 = frem double %n.1.in.1, 1.000000e+10
  %12 = lshr i64 %b.08, 2
  %13 = icmp eq i64 %12, 0
  br i1 %13, label %bb3, label %bb4
}

And the llvm-ir using the native CPU target:

; test::e97
; Function Attrs: noinline norecurse nounwind readnone uwtable
define internal fastcc i64 @_ZN4test3e9717h2a230e2896608cc2E() unnamed_addr #3 {
start:
  br label %bb4

bb3:                                              ; preds = %bb4
  %0 = fmul double %n.1.3, 2.843300e+04
  %1 = fadd double %0, 1.000000e+00
  %2 = frem double %1, 1.000000e+10
  %3 = fptoui double %2 to i64
  ret i64 %3

bb4:                                              ; preds = %bb4, %start
  %b.08 = phi i64 [ 8388608, %start ], [ %20, %bb4 ]
  %n.07 = phi double [ 1.000000e+00, %start ], [ %n.1.3, %bb4 ]
  %4 = and i64 %b.08, 7830457
  %5 = icmp eq i64 %4, 0
  %6 = fmul double %n.07, %n.07
  %7 = fmul double %6, 2.000000e+00
  %n.1.in = select i1 %5, double %6, double %7
  %n.1 = frem double %n.1.in, 1.000000e+10
  %8 = and i64 %b.08, 15660914
  %9 = icmp eq i64 %8, 0
  %10 = fmul double %n.1, %n.1
  %11 = fmul double %10, 2.000000e+00
  %n.1.in.1 = select i1 %9, double %10, double %11
  %n.1.1 = frem double %n.1.in.1, 1.000000e+10
  %12 = and i64 %b.08, 31321828
  %13 = icmp eq i64 %12, 0
  %14 = fmul double %n.1.1, %n.1.1
  %15 = fmul double %14, 2.000000e+00
  %n.1.in.2 = select i1 %13, double %14, double %15
  %n.1.2 = frem double %n.1.in.2, 1.000000e+10
  %16 = and i64 %b.08, 62643656
  %17 = icmp eq i64 %16, 0
  %18 = fmul double %n.1.2, %n.1.2
  %19 = fmul double %18, 2.000000e+00
  %n.1.in.3 = select i1 %17, double %18, double %19
  %n.1.3 = frem double %n.1.in.3, 1.000000e+10
  %20 = lshr i64 %b.08, 4
  %21 = icmp eq i64 %20, 0
  br i1 %21, label %bb3, label %bb4
}

Their diff:

8c8
<   %0 = fmul double %n.1.1, 2.843300e+04
---
>   %0 = fmul double %n.1.3, 2.843300e+04
15,16c15,16
<   %b.08 = phi i64 [ 8388608, %start ], [ %12, %bb4 ]
<   %n.07 = phi double [ 1.000000e+00, %start ], [ %n.1.1, %bb4 ]
---
>   %b.08 = phi i64 [ 8388608, %start ], [ %20, %bb4 ]
>   %n.07 = phi double [ 1.000000e+00, %start ], [ %n.1.3, %bb4 ]
29,31c29,43
<   %12 = lshr i64 %b.08, 2
<   %13 = icmp eq i64 %12, 0
<   br i1 %13, label %bb3, label %bb4
---
>   %12 = and i64 %b.08, 31321828
>   %13 = icmp eq i64 %12, 0
>   %14 = fmul double %n.1.1, %n.1.1
>   %15 = fmul double %14, 2.000000e+00
>   %n.1.in.2 = select i1 %13, double %14, double %15
>   %n.1.2 = frem double %n.1.in.2, 1.000000e+10
>   %16 = and i64 %b.08, 62643656
>   %17 = icmp eq i64 %16, 0
>   %18 = fmul double %n.1.2, %n.1.2
>   %19 = fmul double %18, 2.000000e+00
>   %n.1.in.3 = select i1 %17, double %18, double %19
>   %n.1.3 = frem double %n.1.in.3, 1.000000e+10
>   %20 = lshr i64 %b.08, 4
>   %21 = icmp eq i64 %20, 0
>   br i1 %21, label %bb3, label %bb4

Activity

  1. alexcrichton commented on Dec 26, 2017

    @alexcrichton
    Member

    This looks like a bug in LLVM unfortunately. For example:

    input.ll

    $ opt input.ll -S -O2 -mcpu=haswell -o output.ll
    

    output.ll

    $ opt output.ll -s -O2 
    ; ModuleID = '<stdin>'
    source_filename = "a.ll"
    target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
    target triple = "x86_64-unknown-linux-gnu"
    
    ; Function Attrs: norecurse nounwind readnone
    define i64 @foo() local_unnamed_addr #0 {
    start:
      ret i64 9246255105
    }
    
    attributes #0 = { norecurse nounwind readnone "target-cpu"="haswell" }
    

    The major change in the nightly here is that ThinLTO was enabled. ThinLTO can run optimizations twice (why opt is run twice here) and it seems as if some bug is generating this answer.

    Using LLVM trunk the intermediate result is the same and the second optimization pass generates ret i64 8739992577 instead of the previous number (which is what we'd expect).

    As a result this is probably just a bland "already fixed in LLVM" bug, making this blocked on #43370

  2. added
    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.
    on Dec 26, 2017
  3. added
    I-slowIssue: Problems and improvements with respect to performance of generated code.
    on Jan 7, 2018
  4. added a commit that references this issue on Jan 28, 2018
    d9031ae
  5. added a commit that references this issue on Jan 28, 2018
    38bd381
  6. added a commit that references this issue on Jan 30, 2018
    22d405f
  7. 8 remaining items

  8. added 3 commits that reference this issue on Feb 7, 2018
    ced1f3d
  9. added a commit that references this issue on Feb 9, 2018
    cef95b0
  10. added a commit that references this issue on Feb 10, 2018
    6b7b6b6
  11. added a commit that references this issue on Feb 10, 2018
    39abcc0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.I-slowIssue: Problems and improvements with respect to performance of generated code.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions