tint: Add unary-ops to the intrinsic table
• Declare all the unary ops in the intrinsics.def file. • Reimplement the bulk of Resolver::UnaryOp() with the IntrinsicTable. This will simplify maintenance of the operators, and will greatly simplify the [AbstractInt -> i32|u32] [AbstractFloat -> f32|f16] logic. Bug: tint:1504 Change-Id: Ifc646d086fc93cfbe3f3f861b8c447178664c1f7 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/89028 Reviewed-by: James Price <jrprice@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Ben Clayton <bclayton@chromium.org>
This commit is contained in:
parent
7378612ca5
commit
b61e0452f8
|
@ -109,6 +109,7 @@ type __frexp_result
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
match fiu32: f32 | i32 | u32
|
||||
match fi32: f32 | i32
|
||||
match iu32: i32 | u32
|
||||
match scalar: f32 | i32 | u32 | bool
|
||||
|
||||
|
@ -572,6 +573,18 @@ fn textureLoad(texture: texture_external, coords: vec2<i32>) -> vec4<f32>
|
|||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Unary Operators //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
op ! (bool) -> bool
|
||||
op ! <N: num> (vec<N, bool>) -> vec<N, bool>
|
||||
|
||||
op ~ <T: iu32>(T) -> T
|
||||
op ~ <T: iu32, N: num> (vec<N, T>) -> vec<N, T>
|
||||
|
||||
op - <T: fi32>(T) -> T
|
||||
op - <T: fi32, N: num> (vec<N, T>) -> vec<N, T>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Binary Operators //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -828,6 +828,8 @@ class Impl : public IntrinsicTable {
|
|||
const std::vector<const sem::Type*>& args,
|
||||
const Source& source) override;
|
||||
|
||||
UnaryOperator Lookup(ast::UnaryOp op, const sem::Type* arg, const Source& source) override;
|
||||
|
||||
BinaryOperator Lookup(ast::BinaryOp op,
|
||||
const sem::Type* lhs,
|
||||
const sem::Type* rhs,
|
||||
|
@ -945,6 +947,61 @@ const sem::Builtin* Impl::Lookup(sem::BuiltinType builtin_type,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
IntrinsicTable::UnaryOperator Impl::Lookup(ast::UnaryOp op,
|
||||
const sem::Type* arg,
|
||||
const Source& source) {
|
||||
// The list of failed matches that had promise.
|
||||
std::vector<Candidate> candidates;
|
||||
|
||||
auto [intrinsic_index, intrinsic_name] = [&]() -> std::pair<uint32_t, const char*> {
|
||||
switch (op) {
|
||||
case ast::UnaryOp::kComplement:
|
||||
return {kOperatorComplement, "operator ~ "};
|
||||
case ast::UnaryOp::kNegation:
|
||||
return {kOperatorMinus, "operator - "};
|
||||
case ast::UnaryOp::kNot:
|
||||
return {kOperatorNot, "operator ! "};
|
||||
default:
|
||||
return {0, "<unknown>"};
|
||||
}
|
||||
}();
|
||||
|
||||
auto& builtin = kOperators[intrinsic_index];
|
||||
for (uint32_t o = 0; o < builtin.num_overloads; o++) {
|
||||
int match_score = 1000;
|
||||
auto& overload = builtin.overloads[o];
|
||||
if (overload.num_parameters == 1) {
|
||||
auto match = Match(intrinsic_name, intrinsic_index, overload, {arg}, match_score);
|
||||
if (match.return_type) {
|
||||
return UnaryOperator{match.return_type, match.parameters[0].type};
|
||||
}
|
||||
if (match_score > 0) {
|
||||
candidates.emplace_back(Candidate{&overload, match_score});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the candidates with the most promising first
|
||||
std::stable_sort(candidates.begin(), candidates.end(),
|
||||
[](const Candidate& a, const Candidate& b) { return a.score > b.score; });
|
||||
|
||||
// Generate an error message
|
||||
std::stringstream ss;
|
||||
ss << "no matching overload for " << CallSignature(builder, intrinsic_name, {arg}) << std::endl;
|
||||
if (!candidates.empty()) {
|
||||
ss << std::endl;
|
||||
ss << candidates.size() << " candidate operator" << (candidates.size() > 1 ? "s:" : ":")
|
||||
<< std::endl;
|
||||
for (auto& candidate : candidates) {
|
||||
ss << " ";
|
||||
PrintOverload(ss, *candidate.overload, intrinsic_name);
|
||||
ss << std::endl;
|
||||
}
|
||||
}
|
||||
builder.Diagnostics().add_error(diag::System::Resolver, ss.str(), source);
|
||||
return {};
|
||||
}
|
||||
|
||||
IntrinsicTable::BinaryOperator Impl::Lookup(ast::BinaryOp op,
|
||||
const sem::Type* lhs,
|
||||
const sem::Type* rhs,
|
||||
|
@ -1000,6 +1057,7 @@ IntrinsicTable::BinaryOperator Impl::Lookup(ast::BinaryOp op,
|
|||
for (uint32_t o = 0; o < builtin.num_overloads; o++) {
|
||||
int match_score = 1000;
|
||||
auto& overload = builtin.overloads[o];
|
||||
if (overload.num_parameters == 2) {
|
||||
auto match = Match(intrinsic_name, intrinsic_index, overload, {lhs, rhs}, match_score);
|
||||
if (match.return_type) {
|
||||
return BinaryOperator{match.return_type, match.parameters[0].type,
|
||||
|
@ -1009,6 +1067,7 @@ IntrinsicTable::BinaryOperator Impl::Lookup(ast::BinaryOp op,
|
|||
candidates.emplace_back(Candidate{&overload, match_score});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the candidates with the most promising first
|
||||
std::stable_sort(candidates.begin(), candidates.end(),
|
||||
|
|
|
@ -38,6 +38,14 @@ class IntrinsicTable {
|
|||
/// Destructor
|
||||
virtual ~IntrinsicTable();
|
||||
|
||||
/// UnaryOperator describes a resolved unary operator
|
||||
struct UnaryOperator {
|
||||
/// The result type of the unary operator
|
||||
const sem::Type* result;
|
||||
/// The type of the arg of the unary operator
|
||||
const sem::Type* arg;
|
||||
};
|
||||
|
||||
/// BinaryOperator describes a resolved binary operator
|
||||
struct BinaryOperator {
|
||||
/// The result type of the binary operator
|
||||
|
@ -58,6 +66,15 @@ class IntrinsicTable {
|
|||
const std::vector<const sem::Type*>& args,
|
||||
const Source& source) = 0;
|
||||
|
||||
/// Lookup looks for the unary op overload with the given signature, raising an error
|
||||
/// diagnostic if the operator was not found.
|
||||
/// @param op the unary operator
|
||||
/// @param arg the type of the expression passed to the operator
|
||||
/// @param source the source of the operator call
|
||||
/// @return the operator call target signature. If the operator was not found
|
||||
/// UnaryOperator::result will be nullptr.
|
||||
virtual UnaryOperator Lookup(ast::UnaryOp op, const sem::Type* arg, const Source& source) = 0;
|
||||
|
||||
/// Lookup looks for the binary op overload with the given signature, raising an error
|
||||
/// diagnostic if the operator was not found.
|
||||
/// @param op the binary operator
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -427,7 +427,9 @@ Matchers::~Matchers() = default;
|
|||
{{- else if eq . "&&" -}}LogicalAnd
|
||||
{{- else if eq . "||" -}}LogicalOr
|
||||
{{- else if eq . "==" -}}Equal
|
||||
{{- else if eq . "!" -}}Not
|
||||
{{- else if eq . "!=" -}}NotEqual
|
||||
{{- else if eq . "~" -}}Complement
|
||||
{{- else if eq . "<" -}}LessThan
|
||||
{{- else if eq . ">" -}}GreaterThan
|
||||
{{- else if eq . "<=" -}}LessThanEqual
|
||||
|
|
|
@ -576,6 +576,27 @@ TEST_F(IntrinsicTableTest, SameOverloadReturnsSameBuiltinPointer) {
|
|||
EXPECT_NE(b, c);
|
||||
}
|
||||
|
||||
TEST_F(IntrinsicTableTest, MatchUnaryOp) {
|
||||
auto* i32 = create<sem::I32>();
|
||||
auto* vec3_i32 = create<sem::Vector>(i32, 3u);
|
||||
auto result = table->Lookup(ast::UnaryOp::kNegation, vec3_i32, Source{{12, 34}});
|
||||
EXPECT_EQ(result.result, vec3_i32);
|
||||
EXPECT_EQ(result.result, vec3_i32);
|
||||
EXPECT_EQ(Diagnostics().str(), "");
|
||||
}
|
||||
|
||||
TEST_F(IntrinsicTableTest, MismatchUnaryOp) {
|
||||
auto* bool_ = create<sem::Bool>();
|
||||
auto result = table->Lookup(ast::UnaryOp::kNegation, bool_, Source{{12, 34}});
|
||||
ASSERT_EQ(result.result, nullptr);
|
||||
EXPECT_EQ(Diagnostics().str(), R"(12:34 error: no matching overload for operator - (bool)
|
||||
|
||||
2 candidate operators:
|
||||
operator - (T) -> T where: T is f32 or i32
|
||||
operator - (vecN<T>) -> vecN<T> where: T is f32 or i32
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IntrinsicTableTest, MatchBinaryOp) {
|
||||
auto* i32 = create<sem::I32>();
|
||||
auto* vec3_i32 = create<sem::Vector>(i32, 3u);
|
||||
|
|
|
@ -1771,38 +1771,6 @@ sem::Expression* Resolver::UnaryOp(const ast::UnaryOpExpression* unary) {
|
|||
const sem::Variable* source_var = nullptr;
|
||||
|
||||
switch (unary->op) {
|
||||
case ast::UnaryOp::kNot:
|
||||
// Result type matches the deref'd inner type.
|
||||
ty = expr_ty->UnwrapRef();
|
||||
if (!ty->Is<sem::Bool>() && !ty->is_bool_vector()) {
|
||||
AddError("cannot logical negate expression of type '" + sem_.TypeNameOf(expr_ty),
|
||||
unary->expr->source);
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
|
||||
case ast::UnaryOp::kComplement:
|
||||
// Result type matches the deref'd inner type.
|
||||
ty = expr_ty->UnwrapRef();
|
||||
if (!ty->is_integer_scalar_or_vector()) {
|
||||
AddError(
|
||||
"cannot bitwise complement expression of type '" + sem_.TypeNameOf(expr_ty),
|
||||
unary->expr->source);
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
|
||||
case ast::UnaryOp::kNegation:
|
||||
// Result type matches the deref'd inner type.
|
||||
ty = expr_ty->UnwrapRef();
|
||||
if (!(ty->IsAnyOf<sem::F32, sem::I32>() || ty->is_signed_integer_vector() ||
|
||||
ty->is_float_vector())) {
|
||||
AddError("cannot negate expression of type '" + sem_.TypeNameOf(expr_ty),
|
||||
unary->expr->source);
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
|
||||
case ast::UnaryOp::kAddressOf:
|
||||
if (auto* ref = expr_ty->As<sem::Reference>()) {
|
||||
if (ref->StoreType()->UnwrapRef()->is_handle()) {
|
||||
|
@ -1840,6 +1808,13 @@ sem::Expression* Resolver::UnaryOp(const ast::UnaryOpExpression* unary) {
|
|||
return nullptr;
|
||||
}
|
||||
break;
|
||||
|
||||
default: {
|
||||
ty = intrinsic_table_->Lookup(unary->op, expr_ty, unary->source).result;
|
||||
if (!ty) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto val = EvaluateConstantValue(unary, ty);
|
||||
|
|
|
@ -1932,7 +1932,7 @@ TEST_F(ResolverTest, UnaryOp_Not) {
|
|||
WrapInFunction(der);
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: cannot logical negate expression of type 'vec4<f32>");
|
||||
EXPECT_THAT(r()->error(), HasSubstr("error: no matching overload for operator ! (vec4<f32>)"));
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, UnaryOp_Complement) {
|
||||
|
@ -1942,7 +1942,7 @@ TEST_F(ResolverTest, UnaryOp_Complement) {
|
|||
WrapInFunction(der);
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: cannot bitwise complement expression of type 'vec4<f32>");
|
||||
EXPECT_THAT(r()->error(), HasSubstr("error: no matching overload for operator ~ (vec4<f32>)"));
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, UnaryOp_Negation) {
|
||||
|
@ -1952,7 +1952,7 @@ TEST_F(ResolverTest, UnaryOp_Negation) {
|
|||
WrapInFunction(der);
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: cannot negate expression of type 'u32");
|
||||
EXPECT_THAT(r()->error(), HasSubstr("error: no matching overload for operator - (u32)"));
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, TextureSampler_TextureSample) {
|
||||
|
|
|
@ -72,6 +72,8 @@ func (l *lexer) lex() error {
|
|||
l.tok(1, tok.Modulo)
|
||||
case '^':
|
||||
l.tok(1, tok.Xor)
|
||||
case '~':
|
||||
l.tok(1, tok.Complement)
|
||||
case '"':
|
||||
start := l.loc
|
||||
l.next() // Skip opening quote
|
||||
|
@ -105,6 +107,7 @@ func (l *lexer) lex() error {
|
|||
case l.match("||", tok.OrOr):
|
||||
case l.match("|", tok.Or):
|
||||
case l.match("!=", tok.NotEqual):
|
||||
case l.match("!", tok.Not):
|
||||
case l.match("==", tok.Equal):
|
||||
case l.match("=", tok.Assign):
|
||||
case l.match("<<", tok.Shl):
|
||||
|
|
|
@ -91,6 +91,9 @@ func TestLexTokens(t *testing.T) {
|
|||
{"|", tok.Token{Kind: tok.Or, Runes: []rune("|"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"!", tok.Token{Kind: tok.Not, Runes: []rune("!"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"!=", tok.Token{Kind: tok.NotEqual, Runes: []rune("!="), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 3, 2),
|
||||
}}},
|
||||
|
|
|
@ -32,34 +32,36 @@ const (
|
|||
Operator Kind = "op"
|
||||
Type Kind = "type"
|
||||
Enum Kind = "enum"
|
||||
Colon Kind = ":"
|
||||
Comma Kind = ","
|
||||
Shl Kind = "<<"
|
||||
Shr Kind = ">>"
|
||||
Lt Kind = "<"
|
||||
Le Kind = "<="
|
||||
Gt Kind = ">"
|
||||
Ge Kind = ">="
|
||||
Lbrace Kind = "{"
|
||||
Rbrace Kind = "}"
|
||||
Ldeco Kind = "[["
|
||||
Rdeco Kind = "]]"
|
||||
Lparen Kind = "("
|
||||
Rparen Kind = ")"
|
||||
Or Kind = "|"
|
||||
Arrow Kind = "->"
|
||||
Star Kind = "*"
|
||||
Divide Kind = "/"
|
||||
Modulo Kind = "%"
|
||||
Xor Kind = "^"
|
||||
Plus Kind = "+"
|
||||
Minus Kind = "-"
|
||||
And Kind = "&"
|
||||
AndAnd Kind = "&&"
|
||||
OrOr Kind = "||"
|
||||
NotEqual Kind = "!="
|
||||
Equal Kind = "=="
|
||||
Arrow Kind = "->"
|
||||
Assign Kind = "="
|
||||
Colon Kind = ":"
|
||||
Comma Kind = ","
|
||||
Complement Kind = "~"
|
||||
Divide Kind = "/"
|
||||
Equal Kind = "=="
|
||||
Ge Kind = ">="
|
||||
Gt Kind = ">"
|
||||
Lbrace Kind = "{"
|
||||
Ldeco Kind = "[["
|
||||
Le Kind = "<="
|
||||
Lparen Kind = "("
|
||||
Lt Kind = "<"
|
||||
Minus Kind = "-"
|
||||
Modulo Kind = "%"
|
||||
Not Kind = "!"
|
||||
NotEqual Kind = "!="
|
||||
Or Kind = "|"
|
||||
OrOr Kind = "||"
|
||||
Plus Kind = "+"
|
||||
Rbrace Kind = "}"
|
||||
Rdeco Kind = "]]"
|
||||
Rparen Kind = ")"
|
||||
Shl Kind = "<<"
|
||||
Shr Kind = ">>"
|
||||
Star Kind = "*"
|
||||
Xor Kind = "^"
|
||||
)
|
||||
|
||||
// Invalid represents an invalid token
|
||||
|
|
Loading…
Reference in New Issue