Compiler generates recursive memclr


#1

Platform: bare metal rust (using zinc) on my 32bit ARM teensy 3.1

I had a problem with write!(uart, "{:x}", x); that i tracked down to a weird thing I can not explain any further, so I need your help please.

Depending on the size of an constant array on the stack rust generates different initializition code.

wait(20);
let mut buf = [0u16; 16];
wait(20);

generates

    129e:    2014          movs    r0, #20
    12a0:    f7ff f925     bl    4ee <_ZN10teensytest4wait17hd95ed7517e8d4a2aE>
    12a4:    2014          movs    r0, #20
    12a6:    f8ad 6056     strh.w    r6, [sp, #86]    ; 0x56
    12aa:    f8cd 6052     str.w    r6, [sp, #82]    ; 0x52
    12ae:    f8cd 604e     str.w    r6, [sp, #78]    ; 0x4e
    12b2:    f8cd 604a     str.w    r6, [sp, #74]    ; 0x4a
    12b6:    f8cd 6046     str.w    r6, [sp, #70]    ; 0x46
    12ba:    f8cd 6042     str.w    r6, [sp, #66]    ; 0x42
    12be:    f8cd 603e     str.w    r6, [sp, #62]    ; 0x3e
    12c2:    f8cd 603a     str.w    r6, [sp, #58]    ; 0x3a
    12c6:    f7ff f912     bl    4ee <_ZN10teensytest4wait17hd95ed7517e8d4a2aE>

Arrays with more than 32 bytes

wait(20);
let mut buf = [0u16; 64];
wait(20);

create this code:

    129e:    2014          movs    r0, #20
    12a0:    f7ff f925     bl    4ee <_ZN10teensytest4wait17hd95ed7517e8d4a2aE>
    12a4:    1cb8          adds    r0, r7, #2
    12a6:    217e          movs    r1, #126    ; 0x7e
    12a8:    f000 f860     bl    136c <__aeabi_memclr>
    12ac:    2014          movs    r0, #20
    12ae:    f7ff f91e     bl    4ee <_ZN10teensytest4wait17hd95ed7517e8d4a2aE>

where

0000136c <__aeabi_memclr>:
    136c:    b580          push    {r7, lr}
    136e:    f7ff fffd     bl    136c <__aeabi_memclr>
    1372:    bd80          pop    {r7, pc}

The teensy restarts when executing this code. I think the restart is caused by recursively calling __aeabi_memclr, every time pushing a byte to the stack until it overflows. But it’s hard to verify since I cannot change the code of this function.

Where does this function come from? Why is this function called? And why does it call itself?

-Daniel


I’m using nightly-2016-09-17.

Rustc links to libcore: rustc .... --extern core=...teensytest/target/thumbv7em-none-eabi/release/deps/libcore-882297ed0c39d543.rlib

Cargo.toml:

[target.thumbv7em-none-eabi.dependencies]
rust-libcore = "*"

[dependencies.rlibc]
git = "https://github.com/phaiax/rlibc"
branch = "zinc"

That is the code of the rlibc: https://github.com/phaiax/rlibc/blob/zinc/src/lib.rs. Note that it only provides memset and not memclr, but is that the same thing as __aeabi_foobar?


#2

I figured out where these aeabi_memXXX functions come from.
They are from something called newlib.

That means it depends on the version of the arm-none-eabi toolchain on my computer. That is arm-none-eabi-newlib-2.2.0_1-7.fc24.noarch (fedora 24).

In this project (newlib 2.2) there are two implementations of __aeabi_memcpy: One with optimized assembly and one that simply calls an extern memcpy(). The assembly version is used if #if defined (__ARM_ARCH_7A__) && defined (__ARM_FEATURE_UNALIGNED) && (defined (__ARM_NEON__) || !defined (__SOFTFP__)) but i guess it doesn’t matter much which versions are used.

But memclr is not mentioned. But I figured out that version 2.4 of newlib has much more aeabi functions, amongst others

/* Support the routine __aeabi_memclr.  */
void __aeabi_memclr (void *dest, size_t n)
{
  extern void __aeabi_memset (void *dest, size_t n, int c);
  __aeabi_memset (dest, n, 0);
}

So I guess I’m updating my toolchain.


#3

That was not the solution.

The linking toolchain (gcc) is using newlib, but the __aeabi_memclr from that newlib is not the one that is inserted into the output executable.

Rustc ships with compiler-rt , a LLVM project. This project also contains implementations for __aeabi_memfoo.

So is this a bug in rustc? Tips still appreciated :slight_smile:

-Daniel (Sorry for the monologue)

-> https://github.com/rust-lang/rust/issues/31544


#4

One final post for the solution: (EDIT: Not.)
Add a #![no_builtins] to the main.rs.

The assembly becomes

    12d2:    2014          movs    r0, #20
    12d4:    f7ff f91e     bl    514 <_ZN10teensytest4wait17hd95ed7517e8d4a2aE>
    12d8:    2000          movs    r0, #0
    12da:    522e          strh    r6, [r5, r0]
    12dc:    3002          adds    r0, #2
    12de:    f5b0 6f87     cmp.w    r0, #1080    ; 0x438
    12e2:    d1fa          bne.n    12da <main+0x262>
    12e4:    2014          movs    r0, #20
    12e6:    f7ff f915     bl    514 <_ZN10teensytest4wait17hd95ed7517e8d4a2aE>

without any __aeabi_memfoo functions.

It’s a little drepressing how long it took to figure that out. Hopefully this will help someone.

-Daniel


EDIT:

It seems to work for the crate the #![no_builtins] is in. But other crates (like core) are not using that directive, so that my initial problem with write!(uart, "{:x}", x); still persists. The LowerHex struct internally creates a buffer that is initialized with a recursive __aeabi_memclr. DAMN!