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

Invalid bitcast with repr(simd), array type, and wasm #80108

Closed
calebzulawski opened this issue Dec 17, 2020 · 8 comments · Fixed by #89579
Closed

Invalid bitcast with repr(simd), array type, and wasm #80108

calebzulawski opened this issue Dec 17, 2020 · 8 comments · Fixed by #89579
Labels
A-SIMD Area: SIMD (Single Instruction Multiple Data) C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ O-wasm Target: WASM (WebAssembly), http://webassembly.org/ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@calebzulawski
Copy link
Member

For some reason this only occurs with wasm. If you compile this on the playground, it works just fine until you compile for wasm, which produces this error.

Code

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=685f1384988924fc87ac7760fccab58a

#![feature(repr_simd)]

#[repr(simd)]
pub struct Vector([i32; 4]);

impl Vector {
    pub const fn to_array(self) -> [i32; 4] {
        self.0
    }
}

Meta

rustc --version --verbose:

rustc 1.50.0-nightly (b32e6e6ac 2020-12-16)
binary: rustc
commit-hash: b32e6e6ac8921035177256ab6806e6ab0d4b9b94
commit-date: 2020-12-16
host: x86_64-apple-darwin
release: 1.50.0-nightly

Error output

Invalid bitcast
  %4 = bitcast <4 x i32> %1 to [4 x i32], !dbg !21
in function _ZN10playground6Vector8to_array17h22d416c39ac2333dE
LLVM ERROR: Broken function found, compilation aborted!
@calebzulawski calebzulawski added C-bug Category: This is a bug. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Dec 17, 2020
calebzulawski added a commit to rust-lang/portable-simd that referenced this issue Dec 17, 2020
@bjorn3 bjorn3 added the A-SIMD Area: SIMD (Single Instruction Multiple Data) label Dec 17, 2020
@jonas-schievink jonas-schievink added the O-wasm Target: WASM (WebAssembly), http://webassembly.org/ label Dec 17, 2020
@workingjubilee
Copy link
Member

workingjubilee commented Apr 16, 2021

PS> cargo build --target wasm32-unknown-unknown
   Compiling example v0.1.0 (C:\Users\${name}\project\example)
Invalid bitcast
  %4 = bitcast <4 x i32> %1 to [4 x i32], !dbg !21
in function _ZN7example6Vector8to_array17he7a4eb6f9a744f2cE
LLVM ERROR: Broken function found, compilation aborted!
error: could not compile `example`

This remains an issue on Rust nightly with LLVM 12.

PS> rustc --version --verbose
rustc 1.53.0-nightly (7af1f55ae 2021-04-15)
binary: rustc
commit-hash: 7af1f55ae359e731c2c303f5d98e42a1a8163af0
commit-date: 2021-04-15
host: x86_64-pc-windows-msvc
release: 1.53.0-nightly
LLVM version: 12.0.0

@calebzulawski
Copy link
Member Author

calebzulawski commented Apr 27, 2021

Playing around with this some more in godbolt.

x86_64-unknown-linux-gnu without optimization generates:

define void @_ZN7example6Vector8to_array17h38598b25c9720edeE([8 x i32]* noalias nocapture sret([8 x i32]) dereferenceable(32) %0, <8 x i32>* noalias nocapture dereferenceable(32) %self) unnamed_addr #0 !dbg !6 {
start:
  %1 = bitcast <8 x i32>* %self to [8 x i32]*, !dbg !11
  %2 = bitcast [8 x i32]* %0 to i8*, !dbg !11
  %3 = bitcast [8 x i32]* %1 to i8*, !dbg !11
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %2, i8* align 32 %3, i64 32, i1 false), !dbg !11
  ret void, !dbg !12
}

wasm32-unknown-unknown without optimization generates:

define hidden void @_ZN7example6Vector8to_array17h534c3671215fa5f8E([8 x i32]* noalias nocapture sret([8 x i32]) dereferenceable(32) %0, <8 x i32> %self) unnamed_addr #0 !dbg !5 {
  %1 = bitcast <8 x i32> %self to [8 x i32], !dbg !10
  store [8 x i32] %1, [8 x i32]* %0, align 4, !dbg !10
  ret void, !dbg !11
}

Is it expected that self is not taken by reference? Playing around with more examples indicates to me that for wasm32, vectors are never taken by reference. It looks to me like an assumption was made somewhere that vectors are always taken by reference (and pointers to arrays can be bitcast). Definitely a rustc codegen issue and not LLVM.

@calebzulawski
Copy link
Member Author

I think I see the problem, more or less.

Here the SIMD field is extracted:

// `#[repr(simd)]` types are also immediate.
(OperandValue::Immediate(llval), &Abi::Vector { .. }) => {
OperandValue::Immediate(bx.extract_element(llval, bx.cx().const_usize(i as u64)))
}

and then here it is bitcast:

(OperandValue::Immediate(llval), _) => {
// Bools in union fields needs to be truncated.
*llval = bx.to_immediate(*llval, field);
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
*llval = bx.bitcast(*llval, bx.cx().immediate_backend_type(field));
}

I'm not sure, but I think the correct thing to do is check if the extracted element is an array (and not a pointer to an array?). If it's an array, we must return not the array on line 216 but a pointer to it.

@bjorn3
Copy link
Member

bjorn3 commented Apr 27, 2021

Is it expected that self is not taken by reference?

Yes,

// This is a fun case! The gist of what this is doing is
// that we want callers and callees to always agree on the
// ABI of how they pass SIMD arguments. If we were to *not*
// make these arguments indirect then they'd be immediates
// in LLVM, which means that they'd used whatever the
// appropriate ABI is for the callee and the caller. That
// means, for example, if the caller doesn't have AVX
// enabled but the callee does, then passing an AVX argument
// across this boundary would cause corrupt data to show up.
//
// This problem is fixed by unconditionally passing SIMD
// arguments through memory between callers and callees
// which should get them all to agree on ABI regardless of
// target feature sets. Some more information about this
// issue can be found in #44367.
//
// Note that the platform intrinsic ABI is exempt here as
// that's how we connect up to LLVM and it's unstable
// anyway, we control all calls to it in libstd.
Abi::Vector { .. }
if abi != SpecAbi::PlatformIntrinsic
&& cx.tcx().sess.target.simd_types_indirect =>
{
arg.make_indirect();
return;
}

@calebzulawski
Copy link
Member Author

calebzulawski commented May 30, 2021

I took another shot at this with no success.

It appears that with an arch without indirect SIMD types it completely skips the operand code and hits this branch in this project_field function:

_ if offset.bytes() == 0 => {
// Unions and newtypes only use an offset of 0.
// Also handles the first field of Scalar, ScalarPair, and Vector layouts.
self.llval
}

I tried using alloca and store to create an array pointer from the vector value, and manually skipped the pointercast at the end of that closure, but that didn't work. I can't seem to be able to find where the bitcast is actually coming from.

@workingjubilee
Copy link
Member

Godbolt currently is back on May 31st, --version --verbose:

rustc 1.54.0-nightly (657bc01 2021-05-31) ``` binary: rustc commit-hash: 657bc01 commit-date: 2021-05-31 host: x86_64-unknown-linux-gnu release: 1.54.0-nightly LLVM version: 12.0.1 Compiler returned: 0 ```

#85966 got merged on June 4th. My nightly is dated since then, rustc -vV:

rustc 1.54.0-nightly (6c2dd25 2021-06-05)
binary: rustc
commit-hash: 6c2dd251bbff03c7a3092d43fb5b637eca0810e3
commit-date: 2021-06-05
host: x86_64-pc-windows-msvc
release: 1.54.0-nightly
LLVM version: 12.0.1

Godbolt still fails, but my local rustc now succeeds in emitting wasm for this using rustc example-vector.rs --target wasm32-unknown-unknown --crate-type=lib -O without errors. Updating to the latest rustc -vV:

rustc 1.55.0-nightly (6a758ea 2021-06-22)
binary: rustc
commit-hash: 6a758ea7e48416b968955535094479dc2e7cc9e1
commit-date: 2021-06-22
host: x86_64-pc-windows-msvc
release: 1.55.0-nightly
LLVM version: 12.0.1
gives us another passing version. I believe #85966 fixed this. We should add regression testing for this issue and close it.

@workingjubilee workingjubilee added the E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. label Jun 23, 2021
@calebzulawski
Copy link
Member Author

@workingjubilee passing vectors via indirection definitely fixes the issue. I think the issue still stands if/when another architecture passes them directly.

@workingjubilee
Copy link
Member

I agree that we seem to have an issue where we emit unsound bitcasts, but it seems like that should be documented more fully in another issue.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Oct 10, 2021
…8, r=Mark-Simulacrum

Add regression test for issue 80108

Closes rust-lang#80108
@bors bors closed this as completed in 2bfbf97 Oct 11, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-SIMD Area: SIMD (Single Instruction Multiple Data) C-bug Category: This is a bug. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ O-wasm Target: WASM (WebAssembly), http://webassembly.org/ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants