tint: Implement const eval of unary minus
Bug: tint:1581 Change-Id: I228e8d083229fabfe8a4c0876160d673502e10a3 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/96422 Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Antonio Maiorano <amaiorano@google.com> Reviewed-by: Ben Clayton <bclayton@google.com> Reviewed-by: dan sinclair <dsinclair@google.com> Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
parent
f0a52d8f20
commit
3b8b9699d6
|
@ -18,6 +18,8 @@
|
|||
#include <vector>
|
||||
|
||||
#include "src/tint/program_builder.h"
|
||||
#include "src/tint/sem/abstract_float.h"
|
||||
#include "src/tint/sem/abstract_int.h"
|
||||
|
||||
namespace tint::fuzzers::ast_fuzzer {
|
||||
|
||||
|
@ -98,7 +100,8 @@ std::vector<ast::UnaryOp> MutationWrapUnaryOperator::GetValidUnaryWrapper(
|
|||
return {ast::UnaryOp::kNot};
|
||||
}
|
||||
|
||||
if (expr_type->is_signed_scalar_or_vector()) {
|
||||
if (expr_type->is_signed_scalar_or_vector() ||
|
||||
expr_type->is_abstract_integer_scalar_or_vector()) {
|
||||
return {ast::UnaryOp::kNegation, ast::UnaryOp::kComplement};
|
||||
}
|
||||
|
||||
|
@ -106,7 +109,7 @@ std::vector<ast::UnaryOp> MutationWrapUnaryOperator::GetValidUnaryWrapper(
|
|||
return {ast::UnaryOp::kComplement};
|
||||
}
|
||||
|
||||
if (expr_type->is_float_scalar_or_vector()) {
|
||||
if (expr_type->is_float_scalar_or_vector() || expr_type->is_abstract_float_scalar_or_vector()) {
|
||||
return {ast::UnaryOp::kNegation};
|
||||
}
|
||||
|
||||
|
|
|
@ -134,6 +134,7 @@ match fi32: f32 | i32
|
|||
match fi32f16: f32 | f16 | i32
|
||||
match iu32: i32 | u32
|
||||
match aiu32: ai | i32 | u32
|
||||
match afi32f16: ai | af | f32 | i32 | f16
|
||||
match scalar: f32 | f16 | i32 | u32 | bool
|
||||
match abstract_or_scalar: ai | af | f32 | f16 | i32 | u32 | bool
|
||||
match af_f32: af | f32
|
||||
|
@ -822,8 +823,8 @@ op ! <N: num> (vec<N, bool>) -> vec<N, bool>
|
|||
@const op ~ <T: aiu32>(T) -> T
|
||||
@const op ~ <T: aiu32, N: num> (vec<N, T>) -> vec<N, T>
|
||||
|
||||
op - <T: fi32f16>(T) -> T
|
||||
op - <T: fi32f16, N: num> (vec<N, T>) -> vec<N, T>
|
||||
@const op - <T: afi32f16>(T) -> T
|
||||
@const op - <T: afi32f16, N: num> (vec<N, T>) -> vec<N, T>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Binary Operators //
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
|
@ -45,14 +46,38 @@ namespace tint::resolver {
|
|||
|
||||
namespace {
|
||||
|
||||
/// TypeDispatch is a helper for calling the function `f`, passing a single zero-value argument of
|
||||
/// the C++ type that corresponds to the sem::Type `type`. For example, calling `TypeDispatch()`
|
||||
/// with a type of `sem::I32*` will call the function f with a single argument of `i32(0)`.
|
||||
/// Helper that calls 'f' passing in `c`'s value
|
||||
template <typename F>
|
||||
auto aiu32Dispatch(const sem::Constant* c, F&& f) {
|
||||
return Switch(
|
||||
c->Type(), [&](const sem::AbstractInt*) { return f(c->As<AInt>()); },
|
||||
[&](const sem::I32*) { return f(c->As<i32>()); },
|
||||
[&](const sem::U32*) { return f(c->As<u32>()); });
|
||||
}
|
||||
|
||||
/// Helper that calls 'f' passing in `c`'s value
|
||||
template <typename F>
|
||||
auto afi32f16Dispatch(const sem::Constant* c, F&& f) {
|
||||
return Switch(
|
||||
c->Type(), [&](const sem::AbstractInt*) { return f(c->As<AInt>()); },
|
||||
[&](const sem::AbstractFloat*) { return f(c->As<AFloat>()); },
|
||||
[&](const sem::F32*) { return f(c->As<f32>()); },
|
||||
[&](const sem::I32*) { return f(c->As<i32>()); },
|
||||
[&](const sem::F16*) {
|
||||
// TODO(crbug.com/tint/1502): Support const eval for f16
|
||||
return nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
/// ZeroTypeDispatch is a helper for calling the function `f`, passing a single zero-value argument
|
||||
/// of the C++ type that corresponds to the sem::Type `type`. For example, calling
|
||||
/// `ZeroTypeDispatch()` with a type of `sem::I32*` will call the function f with a single argument
|
||||
/// of `i32(0)`.
|
||||
/// @returns the value returned by calling `f`.
|
||||
/// @note `type` must be a scalar or abstract numeric type. Other types will not call `f`, and will
|
||||
/// return the zero-initialized value of the return type for `f`.
|
||||
template <typename F>
|
||||
auto TypeDispatch(const sem::Type* type, F&& f) {
|
||||
auto ZeroTypeDispatch(const sem::Type* type, F&& f) {
|
||||
return Switch(
|
||||
type, //
|
||||
[&](const sem::AbstractInt*) { return f(AInt(0)); }, //
|
||||
|
@ -64,20 +89,6 @@ auto TypeDispatch(const sem::Type* type, F&& f) {
|
|||
[&](const sem::Bool*) { return f(static_cast<bool>(0)); });
|
||||
}
|
||||
|
||||
/// IntegerDispatch is a helper for calling the function `f`, passing the integer value of the
|
||||
/// constant c.
|
||||
/// @returns the value returned by calling `f`.
|
||||
/// @note `c` must be of an integer type. Other types will not call `f`, and will return the
|
||||
/// zero-initialized value of the return type for `f`
|
||||
template <typename F>
|
||||
auto IntegerDispatch(const sem::Constant* c, F&& f) {
|
||||
return Switch(
|
||||
c->Type(), //
|
||||
[&](const sem::AbstractInt*) { return f(c->As<AInt>()); }, //
|
||||
[&](const sem::I32*) { return f(c->As<i32>()); }, //
|
||||
[&](const sem::U32*) { return f(c->As<u32>()); });
|
||||
}
|
||||
|
||||
/// @returns `value` if `T` is not a Number, otherwise ValueOf returns the inner value of the
|
||||
/// Number.
|
||||
template <typename T>
|
||||
|
@ -142,7 +153,7 @@ struct Element : Constant {
|
|||
return this;
|
||||
}
|
||||
bool failed = false;
|
||||
auto* res = TypeDispatch(target_ty, [&](auto zero_to) -> const Constant* {
|
||||
auto* res = ZeroTypeDispatch(target_ty, [&](auto zero_to) -> const Constant* {
|
||||
// `T` is the source type, `value` is the source value.
|
||||
// `TO` is the target type.
|
||||
using TO = std::decay_t<decltype(zero_to)>;
|
||||
|
@ -333,7 +344,7 @@ const Constant* ZeroValue(ProgramBuilder& builder, const sem::Type* type) {
|
|||
return CreateComposite(builder, s, std::move(zeros));
|
||||
},
|
||||
[&](Default) -> const Constant* {
|
||||
return TypeDispatch(type, [&](auto zero) -> const Constant* {
|
||||
return ZeroTypeDispatch(type, [&](auto zero) -> const Constant* {
|
||||
return CreateElement(builder, type, zero);
|
||||
});
|
||||
});
|
||||
|
@ -643,12 +654,34 @@ const sem::Constant* ConstEval::OpComplement(const sem::Type*,
|
|||
sem::Expression const* const* args,
|
||||
size_t) {
|
||||
return TransformElements(builder, args[0]->ConstantValue(), [&](const sem::Constant* c) {
|
||||
return IntegerDispatch(c, [&](auto i) { //
|
||||
return aiu32Dispatch(c, [&](auto i) { //
|
||||
return CreateElement(builder, c->Type(), decltype(i)(~i.value));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const sem::Constant* ConstEval::OpMinus(const sem::Type*,
|
||||
sem::Expression const* const* args,
|
||||
size_t) {
|
||||
return TransformElements(builder, args[0]->ConstantValue(), [&](const sem::Constant* c) {
|
||||
return afi32f16Dispatch(c, [&](auto i) { //
|
||||
// For signed integrals, avoid C++ UB by not negating the smallest negative number. In
|
||||
// WGSL, this operation is well defined to return the same value, see:
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#arithmetic-expr.
|
||||
using T = UnwrapNumber<decltype(i)>;
|
||||
if constexpr (std::is_integral_v<T>) {
|
||||
auto v = i.value;
|
||||
if (v != std::numeric_limits<T>::min()) {
|
||||
v = -v;
|
||||
}
|
||||
return CreateElement(builder, c->Type(), decltype(i)(v));
|
||||
} else {
|
||||
return CreateElement(builder, c->Type(), decltype(i)(-i.value));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
utils::Result<const sem::Constant*> ConstEval::Convert(const sem::Type* target_ty,
|
||||
const sem::Constant* value,
|
||||
const Source& source) {
|
||||
|
|
|
@ -187,6 +187,15 @@ class ConstEval {
|
|||
sem::Expression const* const* args,
|
||||
size_t num_args);
|
||||
|
||||
/// Minus operator '-'
|
||||
/// @param ty the expression type
|
||||
/// @param args the input arguments
|
||||
/// @param num_args the number of input arguments (must be 1)
|
||||
/// @return the result value, or null if the value cannot be calculated
|
||||
const sem::Constant* OpMinus(const sem::Type* ty,
|
||||
sem::Expression const* const* args,
|
||||
size_t num_args);
|
||||
|
||||
private:
|
||||
/// Adds the given error message to the diagnostics
|
||||
void AddError(const std::string& msg, const Source& source) const;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "src/tint/resolver/resolver_test_helper.h"
|
||||
|
@ -2929,6 +2930,29 @@ TEST_F(ResolverConstEvalTest, MemberAccess) {
|
|||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
namespace unary_op {
|
||||
|
||||
template <typename T>
|
||||
auto Highest() {
|
||||
return T(T::kHighest);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto Lowest() {
|
||||
return T(T::kLowest);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr auto Negate(const Number<T>& v) {
|
||||
// For signed integrals, avoid C++ UB by not negating the smallest negative number. In
|
||||
// WGSL, this operation is well defined to return the same value, see:
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#arithmetic-expr.
|
||||
if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) {
|
||||
if (v == std::numeric_limits<T>::min()) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return -v;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct Values {
|
||||
T input;
|
||||
|
@ -2936,7 +2960,7 @@ struct Values {
|
|||
};
|
||||
|
||||
struct Case {
|
||||
std::variant<Values<AInt>, Values<u32>, Values<i32>> values;
|
||||
std::variant<Values<AInt>, Values<AFloat>, Values<u32>, Values<i32>, Values<f32>> values;
|
||||
};
|
||||
|
||||
static std::ostream& operator<<(std::ostream& o, const Case& c) {
|
||||
|
@ -2952,6 +2976,8 @@ Case C(T input, T expect) {
|
|||
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());
|
||||
std::visit(
|
||||
|
@ -3000,6 +3026,60 @@ INSTANTIATE_TEST_SUITE_P(Complement,
|
|||
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(Highest<AInt>(), -Highest<AInt>()),
|
||||
C(-Highest<AInt>(), Highest<AInt>()),
|
||||
C(Lowest<AInt>(), Negate(Lowest<AInt>())),
|
||||
C(Negate(Lowest<AInt>()), Lowest<AInt>()),
|
||||
// i32
|
||||
C(0_i, -0_i),
|
||||
C(-0_i, 0_i),
|
||||
C(1_i, -1_i),
|
||||
C(-1_i, 1_i),
|
||||
C(Highest<i32>(), -Highest<i32>()),
|
||||
C(-Highest<i32>(), Highest<i32>()),
|
||||
C(Lowest<i32>(), Negate(Lowest<i32>())),
|
||||
C(Negate(Lowest<i32>()), Lowest<i32>()),
|
||||
// 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(Highest<AFloat>(), -Highest<AFloat>()),
|
||||
C(-Highest<AFloat>(), Highest<AFloat>()),
|
||||
C(Lowest<AFloat>(), Negate(Lowest<AFloat>())),
|
||||
C(Negate(Lowest<AFloat>()), Lowest<AFloat>()),
|
||||
// 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(Highest<f32>(), -Highest<f32>()),
|
||||
C(-Highest<f32>(), Highest<f32>()),
|
||||
C(Lowest<f32>(), Negate(Lowest<f32>())),
|
||||
C(Negate(Lowest<f32>()), Lowest<f32>()),
|
||||
})));
|
||||
|
||||
// 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", nullptr, 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);
|
||||
}
|
||||
|
||||
} // namespace unary_op
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -1832,8 +1832,52 @@ std::string Aiu32::String(MatchState*) const {
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
/// TypeMatcher for 'match scalar'
|
||||
/// TypeMatcher for 'match afi32f16'
|
||||
/// @see src/tint/intrinsics.def:137:7
|
||||
class Afi32F16 : public TypeMatcher {
|
||||
public:
|
||||
/// Checks whether the given type matches the matcher rules, and returns the
|
||||
/// expected, canonicalized type on success.
|
||||
/// Match may define and refine the template types and numbers in state.
|
||||
/// @param state the MatchState
|
||||
/// @param type the type to match
|
||||
/// @returns the canonicalized type on match, otherwise nullptr
|
||||
const sem::Type* Match(MatchState& state,
|
||||
const sem::Type* type) const override;
|
||||
/// @param state the MatchState
|
||||
/// @return a string representation of the matcher.
|
||||
std::string String(MatchState* state) const override;
|
||||
};
|
||||
|
||||
const sem::Type* Afi32F16::Match(MatchState& state, const sem::Type* ty) const {
|
||||
if (match_af(ty)) {
|
||||
return build_af(state);
|
||||
}
|
||||
if (match_ai(ty)) {
|
||||
return build_ai(state);
|
||||
}
|
||||
if (match_i32(ty)) {
|
||||
return build_i32(state);
|
||||
}
|
||||
if (match_f32(ty)) {
|
||||
return build_f32(state);
|
||||
}
|
||||
if (match_f16(ty)) {
|
||||
return build_f16(state);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string Afi32F16::String(MatchState*) const {
|
||||
std::stringstream ss;
|
||||
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
|
||||
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
|
||||
ss << Ai().String(nullptr) << ", " << Af().String(nullptr) << ", " << F32().String(nullptr) << ", " << I32().String(nullptr) << " or " << F16().String(nullptr);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/// TypeMatcher for 'match scalar'
|
||||
/// @see src/tint/intrinsics.def:138:7
|
||||
class Scalar : public TypeMatcher {
|
||||
public:
|
||||
/// Checks whether the given type matches the matcher rules, and returns the
|
||||
|
@ -1877,7 +1921,7 @@ std::string Scalar::String(MatchState*) const {
|
|||
}
|
||||
|
||||
/// TypeMatcher for 'match abstract_or_scalar'
|
||||
/// @see src/tint/intrinsics.def:138:7
|
||||
/// @see src/tint/intrinsics.def:139:7
|
||||
class AbstractOrScalar : public TypeMatcher {
|
||||
public:
|
||||
/// Checks whether the given type matches the matcher rules, and returns the
|
||||
|
@ -1927,7 +1971,7 @@ std::string AbstractOrScalar::String(MatchState*) const {
|
|||
}
|
||||
|
||||
/// TypeMatcher for 'match af_f32'
|
||||
/// @see src/tint/intrinsics.def:139:7
|
||||
/// @see src/tint/intrinsics.def:140:7
|
||||
class AfF32 : public TypeMatcher {
|
||||
public:
|
||||
/// Checks whether the given type matches the matcher rules, and returns the
|
||||
|
@ -1962,7 +2006,7 @@ std::string AfF32::String(MatchState*) const {
|
|||
}
|
||||
|
||||
/// TypeMatcher for 'match af_f32f16'
|
||||
/// @see src/tint/intrinsics.def:140:7
|
||||
/// @see src/tint/intrinsics.def:141:7
|
||||
class AfF32F16 : public TypeMatcher {
|
||||
public:
|
||||
/// Checks whether the given type matches the matcher rules, and returns the
|
||||
|
@ -2000,7 +2044,7 @@ std::string AfF32F16::String(MatchState*) const {
|
|||
}
|
||||
|
||||
/// TypeMatcher for 'match scalar_no_f32'
|
||||
/// @see src/tint/intrinsics.def:141:7
|
||||
/// @see src/tint/intrinsics.def:142:7
|
||||
class ScalarNoF32 : public TypeMatcher {
|
||||
public:
|
||||
/// Checks whether the given type matches the matcher rules, and returns the
|
||||
|
@ -2041,7 +2085,7 @@ std::string ScalarNoF32::String(MatchState*) const {
|
|||
}
|
||||
|
||||
/// TypeMatcher for 'match scalar_no_f16'
|
||||
/// @see src/tint/intrinsics.def:142:7
|
||||
/// @see src/tint/intrinsics.def:143:7
|
||||
class ScalarNoF16 : public TypeMatcher {
|
||||
public:
|
||||
/// Checks whether the given type matches the matcher rules, and returns the
|
||||
|
@ -2082,7 +2126,7 @@ std::string ScalarNoF16::String(MatchState*) const {
|
|||
}
|
||||
|
||||
/// TypeMatcher for 'match scalar_no_i32'
|
||||
/// @see src/tint/intrinsics.def:143:7
|
||||
/// @see src/tint/intrinsics.def:144:7
|
||||
class ScalarNoI32 : public TypeMatcher {
|
||||
public:
|
||||
/// Checks whether the given type matches the matcher rules, and returns the
|
||||
|
@ -2123,7 +2167,7 @@ std::string ScalarNoI32::String(MatchState*) const {
|
|||
}
|
||||
|
||||
/// TypeMatcher for 'match scalar_no_u32'
|
||||
/// @see src/tint/intrinsics.def:144:7
|
||||
/// @see src/tint/intrinsics.def:145:7
|
||||
class ScalarNoU32 : public TypeMatcher {
|
||||
public:
|
||||
/// Checks whether the given type matches the matcher rules, and returns the
|
||||
|
@ -2164,7 +2208,7 @@ std::string ScalarNoU32::String(MatchState*) const {
|
|||
}
|
||||
|
||||
/// TypeMatcher for 'match scalar_no_bool'
|
||||
/// @see src/tint/intrinsics.def:145:7
|
||||
/// @see src/tint/intrinsics.def:146:7
|
||||
class ScalarNoBool : public TypeMatcher {
|
||||
public:
|
||||
/// Checks whether the given type matches the matcher rules, and returns the
|
||||
|
@ -2205,7 +2249,7 @@ std::string ScalarNoBool::String(MatchState*) const {
|
|||
}
|
||||
|
||||
/// EnumMatcher for 'match f32_texel_format'
|
||||
/// @see src/tint/intrinsics.def:156:7
|
||||
/// @see src/tint/intrinsics.def:157:7
|
||||
class F32TexelFormat : public NumberMatcher {
|
||||
public:
|
||||
/// Checks whether the given number matches the enum matcher rules.
|
||||
|
@ -2238,7 +2282,7 @@ std::string F32TexelFormat::String(MatchState*) const {
|
|||
}
|
||||
|
||||
/// EnumMatcher for 'match i32_texel_format'
|
||||
/// @see src/tint/intrinsics.def:158:7
|
||||
/// @see src/tint/intrinsics.def:159:7
|
||||
class I32TexelFormat : public NumberMatcher {
|
||||
public:
|
||||
/// Checks whether the given number matches the enum matcher rules.
|
||||
|
@ -2270,7 +2314,7 @@ std::string I32TexelFormat::String(MatchState*) const {
|
|||
}
|
||||
|
||||
/// EnumMatcher for 'match u32_texel_format'
|
||||
/// @see src/tint/intrinsics.def:160:7
|
||||
/// @see src/tint/intrinsics.def:161:7
|
||||
class U32TexelFormat : public NumberMatcher {
|
||||
public:
|
||||
/// Checks whether the given number matches the enum matcher rules.
|
||||
|
@ -2302,7 +2346,7 @@ std::string U32TexelFormat::String(MatchState*) const {
|
|||
}
|
||||
|
||||
/// EnumMatcher for 'match write_only'
|
||||
/// @see src/tint/intrinsics.def:163:7
|
||||
/// @see src/tint/intrinsics.def:164:7
|
||||
class WriteOnly : public NumberMatcher {
|
||||
public:
|
||||
/// Checks whether the given number matches the enum matcher rules.
|
||||
|
@ -2328,7 +2372,7 @@ std::string WriteOnly::String(MatchState*) const {
|
|||
}
|
||||
|
||||
/// EnumMatcher for 'match function_private_workgroup'
|
||||
/// @see src/tint/intrinsics.def:165:7
|
||||
/// @see src/tint/intrinsics.def:166:7
|
||||
class FunctionPrivateWorkgroup : public NumberMatcher {
|
||||
public:
|
||||
/// Checks whether the given number matches the enum matcher rules.
|
||||
|
@ -2358,7 +2402,7 @@ std::string FunctionPrivateWorkgroup::String(MatchState*) const {
|
|||
}
|
||||
|
||||
/// EnumMatcher for 'match workgroup_or_storage'
|
||||
/// @see src/tint/intrinsics.def:166:7
|
||||
/// @see src/tint/intrinsics.def:167:7
|
||||
class WorkgroupOrStorage : public NumberMatcher {
|
||||
public:
|
||||
/// Checks whether the given number matches the enum matcher rules.
|
||||
|
@ -2524,6 +2568,7 @@ class Matchers {
|
|||
Fi32F16 Fi32F16_;
|
||||
Iu32 Iu32_;
|
||||
Aiu32 Aiu32_;
|
||||
Afi32F16 Afi32F16_;
|
||||
Scalar Scalar_;
|
||||
AbstractOrScalar AbstractOrScalar_;
|
||||
AfF32 AfF32_;
|
||||
|
@ -2550,7 +2595,7 @@ class Matchers {
|
|||
~Matchers();
|
||||
|
||||
/// The template types, types, and type matchers
|
||||
TypeMatcher const* const type[66] = {
|
||||
TypeMatcher const* const type[67] = {
|
||||
/* [0] */ &template_type_0_,
|
||||
/* [1] */ &template_type_1_,
|
||||
/* [2] */ &Bool_,
|
||||
|
@ -2608,15 +2653,16 @@ class Matchers {
|
|||
/* [54] */ &Fi32F16_,
|
||||
/* [55] */ &Iu32_,
|
||||
/* [56] */ &Aiu32_,
|
||||
/* [57] */ &Scalar_,
|
||||
/* [58] */ &AbstractOrScalar_,
|
||||
/* [59] */ &AfF32_,
|
||||
/* [60] */ &AfF32F16_,
|
||||
/* [61] */ &ScalarNoF32_,
|
||||
/* [62] */ &ScalarNoF16_,
|
||||
/* [63] */ &ScalarNoI32_,
|
||||
/* [64] */ &ScalarNoU32_,
|
||||
/* [65] */ &ScalarNoBool_,
|
||||
/* [57] */ &Afi32F16_,
|
||||
/* [58] */ &Scalar_,
|
||||
/* [59] */ &AbstractOrScalar_,
|
||||
/* [60] */ &AfF32_,
|
||||
/* [61] */ &AfF32F16_,
|
||||
/* [62] */ &ScalarNoF32_,
|
||||
/* [63] */ &ScalarNoF16_,
|
||||
/* [64] */ &ScalarNoI32_,
|
||||
/* [65] */ &ScalarNoU32_,
|
||||
/* [66] */ &ScalarNoBool_,
|
||||
};
|
||||
|
||||
/// The template numbers, and number matchers
|
||||
|
@ -7914,7 +7960,7 @@ constexpr TemplateTypeInfo kTemplateTypes[] = {
|
|||
{
|
||||
/* [1] */
|
||||
/* name */ "U",
|
||||
/* matcher index */ 65,
|
||||
/* matcher index */ 66,
|
||||
},
|
||||
{
|
||||
/* [2] */
|
||||
|
@ -7924,7 +7970,7 @@ constexpr TemplateTypeInfo kTemplateTypes[] = {
|
|||
{
|
||||
/* [3] */
|
||||
/* name */ "U",
|
||||
/* matcher index */ 61,
|
||||
/* matcher index */ 62,
|
||||
},
|
||||
{
|
||||
/* [4] */
|
||||
|
@ -7934,7 +7980,7 @@ constexpr TemplateTypeInfo kTemplateTypes[] = {
|
|||
{
|
||||
/* [5] */
|
||||
/* name */ "U",
|
||||
/* matcher index */ 62,
|
||||
/* matcher index */ 63,
|
||||
},
|
||||
{
|
||||
/* [6] */
|
||||
|
@ -7944,7 +7990,7 @@ constexpr TemplateTypeInfo kTemplateTypes[] = {
|
|||
{
|
||||
/* [7] */
|
||||
/* name */ "U",
|
||||
/* matcher index */ 63,
|
||||
/* matcher index */ 64,
|
||||
},
|
||||
{
|
||||
/* [8] */
|
||||
|
@ -7954,7 +8000,7 @@ constexpr TemplateTypeInfo kTemplateTypes[] = {
|
|||
{
|
||||
/* [9] */
|
||||
/* name */ "U",
|
||||
/* matcher index */ 64,
|
||||
/* matcher index */ 65,
|
||||
},
|
||||
{
|
||||
/* [10] */
|
||||
|
@ -7964,7 +8010,7 @@ constexpr TemplateTypeInfo kTemplateTypes[] = {
|
|||
{
|
||||
/* [11] */
|
||||
/* name */ "T",
|
||||
/* matcher index */ 60,
|
||||
/* matcher index */ 61,
|
||||
},
|
||||
{
|
||||
/* [12] */
|
||||
|
@ -7979,12 +8025,12 @@ constexpr TemplateTypeInfo kTemplateTypes[] = {
|
|||
{
|
||||
/* [14] */
|
||||
/* name */ "T",
|
||||
/* matcher index */ 58,
|
||||
/* matcher index */ 59,
|
||||
},
|
||||
{
|
||||
/* [15] */
|
||||
/* name */ "T",
|
||||
/* matcher index */ 57,
|
||||
/* matcher index */ 58,
|
||||
},
|
||||
{
|
||||
/* [16] */
|
||||
|
@ -7994,27 +8040,27 @@ constexpr TemplateTypeInfo kTemplateTypes[] = {
|
|||
{
|
||||
/* [17] */
|
||||
/* name */ "T",
|
||||
/* matcher index */ 65,
|
||||
/* matcher index */ 66,
|
||||
},
|
||||
{
|
||||
/* [18] */
|
||||
/* name */ "T",
|
||||
/* matcher index */ 62,
|
||||
/* matcher index */ 63,
|
||||
},
|
||||
{
|
||||
/* [19] */
|
||||
/* name */ "T",
|
||||
/* matcher index */ 61,
|
||||
/* matcher index */ 62,
|
||||
},
|
||||
{
|
||||
/* [20] */
|
||||
/* name */ "T",
|
||||
/* matcher index */ 64,
|
||||
/* matcher index */ 65,
|
||||
},
|
||||
{
|
||||
/* [21] */
|
||||
/* name */ "T",
|
||||
/* matcher index */ 63,
|
||||
/* matcher index */ 64,
|
||||
},
|
||||
{
|
||||
/* [22] */
|
||||
|
@ -8024,7 +8070,7 @@ constexpr TemplateTypeInfo kTemplateTypes[] = {
|
|||
{
|
||||
/* [23] */
|
||||
/* name */ "T",
|
||||
/* matcher index */ 54,
|
||||
/* matcher index */ 57,
|
||||
},
|
||||
{
|
||||
/* [24] */
|
||||
|
@ -13161,7 +13207,7 @@ constexpr OverloadInfo kOverloads[] = {
|
|||
/* parameters */ &kParameters[862],
|
||||
/* return matcher indices */ &kMatcherIndices[1],
|
||||
/* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
|
||||
/* const eval */ nullptr,
|
||||
/* const eval */ &ConstEval::OpMinus,
|
||||
},
|
||||
{
|
||||
/* [423] */
|
||||
|
@ -13173,7 +13219,7 @@ constexpr OverloadInfo kOverloads[] = {
|
|||
/* parameters */ &kParameters[863],
|
||||
/* return matcher indices */ &kMatcherIndices[39],
|
||||
/* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
|
||||
/* const eval */ nullptr,
|
||||
/* const eval */ &ConstEval::OpMinus,
|
||||
},
|
||||
{
|
||||
/* [424] */
|
||||
|
@ -14521,8 +14567,8 @@ constexpr IntrinsicInfo kUnaryOperators[] = {
|
|||
},
|
||||
{
|
||||
/* [2] */
|
||||
/* op -<T : fi32f16>(T) -> T */
|
||||
/* op -<T : fi32f16, N : num>(vec<N, T>) -> vec<N, T> */
|
||||
/* op -<T : afi32f16>(T) -> T */
|
||||
/* op -<T : afi32f16, N : num>(vec<N, T>) -> vec<N, T> */
|
||||
/* num overloads */ 2,
|
||||
/* overloads */ &kOverloads[422],
|
||||
},
|
||||
|
|
|
@ -604,8 +604,8 @@ TEST_F(IntrinsicTableTest, MismatchUnaryOp) {
|
|||
EXPECT_EQ(Diagnostics().str(), R"(12:34 error: no matching overload for operator - (bool)
|
||||
|
||||
2 candidate operators:
|
||||
operator - (T) -> T where: T is f32, f16 or i32
|
||||
operator - (vecN<T>) -> vecN<T> where: T is f32, f16 or i32
|
||||
operator - (T) -> T where: T is abstract-int, abstract-float, f32, i32 or f16
|
||||
operator - (vecN<T>) -> vecN<T> where: T is abstract-int, abstract-float, f32, i32 or f16
|
||||
)");
|
||||
}
|
||||
|
||||
|
|
|
@ -136,6 +136,30 @@ bool Type::is_integer_scalar_or_vector() const {
|
|||
return is_unsigned_scalar_or_vector() || is_signed_scalar_or_vector();
|
||||
}
|
||||
|
||||
bool Type::is_abstract_scalar_vector() const {
|
||||
return Is([](const Vector* v) { return v->type()->Is<sem::AbstractNumeric>(); });
|
||||
}
|
||||
|
||||
bool Type::is_abstract_integer_vector() const {
|
||||
return Is([](const Vector* v) { return v->type()->Is<sem::AbstractInt>(); });
|
||||
}
|
||||
|
||||
bool Type::is_abstract_float_vector() const {
|
||||
return Is([](const Vector* v) { return v->type()->Is<sem::AbstractFloat>(); });
|
||||
}
|
||||
|
||||
bool Type::is_abstract_scalar_or_vector() const {
|
||||
return Is<sem::AbstractNumeric>() || is_abstract_scalar_vector();
|
||||
}
|
||||
|
||||
bool Type::is_abstract_integer_scalar_or_vector() const {
|
||||
return Is<sem::AbstractInt>() || is_abstract_integer_vector();
|
||||
}
|
||||
|
||||
bool Type::is_abstract_float_scalar_or_vector() const {
|
||||
return Is<sem::AbstractFloat>() || is_abstract_float_vector();
|
||||
}
|
||||
|
||||
bool Type::is_bool_vector() const {
|
||||
return Is([](const Vector* v) { return v->type()->Is<Bool>(); });
|
||||
}
|
||||
|
|
|
@ -103,6 +103,18 @@ class Type : public Castable<Type, Node> {
|
|||
bool is_signed_scalar_or_vector() const;
|
||||
/// @returns true if this type is an integer scalar or vector
|
||||
bool is_integer_scalar_or_vector() const;
|
||||
/// @returns true if this type is an abstract scalar vector
|
||||
bool is_abstract_scalar_vector() const;
|
||||
/// @returns true if this type is an abstract integer vector
|
||||
bool is_abstract_integer_vector() const;
|
||||
/// @returns true if this type is an abstract float vector
|
||||
bool is_abstract_float_vector() const;
|
||||
/// @returns true if this type is an abstract scalar or vector
|
||||
bool is_abstract_scalar_or_vector() const;
|
||||
/// @returns true if this type is an abstract integer scalar or vector
|
||||
bool is_abstract_integer_scalar_or_vector() const;
|
||||
/// @returns true if this type is an abstract float scalar or vector
|
||||
bool is_abstract_float_scalar_or_vector() const;
|
||||
/// @returns true if this type is a boolean vector
|
||||
bool is_bool_vector() const;
|
||||
/// @returns true if this type is boolean scalar or vector
|
||||
|
|
|
@ -81,16 +81,15 @@ TEST_F(MslUnaryOpTest, Negation) {
|
|||
EXPECT_EQ(out.str(), "tint_unary_minus(expr)");
|
||||
}
|
||||
|
||||
TEST_F(MslUnaryOpTest, NegationOfIntMin) {
|
||||
auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation,
|
||||
Expr(i32(std::numeric_limits<int32_t>::min())));
|
||||
TEST_F(MslUnaryOpTest, IntMin) {
|
||||
auto* op = Expr(i32(std::numeric_limits<int32_t>::min()));
|
||||
WrapInFunction(op);
|
||||
|
||||
GeneratorImpl& gen = Build();
|
||||
|
||||
std::stringstream out;
|
||||
ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.error();
|
||||
EXPECT_EQ(out.str(), "tint_unary_minus((-2147483647 - 1))");
|
||||
EXPECT_EQ(out.str(), "(-2147483647 - 1)");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
Loading…
Reference in New Issue