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

Wrong results with atomic substraction (LLVM-50) #7

Closed
rodrigorc opened this issue Nov 9, 2019 · 4 comments
Closed

Wrong results with atomic substraction (LLVM-50) #7

rodrigorc opened this issue Nov 9, 2019 · 4 comments

Comments

@rodrigorc
Copy link

Hi! I'm having issues with the result of atomic substractions.
I'm using the experimental Rust front-end, with a code such as this:

pub fn test() -> usize {
    use core::sync::atomic::Ordering::*;
    let mut x = core::sync::atomic::AtomicUsize::new(0);
    x.fetch_add(1, Relaxed);
    x.fetch_add(1, Relaxed);
    x.fetch_sub(1, Release);
    x.fetch_sub(1, Release);
    x.load(SeqCst)
}

I would expect it to return 0, but instead it returns 2.
The code generated seems ok, this is the LLVM-IR:

define internal fastcc void @_ZN6mytest4test17h53b3bfc678f42155E() unnamed_addr #0 {
start:
  %x = alloca %"core::sync::atomic::AtomicUsize", align 4
  %0 = bitcast %"core::sync::atomic::AtomicUsize"* %x to i8*
  call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0)
  %abi_cast.0..sroa_idx = getelementptr inbounds %"core::sync::atomic::AtomicUsize", %"core::sync::atomic::AtomicUsize"* %x, i32 0, i32 0, i32 0
  store i32 0, i32* %abi_cast.0..sroa_idx, align 4
  %1 = atomicrmw add i32* %abi_cast.0..sroa_idx, i32 1 monotonic
  %2 = atomicrmw add i32* %abi_cast.0..sroa_idx, i32 1 monotonic
  %3 = atomicrmw sub i32* %abi_cast.0..sroa_idx, i32 1 release
  %4 = atomicrmw sub i32* %abi_cast.0..sroa_idx, i32 1 release
  %5 = load atomic i32, i32* %abi_cast.0..sroa_idx seq_cst, align 4
  call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0)
  ret void
}

Looking at the intermediate values of x it goes this way:

let mut x = core::sync::atomic::AtomicUsize::new(0);
//x == 0
x.fetch_add(1, Relaxed);
//x == 1
x.fetch_add(1, Relaxed);
//x == 2
x.fetch_sub(1, Release);
//x == 4294967295 (that is -1)
x.fetch_sub(1, Release);
//x == 2 (I think this is kind of -4294967294)

I think it may be related to that comment in the freertos/portmacro.h file:

Warning: From the ISA docs: in some (unspecified) cases, the s32c1i instruction may return the
bitwise inverse of the old mem if the mem wasn't written. This doesn't seem to happen on the
ESP32 (portMUX assertions would fail).

It looks like this only happens when the instruction before the s32c1l instruction is a substraction. If I replace the fetch_sub(1) with a fetch_add(-1) it works as expected, with the following IR:
%4 = atomicrmw add i32* %abi_cast.0..sroa_idx, i32 -1 release

I looked at GCC with an equivalent C++ code using std::atomicand it uses add before s32c1l, even for substractions, never sub.

The conclusion I draw is that the LLVM atomicrmw sub should generate an add with negative value instead of a substraction.

@reitermarkus
Copy link

There is also a bug with AtomicI8, where fetch_add always returns 0:

let n = AtomicI8::new(0);

let n0 = n.fetch_add(1, SeqCst);
let n1 = n.fetch_add(1, SeqCst);

assert_eq!(n.load(SeqCst), 2);
assert_eq!(n0, 0);
assert_eq!(n1, 1);

which results in

thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `0`,
 right: `1`', app/src/main.rs:8:3

fetch_add always returns 0 instead of the previous value, in this case n1 == 0, when you load n in the end, it has the right value, however.

@igrr igrr transferred this issue from espressif/llvm-xtensa Jan 15, 2020
@andreisfr
Copy link
Collaborator

Hi @rodrigorc, @reitermarkus! Please, verify issues with new Xtensa backend based on release 9.0.1. (default branch in repository)

@github-actions github-actions bot changed the title Wrong results with atomic substraction Wrong results with atomic substraction (LLVM-50) Jan 24, 2020
@rodrigorc
Copy link
Author

I've just tested my above sample code agains xtensa_release_9.0.1 branch and it works correctly now.
@reitermarkus' code also seems to work perfectly fine.

Thanks!

@reitermarkus
Copy link

I can confirm that the example I gave above is working as expected now.

igrr pushed a commit that referenced this issue Dec 3, 2021
This patch re-introduces the fix in the commit llvm@66b0cebf7f736 by @yrnkrn

> In DwarfEHPrepare, after all passes are run, RewindFunction may be a dangling
>
> pointer to a dead function. To make sure it's valid, doFinalization nullptrs
> RewindFunction just like the constructor and so it will be found on next run.
>
> llvm-svn: 217737

It seems that the fix was not migrated to `DwarfEHPrepareLegacyPass`.

This patch also updates `llvm/test/CodeGen/X86/dwarf-eh-prepare.ll` to include `-run-twice` to exercise the cleanup. Without this patch `llvm-lit -v llvm/test/CodeGen/X86/dwarf-eh-prepare.ll` fails with

```
-- Testing: 1 tests, 1 workers --
FAIL: LLVM :: CodeGen/X86/dwarf-eh-prepare.ll (1 of 1)
******************** TEST 'LLVM :: CodeGen/X86/dwarf-eh-prepare.ll' FAILED ********************
Script:
--
: 'RUN: at line 1';   /home/arakaki/build/llvm-project/main/bin/opt -mtriple=x86_64-linux-gnu -dwarfehprepare -simplifycfg-require-and-preserve-domtree=1 -run-twice < /home/arakaki/repos/watch/llvm-project/llvm/test/CodeGen/X86/dwarf-eh-prepare.ll -S | /home/arakaki/build/llvm-project/main/bin/FileCheck /home/arakaki/repos/watch/llvm-project/llvm/test/CodeGen/X86/dwarf-eh-prepare.ll
--
Exit Code: 2

Command Output (stderr):
--
Referencing function in another module!
  call void @_Unwind_Resume(i8* %ehptr) #1
; ModuleID = '<stdin>'
void (i8*)* @_Unwind_Resume
; ModuleID = '<stdin>'
in function simple_cleanup_catch
LLVM ERROR: Broken function found, compilation aborted!
PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace.
Stack dump:
0.      Program arguments: /home/arakaki/build/llvm-project/main/bin/opt -mtriple=x86_64-linux-gnu -dwarfehprepare -simplifycfg-require-and-preserve-domtree=1 -run-twice -S
1.      Running pass 'Function Pass Manager' on module '<stdin>'.
2.      Running pass 'Module Verifier' on function '@simple_cleanup_catch'
 #0 0x000056121b570a2c llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/arakaki/repos/watch/llvm-project/llvm/lib/Support/Unix/Signals.inc:569:0
 #1 0x000056121b56eb64 llvm::sys::RunSignalHandlers() /home/arakaki/repos/watch/llvm-project/llvm/lib/Support/Signals.cpp:97:0
 #2 0x000056121b56f28e SignalHandler(int) /home/arakaki/repos/watch/llvm-project/llvm/lib/Support/Unix/Signals.inc:397:0
 #3 0x00007fc7e9b22980 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x12980)
 #4 0x00007fc7e87d3fb7 raise /build/glibc-S7xCS9/glibc-2.27/signal/../sysdeps/unix/sysv/linux/raise.c:51:0
 #5 0x00007fc7e87d5921 abort /build/glibc-S7xCS9/glibc-2.27/stdlib/abort.c:81:0
 #6 0x000056121b4e1386 llvm::raw_svector_ostream::raw_svector_ostream(llvm::SmallVectorImpl<char>&) /home/arakaki/repos/watch/llvm-project/llvm/include/llvm/Support/raw_ostream.h:674:0
 #7 0x000056121b4e1386 llvm::report_fatal_error(llvm::Twine const&, bool) /home/arakaki/repos/watch/llvm-project/llvm/lib/Support/ErrorHandling.cpp:114:0
 #8 0x000056121b4e1528 (/home/arakaki/build/llvm-project/main/bin/opt+0x29e3528)
 #9 0x000056121adfd03f llvm::raw_ostream::operator<<(llvm::StringRef) /home/arakaki/repos/watch/llvm-project/llvm/include/llvm/Support/raw_ostream.h:218:0
FileCheck error: '<stdin>' is empty.
FileCheck command line:  /home/arakaki/build/llvm-project/main/bin/FileCheck /home/arakaki/repos/watch/llvm-project/llvm/test/CodeGen/X86/dwarf-eh-prepare.ll

--

********************
********************
Failed Tests (1):
  LLVM :: CodeGen/X86/dwarf-eh-prepare.ll

Testing Time: 0.22s
  Failed: 1
```

Reviewed By: loladiro

Differential Revision: https://reviews.llvm.org/D110979

(cherry picked from commit e8806d7)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants