test(hex8): pin uniform packet branches and inactive overflow routing
This commit is contained in:
@@ -142,7 +142,9 @@ TEST(Hex8HighLow, FourColumnMaterialPacketMatchesFrozenScalarBits) {
|
|||||||
{std::numeric_limits<double>::min()}, {maximum}, {-maximum},
|
{std::numeric_limits<double>::min()}, {maximum}, {-maximum},
|
||||||
{1, tiny}, {1, -tiny}, {std::nextafter(1.0, 2.0)},
|
{1, tiny}, {1, -tiny}, {std::nextafter(1.0, 2.0)},
|
||||||
{1, 1}, {0, tiny}, {std::numeric_limits<double>::infinity(), 0},
|
{1, 1}, {0, tiny}, {std::numeric_limits<double>::infinity(), 0},
|
||||||
{std::numeric_limits<double>::quiet_NaN(), 0}};
|
{std::numeric_limits<double>::quiet_NaN(), 0},
|
||||||
|
{std::numeric_limits<double>::signaling_NaN(), 0},
|
||||||
|
{0, std::numeric_limits<double>::signaling_NaN()}};
|
||||||
for (std::size_t first = 0; first < boundary.size(); ++first)
|
for (std::size_t first = 0; first < boundary.size(); ++first)
|
||||||
for (std::size_t second = 0; second < boundary.size(); ++second) {
|
for (std::size_t second = 0; second < boundary.size(); ++second) {
|
||||||
MaterialPacketInput input;
|
MaterialPacketInput input;
|
||||||
@@ -230,6 +232,73 @@ TEST(Hex8HighLow, PacketSpecialLaneDoesNotChangeOtherColumns) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Hex8HighLow, UniformDyadicPacketsExerciseEachLowZeroShape) {
|
||||||
|
std::size_t checks = 0, avx2_checks = 0;
|
||||||
|
for (int shape = 0; shape < 4; ++shape)
|
||||||
|
for (int low_weight = 0; low_weight < 2; ++low_weight)
|
||||||
|
for (int zero_accumulator = 0; zero_accumulator < 2; ++zero_accumulator) {
|
||||||
|
fesa::hex8_internal::MaterialPacketInput input;
|
||||||
|
input.weight = {0.5, low_weight != 0 ? std::ldexp(1.0, -57) : 0.0};
|
||||||
|
for (std::size_t lane = 0; lane < 4; ++lane) {
|
||||||
|
const bool left_low = (shape & 1) != 0;
|
||||||
|
const bool right_low = (shape & 2) != 0;
|
||||||
|
// Every lane has the same normalized nonzero dyadic inputs. Component
|
||||||
|
// LBE sums are >= -116 and all Add intermediates are bounded by 4.
|
||||||
|
// Thus each Multiply low-shape is uniform and its component products
|
||||||
|
// bypass zero/unsupported-tail fallback. No mixed-zero Add fallback
|
||||||
|
// is possible; the initial zero accumulator is uniform across lanes.
|
||||||
|
input.deviatoric_left[lane] = {1, left_low ? std::ldexp(1.0, -54) : 0};
|
||||||
|
input.deviatoric_right[lane] = {2, right_low ? std::ldexp(1.0, -54) : 0};
|
||||||
|
input.shear_left[lane] = {0.5, left_low ? std::ldexp(1.0, -56) : 0};
|
||||||
|
input.shear_right[lane] = {0.25, right_low ? std::ldexp(1.0, -57) : 0};
|
||||||
|
input.tensor_left[lane] = {0.75, left_low ? std::ldexp(1.0, -56) : 0};
|
||||||
|
input.gradient_dot[lane] = {0.125, right_low ? std::ldexp(1.0, -58) : 0};
|
||||||
|
input.accumulated[lane] = zero_accumulator != 0 ? HighLow{}
|
||||||
|
: HighLow{0.0625};
|
||||||
|
}
|
||||||
|
avx2_checks += ExpectFrozenMaterialPacket(input) ? 1U : 0U;
|
||||||
|
++checks;
|
||||||
|
}
|
||||||
|
RecordProperty("uniform_low_shape_checks", static_cast<int>(checks));
|
||||||
|
RecordProperty("avx2_uniform_low_shape_checks", static_cast<int>(avx2_checks));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Hex8HighLow, OverflowLaneIsNeutralizedBeforeResidualFma) {
|
||||||
|
const auto evaluator = fesa::hex8_internal::SelectMaterialPacketEvaluator();
|
||||||
|
if (evaluator == fesa::hex8_internal::EvaluateMaterialPacketScalar)
|
||||||
|
GTEST_SKIP() << "AVX2 masked environment is not selected; routing unverified.";
|
||||||
|
std::fenv_t original;
|
||||||
|
ASSERT_EQ(std::fegetenv(&original), 0);
|
||||||
|
for (std::size_t bad = 0; bad < 4; ++bad) {
|
||||||
|
fesa::hex8_internal::MaterialPacketInput input;
|
||||||
|
input.weight = {0.5};
|
||||||
|
for (std::size_t lane = 0; lane < 4; ++lane) {
|
||||||
|
input.deviatoric_left[lane] = {
|
||||||
|
lane == bad ? std::numeric_limits<double>::max() : 1};
|
||||||
|
input.deviatoric_right[lane] = {2};
|
||||||
|
input.shear_left[lane] = {0.5};
|
||||||
|
input.shear_right[lane] = {0.25};
|
||||||
|
input.tensor_left[lane] = {0.75};
|
||||||
|
input.gradient_dot[lane] = {0.125};
|
||||||
|
input.accumulated[lane] = {0.0625};
|
||||||
|
}
|
||||||
|
// All operands are finite/nonzero and max*2 has LBE sum 972, so Product
|
||||||
|
// reaches its vector multiply; only the post-product finite mask rejects it.
|
||||||
|
const auto expected = FrozenMaterialPacket(input);
|
||||||
|
EXPECT_EQ(std::feclearexcept(FE_ALL_EXCEPT), 0);
|
||||||
|
fesa::hex8_internal::PairPacket output;
|
||||||
|
evaluator(input, &output);
|
||||||
|
const bool extra_invalid = (std::fetestexcept(FE_INVALID) & FE_INVALID) != 0;
|
||||||
|
EXPECT_EQ(std::fesetenv(&original), 0);
|
||||||
|
EXPECT_FALSE(extra_invalid);
|
||||||
|
for (std::size_t lane = 0; lane < 4; ++lane) {
|
||||||
|
EXPECT_EQ(PairBits(output[lane].high), PairBits(expected[lane].high));
|
||||||
|
EXPECT_EQ(PairBits(output[lane].low), PairBits(expected[lane].low));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RecordProperty("avx2_product_overflow_positions", 4);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(Hex8HighLow, PacketBackendSelectionRecoversAfterEnvironmentRestoration) {
|
TEST(Hex8HighLow, PacketBackendSelectionRecoversAfterEnvironmentRestoration) {
|
||||||
const auto initial = fesa::hex8_internal::SelectMaterialPacketEvaluator();
|
const auto initial = fesa::hex8_internal::SelectMaterialPacketEvaluator();
|
||||||
const int rounding = std::fegetround();
|
const int rounding = std::fegetround();
|
||||||
|
|||||||
Reference in New Issue
Block a user