[AMDGPU] Use SubtargetPredicates for the f32/f16 -> fp8/bf8 conversions (#213383)

The follow-up #212888 left for the other direction. HasCvtFP8SDWASrcSel
and HasCvtFP8ByteSel replace isGFX940Plus and isGFX11Plus on the f32 ->
fp8/bf8 conversions, which split by the same encoding characteristic, so
the two feature descriptions become direction-neutral. The f16 ->
fp8/bf8 conversions had no feature at all, so add
FeatureF16FP8ConversionInsts, the mirror of the existing
FeatureFP8F16ConversionInsts, and use it in place of isGFX1250Plus.

Assisted-By: Claude Opus 5
This commit is contained in:
Dmitry Sidorov 2026-08-01 12:36:31 +02:00 committed by GitHub
parent 232e8b5f45
commit 71b120f63e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 28 additions and 19 deletions

View file

@ -898,6 +898,11 @@ defm FP8F16ConversionInsts : AMDGPUSubtargetFeature<"fp8-f16-conversion-insts",
"Has fp8 and bf8 conversion to f16 instructions"
>;
// The opposite direction of FP8F16ConversionInsts above.
defm F16FP8ConversionInsts : AMDGPUSubtargetFeature<"f16-fp8-conversion-insts",
"Has f16 conversion to fp8 and bf8 instructions"
>;
defm FP8E5M3Insts : AMDGPUSubtargetFeature<"fp8e5m3-insts",
"Has fp8 e5m3 format support"
>;
@ -909,17 +914,17 @@ defm CvtFP8VOP1Bug : AMDGPUSubtargetFeature<"cvt-fp8-vop1-bug",
[FeatureFP8ConversionInsts]
>;
// The fp8/bf8 to f32 conversions select which byte of the source to convert in
// one of two ways.
// The fp8/bf8 conversions take the byte index in one of two ways, and a
// subtarget uses the same one in both directions.
defm CvtFP8SDWASrcSel : AMDGPUSubtargetFeature<"cvt-fp8-sdwa-src-sel",
"FP8/BF8 conversions to F32 take the source byte from the SDWA src0_sel field",
"FP8/BF8 conversions take the byte index from the SDWA src0_sel or op_sel field",
/*GenPredicate=*/1,
/*GenAssemblerPredicate=*/1,
[FeatureFP8ConversionInsts]
>;
defm CvtFP8ByteSel : AMDGPUSubtargetFeature<"cvt-fp8-byte-sel",
"FP8/BF8 conversions to F32 take the source byte from a byte_sel operand",
"FP8/BF8 conversions take the byte index from a byte_sel operand",
/*GenPredicate=*/1,
/*GenAssemblerPredicate=*/1,
[FeatureFP8ConversionInsts]
@ -2297,6 +2302,7 @@ def FeatureISAVersion12_50_Common : FeatureSet<
FeatureCvtFP8ByteSel,
FeatureOCPFP8ConversionInsts,
FeatureFP8F16ConversionInsts,
FeatureF16FP8ConversionInsts,
FeatureFP8E5M3Insts,
FeaturePackedTID,
FeatureVcmpxPermlaneHazard,
@ -2455,6 +2461,7 @@ def FeatureISAVersion13 : FeatureSet<
FeatureCvtFP8ByteSel,
FeatureOCPFP8ConversionInsts,
FeatureFP8F16ConversionInsts,
FeatureF16FP8ConversionInsts,
FeaturePackedTID,
FeatureVcmpxPermlaneHazard,
FeatureSALUFloatInsts,

View file

@ -1228,7 +1228,7 @@ class VOP1_CVT_PK_F8_F16_Fake16_Profile<RegisterOperand RC> : VOPProfile_Fake16<
}
}
let SubtargetPredicate = isGFX1250Plus in {
let SubtargetPredicate = HasF16FP8ConversionInsts in {
let ReadsModeReg = 0 in {
defm V_CVT_PK_FP8_F16 : VOP1Inst_t16_with_profiles<"v_cvt_pk_fp8_f16",
VOP1_CVT_PK_F8_F16_Profile<VSrc_NoInline_v2f16>,

View file

@ -813,19 +813,19 @@ let OtherPredicates = [HasFP8ConversionInsts], mayRaiseFPException = 0,
VOP3_CVT_PK_F8_F32_Profile_t16<>,
VOP3_CVT_PK_F8_F32_Profile_fake16<>>;
let SubtargetPredicate = isGFX11Plus in {
let SubtargetPredicate = HasCvtFP8ByteSel in {
let OtherPredicates = [HasFP8ConversionInsts, NotHasFP8E5M3Insts] in
defm V_CVT_SR_FP8_F32_gfx12 : VOP3Inst<"v_cvt_sr_fp8_f32_gfx12", VOP3_CVT_SR_F8_ByteSel_Profile<f32>>;
let OtherPredicates = [HasFP8ConversionInsts, HasFP8E5M3Insts] in
defm V_CVT_SR_FP8_F32_gfx1250 : VOP3Inst<"v_cvt_sr_fp8_f32_gfx1250", VOP3_CVT_SR_F8_ByteSel_Profile<f32, true>>;
defm V_CVT_SR_BF8_F32_gfx12 : VOP3Inst<"v_cvt_sr_bf8_f32_gfx12", VOP3_CVT_SR_F8_ByteSel_Profile<f32>>;
} // End SubtargetPredicate = isGFX11Plus
} // End SubtargetPredicate = HasCvtFP8ByteSel
}
// These instructions have non-standard use of op_sel. In particular they are
// using op_sel bits 2 and 3 while only having two sources. Therefore dummy
// src2 is used to hold the op_sel value.
let Constraints = "$vdst = $src2", SubtargetPredicate = isGFX940Plus in {
let Constraints = "$vdst = $src2", SubtargetPredicate = HasCvtFP8SDWASrcSel in {
defm V_CVT_SR_FP8_F32 : VOP3Inst<"v_cvt_sr_fp8_f32", VOP3_CVT_SR_F8_F32_Profile>;
defm V_CVT_SR_BF8_F32 : VOP3Inst<"v_cvt_sr_bf8_f32", VOP3_CVT_SR_F8_F32_Profile>;
}
@ -920,14 +920,14 @@ defm : Cvt_PK_F8_F32_t16_Pat<int_amdgcn_cvt_pk_bf8_f32, V_CVT_PK_BF8_F32_t16_e64
}
}
let SubtargetPredicate = isGFX940Plus in {
let SubtargetPredicate = HasCvtFP8SDWASrcSel in {
foreach Index = [0, 1, 2, 3] in {
def : Cvt_SR_F8_F32_Pat<int_amdgcn_cvt_sr_fp8_f32, Index, V_CVT_SR_FP8_F32_e64>;
def : Cvt_SR_F8_F32_Pat<int_amdgcn_cvt_sr_bf8_f32, Index, V_CVT_SR_BF8_F32_e64>;
}
}
let SubtargetPredicate = isGFX11Plus in {
let SubtargetPredicate = HasCvtFP8ByteSel in {
let OtherPredicates = [HasFP8ConversionInsts, NotHasFP8E5M3Insts] in
def : Cvt_SR_F8_ByteSel_Pat<int_amdgcn_cvt_sr_fp8_f32, V_CVT_SR_FP8_F32_gfx12_e64, f32>;
let OtherPredicates = [HasFP8ConversionInsts, HasFP8E5M3Insts] in {
@ -935,7 +935,7 @@ let SubtargetPredicate = isGFX11Plus in {
def : Cvt_SR_F8_ByteSel_E5M3_Pat<int_amdgcn_cvt_sr_fp8_f32_e5m3, V_CVT_SR_FP8_F32_gfx1250_e64, f32, DSTCLAMP.ENABLE>;
}
def : Cvt_SR_F8_ByteSel_Pat<int_amdgcn_cvt_sr_bf8_f32, V_CVT_SR_BF8_F32_gfx12_e64, f32>;
} // End SubtargetPredicate = isGFX11Plus
} // End SubtargetPredicate = HasCvtFP8ByteSel
}
class ThreeOp_i32_Pats <SDPatternOperator op1, SDPatternOperator op2, Instruction inst> : GCNPat <
@ -1850,7 +1850,7 @@ let SubtargetPredicate = isGFX1250Plus in {
// These instructions have non-standard use of op_sel. They are using bits 2 and 3 of opsel
// to select a byte in the vdst. Bits 0 and 1 are unused.
let Constraints = "$vdst = $vdst_in" in {
let Constraints = "$vdst = $vdst_in", SubtargetPredicate = HasF16FP8ConversionInsts in {
defm V_CVT_SR_FP8_F16 : VOP3Inst_t16_with_profiles<"v_cvt_sr_fp8_f16", VOP3_CVT_SR_F8_F16_Profile,
VOP3_CVT_SR_F8_F16_True16_Profile, VOP3_CVT_SR_F8_F16_Fake16_Profile>;
defm V_CVT_SR_BF8_F16 : VOP3Inst_t16_with_profiles<"v_cvt_sr_bf8_f16", VOP3_CVT_SR_F8_F16_Profile,
@ -1919,13 +1919,15 @@ let SubtargetPredicate = isGFX1250Plus in {
defm V_CVT_SCALEF32_SR_PK16_FP6_F32 : VOP3Inst<"v_cvt_scalef32_sr_pk16_fp6_f32", VOP3_CVT_SCALEF32_PK_F864_Profile<VOP_V3I32_V16F32_I32_F32>, int_amdgcn_cvt_scalef32_sr_pk16_fp6_f32>;
} // End Constraints = "@earlyclobber $vdst"
let True16Predicate = UseRealTrue16Insts in {
def : Cvt_SR_F8_ByteSel_Pat<int_amdgcn_cvt_sr_fp8_f16, V_CVT_SR_FP8_F16_t16_e64, f16>;
def : Cvt_SR_F8_ByteSel_Pat<int_amdgcn_cvt_sr_bf8_f16, V_CVT_SR_BF8_F16_t16_e64, f16>;
}
let True16Predicate = UseFakeTrue16Insts in {
def : Cvt_SR_F8_ByteSel_Pat<int_amdgcn_cvt_sr_fp8_f16, V_CVT_SR_FP8_F16_fake16_e64, f16>;
def : Cvt_SR_F8_ByteSel_Pat<int_amdgcn_cvt_sr_bf8_f16, V_CVT_SR_BF8_F16_fake16_e64, f16>;
let SubtargetPredicate = HasF16FP8ConversionInsts in {
let True16Predicate = UseRealTrue16Insts in {
def : Cvt_SR_F8_ByteSel_Pat<int_amdgcn_cvt_sr_fp8_f16, V_CVT_SR_FP8_F16_t16_e64, f16>;
def : Cvt_SR_F8_ByteSel_Pat<int_amdgcn_cvt_sr_bf8_f16, V_CVT_SR_BF8_F16_t16_e64, f16>;
}
let True16Predicate = UseFakeTrue16Insts in {
def : Cvt_SR_F8_ByteSel_Pat<int_amdgcn_cvt_sr_fp8_f16, V_CVT_SR_FP8_F16_fake16_e64, f16>;
def : Cvt_SR_F8_ByteSel_Pat<int_amdgcn_cvt_sr_bf8_f16, V_CVT_SR_BF8_F16_fake16_e64, f16>;
}
}
} // End SubtargetPredicate = isGFX1250Plus