shader: compute add-with-carry with compares and remove IAddCarry32

This commit is contained in:
nmzik
2026-10-21 22:28:11 +02:00
parent c571d8ed5a
commit 6e5167598e
8 changed files with 17 additions and 50 deletions
@@ -62,7 +62,6 @@ EMIT_NATIVE(IMul32, OpIMul, U32, uint32_t, uint32_t)
EMIT_NATIVE(UDiv32, OpUDiv, U32, uint32_t, uint32_t)
EMIT_NATIVE(IAdd64, OpIAdd, U64, uint32_t, uint32_t)
EMIT_NATIVE(ISub64, OpISub, U64, uint32_t, uint32_t)
EMIT_NATIVE(IAddCarry32, OpIAddCarry, U32x2, uint32_t, uint32_t)
uint32_t EmitSMulHi(EmitterState& state, uint32_t arg0, uint32_t arg1);
uint32_t EmitUMulHi(EmitterState& state, uint32_t arg0, uint32_t arg1);
inline constexpr auto EmitIAbs32 = EmitGlsl<GLSLstd450SAbs, IR::Type::U32, uint32_t>;
@@ -82,19 +82,17 @@ void Translator::S_SAVEEXEC(const Decoder::Instruction& inst, IR::ValueOpcode op
}
void Translator::ADD_U32(const Decoder::Instruction& inst, bool vector, bool use_carry_in) {
const auto zero = IR::U32(IR::Value(0u));
const auto lhs = ReadU32(inst.src0);
const auto rhs = ReadU32(inst.src1);
const auto carry_in = !use_carry_in ? zero
: vector ? ConditionBit(inst.src2)
: ConditionBit(ConditionOperand(Decoder::OperandKind::Scc));
const auto add0 = ir.Emit(IR::ValueOpcode::IAddCarry32, {lhs, rhs});
const auto partial = ir.CompositeExtract(add0, 0);
const auto carry0 = ir.CompositeExtract(add0, 1);
const auto add1 = ir.Emit(IR::ValueOpcode::IAddCarry32, {partial, carry_in});
const auto result = ir.CompositeExtract(add1, 0);
const auto carry1 = ir.CompositeExtract(add1, 1);
const auto carry = ir.INotEqual(ir.BitwiseOr(carry0, carry1), zero);
const auto lhs = ReadU32(inst.src0);
const auto rhs = ReadU32(inst.src1);
const auto partial = ir.IAdd(lhs, rhs);
auto result = partial;
auto carry = ir.ULessThan(partial, lhs);
if (use_carry_in) {
// The carry-in is 0 or 1, so the second add carries out only when it wraps to zero.
result = ir.IAdd(partial, vector ? ConditionBit(inst.src2)
: ConditionBit(ConditionOperand(Decoder::OperandKind::Scc)));
carry = ir.LogicalOr(carry, ir.ULessThan(result, partial));
}
WriteOperand(DestinationOperand(inst), result);
if (vector) {
WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), carry));
@@ -97,7 +97,6 @@ VALUE_OPCODE(SelectU1, U1, U1, U1, U1)
VALUE_OPCODE(SelectF32, F32, U1, F32, F32)
VALUE_OPCODE(IAdd32, U32, U32, U32)
VALUE_OPCODE(IAdd64, U64, U64, U64)
VALUE_OPCODE(IAddCarry32, U32x2, U32, U32)
VALUE_OPCODE(ISub32, U32, U32, U32)
VALUE_OPCODE(ISub64, U64, U64, U64)
VALUE_OPCODE(IMul32, U32, U32, U32)
@@ -244,17 +244,6 @@ bool FoldCompositeExtract(Inst& inst, ValueOpcode construct, size_t components)
Replace(inst, producer->Arg(component));
return true;
}
if (component < 2u && producer != nullptr &&
producer->GetOpcode() == ValueOpcode::IAddCarry32) {
const auto lhs = producer->Arg(0).Resolve();
const auto rhs = producer->Arg(1).Resolve();
if (IsImmediate(lhs, Type::U32) && IsImmediate(rhs, Type::U32)) {
const auto sum = static_cast<uint64_t>(lhs.U32()) + rhs.U32();
Replace(inst, Value(component == 0u ? static_cast<uint32_t>(sum)
: static_cast<uint32_t>(sum >> 32u)));
return true;
}
}
return false;
}
@@ -134,7 +134,6 @@ bool IsRuntimeUniformOp(ValueOpcode op) {
case ValueOpcode::BitFieldSExtract:
case ValueOpcode::IAdd32:
case ValueOpcode::IAdd64:
case ValueOpcode::IAddCarry32:
case ValueOpcode::ISub32:
case ValueOpcode::ISub64:
case ValueOpcode::IMul32:
@@ -338,9 +337,7 @@ private:
const auto* source = inst->NumArgs() == 2 ? inst->Arg(0).ResolveInstruction() : nullptr;
const auto index = inst->NumArgs() == 2 ? inst->Arg(1).Resolve() : Value {};
if (source == nullptr || !index.IsImmediate() || index.GetType() != Type::U32 ||
index.U32() >= 2u ||
(source->GetOpcode() != ValueOpcode::CompositeConstructU32x2 &&
source->GetOpcode() != ValueOpcode::IAddCarry32)) {
index.U32() >= 2u || source->GetOpcode() != ValueOpcode::CompositeConstructU32x2) {
return finish(false);
}
}
@@ -653,18 +650,6 @@ bool SrtWalker::EvaluateExtract(const Inst& inst, uint64_t& result) {
if (source->GetOpcode() == ValueOpcode::CompositeConstructU32x2) {
return EvaluateWide(source->Arg(component), result);
}
if (source->GetOpcode() == ValueOpcode::IAddCarry32) {
uint64_t lhs = 0;
uint64_t rhs = 0;
if (!Arg(*source, 0, lhs) || !Arg(*source, 1, rhs)) {
return false;
}
const auto sum =
static_cast<uint64_t>(static_cast<uint32_t>(lhs)) + static_cast<uint32_t>(rhs);
result =
component == 0u ? static_cast<uint32_t>(sum) : static_cast<uint32_t>(sum >> 32u);
return true;
}
return false;
}
+3 -6
View File
@@ -237,12 +237,9 @@ void TestShaderBaseAndUserData() {
void TestCarryAndBitFields() {
Fixture fixture;
const auto carry =
fixture.Emit(ValueOpcode::IAddCarry32, {Value(0xffffffffu), Value(2u)});
const auto low =
fixture.Emit(ValueOpcode::CompositeExtractU32x2, {carry, Value(0u)});
const auto high =
fixture.Emit(ValueOpcode::CompositeExtractU32x2, {carry, Value(1u)});
const auto low = fixture.Emit(ValueOpcode::IAdd32, {Value(0xffffffffu), Value(2u)});
const auto carry = fixture.Emit(ValueOpcode::ULessThan32, {low, Value(0xffffffffu)});
const auto high = fixture.Emit(ValueOpcode::SelectU32, {carry, Value(1u), Value(0u)});
const auto inserted =
fixture.Emit(ValueOpcode::BitFieldInsert,
{Value(0u), Value(0x89abcdefu), Value(0u), Value(32u)});
+1 -1
View File
@@ -25806,7 +25806,7 @@ TestCase VectorAddcWritesPerLaneCarryOut() {
O::S_ENDPGM};
test.decoded_counts = {
{"V_ADDC_U32 v2, vcc_lo, 0, v26.sdwa(sel=4,sext=1), vcc_lo", 1}};
test.ir_counts = {{" = BitFieldSExtract ", 1}, {" = IAddCarry32 ", 2}};
test.ir_counts = {{" = BitFieldSExtract ", 1}, {" = ULessThan32 ", 3}};
test.required_spirv = {"OpBitFieldSExtract"};
test.compute_info.threads_num[0] = 4;
test.compute_info.threads_num[1] = 1;
+1 -1
View File
@@ -15144,7 +15144,7 @@ void TestTypedDescriptorRealCarryAndScalarLoads() {
ValueDependsOn(carry_source->dwords[1],
ShaderRecompiler::IR::ValueOpcode::GetShaderBase) &&
ValueDependsOn(carry_source->dwords[1],
ShaderRecompiler::IR::ValueOpcode::IAddCarry32),
ShaderRecompiler::IR::ValueOpcode::ULessThan32),
"real s_add_u32/s_addc_u32 translation lost SCC carry provenance");
std::array<uint32_t, 64> carry_user_data{};
const uint64_t shader_base = 0x00000012fffffffbull;