Skip to content

armhf code generation is broken  #10482

Closed
@ibukanov

Description

@ibukanov
Contributor

I am using armhf build from http://luqman.ca/rust-builds/rust-0.9-pre-3b0d486-arm-unknown-linux-gnueabihf.zip released 2013-11-12. That generates broken code with the following program:

enum Test {
    A(f64),
    B
}

impl Test {
    fn draw(&self) {
        match *self {
            A(x) => println!("A {}", x),
            B => println!("B")
        }
    }
}

fn main() {
    let s = A(0.1);
    s.draw();
}

When running on armhf (Samsung ARM Chromebook) the program just goes into infinite loop instead of printing A 0.1.

Activity

ibukanov

ibukanov commented on Nov 14, 2013

@ibukanov
ContributorAuthor

http://pastebin.mozilla.org/3601836 is the asm output of compiling the above file with rustc -S source.rs

ibukanov

ibukanov commented on Nov 14, 2013

@ibukanov
ContributorAuthor

Apparently it is the call to println with a floating point to blame. The following test case is enough to show the problem:

fn main() {
   println!("a={}", 1);
   println!("b={}", 1.1);
}

It inters oo loop after printing a=1

alexcrichton

alexcrichton commented on Nov 14, 2013

@alexcrichton
Member

Are you able to get a stack trace when the program is infinite looping? Printing is actually a very large amount of machinery, so it may be helpful to reduce this down even further.

ibukanov

ibukanov commented on Nov 14, 2013

@ibukanov
ContributorAuthor

Given the program:

fn main() {
   format!("{}", 1.1);
}

The stack trace of the looping thread looks like oo recursion:

#0  0x76aba534 in __divdf3 ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#1  0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#2  0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#3  0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#4  0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#5  0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#6  0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#7  0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#8  0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#9  0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#10 0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#11 0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#12 0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#13 0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#14 0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#15 0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#16 0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#17 0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#18 0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
#19 0x769c7aec in num::strconv::float_to_str_common::h6babd82e453a2ffeaw::v0.9$x2dpre ()
   from /home/user/a/dev/rust/../../../opt/rust/lib/rustc/arm-unknown-linux-gnueabihf/lib/libstd-6425b930ca146ae9-0.9-pre.so
ibukanov

ibukanov commented on Nov 14, 2013

@ibukanov
ContributorAuthor

A variation of the example without format:

fn main() {
   (1.1_f64).to_str();
}

also leads to oo recursion:

bt
#0  0x00013e2c in __divdf3 ()
#1  0x00010d08 in f64::Div$f64::div::h22c34fa84d77518naU::v0.0 ()
#2  0x00010d08 in f64::Div$f64::div::h22c34fa84d77518naU::v0.0 ()
#3  0x00010d08 in f64::Div$f64::div::h22c34fa84d77518naU::v0.0 ()
#4  0x00010d08 in f64::Div$f64::div::h22c34fa84d77518naU::v0.0 ()
#5  0x00010d08 in f64::Div$f64::div::h22c34fa84d77518naU::v0.0 ()
#6  0x00010d08 in f64::Div$f64::div::h22c34fa84d77518naU::v0.0 ()
#7  0x00010d08 in f64::Div$f64::div::h22c34fa84d77518naU::v0.0 ()
#8  0x00010d08 in f64::Div$f64::div::h22c34fa84d77518naU::v0.0 ()
#9  0x00010d08 in f64::Div$f64::div::h22c34fa84d77518naU::v0.0 ()
#10 0x00010d08 in f64::Div$f64::div::h22c34fa84d77518naU::v0.0 ()
#11 0x00010d08 in f64::Div$f64::div::h22c34fa84d77518naU::v0.0 ()

It is

ibukanov

ibukanov commented on Nov 14, 2013

@ibukanov
ContributorAuthor

More direct test case:

use std::num::strconv;

fn main() {
   strconv::float_to_str_common(
        1.1_f64, 10u, true, strconv::SignNeg, strconv::DigAll);
}

So something breaks code in the float_to_str_common implementation.

chris-morgan

chris-morgan commented on Dec 23, 2013

@chris-morgan
Member

I ran into this this very issue this evening (when rust-http tests were inexplicably not completing), and pinpointed the cause of failure more accurately. (I may say that after that, I was a little disappointed to find it had already been filed!)

  1. Only gnueabihf is affected (my tablet used to run an armel OS and gnueabi build and at that time the aforementioned tests ran successfully)
  2. f32 is not affected.
  3. The string formatting fails because the loop for the integral part of the number never reaches the terminating condition where it is zero. This is because
  4. std::num::cmath::c_double_utils::trunc, which provides f64.trunc(), is unsound: 0f64.trunc() != 0f64. A little transmutation (unsafe { std::cast::transmute::<f64, u64>(0f64) }.to_str_radix(2)) consistently shows it is yielding the bits 1111111111111111111111000000000100000000000000000000000000000000 (the last 32 bits are zero) rather than 0, the expected value. (I'm not certain of the f64 layout on this architecture, but sign, exponent and mantissa should certainly all be zero.)

This is as far as I have gone at present.

I suggest that this be escalated to (at the least!) high priority. It also needs more tags added.

Could someone please update this issue's title and main body to reflect the underlying problem?

emberian

emberian commented on Dec 23, 2013

@emberian
Member

Nominating for 1.0.

chris-morgan

chris-morgan commented on Jan 2, 2014

@chris-morgan
Member

@ibukanov: BTW, the stack traces showing recursion is actually bogus; I've experienced it myself on armel also, that it simply doesn't work. The backtraces are broken. But that's a separate issue (#5934, I think).

itdaniher

itdaniher commented on Jan 2, 2014

@itdaniher
Contributor
fn i2f(i: u8) -> f64 {(i as f64)/127f64 - 1f64}
println!("{:?}", i2f(125u8));

Hangs indefinitely, as expected.

fn i2f(i: u8) -> f64 {(i as f32)/127f32 - 1f32}
println!("{:?}", i2f(125u8));

Prints -00000000000000000000000000000000000000000000f32.

Any operations upon the result of 'i2f' are printed as NaN.

This is using:

rustc 0.9-pre (d5d5c50 2013-12-20 12:41:33 -0800)
host: arm-unknown-linux-gnueabihf

Any idea if this issue is related? @chris-morgan suggested f32 to not be impacted, but something's definitely wonky here.

itdaniher

itdaniher commented on Jan 2, 2014

@itdaniher
Contributor

I got bored and wrote an implementation of ftoa in rust. It works fine on x86, fails miserably on armhf.

use std::num::{pow, log10};

fn main() {
    let mut out = ~"";
    let mut n: f32 = -0.1231;
    if (n < 0.0) { n *= -1.0; out.push_char('-');}
    let mut m: f32 = log10(n).floor();
    if (m < 1.0) { m = 0.0 }
    loop {
        if (n > 0.000001f32) || (m >= 0.0) {
            let w: f32 = pow(10.0, m);
            let d = (n / w).floor();
            n -= d*w;
            out.push_str((d as uint).to_str_radix(10u));
            if (m == 0.0) { out.push_char('.') }
            m -= 1.0;
        }
        else {
            break
        }
    }
    println!("{}", out);
}

Prints -0.123099 on x86, -0. on ARMHF. Some combination of 'pow', 'floor', or 'log10' are seriously mucked up. (edit: to_str_radix isn't culpable)

itdaniher

itdaniher commented on Jan 2, 2014

@itdaniher
Contributor

This seems as if it ought to be high priority, with even fairly simple code failing to behave as expected.

@luqmana - have you encountered any related floating point weirdness on ARM? Any insights?

itdaniher

itdaniher commented on Jan 3, 2014

@itdaniher
Contributor

It looks like the call to the llvm intrinsics for log10 are at least partially culpable.

use std::num::log10;

fn main() {
        let x = log10(100f32);
        let y = x as u8;
        println!("{}", y);
}

prints 0.

fn main() {
        let x = 100f32;
        let y = x as u8;
        println!("{}", y);
}

prints 100.

itdaniher

itdaniher commented on Jan 4, 2014

@itdaniher
Contributor

Minimal test case:

use std::unstable::intrinsics::log10f32;
use std::cast::transmute;

fn main() {
        let x = unsafe { log10f32(100f32)};
        let y = unsafe { transmute::<f32,u32>(x)}.to_str_radix(2);
        println(y);
}

With optimizations on, it prints 1000000000000000000000000000000 aka 2.0, as expected, as an intrinsic function of a constant is optimized to a constant. Without optimizations, it prints 0 (wrong) as expected.

pnkfelix

pnkfelix commented on Jan 6, 2014

@pnkfelix
Member

cc me

24 remaining items

Loading
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

    O-ArmTarget: 32-bit Arm processors (armv6, armv7, thumb...), including 64-bit Arm in AArch32 stateP-mediumMedium priority

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @itdaniher@alexcrichton@pnkfelix@chris-morgan@emberian

        Issue actions

          armhf code generation is broken · Issue #10482 · rust-lang/rust