@@ -1363,10 +1363,10 @@ fn select_inst( |
| 1363 | 1363 | // Slow path (unfused): the condition is an arbitrary boolean in a |
| 1364 | 1364 | // register. Materialize with `CMP cond, #0; CSEL dest, tv, fv, NE`. |
| 1365 | 1365 | InstKind::Select(cond, tv, fv) => { |
| 1366 | | - let true_reg = ctx.lookup_vreg(*tv); |
| 1367 | | - let false_reg = ctx.lookup_vreg(*fv); |
| 1368 | 1366 | let class = type_to_reg_class(&inst.ty); |
| 1369 | 1367 | let dest = ctx.get_vreg(mf, inst.id, class); |
| 1368 | + let true_reg = coerce_select_operand_vreg(mf, ctx, mb, func, *tv, &inst.ty); |
| 1369 | + let false_reg = coerce_select_operand_vreg(mf, ctx, mb, func, *fv, &inst.ty); |
| 1370 | 1370 | |
| 1371 | 1371 | let arm_cond = if let Some(&fused_cond) = ctx.fused_arm_cond.get(cond) { |
| 1372 | 1372 | // Flags already set by the fused CMP — no extra compare needed. |
@@ -2773,6 +2773,60 @@ fn icmp_operand_vreg( |
| 2773 | 2773 | dest |
| 2774 | 2774 | } |
| 2775 | 2775 | |
| 2776 | +fn machine_vreg_class(mf: &MachineFunction, vreg: VRegId) -> RegClass { |
| 2777 | + mf.vregs |
| 2778 | + .iter() |
| 2779 | + .find(|r| r.id == vreg) |
| 2780 | + .map(|r| r.class) |
| 2781 | + .expect("isel: vreg not registered") |
| 2782 | +} |
| 2783 | + |
| 2784 | +fn coerce_select_operand_vreg( |
| 2785 | + mf: &mut MachineFunction, |
| 2786 | + ctx: &mut ISelCtx, |
| 2787 | + mb: MBlockId, |
| 2788 | + func: &Function, |
| 2789 | + value: ValueId, |
| 2790 | + target_ty: &IrType, |
| 2791 | +) -> VRegId { |
| 2792 | + let src = ctx.lookup_vreg(value); |
| 2793 | + let src_class = machine_vreg_class(mf, src); |
| 2794 | + let target_class = type_to_reg_class(target_ty); |
| 2795 | + if src_class == target_class { |
| 2796 | + return src; |
| 2797 | + } |
| 2798 | + |
| 2799 | + let dest = mf.new_vreg(target_class); |
| 2800 | + let src_ty = func.value_type(value); |
| 2801 | + let opcode = match (src_class, target_class) { |
| 2802 | + (RegClass::Gp32, RegClass::Gp64) => { |
| 2803 | + if matches!(target_ty, IrType::Ptr(_) | IrType::FuncPtr(_)) |
| 2804 | + || zero_extend_cmp_type(src_ty.as_ref()) |
| 2805 | + { |
| 2806 | + ArmOpcode::MovReg |
| 2807 | + } else { |
| 2808 | + match src_ty.as_ref() { |
| 2809 | + Some(IrType::Int(IntWidth::I8)) => ArmOpcode::Sxtb, |
| 2810 | + Some(IrType::Int(IntWidth::I16)) => ArmOpcode::Sxth, |
| 2811 | + Some(IrType::Int(IntWidth::I32)) | Some(IrType::Bool) => ArmOpcode::Sxtw, |
| 2812 | + _ => ArmOpcode::MovReg, |
| 2813 | + } |
| 2814 | + } |
| 2815 | + } |
| 2816 | + (RegClass::Gp64, RegClass::Gp32) => ArmOpcode::MovReg, |
| 2817 | + (RegClass::Fp32, RegClass::Fp64) => ArmOpcode::FcvtDS, |
| 2818 | + (RegClass::Fp64, RegClass::Fp32) => ArmOpcode::FcvtSD, |
| 2819 | + _ => ArmOpcode::MovReg, |
| 2820 | + }; |
| 2821 | + |
| 2822 | + mf.block_mut(mb).insts.push(MachineInst { |
| 2823 | + opcode, |
| 2824 | + operands: vec![MachineOperand::VReg(dest), MachineOperand::VReg(src)], |
| 2825 | + def: Some(dest), |
| 2826 | + }); |
| 2827 | + dest |
| 2828 | +} |
| 2829 | + |
| 2776 | 2830 | fn int_width_class(w: &IntWidth) -> RegClass { |
| 2777 | 2831 | match w { |
| 2778 | 2832 | IntWidth::I64 => RegClass::Gp64, |
@@ -3103,6 +3157,32 @@ mod tests { |
| 3103 | 3157 | ); |
| 3104 | 3158 | } |
| 3105 | 3159 | |
| 3160 | + #[test] |
| 3161 | + fn select_coerces_mixed_gp_widths_before_csel() { |
| 3162 | + let mf = select_simple(|b| { |
| 3163 | + let cond = b.const_bool(true); |
| 3164 | + let wide = b.const_i64(7); |
| 3165 | + let narrow = b.const_i32(-1); |
| 3166 | + let _s = b.select(cond, wide, narrow); |
| 3167 | + b.ret_void(); |
| 3168 | + }); |
| 3169 | + let csel = mf.blocks[0] |
| 3170 | + .insts |
| 3171 | + .iter() |
| 3172 | + .find(|i| i.opcode == ArmOpcode::CselReg) |
| 3173 | + .expect("expected CSEL for mixed-width select"); |
| 3174 | + for operand in csel.operands.iter().take(3) { |
| 3175 | + let MachineOperand::VReg(vreg) = operand else { |
| 3176 | + continue; |
| 3177 | + }; |
| 3178 | + assert_eq!( |
| 3179 | + machine_vreg_class(&mf, *vreg), |
| 3180 | + RegClass::Gp64, |
| 3181 | + "mixed-width select operands should be coerced to the result width before CSEL" |
| 3182 | + ); |
| 3183 | + } |
| 3184 | + } |
| 3185 | + |
| 3106 | 3186 | #[test] |
| 3107 | 3187 | fn csel_fusion_eliminates_cset_and_extra_cmp() { |
| 3108 | 3188 | // ICmp used solely by a Select → CSET and CMP cond, #0 must NOT appear. |