diff --git a/crates/rustc_codegen_spirv/src/linker/destructure_composites.rs b/crates/rustc_codegen_spirv/src/linker/destructure_composites.rs index 0185be16f9f..69c9a486672 100644 --- a/crates/rustc_codegen_spirv/src/linker/destructure_composites.rs +++ b/crates/rustc_codegen_spirv/src/linker/destructure_composites.rs @@ -20,32 +20,52 @@ pub fn destructure_composites(function: &mut Function) { _ => None, }) .collect(); + for inst in function.all_inst_iter_mut() { - if inst.class.opcode == Op::CompositeExtract && inst.operands.len() == 2 { - let mut composite = inst.operands[0].unwrap_id_ref(); - let index = inst.operands[1].unwrap_literal_bit32(); + // multi-index extraction for nested tuples/structs + if inst.class.opcode == Op::CompositeExtract && inst.operands.len() >= 2 { + let mut current_id = inst.operands[0].unwrap_id_ref(); + let mut final_origin = None; + + // step through each index sequentially to resolve deeply nested extracts + for index_operand in &inst.operands[1..] { + let index = index_operand.unwrap_literal_bit32(); + + let mut search_id = current_id; + let mut resolved_id = None; - let origin = loop { - if let Some(inst) = reference.get(&composite) { - match inst.class.opcode { + while let Some(ref_inst) = reference.get(&search_id) { + match ref_inst.class.opcode { Op::CompositeInsert => { - let insert_index = inst.operands[2].unwrap_literal_bit32(); + let insert_index = ref_inst.operands[2].unwrap_literal_bit32(); if insert_index == index { - break Some(inst.operands[0].unwrap_id_ref()); + resolved_id = Some(ref_inst.operands[0].unwrap_id_ref()); + break; } - composite = inst.operands[1].unwrap_id_ref(); + // If not our index, continue down the insert chain + search_id = ref_inst.operands[1].unwrap_id_ref(); } Op::CompositeConstruct => { - break inst.operands.get(index as usize).map(|o| o.unwrap_id_ref()); + resolved_id = ref_inst + .operands + .get(index as usize) + .map(|o| o.unwrap_id_ref()); + break; } _ => unreachable!(), } + } + + if let Some(res) = resolved_id { + current_id = res; + final_origin = Some(res); } else { - break None; + final_origin = None; + break; } - }; + } - if let Some(origin_id) = origin { + if let Some(origin_id) = final_origin { rewrite_rules.insert( inst.result_id.unwrap(), rewrite_rules.get(&origin_id).map_or(origin_id, |id| *id), diff --git a/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr b/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr deleted file mode 100644 index 17c52a8fabc..00000000000 --- a/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr +++ /dev/null @@ -1,45 +0,0 @@ -warning: `#[inline(never)]` function `member_ref_arg_broken::f` has been inlined - --> <$DIR/member_ref_arg-broken.rs>:20:4 - | -LL | fn f(x: &u32) -> u32 { - | ^ - | - = note: inlining was required due to illegal (pointer) argument - = note: called from `member_ref_arg_broken::main` - -warning: `#[inline(never)]` function `member_ref_arg_broken::g` has been inlined - --> <$DIR/member_ref_arg-broken.rs>:25:4 - | -LL | fn g(xy: (&u32, &u32)) -> (u32, u32) { - | ^ - | - = note: inlining was required due to illegal (pointer) argument - = note: called from `member_ref_arg_broken::main` - -warning: `#[inline(never)]` function `member_ref_arg_broken::h` has been inlined - --> <$DIR/member_ref_arg-broken.rs>:30:4 - | -LL | fn h(xyz: (&u32, &u32, &u32)) -> (u32, u32, u32) { - | ^ - | - = note: inlining was required due to illegal parameter type - = note: called from `member_ref_arg_broken::main` - -warning: `#[inline(never)]` function `member_ref_arg_broken::h_newtyped` has been inlined - --> <$DIR/member_ref_arg-broken.rs>:41:4 - | -LL | fn h_newtyped(xyz: ((&u32, &u32, &u32),)) -> (u32, u32, u32) { - | ^^^^^^^^^^ - | - = note: inlining was required due to illegal parameter type - = note: called from `member_ref_arg_broken::main` - -error: error:0:0 - OpLoad Pointer '$ID[%$ID]' is not a logical pointer. - %39 = OpLoad %uint %38 - - | - = note: spirv-val failed - = note: module `$TEST_BUILD_DIR/lang/core/ref/member_ref_arg-broken` - -error: aborting due to 1 previous error; 4 warnings emitted - diff --git a/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.rs b/tests/compiletests/ui/lang/core/ref/member_ref_arg_tuples.rs similarity index 85% rename from tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.rs rename to tests/compiletests/ui/lang/core/ref/member_ref_arg_tuples.rs index e0f13873bad..3f43d2d94f2 100644 --- a/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.rs +++ b/tests/compiletests/ui/lang/core/ref/member_ref_arg_tuples.rs @@ -1,8 +1,4 @@ -// FIXME(eddyb) this is like `member_ref_arg`, but testing the error messages -// in some broken cases - this test should eventually pass, but for now -// we care more that the error messages do not regress too much. - -// build-fail +// build-pass // normalize-stderr-test "ref/member_ref_arg-broken\.[^`]*" -> "ref/member_ref_arg-broken" // normalize-stderr-test "38\[%38\]" -> "$$ID[%$$ID]" @@ -37,6 +33,7 @@ fn h(xyz: (&u32, &u32, &u32)) -> (u32, u32, u32) { // instructions, but the extra nesting here stops them dead in their tracks // (they're also not really the right solution for this problem, such composites // should just never exist by-value, and `qptr` may very well get rid of them). +// update : this now works but idealy it should be fixed by `qptr` but for now w'll use `destructure_composites` #[inline(never)] fn h_newtyped(xyz: ((&u32, &u32, &u32),)) -> (u32, u32, u32) { (*xyz.0.0, *xyz.0.1, *xyz.0.2) diff --git a/tests/compiletests/ui/lang/core/ref/member_ref_arg_tuples.stderr b/tests/compiletests/ui/lang/core/ref/member_ref_arg_tuples.stderr new file mode 100644 index 00000000000..26a651c3ebc --- /dev/null +++ b/tests/compiletests/ui/lang/core/ref/member_ref_arg_tuples.stderr @@ -0,0 +1,38 @@ +warning: `#[inline(never)]` function `member_ref_arg_tuples::f` has been inlined + --> <$DIR/member_ref_arg_tuples.rs>:16:4 + | +LL | fn f(x: &u32) -> u32 { + | ^ + | + = note: inlining was required due to illegal (pointer) argument + = note: called from `member_ref_arg_tuples::main` + +warning: `#[inline(never)]` function `member_ref_arg_tuples::g` has been inlined + --> <$DIR/member_ref_arg_tuples.rs>:21:4 + | +LL | fn g(xy: (&u32, &u32)) -> (u32, u32) { + | ^ + | + = note: inlining was required due to illegal (pointer) argument + = note: called from `member_ref_arg_tuples::main` + +warning: `#[inline(never)]` function `member_ref_arg_tuples::h` has been inlined + --> <$DIR/member_ref_arg_tuples.rs>:26:4 + | +LL | fn h(xyz: (&u32, &u32, &u32)) -> (u32, u32, u32) { + | ^ + | + = note: inlining was required due to illegal parameter type + = note: called from `member_ref_arg_tuples::main` + +warning: `#[inline(never)]` function `member_ref_arg_tuples::h_newtyped` has been inlined + --> <$DIR/member_ref_arg_tuples.rs>:38:4 + | +LL | fn h_newtyped(xyz: ((&u32, &u32, &u32),)) -> (u32, u32, u32) { + | ^^^^^^^^^^ + | + = note: inlining was required due to illegal parameter type + = note: called from `member_ref_arg_tuples::main` + +warning: 4 warnings emitted +