refactor(shader): compact paired 64-bit lowering

This commit is contained in:
nmzik
2026-08-19 10:22:31 +02:00
parent 8063822243
commit 6e9f162bd4
19 changed files with 673 additions and 461 deletions
@@ -39,23 +39,22 @@ Pair ExtractPair(EmitterState& state, uint32_t value) {
uint32_t MakePair(EmitterState& state, uint32_t low, uint32_t high) {
const auto result = state.builder.AllocateId();
state.builder.AddFunction({OpCompositeConstruct, TypeU32Pair(state), result, low, high});
state.builder.AddFunction({OpCompositeConstruct, TypeU64(state), result, low, high});
return result;
}
uint32_t Compare64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value,
uint32_t high_compare, uint32_t low_compare, bool equal_compare = false,
bool not_equal = false) {
const auto lhs = ExtractPair(state, lhs_value);
const auto rhs = ExtractPair(state, rhs_value);
const auto high_equal = NewBinary(state, OpIEqual, TypeBool(state), lhs.high, rhs.high);
const auto low_equal = NewBinary(state, OpIEqual, TypeBool(state), lhs.low, rhs.low);
if (equal_compare) {
return NewBinary(
state, not_equal ? OpLogicalOr : OpLogicalAnd, TypeBool(state),
not_equal ? NewUnary(state, OpLogicalNot, TypeBool(state), high_equal) : high_equal,
not_equal ? NewUnary(state, OpLogicalNot, TypeBool(state), low_equal) : low_equal);
}
uint32_t CompareEqual64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value,
bool not_equal) {
const auto compare = NewBinary(state, not_equal ? OpINotEqual : OpIEqual,
TypeBoolVector(state, 2), lhs_value, rhs_value);
return NewUnary(state, not_equal ? OpAny : OpAll, TypeBool(state), compare);
}
uint32_t CompareOrdered64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value,
uint32_t high_compare, uint32_t low_compare) {
const auto lhs = ExtractPair(state, lhs_value);
const auto rhs = ExtractPair(state, rhs_value);
const auto high_equal = NewBinary(state, OpIEqual, TypeBool(state), lhs.high, rhs.high);
const auto high_result = NewBinary(state, high_compare, TypeBool(state), lhs.high, rhs.high);
const auto low_result = NewBinary(state, low_compare, TypeBool(state), lhs.low, rhs.low);
const auto low_path = NewBinary(state, OpLogicalAnd, TypeBool(state), high_equal, low_result);
@@ -63,12 +62,17 @@ uint32_t Compare64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value,
}
uint32_t EmitAdd64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value) {
const auto lhs = ExtractPair(state, lhs_value);
const auto rhs = ExtractPair(state, rhs_value);
const auto carry = EmitAddCarryValues(state, lhs.low, rhs.low, ConstantU32(state, 0));
const auto lhs = ExtractPair(state, lhs_value);
const auto rhs = ExtractPair(state, rhs_value);
const auto low_pair = state.builder.AllocateId();
const auto low = state.builder.AllocateId();
const auto carry = state.builder.AllocateId();
state.builder.AddFunction({OpIAddCarry, TypeU32Pair(state), low_pair, lhs.low, rhs.low});
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), low, low_pair, 0});
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), carry, low_pair, 1});
const auto high0 = NewBinary(state, OpIAdd, TypeU32(state), lhs.high, rhs.high);
const auto high = NewBinary(state, OpIAdd, TypeU32(state), high0, carry.carry);
return MakePair(state, carry.sum, high);
const auto high = NewBinary(state, OpIAdd, TypeU32(state), high0, carry);
return MakePair(state, low, high);
}
uint32_t EmitSub64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value) {
@@ -112,14 +116,6 @@ uint32_t EmitMul64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value)
NewBinary(state, OpIAdd, TypeU32(state), high0, high1), high2));
}
uint32_t EmitBitwise64(EmitterState& state, uint32_t opcode, uint32_t lhs_value,
uint32_t rhs_value) {
const auto lhs = ExtractPair(state, lhs_value);
const auto rhs = ExtractPair(state, rhs_value);
return MakePair(state, NewBinary(state, opcode, TypeU32(state), lhs.low, rhs.low),
NewBinary(state, opcode, TypeU32(state), lhs.high, rhs.high));
}
uint32_t EmitShift64(EmitterState& state, uint32_t opcode, uint32_t value, uint32_t shift) {
const auto pair = ExtractPair(state, value);
uint32_t low = 0;
@@ -129,38 +125,80 @@ uint32_t EmitShift64(EmitterState& state, uint32_t opcode, uint32_t value, uint3
} else if (opcode == OpShiftRightLogical) {
EmitShiftRightLogicalU64Values(state, pair.low, pair.high, shift, low, high);
} else {
const auto logical = EmitShift64(state, OpShiftRightLogical, value, shift);
const auto logical_pair = ExtractPair(state, logical);
const auto sign =
NewBinary(state, OpSLessThan, TypeBool(state), pair.high, ConstantU32(state, 0));
const auto inverse =
NewBinary(state, OpISub, TypeU32(state), ConstantU32(state, 64), shift);
const auto fill = NewBinary(state, OpShiftLeftLogical, TypeU32(state),
ConstantU32(state, 0xffffffffu), inverse);
const auto high_shift =
NewBinary(state, OpISub, TypeU32(state), shift, ConstantU32(state, 32));
const auto high_fill = NewSelect(
state, TypeU32(state),
NewBinary(state, OpUGreaterThanEqual, TypeBool(state), shift, ConstantU32(state, 32)),
ConstantU32(state, 0xffffffffu), fill);
const auto fill_low =
NewSelect(state, TypeU32(state), sign,
NewSelect(state, TypeU32(state),
NewBinary(state, OpUGreaterThanEqual, TypeBool(state), shift,
ConstantU32(state, 32)),
NewBinary(state, OpShiftLeftLogical, TypeU32(state),
ConstantU32(state, 0xffffffffu), high_shift),
ConstantU32(state, 0)),
const auto amount =
NewBinary(state, OpBitwiseAnd, TypeU32(state), shift, ConstantU32(state, 63));
const auto word_shift =
NewBinary(state, OpBitwiseAnd, TypeU32(state), amount, ConstantU32(state, 31));
const auto at_least_32 =
NewBinary(state, OpUGreaterThanEqual, TypeBool(state), amount, ConstantU32(state, 32));
const auto nonzero =
NewBinary(state, OpINotEqual, TypeBool(state), amount, ConstantU32(state, 0));
const auto high_shifted =
NewBinary(state, OpShiftRightArithmetic, TypeU32(state), pair.high, word_shift);
const auto low_shifted =
NewBinary(state, OpShiftRightLogical, TypeU32(state), pair.low, word_shift);
const auto carry_count =
NewBinary(state, OpBitwiseAnd, TypeU32(state),
NewBinary(state, OpISub, TypeU32(state), ConstantU32(state, 32), word_shift),
ConstantU32(state, 31));
const auto carry =
NewSelect(state, TypeU32(state), nonzero,
NewBinary(state, OpShiftLeftLogical, TypeU32(state), pair.high, carry_count),
ConstantU32(state, 0));
const auto fill_high =
NewSelect(state, TypeU32(state), sign, high_fill, ConstantU32(state, 0));
return MakePair(
state, NewBinary(state, OpBitwiseOr, TypeU32(state), logical_pair.low, fill_low),
NewBinary(state, OpBitwiseOr, TypeU32(state), logical_pair.high, fill_high));
const auto low_below_32 = NewBinary(state, OpBitwiseOr, TypeU32(state), low_shifted, carry);
const auto negative =
NewBinary(state, OpSLessThan, TypeBool(state), pair.high, ConstantU32(state, 0));
const auto sign_fill = NewSelect(state, TypeU32(state), negative,
ConstantU32(state, 0xffffffffu), ConstantU32(state, 0));
return MakePair(state,
NewSelect(state, TypeU32(state), at_least_32, high_shifted, low_below_32),
NewSelect(state, TypeU32(state), at_least_32, sign_fill, high_shifted));
}
return MakePair(state, low, high);
}
uint32_t EmitConstantShift64(EmitterState& state, uint32_t opcode, uint32_t value, uint32_t shift) {
shift &= 63u;
if (shift == 0u) {
return value;
}
const auto pair = ExtractPair(state, value);
if (opcode == OpShiftLeftLogical) {
if (shift < 32u) {
const auto low = NewBinary(state, OpShiftLeftLogical, TypeU32(state), pair.low,
ConstantU32(state, shift));
const auto high = NewBinary(state, OpBitwiseOr, TypeU32(state),
NewBinary(state, OpShiftLeftLogical, TypeU32(state),
pair.high, ConstantU32(state, shift)),
NewBinary(state, OpShiftRightLogical, TypeU32(state),
pair.low, ConstantU32(state, 32u - shift)));
return MakePair(state, low, high);
}
return MakePair(state, ConstantU32(state, 0),
shift == 32u ? pair.low
: NewBinary(state, OpShiftLeftLogical, TypeU32(state),
pair.low, ConstantU32(state, shift - 32u)));
}
if (shift < 32u) {
const auto low = NewBinary(state, OpBitwiseOr, TypeU32(state),
NewBinary(state, OpShiftRightLogical, TypeU32(state), pair.low,
ConstantU32(state, shift)),
NewBinary(state, OpShiftLeftLogical, TypeU32(state), pair.high,
ConstantU32(state, 32u - shift)));
const auto high =
NewBinary(state, opcode, TypeU32(state), pair.high, ConstantU32(state, shift));
return MakePair(state, low, high);
}
const auto high = opcode == OpShiftRightArithmetic
? NewBinary(state, OpShiftRightArithmetic, TypeU32(state), pair.high,
ConstantU32(state, 31u))
: ConstantU32(state, 0);
const auto low = shift == 32u ? pair.high
: NewBinary(state, opcode, TypeU32(state), pair.high,
ConstantU32(state, shift - 32u));
return MakePair(state, low, high);
}
uint32_t EmitFindMsb64(EmitterState& state, uint32_t value) {
const auto pair = ExtractPair(state, value);
const auto high_i = state.builder.AllocateId();
@@ -285,8 +323,6 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
case IR::ValueOpcode::BitCastF16U16:
case IR::ValueOpcode::ConvertU32U16:
case IR::ValueOpcode::ConvertU32U8:
case IR::ValueOpcode::PackUint2x32:
case IR::ValueOpcode::UnpackUint2x32: ctx.Define(inst, ctx.Arg(inst, 0)); return true;
case IR::ValueOpcode::BitCastU32F32: return unary(OpBitcast, IR::Type::U32);
case IR::ValueOpcode::BitCastF32U32: return unary(OpBitcast, IR::Type::F32);
case IR::ValueOpcode::ConvertU16U32:
@@ -319,6 +355,10 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
return true;
}
case IR::ValueOpcode::ConvertF32U32: return unary(OpConvertUToF, IR::Type::F32);
case IR::ValueOpcode::CompositeConstructU64:
ctx.Emit(inst, OpCompositeConstruct, IR::Type::U64,
{ctx.Arg(inst, 0), ctx.Arg(inst, 1)});
return true;
case IR::ValueOpcode::CompositeConstructU32x2:
ctx.Emit(inst, OpCompositeConstruct, IR::Type::U32x2,
{ctx.Arg(inst, 0), ctx.Arg(inst, 1)});
@@ -335,6 +375,7 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
ctx.Emit(inst, OpCompositeConstruct, IR::Type::U32x4,
{ctx.Arg(inst, 0), ctx.Arg(inst, 1), ctx.Arg(inst, 2), ctx.Arg(inst, 3)});
return true;
case IR::ValueOpcode::CompositeExtractU64:
case IR::ValueOpcode::CompositeExtractU32x2:
case IR::ValueOpcode::CompositeExtractU32x3:
case IR::ValueOpcode::CompositeExtractU32x4:
@@ -429,15 +470,25 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
case IR::ValueOpcode::ShiftRightArithmetic32:
return binary(OpShiftRightArithmetic, IR::Type::U32);
case IR::ValueOpcode::ShiftLeftLogical64:
ctx.Define(inst,
EmitShift64(state, OpShiftLeftLogical, ctx.Arg(inst, 0), ctx.Arg(inst, 1)));
ctx.Define(inst, inst.Arg(1).Resolve().IsImmediate()
? EmitConstantShift64(state, OpShiftLeftLogical, ctx.Arg(inst, 0),
inst.Arg(1).Resolve().U32())
: EmitShift64(state, OpShiftLeftLogical, ctx.Arg(inst, 0),
ctx.Arg(inst, 1)));
return true;
case IR::ValueOpcode::ShiftRightLogical64:
ctx.Define(inst,
EmitShift64(state, OpShiftRightLogical, ctx.Arg(inst, 0), ctx.Arg(inst, 1)));
ctx.Define(inst, inst.Arg(1).Resolve().IsImmediate()
? EmitConstantShift64(state, OpShiftRightLogical, ctx.Arg(inst, 0),
inst.Arg(1).Resolve().U32())
: EmitShift64(state, OpShiftRightLogical, ctx.Arg(inst, 0),
ctx.Arg(inst, 1)));
return true;
case IR::ValueOpcode::ShiftRightArithmetic64:
ctx.Define(inst, EmitShift64(state, OpShiftRightArithmetic, ctx.Arg(inst, 0),
ctx.Define(inst,
inst.Arg(1).Resolve().IsImmediate()
? EmitConstantShift64(state, OpShiftRightArithmetic, ctx.Arg(inst, 0),
inst.Arg(1).Resolve().U32())
: EmitShift64(state, OpShiftRightArithmetic, ctx.Arg(inst, 0),
ctx.Arg(inst, 1)));
return true;
case IR::ValueOpcode::BitwiseAnd32: return binary(OpBitwiseAnd, IR::Type::U32);
@@ -445,19 +496,15 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
case IR::ValueOpcode::BitwiseXor32: return binary(OpBitwiseXor, IR::Type::U32);
case IR::ValueOpcode::BitwiseNot32: return unary(OpNot, IR::Type::U32);
case IR::ValueOpcode::BitwiseAnd64:
ctx.Define(inst,
EmitBitwise64(state, OpBitwiseAnd, ctx.Arg(inst, 0), ctx.Arg(inst, 1)));
return true;
case IR::ValueOpcode::BitwiseOr64:
ctx.Define(inst, EmitBitwise64(state, OpBitwiseOr, ctx.Arg(inst, 0), ctx.Arg(inst, 1)));
ctx.Define(inst, NewBinary(state, OpBitwiseAnd, TypeU64(state), ctx.Arg(inst, 0),
ctx.Arg(inst, 1)));
return true;
case IR::ValueOpcode::BitReverse32: return unary(OpBitReverse, IR::Type::U32);
case IR::ValueOpcode::BitCount32: return unary(OpBitCount, IR::Type::U32);
case IR::ValueOpcode::BitCount64: {
const auto pair = ExtractPair(state, ctx.Arg(inst, 0));
ctx.Define(inst, NewBinary(state, OpIAdd, TypeU32(state),
NewUnary(state, OpBitCount, TypeU32(state), pair.low),
NewUnary(state, OpBitCount, TypeU32(state), pair.high)));
const auto pair =
ExtractPair(state, NewUnary(state, OpBitCount, TypeU64(state), ctx.Arg(inst, 0)));
ctx.Define(inst, NewBinary(state, OpIAdd, TypeU32(state), pair.low, pair.high));
return true;
}
case IR::ValueOpcode::FindILsb32: {
@@ -527,53 +574,43 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
case IR::ValueOpcode::SGreaterThanEqual32: return binary(OpSGreaterThanEqual, IR::Type::U1);
case IR::ValueOpcode::UGreaterThanEqual32: return binary(OpUGreaterThanEqual, IR::Type::U1);
case IR::ValueOpcode::IEqual64:
ctx.Define(inst,
Compare64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), 0, 0, true, false));
ctx.Define(inst, CompareEqual64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), false));
return true;
case IR::ValueOpcode::INotEqual64:
ctx.Define(inst,
Compare64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), 0, 0, true, true));
ctx.Define(inst, CompareEqual64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), true));
return true;
case IR::ValueOpcode::ULessThan64:
ctx.Define(inst, Compare64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), OpULessThan,
OpULessThan));
ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpULessThan, OpULessThan));
return true;
case IR::ValueOpcode::SLessThan64:
ctx.Define(inst, Compare64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), OpSLessThan,
OpULessThan));
ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpSLessThan, OpULessThan));
return true;
case IR::ValueOpcode::UGreaterThan64:
ctx.Define(inst, Compare64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), OpUGreaterThan,
OpUGreaterThan));
ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpUGreaterThan, OpUGreaterThan));
return true;
case IR::ValueOpcode::SGreaterThan64:
ctx.Define(inst, Compare64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), OpSGreaterThan,
OpUGreaterThan));
ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpSGreaterThan, OpUGreaterThan));
return true;
case IR::ValueOpcode::ULessThanEqual64: {
const auto greater = Compare64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpUGreaterThan, OpUGreaterThan);
ctx.Define(inst, NewUnary(state, OpLogicalNot, TypeBool(state), greater));
case IR::ValueOpcode::ULessThanEqual64:
ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpULessThan, OpULessThanEqual));
return true;
}
case IR::ValueOpcode::SLessThanEqual64: {
const auto greater = Compare64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpSGreaterThan, OpUGreaterThan);
ctx.Define(inst, NewUnary(state, OpLogicalNot, TypeBool(state), greater));
case IR::ValueOpcode::SLessThanEqual64:
ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpSLessThan, OpULessThanEqual));
return true;
}
case IR::ValueOpcode::UGreaterThanEqual64: {
const auto less =
Compare64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), OpULessThan, OpULessThan);
ctx.Define(inst, NewUnary(state, OpLogicalNot, TypeBool(state), less));
case IR::ValueOpcode::UGreaterThanEqual64:
ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpUGreaterThan, OpUGreaterThanEqual));
return true;
}
case IR::ValueOpcode::SGreaterThanEqual64: {
const auto less =
Compare64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), OpSLessThan, OpULessThan);
ctx.Define(inst, NewUnary(state, OpLogicalNot, TypeBool(state), less));
case IR::ValueOpcode::SGreaterThanEqual64:
ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1),
OpSGreaterThan, OpUGreaterThanEqual));
return true;
}
case IR::ValueOpcode::LogicalOr: return binary(OpLogicalOr, IR::Type::U1);
case IR::ValueOpcode::LogicalAnd: return binary(OpLogicalAnd, IR::Type::U1);
case IR::ValueOpcode::LogicalXor: return binary(OpLogicalNotEqual, IR::Type::U1);
@@ -83,25 +83,6 @@ uint32_t EmitAndConstant(EmitterState& state, uint32_t value, uint32_t 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, TypeU32Pair(state), pair0, lhs, rhs});
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), sum0, pair0, 0});
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), carry0, pair0, 1});
state.builder.AddFunction({OpIAddCarry, TypeU32Pair(state), pair1, sum0, carry_in});
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), sum1, pair1, 0});
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), carry1, pair1, 1});
state.builder.AddFunction({OpBitwiseOr, TypeU32(state), carry, carry0, carry1});
return {sum1, carry};
}
uint32_t EmitShiftRightConstant(EmitterState& state, uint32_t value, uint32_t shift) {
const auto ret = state.builder.AllocateId();
state.builder.AddFunction(
@@ -39,13 +39,22 @@ uint32_t EmitWqmWordU32(EmitterState& state, uint32_t value) {
}
uint32_t EmitWqmU64(EmitterState& state, uint32_t value) {
const auto low = state.builder.AllocateId();
const auto high = state.builder.AllocateId();
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), low, value, 0});
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), high, value, 1});
const auto result = state.builder.AllocateId();
state.builder.AddFunction({OpCompositeConstruct, TypeU32Pair(state), result,
EmitWqmWordU32(state, low), EmitWqmWordU32(state, high)});
const auto shifted_one = state.builder.AllocateId();
const auto merged_one = state.builder.AllocateId();
const auto shifted_two = state.builder.AllocateId();
const auto merged_two = state.builder.AllocateId();
const auto quad_bits = state.builder.AllocateId();
const auto result = state.builder.AllocateId();
state.builder.AddFunction({OpShiftRightLogical, TypeU64(state), shifted_one, value,
ConstantU64(state, 0x0000000100000001ull)});
state.builder.AddFunction({OpBitwiseOr, TypeU64(state), merged_one, value, shifted_one});
state.builder.AddFunction({OpShiftRightLogical, TypeU64(state), shifted_two, merged_one,
ConstantU64(state, 0x0000000200000002ull)});
state.builder.AddFunction({OpBitwiseOr, TypeU64(state), merged_two, merged_one, shifted_two});
state.builder.AddFunction({OpBitwiseAnd, TypeU64(state), quad_bits, merged_two,
ConstantU64(state, 0x1111111111111111ull)});
state.builder.AddFunction(
{OpIMul, TypeU64(state), result, quad_bits, ConstantU64(state, 0x0000000f0000000full)});
return result;
}
@@ -182,6 +182,8 @@ enum : uint32_t {
OpIAddCarry = 149,
OpUMulExtended = 151,
OpSMulExtended = 152,
OpAny = 154,
OpAll = 155,
OpLogicalNotEqual = 165,
OpLogicalOr = 166,
OpLogicalAnd = 167,
@@ -337,7 +339,9 @@ struct EmitterState {
uint32_t TypeVoid(EmitterState& state);
uint32_t TypeBool(EmitterState& state);
uint32_t TypeBoolVector(EmitterState& state, uint32_t components);
uint32_t TypeU32(EmitterState& state);
uint32_t TypeU64(EmitterState& state);
uint32_t TypeU32Pair(EmitterState& state);
uint32_t TypeI32(EmitterState& state);
uint32_t TypeI32Pair(EmitterState& state);
@@ -505,11 +509,6 @@ constexpr uint32_t ImageSpirvMultisampled(ImageViewKind view) {
return view == ImageViewKind::Dim2DMsaa || view == ImageViewKind::Dim2DMsaaArray ? 1u : 0u;
}
struct AddCarryResult {
uint32_t sum = 0;
uint32_t carry = 0;
};
struct F32Class {
uint32_t bits = 0;
uint32_t nan = 0;
@@ -730,8 +729,6 @@ uint32_t EmitDsSwizzleTargetLane(EmitterState& state, uint32_t subid, uint32_t c
uint32_t EmitSelectValueU32(EmitterState& state, uint32_t cond, uint32_t true_value,
uint32_t false_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);
@@ -740,9 +737,6 @@ void EmitShiftRightLogicalU64Values(EmitterState& state, uint32_t low, uint32_t
uint32_t EmitAndConstant(EmitterState& state, uint32_t value, uint32_t mask);
AddCarryResult EmitAddCarryValues(EmitterState& state, uint32_t lhs, uint32_t rhs,
uint32_t carry_in);
uint32_t EmitShiftRightConstant(EmitterState& state, uint32_t value, uint32_t shift);
uint32_t EmitOrU32(EmitterState& state, uint32_t lhs, uint32_t rhs);
@@ -16,10 +16,18 @@ uint32_t TypeBool(EmitterState& state) {
return state.builder.Type(OpTypeBool);
}
uint32_t TypeBoolVector(EmitterState& state, uint32_t components) {
return state.builder.Type(OpTypeVector, {TypeBool(state), components});
}
uint32_t TypeU32(EmitterState& state) {
return state.builder.Type(OpTypeInt, {32, 0});
}
uint32_t TypeU64(EmitterState& state) {
return TypeU32Vector(state, 2);
}
uint32_t TypeU32Pair(EmitterState& state) {
const auto element = TypeU32(state);
return state.builder.Type(OpTypeStruct, {element, element});
@@ -241,7 +249,7 @@ uint32_t ConstantBool(EmitterState& state, bool value) {
}
uint32_t ConstantU64(EmitterState& state, uint64_t value) {
return state.builder.Constant(OpConstantComposite, TypeU32Pair(state),
return state.builder.Constant(OpConstantComposite, TypeU64(state),
{ConstantU32(state, static_cast<uint32_t>(value)),
ConstantU32(state, static_cast<uint32_t>(value >> 32u))});
}
@@ -44,7 +44,7 @@ uint32_t SpillPointerType(EmitterState& state, IR::Type type) {
case IR::Type::U32:
case IR::Type::F16: return TypePointer(state, StorageClassFunction, TypeU32(state));
case IR::Type::F32: return TypePointer(state, StorageClassFunction, TypeF32(state));
case IR::Type::U64:
case IR::Type::U64: return TypePointer(state, StorageClassFunction, TypeU64(state));
case IR::Type::U32x2: return TypePointer(state, StorageClassFunction, TypeU32Pair(state));
case IR::Type::U32x3:
return TypePointer(state, StorageClassFunction, TypeU32Vector(state, 3));
@@ -404,7 +404,7 @@ uint32_t ValueEmitContext::TypeId(IR::Type type) const {
case IR::Type::U16:
case IR::Type::U32:
case IR::Type::F16: return TypeU32(state);
case IR::Type::U64:
case IR::Type::U64: return TypeU64(state);
case IR::Type::U32x2: return TypeU32Pair(state);
case IR::Type::F32: return TypeF32(state);
case IR::Type::U32x3: return TypeU32Vector(state, 3);
@@ -27,7 +27,7 @@ bool Translator::TranslateStateOperation(const IR::Instruction& inst) {
const auto result_high =
inst.op == IR::Opcode::SaveexecB64 ? apply(old_high, src_high) : old_high;
if (inst.op == IR::Opcode::SaveexecB64) {
WriteOperand(inst.dst, ir.PackUint2x32(old_low, old_high));
WriteU32Pair(inst.dst, {old_low, old_high});
} else {
WriteOperand(inst.dst, old_low);
}
@@ -157,12 +157,11 @@ bool Translator::TranslateMove(const IR::Instruction& inst) {
WriteOperand(inst.dst, ReadOperand(inst.src[0], IR::Type::F32));
return true;
case IR::Opcode::MoveU64: {
const bool scalar_pair = inst.dst.kind == IR::OperandKind::Register &&
inst.src[0].kind == IR::OperandKind::Register &&
inst.dst.reg.file == IR::RegisterFile::Scalar &&
inst.src[0].reg.file == IR::RegisterFile::Scalar;
WriteOperand(inst.dst, ReadOperand(inst.src[0], IR::Type::U64));
if (scalar_pair) {
WriteU32Pair(inst.dst, ReadU32Pair(inst.src[0]));
if (inst.dst.kind == IR::OperandKind::Register &&
inst.src[0].kind == IR::OperandKind::Register &&
inst.dst.reg.file == IR::RegisterFile::Scalar &&
inst.src[0].reg.file == IR::RegisterFile::Scalar) {
ir.SetThreadBitScalarReg(
static_cast<IR::ScalarReg>(inst.dst.reg.index),
ir.GetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.src[0].reg.index)));
@@ -171,8 +170,8 @@ bool Translator::TranslateMove(const IR::Instruction& inst) {
}
case IR::Opcode::WqmB64: {
if (!current_per_invocation_masks) {
const auto result = IR::U64(ir.Emit(
IR::ValueOpcode::WqmU64, {ReadOperand(inst.src[0], IR::Type::U64)}));
const auto result = IR::U64(
ir.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src[0], IR::Type::U64)}));
WriteOperand(inst.dst, result);
ir.SetScc(IR::U1(
ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
@@ -6,7 +6,8 @@ bool Translator::TranslateU64MaskOperation(const IR::Instruction& inst) {
if (current_per_invocation_masks) {
return TranslatePerInvocationU64Mask(inst);
}
return inst.op == IR::Opcode::BitwiseAndU64 || inst.op == IR::Opcode::BitwiseOrU64
return inst.op == IR::Opcode::BitwiseAndU64 || inst.op == IR::Opcode::BitwiseOrU64 ||
inst.op == IR::Opcode::BitwiseXorU64 || inst.op == IR::Opcode::BitwiseNotU64
? TranslateSimpleInteger(inst)
: TranslateComposedInteger(inst);
}
@@ -157,6 +157,31 @@ bool Translator::TranslatePerInvocationU64Mask(const IR::Instruction& inst) {
}
bool Translator::TranslateSimpleInteger(const IR::Instruction& inst) {
if (inst.op == IR::Opcode::BitwiseAndU64 || inst.op == IR::Opcode::BitwiseOrU64 ||
inst.op == IR::Opcode::BitwiseXorU64 || inst.op == IR::Opcode::BitwiseNotU64) {
const auto lhs = ReadU32Pair(inst.src[0]);
std::array<IR::U32, 2> result;
if (inst.op == IR::Opcode::BitwiseNotU64) {
result = {ir.BitwiseNot(lhs[0]), ir.BitwiseNot(lhs[1])};
} else {
const auto rhs = ReadU32Pair(inst.src[1]);
for (uint32_t component = 0; component < 2; component++) {
switch (inst.op) {
case IR::Opcode::BitwiseAndU64:
result[component] = ir.BitwiseAnd(lhs[component], rhs[component]);
break;
case IR::Opcode::BitwiseOrU64:
result[component] = ir.BitwiseOr(lhs[component], rhs[component]);
break;
default:
result[component] = ir.BitwiseXor(lhs[component], rhs[component]);
break;
}
}
}
WriteU32Pair(inst.dst, result);
return true;
}
IR::ValueOpcode opcode {};
IR::Type type = IR::Type::U32;
switch (inst.op) {
@@ -187,14 +212,6 @@ bool Translator::TranslateSimpleInteger(const IR::Instruction& inst) {
type = IR::Type::U64;
break;
case IR::Opcode::FindLsbU32: opcode = IR::ValueOpcode::FindILsb32; break;
case IR::Opcode::BitwiseAndU64:
opcode = IR::ValueOpcode::BitwiseAnd64;
type = IR::Type::U64;
break;
case IR::Opcode::BitwiseOrU64:
opcode = IR::ValueOpcode::BitwiseOr64;
type = IR::Type::U64;
break;
case IR::Opcode::ShiftLeftLogicalU32: opcode = IR::ValueOpcode::ShiftLeftLogical32; break;
case IR::Opcode::ShiftRightLogicalU32: opcode = IR::ValueOpcode::ShiftRightLogical32; break;
case IR::Opcode::ShiftRightArithmeticI32:
@@ -219,10 +236,6 @@ bool Translator::TranslateSimpleInteger(const IR::Instruction& inst) {
opcode == IR::ValueOpcode::ShiftRightLogical32 ||
opcode == IR::ValueOpcode::ShiftRightArithmetic32)) {
args[index] = ir.BitwiseAnd(IR::U32(args[index]), IR::U32(IR::Value(31u)));
} else if (index == 1u && (opcode == IR::ValueOpcode::ShiftLeftLogical64 ||
opcode == IR::ValueOpcode::ShiftRightLogical64 ||
opcode == IR::ValueOpcode::ShiftRightArithmetic64)) {
args[index] = ir.BitwiseAnd(IR::U32(args[index]), IR::U32(IR::Value(63u)));
}
}
IR::Value result;
@@ -233,32 +246,46 @@ bool Translator::TranslateSimpleInteger(const IR::Instruction& inst) {
default: EXIT("invalid simple integer source count: %u", inst.src_count);
}
WriteOperand(inst.dst, result);
if (type == IR::Type::U64 && inst.dst.kind == IR::OperandKind::Register &&
inst.dst.reg.file == IR::RegisterFile::Scalar) {
IR::U1 mask;
if (inst.op == IR::Opcode::BitwiseAndU64) {
mask = ir.LogicalAnd(ReadMask(inst.src[0]), ReadMask(inst.src[1]));
} else if (inst.op == IR::Opcode::BitwiseOrU64) {
mask = ir.LogicalOr(ReadMask(inst.src[0]), ReadMask(inst.src[1]));
}
if (!mask.IsEmpty()) {
ir.SetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.dst.reg.index), mask);
}
}
return true;
}
bool Translator::TranslateComposedInteger(const IR::Instruction& inst) {
if (inst.op == IR::Opcode::BitwiseAndNotU64 || inst.op == IR::Opcode::BitwiseOrNotU64 ||
inst.op == IR::Opcode::BitwiseNandU64 || inst.op == IR::Opcode::BitwiseNorU64 ||
inst.op == IR::Opcode::BitwiseXnorU64) {
const auto lhs = ReadU32Pair(inst.src[0]);
const auto rhs = ReadU32Pair(inst.src[1]);
std::array<IR::U32, 2> result;
for (uint32_t component = 0; component < 2; component++) {
switch (inst.op) {
case IR::Opcode::BitwiseAndNotU64:
result[component] =
ir.BitwiseAnd(lhs[component], ir.BitwiseNot(rhs[component]));
break;
case IR::Opcode::BitwiseOrNotU64:
result[component] = ir.BitwiseOr(lhs[component], ir.BitwiseNot(rhs[component]));
break;
case IR::Opcode::BitwiseNandU64:
result[component] =
ir.BitwiseNot(ir.BitwiseAnd(lhs[component], rhs[component]));
break;
case IR::Opcode::BitwiseNorU64:
result[component] = ir.BitwiseNot(ir.BitwiseOr(lhs[component], rhs[component]));
break;
default:
result[component] =
ir.BitwiseNot(ir.BitwiseXor(lhs[component], rhs[component]));
break;
}
}
WriteU32Pair(inst.dst, result);
return true;
}
const auto binary_u32 = [&](auto operation) {
const auto lhs = ReadU32(inst.src[0]);
const auto rhs = ReadU32(inst.src[1]);
return operation(lhs, rhs);
};
const auto binary_u64 = [&](auto operation) {
const auto lhs = UnpackU64(ReadU64(inst.src[0]));
const auto rhs = UnpackU64(ReadU64(inst.src[1]));
return ir.PackUint2x32(operation(lhs[0], rhs[0]), operation(lhs[1], rhs[1]));
};
IR::Value result;
switch (inst.op) {
@@ -282,34 +309,6 @@ bool Translator::TranslateComposedInteger(const IR::Instruction& inst) {
result = binary_u32(
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseNot(ir.BitwiseXor(lhs, rhs)); });
break;
case IR::Opcode::BitwiseXorU64:
result = binary_u64([&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseXor(lhs, rhs); });
break;
case IR::Opcode::BitwiseAndNotU64:
result = binary_u64(
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseAnd(lhs, ir.BitwiseNot(rhs)); });
break;
case IR::Opcode::BitwiseOrNotU64:
result = binary_u64(
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseOr(lhs, ir.BitwiseNot(rhs)); });
break;
case IR::Opcode::BitwiseNandU64:
result = binary_u64(
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseNot(ir.BitwiseAnd(lhs, rhs)); });
break;
case IR::Opcode::BitwiseNorU64:
result = binary_u64(
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseNot(ir.BitwiseOr(lhs, rhs)); });
break;
case IR::Opcode::BitwiseXnorU64:
result = binary_u64(
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseNot(ir.BitwiseXor(lhs, rhs)); });
break;
case IR::Opcode::BitwiseNotU64: {
const auto source = UnpackU64(ReadU64(inst.src[0]));
result = ir.PackUint2x32(ir.BitwiseNot(source[0]), ir.BitwiseNot(source[1]));
break;
}
case IR::Opcode::BitwiseAndOrU32: {
const auto lhs = ReadU32(inst.src[0]);
const auto rhs = ReadU32(inst.src[1]);
@@ -326,7 +325,7 @@ bool Translator::TranslateComposedInteger(const IR::Instruction& inst) {
ReadU32(inst.src[2]));
break;
case IR::Opcode::FindLsbU64: {
const auto source = UnpackU64(ReadU64(inst.src[0]));
const auto source = ExtractU64(ReadU64(inst.src[0]));
const auto low_lsb = IR::U32(ir.Emit(IR::ValueOpcode::FindILsb32, {source[0]}));
const auto high_lsb = IR::U32(ir.Emit(IR::ValueOpcode::FindILsb32, {source[1]}));
const auto high_position = ir.IAdd(high_lsb, IR::U32(IR::Value(32u)));
@@ -349,44 +348,13 @@ bool Translator::TranslateComposedInteger(const IR::Instruction& inst) {
const auto position = ir.ISub(IR::U32(IR::Value(63u)), msb);
const auto nonzero = IR::U1(
ir.Emit(IR::ValueOpcode::INotEqual64,
{source, ir.PackUint2x32(IR::U32(IR::Value(0u)), IR::U32(IR::Value(0u)))}));
{source, ir.ConstructU64(IR::U32(IR::Value(0u)), IR::U32(IR::Value(0u)))}));
result = ir.Select(nonzero, position, IR::U32(IR::Value(0xffffffffu)));
break;
}
default: return false;
}
WriteOperand(inst.dst, result);
if (result.GetType() == IR::Type::U64 && inst.dst.kind == IR::OperandKind::Register &&
inst.dst.reg.file == IR::RegisterFile::Scalar) {
const auto lhs = ReadMask(inst.src[0]);
IR::U1 mask;
switch (inst.op) {
case IR::Opcode::BitwiseXorU64:
mask = IR::U1(ir.Emit(IR::ValueOpcode::LogicalXor, {lhs, ReadMask(inst.src[1])}));
break;
case IR::Opcode::BitwiseAndNotU64:
mask = ir.LogicalAnd(lhs, ir.LogicalNot(ReadMask(inst.src[1])));
break;
case IR::Opcode::BitwiseOrNotU64:
mask = ir.LogicalOr(lhs, ir.LogicalNot(ReadMask(inst.src[1])));
break;
case IR::Opcode::BitwiseNandU64:
mask = ir.LogicalNot(ir.LogicalAnd(lhs, ReadMask(inst.src[1])));
break;
case IR::Opcode::BitwiseNorU64:
mask = ir.LogicalNot(ir.LogicalOr(lhs, ReadMask(inst.src[1])));
break;
case IR::Opcode::BitwiseXnorU64:
mask = ir.LogicalNot(
IR::U1(ir.Emit(IR::ValueOpcode::LogicalXor, {lhs, ReadMask(inst.src[1])})));
break;
case IR::Opcode::BitwiseNotU64: mask = ir.LogicalNot(lhs); break;
default: break;
}
if (!mask.IsEmpty()) {
ir.SetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.dst.reg.index), mask);
}
}
return true;
}
@@ -402,18 +370,12 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) {
return IR::U32(
ir.Emit(IR::ValueOpcode::BitFieldInsert, {imm(0), imm(0xffffffffu), imm(0), count}));
};
const auto select_u64 = [&](IR::U1 condition, IR::U64 true_value, IR::U64 false_value) {
const auto lhs = UnpackU64(true_value);
const auto rhs = UnpackU64(false_value);
return ir.PackUint2x32(ir.Select(condition, lhs[0], rhs[0]),
ir.Select(condition, lhs[1], rhs[1]));
};
const auto right_mask64 = [&](IR::U32 count) {
const auto below32 = IR::U1(ir.Emit(IR::ValueOpcode::ULessThan32, {count, imm(32)}));
const auto above32 = IR::U1(ir.Emit(IR::ValueOpcode::UGreaterThan32, {count, imm(32)}));
const auto low_count = ir.Select(below32, count, imm(32));
const auto high_count = ir.Select(above32, ir.ISub(count, imm(32)), imm(0));
return ir.PackUint2x32(right_mask32(low_count), right_mask32(high_count));
return ir.ConstructU64(right_mask32(low_count), right_mask32(high_count));
};
IR::Value result;
@@ -436,7 +398,7 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) {
case IR::Opcode::UMadU64U32: {
const auto lhs = ReadU32(inst.src[0]);
const auto rhs = ReadU32(inst.src[1]);
const auto add = UnpackU64(ReadU64(inst.src[2]));
const auto add = ExtractU64(ReadU64(inst.src[2]));
const auto mul_low = ir.IMul(lhs, rhs);
const auto mul_high = IR::U32(ir.Emit(IR::ValueOpcode::UMulHi, {lhs, rhs}));
const auto low = ir.IAdd(mul_low, add[0]);
@@ -445,7 +407,7 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) {
const auto carry0 = ir.ULessThan(high0, mul_high);
const auto high = ir.IAdd(high0, ir.Select(carry_low, imm(1), imm(0)));
const auto carry1 = ir.ULessThan(high, high0);
WriteOperand(inst.dst, ir.PackUint2x32(low, high));
WriteOperand(inst.dst, ir.ConstructU64(low, high));
if (inst.dst2.kind != IR::OperandKind::Null) {
WriteMask(inst.dst2, ir.LogicalOr(carry0, carry1));
}
@@ -503,7 +465,7 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) {
return ir.BitwiseOr(bits, ir.ShiftLeftLogical(bits, imm(1)));
};
const auto source = ReadU32(inst.src[0]);
result = ir.PackUint2x32(replicate(mask(source, 0xffffu)),
result = ir.ConstructU64(replicate(mask(source, 0xffffu)),
replicate(ir.ShiftRightLogical(source, imm(16))));
break;
}
@@ -590,12 +552,12 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) {
const auto lo = ReadU32(inst.src[1]);
const auto byte_offset = mask(ReadU32(inst.src[2]), 31u);
const auto bit_offset = ir.ShiftLeftLogical(byte_offset, imm(3));
const auto concatenated = ir.PackUint2x32(lo, hi);
const auto concatenated = ir.ConstructU64(lo, hi);
const auto shifted = IR::U64(ir.Emit(IR::ValueOpcode::ShiftRightLogical64,
{concatenated, mask(bit_offset, 63u)}));
const auto in_range =
IR::U1(ir.Emit(IR::ValueOpcode::ULessThan32, {byte_offset, imm(8)}));
result = ir.Select(in_range, UnpackU64(shifted)[0], imm(0));
result = ir.Select(in_range, ExtractU64(shifted)[0], imm(0));
break;
}
case IR::Opcode::ShiftLeftAddU32:
@@ -633,10 +595,14 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) {
ReadOperand(inst.src[2], IR::Type::F32)});
break;
}
case IR::Opcode::SelectU64:
result =
select_u64(ReadCondition(inst.src[0]), ReadU64(inst.src[1]), ReadU64(inst.src[2]));
break;
case IR::Opcode::SelectU64: {
const auto condition = ReadCondition(inst.src[0]);
const auto lhs = ReadU32Pair(inst.src[1]);
const auto rhs = ReadU32Pair(inst.src[2]);
WriteU32Pair(inst.dst, {ir.Select(condition, lhs[0], rhs[0]),
ir.Select(condition, lhs[1], rhs[1])});
return true;
}
case IR::Opcode::PackLowLowU16:
case IR::Opcode::PackLowHighU16:
case IR::Opcode::PackHighHighU16:
@@ -87,10 +87,7 @@ IR::U32 Translator::ReadPcRelativeU32(const IR::Operand& operand) {
const auto address =
ir.Emit(IR::ValueOpcode::IAdd64, {ir.Emit(IR::ValueOpcode::GetShaderBase),
IR::Value(static_cast<uint64_t>(operand.imm))});
const auto pair = ir.Emit(IR::ValueOpcode::UnpackUint2x32, {address});
return IR::U32(
ir.Emit(IR::ValueOpcode::CompositeExtractU32x2,
{pair, IR::Value(operand.kind == IR::OperandKind::PcRelativeU32 ? 0u : 1u)}));
return ir.CompositeExtract(address, operand.kind == IR::OperandKind::PcRelativeU32 ? 0u : 1u);
}
std::array<IR::U32, 2> Translator::BallotMask(IR::U1 value) {
@@ -205,22 +202,8 @@ IR::Value Translator::ReadOperand(const IR::Operand& operand, IR::Type type) {
return ir.INotEqual(ReadRawU32(operand), IR::U32(IR::Value(0u)));
}
if (type == IR::Type::U64) {
if (operand.kind == IR::OperandKind::Register &&
operand.reg.file == IR::RegisterFile::Exec) {
return ir.PackUint2x32(ir.GetExecLo(), ir.GetExecHi());
}
if (operand.kind == IR::OperandKind::Register &&
operand.reg.file == IR::RegisterFile::Vcc) {
return ir.PackUint2x32(ir.GetVccLo(), ir.GetVccHi());
}
const auto low = ApplyBitSourceModifiers(operand, ReadRawU32(operand));
IR::U32 high(IR::Value(0u));
if (operand.kind == IR::OperandKind::Register) {
high = ReadRawU32(OffsetOperand(operand, 1));
} else if (operand.kind == IR::OperandKind::ImmediateU32 && operand.sext_64) {
high = IR::U32(IR::Value(0xffffffffu));
}
return ir.PackUint2x32(low, high);
const auto pair = ReadU32Pair(operand);
return ir.ConstructU64(pair[0], pair[1]);
}
auto bits = ApplyBitSourceModifiers(operand, ReadRawU32(operand));
if (TypesOverlap(type, IR::Type::F32) && !TypesOverlap(type, IR::Type::U32)) {
@@ -396,31 +379,7 @@ void Translator::WriteOperand(const IR::Operand& operand, IR::Value value) {
return;
}
if (type == IR::Type::U64) {
const auto unpacked = ir.Emit(IR::ValueOpcode::UnpackUint2x32, {value});
const auto low = ir.CompositeExtract(unpacked, 0);
const auto high = ir.CompositeExtract(unpacked, 1);
if (operand.kind == IR::OperandKind::Register) {
const auto thread_bit = ThreadBit(low, high);
switch (operand.reg.file) {
case IR::RegisterFile::Exec:
ir.SetExec(thread_bit);
ir.SetExecLo(low);
ir.SetExecHi(high);
return;
case IR::RegisterFile::Vcc:
ir.SetVcc(thread_bit);
ir.SetVccLo(low);
ir.SetVccHi(high);
return;
case IR::RegisterFile::Scalar:
ir.SetThreadBitScalarReg(static_cast<IR::ScalarReg>(operand.reg.index),
thread_bit);
break;
default: break;
}
}
WriteRawU32(operand, low);
WriteRawU32(OffsetOperand(operand, 1), high);
WriteU32Pair(operand, {ir.CompositeExtract(value, 0), ir.CompositeExtract(value, 1)});
return;
}
if (type == IR::Type::F32) {
@@ -474,6 +433,25 @@ IR::U32 Translator::ReadU32(const IR::Operand& operand) {
return IR::U32(ReadOperand(operand, IR::Type::U32));
}
std::array<IR::U32, 2> Translator::ReadU32Pair(const IR::Operand& operand) {
if (operand.kind == IR::OperandKind::Register) {
if (operand.reg.file == IR::RegisterFile::Exec) {
return {ir.GetExecLo(), ir.GetExecHi()};
}
if (operand.reg.file == IR::RegisterFile::Vcc) {
return {ir.GetVccLo(), ir.GetVccHi()};
}
}
const auto low = ApplyBitSourceModifiers(operand, ReadRawU32(operand));
IR::U32 high(IR::Value(0u));
if (operand.kind == IR::OperandKind::Register) {
high = ReadRawU32(OffsetOperand(operand, 1));
} else if (operand.kind == IR::OperandKind::ImmediateU32 && operand.sext_64) {
high = IR::U32(IR::Value(0xffffffffu));
}
return {low, high};
}
IR::U64 Translator::ReadU64(const IR::Operand& operand) {
return IR::U64(ReadOperand(operand, IR::Type::U64));
}
@@ -560,9 +538,35 @@ IR::U32 Translator::ReadF16LaneBits(const IR::Operand& operand, bool high_lane)
return value;
}
std::array<IR::U32, 2> Translator::UnpackU64(IR::U64 value) {
const auto unpacked = ir.Emit(IR::ValueOpcode::UnpackUint2x32, {value});
return {ir.CompositeExtract(unpacked, 0), ir.CompositeExtract(unpacked, 1)};
std::array<IR::U32, 2> Translator::ExtractU64(IR::U64 value) {
return {ir.CompositeExtract(value, 0), ir.CompositeExtract(value, 1)};
}
void Translator::WriteU32Pair(const IR::Operand& operand, const std::array<IR::U32, 2>& value) {
if (operand.kind == IR::OperandKind::Null) {
return;
}
if (operand.kind == IR::OperandKind::Register) {
const auto thread_bit = ThreadBit(value[0], value[1]);
switch (operand.reg.file) {
case IR::RegisterFile::Exec:
ir.SetExec(thread_bit);
ir.SetExecLo(value[0]);
ir.SetExecHi(value[1]);
return;
case IR::RegisterFile::Vcc:
ir.SetVcc(thread_bit);
ir.SetVccLo(value[0]);
ir.SetVccHi(value[1]);
return;
case IR::RegisterFile::Scalar:
ir.SetThreadBitScalarReg(static_cast<IR::ScalarReg>(operand.reg.index), thread_bit);
break;
default: break;
}
}
WriteRawU32(operand, value[0]);
WriteRawU32(OffsetOperand(operand, 1), value[1]);
}
IR::U1 Translator::ThreadBit(IR::U32 low, IR::U32 high) {
@@ -36,6 +36,7 @@ private:
void WriteF16(const IR::Operand& operand, IR::F32 value);
void WriteU16(const IR::Operand& operand, IR::U32 value);
IR::U32 ReadU32(const IR::Operand& operand);
std::array<IR::U32, 2> ReadU32Pair(const IR::Operand& operand);
IR::U64 ReadU64(const IR::Operand& operand);
IR::F32 ReadF16LaneAsF32(const IR::Operand& operand, bool high_lane);
IR::F32 ReadF16AsF32(const IR::Operand& operand);
@@ -44,13 +45,14 @@ private:
IR::U32 ReadU16LaneAsU32(const IR::Operand& operand, bool high_lane, bool sign_extend);
IR::U32 ReadU16AsU32(const IR::Operand& operand, bool sign_extend);
IR::U32 ReadF16LaneBits(const IR::Operand& operand, bool high_lane);
std::array<IR::U32, 2> UnpackU64(IR::U64 value);
IR::U1 ReadCondition(const IR::Operand& operand);
IR::U32 ConditionBit(const IR::Operand& operand);
IR::U1 ReadMask(const IR::Operand& operand);
void WriteMask(const IR::Operand& operand, IR::U1 value);
void WriteMask64(const IR::Operand& operand, IR::U1 value);
void WriteCompareResult(const IR::Operand& operand, IR::U1 value);
std::array<IR::U32, 2> ExtractU64(IR::U64 value);
void WriteU32Pair(const IR::Operand& operand, const std::array<IR::U32, 2>& value);
IR::U1 ReadCondition(const IR::Operand& operand);
IR::U32 ConditionBit(const IR::Operand& operand);
IR::U1 ReadMask(const IR::Operand& operand);
void WriteMask(const IR::Operand& operand, IR::U1 value);
void WriteMask64(const IR::Operand& operand, IR::U1 value);
void WriteCompareResult(const IR::Operand& operand, IR::U1 value);
IR::MemoryFlags AddMemoryInfo(const IR::Instruction& inst);
IR::ExportFlags AddExportInfo(const IR::Instruction& inst);
@@ -82,13 +84,13 @@ private:
IR::ValueOpcode BufferAtomicOpcode(IR::Opcode opcode);
IR::ValueOpcode SharedAtomicOpcode(IR::Opcode opcode);
bool TranslateScalarMemory(const IR::Instruction& inst);
bool TranslateBufferLoad(const IR::Instruction& inst);
bool TranslateBufferStore(const IR::Instruction& inst);
bool TranslateAtomicMemory(const IR::Instruction& inst);
bool TranslateFlatLoad(const IR::Instruction& inst);
bool TranslateFlatStore(const IR::Instruction& inst);
bool TranslateImageMemory(const IR::Instruction& inst);
bool TranslateSharedMemory(const IR::Instruction& inst);
bool TranslateBufferLoad(const IR::Instruction& inst);
bool TranslateBufferStore(const IR::Instruction& inst);
bool TranslateAtomicMemory(const IR::Instruction& inst);
bool TranslateFlatLoad(const IR::Instruction& inst);
bool TranslateFlatStore(const IR::Instruction& inst);
bool TranslateImageMemory(const IR::Instruction& inst);
bool TranslateSharedMemory(const IR::Instruction& inst);
IR::F32 SelectF32(IR::U1 condition, IR::F32 true_value, IR::F32 false_value);
IR::U32 ConvertF32ToU32Saturated(IR::F32 value, float upper_bound, float safe_upper,
@@ -134,14 +134,14 @@ U32 IREmitter::BitCastU32(F16 value) {
return U32(Emit(ValueOpcode::ConvertU32U16, {half}));
}
U64 IREmitter::PackUint2x32(U32 low, U32 high) {
const auto pair = Emit(ValueOpcode::CompositeConstructU32x2, {low, high});
return U64(Emit(ValueOpcode::PackUint2x32, {pair}));
U64 IREmitter::ConstructU64(U32 low, U32 high) {
return U64(Emit(ValueOpcode::CompositeConstructU64, {low, high}));
}
U32 IREmitter::CompositeExtract(Value composite, uint32_t index) {
ValueOpcode opcode;
switch (composite.GetType()) {
case Type::U64: opcode = ValueOpcode::CompositeExtractU64; break;
case Type::U32x2: opcode = ValueOpcode::CompositeExtractU32x2; break;
case Type::U32x3: opcode = ValueOpcode::CompositeExtractU32x3; break;
case Type::U32x4: opcode = ValueOpcode::CompositeExtractU32x4; break;
@@ -54,7 +54,7 @@ public:
U32 BitCastU32(F32 value);
F16 BitCastF16(U32 value);
U32 BitCastU32(F16 value);
U64 PackUint2x32(U32 low, U32 high);
U64 ConstructU64(U32 low, U32 high);
U32 CompositeExtract(Value composite, uint32_t index);
U32 IAdd(U32 lhs, U32 rhs);
@@ -317,9 +317,9 @@ bool ValidateValueProgram(const ValueProgram& program, bool require_ssa, std::st
return Fail(error, fmt::format("{} has an invalid memory-info index",
ValueOpcodeName(inst.GetOpcode())));
}
const auto& memory = program.memory_info[memory_index];
const bool scalar_buffer = inst.GetOpcode() == ValueOpcode::ReadConstBuffer;
const bool scalar_address = memory.kind == ResourceKind::ScalarAddress;
const auto& memory = program.memory_info[memory_index];
const bool scalar_buffer = inst.GetOpcode() == ValueOpcode::ReadConstBuffer;
const bool scalar_address = memory.kind == ResourceKind::ScalarAddress;
if ((scalar_buffer && memory.kind != ResourceKind::ScalarBuffer) ||
(!scalar_buffer && !scalar_address && memory.kind != ResourceKind::Flat &&
memory.kind != ResourceKind::Global && memory.kind != ResourceKind::Scratch)) {
@@ -328,12 +328,13 @@ bool ValidateValueProgram(const ValueProgram& program, bool require_ssa, std::st
}
const bool scalar_memory = scalar_buffer || scalar_address;
const bool valid_group_width =
scalar_memory ? memory.component_count == 1u || memory.component_count == 2u ||
memory.component_count == 4u || memory.component_count == 8u ||
memory.component_count == 16u
: memory.component_count >= 1u && memory.component_count <= 4u;
if (memory.data_bits != 32u || memory.data_dwords != 1u ||
!valid_group_width || memory.component_index >= memory.component_count) {
scalar_memory
? memory.component_count == 1u || memory.component_count == 2u ||
memory.component_count == 4u || memory.component_count == 8u ||
memory.component_count == 16u
: memory.component_count >= 1u && memory.component_count <= 4u;
if (memory.data_bits != 32u || memory.data_dwords != 1u || !valid_group_width ||
memory.component_index >= memory.component_count) {
return Fail(error, fmt::format("{} has inconsistent scalar-memory metadata",
ValueOpcodeName(inst.GetOpcode())));
}
@@ -395,6 +396,7 @@ bool ValidateValueProgram(const ValueProgram& program, bool require_ssa, std::st
}
uint32_t composite_components = 0u;
switch (inst.GetOpcode()) {
case ValueOpcode::CompositeExtractU64: composite_components = 2u; break;
case ValueOpcode::CompositeExtractU32x2: composite_components = 2u; break;
case ValueOpcode::CompositeExtractU32x3: composite_components = 3u; break;
case ValueOpcode::CompositeExtractU32x4: composite_components = 4u; break;
@@ -56,15 +56,15 @@ VALUE_OPCODE(ConvertS32F32, U32, F32)
VALUE_OPCODE(ConvertU32F32, U32, F32)
VALUE_OPCODE(ConvertF32S32, F32, U32)
VALUE_OPCODE(ConvertF32U32, F32, U32)
VALUE_OPCODE(CompositeConstructU64, U64, U32, U32)
VALUE_OPCODE(CompositeConstructU32x2, U32x2, U32, U32)
VALUE_OPCODE(CompositeConstructU32x3, U32x3, U32, U32, U32)
VALUE_OPCODE(CompositeConstructF32x2, F32x2, F32, F32)
VALUE_OPCODE(CompositeConstructU32x4, U32x4, U32, U32, U32, U32)
VALUE_OPCODE(CompositeExtractU64, U32, U64, U32)
VALUE_OPCODE(CompositeExtractU32x2, U32, U32x2, U32)
VALUE_OPCODE(CompositeExtractU32x3, U32, U32x3, U32)
VALUE_OPCODE(CompositeExtractU32x4, U32, U32x4, U32)
VALUE_OPCODE(PackUint2x32, U64, U32x2)
VALUE_OPCODE(UnpackUint2x32, U32x2, U64)
VALUE_OPCODE(PackHalf2x16, U32, F32x2)
VALUE_OPCODE(PackSnorm2x16, U32, F32x2)
VALUE_OPCODE(PackUnorm2x16, U32, F32x2)
@@ -105,7 +105,6 @@ VALUE_OPCODE(ShiftRightArithmetic64, U64, U64, U32)
VALUE_OPCODE(BitwiseAnd32, U32, U32, U32)
VALUE_OPCODE(BitwiseAnd64, U64, U64, U64)
VALUE_OPCODE(BitwiseOr32, U32, U32, U32)
VALUE_OPCODE(BitwiseOr64, U64, U64, U64)
VALUE_OPCODE(BitwiseXor32, U32, U32, U32)
VALUE_OPCODE(BitwiseNot32, U32, U32)
VALUE_OPCODE(BitReverse32, U32, U32)
@@ -170,6 +170,10 @@ bool FoldCompositeExtract(Inst& inst, ValueOpcode construct, size_t components)
}
const auto component = index.U32();
const auto* producer = composite.TryInstruction();
if (IsImmediate(composite, Type::U64)) {
Replace(inst, Value(static_cast<uint32_t>(composite.U64() >> (component * 32u))));
return true;
}
if (producer != nullptr && producer->GetOpcode() == construct) {
Replace(inst, producer->Arg(component));
return true;
@@ -185,14 +189,6 @@ bool FoldCompositeExtract(Inst& inst, ValueOpcode construct, size_t components)
return true;
}
}
if (component < 2u && producer != nullptr &&
producer->GetOpcode() == ValueOpcode::UnpackUint2x32) {
const auto packed = producer->Arg(0).Resolve();
if (IsImmediate(packed, Type::U64)) {
Replace(inst, Value(static_cast<uint32_t>(packed.U64() >> (component * 32u))));
return true;
}
}
return false;
}
@@ -284,6 +280,9 @@ void FoldInstruction(Inst& inst) {
}
return;
}
case ValueOpcode::CompositeExtractU64:
FoldCompositeExtract(inst, ValueOpcode::CompositeConstructU64, 2);
return;
case ValueOpcode::CompositeExtractU32x2:
FoldCompositeExtract(inst, ValueOpcode::CompositeConstructU32x2, 2);
return;
@@ -293,27 +292,12 @@ void FoldInstruction(Inst& inst) {
case ValueOpcode::CompositeExtractU32x4:
FoldCompositeExtract(inst, ValueOpcode::CompositeConstructU32x4, 4);
return;
case ValueOpcode::PackUint2x32: {
const auto value = Arg(inst, 0);
const auto* producer = value.TryInstruction();
if (producer != nullptr && producer->GetOpcode() == ValueOpcode::UnpackUint2x32) {
Replace(inst, producer->Arg(0));
} else if (producer != nullptr &&
producer->GetOpcode() == ValueOpcode::CompositeConstructU32x2) {
const auto low = producer->Arg(0).Resolve();
const auto high = producer->Arg(1).Resolve();
if (IsImmediate(low, Type::U32) && IsImmediate(high, Type::U32)) {
Replace(inst, Value(static_cast<uint64_t>(low.U32()) |
(static_cast<uint64_t>(high.U32()) << 32u)));
}
}
return;
}
case ValueOpcode::UnpackUint2x32: {
const auto value = Arg(inst, 0);
const auto* producer = value.TryInstruction();
if (producer != nullptr && producer->GetOpcode() == ValueOpcode::PackUint2x32) {
Replace(inst, producer->Arg(0));
case ValueOpcode::CompositeConstructU64: {
const auto low = Arg(inst, 0);
const auto high = Arg(inst, 1);
if (IsImmediate(low, Type::U32) && IsImmediate(high, Type::U32)) {
Replace(inst, Value(static_cast<uint64_t>(low.U32()) |
(static_cast<uint64_t>(high.U32()) << 32u)));
}
return;
}
@@ -367,8 +351,8 @@ void FoldInstruction(Inst& inst) {
};
const auto low = expand(static_cast<uint32_t>(value.U64()));
const auto high = expand(static_cast<uint32_t>(value.U64() >> 32u));
Replace(inst, Value(static_cast<uint64_t>(low) |
(static_cast<uint64_t>(high) << 32u)));
Replace(inst,
Value(static_cast<uint64_t>(low) | (static_cast<uint64_t>(high) << 32u)));
}
return;
}
@@ -461,11 +445,6 @@ void FoldInstruction(Inst& inst) {
ReplaceBinaryIdentity(inst, Type::U32, 0u);
}
return;
case ValueOpcode::BitwiseOr64:
if (!FoldU64(inst, [](uint64_t a, uint64_t b) { return a | b; })) {
ReplaceBinaryIdentity(inst, Type::U64, 0u);
}
return;
case ValueOpcode::BitwiseNot32: {
const auto value = Arg(inst, 0);
if (IsImmediate(value, Type::U32)) {
@@ -76,10 +76,10 @@ bool IsDescriptorHandle(ValueOpcode opcode) {
bool IsRuntimeIntegerOp(ValueOpcode op) {
switch (op) {
case ValueOpcode::CompositeConstructU64:
case ValueOpcode::CompositeExtractU64:
case ValueOpcode::CompositeConstructU32x2:
case ValueOpcode::CompositeExtractU32x2:
case ValueOpcode::PackUint2x32:
case ValueOpcode::UnpackUint2x32:
case ValueOpcode::BitFieldInsert:
case ValueOpcode::BitFieldUExtract:
case ValueOpcode::BitFieldSExtract:
@@ -100,7 +100,6 @@ bool IsRuntimeIntegerOp(ValueOpcode op) {
case ValueOpcode::BitwiseAnd32:
case ValueOpcode::BitwiseAnd64:
case ValueOpcode::BitwiseOr32:
case ValueOpcode::BitwiseOr64:
case ValueOpcode::BitwiseXor32:
case ValueOpcode::BitwiseNot32:
case ValueOpcode::SelectU1:
@@ -211,10 +210,10 @@ private:
reason = fmt::format("contains a non-scalar {}", ValueOpcodeName(op));
return finish(false);
}
} else if (op == ValueOpcode::PackUint2x32) {
const auto* pair = inst->NumArgs() == 1 ? inst->Arg(0).ResolveInstruction() : nullptr;
if (pair == nullptr || pair->GetOpcode() != ValueOpcode::CompositeConstructU32x2) {
reason = "contains an unsupported packed runtime value";
} else if (op == ValueOpcode::CompositeExtractU64) {
const auto index = inst->NumArgs() == 2 ? inst->Arg(1).Resolve() : Value {};
if (!index.IsImmediate() || index.GetType() != Type::U32 || index.U32() >= 2u) {
reason = "contains an invalid U64 component index";
return finish(false);
}
} else if (op == ValueOpcode::CompositeExtractU32x2) {
@@ -223,7 +222,6 @@ private:
if (source == nullptr || !index.IsImmediate() || index.GetType() != Type::U32 ||
index.U32() >= 2u ||
(source->GetOpcode() != ValueOpcode::CompositeConstructU32x2 &&
source->GetOpcode() != ValueOpcode::UnpackUint2x32 &&
source->GetOpcode() != ValueOpcode::IAddCarry32)) {
reason = "contains an unsupported composite runtime source";
return finish(false);
@@ -484,22 +482,25 @@ private:
if (!index.IsImmediate() || index.GetType() != Type::U32) {
return Fail(error, "dynamic composite extract in runtime expression");
}
const auto component = index.U32();
const auto* source = inst.Arg(0).ResolveInstruction();
if (source == nullptr || component >= 2u) {
const auto component = index.U32();
if (component >= 2u) {
return Fail(error, "unsupported composite runtime source");
}
if (source->GetOpcode() == ValueOpcode::CompositeConstructU32x2) {
return EvaluateWide(source->Arg(component), result, error);
}
if (source->GetOpcode() == ValueOpcode::UnpackUint2x32) {
if (inst.GetOpcode() == ValueOpcode::CompositeExtractU64) {
uint64_t packed = 0;
if (!Arg(*source, 0, packed, error)) {
if (!Arg(inst, 0, packed, error)) {
return false;
}
result = static_cast<uint32_t>(packed >> (component * 32u));
return true;
}
const auto* source = inst.Arg(0).ResolveInstruction();
if (source == nullptr) {
return Fail(error, "unsupported composite runtime source");
}
if (source->GetOpcode() == ValueOpcode::CompositeConstructU32x2) {
return EvaluateWide(source->Arg(component), result, error);
}
if (source->GetOpcode() == ValueOpcode::IAddCarry32) {
uint64_t lhs = 0;
uint64_t rhs = 0;
@@ -596,17 +597,15 @@ private:
}
case ValueOpcode::GetShaderBase: result = m_runtime.shader_base; return true;
case ValueOpcode::Phi: return EvaluatePhi(inst, result, error);
case ValueOpcode::CompositeExtractU64:
case ValueOpcode::CompositeExtractU32x2: return EvaluateExtract(inst, result, error);
case ValueOpcode::PackUint2x32: {
const auto* pair = inst.Arg(0).ResolveInstruction();
if (pair == nullptr || pair->GetOpcode() != ValueOpcode::CompositeConstructU32x2 ||
!Arg(*pair, 0, a, error) || !Arg(*pair, 1, b, error)) {
return Fail(error, "unsupported packed runtime value");
case ValueOpcode::CompositeConstructU64:
if (!binary()) {
return false;
}
result = static_cast<uint32_t>(a) |
(static_cast<uint64_t>(static_cast<uint32_t>(b)) << 32u);
return true;
}
case ValueOpcode::ReadConst: {
const auto slot = inst.Arg(1).Resolve();
if (!slot.IsImmediate() || slot.GetType() != Type::U32 ||
@@ -687,12 +686,6 @@ private:
return true;
}
return false;
case ValueOpcode::BitwiseOr64:
if (binary()) {
result = a | b;
return true;
}
return false;
case ValueOpcode::BitwiseXor32:
if (binary()) {
result = static_cast<uint32_t>(a ^ b);