#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h" namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter { void EmitPerInvocationMask(EmitterState& state, const IR::Operand& dst, uint32_t value_bool) { const auto value = state.builder.AllocateId(); state.builder.AddFunction({OpSelect, state.uint_type, value, value_bool, ConstantU32(state, 1), ConstantU32(state, 0)}); EmitStoreU32(state, dst, value); EmitStoreU32(state, OffsetRegisterOperand(dst, 1), ConstantU32(state, 0)); } uint32_t EmitLogicalBinary(EmitterState& state, uint32_t opcode, uint32_t lhs, uint32_t rhs) { uint32_t logical_opcode = OpLogicalNotEqual; switch (opcode) { case OpBitwiseAnd: logical_opcode = OpLogicalAnd; break; case OpBitwiseOr: logical_opcode = OpLogicalOr; break; default: break; } const auto value = state.builder.AllocateId(); state.builder.AddFunction({logical_opcode, state.bool_type, value, lhs, rhs}); return value; } void EmitAbsI32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitValueLoad(state, inst.src[0]); const auto neg = state.builder.AllocateId(); const auto cond = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpSNegate, state.uint_type, neg, src}); state.builder.AddFunction({OpSLessThan, state.bool_type, cond, src, ConstantU32(state, 0)}); state.builder.AddFunction({OpSelect, state.uint_type, ret, cond, neg, src}); EmitStoreU32(state, inst.dst, ret); } void EmitUnaryU32(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto src = EmitValueLoad(state, inst.src[0]); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.uint_type, ret, src}); EmitStoreU32(state, inst.dst, ret); } void EmitUnaryU64(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { if (state.per_invocation_masks) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpLogicalNot, state.bool_type, ret, EmitLaneMaskOperandActiveBool(state, inst.src[0])}); EmitPerInvocationMask(state, inst.dst, ret); return; } const auto low = EmitSequentialValueLoad(state, inst.src[0], 0); const auto high = EmitSequentialValueLoad(state, inst.src[0], 1); const auto ret_low = state.builder.AllocateId(); const auto ret_high = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.uint_type, ret_low, low}); state.builder.AddFunction({opcode, state.uint_type, ret_high, high}); EmitStoreU32(state, inst.dst, ret_low); EmitStoreU32(state, OffsetRegisterOperand(inst.dst, 1), ret_high); } void EmitFindLsbU32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitValueLoad(state, inst.src[0]); const auto i32 = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.int_type, i32, state.glsl_std450, GlslFindILsb, src}); state.builder.AddFunction({OpBitcast, state.uint_type, u32, i32}); EmitStoreU32(state, inst.dst, u32); } void EmitFindMsbFromHighU32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitValueLoad(state, inst.src[0]); const auto i32 = state.builder.AllocateId(); const auto msb = state.builder.AllocateId(); const auto pos = state.builder.AllocateId(); const auto non_zero = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.int_type, i32, state.glsl_std450, GlslFindUMsb, src}); state.builder.AddFunction({OpBitcast, state.uint_type, msb, i32}); state.builder.AddFunction({OpISub, state.uint_type, pos, ConstantU32(state, 31), msb}); state.builder.AddFunction({OpINotEqual, state.bool_type, non_zero, src, ConstantU32(state, 0)}); state.builder.AddFunction( {OpSelect, state.uint_type, ret, non_zero, pos, ConstantU32(state, 0xffffffffu)}); EmitStoreU32(state, inst.dst, ret); } void EmitFindMsbFromHighU64(EmitterState& state, const IR::Instruction& inst) { const auto low = EmitSequentialValueLoad(state, inst.src[0], 0); const auto high = EmitSequentialValueLoad(state, inst.src[0], 1); const auto high_i32 = state.builder.AllocateId(); const auto high_msb = state.builder.AllocateId(); const auto high_pos = state.builder.AllocateId(); const auto high_non_zero = state.builder.AllocateId(); const auto low_i32 = state.builder.AllocateId(); const auto low_msb = state.builder.AllocateId(); const auto low_pos = state.builder.AllocateId(); const auto low_total = state.builder.AllocateId(); const auto low_non_zero = state.builder.AllocateId(); const auto low_ret = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.int_type, high_i32, state.glsl_std450, GlslFindUMsb, high}); state.builder.AddFunction({OpBitcast, state.uint_type, high_msb, high_i32}); state.builder.AddFunction( {OpISub, state.uint_type, high_pos, ConstantU32(state, 31), high_msb}); state.builder.AddFunction( {OpINotEqual, state.bool_type, high_non_zero, high, ConstantU32(state, 0)}); state.builder.AddFunction( {OpExtInst, state.int_type, low_i32, state.glsl_std450, GlslFindUMsb, low}); state.builder.AddFunction({OpBitcast, state.uint_type, low_msb, low_i32}); state.builder.AddFunction({OpISub, state.uint_type, low_pos, ConstantU32(state, 31), low_msb}); state.builder.AddFunction( {OpIAdd, state.uint_type, low_total, low_pos, ConstantU32(state, 32)}); state.builder.AddFunction( {OpINotEqual, state.bool_type, low_non_zero, low, ConstantU32(state, 0)}); state.builder.AddFunction({OpSelect, state.uint_type, low_ret, low_non_zero, low_total, ConstantU32(state, 0xffffffffu)}); state.builder.AddFunction({OpSelect, state.uint_type, ret, high_non_zero, high_pos, low_ret}); EmitStoreU32(state, inst.dst, ret); } void EmitBitCountU64(EmitterState& state, const IR::Instruction& inst) { const auto low = EmitSequentialValueLoad(state, inst.src[0], 0); const auto high = EmitSequentialValueLoad(state, inst.src[0], 1); const auto low_count = state.builder.AllocateId(); const auto high_count = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitCount, state.uint_type, low_count, low}); state.builder.AddFunction({OpBitCount, state.uint_type, high_count, high}); state.builder.AddFunction({OpIAdd, state.uint_type, ret, low_count, high_count}); EmitStoreU32(state, inst.dst, ret); } void EmitBitCountU32(EmitterState& state, const IR::Instruction& inst) { const auto bits = EmitValueLoad(state, inst.src[0]); const auto count = state.builder.AllocateId(); state.builder.AddFunction({OpBitCount, state.uint_type, count, bits}); EmitStoreU32(state, inst.dst, count); } void EmitBitCountAddU32(EmitterState& state, const IR::Instruction& inst) { const auto bits = EmitValueLoad(state, inst.src[0]); const auto add = EmitValueLoad(state, inst.src[1]); const auto count = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitCount, state.uint_type, count, bits}); state.builder.AddFunction({OpIAdd, state.uint_type, ret, count, add}); EmitStoreU32(state, inst.dst, ret); } void EmitBinaryU32(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto lhs = EmitValueLoad(state, inst.src[0]); auto rhs = EmitValueLoad(state, inst.src[1]); if (opcode == OpShiftLeftLogical || opcode == OpShiftRightLogical || opcode == OpShiftRightArithmetic) { rhs = EmitAndConstant(state, rhs, 31u); } const auto ret = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.uint_type, ret, lhs, rhs}); EmitStoreU32(state, inst.dst, ret); } void EmitBinaryNotRhsU32(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto not_rhs = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpNot, state.uint_type, not_rhs, rhs}); state.builder.AddFunction({opcode, state.uint_type, ret, lhs, not_rhs}); EmitStoreU32(state, inst.dst, ret); } void EmitBinaryThenNotU32(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto tmp = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.uint_type, tmp, lhs, rhs}); state.builder.AddFunction({OpNot, state.uint_type, ret, tmp}); EmitStoreU32(state, inst.dst, ret); } void EmitMaskedBitCountU32(EmitterState& state, const IR::Instruction& inst, uint32_t exec_index) { const auto bits = EmitValueLoad(state, inst.src[0]); const auto add = EmitValueLoad(state, inst.src[1]); const auto thread_mask = EmitThreadMaskBelowPartU32(state, exec_index); const auto active_bits = state.builder.AllocateId(); const auto count = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitwiseAnd, state.uint_type, active_bits, bits, thread_mask}); state.builder.AddFunction({OpBitCount, state.uint_type, count, active_bits}); state.builder.AddFunction({OpIAdd, state.uint_type, ret, count, add}); EmitStoreU32(state, inst.dst, ret); } uint32_t EmitMask5U32(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitwiseAnd, state.uint_type, ret, value, ConstantU32(state, 31)}); return ret; } void EmitChainedBinaryU32(EmitterState& state, const IR::Instruction& inst, uint32_t first_op, uint32_t second_op, bool mask_first_rhs, bool mask_second_rhs) { const auto src0 = EmitValueLoad(state, inst.src[0]); const auto src1_raw = EmitValueLoad(state, inst.src[1]); const auto src2_raw = EmitValueLoad(state, inst.src[2]); const auto src1 = mask_first_rhs ? EmitMask5U32(state, src1_raw) : src1_raw; const auto src2 = mask_second_rhs ? EmitMask5U32(state, src2_raw) : src2_raw; const auto tmp = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({first_op, state.uint_type, tmp, src0, src1}); state.builder.AddFunction({second_op, state.uint_type, ret, tmp, src2}); EmitStoreU32(state, inst.dst, ret); } void EmitBinaryU64(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { if (state.per_invocation_masks) { const auto ret = EmitLogicalBinary(state, opcode, EmitLaneMaskOperandActiveBool(state, inst.src[0]), EmitLaneMaskOperandActiveBool(state, inst.src[1])); EmitPerInvocationMask(state, inst.dst, ret); return; } const auto lhs_low = EmitSequentialValueLoad(state, inst.src[0], 0); const auto lhs_high = EmitSequentialValueLoad(state, inst.src[0], 1); const auto rhs_low = EmitSequentialValueLoad(state, inst.src[1], 0); const auto rhs_high = EmitSequentialValueLoad(state, inst.src[1], 1); const auto ret_low = state.builder.AllocateId(); const auto ret_high = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.uint_type, ret_low, lhs_low, rhs_low}); state.builder.AddFunction({opcode, state.uint_type, ret_high, lhs_high, rhs_high}); EmitStoreU32(state, inst.dst, ret_low); EmitStoreU32(state, OffsetRegisterOperand(inst.dst, 1), ret_high); } void EmitBinaryNotRhsU64(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { if (state.per_invocation_masks) { const auto not_rhs = state.builder.AllocateId(); state.builder.AddFunction({OpLogicalNot, state.bool_type, not_rhs, EmitLaneMaskOperandActiveBool(state, inst.src[1])}); const auto ret = EmitLogicalBinary( state, opcode, EmitLaneMaskOperandActiveBool(state, inst.src[0]), not_rhs); EmitPerInvocationMask(state, inst.dst, ret); return; } const auto lhs_low = EmitSequentialValueLoad(state, inst.src[0], 0); const auto lhs_high = EmitSequentialValueLoad(state, inst.src[0], 1); const auto rhs_low = EmitSequentialValueLoad(state, inst.src[1], 0); const auto rhs_high = EmitSequentialValueLoad(state, inst.src[1], 1); const auto not_low = state.builder.AllocateId(); const auto not_high = state.builder.AllocateId(); const auto ret_low = state.builder.AllocateId(); const auto ret_high = state.builder.AllocateId(); state.builder.AddFunction({OpNot, state.uint_type, not_low, rhs_low}); state.builder.AddFunction({OpNot, state.uint_type, not_high, rhs_high}); state.builder.AddFunction({opcode, state.uint_type, ret_low, lhs_low, not_low}); state.builder.AddFunction({opcode, state.uint_type, ret_high, lhs_high, not_high}); EmitStoreU32(state, inst.dst, ret_low); EmitStoreU32(state, OffsetRegisterOperand(inst.dst, 1), ret_high); } void EmitBinaryThenNotU64(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { if (state.per_invocation_masks) { const auto value = EmitLogicalBinary(state, opcode, EmitLaneMaskOperandActiveBool(state, inst.src[0]), EmitLaneMaskOperandActiveBool(state, inst.src[1])); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpLogicalNot, state.bool_type, ret, value}); EmitPerInvocationMask(state, inst.dst, ret); return; } const auto lhs_low = EmitSequentialValueLoad(state, inst.src[0], 0); const auto lhs_high = EmitSequentialValueLoad(state, inst.src[0], 1); const auto rhs_low = EmitSequentialValueLoad(state, inst.src[1], 0); const auto rhs_high = EmitSequentialValueLoad(state, inst.src[1], 1); const auto tmp_low = state.builder.AllocateId(); const auto tmp_high = state.builder.AllocateId(); const auto ret_low = state.builder.AllocateId(); const auto ret_high = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.uint_type, tmp_low, lhs_low, rhs_low}); state.builder.AddFunction({opcode, state.uint_type, tmp_high, lhs_high, rhs_high}); state.builder.AddFunction({OpNot, state.uint_type, ret_low, tmp_low}); state.builder.AddFunction({OpNot, state.uint_type, ret_high, tmp_high}); EmitStoreU32(state, inst.dst, ret_low); EmitStoreU32(state, OffsetRegisterOperand(inst.dst, 1), ret_high); } void EmitSelectU64(EmitterState& state, const IR::Instruction& inst) { const auto cond = EmitConditionBool(state, inst.src[0]); const auto true_low = EmitSequentialValueLoad(state, inst.src[1], 0); const auto true_high = EmitSequentialValueLoad(state, inst.src[1], 1); const auto false_low = EmitSequentialValueLoad(state, inst.src[2], 0); const auto false_high = EmitSequentialValueLoad(state, inst.src[2], 1); const auto ret_low = state.builder.AllocateId(); const auto ret_high = state.builder.AllocateId(); state.builder.AddFunction({OpSelect, state.uint_type, ret_low, cond, true_low, false_low}); state.builder.AddFunction({OpSelect, state.uint_type, ret_high, cond, true_high, false_high}); EmitStoreU32(state, inst.dst, ret_low); EmitStoreU32(state, OffsetRegisterOperand(inst.dst, 1), ret_high); } uint32_t EmitSelectValueU32(EmitterState& state, uint32_t cond, uint32_t true_value, uint32_t false_value) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpSelect, state.uint_type, ret, cond, true_value, false_value}); return ret; } void EmitStoreSccBool(EmitterState& state, uint32_t cond) { const auto value = EmitSelectValueU32(state, cond, ConstantU32(state, 1), ConstantU32(state, 0)); EmitStoreU32(state, SccOperand(), value); } uint32_t EmitAndConstant(EmitterState& state, uint32_t value, uint32_t mask); void EmitShiftLeftLogicalU64Values(EmitterState& state, uint32_t low, uint32_t high, uint32_t shift, uint32_t& out_low, uint32_t& out_high) { const auto amount = EmitAndConstant(state, shift, 63u); const auto dword_shift = EmitAndConstant(state, amount, 31u); const auto less32 = state.builder.AllocateId(); const auto non_zero = state.builder.AllocateId(); const auto carry_count_base = state.builder.AllocateId(); const auto carry_count = state.builder.AllocateId(); const auto low_part = state.builder.AllocateId(); const auto high_part = state.builder.AllocateId(); const auto carry_part = state.builder.AllocateId(); const auto carry = state.builder.AllocateId(); const auto high_less = state.builder.AllocateId(); const auto high_ge = state.builder.AllocateId(); state.builder.AddFunction( {OpULessThan, state.bool_type, less32, amount, ConstantU32(state, 32)}); state.builder.AddFunction( {OpINotEqual, state.bool_type, non_zero, amount, ConstantU32(state, 0)}); state.builder.AddFunction( {OpISub, state.uint_type, carry_count_base, ConstantU32(state, 32), dword_shift}); state.builder.AddFunction( {OpBitwiseAnd, state.uint_type, carry_count, carry_count_base, ConstantU32(state, 31)}); state.builder.AddFunction({OpShiftLeftLogical, state.uint_type, low_part, low, dword_shift}); state.builder.AddFunction({OpShiftLeftLogical, state.uint_type, high_part, high, dword_shift}); state.builder.AddFunction({OpShiftRightLogical, state.uint_type, carry_part, low, carry_count}); state.builder.AddFunction( {OpSelect, state.uint_type, carry, non_zero, carry_part, ConstantU32(state, 0)}); state.builder.AddFunction({OpBitwiseOr, state.uint_type, high_less, high_part, carry}); state.builder.AddFunction({OpShiftLeftLogical, state.uint_type, high_ge, low, dword_shift}); out_low = EmitSelectValueU32(state, less32, low_part, ConstantU32(state, 0)); out_high = EmitSelectValueU32(state, less32, high_less, high_ge); } void EmitShiftLeftLogicalU64(EmitterState& state, const IR::Instruction& inst) { const auto low = EmitSequentialValueLoad(state, inst.src[0], 0); const auto high = EmitSequentialValueLoad(state, inst.src[0], 1); const auto shift = EmitValueLoad(state, inst.src[1]); uint32_t ret_low = 0; uint32_t ret_high = 0; EmitShiftLeftLogicalU64Values(state, low, high, shift, ret_low, ret_high); EmitStoreU32(state, inst.dst, ret_low); EmitStoreU32(state, OffsetRegisterOperand(inst.dst, 1), ret_high); } void EmitShiftRightLogicalU64Values(EmitterState& state, uint32_t low, uint32_t high, uint32_t shift, uint32_t& out_low, uint32_t& out_high) { const auto amount = EmitAndConstant(state, shift, 63u); const auto dword_shift = EmitAndConstant(state, amount, 31u); const auto less32 = state.builder.AllocateId(); const auto non_zero = state.builder.AllocateId(); const auto carry_count_base = state.builder.AllocateId(); const auto carry_count = state.builder.AllocateId(); const auto low_part = state.builder.AllocateId(); const auto high_part = state.builder.AllocateId(); const auto carry_part = state.builder.AllocateId(); const auto carry = state.builder.AllocateId(); const auto low_less = state.builder.AllocateId(); const auto low_ge = state.builder.AllocateId(); state.builder.AddFunction( {OpULessThan, state.bool_type, less32, amount, ConstantU32(state, 32)}); state.builder.AddFunction( {OpINotEqual, state.bool_type, non_zero, amount, ConstantU32(state, 0)}); state.builder.AddFunction( {OpISub, state.uint_type, carry_count_base, ConstantU32(state, 32), dword_shift}); state.builder.AddFunction( {OpBitwiseAnd, state.uint_type, carry_count, carry_count_base, ConstantU32(state, 31)}); state.builder.AddFunction({OpShiftRightLogical, state.uint_type, low_part, low, dword_shift}); state.builder.AddFunction({OpShiftRightLogical, state.uint_type, high_part, high, dword_shift}); state.builder.AddFunction({OpShiftLeftLogical, state.uint_type, carry_part, high, carry_count}); state.builder.AddFunction( {OpSelect, state.uint_type, carry, non_zero, carry_part, ConstantU32(state, 0)}); state.builder.AddFunction({OpBitwiseOr, state.uint_type, low_less, low_part, carry}); state.builder.AddFunction({OpShiftRightLogical, state.uint_type, low_ge, high, dword_shift}); out_low = EmitSelectValueU32(state, less32, low_less, low_ge); out_high = EmitSelectValueU32(state, less32, high_part, ConstantU32(state, 0)); } void EmitShiftRightLogicalU64(EmitterState& state, const IR::Instruction& inst) { const auto low = EmitSequentialValueLoad(state, inst.src[0], 0); const auto high = EmitSequentialValueLoad(state, inst.src[0], 1); const auto shift = EmitValueLoad(state, inst.src[1]); uint32_t ret_low = 0; uint32_t ret_high = 0; EmitShiftRightLogicalU64Values(state, low, high, shift, ret_low, ret_high); EmitStoreU32(state, inst.dst, ret_low); EmitStoreU32(state, OffsetRegisterOperand(inst.dst, 1), ret_high); } void EmitAdd3U32(EmitterState& state, const IR::Instruction& inst) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto add = EmitValueLoad(state, inst.src[2]); const auto tmp = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpIAdd, state.uint_type, tmp, lhs, rhs}); state.builder.AddFunction({OpIAdd, state.uint_type, ret, tmp, add}); EmitStoreU32(state, inst.dst, ret); } uint32_t EmitAndConstant(EmitterState& state, uint32_t value, uint32_t mask) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpBitwiseAnd, state.uint_type, ret, value, ConstantU32(state, mask)}); return ret; } void EmitShiftU32(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitAndConstant(state, EmitValueLoad(state, inst.src[1]), 31u); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.uint_type, ret, lhs, rhs}); EmitStoreU32(state, inst.dst, ret); } void EmitBitClearU32(EmitterState& state, const IR::Instruction& inst) { const auto value = EmitValueLoad(state, inst.src[0]); const auto bit = EmitAndConstant(state, EmitValueLoad(state, inst.src[1]), 31u); const auto mask = state.builder.AllocateId(); const auto inv = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpShiftLeftLogical, state.uint_type, mask, ConstantU32(state, 1), bit}); state.builder.AddFunction({OpNot, state.uint_type, inv, mask}); state.builder.AddFunction({OpBitwiseAnd, state.uint_type, ret, value, inv}); EmitStoreU32(state, inst.dst, ret); } void EmitBitSetU32(EmitterState& state, const IR::Instruction& inst) { const auto value = EmitValueLoad(state, inst.src[0]); const auto bit = EmitAndConstant(state, EmitValueLoad(state, inst.src[1]), 31u); const auto mask = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpShiftLeftLogical, state.uint_type, mask, ConstantU32(state, 1), bit}); state.builder.AddFunction({OpBitwiseOr, state.uint_type, ret, value, mask}); EmitStoreU32(state, inst.dst, ret); } uint32_t EmitNegateU32(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpSNegate, state.uint_type, ret, value}); return ret; } uint32_t EmitSignExtendLow16U32(EmitterState& state, uint32_t value) { const auto signed_value = state.builder.AllocateId(); const auto extracted = state.builder.AllocateId(); const auto bits = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.int_type, signed_value, value}); state.builder.AddFunction({OpBitFieldSExtract, state.int_type, extracted, signed_value, ConstantU32(state, 0), ConstantU32(state, 16)}); state.builder.AddFunction({OpBitcast, state.uint_type, bits, extracted}); return bits; } uint32_t EmitU16LaneBits(EmitterState& state, const IR::Operand& operand, bool high_lane, bool sign_extend) { const auto raw = EmitValueLoad(state, operand); const auto selected_lane = high_lane ? operand.op_sel_hi : operand.op_sel; const auto lane = selected_lane ? 1u : 0u; auto bits = lane != 0u ? EmitShiftRightConstant(state, raw, 16) : raw; bits = EmitAndConstant(state, bits, 0xffffu); if (high_lane ? operand.negate_hi : operand.negate) { bits = EmitAndConstant(state, EmitNegateU32(state, bits), 0xffffu); } return sign_extend ? EmitSignExtendLow16U32(state, bits) : bits; } void EmitShiftU16(EmitterState& state, const IR::Instruction& inst, uint32_t opcode, bool arithmetic) { const auto lhs = EmitU16LaneBits(state, inst.src[0], false, arithmetic); const auto rhs = EmitAndConstant(state, EmitU16LaneBits(state, inst.src[1], false), 0xfu); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.uint_type, ret, lhs, rhs}); EmitStoreU32(state, inst.dst, EmitAndConstant(state, ret, 0xffffu)); } void EmitBinaryU16(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto lhs = EmitU16LaneBits(state, inst.src[0], false); const auto rhs = EmitU16LaneBits(state, inst.src[1], false); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.uint_type, ret, lhs, rhs}); EmitStoreU32(state, inst.dst, EmitAndConstant(state, ret, 0xffffu)); } void EmitMinMaxU16(EmitterState& state, const IR::Instruction& inst, bool signed_value, bool max_value) { const auto lhs = EmitU16LaneBits(state, inst.src[0], false, signed_value); const auto rhs = EmitU16LaneBits(state, inst.src[1], false, signed_value); const auto cond = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); uint32_t op = 0; if (signed_value) { op = max_value ? OpSGreaterThan : OpSLessThan; } else { op = max_value ? OpUGreaterThan : OpULessThan; } state.builder.AddFunction({op, state.bool_type, cond, lhs, rhs}); state.builder.AddFunction({OpSelect, state.uint_type, ret, cond, lhs, rhs}); EmitStoreU32(state, inst.dst, EmitAndConstant(state, ret, 0xffffu)); } uint32_t EmitXorConstant(EmitterState& state, uint32_t value, uint32_t mask) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpBitwiseXor, state.uint_type, ret, value, ConstantU32(state, mask)}); return ret; } AddCarryResult EmitAddCarryValues(EmitterState& state, uint32_t lhs, uint32_t rhs, uint32_t carry_in) { const auto pair0 = state.builder.AllocateId(); const auto sum0 = state.builder.AllocateId(); const auto carry0 = state.builder.AllocateId(); const auto pair1 = state.builder.AllocateId(); const auto sum1 = state.builder.AllocateId(); const auto carry1 = state.builder.AllocateId(); const auto carry = state.builder.AllocateId(); state.builder.AddFunction({OpIAddCarry, state.uint_pair_type, pair0, lhs, rhs}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, sum0, pair0, 0}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, carry0, pair0, 1}); state.builder.AddFunction({OpIAddCarry, state.uint_pair_type, pair1, sum0, carry_in}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, sum1, pair1, 0}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, carry1, pair1, 1}); state.builder.AddFunction({OpBitwiseOr, state.uint_type, carry, carry0, carry1}); return {sum1, carry}; } void EmitLaneMaskPairFromBool(EmitterState& state, const IR::Operand& dst, uint32_t value_bool) { const auto write_mask = state.builder.AllocateId(); state.builder.AddFunction( {OpLogicalAnd, state.bool_type, write_mask, value_bool, EmitExecActiveBool(state)}); if (state.per_invocation_masks) { EmitPerInvocationMask(state, dst, write_mask); return; } const auto ballot = state.builder.AllocateId(); state.builder.AddFunction({OpGroupNonUniformBallot, state.vec4_uint_type, ballot, ConstantU32(state, ScopeSubgroup), write_mask}); for (uint32_t part = 0; part < 2u; part++) { uint32_t result = ConstantU32(state, 0); if (state.wave_size != 32u || part == 0u) { result = state.builder.AllocateId(); state.builder.AddFunction({OpCompositeExtract, state.uint_type, result, ballot, part}); } EmitStoreU32(state, OffsetRegisterOperand(dst, part), result); } } void EmitIAddCarryU32(EmitterState& state, const IR::Instruction& inst) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto carry_in_active = EmitLaneMaskOperandActiveBool(state, inst.src[2]); const auto carry_in = state.builder.AllocateId(); state.builder.AddFunction({OpSelect, state.uint_type, carry_in, carry_in_active, ConstantU32(state, 1), ConstantU32(state, 0)}); const auto result = EmitAddCarryValues(state, lhs, rhs, carry_in); const auto carry_bool = state.builder.AllocateId(); state.builder.AddFunction( {OpINotEqual, state.bool_type, carry_bool, result.carry, ConstantU32(state, 0)}); EmitStoreU32(state, inst.dst, result.sum); EmitLaneMaskPairFromBool(state, inst.dst2, carry_bool); } void EmitISubBorrowU32(EmitterState& state, const IR::Instruction& inst) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto result = state.builder.AllocateId(); const auto borrow = state.builder.AllocateId(); state.builder.AddFunction({OpISub, state.uint_type, result, lhs, rhs}); state.builder.AddFunction({OpUGreaterThan, state.bool_type, borrow, rhs, lhs}); EmitStoreU32(state, inst.dst, result); EmitLaneMaskPairFromBool(state, inst.dst2, borrow); } void EmitScalarAddCarryU32(EmitterState& state, const IR::Instruction& inst) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto carry_in = EmitAndConstant(state, EmitValueLoad(state, inst.src[2]), 1u); const auto result = EmitAddCarryValues(state, lhs, rhs, carry_in); EmitStoreU32(state, inst.dst, result.sum); EmitStoreU32(state, SccOperand(), result.carry); } void EmitScalarSubBorrowU32(EmitterState& state, const IR::Instruction& inst) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto result = state.builder.AllocateId(); const auto borrow = state.builder.AllocateId(); state.builder.AddFunction({OpISub, state.uint_type, result, lhs, rhs}); state.builder.AddFunction({OpUGreaterThan, state.bool_type, borrow, rhs, lhs}); EmitStoreU32(state, inst.dst, result); EmitStoreSccBool(state, borrow); } void EmitScalarSubBorrowCarryU32(EmitterState& state, const IR::Instruction& inst) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto borrow_in = EmitAndConstant(state, EmitValueLoad(state, inst.src[2]), 1u); const auto partial = state.builder.AllocateId(); const auto result = state.builder.AllocateId(); const auto borrow0 = state.builder.AllocateId(); const auto borrow1 = state.builder.AllocateId(); state.builder.AddFunction({OpISub, state.uint_type, partial, lhs, rhs}); state.builder.AddFunction({OpUGreaterThan, state.bool_type, borrow0, rhs, lhs}); state.builder.AddFunction({OpISub, state.uint_type, result, partial, borrow_in}); state.builder.AddFunction({OpUGreaterThan, state.bool_type, borrow1, borrow_in, partial}); EmitStoreU32(state, inst.dst, result); EmitStoreSccBool(state, EmitLogicalOrBool(state, borrow0, borrow1)); } void EmitUMadU64U32(EmitterState& state, const IR::Instruction& inst) { const auto src0 = EmitValueLoad(state, inst.src[0]); const auto src1 = EmitValueLoad(state, inst.src[1]); const auto add_low = EmitSequentialValueLoad(state, inst.src[2], 0); const auto add_high = EmitSequentialValueLoad(state, inst.src[2], 1); const auto mul_pair = state.builder.AllocateId(); const auto mul_low = state.builder.AllocateId(); const auto mul_high = state.builder.AllocateId(); const auto low_pair = state.builder.AllocateId(); const auto result_low = state.builder.AllocateId(); const auto carry_low = state.builder.AllocateId(); const auto high_pair = state.builder.AllocateId(); const auto high_sum = state.builder.AllocateId(); const auto carry_high0 = state.builder.AllocateId(); const auto carry_pair = state.builder.AllocateId(); const auto result_high = state.builder.AllocateId(); const auto carry_high1 = state.builder.AllocateId(); const auto carry = state.builder.AllocateId(); const auto carry_bool = state.builder.AllocateId(); state.builder.AddFunction({OpUMulExtended, state.uint_pair_type, mul_pair, src0, src1}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, mul_low, mul_pair, 0}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, mul_high, mul_pair, 1}); state.builder.AddFunction({OpIAddCarry, state.uint_pair_type, low_pair, mul_low, add_low}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, result_low, low_pair, 0}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, carry_low, low_pair, 1}); state.builder.AddFunction({OpIAddCarry, state.uint_pair_type, high_pair, mul_high, add_high}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, high_sum, high_pair, 0}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, carry_high0, high_pair, 1}); state.builder.AddFunction({OpIAddCarry, state.uint_pair_type, carry_pair, high_sum, carry_low}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, result_high, carry_pair, 0}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, carry_high1, carry_pair, 1}); state.builder.AddFunction({OpBitwiseOr, state.uint_type, carry, carry_high0, carry_high1}); state.builder.AddFunction( {OpINotEqual, state.bool_type, carry_bool, carry, ConstantU32(state, 0)}); EmitStoreU32(state, inst.dst, result_low); EmitStoreU32(state, OffsetRegisterOperand(inst.dst, 1), result_high); EmitLaneMaskPairFromBool(state, inst.dst2, carry_bool); } void EmitScalarSignedOverflowI32(EmitterState& state, const IR::Instruction& inst, bool subtract) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto result = state.builder.AllocateId(); const auto lhs_sign = state.builder.AllocateId(); const auto rhs_sign = state.builder.AllocateId(); const auto result_sign = state.builder.AllocateId(); const auto sign_match = state.builder.AllocateId(); const auto sign_change = state.builder.AllocateId(); const auto overflow = state.builder.AllocateId(); state.builder.AddFunction({subtract ? OpISub : OpIAdd, state.uint_type, result, lhs, rhs}); state.builder.AddFunction( {OpShiftRightLogical, state.uint_type, lhs_sign, lhs, ConstantU32(state, 31)}); state.builder.AddFunction( {OpShiftRightLogical, state.uint_type, rhs_sign, rhs, ConstantU32(state, 31)}); state.builder.AddFunction( {OpShiftRightLogical, state.uint_type, result_sign, result, ConstantU32(state, 31)}); state.builder.AddFunction( {subtract ? OpINotEqual : OpIEqual, state.bool_type, sign_match, lhs_sign, rhs_sign}); state.builder.AddFunction({OpINotEqual, state.bool_type, sign_change, lhs_sign, result_sign}); state.builder.AddFunction({OpLogicalAnd, state.bool_type, overflow, sign_match, sign_change}); EmitStoreU32(state, inst.dst, result); EmitStoreSccBool(state, overflow); } void EmitScalarShiftLeftAddCarryU32(EmitterState& state, const IR::Instruction& inst) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[2]); const auto shift = inst.src[1].kind == IR::OperandKind::ImmediateU32 ? inst.src[1].imm : 0u; const auto shifted = state.builder.AllocateId(); const auto pair = state.builder.AllocateId(); const auto result = state.builder.AllocateId(); const auto add_carry = state.builder.AllocateId(); const auto shifted_out = state.builder.AllocateId(); const auto shift_carry = state.builder.AllocateId(); const auto add_carry_bool = state.builder.AllocateId(); const auto carry = state.builder.AllocateId(); state.builder.AddFunction( {OpShiftLeftLogical, state.uint_type, shifted, lhs, ConstantU32(state, shift)}); state.builder.AddFunction({OpIAddCarry, state.uint_pair_type, pair, shifted, rhs}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, result, pair, 0}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, add_carry, pair, 1}); state.builder.AddFunction( {OpShiftRightLogical, state.uint_type, shifted_out, lhs, ConstantU32(state, 32u - shift)}); state.builder.AddFunction( {OpINotEqual, state.bool_type, shift_carry, shifted_out, ConstantU32(state, 0)}); state.builder.AddFunction( {OpINotEqual, state.bool_type, add_carry_bool, add_carry, ConstantU32(state, 0)}); state.builder.AddFunction({OpLogicalOr, state.bool_type, carry, shift_carry, add_carry_bool}); EmitStoreU32(state, inst.dst, result); EmitStoreSccBool(state, carry); } void EmitMulU24U32(EmitterState& state, const IR::Instruction& inst) { const auto lhs = EmitAndConstant(state, EmitValueLoad(state, inst.src[0]), 0x00ffffffu); const auto rhs = EmitAndConstant(state, EmitValueLoad(state, inst.src[1]), 0x00ffffffu); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpIMul, state.uint_type, ret, lhs, rhs}); EmitStoreU32(state, inst.dst, ret); } void EmitMulI24U32(EmitterState& state, const IR::Instruction& inst) { const auto lhs_bits = EmitValueLoad(state, inst.src[0]); const auto rhs_bits = EmitValueLoad(state, inst.src[1]); const auto lhs_i32 = state.builder.AllocateId(); const auto rhs_i32 = state.builder.AllocateId(); const auto lhs_i24 = state.builder.AllocateId(); const auto rhs_i24 = state.builder.AllocateId(); const auto mul = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.int_type, lhs_i32, lhs_bits}); state.builder.AddFunction({OpBitcast, state.int_type, rhs_i32, rhs_bits}); state.builder.AddFunction({OpBitFieldSExtract, state.int_type, lhs_i24, lhs_i32, ConstantU32(state, 0), ConstantU32(state, 24)}); state.builder.AddFunction({OpBitFieldSExtract, state.int_type, rhs_i24, rhs_i32, ConstantU32(state, 0), ConstantU32(state, 24)}); state.builder.AddFunction({OpIMul, state.int_type, mul, lhs_i24, rhs_i24}); state.builder.AddFunction({OpBitcast, state.uint_type, ret, mul}); EmitStoreU32(state, inst.dst, ret); } void EmitMulHighU32(EmitterState& state, const IR::Instruction& inst) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto full = state.builder.AllocateId(); const auto high = state.builder.AllocateId(); state.builder.AddFunction({OpUMulExtended, state.uint_pair_type, full, lhs, rhs}); state.builder.AddFunction({OpCompositeExtract, state.uint_type, high, full, 1}); EmitStoreU32(state, inst.dst, high); } void EmitMulHighI32(EmitterState& state, const IR::Instruction& inst) { const auto lhs_bits = EmitValueLoad(state, inst.src[0]); const auto rhs_bits = EmitValueLoad(state, inst.src[1]); const auto lhs = state.builder.AllocateId(); const auto rhs = state.builder.AllocateId(); const auto full = state.builder.AllocateId(); const auto high_i32 = state.builder.AllocateId(); const auto high_u32 = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.int_type, lhs, lhs_bits}); state.builder.AddFunction({OpBitcast, state.int_type, rhs, rhs_bits}); state.builder.AddFunction({OpSMulExtended, state.int_pair_type, full, lhs, rhs}); state.builder.AddFunction({OpCompositeExtract, state.int_type, high_i32, full, 1}); state.builder.AddFunction({OpBitcast, state.uint_type, high_u32, high_i32}); EmitStoreU32(state, inst.dst, high_u32); } void EmitMadU32U24(EmitterState& state, const IR::Instruction& inst) { const auto lhs = EmitAndConstant(state, EmitValueLoad(state, inst.src[0]), 0x00ffffffu); const auto rhs = EmitAndConstant(state, EmitValueLoad(state, inst.src[1]), 0x00ffffffu); const auto add = EmitValueLoad(state, inst.src[2]); const auto mul = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpIMul, state.uint_type, mul, lhs, rhs}); state.builder.AddFunction({OpIAdd, state.uint_type, ret, mul, add}); EmitStoreU32(state, inst.dst, ret); } void EmitMadI32I24(EmitterState& state, const IR::Instruction& inst) { const auto lhs_bits = EmitValueLoad(state, inst.src[0]); const auto rhs_bits = EmitValueLoad(state, inst.src[1]); const auto add_bits = EmitValueLoad(state, inst.src[2]); const auto lhs_i32 = state.builder.AllocateId(); const auto rhs_i32 = state.builder.AllocateId(); const auto add_i32 = state.builder.AllocateId(); const auto lhs_i24 = state.builder.AllocateId(); const auto rhs_i24 = state.builder.AllocateId(); const auto mul = state.builder.AllocateId(); const auto add = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.int_type, lhs_i32, lhs_bits}); state.builder.AddFunction({OpBitcast, state.int_type, rhs_i32, rhs_bits}); state.builder.AddFunction({OpBitcast, state.int_type, add_i32, add_bits}); state.builder.AddFunction({OpBitFieldSExtract, state.int_type, lhs_i24, lhs_i32, ConstantU32(state, 0), ConstantU32(state, 24)}); state.builder.AddFunction({OpBitFieldSExtract, state.int_type, rhs_i24, rhs_i32, ConstantU32(state, 0), ConstantU32(state, 24)}); state.builder.AddFunction({OpIMul, state.int_type, mul, lhs_i24, rhs_i24}); state.builder.AddFunction({OpIAdd, state.int_type, add, mul, add_i32}); state.builder.AddFunction({OpBitcast, state.uint_type, ret, add}); EmitStoreU32(state, inst.dst, ret); } uint32_t EmitShiftLeftConstant(EmitterState& state, uint32_t value, uint32_t shift) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpShiftLeftLogical, state.uint_type, ret, value, ConstantU32(state, shift)}); return ret; } uint32_t EmitShiftRightConstant(EmitterState& state, uint32_t value, uint32_t shift) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpShiftRightLogical, state.uint_type, ret, value, ConstantU32(state, shift)}); return ret; } uint32_t EmitOrU32(EmitterState& state, uint32_t lhs, uint32_t rhs) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitwiseOr, state.uint_type, ret, lhs, rhs}); return ret; } uint32_t EmitBitReplicateHalfU32(EmitterState& state, uint32_t value) { auto ret = EmitOrU32(state, value, EmitShiftLeftConstant(state, value, 8)); ret = EmitAndConstant(state, ret, 0x00ff00ffu); ret = EmitOrU32(state, ret, EmitShiftLeftConstant(state, ret, 4)); ret = EmitAndConstant(state, ret, 0x0f0f0f0fu); ret = EmitOrU32(state, ret, EmitShiftLeftConstant(state, ret, 2)); ret = EmitAndConstant(state, ret, 0x33333333u); ret = EmitOrU32(state, ret, EmitShiftLeftConstant(state, ret, 1)); ret = EmitAndConstant(state, ret, 0x55555555u); return EmitOrU32(state, ret, EmitShiftLeftConstant(state, ret, 1)); } void EmitBitReplicateB64B32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitValueLoad(state, inst.src[0]); const auto low = EmitBitReplicateHalfU32(state, EmitAndConstant(state, src, 0x0000ffffu)); const auto high = EmitBitReplicateHalfU32(state, EmitShiftRightConstant(state, src, 16)); EmitStoreU32(state, inst.dst, low); EmitStoreU32(state, OffsetRegisterOperand(inst.dst, 1), high); } uint32_t EmitBitFieldExtractConstant(EmitterState& state, uint32_t value, uint32_t offset, uint32_t count) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitFieldUExtract, state.uint_type, ret, value, ConstantU32(state, offset), ConstantU32(state, count)}); return ret; } uint32_t EmitRightAlignedMaskU32(EmitterState& state, uint32_t count) { const auto shifted_one = state.builder.AllocateId(); const auto mask = state.builder.AllocateId(); state.builder.AddFunction( {OpShiftLeftLogical, state.uint_type, shifted_one, ConstantU32(state, 1), count}); state.builder.AddFunction({OpISub, state.uint_type, mask, shifted_one, ConstantU32(state, 1)}); return mask; } void EmitBitFieldMaskU32(EmitterState& state, const IR::Instruction& inst) { const auto count = EmitAndConstant(state, EmitValueLoad(state, inst.src[0]), 31u); const auto offset = EmitAndConstant(state, EmitValueLoad(state, inst.src[1]), 31u); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitFieldInsert, state.uint_type, ret, ConstantU32(state, 0), ConstantU32(state, 0xffffffffu), offset, count}); EmitStoreU32(state, inst.dst, ret); } void EmitRightAlignedMaskU64(EmitterState& state, uint32_t count, uint32_t& out_low, uint32_t& out_high) { const auto count_lt32 = state.builder.AllocateId(); const auto count_gt32 = state.builder.AllocateId(); const auto low_count = state.builder.AllocateId(); const auto high_base = state.builder.AllocateId(); const auto high_count = state.builder.AllocateId(); state.builder.AddFunction( {OpULessThan, state.bool_type, count_lt32, count, ConstantU32(state, 32)}); state.builder.AddFunction( {OpUGreaterThan, state.bool_type, count_gt32, count, ConstantU32(state, 32)}); state.builder.AddFunction( {OpSelect, state.uint_type, low_count, count_lt32, count, ConstantU32(state, 32)}); state.builder.AddFunction({OpISub, state.uint_type, high_base, count, ConstantU32(state, 32)}); state.builder.AddFunction( {OpSelect, state.uint_type, high_count, count_gt32, high_base, ConstantU32(state, 0)}); out_low = state.builder.AllocateId(); out_high = state.builder.AllocateId(); state.builder.AddFunction({OpBitFieldInsert, state.uint_type, out_low, ConstantU32(state, 0), ConstantU32(state, 0xffffffffu), ConstantU32(state, 0), low_count}); state.builder.AddFunction({OpBitFieldInsert, state.uint_type, out_high, ConstantU32(state, 0), ConstantU32(state, 0xffffffffu), ConstantU32(state, 0), high_count}); } void EmitBitFieldMaskU64(EmitterState& state, const IR::Instruction& inst) { const auto count = EmitAndConstant(state, EmitValueLoad(state, inst.src[0]), 63u); if (state.per_invocation_masks && inst.dst.kind == IR::OperandKind::Register && IsMaskRegisterFile(inst.dst.reg.file)) { const auto active = state.builder.AllocateId(); state.builder.AddFunction( {OpINotEqual, state.bool_type, active, count, ConstantU32(state, 0)}); EmitPerInvocationMask(state, inst.dst, active); return; } const auto offset = EmitAndConstant(state, EmitValueLoad(state, inst.src[1]), 63u); uint32_t low_mask = 0; uint32_t high_mask = 0; uint32_t shifted_low = 0; uint32_t shifted_high = 0; EmitRightAlignedMaskU64(state, count, low_mask, high_mask); EmitShiftLeftLogicalU64Values(state, low_mask, high_mask, offset, shifted_low, shifted_high); EmitStoreU32(state, inst.dst, shifted_low); EmitStoreU32(state, OffsetRegisterOperand(inst.dst, 1), shifted_high); } void EmitBitFieldExtractU64(EmitterState& state, const IR::Instruction& inst) { const auto src_low = EmitSequentialValueLoad(state, inst.src[0], 0); const auto src_high = EmitSequentialValueLoad(state, inst.src[0], 1); const auto field = EmitValueLoad(state, inst.src[1]); const auto offset = EmitBitFieldExtractConstant(state, field, 0, 6); const auto raw_count = EmitBitFieldExtractConstant(state, field, 16, 7); const auto available = state.builder.AllocateId(); const auto use_raw = state.builder.AllocateId(); const auto count = state.builder.AllocateId(); uint32_t shifted_low = 0; uint32_t shifted_high = 0; uint32_t mask_low = 0; uint32_t mask_high = 0; const auto ret_low = state.builder.AllocateId(); const auto ret_high = state.builder.AllocateId(); state.builder.AddFunction({OpISub, state.uint_type, available, ConstantU32(state, 64), offset}); state.builder.AddFunction({OpULessThan, state.bool_type, use_raw, raw_count, available}); state.builder.AddFunction({OpSelect, state.uint_type, count, use_raw, raw_count, available}); EmitShiftRightLogicalU64Values(state, src_low, src_high, offset, shifted_low, shifted_high); EmitRightAlignedMaskU64(state, count, mask_low, mask_high); state.builder.AddFunction({OpBitwiseAnd, state.uint_type, ret_low, shifted_low, mask_low}); state.builder.AddFunction({OpBitwiseAnd, state.uint_type, ret_high, shifted_high, mask_high}); EmitStoreU32(state, inst.dst, ret_low); EmitStoreU32(state, OffsetRegisterOperand(inst.dst, 1), ret_high); } void EmitBitFieldExtractU32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitValueLoad(state, inst.src[0]); const auto field = EmitValueLoad(state, inst.src[1]); const auto offset = EmitBitFieldExtractConstant(state, field, 0, 5); const auto count = EmitBitFieldExtractConstant(state, field, 16, 7); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitFieldUExtract, state.uint_type, ret, src, offset, count}); EmitStoreU32(state, inst.dst, ret); } void EmitBitFieldExtract3U32(EmitterState& state, const IR::Instruction& inst, bool signed_value) { const auto src = EmitValueLoad(state, inst.src[0]); const auto offset = EmitAndConstant(state, EmitValueLoad(state, inst.src[1]), 31u); const auto count = EmitAndConstant(state, EmitValueLoad(state, inst.src[2]), 31u); const auto ret = state.builder.AllocateId(); if (signed_value) { const auto shifted = state.builder.AllocateId(); const auto mask = EmitRightAlignedMaskU32(state, count); state.builder.AddFunction({OpShiftRightArithmetic, state.uint_type, shifted, src, offset}); state.builder.AddFunction({OpBitwiseAnd, state.uint_type, ret, shifted, mask}); EmitStoreU32(state, inst.dst, ret); return; } state.builder.AddFunction({OpBitFieldUExtract, state.uint_type, ret, src, offset, count}); EmitStoreU32(state, inst.dst, ret); } void EmitBitFieldInsertSelectU32(EmitterState& state, const IR::Instruction& inst) { const auto mask = EmitValueLoad(state, inst.src[0]); const auto insert = EmitValueLoad(state, inst.src[1]); const auto base = EmitValueLoad(state, inst.src[2]); const auto not_mask = state.builder.AllocateId(); const auto selected = state.builder.AllocateId(); const auto preserved = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpNot, state.uint_type, not_mask, mask}); state.builder.AddFunction({OpBitwiseAnd, state.uint_type, selected, mask, insert}); state.builder.AddFunction({OpBitwiseAnd, state.uint_type, preserved, not_mask, base}); state.builder.AddFunction({OpBitwiseOr, state.uint_type, ret, selected, preserved}); EmitStoreU32(state, inst.dst, ret); } void EmitAlignBitU32(EmitterState& state, const IR::Instruction& inst) { const auto hi = EmitValueLoad(state, inst.src[0]); const auto lo = EmitValueLoad(state, inst.src[1]); const auto shift = EmitAndConstant(state, EmitValueLoad(state, inst.src[2]), 31u); const auto lo_part = state.builder.AllocateId(); const auto hi_shift_raw = state.builder.AllocateId(); const auto hi_shift = state.builder.AllocateId(); const auto hi_part_raw = state.builder.AllocateId(); const auto non_zero = state.builder.AllocateId(); const auto hi_part = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpShiftRightLogical, state.uint_type, lo_part, lo, shift}); state.builder.AddFunction( {OpISub, state.uint_type, hi_shift_raw, ConstantU32(state, 32), shift}); state.builder.AddFunction( {OpBitwiseAnd, state.uint_type, hi_shift, hi_shift_raw, ConstantU32(state, 31)}); state.builder.AddFunction({OpShiftLeftLogical, state.uint_type, hi_part_raw, hi, hi_shift}); state.builder.AddFunction( {OpINotEqual, state.bool_type, non_zero, shift, ConstantU32(state, 0)}); state.builder.AddFunction( {OpSelect, state.uint_type, hi_part, non_zero, hi_part_raw, ConstantU32(state, 0)}); state.builder.AddFunction({OpBitwiseOr, state.uint_type, ret, lo_part, hi_part}); EmitStoreU32(state, inst.dst, ret); } void EmitSelectU32(EmitterState& state, const IR::Instruction& inst) { const auto cond = EmitConditionBool(state, inst.src[0]); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpSelect, state.uint_type, ret, cond, EmitValueLoad(state, inst.src[1]), EmitValueLoad(state, inst.src[2])}); EmitStoreU32(state, inst.dst, ret); } void EmitSelectMaskU32(EmitterState& state, const IR::Instruction& inst) { const auto cond = EmitLaneMaskOperandActiveBool(state, inst.src[0]); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpSelect, state.uint_type, ret, cond, EmitValueLoad(state, inst.src[1]), EmitValueLoad(state, inst.src[2])}); EmitStoreU32(state, inst.dst, ret); } void EmitSelectF32Bits(EmitterState& state, const IR::Instruction& inst) { const auto cond = EmitConditionBool(state, inst.src[0]); const auto selected = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpSelect, state.float_type, selected, cond, EmitFloatLoad(state, inst.src[1]), EmitFloatLoad(state, inst.src[2])}); state.builder.AddFunction({OpBitcast, state.uint_type, ret, selected}); EmitStoreU32(state, inst.dst, ret); } void EmitSelectMaskF32Bits(EmitterState& state, const IR::Instruction& inst) { const auto cond = EmitLaneMaskOperandActiveBool(state, inst.src[0]); const auto selected = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpSelect, state.float_type, selected, cond, EmitFloatLoad(state, inst.src[1]), EmitFloatLoad(state, inst.src[2])}); state.builder.AddFunction({OpBitcast, state.uint_type, ret, selected}); EmitStoreU32(state, inst.dst, ret); } void EmitPackU16(EmitterState& state, const IR::Instruction& inst, bool high_src0, bool high_src1) { const auto src0 = EmitValueLoad(state, inst.src[0]); const auto src1 = EmitValueLoad(state, inst.src[1]); const auto lo = high_src0 ? EmitBitFieldExtractConstant(state, src0, 16, 16) : EmitAndConstant(state, src0, 0xffffu); const auto hi = high_src1 ? EmitBitFieldExtractConstant(state, src1, 16, 16) : EmitAndConstant(state, src1, 0xffffu); const auto shifted = EmitShiftLeftConstant(state, hi, 16); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitwiseOr, state.uint_type, ret, lo, shifted}); EmitStoreU32(state, inst.dst, ret); } void EmitPackU8F32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitFloatLoad(state, inst.src[0]); const auto byte_index = EmitAndConstant(state, EmitValueLoad(state, inst.src[1]), 3u); const auto base = EmitValueLoad(state, inst.src[2]); const auto truncated = EmitTruncF32Value(state, src); const auto below_zero = state.builder.AllocateId(); const auto above_max = state.builder.AllocateId(); const auto safe_low = state.builder.AllocateId(); const auto safe_value = state.builder.AllocateId(); const auto converted = state.builder.AllocateId(); state.builder.AddFunction( {OpFOrdLessThanEqual, state.bool_type, below_zero, src, ConstantF32(state, 0)}); state.builder.AddFunction( {OpFOrdGreaterThanEqual, state.bool_type, above_max, src, ConstantF32(state, 0x437f0000u)}); const auto zero_or_nan = EmitLogicalOrBool(state, EmitClassifyF32(state, src).nan, below_zero); state.builder.AddFunction( {OpSelect, state.float_type, safe_low, zero_or_nan, ConstantF32(state, 0), truncated}); state.builder.AddFunction({OpSelect, state.float_type, safe_value, above_max, ConstantF32(state, 0x437f0000u), safe_low}); state.builder.AddFunction({OpConvertFToU, state.uint_type, converted, safe_value}); const auto byte_value = EmitAndConstant(state, converted, 0xffu); const auto shift = state.builder.AllocateId(); state.builder.AddFunction( {OpShiftLeftLogical, state.uint_type, shift, byte_index, ConstantU32(state, 3)}); const auto byte_mask = state.builder.AllocateId(); const auto inv_mask = state.builder.AllocateId(); const auto cleared = state.builder.AllocateId(); const auto shifted = state.builder.AllocateId(); state.builder.AddFunction( {OpShiftLeftLogical, state.uint_type, byte_mask, ConstantU32(state, 0xffu), shift}); state.builder.AddFunction({OpNot, state.uint_type, inv_mask, byte_mask}); state.builder.AddFunction({OpBitwiseAnd, state.uint_type, cleared, base, inv_mask}); state.builder.AddFunction({OpShiftLeftLogical, state.uint_type, shifted, byte_value, shift}); EmitStoreU32(state, inst.dst, EmitOrU32(state, cleared, shifted)); } void EmitBinaryF32(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto lhs = EmitFloatLoad(state, inst.src[0]); const auto rhs = EmitFloatLoad(state, inst.src[1]); const auto f32 = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.float_type, f32, lhs, rhs}); const auto result = ApplyResultModifiersF32(state, f32, inst.dst); state.builder.AddFunction({OpBitcast, state.uint_type, u32, result}); EmitStoreU32(state, inst.dst, u32); } void EmitLdexpF32(EmitterState& state, const IR::Instruction& inst) { const auto value = EmitFloatLoad(state, inst.src[0]); const auto exp_bits = EmitValueLoad(state, inst.src[1]); const auto exp_i32 = state.builder.AllocateId(); const auto ret_f32 = state.builder.AllocateId(); const auto ret_u32 = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.int_type, exp_i32, exp_bits}); state.builder.AddFunction( {OpExtInst, state.float_type, ret_f32, state.glsl_std450, GlslLdexp, value, exp_i32}); const auto result = ApplyResultModifiersF32(state, ret_f32, inst.dst); state.builder.AddFunction({OpBitcast, state.uint_type, ret_u32, result}); EmitStoreU32(state, inst.dst, ret_u32); } uint32_t EmitCompareU32Constant(EmitterState& state, uint32_t opcode, uint32_t value, uint32_t constant) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.bool_type, ret, value, ConstantU32(state, constant)}); return ret; } uint32_t EmitSubConstantMinusU32(EmitterState& state, uint32_t constant, uint32_t value) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpISub, state.uint_type, ret, ConstantU32(state, constant), value}); return ret; } uint32_t EmitF32ToF16RtzBits(EmitterState& state, uint32_t f32) { const auto bits = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.uint_type, bits, f32}); const auto sign = EmitAndConstant(state, EmitShiftRightConstant(state, bits, 16), 0x8000u); const auto exp = EmitAndConstant(state, EmitShiftRightConstant(state, bits, 23), 0xffu); const auto mant = EmitAndConstant(state, bits, 0x007fffffu); const auto half_exp = state.builder.AllocateId(); const auto normal_exp = state.builder.AllocateId(); const auto normal_mant = EmitShiftRightConstant(state, mant, 13); const auto normal_payload = state.builder.AllocateId(); const auto normal = state.builder.AllocateId(); state.builder.AddFunction({OpISub, state.uint_type, half_exp, exp, ConstantU32(state, 112)}); state.builder.AddFunction( {OpShiftLeftLogical, state.uint_type, normal_exp, half_exp, ConstantU32(state, 10)}); state.builder.AddFunction( {OpBitwiseOr, state.uint_type, normal_payload, normal_exp, normal_mant}); state.builder.AddFunction({OpBitwiseOr, state.uint_type, normal, sign, normal_payload}); const auto mant_with_hidden = EmitOrU32(state, mant, ConstantU32(state, 0x00800000u)); const auto raw_sub_shift = EmitSubConstantMinusU32(state, 126, exp); const auto exp_lt_103 = EmitCompareU32Constant(state, OpULessThan, exp, 103); const auto exp_gt_112 = EmitCompareU32Constant(state, OpUGreaterThan, exp, 112); const auto sub_shift_low = EmitSelectValueU32(state, exp_lt_103, ConstantU32(state, 31), raw_sub_shift); const auto sub_shift = EmitSelectValueU32(state, exp_gt_112, ConstantU32(state, 14), sub_shift_low); const auto sub_mant = state.builder.AllocateId(); const auto subnormal = state.builder.AllocateId(); state.builder.AddFunction( {OpShiftRightLogical, state.uint_type, sub_mant, mant_with_hidden, sub_shift}); state.builder.AddFunction({OpBitwiseOr, state.uint_type, subnormal, sign, sub_mant}); const auto nan_payload = EmitOrU32(state, EmitShiftRightConstant(state, mant, 13), ConstantU32(state, 0x0200u)); const auto nan = EmitOrU32(state, sign, EmitOrU32(state, ConstantU32(state, 0x7c00u), nan_payload)); const auto inf = EmitOrU32(state, sign, ConstantU32(state, 0x7c00u)); const auto max_finite = EmitOrU32(state, sign, ConstantU32(state, 0x7bffu)); const auto mant_zero = EmitCompareU32Constant(state, OpIEqual, mant, 0); const auto special = EmitSelectValueU32(state, mant_zero, inf, nan); const auto exp_le_112 = EmitCompareU32Constant(state, OpULessThanEqual, exp, 112); const auto exp_ge_143 = EmitCompareU32Constant(state, OpUGreaterThanEqual, exp, 143); const auto exp_eq_255 = EmitCompareU32Constant(state, OpIEqual, exp, 255); const auto finite0 = EmitSelectValueU32(state, exp_le_112, subnormal, normal); const auto finite1 = EmitSelectValueU32(state, exp_lt_103, sign, finite0); const auto finite2 = EmitSelectValueU32(state, exp_ge_143, max_finite, finite1); return EmitAndConstant(state, EmitSelectValueU32(state, exp_eq_255, special, finite2), 0xffffu); } void EmitPackF32ToF16Rtz(EmitterState& state, const IR::Instruction& inst) { const auto lo = EmitF32ToF16RtzBits(state, EmitFloatLoad(state, inst.src[0])); const auto hi = EmitF32ToF16RtzBits(state, EmitFloatLoad(state, inst.src[1])); const auto shifted = EmitShiftLeftConstant(state, hi, 16); const auto ret = EmitOrU32(state, lo, shifted); EmitStoreU32(state, inst.dst, ret); } void EmitPackNormalizedF32(EmitterState& state, const IR::Instruction& inst, uint32_t ext_inst) { const auto src0 = EmitFloatLoad(state, inst.src[0]); const auto src1 = EmitFloatLoad(state, inst.src[1]); const auto pair = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpCompositeConstruct, state.vec2_float_type, pair, src0, src1}); state.builder.AddFunction({OpExtInst, state.uint_type, ret, state.glsl_std450, ext_inst, pair}); EmitStoreU32(state, inst.dst, ret); } uint32_t EmitMinMaxU32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value) { const auto cond = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {max_value ? OpUGreaterThan : OpULessThan, state.bool_type, cond, lhs, rhs}); state.builder.AddFunction({OpSelect, state.uint_type, ret, cond, lhs, rhs}); return ret; } void EmitMinMaxU32(EmitterState& state, const IR::Instruction& inst, bool max_value) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto ret = EmitMinMaxU32Value(state, lhs, rhs, max_value); EmitStoreU32(state, inst.dst, ret); } void EmitSadU32(EmitterState& state, const IR::Instruction& inst) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto add = EmitValueLoad(state, inst.src[2]); const auto high = EmitMinMaxU32Value(state, lhs, rhs, true); const auto low = EmitMinMaxU32Value(state, lhs, rhs, false); const auto diff = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpISub, state.uint_type, diff, high, low}); state.builder.AddFunction({OpIAdd, state.uint_type, ret, diff, add}); EmitStoreU32(state, inst.dst, ret); } uint32_t EmitMinMaxI32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value) { const auto cond = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {max_value ? OpSGreaterThan : OpSLessThan, state.bool_type, cond, lhs, rhs}); state.builder.AddFunction({OpSelect, state.uint_type, ret, cond, lhs, rhs}); return ret; } void EmitMinMaxI32(EmitterState& state, const IR::Instruction& inst, bool max_value) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto ret = EmitMinMaxI32Value(state, lhs, rhs, max_value); EmitStoreU32(state, inst.dst, ret); } void EmitMinMax3U32(EmitterState& state, const IR::Instruction& inst, bool signed_value, bool max_value) { const auto src0 = EmitValueLoad(state, inst.src[0]); const auto src1 = EmitValueLoad(state, inst.src[1]); const auto src2 = EmitValueLoad(state, inst.src[2]); const auto tmp = signed_value ? EmitMinMaxI32Value(state, src0, src1, max_value) : EmitMinMaxU32Value(state, src0, src1, max_value); const auto ret = signed_value ? EmitMinMaxI32Value(state, tmp, src2, max_value) : EmitMinMaxU32Value(state, tmp, src2, max_value); EmitStoreU32(state, inst.dst, ret); } void EmitMed3U32(EmitterState& state, const IR::Instruction& inst, bool signed_value) { const auto src0 = EmitValueLoad(state, inst.src[0]); const auto src1 = EmitValueLoad(state, inst.src[1]); const auto src2 = EmitValueLoad(state, inst.src[2]); const auto low_pair = signed_value ? EmitMinMaxI32Value(state, src0, src1, false) : EmitMinMaxU32Value(state, src0, src1, false); const auto high_pair = signed_value ? EmitMinMaxI32Value(state, src0, src1, true) : EmitMinMaxU32Value(state, src0, src1, true); const auto high_min = signed_value ? EmitMinMaxI32Value(state, high_pair, src2, false) : EmitMinMaxU32Value(state, high_pair, src2, false); const auto ret = signed_value ? EmitMinMaxI32Value(state, low_pair, high_min, true) : EmitMinMaxU32Value(state, low_pair, high_min, true); EmitStoreU32(state, inst.dst, ret); } uint32_t EmitBitcastF32ToU32(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.uint_type, ret, value}); return ret; } uint32_t EmitBitcastU32ToF32(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.float_type, ret, value}); return ret; } uint32_t EmitAndU32(EmitterState& state, uint32_t lhs, uint32_t rhs) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitwiseAnd, state.uint_type, ret, lhs, rhs}); return ret; } uint32_t EmitLogicalAndBool(EmitterState& state, uint32_t lhs, uint32_t rhs) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpLogicalAnd, state.bool_type, ret, lhs, rhs}); return ret; } uint32_t EmitLogicalOrBool(EmitterState& state, uint32_t lhs, uint32_t rhs) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpLogicalOr, state.bool_type, ret, lhs, rhs}); return ret; } uint32_t EmitLogicalNotBool(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpLogicalNot, state.bool_type, ret, value}); return ret; } F32Class EmitClassifyF32(EmitterState& state, uint32_t value) { F32Class cls; cls.bits = EmitBitcastF32ToU32(state, value); const auto abs_bits = EmitAndConstant(state, cls.bits, 0x7fffffffu); const auto exponent_bits = EmitAndConstant(state, abs_bits, 0x7f800000u); const auto mantissa_bits = EmitAndConstant(state, abs_bits, 0x007fffffu); const auto exponent_max = EmitCompareU32Constant(state, OpIEqual, exponent_bits, 0x7f800000u); const auto mantissa_nonzero = EmitCompareU32Constant(state, OpINotEqual, mantissa_bits, 0); const auto quiet_bit_clear = EmitCompareU32Constant(state, OpIEqual, EmitAndConstant(state, cls.bits, 0x00400000u), 0); cls.nan = EmitLogicalAndBool(state, exponent_max, mantissa_nonzero); cls.snan = EmitLogicalAndBool(state, cls.nan, quiet_bit_clear); cls.zero = EmitCompareU32Constant(state, OpIEqual, abs_bits, 0); cls.quiet_bits = EmitOrU32(state, cls.bits, ConstantU32(state, 0x00400000u)); return cls; } uint32_t EmitClassMaskBitMatch(EmitterState& state, uint32_t mask, uint32_t bit, uint32_t class_match) { const auto selected = EmitCompareU32Constant(state, OpINotEqual, EmitAndConstant(state, mask, 1u << bit), 0); return EmitLogicalAndBool(state, selected, class_match); } uint32_t EmitClassMaskF32(EmitterState& state, uint32_t value, uint32_t mask) { const auto bits = EmitBitcastF32ToU32(state, value); const auto sign_bits = EmitAndConstant(state, bits, 0x80000000u); const auto abs_bits = EmitAndConstant(state, bits, 0x7fffffffu); const auto exponent_bits = EmitAndConstant(state, abs_bits, 0x7f800000u); const auto mantissa_bits = EmitAndConstant(state, abs_bits, 0x007fffffu); const auto quiet_bits = EmitAndConstant(state, mantissa_bits, 0x00400000u); const auto sign = EmitCompareU32Constant(state, OpINotEqual, sign_bits, 0); const auto positive = EmitLogicalNotBool(state, sign); const auto exponent_zero = EmitCompareU32Constant(state, OpIEqual, exponent_bits, 0); const auto exponent_nonzero = EmitLogicalNotBool(state, exponent_zero); const auto exponent_inf = EmitCompareU32Constant(state, OpIEqual, exponent_bits, 0x7f800000u); const auto finite_exponent = EmitLogicalNotBool(state, exponent_inf); const auto mantissa_zero = EmitCompareU32Constant(state, OpIEqual, mantissa_bits, 0); const auto mantissa_nonzero = EmitLogicalNotBool(state, mantissa_zero); const auto quiet = EmitCompareU32Constant(state, OpINotEqual, quiet_bits, 0); const auto nan_common = EmitLogicalAndBool(state, exponent_inf, mantissa_nonzero); const auto snan = EmitLogicalAndBool(state, nan_common, EmitLogicalNotBool(state, quiet)); const auto qnan = EmitLogicalAndBool(state, nan_common, quiet); const auto inf = EmitLogicalAndBool(state, exponent_inf, mantissa_zero); const auto normal = EmitLogicalAndBool(state, exponent_nonzero, finite_exponent); const auto denorm = EmitLogicalAndBool(state, exponent_zero, mantissa_nonzero); const auto zero = EmitCompareU32Constant(state, OpIEqual, abs_bits, 0); uint32_t match = EmitClassMaskBitMatch(state, mask, 0, snan); match = EmitLogicalOrBool(state, match, EmitClassMaskBitMatch(state, mask, 1, qnan)); match = EmitLogicalOrBool( state, match, EmitClassMaskBitMatch(state, mask, 2, EmitLogicalAndBool(state, inf, sign))); match = EmitLogicalOrBool( state, match, EmitClassMaskBitMatch(state, mask, 3, EmitLogicalAndBool(state, normal, sign))); match = EmitLogicalOrBool( state, match, EmitClassMaskBitMatch(state, mask, 4, EmitLogicalAndBool(state, denorm, sign))); match = EmitLogicalOrBool( state, match, EmitClassMaskBitMatch(state, mask, 5, EmitLogicalAndBool(state, zero, sign))); match = EmitLogicalOrBool( state, match, EmitClassMaskBitMatch(state, mask, 6, EmitLogicalAndBool(state, zero, positive))); match = EmitLogicalOrBool( state, match, EmitClassMaskBitMatch(state, mask, 7, EmitLogicalAndBool(state, denorm, positive))); match = EmitLogicalOrBool( state, match, EmitClassMaskBitMatch(state, mask, 8, EmitLogicalAndBool(state, normal, positive))); return EmitLogicalOrBool( state, match, EmitClassMaskBitMatch(state, mask, 9, EmitLogicalAndBool(state, inf, positive))); } uint32_t EmitMinMaxF32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value) { const auto lhs_class = EmitClassifyF32(state, lhs); const auto rhs_class = EmitClassifyF32(state, rhs); const auto numeric_cond = state.builder.AllocateId(); state.builder.AddFunction({max_value ? OpFOrdGreaterThanEqual : OpFOrdLessThan, state.bool_type, numeric_cond, lhs, rhs}); const auto ordered_bits = EmitSelectValueU32(state, numeric_cond, lhs_class.bits, rhs_class.bits); const auto both_zero = EmitLogicalAndBool(state, lhs_class.zero, rhs_class.zero); const auto zero_bits = max_value ? EmitAndU32(state, lhs_class.bits, rhs_class.bits) : EmitOrU32(state, lhs_class.bits, rhs_class.bits); const auto numeric_bits = EmitSelectValueU32(state, both_zero, zero_bits, ordered_bits); const auto rhs_nan_bits = EmitSelectValueU32(state, rhs_class.nan, lhs_class.bits, numeric_bits); const auto non_snan_bits = EmitSelectValueU32(state, lhs_class.nan, rhs_class.bits, rhs_nan_bits); const auto rhs_snan_bits = EmitSelectValueU32(state, rhs_class.snan, rhs_class.quiet_bits, non_snan_bits); const auto result_bits = EmitSelectValueU32(state, lhs_class.snan, lhs_class.quiet_bits, rhs_snan_bits); return EmitBitcastU32ToF32(state, result_bits); } void EmitMinMaxF32(EmitterState& state, const IR::Instruction& inst, bool max_value) { const auto lhs = EmitFloatLoad(state, inst.src[0]); const auto rhs = EmitFloatLoad(state, inst.src[1]); const auto u32 = state.builder.AllocateId(); const auto f32 = EmitMinMaxF32Value(state, lhs, rhs, max_value); const auto result = ApplyResultModifiersF32(state, f32, inst.dst); state.builder.AddFunction({OpBitcast, state.uint_type, u32, result}); EmitStoreU32(state, inst.dst, u32); } void EmitCompareResult(EmitterState& state, const IR::Operand& dst, uint32_t cond) { if (IsSccOperand(dst)) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpSelect, state.uint_type, ret, cond, ConstantU32(state, 1), ConstantU32(state, 0)}); EmitStoreU32(state, dst, ret); return; } EmitLaneMaskPairFromBool(state, dst, cond); } void EmitCompareU32(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto cond = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.bool_type, cond, lhs, rhs}); EmitCompareResult(state, inst.dst, cond); } void EmitCompareU16(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto lhs = EmitAndConstant(state, EmitValueLoad(state, inst.src[0]), 0xffffu); const auto rhs = EmitAndConstant(state, EmitValueLoad(state, inst.src[1]), 0xffffu); const auto cond = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.bool_type, cond, lhs, rhs}); EmitCompareResult(state, inst.dst, cond); } void EmitCompareI16(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto lhs = EmitSignExtendLow16U32(state, EmitValueLoad(state, inst.src[0])); const auto rhs = EmitSignExtendLow16U32(state, EmitValueLoad(state, inst.src[1])); const auto cond = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.bool_type, cond, lhs, rhs}); EmitCompareResult(state, inst.dst, cond); } void EmitBitCompareB32(EmitterState& state, const IR::Instruction& inst, bool bit_set) { const auto value = EmitValueLoad(state, inst.src[0]); const auto bit_index = state.builder.AllocateId(); const auto bit = state.builder.AllocateId(); const auto cond = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitwiseAnd, state.uint_type, bit_index, EmitValueLoad(state, inst.src[1]), ConstantU32(state, 31)}); state.builder.AddFunction( {OpBitFieldUExtract, state.uint_type, bit, value, bit_index, ConstantU32(state, 1)}); state.builder.AddFunction( {OpIEqual, state.bool_type, cond, bit, ConstantU32(state, bit_set ? 1u : 0u)}); state.builder.AddFunction( {OpSelect, state.uint_type, ret, cond, ConstantU32(state, 1), ConstantU32(state, 0)}); EmitStoreU32(state, inst.dst, ret); } void EmitCompareU64(EmitterState& state, const IR::Instruction& inst) { const bool equal = inst.op == IR::Opcode::CompareEqU64; const auto lhs_low = EmitSequentialValueLoad(state, inst.src[0], 0); const auto lhs_high = EmitSequentialValueLoad(state, inst.src[0], 1); const auto rhs_low = EmitSequentialValueLoad(state, inst.src[1], 0); const auto rhs_high = EmitSequentialValueLoad(state, inst.src[1], 1); const auto low = state.builder.AllocateId(); const auto high = state.builder.AllocateId(); const auto cond = state.builder.AllocateId(); const auto compare_op = equal ? OpIEqual : OpINotEqual; state.builder.AddFunction({compare_op, state.bool_type, low, lhs_low, rhs_low}); state.builder.AddFunction({compare_op, state.bool_type, high, lhs_high, rhs_high}); state.builder.AddFunction( {equal ? OpLogicalAnd : OpLogicalOr, state.bool_type, cond, low, high}); EmitCompareResult(state, inst.dst, cond); } void EmitCompareConstant(EmitterState& state, const IR::Instruction& inst, bool value) { const auto cond = state.builder.AllocateId(); state.builder.AddFunction({value ? OpIEqual : OpINotEqual, state.bool_type, cond, ConstantU32(state, 0), ConstantU32(state, 0)}); EmitCompareResult(state, inst.dst, cond); } void EmitCompareMaskU32(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto lhs = EmitValueLoad(state, inst.src[0]); const auto rhs = EmitValueLoad(state, inst.src[1]); const auto cond = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.bool_type, cond, lhs, rhs}); EmitCompareResult(state, inst.dst, cond); } void EmitCompareF32(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto lhs = EmitFloatLoad(state, inst.src[0]); const auto rhs = EmitFloatLoad(state, inst.src[1]); const auto cond = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.bool_type, cond, lhs, rhs}); EmitCompareResult(state, inst.dst, cond); } void EmitCompareOrderedF32(EmitterState& state, const IR::Instruction& inst, bool ordered) { const auto lhs = EmitFloatLoad(state, inst.src[0]); const auto rhs = EmitFloatLoad(state, inst.src[1]); const auto lhs_check = state.builder.AllocateId(); const auto rhs_check = state.builder.AllocateId(); const auto cond = state.builder.AllocateId(); if (ordered) { state.builder.AddFunction({OpFOrdEqual, state.bool_type, lhs_check, lhs, lhs}); state.builder.AddFunction({OpFOrdEqual, state.bool_type, rhs_check, rhs, rhs}); state.builder.AddFunction({OpLogicalAnd, state.bool_type, cond, lhs_check, rhs_check}); } else { state.builder.AddFunction({OpFUnordNotEqual, state.bool_type, lhs_check, lhs, lhs}); state.builder.AddFunction({OpFUnordNotEqual, state.bool_type, rhs_check, rhs, rhs}); state.builder.AddFunction({OpLogicalOr, state.bool_type, cond, lhs_check, rhs_check}); } EmitCompareResult(state, inst.dst, cond); } void EmitCompareClassF32(EmitterState& state, const IR::Instruction& inst) { const auto value = EmitFloatLoad(state, inst.src[0]); const auto mask = EmitValueLoad(state, inst.src[1]); EmitCompareResult(state, inst.dst, EmitClassMaskF32(state, value, mask)); } void EmitCompareMaskF32(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto lhs = EmitFloatLoad(state, inst.src[0]); const auto rhs = EmitFloatLoad(state, inst.src[1]); const auto cond = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.bool_type, cond, lhs, rhs}); EmitCompareResult(state, inst.dst, cond); } void EmitConvertU32ToF32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitValueLoad(state, inst.src[0]); const auto f32 = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction({OpConvertUToF, state.float_type, f32, src}); const auto result = ApplyResultModifiersF32(state, f32, inst.dst); state.builder.AddFunction({OpBitcast, state.uint_type, u32, result}); EmitStoreU32(state, inst.dst, u32); } void EmitConvertI32ToF32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitValueLoad(state, inst.src[0]); const auto i32 = state.builder.AllocateId(); const auto f32 = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.int_type, i32, src}); state.builder.AddFunction({OpConvertSToF, state.float_type, f32, i32}); const auto result = ApplyResultModifiersF32(state, f32, inst.dst); state.builder.AddFunction({OpBitcast, state.uint_type, u32, result}); EmitStoreU32(state, inst.dst, u32); } uint32_t EmitTruncF32Value(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, ret, state.glsl_std450, GlslTrunc, value}); return ret; } void EmitConvertF32ToU32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitFloatLoad(state, inst.src[0]); const auto trunc = EmitTruncF32Value(state, src); const auto converted = state.builder.AllocateId(); const auto below_zero = state.builder.AllocateId(); const auto above_max = state.builder.AllocateId(); state.builder.AddFunction({OpConvertFToU, state.uint_type, converted, trunc}); state.builder.AddFunction( {OpFOrdLessThanEqual, state.bool_type, below_zero, src, ConstantF32(state, 0)}); state.builder.AddFunction( {OpFOrdGreaterThanEqual, state.bool_type, above_max, src, ConstantF32(state, 0x4f800000u)}); const auto zero_or_nan = EmitLogicalOrBool(state, EmitClassifyF32(state, src).nan, below_zero); const auto saturated = EmitSelectValueU32(state, above_max, ConstantU32(state, 0xffffffffu), converted); const auto ret = EmitSelectValueU32(state, zero_or_nan, ConstantU32(state, 0), saturated); EmitStoreU32(state, inst.dst, ret); } void EmitConvertF32ToI32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitFloatLoad(state, inst.src[0]); const auto trunc = EmitTruncF32Value(state, src); const auto i32 = state.builder.AllocateId(); const auto converted = state.builder.AllocateId(); const auto below_min = state.builder.AllocateId(); const auto above_max = state.builder.AllocateId(); state.builder.AddFunction({OpConvertFToS, state.int_type, i32, trunc}); state.builder.AddFunction({OpBitcast, state.uint_type, converted, i32}); state.builder.AddFunction( {OpFOrdLessThanEqual, state.bool_type, below_min, src, ConstantF32(state, 0xcf000000u)}); state.builder.AddFunction( {OpFOrdGreaterThanEqual, state.bool_type, above_max, src, ConstantF32(state, 0x4f000000u)}); const auto high_sat = EmitSelectValueU32(state, above_max, ConstantU32(state, 0x7fffffffu), converted); const auto low_sat = EmitSelectValueU32(state, below_min, ConstantU32(state, 0x80000000u), high_sat); const auto ret = EmitSelectValueU32(state, EmitClassifyF32(state, src).nan, ConstantU32(state, 0), low_sat); EmitStoreU32(state, inst.dst, ret); } void EmitConvertF32ToF16(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitFloatLoad(state, inst.src[0]); const auto pair = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction( {OpCompositeConstruct, state.vec2_float_type, pair, src, ConstantF32(state, 0)}); state.builder.AddFunction( {OpExtInst, state.uint_type, u32, state.glsl_std450, GlslPackHalf2x16, pair}); EmitStoreU32(state, inst.dst, u32); } void EmitConvertF16ToF32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitValueLoad(state, inst.src[0]); const auto unpacked = state.builder.AllocateId(); auto f32 = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.vec2_float_type, unpacked, state.glsl_std450, GlslUnpackHalf2x16, src}); state.builder.AddFunction({OpCompositeExtract, state.float_type, f32, unpacked, 0}); if (inst.src[0].absolute) { const auto abs_value = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, abs_value, state.glsl_std450, GlslFAbs, f32}); f32 = abs_value; } if (inst.src[0].negate) { const auto neg_value = state.builder.AllocateId(); state.builder.AddFunction({OpFNegate, state.float_type, neg_value, f32}); f32 = neg_value; } const auto result = ApplyResultModifiersF32(state, f32, inst.dst); state.builder.AddFunction({OpBitcast, state.uint_type, u32, result}); EmitStoreU32(state, inst.dst, u32); } void EmitConvertF16ToU16(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitValueLoad(state, inst.src[0]); const auto unpacked = state.builder.AllocateId(); auto f32 = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.vec2_float_type, unpacked, state.glsl_std450, GlslUnpackHalf2x16, src}); state.builder.AddFunction({OpCompositeExtract, state.float_type, f32, unpacked, 0}); if (inst.src[0].absolute) { const auto abs_value = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, abs_value, state.glsl_std450, GlslFAbs, f32}); f32 = abs_value; } if (inst.src[0].negate) { const auto neg_value = state.builder.AllocateId(); state.builder.AddFunction({OpFNegate, state.float_type, neg_value, f32}); f32 = neg_value; } const auto truncated = EmitTruncF32Value(state, f32); const auto below_zero = state.builder.AllocateId(); const auto above_max = state.builder.AllocateId(); const auto safe_low = state.builder.AllocateId(); const auto safe_value = state.builder.AllocateId(); const auto converted = state.builder.AllocateId(); state.builder.AddFunction( {OpFOrdLessThanEqual, state.bool_type, below_zero, f32, ConstantF32(state, 0)}); state.builder.AddFunction( {OpFOrdGreaterThanEqual, state.bool_type, above_max, f32, ConstantF32(state, 0x477fff00u)}); const auto zero_or_nan = EmitLogicalOrBool(state, EmitClassifyF32(state, f32).nan, below_zero); state.builder.AddFunction( {OpSelect, state.float_type, safe_low, zero_or_nan, ConstantF32(state, 0), truncated}); state.builder.AddFunction({OpSelect, state.float_type, safe_value, above_max, ConstantF32(state, 0x477fff00u), safe_low}); state.builder.AddFunction({OpConvertFToU, state.uint_type, converted, safe_value}); EmitStoreU32(state, inst.dst, EmitAndConstant(state, converted, 0xffffu)); } void EmitConvertU16ToF16(EmitterState& state, const IR::Instruction& inst) { const auto src_bits = EmitU16LaneBits(state, inst.src[0], false, false); const auto f32 = state.builder.AllocateId(); state.builder.AddFunction({OpConvertUToF, state.float_type, f32, src_bits}); EmitStoreU32(state, inst.dst, EmitPackLowF32ToF16Bits(state, f32)); } void EmitConvertI16ToF16(EmitterState& state, const IR::Instruction& inst) { const auto src_bits = EmitU16LaneBits(state, inst.src[0], false, true); const auto i32 = state.builder.AllocateId(); const auto f32 = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.int_type, i32, src_bits}); state.builder.AddFunction({OpConvertSToF, state.float_type, f32, i32}); EmitStoreU32(state, inst.dst, EmitPackLowF32ToF16Bits(state, f32)); } void EmitConvertF16ToI16(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitValueLoad(state, inst.src[0]); const auto unpacked = state.builder.AllocateId(); auto f32 = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.vec2_float_type, unpacked, state.glsl_std450, GlslUnpackHalf2x16, src}); state.builder.AddFunction({OpCompositeExtract, state.float_type, f32, unpacked, 0}); if (inst.src[0].absolute) { const auto abs_value = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, abs_value, state.glsl_std450, GlslFAbs, f32}); f32 = abs_value; } if (inst.src[0].negate) { const auto neg_value = state.builder.AllocateId(); state.builder.AddFunction({OpFNegate, state.float_type, neg_value, f32}); f32 = neg_value; } const auto truncated = EmitTruncF32Value(state, f32); const auto below_min = state.builder.AllocateId(); const auto above_max = state.builder.AllocateId(); const auto safe_low = state.builder.AllocateId(); const auto safe_value = state.builder.AllocateId(); const auto nan_safe = state.builder.AllocateId(); const auto i32 = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction( {OpFOrdLessThanEqual, state.bool_type, below_min, f32, ConstantF32(state, 0xc7000000u)}); state.builder.AddFunction( {OpFOrdGreaterThanEqual, state.bool_type, above_max, f32, ConstantF32(state, 0x46fffe00u)}); state.builder.AddFunction({OpSelect, state.float_type, safe_low, below_min, ConstantF32(state, 0xc7000000u), truncated}); state.builder.AddFunction({OpSelect, state.float_type, safe_value, above_max, ConstantF32(state, 0x46fffe00u), safe_low}); state.builder.AddFunction({OpSelect, state.float_type, nan_safe, EmitClassifyF32(state, f32).nan, ConstantF32(state, 0), safe_value}); state.builder.AddFunction({OpConvertFToS, state.int_type, i32, nan_safe}); state.builder.AddFunction({OpBitcast, state.uint_type, u32, i32}); EmitStoreU32(state, inst.dst, EmitAndConstant(state, u32, 0xffffu)); } void EmitConvertRoundPlusInfF32ToI32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitFloatLoad(state, inst.src[0]); const auto biased = state.builder.AllocateId(); const auto rounded = state.builder.AllocateId(); const auto i32 = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction( {OpFAdd, state.float_type, biased, src, ConstantF32(state, 0x3f000000u)}); state.builder.AddFunction( {OpExtInst, state.float_type, rounded, state.glsl_std450, GlslFloor, biased}); state.builder.AddFunction({OpConvertFToS, state.int_type, i32, rounded}); state.builder.AddFunction({OpBitcast, state.uint_type, u32, i32}); EmitStoreU32(state, inst.dst, u32); } void EmitConvertFloorF32ToI32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitFloatLoad(state, inst.src[0]); const auto floor = state.builder.AllocateId(); const auto i32 = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, floor, state.glsl_std450, GlslFloor, src}); state.builder.AddFunction({OpConvertFToS, state.int_type, i32, floor}); state.builder.AddFunction({OpBitcast, state.uint_type, u32, i32}); EmitStoreU32(state, inst.dst, u32); } void EmitConvertI4ToOffsetF32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitValueLoad(state, inst.src[0]); const auto src_i32 = state.builder.AllocateId(); const auto nibble = state.builder.AllocateId(); const auto f32 = state.builder.AllocateId(); const auto scaled = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.int_type, src_i32, src}); state.builder.AddFunction({OpBitFieldSExtract, state.int_type, nibble, src_i32, ConstantU32(state, 0), ConstantU32(state, 4)}); state.builder.AddFunction({OpConvertSToF, state.float_type, f32, nibble}); state.builder.AddFunction( {OpFDiv, state.float_type, scaled, f32, ConstantF32(state, 0x41800000u)}); const auto result = ApplyResultModifiersF32(state, scaled, inst.dst); state.builder.AddFunction({OpBitcast, state.uint_type, u32, result}); EmitStoreU32(state, inst.dst, u32); } void EmitConvertByteU32ToF32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitValueLoad(state, inst.src[0]); const auto index = inst.src[1].kind == IR::OperandKind::ImmediateU32 ? inst.src[1].imm : 0u; const auto byte = EmitBitFieldExtractConstant(state, src, (index & 3u) * 8u, 8u); const auto f32 = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction({OpConvertUToF, state.float_type, f32, byte}); const auto result = ApplyResultModifiersF32(state, f32, inst.dst); state.builder.AddFunction({OpBitcast, state.uint_type, u32, result}); EmitStoreU32(state, inst.dst, u32); } uint32_t EmitFlushF32DenormToSignedZero(EmitterState& state, uint32_t value) { const auto bits = state.builder.AllocateId(); const auto abs_bits = state.builder.AllocateId(); const auto sign_bits = state.builder.AllocateId(); const auto non_zero = state.builder.AllocateId(); const auto subnormal = state.builder.AllocateId(); const auto flush = state.builder.AllocateId(); const auto selected = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.uint_type, bits, value}); state.builder.AddFunction( {OpBitwiseAnd, state.uint_type, abs_bits, bits, ConstantU32(state, 0x7fffffffu)}); state.builder.AddFunction( {OpBitwiseAnd, state.uint_type, sign_bits, bits, ConstantU32(state, 0x80000000u)}); state.builder.AddFunction( {OpINotEqual, state.bool_type, non_zero, abs_bits, ConstantU32(state, 0)}); state.builder.AddFunction( {OpULessThan, state.bool_type, subnormal, abs_bits, ConstantU32(state, 0x00800000u)}); state.builder.AddFunction({OpLogicalAnd, state.bool_type, flush, non_zero, subnormal}); state.builder.AddFunction({OpSelect, state.uint_type, selected, flush, sign_bits, bits}); state.builder.AddFunction({OpBitcast, state.float_type, ret, selected}); return ret; } void EmitRcpF32(EmitterState& state, const IR::Instruction& inst) { const auto src = EmitFlushF32DenormToSignedZero(state, EmitFloatLoad(state, inst.src[0])); const auto f32 = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction( {OpFDiv, state.float_type, f32, ConstantF32(state, 0x3f800000u), src}); const auto result = ApplyResultModifiersF32(state, f32, inst.dst); state.builder.AddFunction({OpBitcast, state.uint_type, u32, result}); EmitStoreU32(state, inst.dst, u32); } uint32_t EmitTrigCycleF32(EmitterState& state, uint32_t src, bool preserve_signed_zero) { const auto fract = state.builder.AllocateId(); const auto bits = state.builder.AllocateId(); const auto abs_bits = state.builder.AllocateId(); const auto large = state.builder.AllocateId(); const auto finite = state.builder.AllocateId(); const auto large_finite = state.builder.AllocateId(); const auto reduced = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, fract, state.glsl_std450, GlslFract, src}); state.builder.AddFunction({OpBitcast, state.uint_type, bits, src}); state.builder.AddFunction( {OpBitwiseAnd, state.uint_type, abs_bits, bits, ConstantU32(state, 0x7fffffffu)}); state.builder.AddFunction( {OpUGreaterThanEqual, state.bool_type, large, abs_bits, ConstantU32(state, 0x4b000000u)}); state.builder.AddFunction( {OpULessThan, state.bool_type, finite, abs_bits, ConstantU32(state, 0x7f800000u)}); state.builder.AddFunction({OpLogicalAnd, state.bool_type, large_finite, large, finite}); state.builder.AddFunction( {OpSelect, state.float_type, reduced, large_finite, ConstantF32(state, 0), fract}); if (!preserve_signed_zero) { return reduced; } const auto zero = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpIEqual, state.bool_type, zero, abs_bits, ConstantU32(state, 0)}); state.builder.AddFunction({OpSelect, state.float_type, ret, zero, src, reduced}); return ret; } void EmitFloatExtInst(EmitterState& state, const IR::Instruction& inst, uint32_t ext_inst, bool scale_by_two_pi, bool flush_denorm) { auto src = EmitFloatLoad(state, inst.src[0]); if (flush_denorm) { src = EmitFlushF32DenormToSignedZero(state, src); } if (scale_by_two_pi) { src = EmitTrigCycleF32(state, src, ext_inst == GlslSin); const auto scaled = state.builder.AllocateId(); state.builder.AddFunction( {OpFMul, state.float_type, scaled, src, ConstantF32(state, 0x40c90fdbu)}); src = scaled; } const auto f32 = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction({OpExtInst, state.float_type, f32, state.glsl_std450, ext_inst, src}); const auto result = ApplyResultModifiersF32(state, f32, inst.dst); state.builder.AddFunction({OpBitcast, state.uint_type, u32, result}); EmitStoreU32(state, inst.dst, u32); } void EmitMadF32(EmitterState& state, const IR::Instruction& inst) { const auto lhs = EmitMixF32Load(state, inst.src[0]); const auto rhs = EmitMixF32Load(state, inst.src[1]); const auto add = EmitMixF32Load(state, inst.src[2]); const auto f32 = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, f32, state.glsl_std450, GlslFma, lhs, rhs, add}); const auto result = ApplyResultModifiersF32(state, f32, inst.dst); state.builder.AddFunction({OpBitcast, state.uint_type, u32, result}); EmitStoreU32(state, inst.dst, u32); } uint32_t EmitFNegateValue(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpFNegate, state.float_type, ret, value}); return ret; } uint32_t EmitFAbsValue(EmitterState& state, uint32_t value) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, ret, state.glsl_std450, GlslFAbs, value}); return ret; } uint32_t EmitFCompareValue(EmitterState& state, uint32_t opcode, uint32_t lhs, uint32_t rhs) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.bool_type, ret, lhs, rhs}); return ret; } uint32_t EmitLogicalAndValue(EmitterState& state, uint32_t lhs, uint32_t rhs) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpLogicalAnd, state.bool_type, ret, lhs, rhs}); return ret; } uint32_t EmitFSelectValue(EmitterState& state, uint32_t cond, uint32_t true_value, uint32_t false_value) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpSelect, state.float_type, ret, cond, true_value, false_value}); return ret; } uint32_t EmitFMulConstant(EmitterState& state, uint32_t value, uint32_t bits) { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpFMul, state.float_type, ret, value, ConstantF32(state, bits)}); return ret; } CubeF32Values EmitCubeF32Values(EmitterState& state, const IR::Instruction& inst) { CubeF32Values cube; cube.x = EmitFloatLoad(state, inst.src[0]); cube.y = EmitFloatLoad(state, inst.src[1]); cube.z = EmitFloatLoad(state, inst.src[2]); cube.nx = EmitFNegateValue(state, cube.x); cube.ny = EmitFNegateValue(state, cube.y); cube.nz = EmitFNegateValue(state, cube.z); const auto ax = EmitFAbsValue(state, cube.x); const auto ay = EmitFAbsValue(state, cube.y); const auto az = EmitFAbsValue(state, cube.z); const auto z_ge_x = EmitFCompareValue(state, OpFOrdGreaterThanEqual, az, ax); const auto z_ge_y = EmitFCompareValue(state, OpFOrdGreaterThanEqual, az, ay); cube.z_face = EmitLogicalAndValue(state, z_ge_x, z_ge_y); cube.y_face = EmitFCompareValue(state, OpFOrdGreaterThanEqual, ay, ax); cube.x_neg = EmitFCompareValue(state, OpFOrdLessThan, cube.x, ConstantF32(state, 0)); cube.y_neg = EmitFCompareValue(state, OpFOrdLessThan, cube.y, ConstantF32(state, 0)); cube.z_neg = EmitFCompareValue(state, OpFOrdLessThan, cube.z, ConstantF32(state, 0)); return cube; } uint32_t EmitCubeIdF32(EmitterState& state, const CubeF32Values& cube) { const auto x_face = EmitFSelectValue(state, cube.x_neg, ConstantF32(state, 0x3f800000u), ConstantF32(state, 0)); const auto y_face = EmitFSelectValue(state, cube.y_neg, ConstantF32(state, 0x40400000u), ConstantF32(state, 0x40000000u)); const auto z_face = EmitFSelectValue(state, cube.z_neg, ConstantF32(state, 0x40a00000u), ConstantF32(state, 0x40800000u)); const auto xy = EmitFSelectValue(state, cube.y_face, y_face, x_face); return EmitFSelectValue(state, cube.z_face, z_face, xy); } uint32_t EmitCubeScF32(EmitterState& state, const CubeF32Values& cube) { const auto x_sc = EmitFSelectValue(state, cube.x_neg, cube.z, cube.nz); const auto z_sc = EmitFSelectValue(state, cube.z_neg, cube.nx, cube.x); const auto xy = EmitFSelectValue(state, cube.y_face, cube.x, x_sc); return EmitFSelectValue(state, cube.z_face, z_sc, xy); } uint32_t EmitCubeTcF32(EmitterState& state, const CubeF32Values& cube) { const auto y_tc = EmitFSelectValue(state, cube.y_neg, cube.nz, cube.z); const auto xy = EmitFSelectValue(state, cube.y_face, y_tc, cube.ny); return EmitFSelectValue(state, cube.z_face, cube.ny, xy); } uint32_t EmitCubeMaF32(EmitterState& state, const CubeF32Values& cube) { const auto x_ma = EmitFMulConstant(state, cube.x, 0x40000000u); const auto y_ma = EmitFMulConstant(state, cube.y, 0x40000000u); const auto z_ma = EmitFMulConstant(state, cube.z, 0x40000000u); const auto xy = EmitFSelectValue(state, cube.y_face, y_ma, x_ma); return EmitFSelectValue(state, cube.z_face, z_ma, xy); } void EmitCubeF32(EmitterState& state, const IR::Instruction& inst) { const auto cube = EmitCubeF32Values(state, inst); uint32_t ret = 0; switch (inst.op) { case IR::Opcode::CubeIdF32: ret = EmitCubeIdF32(state, cube); break; case IR::Opcode::CubeScF32: ret = EmitCubeScF32(state, cube); break; case IR::Opcode::CubeTcF32: ret = EmitCubeTcF32(state, cube); break; case IR::Opcode::CubeMaF32: ret = EmitCubeMaF32(state, cube); break; default: ret = ConstantF32(state, 0); break; } const auto bits = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.uint_type, bits, ret}); EmitStoreU32(state, inst.dst, bits); } void EmitDot2AccF32F16(EmitterState& state, const IR::Instruction& inst) { const auto src0 = EmitValueLoad(state, inst.src[0]); const auto src1 = EmitValueLoad(state, inst.src[1]); const auto vec0 = state.builder.AllocateId(); const auto vec1 = state.builder.AllocateId(); const auto a_lo = state.builder.AllocateId(); const auto a_hi = state.builder.AllocateId(); const auto b_lo = state.builder.AllocateId(); const auto b_hi = state.builder.AllocateId(); const auto acc = EmitFloatLoad(state, inst.src[2]); const auto lo = state.builder.AllocateId(); const auto dot = state.builder.AllocateId(); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.vec2_float_type, vec0, state.glsl_std450, GlslUnpackHalf2x16, src0}); state.builder.AddFunction( {OpExtInst, state.vec2_float_type, vec1, state.glsl_std450, GlslUnpackHalf2x16, src1}); state.builder.AddFunction({OpCompositeExtract, state.float_type, a_lo, vec0, 0}); state.builder.AddFunction({OpCompositeExtract, state.float_type, a_hi, vec0, 1}); state.builder.AddFunction({OpCompositeExtract, state.float_type, b_lo, vec1, 0}); state.builder.AddFunction({OpCompositeExtract, state.float_type, b_hi, vec1, 1}); state.builder.AddFunction( {OpExtInst, state.float_type, lo, state.glsl_std450, GlslFma, a_lo, b_lo, acc}); state.builder.AddFunction( {OpExtInst, state.float_type, dot, state.glsl_std450, GlslFma, a_hi, b_hi, lo}); state.builder.AddFunction({OpBitcast, state.uint_type, u32, dot}); EmitStoreU32(state, inst.dst, u32); } uint32_t EmitPackedF16Lane(EmitterState& state, const IR::Operand& operand, bool high_lane) { const auto raw = EmitValueLoad(state, operand); const auto unpacked = state.builder.AllocateId(); auto value = state.builder.AllocateId(); const auto selected_lane = high_lane ? operand.op_sel_hi : operand.op_sel; const auto lane = selected_lane ? 1u : 0u; state.builder.AddFunction( {OpExtInst, state.vec2_float_type, unpacked, state.glsl_std450, GlslUnpackHalf2x16, raw}); state.builder.AddFunction({OpCompositeExtract, state.float_type, value, unpacked, lane}); if (operand.absolute) { const auto abs_value = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, abs_value, state.glsl_std450, GlslFAbs, value}); value = abs_value; } if (high_lane ? operand.negate_hi : operand.negate) { const auto neg_value = state.builder.AllocateId(); state.builder.AddFunction({OpFNegate, state.float_type, neg_value, value}); value = neg_value; } return value; } void EmitCompareF16(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto lhs = EmitPackedF16Lane(state, inst.src[0], false); const auto rhs = EmitPackedF16Lane(state, inst.src[1], false); const auto cond = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.bool_type, cond, lhs, rhs}); EmitCompareResult(state, inst.dst, cond); } uint32_t EmitPackedF16LaneBits(EmitterState& state, const IR::Operand& operand, bool high_lane) { const auto raw = EmitValueLoad(state, operand); const auto selected_lane = high_lane ? operand.op_sel_hi : operand.op_sel; const auto lane = selected_lane ? 1u : 0u; auto bits = lane != 0u ? EmitShiftRightConstant(state, raw, 16) : raw; bits = EmitAndConstant(state, bits, 0xffffu); if (operand.absolute) { bits = EmitAndConstant(state, bits, 0x7fffu); } if (high_lane ? operand.negate_hi : operand.negate) { bits = EmitXorConstant(state, bits, 0x8000u); } return bits; } uint32_t EmitF16BitsToF32(EmitterState& state, uint32_t bits) { const auto unpacked = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.vec2_float_type, unpacked, state.glsl_std450, GlslUnpackHalf2x16, bits}); state.builder.AddFunction({OpCompositeExtract, state.float_type, ret, unpacked, 0}); return ret; } uint32_t EmitPackLowF32ToF16Bits(EmitterState& state, uint32_t value) { const auto pair = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpCompositeConstruct, state.vec2_float_type, pair, value, ConstantF32(state, 0)}); state.builder.AddFunction( {OpExtInst, state.uint_type, ret, state.glsl_std450, GlslPackHalf2x16, pair}); return EmitAndConstant(state, ret, 0xffffu); } void EmitPackB32F16(EmitterState& state, const IR::Instruction& inst) { const auto lo = EmitPackedF16LaneBits(state, inst.src[0], false); const auto hi = EmitShiftLeftConstant(state, EmitPackedF16LaneBits(state, inst.src[1], false), 16); EmitStoreU32(state, inst.dst, EmitOrU32(state, lo, hi)); } F16Class EmitClassifyF16Bits(EmitterState& state, uint32_t value) { F16Class cls; cls.bits = EmitAndConstant(state, value, 0xffffu); const auto abs_bits = EmitAndConstant(state, cls.bits, 0x7fffu); const auto exponent_bits = EmitAndConstant(state, abs_bits, 0x7c00u); const auto mantissa_bits = EmitAndConstant(state, abs_bits, 0x03ffu); const auto exponent_max = EmitCompareU32Constant(state, OpIEqual, exponent_bits, 0x7c00u); const auto mantissa_nonzero = EmitCompareU32Constant(state, OpINotEqual, mantissa_bits, 0); const auto quiet_bit_clear = EmitCompareU32Constant(state, OpIEqual, EmitAndConstant(state, cls.bits, 0x0200u), 0); cls.nan = EmitLogicalAndBool(state, exponent_max, mantissa_nonzero); cls.snan = EmitLogicalAndBool(state, cls.nan, quiet_bit_clear); cls.zero = EmitCompareU32Constant(state, OpIEqual, abs_bits, 0); cls.quiet_bits = EmitOrU32(state, cls.bits, ConstantU32(state, 0x0200u)); return cls; } uint32_t EmitNegativeNumericF16Bool(EmitterState& state, const F16Class& cls) { const auto sign = EmitCompareU32Constant(state, OpINotEqual, EmitAndConstant(state, cls.bits, 0x8000u), 0); const auto nonzero = EmitLogicalNotBool(state, cls.zero); return EmitLogicalAndBool(state, EmitLogicalAndBool(state, sign, nonzero), EmitLogicalNotBool(state, cls.nan)); } uint32_t EmitRcpF16Bits(EmitterState& state, uint32_t bits) { const auto f32 = EmitF16BitsToF32(state, bits); const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpFDiv, state.float_type, ret, ConstantF32(state, 0x3f800000u), f32}); return EmitPackLowF32ToF16Bits(state, ret); } uint32_t EmitSqrtF16Bits(EmitterState& state, uint32_t bits) { const auto cls = EmitClassifyF16Bits(state, bits); const auto f32 = EmitF16BitsToF32(state, cls.bits); const auto sqrt = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, sqrt, state.glsl_std450, GlslSqrt, f32}); const auto packed = EmitPackLowF32ToF16Bits(state, sqrt); const auto numeric = EmitSelectValueU32(state, EmitNegativeNumericF16Bool(state, cls), ConstantU32(state, 0xfe00u), packed); return EmitSelectValueU32(state, cls.zero, cls.bits, numeric); } uint32_t EmitRsqF16Bits(EmitterState& state, uint32_t bits) { const auto cls = EmitClassifyF16Bits(state, bits); const auto f32 = EmitF16BitsToF32(state, cls.bits); const auto rsq = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, rsq, state.glsl_std450, GlslInverseSqrt, f32}); const auto packed = EmitPackLowF32ToF16Bits(state, rsq); const auto sign = EmitCompareU32Constant(state, OpINotEqual, EmitAndConstant(state, cls.bits, 0x8000u), 0); const auto zero_result = EmitSelectValueU32(state, sign, ConstantU32(state, 0xfc00u), ConstantU32(state, 0x7c00u)); const auto numeric = EmitSelectValueU32(state, EmitNegativeNumericF16Bool(state, cls), ConstantU32(state, 0xfe00u), packed); return EmitSelectValueU32(state, cls.zero, zero_result, numeric); } uint32_t EmitLog2F16Bits(EmitterState& state, uint32_t bits) { const auto cls = EmitClassifyF16Bits(state, bits); const auto f32 = EmitF16BitsToF32(state, cls.bits); const auto log2 = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, log2, state.glsl_std450, GlslLog2, f32}); const auto packed = EmitPackLowF32ToF16Bits(state, log2); return EmitSelectValueU32(state, EmitNegativeNumericF16Bool(state, cls), ConstantU32(state, 0xfe00u), packed); } uint32_t EmitExp2F16Bits(EmitterState& state, uint32_t bits) { const auto f32 = EmitF16BitsToF32(state, bits); const auto exp2 = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, exp2, state.glsl_std450, GlslExp2, f32}); return EmitPackLowF32ToF16Bits(state, exp2); } uint32_t EmitF16MathExtInstBits(EmitterState& state, uint32_t bits, uint32_t ext_inst) { const auto f32 = EmitF16BitsToF32(state, bits); const auto result = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, result, state.glsl_std450, ext_inst, f32}); return EmitPackLowF32ToF16Bits(state, result); } void EmitSpecialF16(EmitterState& state, const IR::Instruction& inst) { const auto bits = EmitPackedF16LaneBits(state, inst.src[0], false); uint32_t ret = ConstantU32(state, 0); switch (inst.op) { case IR::Opcode::RcpF16: ret = EmitRcpF16Bits(state, bits); break; case IR::Opcode::SqrtF16: ret = EmitSqrtF16Bits(state, bits); break; case IR::Opcode::InverseSqrtF16: ret = EmitRsqF16Bits(state, bits); break; case IR::Opcode::Log2F16: ret = EmitLog2F16Bits(state, bits); break; case IR::Opcode::Exp2F16: ret = EmitExp2F16Bits(state, bits); break; case IR::Opcode::FloorF16: ret = EmitF16MathExtInstBits(state, bits, GlslFloor); break; case IR::Opcode::CeilF16: ret = EmitF16MathExtInstBits(state, bits, GlslCeil); break; case IR::Opcode::TruncF16: ret = EmitF16MathExtInstBits(state, bits, GlslTrunc); break; case IR::Opcode::RoundEvenF16: ret = EmitF16MathExtInstBits(state, bits, GlslRoundEven); break; default: break; } EmitStoreU32(state, inst.dst, ret); } void EmitBinaryF16(EmitterState& state, const IR::Instruction& inst, uint32_t opcode) { const auto lhs = EmitPackedF16Lane(state, inst.src[0], false); const auto rhs = EmitPackedF16Lane(state, inst.src[1], false); auto value = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.float_type, value, lhs, rhs}); value = ApplyResultModifiersF32(state, value, inst.dst); EmitStoreU32(state, inst.dst, EmitPackLowF32ToF16Bits(state, value)); } void EmitFmaF16(EmitterState& state, const IR::Instruction& inst) { const auto fma = ApplyResultModifiersF32(state, EmitPackedF16FmaLane(state, inst, false), inst.dst); EmitStoreU32(state, inst.dst, EmitPackLowF32ToF16Bits(state, fma)); } uint32_t EmitMinMaxF16Bits(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value) { const auto lhs_class = EmitClassifyF16Bits(state, lhs); const auto rhs_class = EmitClassifyF16Bits(state, rhs); const auto lhs_f32 = EmitF16BitsToF32(state, lhs_class.bits); const auto rhs_f32 = EmitF16BitsToF32(state, rhs_class.bits); const auto numeric_cond = state.builder.AllocateId(); state.builder.AddFunction({max_value ? OpFOrdGreaterThanEqual : OpFOrdLessThan, state.bool_type, numeric_cond, lhs_f32, rhs_f32}); const auto ordered_bits = EmitSelectValueU32(state, numeric_cond, lhs_class.bits, rhs_class.bits); const auto both_zero = EmitLogicalAndBool(state, lhs_class.zero, rhs_class.zero); const auto zero_bits = max_value ? EmitAndU32(state, lhs_class.bits, rhs_class.bits) : EmitOrU32(state, lhs_class.bits, rhs_class.bits); const auto numeric_bits = EmitSelectValueU32(state, both_zero, zero_bits, ordered_bits); const auto rhs_nan_bits = EmitSelectValueU32(state, rhs_class.nan, lhs_class.bits, numeric_bits); const auto non_snan_bits = EmitSelectValueU32(state, lhs_class.nan, rhs_class.bits, rhs_nan_bits); const auto rhs_snan_bits = EmitSelectValueU32(state, rhs_class.snan, rhs_class.quiet_bits, non_snan_bits); const auto result_bits = EmitSelectValueU32(state, lhs_class.snan, lhs_class.quiet_bits, rhs_snan_bits); return EmitAndConstant(state, result_bits, 0xffffu); } void EmitMinMaxF16(EmitterState& state, const IR::Instruction& inst, bool max_value) { const auto bits = EmitMinMaxF16Bits(state, EmitPackedF16LaneBits(state, inst.src[0], false), EmitPackedF16LaneBits(state, inst.src[1], false), max_value); EmitStoreU32(state, inst.dst, bits); } uint32_t EmitMinMax3F16Bits(EmitterState& state, uint32_t src0, uint32_t src1, uint32_t src2, bool max_value) { return EmitMinMaxF16Bits(state, EmitMinMaxF16Bits(state, src0, src1, max_value), src2, max_value); } void EmitMinMax3F16(EmitterState& state, const IR::Instruction& inst, bool max_value) { const auto bits = EmitMinMax3F16Bits(state, EmitPackedF16LaneBits(state, inst.src[0], false), EmitPackedF16LaneBits(state, inst.src[1], false), EmitPackedF16LaneBits(state, inst.src[2], false), max_value); EmitStoreU32(state, inst.dst, bits); } uint32_t EmitF16BitsEqualBool(EmitterState& state, uint32_t lhs, uint32_t rhs) { const auto lhs_f32 = EmitF16BitsToF32(state, lhs); const auto rhs_f32 = EmitF16BitsToF32(state, rhs); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpFOrdEqual, state.bool_type, ret, lhs_f32, rhs_f32}); return ret; } uint32_t EmitAnyNanF16Bool(EmitterState& state, uint32_t src0, uint32_t src1, uint32_t src2) { const auto nan01 = EmitLogicalOrBool(state, EmitClassifyF16Bits(state, src0).nan, EmitClassifyF16Bits(state, src1).nan); return EmitLogicalOrBool(state, nan01, EmitClassifyF16Bits(state, src2).nan); } void EmitMed3F16(EmitterState& state, const IR::Instruction& inst) { const auto src0 = EmitPackedF16LaneBits(state, inst.src[0], false); const auto src1 = EmitPackedF16LaneBits(state, inst.src[1], false); const auto src2 = EmitPackedF16LaneBits(state, inst.src[2], false); const auto min3 = EmitMinMax3F16Bits(state, src0, src1, src2, false); const auto max3 = EmitMinMax3F16Bits(state, src0, src1, src2, true); const auto med_src0 = EmitMinMaxF16Bits(state, src1, src2, true); const auto med_src1 = EmitMinMaxF16Bits(state, src0, src2, true); const auto med_src2 = EmitMinMaxF16Bits(state, src0, src1, true); const auto med01 = EmitSelectValueU32(state, EmitF16BitsEqualBool(state, max3, src1), med_src1, med_src2); const auto median = EmitSelectValueU32(state, EmitF16BitsEqualBool(state, max3, src0), med_src0, med01); const auto bits = EmitSelectValueU32(state, EmitAnyNanF16Bool(state, src0, src1, src2), min3, median); EmitStoreU32(state, inst.dst, bits); } uint32_t EmitPackedMinMaxF16(EmitterState& state, const IR::Instruction& inst, bool max_value) { const auto lo = EmitMinMaxF16Bits(state, EmitPackedF16LaneBits(state, inst.src[0], false), EmitPackedF16LaneBits(state, inst.src[1], false), max_value); const auto hi = EmitMinMaxF16Bits(state, EmitPackedF16LaneBits(state, inst.src[0], true), EmitPackedF16LaneBits(state, inst.src[1], true), max_value); return EmitOrU32(state, lo, EmitShiftLeftConstant(state, hi, 16)); } uint32_t EmitPackedF16BinaryLane(EmitterState& state, const IR::Instruction& inst, bool high_lane, uint32_t opcode) { const auto lhs = EmitPackedF16Lane(state, inst.src[0], high_lane); const auto rhs = EmitPackedF16Lane(state, inst.src[1], high_lane); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({opcode, state.float_type, ret, lhs, rhs}); return ret; } uint32_t EmitPackedF16FmaLane(EmitterState& state, const IR::Instruction& inst, bool high_lane) { const auto lhs = EmitPackedF16Lane(state, inst.src[0], high_lane); const auto rhs = EmitPackedF16Lane(state, inst.src[1], high_lane); const auto add = EmitPackedF16Lane(state, inst.src[2], high_lane); const auto ret = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, ret, state.glsl_std450, GlslFma, lhs, rhs, add}); return ret; } uint32_t EmitPackedF16BitsWithResultModifiers(EmitterState& state, uint32_t bits, const IR::Operand& dst) { if (!dst.clamp && dst.omod == 0u) { return bits; } auto lo = ApplyResultModifiersF32( state, EmitF16BitsToF32(state, EmitAndConstant(state, bits, 0xffffu)), dst); auto hi = ApplyResultModifiersF32( state, EmitF16BitsToF32(state, EmitShiftRightConstant(state, bits, 16)), dst); const auto pair = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpCompositeConstruct, state.vec2_float_type, pair, lo, hi}); state.builder.AddFunction( {OpExtInst, state.uint_type, ret, state.glsl_std450, GlslPackHalf2x16, pair}); return ret; } void EmitPackedF16(EmitterState& state, const IR::Instruction& inst) { uint32_t lo = 0; uint32_t hi = 0; switch (inst.op) { case IR::Opcode::PackedAddF16: lo = EmitPackedF16BinaryLane(state, inst, false, OpFAdd); hi = EmitPackedF16BinaryLane(state, inst, true, OpFAdd); break; case IR::Opcode::PackedMulF16: lo = EmitPackedF16BinaryLane(state, inst, false, OpFMul); hi = EmitPackedF16BinaryLane(state, inst, true, OpFMul); break; case IR::Opcode::PackedMinF16: EmitStoreU32(state, inst.dst, EmitPackedF16BitsWithResultModifiers( state, EmitPackedMinMaxF16(state, inst, false), inst.dst)); return; case IR::Opcode::PackedMaxF16: EmitStoreU32(state, inst.dst, EmitPackedF16BitsWithResultModifiers( state, EmitPackedMinMaxF16(state, inst, true), inst.dst)); return; case IR::Opcode::PackedFmaF16: lo = EmitPackedF16FmaLane(state, inst, false); hi = EmitPackedF16FmaLane(state, inst, true); break; default: lo = ConstantF32(state, 0); hi = ConstantF32(state, 0); break; } lo = ApplyResultModifiersF32(state, lo, inst.dst); hi = ApplyResultModifiersF32(state, hi, inst.dst); const auto pair = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpCompositeConstruct, state.vec2_float_type, pair, lo, hi}); state.builder.AddFunction( {OpExtInst, state.uint_type, ret, state.glsl_std450, GlslPackHalf2x16, pair}); EmitStoreU32(state, inst.dst, ret); } uint32_t EmitPackedInteger16Select(EmitterState& state, uint32_t lhs, uint32_t rhs, bool signed_compare, bool max_value) { const auto cond = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); uint32_t op = 0; if (signed_compare) { op = max_value ? OpSGreaterThanEqual : OpSLessThan; } else { op = max_value ? OpUGreaterThanEqual : OpULessThan; } state.builder.AddFunction({op, state.bool_type, cond, lhs, rhs}); state.builder.AddFunction({OpSelect, state.uint_type, ret, cond, lhs, rhs}); return ret; } uint32_t EmitPackedInteger16Lane(EmitterState& state, const IR::Instruction& inst, bool high_lane) { const auto opcode = inst.op; if (opcode == IR::Opcode::PackedLshlrevB16 || opcode == IR::Opcode::PackedLshrrevB16 || opcode == IR::Opcode::PackedAshrrevI16) { const auto count = EmitAndConstant(state, EmitU16LaneBits(state, inst.src[0], high_lane), 0xfu); const auto arithmetic = opcode == IR::Opcode::PackedAshrrevI16; const auto value = EmitU16LaneBits(state, inst.src[1], high_lane, arithmetic); const auto ret = state.builder.AllocateId(); uint32_t shift_op = OpShiftRightLogical; switch (opcode) { case IR::Opcode::PackedLshlrevB16: shift_op = OpShiftLeftLogical; break; case IR::Opcode::PackedAshrrevI16: shift_op = OpShiftRightArithmetic; break; default: break; } state.builder.AddFunction({shift_op, state.uint_type, ret, value, count}); return ret; } const auto signed_compare = opcode == IR::Opcode::PackedMaxI16 || opcode == IR::Opcode::PackedMinI16; const auto lhs = EmitU16LaneBits(state, inst.src[0], high_lane, signed_compare); const auto rhs = EmitU16LaneBits(state, inst.src[1], high_lane, signed_compare); switch (opcode) { case IR::Opcode::PackedMadI16: case IR::Opcode::PackedMadU16: { const auto addend = EmitU16LaneBits(state, inst.src[2], high_lane, opcode == IR::Opcode::PackedMadI16); const auto mul = state.builder.AllocateId(); const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpIMul, state.uint_type, mul, lhs, rhs}); state.builder.AddFunction({OpIAdd, state.uint_type, ret, mul, addend}); return ret; } case IR::Opcode::PackedMulLoU16: { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpIMul, state.uint_type, ret, lhs, rhs}); return ret; } case IR::Opcode::PackedAddI16: case IR::Opcode::PackedAddU16: { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpIAdd, state.uint_type, ret, lhs, rhs}); return ret; } case IR::Opcode::PackedSubI16: case IR::Opcode::PackedSubU16: { const auto ret = state.builder.AllocateId(); state.builder.AddFunction({OpISub, state.uint_type, ret, lhs, rhs}); return ret; } case IR::Opcode::PackedMaxI16: case IR::Opcode::PackedMaxU16: return EmitPackedInteger16Select(state, lhs, rhs, signed_compare, true); case IR::Opcode::PackedMinI16: case IR::Opcode::PackedMinU16: return EmitPackedInteger16Select(state, lhs, rhs, signed_compare, false); default: break; } return ConstantU32(state, 0); } void EmitPackedInteger16(EmitterState& state, const IR::Instruction& inst) { const auto lo = EmitPackedInteger16Lane(state, inst, false); const auto hi = EmitPackedInteger16Lane(state, inst, true); const auto lo_bits = EmitAndConstant(state, lo, 0xffffu); const auto hi_bits = EmitShiftLeftConstant(state, EmitAndConstant(state, hi, 0xffffu), 16); EmitStoreU32(state, inst.dst, EmitOrU32(state, lo_bits, hi_bits)); } void EmitMadMixF16(EmitterState& state, const IR::Instruction& inst) { const auto src0 = EmitMixF32Load(state, inst.src[0]); const auto src1 = EmitMixF32Load(state, inst.src[1]); const auto src2 = EmitMixF32Load(state, inst.src[2]); const auto fma = state.builder.AllocateId(); const auto pair = state.builder.AllocateId(); const auto packed = state.builder.AllocateId(); state.builder.AddFunction( {OpExtInst, state.float_type, fma, state.glsl_std450, GlslFma, src0, src1, src2}); const auto result = ApplyResultModifiersF32(state, fma, inst.dst); state.builder.AddFunction( {OpCompositeConstruct, state.vec2_float_type, pair, result, ConstantF32(state, 0)}); state.builder.AddFunction( {OpExtInst, state.uint_type, packed, state.glsl_std450, GlslPackHalf2x16, pair}); EmitStoreU32(state, inst.dst, packed); } void EmitMinMax3F32(EmitterState& state, const IR::Instruction& inst, bool max_value) { const auto src0 = EmitFloatLoad(state, inst.src[0]); const auto src1 = EmitFloatLoad(state, inst.src[1]); const auto src2 = EmitFloatLoad(state, inst.src[2]); const auto tmp = EmitMinMaxF32Value(state, src0, src1, max_value); const auto base = EmitMinMaxF32Value(state, tmp, src2, max_value); const auto f32 = ApplyResultModifiersF32(state, base, inst.dst); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.uint_type, u32, f32}); EmitStoreU32(state, inst.dst, u32); } void EmitMed3F32(EmitterState& state, const IR::Instruction& inst) { const auto src0 = EmitFloatLoad(state, inst.src[0]); const auto src1 = EmitFloatLoad(state, inst.src[1]); const auto src2 = EmitFloatLoad(state, inst.src[2]); const auto low_pair = EmitMinMaxF32Value(state, src0, src1, false); const auto min3 = EmitMinMaxF32Value(state, low_pair, src2, false); const auto high_pair = EmitMinMaxF32Value(state, src0, src1, true); const auto high_min = EmitMinMaxF32Value(state, high_pair, src2, false); const auto median = EmitMinMaxF32Value(state, low_pair, high_min, true); const auto nan01 = EmitLogicalOrBool(state, EmitClassifyF32(state, src0).nan, EmitClassifyF32(state, src1).nan); const auto any_nan = EmitLogicalOrBool(state, nan01, EmitClassifyF32(state, src2).nan); const auto base = EmitFSelectValue(state, any_nan, min3, median); const auto f32 = ApplyResultModifiersF32(state, base, inst.dst); const auto u32 = state.builder.AllocateId(); state.builder.AddFunction({OpBitcast, state.uint_type, u32, f32}); EmitStoreU32(state, inst.dst, u32); } } // namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter