mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-20 02:41:48 +00:00
The reason for slow compile times is because the very large variants of builder::Value<T>s combined with the many std::visits over these variants result in many combinatorial instantiations of the visit callbacks. To address this, I added a polymorphic base class ValueBase to Value<T>, and replaced most of the std::visit-based compile time code with runtime virtual calls. For the two heaviest users of std::visit over the large variants, compiles times dropped more than half (clang-10, debug): const_eval_binary_op_test.cc: 19.079s to 7.736s const_eval_unary_op_test.cc: 10.021s to 4.789s Bug: tint:1711 Change-Id: Iba05e6ae1004ef0814250e2a8ea50aa2b26b85f2 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/105782 Reviewed-by: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Antonio Maiorano <amaiorano@google.com>
184 lines
8.9 KiB
C++
184 lines
8.9 KiB
C++
// Copyright 2022 The Tint Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "src/tint/resolver/const_eval_test.h"
|
|
|
|
using namespace tint::number_suffixes; // NOLINT
|
|
|
|
namespace tint::resolver {
|
|
namespace {
|
|
|
|
// Bring in std::ostream& operator<<(std::ostream& o, const Types& types)
|
|
using resolver::operator<<;
|
|
|
|
struct Case {
|
|
Types input;
|
|
Types expected;
|
|
};
|
|
|
|
static std::ostream& operator<<(std::ostream& o, const Case& c) {
|
|
o << "input: " << c.input << ", expected: " << c.expected;
|
|
return o;
|
|
}
|
|
|
|
/// Creates a Case with Values of any type
|
|
template <typename T, typename U>
|
|
Case C(Value<T> input, Value<U> expected) {
|
|
return Case{std::move(input), std::move(expected)};
|
|
}
|
|
|
|
/// Convenience overload to creates a Case with just scalars
|
|
template <typename T, typename U, typename = std::enable_if_t<!IsValue<T>>>
|
|
Case C(T input, U expected) {
|
|
return Case{Val(input), Val(expected)};
|
|
}
|
|
|
|
using ResolverConstEvalUnaryOpTest = ResolverTestWithParam<std::tuple<ast::UnaryOp, Case>>;
|
|
|
|
TEST_P(ResolverConstEvalUnaryOpTest, Test) {
|
|
Enable(ast::Extension::kF16);
|
|
|
|
auto op = std::get<0>(GetParam());
|
|
auto& c = std::get<1>(GetParam());
|
|
|
|
auto* expected = ToValueBase(c.expected);
|
|
auto* input = ToValueBase(c.input);
|
|
|
|
auto* input_expr = input->Expr(*this);
|
|
auto* expr = create<ast::UnaryOpExpression>(op, input_expr);
|
|
|
|
GlobalConst("C", expr);
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
|
|
auto* sem = Sem().Get(expr);
|
|
const sem::Constant* value = sem->ConstantValue();
|
|
ASSERT_NE(value, nullptr);
|
|
EXPECT_TYPE(value->Type(), sem->Type());
|
|
|
|
auto values_flat = ScalarArgsFrom(value);
|
|
auto expected_values_flat = expected->Args();
|
|
ASSERT_EQ(values_flat.values.Length(), expected_values_flat.values.Length());
|
|
for (size_t i = 0; i < values_flat.values.Length(); ++i) {
|
|
auto& a = values_flat.values[i];
|
|
auto& b = expected_values_flat.values[i];
|
|
EXPECT_EQ(a, b);
|
|
if (expected->IsIntegral()) {
|
|
// Check that the constant's integer doesn't contain unexpected
|
|
// data in the MSBs that are outside of the bit-width of T.
|
|
EXPECT_EQ(builder::As<AInt>(a), builder::As<AInt>(b));
|
|
}
|
|
}
|
|
}
|
|
INSTANTIATE_TEST_SUITE_P(Complement,
|
|
ResolverConstEvalUnaryOpTest,
|
|
testing::Combine(testing::Values(ast::UnaryOp::kComplement),
|
|
testing::ValuesIn({
|
|
// AInt
|
|
C(0_a, 0xffffffffffffffff_a),
|
|
C(0xffffffffffffffff_a, 0_a),
|
|
C(0xf0f0f0f0f0f0f0f0_a, 0x0f0f0f0f0f0f0f0f_a),
|
|
C(0xaaaaaaaaaaaaaaaa_a, 0x5555555555555555_a),
|
|
C(0x5555555555555555_a, 0xaaaaaaaaaaaaaaaa_a),
|
|
// u32
|
|
C(0_u, 0xffffffff_u),
|
|
C(0xffffffff_u, 0_u),
|
|
C(0xf0f0f0f0_u, 0x0f0f0f0f_u),
|
|
C(0xaaaaaaaa_u, 0x55555555_u),
|
|
C(0x55555555_u, 0xaaaaaaaa_u),
|
|
// i32
|
|
C(0_i, -1_i),
|
|
C(-1_i, 0_i),
|
|
C(1_i, -2_i),
|
|
C(-2_i, 1_i),
|
|
C(2_i, -3_i),
|
|
C(-3_i, 2_i),
|
|
})));
|
|
|
|
INSTANTIATE_TEST_SUITE_P(Negation,
|
|
ResolverConstEvalUnaryOpTest,
|
|
testing::Combine(testing::Values(ast::UnaryOp::kNegation),
|
|
testing::ValuesIn({
|
|
// AInt
|
|
C(0_a, -0_a),
|
|
C(-0_a, 0_a),
|
|
C(1_a, -1_a),
|
|
C(-1_a, 1_a),
|
|
C(AInt::Highest(), -AInt::Highest()),
|
|
C(-AInt::Highest(), AInt::Highest()),
|
|
C(AInt::Lowest(), Negate(AInt::Lowest())),
|
|
C(Negate(AInt::Lowest()), AInt::Lowest()),
|
|
// i32
|
|
C(0_i, -0_i),
|
|
C(-0_i, 0_i),
|
|
C(1_i, -1_i),
|
|
C(-1_i, 1_i),
|
|
C(i32::Highest(), -i32::Highest()),
|
|
C(-i32::Highest(), i32::Highest()),
|
|
C(i32::Lowest(), Negate(i32::Lowest())),
|
|
C(Negate(i32::Lowest()), i32::Lowest()),
|
|
// AFloat
|
|
C(0.0_a, -0.0_a),
|
|
C(-0.0_a, 0.0_a),
|
|
C(1.0_a, -1.0_a),
|
|
C(-1.0_a, 1.0_a),
|
|
C(AFloat::Highest(), -AFloat::Highest()),
|
|
C(-AFloat::Highest(), AFloat::Highest()),
|
|
C(AFloat::Lowest(), Negate(AFloat::Lowest())),
|
|
C(Negate(AFloat::Lowest()), AFloat::Lowest()),
|
|
// f32
|
|
C(0.0_f, -0.0_f),
|
|
C(-0.0_f, 0.0_f),
|
|
C(1.0_f, -1.0_f),
|
|
C(-1.0_f, 1.0_f),
|
|
C(f32::Highest(), -f32::Highest()),
|
|
C(-f32::Highest(), f32::Highest()),
|
|
C(f32::Lowest(), Negate(f32::Lowest())),
|
|
C(Negate(f32::Lowest()), f32::Lowest()),
|
|
// f16
|
|
C(0.0_h, -0.0_h),
|
|
C(-0.0_h, 0.0_h),
|
|
C(1.0_h, -1.0_h),
|
|
C(-1.0_h, 1.0_h),
|
|
C(f16::Highest(), -f16::Highest()),
|
|
C(-f16::Highest(), f16::Highest()),
|
|
C(f16::Lowest(), Negate(f16::Lowest())),
|
|
C(Negate(f16::Lowest()), f16::Lowest()),
|
|
})));
|
|
|
|
// Make sure UBSan doesn't trip on C++'s undefined behaviour of negating the smallest negative
|
|
// number.
|
|
TEST_F(ResolverConstEvalTest, UnaryNegateLowestAbstract) {
|
|
// const break_me = -(-9223372036854775808);
|
|
auto* c = GlobalConst("break_me", Negation(Negation(Expr(9223372036854775808_a))));
|
|
(void)c;
|
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* sem = Sem().Get(c);
|
|
EXPECT_EQ(sem->ConstantValue()->As<AInt>(), 9223372036854775808_a);
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(Not,
|
|
ResolverConstEvalUnaryOpTest,
|
|
testing::Combine(testing::Values(ast::UnaryOp::kNot),
|
|
testing::ValuesIn({
|
|
C(true, false),
|
|
C(false, true),
|
|
C(Vec(true, true), Vec(false, false)),
|
|
C(Vec(true, false), Vec(false, true)),
|
|
C(Vec(false, true), Vec(true, false)),
|
|
C(Vec(false, false), Vec(true, true)),
|
|
})));
|
|
|
|
} // namespace
|
|
} // namespace tint::resolver
|