Repository navigation
Expand file tree
/
Copy pathFloat64Instructions.cpp
More file actions
227 lines (215 loc) · 15.6 KB
/
Copy pathFloat64Instructions.cpp
File metadata and controls
227 lines (215 loc) · 15.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#include "Translation/TranslationContext.hpp"
#include <array>
#include <cstdint>
namespace ShaderRecompiler {
IrU64 TranslationContext::readF64(const RdnaOperand& operand) {
const std::array<IrU32, 2> bits = readF64Bits(operand);
return IrU64(ir.ConstructU64(bits[0].Value(), bits[1].Value()));
}
bool TranslationContext::float64Operation(const RdnaInstruction& inst, IrOpcode opcode) {
const bool arithmetic = opcode == IrOpcode::FPAdd64 || opcode == IrOpcode::FPMul64;
const std::size_t count = IrOpcodeOperandCount(opcode);
std::array<IrValue*, 3> args{};
for (std::uint32_t index = 0u; index < count; ++index) {
const RdnaOperand& operand = sourceAt(inst, index);
switch (IrOpcodeArgumentType(opcode, index)) {
case IrType::U64: {
const std::array<IrU32, 2> bits = opcode == IrOpcode::FPFrexpExp64 || arithmetic ? flushF64Bits(readF64Bits(operand), 0x40u) : readF64Bits(operand);
args[index] = &ir.ConstructU64(bits[0].Value(), bits[1].Value());
break;
}
case IrType::F32: args[index] = readOperand(operand, IrType::F32); break;
default: args[index] = &readU32(operand).Value(); break;
}
}
const IrType type = IrOpcodeType(opcode);
IrValue* result = count == 1u ? &ir.Emit(opcode, type, {args[0]}) : count == 2u ? &ir.Emit(opcode, type, {args[0], args[1]}) : &ir.Emit(opcode, type, {args[0], args[1], args[2]});
if (arithmetic) result = &flushTinyF64Result(opcode, *args[0], *args[1], *result);
if (type == IrType::U64) {
writeF64Result(inst.destination, *result);
return true;
}
rejectHalfOrDoubleOutputModifier(inst.destination);
RdnaOperand destination = inst.destination;
destination.omod = 0u;
writeOperand(destination, result);
return true;
}
IrValue& TranslationContext::flushTinyF64Result(IrOpcode opcode, IrValue& lhs, IrValue& rhs, IrValue& result) {
if (!floatMode.has_value() || (floatMode->floatMode & 0x80u) != 0u) return result;
const bool product = opcode == IrOpcode::FPMul64;
IrValue& tested = product ? ir.Emit(IrOpcode::FPMul64, IrType::U64, {&ir.Emit(IrOpcode::FPMul64, IrType::U64, {&lhs, &ir.ConstantU64(0x43f0000000000000ull)}), &rhs}) : result;
IrValue& magnitude = ir.Emit(IrOpcode::BitwiseAnd64, IrType::U64, {&tested, &ir.ConstantU64(0x7fffffffffffffffull)});
IrValue& below = ir.Emit(IrOpcode::ISub64, IrType::U64, {&magnitude, &ir.ConstantU64(1u)});
IrValue& tiny = ir.Emit(IrOpcode::ULessThan64, IrType::U1, {&below, &ir.ConstantU64(product ? 0x040fffffffffffffull : 0x000fffffffffffffull)});
IrValue& sign = ir.BitwiseAnd(ir.CompositeExtract(result, 1u), ir.Constant(0x80000000u));
return ir.ConstructU64(ir.Select(tiny, ir.Constant(0u), ir.CompositeExtract(result, 0u)), ir.Select(tiny, sign, ir.CompositeExtract(result, 1u)));
}
bool TranslationContext::minMaxF64(const RdnaInstruction& inst, IrOpcode opcode) {
std::array<std::array<IrU32, 2>, 2> bits{readF64Bits(sourceAt(inst, 0u)), readF64Bits(sourceAt(inst, 1u))};
if (floatMode.has_value() && (floatMode->floatMode & 0x40u) == 0u) {
for (auto& value : bits) {
const IrU1 zeroExponent(ir.IEqual(ir.BitwiseAnd(value[1].Value(), ir.Constant(0x7ff00000u)), ir.Constant(0u)));
value[0] = IrU32(ir.Select(zeroExponent.Value(), ir.Constant(0u), value[0].Value()));
value[1] = IrU32(ir.Select(zeroExponent.Value(), ir.BitwiseAnd(value[1].Value(), ir.Constant(0x80000000u)), value[1].Value()));
}
}
const auto isNan = [&](const std::array<IrU32, 2>& value) {
IrValue& magnitude = ir.BitwiseAnd(value[1].Value(), ir.Constant(0x7fffffffu));
return &ir.LogicalOr(ir.UGreaterThan(magnitude, ir.Constant(0x7ff00000u)), ir.LogicalAnd(ir.IEqual(magnitude, ir.Constant(0x7ff00000u)), ir.INotEqual(value[0].Value(), ir.Constant(0u))));
};
IrValue& plain = ir.Emit(opcode, IrType::U64, {&ir.ConstructU64(bits[0][0].Value(), bits[0][1].Value()), &ir.ConstructU64(bits[1][0].Value(), bits[1][1].Value())});
if (ieeeMode) {
writeF64Result(inst.destination, plain);
return true;
}
IrValue* lhsNan = isNan(bits[0]);
IrValue* rhsNan = isNan(bits[1]);
std::array<IrValue*, 2> words{};
for (std::uint32_t index = 0u; index < 2u; ++index) {
IrValue& lhsChecked = ir.Select(*lhsNan, bits[1][index].Value(), ir.CompositeExtract(plain, index));
words[index] = &ir.Select(*rhsNan, bits[0][index].Value(), lhsChecked);
}
writeF64Result(inst.destination, ir.ConstructU64(*words[0], *words[1]));
return true;
}
bool TranslationContext::float64Unary(const RdnaInstruction& inst, IrOpcode opcode) {
const bool flushesInput = opcode == IrOpcode::FPFloor64 || opcode == IrOpcode::FPCeil64 || opcode == IrOpcode::FPFract64 || opcode == IrOpcode::FPFrexpMant64;
const auto bits = flushesInput ? flushF64Bits(readF64Bits(sourceAt(inst, 0u)), 0x40u) : readF64Bits(sourceAt(inst, 0u));
IrValue& argument = ir.ConstructU64(bits[0].Value(), bits[1].Value());
IrValue& result = opcode == IrOpcode::FPLdexp64 ? ir.Emit(opcode, IrType::U64, {&argument, &readU32(sourceAt(inst, 1u)).Value()}) : ir.Emit(opcode, IrType::U64, {&argument});
std::array<IrU32, 2> words{IrU32(ir.CompositeExtract(result, 0u)), IrU32(ir.CompositeExtract(result, 1u))};
if (opcode == IrOpcode::FPFract64) words = flushF64Bits(words, 0x80u);
const IrU32 magnitude(ir.BitwiseAnd(bits[1].Value(), ir.Constant(0x7fffffffu)));
const IrU1 nan(ir.LogicalOr(ir.UGreaterThan(magnitude.Value(), ir.Constant(0x7ff00000u)),
ir.LogicalAnd(ir.IEqual(magnitude.Value(), ir.Constant(0x7ff00000u)), ir.INotEqual(bits[0].Value(), ir.Constant(0u)))));
const auto quiet = quietNan64(bits);
writeF64Result(inst.destination, ir.ConstructU64(ir.Select(nan.Value(), quiet[0].Value(), words[0].Value()), ir.Select(nan.Value(), quiet[1].Value(), words[1].Value())));
return true;
}
bool TranslationContext::vCvtF64F32(const RdnaInstruction& inst) {
IrValue* value = readOperand(sourceAt(inst, 0u), IrType::F32);
IrValue& result = ir.Emit(IrOpcode::ConvertF64F32, IrType::U64, {value});
const IrU32 bits(ir.BitCastU32(*value));
const IrU1 nan(ir.UGreaterThan(ir.BitwiseAnd(bits.Value(), ir.Constant(0x7fffffffu)), ir.Constant(0x7f800000u)));
const IrU32 mantissa(ir.BitwiseAnd(bits.Value(), ir.Constant(0x007fffffu)));
const IrU32 high(ir.BitwiseOr(ir.BitwiseOr(ir.BitwiseAnd(bits.Value(), ir.Constant(0x80000000u)), ir.Constant(0x7ff00000u)), ir.ShiftRightLogical(mantissa.Value(), ir.Constant(3u))));
const auto quiet = quietNan64({IrU32(ir.ShiftLeftLogical(mantissa.Value(), ir.Constant(29u))), high});
writeF64Result(inst.destination, ir.ConstructU64(ir.Select(nan.Value(), quiet[0].Value(), ir.CompositeExtract(result, 0u)), ir.Select(nan.Value(), quiet[1].Value(), ir.CompositeExtract(result, 1u))));
return true;
}
void TranslationContext::writeF64Result(const RdnaOperand& operand, IrValue& value) {
rejectHalfOrDoubleOutputModifier(operand);
RdnaOperand destination = operand;
destination.omod = 0u;
IrValue* result = &value;
if (destination.clamp) {
if (!dx10Clamp()) throw std::runtime_error("clamp on an f64 result with DX10_CLAMP=0 is not implemented");
result = &ir.Emit(IrOpcode::FPSaturate64, IrType::U64, {result});
}
writeOperand(destination, result);
}
bool TranslationContext::vDivScaleF64(const RdnaInstruction& inst) {
const auto value = readF64Bits(sourceAt(inst, 0u));
const auto denominator = readF64Bits(sourceAt(inst, 1u));
const auto numerator = readF64Bits(sourceAt(inst, 2u));
const auto exponent = [&](IrU32 high) {
return IrU32(ir.Emit(IrOpcode::BitFieldUExtract, IrType::U32, {&high.Value(), &ir.Constant(20u), &ir.Constant(11u)}));
};
const IrU32 denominatorExponent = exponent(denominator[1]);
const IrU32 numeratorExponent = exponent(numerator[1]);
const IrU32 difference(ir.ISub(numeratorExponent.Value(), denominatorExponent.Value()));
const IrU1 nearMax(ir.Emit(IrOpcode::SGreaterThanEqual32, IrType::U1, {&difference.Value(), &ir.Constant(768u)}));
const IrU1 nearMin(ir.Emit(IrOpcode::SLessThanEqual32, IrType::U1, {&difference.Value(), &ir.Constant(static_cast<std::uint32_t>(-768))}));
const IrU1 denominatorDenormal(ir.IEqual(denominatorExponent.Value(), ir.Constant(0u)));
const IrU1 denominatorHuge(ir.Emit(IrOpcode::UGreaterThanEqual32, IrType::U1, {&denominatorExponent.Value(), &ir.Constant(2045u)}));
const IrU1 numeratorTiny(ir.Emit(IrOpcode::ULessThanEqual32, IrType::U1, {&numeratorExponent.Value(), &ir.Constant(53u)}));
const IrU1 isDenominator(ir.LogicalAnd(ir.IEqual(value[0].Value(), denominator[0].Value()), ir.IEqual(value[1].Value(), denominator[1].Value())));
const IrU1 notNearMax(ir.LogicalNot(nearMax.Value()));
const IrU1 notDenormal(ir.LogicalNot(denominatorDenormal.Value()));
const IrU1 normalRange(ir.LogicalAnd(notNearMax.Value(), notDenormal.Value()));
const IrU1 belowHuge(ir.LogicalAnd(normalRange.Value(), ir.LogicalNot(denominatorHuge.Value())));
const IrU1 scaleUp(ir.LogicalOr(
ir.LogicalOr(ir.LogicalAnd(nearMax.Value(), isDenominator.Value()), ir.LogicalAnd(notNearMax.Value(), denominatorDenormal.Value())),
ir.LogicalAnd(belowHuge.Value(), ir.LogicalOr(ir.LogicalAnd(nearMin.Value(), ir.LogicalNot(isDenominator.Value())),
ir.LogicalAnd(ir.LogicalNot(nearMin.Value()), numeratorTiny.Value())))));
const IrU1 scaleDown(ir.LogicalAnd(ir.LogicalAnd(normalRange.Value(), denominatorHuge.Value()),
ir.LogicalOr(ir.LogicalNot(nearMin.Value()), isDenominator.Value())));
IrValue& bits = ir.ConstructU64(value[0].Value(), value[1].Value());
IrValue& power = ir.Select(scaleUp.Value(), ir.Constant(128u), ir.Constant(static_cast<std::uint32_t>(-128)));
IrValue& scaled = ir.Emit(IrOpcode::FPLdexp64, IrType::U64, {&bits, &power});
const auto magnitude = [&](IrU32 high) { return IrU32(ir.BitwiseAnd(high.Value(), ir.Constant(0x7fffffffu))); };
const IrU1 valueNan(ir.LogicalOr(ir.UGreaterThan(magnitude(value[1]).Value(), ir.Constant(0x7ff00000u)),
ir.LogicalAnd(ir.IEqual(magnitude(value[1]).Value(), ir.Constant(0x7ff00000u)), ir.INotEqual(value[0].Value(), ir.Constant(0u)))));
const IrU1 rescale(ir.LogicalAnd(ir.LogicalOr(scaleUp.Value(), scaleDown.Value()), ir.LogicalNot(valueNan.Value())));
const auto isZero = [&](const std::array<IrU32, 2>& word) {
return IrU1(ir.IEqual(ir.BitwiseOr(magnitude(word[1]).Value(), word[0].Value()), ir.Constant(0u)));
};
const IrU1 zero(ir.LogicalOr(isZero(denominator).Value(), isZero(numerator).Value()));
std::array<IrU32, 2> result{IrU32(ir.CompositeExtract(scaled, 0u)), IrU32(ir.CompositeExtract(scaled, 1u))};
result[0] = IrU32(ir.Select(rescale.Value(), result[0].Value(), value[0].Value()));
result[1] = IrU32(ir.Select(rescale.Value(), result[1].Value(), value[1].Value()));
result[0] = IrU32(ir.Select(zero.Value(), ir.Constant(0u), result[0].Value()));
result[1] = IrU32(ir.Select(zero.Value(), ir.Constant(0xfff80000u), result[1].Value()));
writeF64Result(inst.destination, ir.ConstructU64(result[0].Value(), result[1].Value()));
writeMask(inst.destination2, IrU1(ir.LogicalOr(nearMax.Value(), ir.LogicalAnd(notDenormal.Value(), nearMin.Value()))));
return true;
}
bool TranslationContext::vDivFmasF64(const RdnaInstruction& inst) {
RdnaOperand vcc{};
vcc.kind = RdnaOperandKind::VccLo;
const IrU1 scale = readMask(vcc);
const auto addend = readF64Bits(sourceAt(inst, 2u));
IrValue& addendExponent = ir.Emit(IrOpcode::BitFieldUExtract, IrType::U32, {&addend[1].Value(), &ir.Constant(20u), &ir.Constant(11u)});
const IrU1 up(ir.Emit(IrOpcode::UGreaterThanEqual32, IrType::U1, {&addendExponent, &ir.Constant(1024u)}));
IrValue& power = ir.Select(scale.Value(), ir.Select(up.Value(), ir.Constant(128u), ir.Constant(static_cast<std::uint32_t>(-128))), ir.Constant(0u));
IrValue& result = ir.Emit(IrOpcode::FPFmaScale64, IrType::U64,
{&readF64(sourceAt(inst, 0u)).Value(), &readF64(sourceAt(inst, 1u)).Value(), &ir.ConstructU64(addend[0].Value(), addend[1].Value()), &power});
writeF64Result(inst.destination, result);
return true;
}
bool TranslationContext::vDivFixupF64(const RdnaInstruction& inst) {
const auto flush = [&](const std::array<IrU32, 2>& word) {
if (!floatMode.has_value() || (floatMode->floatMode & 0x40u) != 0u) return word;
const IrU1 tiny(ir.IEqual(ir.BitwiseAnd(word[1].Value(), ir.Constant(0x7ff00000u)), ir.Constant(0u)));
return std::array<IrU32, 2>{IrU32(ir.Select(tiny.Value(), ir.Constant(0u), word[0].Value())), IrU32(ir.Select(tiny.Value(), ir.BitwiseAnd(word[1].Value(), ir.Constant(0x80000000u)), word[1].Value()))};
};
const auto quotient = readF64Bits(sourceAt(inst, 0u));
const auto denominator = flush(readF64Bits(sourceAt(inst, 1u)));
const auto numerator = flush(readF64Bits(sourceAt(inst, 2u)));
const auto magnitude = [&](IrU32 high) { return IrU32(ir.BitwiseAnd(high.Value(), ir.Constant(0x7fffffffu))); };
const auto lowNonzero = [&](const std::array<IrU32, 2>& word) { return IrU1(ir.INotEqual(word[0].Value(), ir.Constant(0u))); };
const auto isNan = [&](const std::array<IrU32, 2>& word) {
return IrU1(ir.LogicalOr(ir.UGreaterThan(magnitude(word[1]).Value(), ir.Constant(0x7ff00000u)),
ir.LogicalAnd(ir.IEqual(magnitude(word[1]).Value(), ir.Constant(0x7ff00000u)), lowNonzero(word).Value())));
};
const auto isInf = [&](const std::array<IrU32, 2>& word) {
return IrU1(ir.LogicalAnd(ir.IEqual(magnitude(word[1]).Value(), ir.Constant(0x7ff00000u)), ir.LogicalNot(lowNonzero(word).Value())));
};
const auto isZero = [&](const std::array<IrU32, 2>& word) {
return IrU1(ir.IEqual(ir.BitwiseOr(magnitude(word[1]).Value(), word[0].Value()), ir.Constant(0u)));
};
const auto exponent = [&](IrU32 high) {
return IrU32(ir.Emit(IrOpcode::BitFieldUExtract, IrType::U32, {&high.Value(), &ir.Constant(20u), &ir.Constant(11u)}));
};
const IrU32 sign(ir.BitwiseAnd(ir.BitwiseXor(denominator[1].Value(), numerator[1].Value()), ir.Constant(0x80000000u)));
const std::array<IrU32, 2> zero{IrU32(ir.Constant(0u)), sign};
const std::array<IrU32, 2> infinity{IrU32(ir.Constant(0u)), IrU32(ir.BitwiseOr(sign.Value(), ir.Constant(0x7ff00000u)))};
const std::array<IrU32, 2> defaultNan{IrU32(ir.Constant(0u)), IrU32(ir.Constant(0xfff80000u))};
const IrU32 difference(ir.ISub(exponent(numerator[1]).Value(), exponent(denominator[1]).Value()));
const IrU1 underflow(ir.Emit(IrOpcode::SLessThan32, IrType::U1, {&difference.Value(), &ir.Constant(static_cast<std::uint32_t>(-1075))}));
std::array<IrU32, 2> result{quotient[0], IrU32(ir.BitwiseOr(sign.Value(), magnitude(quotient[1]).Value()))};
const auto choose = [&](IrU1 condition, const std::array<IrU32, 2>& word) {
result = {IrU32(ir.Select(condition.Value(), word[0].Value(), result[0].Value())), IrU32(ir.Select(condition.Value(), word[1].Value(), result[1].Value()))};
};
choose(isNan(quotient), infinity);
choose(underflow, zero);
choose(IrU1(ir.LogicalOr(isInf(denominator).Value(), isZero(numerator).Value())), zero);
choose(IrU1(ir.LogicalOr(isZero(denominator).Value(), isInf(numerator).Value())), infinity);
choose(IrU1(ir.LogicalOr(ir.LogicalAnd(isZero(denominator).Value(), isZero(numerator).Value()), ir.LogicalAnd(isInf(denominator).Value(), isInf(numerator).Value()))), defaultNan);
choose(isNan(denominator), quietNan64(denominator));
choose(isNan(numerator), quietNan64(numerator));
writeF64Result(inst.destination, ir.ConstructU64(result[0].Value(), result[1].Value()));
return true;
}
}