diff --git a/shared/stinkytofu/src/analysis/asm/AsmVerifierPass.cpp b/shared/stinkytofu/src/analysis/asm/AsmVerifierPass.cpp index d39b786e2b44..c8b411c8da2f 100644 --- a/shared/stinkytofu/src/analysis/asm/AsmVerifierPass.cpp +++ b/shared/stinkytofu/src/analysis/asm/AsmVerifierPass.cpp @@ -173,11 +173,23 @@ static std::string checkRegisterWidths(const StinkyInstruction* inst, // receive a VGPR, not a SGPR (and vice versa). RegType expectedType = fieldTypeToRegType(field.fieldType); if (!isExpectedTypeMatch(field.fieldType, expectedType, reg.reg.type)) { - errors << "Instruction '"; - inst->dump(errors); - errors << "' operand " << (isDest ? "dest[" : "src[") << operandIndex << "] " - << "has register type '" << regTypeToString(reg.reg.type) << "', expected '" - << regTypeToString(expectedType) << "'\n"; + // Check promoted encoding: e.g. VOP2 src1 is vgpr-only, but VOP3 + // promotion widens it to src (accepts SGPR). The assembler will + // promote automatically, so accept the promoted field type too. + unsigned fieldIdx = static_cast(&field - hwDesc->operandFields.data()); + bool promotedOk = false; + if (fieldIdx < hwDesc->promotedFields.size()) { + auto promotedFT = hwDesc->promotedFields[fieldIdx].fieldType; + auto promotedET = fieldTypeToRegType(promotedFT); + promotedOk = isExpectedTypeMatch(promotedFT, promotedET, reg.reg.type); + } + if (!promotedOk) { + errors << "Instruction '"; + inst->dump(errors); + errors << "' operand " << (isDest ? "dest[" : "src[") << operandIndex << "] " + << "has register type '" << regTypeToString(reg.reg.type) << "', expected '" + << regTypeToString(expectedType) << "'\n"; + } } } diff --git a/shared/stinkytofu/tests/filecheck/verifier_vop2_sgpr_src.stir b/shared/stinkytofu/tests/filecheck/verifier_vop2_sgpr_src.stir new file mode 100644 index 000000000000..95df9165e05b --- /dev/null +++ b/shared/stinkytofu/tests/filecheck/verifier_vop2_sgpr_src.stir @@ -0,0 +1,46 @@ +# RUN: %stinkytofu-opt --arch gfx1250 %s --StinkyIRVerifierPass --print-output 2>&1 +# +# Verify that VOP2/VOPC instructions accept SGPR source operands without +# triggering register-type errors. The base VOP2 encoding restricts src1 +# to vgpr, but VOP3 promotion widens it to accept SGPRs. The verifier +# must check promotedFields before rejecting. + +#--- v_mul_u32_u24 with SGPR src1 +# CHECK-LABEL: @vop2_sgpr_src1_mul +# CHECK-NOT: [StinkyIRVerifier] +# CHECK: v_mul_u32_u24 + +st.func @vop2_sgpr_src1_mul() { +^entry: + v1 = "st.v_mul_u32_u24"(v0, s16) { issueCycles = 1, latencyCycles = 5 } +} + +#--- v_cmp_eq_u32 with SGPR src1 +# CHECK-LABEL: @vopc_sgpr_src1_cmp_eq +# CHECK-NOT: [StinkyIRVerifier] +# CHECK: v_cmp_eq_u32 + +st.func @vopc_sgpr_src1_cmp_eq() { +^entry: + vcc_lo0 = "st.v_cmp_eq_u32"(v1, s16) { issueCycles = 1, latencyCycles = 5 } +} + +#--- v_cmp_gt_u32 with SGPR src1 +# CHECK-LABEL: @vopc_sgpr_src1_cmp_gt +# CHECK-NOT: [StinkyIRVerifier] +# CHECK: v_cmp_gt_u32 + +st.func @vopc_sgpr_src1_cmp_gt() { +^entry: + vcc_lo0 = "st.v_cmp_gt_u32"(v1, s15) { issueCycles = 1, latencyCycles = 5 } +} + +#--- v_cmp_ge_u32 with SGPR src1 +# CHECK-LABEL: @vopc_sgpr_src1_cmp_ge +# CHECK-NOT: [StinkyIRVerifier] +# CHECK: v_cmp_ge_u32 + +st.func @vopc_sgpr_src1_cmp_ge() { +^entry: + vcc_lo0 = "st.v_cmp_ge_u32"(v2, s18) { issueCycles = 1, latencyCycles = 5 } +}