aco: implement VK_KHR_shader_float_controls
This actually supports more of the extension than the LLVM backend but we can't enable it because ACO doesn't work with all stages yet. With more of it enabled, some CTS tests fail because our 64-bit sqrt is very imprecise. I can't find any precision requirements for it anywhere, so I'm thinking it might be a CTS issue. Signed-off-by: Rhys Perry <pendingchaos02@gmail.com> Reviewed-by: Daniel Schürmann <daniel@schuermann.dev>
This commit is contained in:
@@ -647,6 +647,61 @@ void emit_bcsel(isel_context *ctx, nir_alu_instr *instr, Temp dst)
|
||||
bld.sop2(aco_opcode::s_andn2_b64, bld.def(s2), bld.def(s1, scc), els, cond));
|
||||
}
|
||||
|
||||
void emit_scaled_op(isel_context *ctx, Builder& bld, Definition dst, Temp val,
|
||||
aco_opcode op, uint32_t undo)
|
||||
{
|
||||
/* multiply by 16777216 to handle denormals */
|
||||
Temp is_denormal = bld.vopc(aco_opcode::v_cmp_class_f32, bld.hint_vcc(bld.def(s2)),
|
||||
as_vgpr(ctx, val), bld.copy(bld.def(v1), Operand((1u << 7) | (1u << 4))));
|
||||
Temp scaled = bld.vop2(aco_opcode::v_mul_f32, bld.def(v1), Operand(0x4b800000u), val);
|
||||
scaled = bld.vop1(op, bld.def(v1), scaled);
|
||||
scaled = bld.vop2(aco_opcode::v_mul_f32, bld.def(v1), Operand(undo), scaled);
|
||||
|
||||
Temp not_scaled = bld.vop1(op, bld.def(v1), val);
|
||||
|
||||
bld.vop2(aco_opcode::v_cndmask_b32, dst, not_scaled, scaled, is_denormal);
|
||||
}
|
||||
|
||||
void emit_rcp(isel_context *ctx, Builder& bld, Definition dst, Temp val)
|
||||
{
|
||||
if (ctx->block->fp_mode.denorm32 == 0) {
|
||||
bld.vop1(aco_opcode::v_rcp_f32, dst, val);
|
||||
return;
|
||||
}
|
||||
|
||||
emit_scaled_op(ctx, bld, dst, val, aco_opcode::v_rcp_f32, 0x4b800000u);
|
||||
}
|
||||
|
||||
void emit_rsq(isel_context *ctx, Builder& bld, Definition dst, Temp val)
|
||||
{
|
||||
if (ctx->block->fp_mode.denorm32 == 0) {
|
||||
bld.vop1(aco_opcode::v_rsq_f32, dst, val);
|
||||
return;
|
||||
}
|
||||
|
||||
emit_scaled_op(ctx, bld, dst, val, aco_opcode::v_rsq_f32, 0x45800000u);
|
||||
}
|
||||
|
||||
void emit_sqrt(isel_context *ctx, Builder& bld, Definition dst, Temp val)
|
||||
{
|
||||
if (ctx->block->fp_mode.denorm32 == 0) {
|
||||
bld.vop1(aco_opcode::v_sqrt_f32, dst, val);
|
||||
return;
|
||||
}
|
||||
|
||||
emit_scaled_op(ctx, bld, dst, val, aco_opcode::v_sqrt_f32, 0x39800000u);
|
||||
}
|
||||
|
||||
void emit_log2(isel_context *ctx, Builder& bld, Definition dst, Temp val)
|
||||
{
|
||||
if (ctx->block->fp_mode.denorm32 == 0) {
|
||||
bld.vop1(aco_opcode::v_log_f32, dst, val);
|
||||
return;
|
||||
}
|
||||
|
||||
emit_scaled_op(ctx, bld, dst, val, aco_opcode::v_log_f32, 0xc1c00000u);
|
||||
}
|
||||
|
||||
void visit_alu_instr(isel_context *ctx, nir_alu_instr *instr)
|
||||
{
|
||||
if (!instr->dest.dest.is_ssa) {
|
||||
@@ -1399,7 +1454,7 @@ void visit_alu_instr(isel_context *ctx, nir_alu_instr *instr)
|
||||
}
|
||||
case nir_op_frsq: {
|
||||
if (dst.size() == 1) {
|
||||
emit_vop1_instruction(ctx, instr, aco_opcode::v_rsq_f32, dst);
|
||||
emit_rsq(ctx, bld, Definition(dst), get_alu_src(ctx, instr->src[0]));
|
||||
} else if (dst.size() == 2) {
|
||||
emit_vop1_instruction(ctx, instr, aco_opcode::v_rsq_f64, dst);
|
||||
} else {
|
||||
@@ -1412,8 +1467,12 @@ void visit_alu_instr(isel_context *ctx, nir_alu_instr *instr)
|
||||
case nir_op_fneg: {
|
||||
Temp src = get_alu_src(ctx, instr->src[0]);
|
||||
if (dst.size() == 1) {
|
||||
if (ctx->block->fp_mode.must_flush_denorms32)
|
||||
src = bld.vop2(aco_opcode::v_mul_f32, bld.def(v1), Operand(0x3f800000u), as_vgpr(ctx, src));
|
||||
bld.vop2(aco_opcode::v_xor_b32, Definition(dst), Operand(0x80000000u), as_vgpr(ctx, src));
|
||||
} else if (dst.size() == 2) {
|
||||
if (ctx->block->fp_mode.must_flush_denorms16_64)
|
||||
src = bld.vop3(aco_opcode::v_mul_f64, bld.def(v2), Operand(0x3FF0000000000000lu), as_vgpr(ctx, src));
|
||||
Temp upper = bld.tmp(v1), lower = bld.tmp(v1);
|
||||
bld.pseudo(aco_opcode::p_split_vector, Definition(lower), Definition(upper), src);
|
||||
upper = bld.vop2(aco_opcode::v_xor_b32, bld.def(v1), Operand(0x80000000u), upper);
|
||||
@@ -1428,8 +1487,12 @@ void visit_alu_instr(isel_context *ctx, nir_alu_instr *instr)
|
||||
case nir_op_fabs: {
|
||||
Temp src = get_alu_src(ctx, instr->src[0]);
|
||||
if (dst.size() == 1) {
|
||||
if (ctx->block->fp_mode.must_flush_denorms32)
|
||||
src = bld.vop2(aco_opcode::v_mul_f32, bld.def(v1), Operand(0x3f800000u), as_vgpr(ctx, src));
|
||||
bld.vop2(aco_opcode::v_and_b32, Definition(dst), Operand(0x7FFFFFFFu), as_vgpr(ctx, src));
|
||||
} else if (dst.size() == 2) {
|
||||
if (ctx->block->fp_mode.must_flush_denorms16_64)
|
||||
src = bld.vop3(aco_opcode::v_mul_f64, bld.def(v2), Operand(0x3FF0000000000000lu), as_vgpr(ctx, src));
|
||||
Temp upper = bld.tmp(v1), lower = bld.tmp(v1);
|
||||
bld.pseudo(aco_opcode::p_split_vector, Definition(lower), Definition(upper), src);
|
||||
upper = bld.vop2(aco_opcode::v_and_b32, bld.def(v1), Operand(0x7FFFFFFFu), upper);
|
||||
@@ -1458,7 +1521,7 @@ void visit_alu_instr(isel_context *ctx, nir_alu_instr *instr)
|
||||
}
|
||||
case nir_op_flog2: {
|
||||
if (dst.size() == 1) {
|
||||
emit_vop1_instruction(ctx, instr, aco_opcode::v_log_f32, dst);
|
||||
emit_log2(ctx, bld, Definition(dst), get_alu_src(ctx, instr->src[0]));
|
||||
} else {
|
||||
fprintf(stderr, "Unimplemented NIR instr bit size: ");
|
||||
nir_print_instr(&instr->instr, stderr);
|
||||
@@ -1468,7 +1531,7 @@ void visit_alu_instr(isel_context *ctx, nir_alu_instr *instr)
|
||||
}
|
||||
case nir_op_frcp: {
|
||||
if (dst.size() == 1) {
|
||||
emit_vop1_instruction(ctx, instr, aco_opcode::v_rcp_f32, dst);
|
||||
emit_rcp(ctx, bld, Definition(dst), get_alu_src(ctx, instr->src[0]));
|
||||
} else if (dst.size() == 2) {
|
||||
emit_vop1_instruction(ctx, instr, aco_opcode::v_rcp_f64, dst);
|
||||
} else {
|
||||
@@ -1490,7 +1553,7 @@ void visit_alu_instr(isel_context *ctx, nir_alu_instr *instr)
|
||||
}
|
||||
case nir_op_fsqrt: {
|
||||
if (dst.size() == 1) {
|
||||
emit_vop1_instruction(ctx, instr, aco_opcode::v_sqrt_f32, dst);
|
||||
emit_sqrt(ctx, bld, Definition(dst), get_alu_src(ctx, instr->src[0]));
|
||||
} else if (dst.size() == 2) {
|
||||
emit_vop1_instruction(ctx, instr, aco_opcode::v_sqrt_f64, dst);
|
||||
} else {
|
||||
@@ -2040,8 +2103,12 @@ void visit_alu_instr(isel_context *ctx, nir_alu_instr *instr)
|
||||
Temp src0 = bld.tmp(v1);
|
||||
Temp src1 = bld.tmp(v1);
|
||||
bld.pseudo(aco_opcode::p_split_vector, Definition(src0), Definition(src1), src);
|
||||
bld.vop3(aco_opcode::v_cvt_pkrtz_f16_f32, Definition(dst), src0, src1);
|
||||
|
||||
if (!ctx->block->fp_mode.care_about_round32 || ctx->block->fp_mode.round32 == fp_round_tz)
|
||||
bld.vop3(aco_opcode::v_cvt_pkrtz_f16_f32, Definition(dst), src0, src1);
|
||||
else
|
||||
bld.vop3(aco_opcode::v_cvt_pk_u16_u32, Definition(dst),
|
||||
bld.vop1(aco_opcode::v_cvt_f32_f16, bld.def(v1), src0),
|
||||
bld.vop1(aco_opcode::v_cvt_f32_f16, bld.def(v1), src1));
|
||||
} else {
|
||||
fprintf(stderr, "Unimplemented NIR instr bit size: ");
|
||||
nir_print_instr(&instr->instr, stderr);
|
||||
@@ -2074,7 +2141,8 @@ void visit_alu_instr(isel_context *ctx, nir_alu_instr *instr)
|
||||
break;
|
||||
}
|
||||
case nir_op_fquantize2f16: {
|
||||
Temp f16 = bld.vop1(aco_opcode::v_cvt_f16_f32, bld.def(v1), get_alu_src(ctx, instr->src[0]));
|
||||
Temp src = get_alu_src(ctx, instr->src[0]);
|
||||
Temp f16 = bld.vop1(aco_opcode::v_cvt_f16_f32, bld.def(v1), src);
|
||||
|
||||
Temp mask = bld.copy(bld.def(s1), Operand(0x36Fu)); /* value is NOT negative/positive denormal value */
|
||||
|
||||
@@ -2083,7 +2151,12 @@ void visit_alu_instr(isel_context *ctx, nir_alu_instr *instr)
|
||||
|
||||
Temp f32 = bld.vop1(aco_opcode::v_cvt_f32_f16, bld.def(v1), f16);
|
||||
|
||||
bld.vop2(aco_opcode::v_cndmask_b32, Definition(dst), Operand(0u), f32, cmp_res);
|
||||
if (ctx->block->fp_mode.preserve_signed_zero_inf_nan32) {
|
||||
Temp copysign_0 = bld.vop2(aco_opcode::v_mul_f32, bld.def(v1), Operand(0u), as_vgpr(ctx, src));
|
||||
bld.vop2(aco_opcode::v_cndmask_b32, Definition(dst), copysign_0, f32, cmp_res);
|
||||
} else {
|
||||
bld.vop2(aco_opcode::v_cndmask_b32, Definition(dst), Operand(0u), f32, cmp_res);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case nir_op_bfm: {
|
||||
@@ -7593,6 +7666,56 @@ void handle_bc_optimize(isel_context *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
void setup_fp_mode(isel_context *ctx, nir_shader *shader)
|
||||
{
|
||||
Program *program = ctx->program;
|
||||
|
||||
unsigned float_controls = shader->info.float_controls_execution_mode;
|
||||
|
||||
program->next_fp_mode.preserve_signed_zero_inf_nan32 =
|
||||
float_controls & FLOAT_CONTROLS_SIGNED_ZERO_INF_NAN_PRESERVE_FP32;
|
||||
program->next_fp_mode.preserve_signed_zero_inf_nan16_64 =
|
||||
float_controls & (FLOAT_CONTROLS_SIGNED_ZERO_INF_NAN_PRESERVE_FP16 |
|
||||
FLOAT_CONTROLS_SIGNED_ZERO_INF_NAN_PRESERVE_FP64);
|
||||
|
||||
program->next_fp_mode.must_flush_denorms32 =
|
||||
float_controls & FLOAT_CONTROLS_DENORM_FLUSH_TO_ZERO_FP32;
|
||||
program->next_fp_mode.must_flush_denorms16_64 =
|
||||
float_controls & (FLOAT_CONTROLS_DENORM_FLUSH_TO_ZERO_FP16 |
|
||||
FLOAT_CONTROLS_DENORM_FLUSH_TO_ZERO_FP64);
|
||||
|
||||
program->next_fp_mode.care_about_round32 =
|
||||
float_controls & (FLOAT_CONTROLS_ROUNDING_MODE_RTZ_FP32 | FLOAT_CONTROLS_ROUNDING_MODE_RTE_FP32);
|
||||
|
||||
program->next_fp_mode.care_about_round16_64 =
|
||||
float_controls & (FLOAT_CONTROLS_ROUNDING_MODE_RTZ_FP16 | FLOAT_CONTROLS_ROUNDING_MODE_RTZ_FP64 |
|
||||
FLOAT_CONTROLS_ROUNDING_MODE_RTE_FP16 | FLOAT_CONTROLS_ROUNDING_MODE_RTE_FP64);
|
||||
|
||||
/* default to preserving fp16 and fp64 denorms, since it's free */
|
||||
if (program->next_fp_mode.must_flush_denorms16_64)
|
||||
program->next_fp_mode.denorm16_64 = 0;
|
||||
else
|
||||
program->next_fp_mode.denorm16_64 = fp_denorm_keep;
|
||||
|
||||
/* preserving fp32 denorms is expensive, so only do it if asked */
|
||||
if (float_controls & FLOAT_CONTROLS_DENORM_PRESERVE_FP32)
|
||||
program->next_fp_mode.denorm32 = fp_denorm_keep;
|
||||
else
|
||||
program->next_fp_mode.denorm32 = 0;
|
||||
|
||||
if (float_controls & FLOAT_CONTROLS_ROUNDING_MODE_RTZ_FP32)
|
||||
program->next_fp_mode.round32 = fp_round_tz;
|
||||
else
|
||||
program->next_fp_mode.round32 = fp_round_ne;
|
||||
|
||||
if (float_controls & (FLOAT_CONTROLS_ROUNDING_MODE_RTZ_FP16 | FLOAT_CONTROLS_ROUNDING_MODE_RTZ_FP64))
|
||||
program->next_fp_mode.round16_64 = fp_round_tz;
|
||||
else
|
||||
program->next_fp_mode.round16_64 = fp_round_ne;
|
||||
|
||||
ctx->block->fp_mode = program->next_fp_mode;
|
||||
}
|
||||
|
||||
void select_program(Program *program,
|
||||
unsigned shader_count,
|
||||
struct nir_shader *const *shaders,
|
||||
@@ -7606,6 +7729,8 @@ void select_program(Program *program,
|
||||
nir_shader *nir = shaders[i];
|
||||
init_context(&ctx, nir);
|
||||
|
||||
setup_fp_mode(&ctx, nir);
|
||||
|
||||
if (!i) {
|
||||
add_startpgm(&ctx); /* needs to be after init_context() for FS */
|
||||
append_logical_start(ctx.block);
|
||||
@@ -7648,6 +7773,8 @@ void select_program(Program *program,
|
||||
ralloc_free(ctx.divergent_vals);
|
||||
}
|
||||
|
||||
program->config->float_mode = program->blocks[0].fp_mode.val;
|
||||
|
||||
append_logical_end(ctx.block);
|
||||
ctx.block->kind |= block_kind_uniform;
|
||||
Builder bld(ctx.program, ctx.block);
|
||||
|
||||
Reference in New Issue
Block a user