When enabled, the weak-intrinsics feature will cause all intrinsics
functions to be marked with weak linkage (i.e. `#[linkage = "weak"])
so that they can be replaced at link time by a stronger symbol.
This can be set to use C++ intrinsics from the compiler-rt library,
as it will avoid Rust's implementation replacing the compiler-rt
implementation as long as the compiler-rt symbols are linked as
strong symbols. Typically this requires the compiler-rt library to
be explicitly specified in the link command.
Addresses https://github.com/rust-lang/compiler-builtins/issues/525.
Without weak-intrinsics, from nm:
```
00000000 W __aeabi_memclr8 // Is explicitly weak
00000000 T __udivsi3 // Is not.
```
With weak-intrinsics, from nm:
```
00000000 W __aeabi_memclr8 // Is explicitly weak
00000000 W __udivsi3 // Is weak due to weak-intrinsics
```
This commit follows the same logic as:
- https://github.com/rust-lang/compiler-builtins/pull/462
- https://github.com/rust-lang/compiler-builtins/pull/466
I've tested the changes by preparing a simple program:
```rust
fn calc() -> ... {
let x = hint::black_box(4u...); // 4u8, 4u16, 4u32, 4u64, 4u128 + signed
let y = hint::black_box(1u32);
// x >> y
// x << y
}
fn main() -> ! {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
let mut serial = arduino_hal::default_serial!(dp, pins, 57600);
for b in calc().to_le_bytes() {
_ = ufmt::uwrite!(&mut serial, "{} ", b);
}
_ = ufmt::uwriteln!(&mut serial, "");
loop {
//
}
}
```
... switching types & operators in `calc()`, and observing the results;
what I ended up with was:
```
u32 << u32 - ok
u64 << u32 - ok
u128 << u32 - error (undefined reference to `__ashlti3')
i32 >> u32 - ok
i64 >> u32 - ok
i128 >> u32 - error (undefined reference to `__ashrti3')
u32 >> u32 - ok
u64 >> u32 - ok
u128 >> u32 - error (undefined reference to `__lshrti3')
(where "ok" = compiles and returns correct results)
```
As with multiplication and division, so do in here 128-bit operations
not work, because avr-gcc's standard library doesn't provide them (at
the same time, requiring that specific calling convention, making it
pretty difficult for compiler-builtins to jump in).
I think 128-bit operations non-working on an 8-bit controller is an
acceptable trade-off - 😇 - and so the entire fix in here is
just about skipping those functions.
int_util.c includes stdlib.h if `_WIN32` is defined. When compiling
the UEFI targets with clang they are treated as Windows targets (e.g. if
the Rust target is x86_64-unknown-uefi, the clang target is
x86_64-unknown-windows-gnu). So stdlib.h gets included, even though we
are compilling with `-ffreestanding` and don't want stdlib.h to be
used. That file may not be present, or an incompatible version might be
installed leading to typedef redefinition errors.
The contents of stdlib.h aren't actually needed for these targets anyway
(due to `__STDC_HOSTED__` being 0), so create a minimal stdlib.h in
`build.rs` when `target_os == uefi` and add it to the include path.
The UEFI targets link with `/SAFESEH`. That requires that objects have a
symbol called [`@feat.00`]. Clang adds that symbol for COFF targets if
the input is a C file, but not if the input is an ASM file. That doesn't
prevent compiler_builtins or rustc from building, but using the
resulting rustc to compile something that references one of the objects
lacking `@feat.00` will result in a linker error.
Fix by removing all the `.S` implementations when `target_os == uefi`.
[`@feat.00`]: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#the-sxdata-section
The conversion functions from i128/u128 to f32/f64 have the
`unadjusted_on_win64` attribute, but it is disabled starting with
LLVM14. This seems to be the correct thing to do for Win64, but for some
reason x86_64-unknown-uefi is different, despite generally using the
same ABI as Win64.