Repository navigation
Miscompilation of Bevy (and some wgpu) apps resulting in segfault on macOS. #117902
Description
Activity
- addedC-bugCategory: This is a bug.Category: This is a bug.regression-untriagedUntriaged performance or correctness regression.Untriaged performance or correctness regression.
on Nov 14, 2023 - addedI-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}Issue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triagingThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
on Nov 14, 2023 inodentry commented
on Nov 14, 2023 on Nov 14, 2023 · Hidden as off-topicAuthorshow commentMore actions- addedE-needs-bisectionCall for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustcCall for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustc
on Nov 14, 2023 - addedE-needs-mcveCall for participation: This issue has a repro, but needs a Minimal Complete and Verifiable ExampleCall for participation: This issue has a repro, but needs a Minimal Complete and Verifiable ExampleT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.and removedneeds-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triagingThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
on Nov 14, 2023 It would be useful if you could use
cargo-bisect-rustcto bisect the regression to a single PR, which would make it easier to figure out what's happening and whether it really is a miscompilation or just UB that's now going wrong in bevy's dep tree.I was not able to reproduce this on Windows. Marking as O-MacOS, but I suspect that the problem is platform-independent and just only manifests in platform-specific bevy code.
OK. TIL about
cargo-bisect-rustc. Installing it and reading the documentation now. Will try it in a moment.Edit: I created a simple bevy project that should automatically exit after the first frame is rendered to the screen. This should be scriptable. It will exit successfully on older rustc, or segfault on new rustc.
cargo bisect-rustcis running now, as I am typing this.Here is the bevy code if anyone else wants to try it:
use bevy::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Update, exit) .run(); } fn exit(mut evw: EventWriter<bevy::app::AppExit>) { evw.send(bevy::app::AppExit); }
Reacted by nora42 remaining items
Yes that should turn off the MIR inliner. I was aiming for a reproducer that has
-Zmir-opt-level=0because that would turn off all MIR optimizations, and I found that when I added that flag I stopped getting segfaults unless I enabled MIR inlining (which is a pass, but it also has its own special flag).Reacted by Rémy RakicMaybe the smaller repro behaves slightly differently now,
-Zinline-mir=no -Zmir-opt-level=0still segfaults for me, interestingly enough.Reacted by Ben KimockIn any case, the LLVM optimization pass that seems to start introducing the segfault is
Machine Copy Propagation Pass on function (repro_117902::TextureFormat::required_features)from the lib —RUSTFLAGS="-Cllvm-args=-opt-bisect-limit=1255"to reproduce this on the latest revision (with-Zinline-mir=noor-Zinline-mir=no -Zmir-opt-level=0, the limit is 1404 on the lib).cc @dianqk
Nice reproduction. But I can't reproduce it on macOS x86_64. Purchasing M1.
I have written very less Machine IR related code. But I'll try to look into it when the M1 arrives.Reacted by Rémy RakicUpstream issue: llvm/llvm-project#74680.
I tried to submit a fix as well.Base on https://github.com/lqd/repro-117902, I got
fn main() { call_from_main(); } #[inline(never)] #[no_mangle] fn call_from_main() { let desc = TextureDescriptor(TextureFormat::Bc6hRgbUfloat); device_create_texture(desc); } #[inline(never)] #[no_mangle] pub fn device_create_texture(desc: TextureDescriptor) { let format = desc.0; format.required_features().used(); format.used(); } enum Feature { A, B, } impl Feature { #[inline(never)] fn used(self) { core::hint::black_box(self); } } #[repr(C)] pub enum AstcBlock { B12x12, } pub enum AstcChannel { Unorm, Hdr, } #[repr(C)] pub enum TextureFormat { Rgba8UnormSrgb, Bc6hRgbUfloat, Key, Astc { channel: AstcChannel, block: AstcBlock, }, } impl TextureFormat { #[inline(never)] fn used(&self) { core::hint::black_box(self); } #[inline(never)] // #[no_mangle] fn required_features(&self) -> Feature { match self { Self::Rgba8UnormSrgb => Feature::A, Self::Bc6hRgbUfloat => Feature::B, Self::Key => Feature::B, Self::Astc { channel, .. } => match channel { AstcChannel::Hdr => Feature::A, AstcChannel::Unorm => Feature::B, }, } } } pub struct TextureDescriptor(pub TextureFormat);
Reacted by Ben Kimock, Rémy Rakic, Giacomo Stevanato, nora, apiraino and MKHmm, I can only use nightly to reproduce, and I can't use local stage1/stage2 to reproduce. This could be a difference in build configuration. So I think this minimal reproduction is unstable. If we want to use this as a test case, I would expect a more stable test case.
I added a more detailed explanation of this issue at llvm/llvm-project#74682 (comment). We should be able to create a must-be-dirty stack to reproduce.
I got a "perfect" test case.
I can reproduce it with nightly, 1.74.1 and even 1.69.0.
fn main() { let desc = TextureDescriptor(TextureFormat::A, 1, 1); device_create_texture(desc); } #[inline(never)] #[no_mangle] pub fn device_create_texture(desc: TextureDescriptor) { let format = desc.0; assert_eq!(format.required_features(desc.1), Feature::A); } #[derive(PartialEq, Eq, Debug)] enum Feature { A, B, } #[repr(i32)] pub enum TextureFormat { A, B, C, D, } impl TextureFormat { #[inline(never)] fn required_features(&self, v: i8) -> Feature { match self { Self::A => Feature::A, Self::B => Feature::B, Self::C => Feature::B, Self::D => match v { 0 => Feature::A, _ => Feature::B, }, } } } pub struct TextureDescriptor(pub TextureFormat, i8, i16);
By examining the generated instructions, the following instruction sets
x0to0x0001000100000000:mov x0, #4294967296 movk x0, #1, lsl #48
godbolt: https://rust.godbolt.org/z/vnnvEszoP
Reacted by Nicholas R. Smith and Pop- added a commit that references this issue
on Dec 15, 2023 Reopening to track beta backport in #118994.
- Reacted by dianqk, gak, Adam Heller and Fan Jiang
- removedE-needs-mcveCall for participation: This issue has a repro, but needs a Minimal Complete and Verifiable ExampleCall for participation: This issue has a repro, but needs a Minimal Complete and Verifiable Example
on Mar 28, 2024
Recent rustc seems to miscompile something somewhere in the dependency tree of Bevy, causing apps to crash with a segfault.
The issue seems to only manifest when optimizations are enabled and codegen units are > 1. The default cargo release configuration triggers it. Unoptimized builds are fine. Setting
codegen-units = 1withopt-level = 3also seems to be fine.I am on macOS 14.1 Sonoma, on Apple M1 Pro hardware.
Code
You can reproduce with any Bevy application. It is a huge code base, I know, I'm sorry I can't find a more minimal example.
Clone the Bevy repo: https://github.com/bevyengine/bevy
all these revs have the issue:
Run some example in release mode:
Version it worked on
The previous 1.74.0-beta.7 did not have the issue. I am not sure which nightly introduced the regression.
Version with regression
I first noticed the issue when I installed the 2023-11-11 nightly, though it might also be present in earlier nightlies. I just installed 1.75.0-beta.1 and updated my nightly, and can confirm the issue is present there, too.