tint: Stub intrinsic @const evaluation

Add support for @const to builtins in intrinsics.def.
Propagate this flag through to the intrinsic table.
Handle builtins that are @const annotated in the resolver.

Currently no intrinsics are decorated with @const, so there's nothing to
test (yet).

Bug: tint:1504
Change-Id: I172483688617782bd7c58b70e3f38d0222a5d1af
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/92323
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
This commit is contained in:
Ben Clayton 2022-06-01 23:57:20 +00:00 committed by Dawn LUCI CQ
parent e0cd855aa2
commit 451eee0fed
13 changed files with 814 additions and 262 deletions

View File

@ -370,6 +370,8 @@ libtint_source_set("libtint_core_all_src") {
"program_id.h", "program_id.h",
"reader/reader.cc", "reader/reader.cc",
"reader/reader.h", "reader/reader.h",
"resolver/const_eval.cc",
"resolver/const_eval.h",
"resolver/ctor_conv_intrinsic.cc", "resolver/ctor_conv_intrinsic.cc",
"resolver/ctor_conv_intrinsic.h", "resolver/ctor_conv_intrinsic.h",
"resolver/dependency_graph.cc", "resolver/dependency_graph.cc",

View File

@ -249,6 +249,8 @@ set(TINT_LIB_SRCS
program.h program.h
reader/reader.cc reader/reader.cc
reader/reader.h reader/reader.h
resolver/const_eval.cc
resolver/const_eval.h
resolver/ctor_conv_intrinsic.cc resolver/ctor_conv_intrinsic.cc
resolver/ctor_conv_intrinsic.h resolver/ctor_conv_intrinsic.h
resolver/dependency_graph.cc resolver/dependency_graph.cc

View File

@ -0,0 +1,19 @@
// 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.h"
#include "src/tint/sem/constant.h"
namespace tint::resolver::const_eval {} // namespace tint::resolver::const_eval

View File

@ -0,0 +1,37 @@
// 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.
#ifndef SRC_TINT_RESOLVER_CONST_EVAL_H_
#define SRC_TINT_RESOLVER_CONST_EVAL_H_
#include <stddef.h>
// Forward declarations
namespace tint {
class ProgramBuilder;
} // namespace tint
// Forward declarations
namespace tint::sem {
class Constant;
} // namespace tint::sem
namespace tint::resolver::const_eval {
/// Typedef for a constant evaluation function
using Function = sem::Constant(ProgramBuilder& builder, const sem::Constant* args, size_t num_args);
} // namespace tint::resolver::const_eval
#endif // SRC_TINT_RESOLVER_CONST_EVAL_H_

View File

@ -844,6 +844,8 @@ struct OverloadInfo {
MatcherIndex const* const return_matcher_indices; MatcherIndex const* const return_matcher_indices;
/// The flags for the overload /// The flags for the overload
OverloadFlags flags; OverloadFlags flags;
/// The function used to evaluate the overload at shader-creation time.
const_eval::Function* const const_eval_fn;
}; };
/// IntrinsicInfo describes a builtin function or operator overload /// IntrinsicInfo describes a builtin function or operator overload
@ -905,9 +907,9 @@ class Impl : public IntrinsicTable {
public: public:
explicit Impl(ProgramBuilder& builder); explicit Impl(ProgramBuilder& builder);
const sem::Builtin* Lookup(sem::BuiltinType builtin_type, Builtin Lookup(sem::BuiltinType builtin_type,
const std::vector<const sem::Type*>& args, const std::vector<const sem::Type*>& args,
const Source& source) override; const Source& source) override;
UnaryOperator Lookup(ast::UnaryOp op, const sem::Type* arg, const Source& source) override; UnaryOperator Lookup(ast::UnaryOp op, const sem::Type* arg, const Source& source) override;
@ -1064,9 +1066,9 @@ std::string TemplateNumberMatcher::String(MatchState* state) const {
Impl::Impl(ProgramBuilder& b) : builder(b) {} Impl::Impl(ProgramBuilder& b) : builder(b) {}
const sem::Builtin* Impl::Lookup(sem::BuiltinType builtin_type, Impl::Builtin Impl::Lookup(sem::BuiltinType builtin_type,
const std::vector<const sem::Type*>& args, const std::vector<const sem::Type*>& args,
const Source& source) { const Source& source) {
const char* intrinsic_name = sem::str(builtin_type); const char* intrinsic_name = sem::str(builtin_type);
// Generates an error when no overloads match the provided arguments // Generates an error when no overloads match the provided arguments
@ -1090,7 +1092,7 @@ const sem::Builtin* Impl::Lookup(sem::BuiltinType builtin_type,
} }
// De-duplicate builtins that are identical. // De-duplicate builtins that are identical.
return utils::GetOrCreate(builtins, match, [&] { auto* sem = utils::GetOrCreate(builtins, match, [&] {
std::vector<sem::Parameter*> params; std::vector<sem::Parameter*> params;
params.reserve(match.parameters.size()); params.reserve(match.parameters.size());
for (auto& p : match.parameters) { for (auto& p : match.parameters) {
@ -1112,6 +1114,7 @@ const sem::Builtin* Impl::Lookup(sem::BuiltinType builtin_type,
builtin_type, match.return_type, std::move(params), supported_stages, builtin_type, match.return_type, std::move(params), supported_stages,
match.overload->flags.Contains(OverloadFlag::kIsDeprecated)); match.overload->flags.Contains(OverloadFlag::kIsDeprecated));
}); });
return Builtin{sem, match.overload->const_eval_fn};
} }
IntrinsicTable::UnaryOperator Impl::Lookup(ast::UnaryOp op, IntrinsicTable::UnaryOperator Impl::Lookup(ast::UnaryOp op,

View File

@ -19,6 +19,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "src/tint/resolver/const_eval.h"
#include "src/tint/resolver/ctor_conv_intrinsic.h" #include "src/tint/resolver/ctor_conv_intrinsic.h"
#include "src/tint/sem/builtin.h" #include "src/tint/sem/builtin.h"
@ -39,22 +40,30 @@ class IntrinsicTable {
/// Destructor /// Destructor
virtual ~IntrinsicTable(); virtual ~IntrinsicTable();
/// Builtin describes a resolved builtin function
struct Builtin {
/// The semantic info for the builtin
const sem::Builtin* sem = nullptr;
/// The constant evaluation function
const_eval::Function* const_eval_fn = nullptr;
};
/// UnaryOperator describes a resolved unary operator /// UnaryOperator describes a resolved unary operator
struct UnaryOperator { struct UnaryOperator {
/// The result type of the unary operator /// The result type of the unary operator
const sem::Type* result; const sem::Type* result = nullptr;
/// The type of the parameter of the unary operator /// The type of the parameter of the unary operator
const sem::Type* parameter; const sem::Type* parameter = nullptr;
}; };
/// BinaryOperator describes a resolved binary operator /// BinaryOperator describes a resolved binary operator
struct BinaryOperator { struct BinaryOperator {
/// The result type of the binary operator /// The result type of the binary operator
const sem::Type* result; const sem::Type* result = nullptr;
/// The type of LHS parameter of the binary operator /// The type of LHS parameter of the binary operator
const sem::Type* lhs; const sem::Type* lhs = nullptr;
/// The type of RHS parameter of the binary operator /// The type of RHS parameter of the binary operator
const sem::Type* rhs; const sem::Type* rhs = nullptr;
}; };
/// Lookup looks for the builtin overload with the given signature, raising an error diagnostic /// Lookup looks for the builtin overload with the given signature, raising an error diagnostic
@ -63,9 +72,9 @@ class IntrinsicTable {
/// @param args the argument types passed to the builtin function /// @param args the argument types passed to the builtin function
/// @param source the source of the builtin call /// @param source the source of the builtin call
/// @return the semantic builtin if found, otherwise nullptr /// @return the semantic builtin if found, otherwise nullptr
virtual const sem::Builtin* Lookup(sem::BuiltinType type, virtual Builtin Lookup(sem::BuiltinType type,
const std::vector<const sem::Type*>& args, const std::vector<const sem::Type*>& args,
const Source& source) = 0; const Source& source) = 0;
/// Lookup looks for the unary op overload with the given signature, raising an error /// Lookup looks for the unary op overload with the given signature, raising an error
/// diagnostic if the operator was not found. /// diagnostic if the operator was not found.

File diff suppressed because it is too large Load Diff

View File

@ -86,11 +86,11 @@ constexpr OverloadInfo kOverloads[] = {
/* num template numbers */ {{$o.NumTemplateNumbers}}, /* num template numbers */ {{$o.NumTemplateNumbers}},
/* template types */ /* template types */
{{- if $o.TemplateTypesOffset }} &kTemplateTypes[{{$o.TemplateTypesOffset}}], {{- if $o.TemplateTypesOffset }} &kTemplateTypes[{{$o.TemplateTypesOffset}}],
{{- else }} nullptr, {{- else }} nullptr,
{{- end }} {{- end }}
/* template numbers */ /* template numbers */
{{- if $o.TemplateNumbersOffset }} &kTemplateNumbers[{{$o.TemplateNumbersOffset}}] {{- if $o.TemplateNumbersOffset }} &kTemplateNumbers[{{$o.TemplateNumbersOffset}}]
{{- else }} nullptr {{- else }} nullptr
{{- end }}, {{- end }},
/* parameters */ &kParameters[{{$o.ParametersOffset}}], /* parameters */ &kParameters[{{$o.ParametersOffset}}],
/* return matcher indices */ /* return matcher indices */
@ -102,6 +102,10 @@ constexpr OverloadInfo kOverloads[] = {
, OverloadFlag::kSupports{{Title $u}}Pipeline , OverloadFlag::kSupports{{Title $u}}Pipeline
{{- end }} {{- end }}
{{- if $o.IsDeprecated}}, OverloadFlag::kIsDeprecated{{end }}), {{- if $o.IsDeprecated}}, OverloadFlag::kIsDeprecated{{end }}),
/* const eval */
{{- if $o.ConstEvalFunction }} const_eval::{{$o.ConstEvalFunction}},
{{- else }} nullptr,
{{- end }}
}, },
{{- end }} {{- end }}
}; };

View File

@ -53,19 +53,19 @@ class IntrinsicTableTest : public testing::Test, public ProgramBuilder {
TEST_F(IntrinsicTableTest, MatchF32) { TEST_F(IntrinsicTableTest, MatchF32) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kCos, {f32}, Source{}); auto result = table->Lookup(BuiltinType::kCos, {f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kCos); EXPECT_EQ(result.sem->Type(), BuiltinType::kCos);
EXPECT_EQ(result->ReturnType(), f32); EXPECT_EQ(result.sem->ReturnType(), f32);
ASSERT_EQ(result->Parameters().size(), 1u); ASSERT_EQ(result.sem->Parameters().size(), 1u);
EXPECT_EQ(result->Parameters()[0]->Type(), f32); EXPECT_EQ(result.sem->Parameters()[0]->Type(), f32);
} }
TEST_F(IntrinsicTableTest, MismatchF32) { TEST_F(IntrinsicTableTest, MismatchF32) {
auto* i32 = create<sem::I32>(); auto* i32 = create<sem::I32>();
auto* result = table->Lookup(BuiltinType::kCos, {i32}, Source{}); auto result = table->Lookup(BuiltinType::kCos, {i32}, Source{});
ASSERT_EQ(result, nullptr); ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call")); ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
} }
@ -73,19 +73,19 @@ TEST_F(IntrinsicTableTest, MatchU32) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* u32 = create<sem::U32>(); auto* u32 = create<sem::U32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u); auto* vec2_f32 = create<sem::Vector>(f32, 2u);
auto* result = table->Lookup(BuiltinType::kUnpack2x16float, {u32}, Source{}); auto result = table->Lookup(BuiltinType::kUnpack2x16float, {u32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kUnpack2x16float); EXPECT_EQ(result.sem->Type(), BuiltinType::kUnpack2x16float);
EXPECT_EQ(result->ReturnType(), vec2_f32); EXPECT_EQ(result.sem->ReturnType(), vec2_f32);
ASSERT_EQ(result->Parameters().size(), 1u); ASSERT_EQ(result.sem->Parameters().size(), 1u);
EXPECT_EQ(result->Parameters()[0]->Type(), u32); EXPECT_EQ(result.sem->Parameters()[0]->Type(), u32);
} }
TEST_F(IntrinsicTableTest, MismatchU32) { TEST_F(IntrinsicTableTest, MismatchU32) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kUnpack2x16float, {f32}, Source{}); auto result = table->Lookup(BuiltinType::kUnpack2x16float, {f32}, Source{});
ASSERT_EQ(result, nullptr); ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call")); ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
} }
@ -94,121 +94,121 @@ TEST_F(IntrinsicTableTest, MatchI32) {
auto* i32 = create<sem::I32>(); auto* i32 = create<sem::I32>();
auto* vec4_f32 = create<sem::Vector>(f32, 4u); auto* vec4_f32 = create<sem::Vector>(f32, 4u);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32); auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32);
auto* result = table->Lookup(BuiltinType::kTextureLoad, {tex, i32, i32}, Source{}); auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureLoad); EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result->ReturnType(), vec4_f32); EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
ASSERT_EQ(result->Parameters().size(), 3u); ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex); EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture); EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), i32); EXPECT_EQ(result.sem->Parameters()[1]->Type(), i32);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kCoords); EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result->Parameters()[2]->Type(), i32); EXPECT_EQ(result.sem->Parameters()[2]->Type(), i32);
EXPECT_EQ(result->Parameters()[2]->Usage(), ParameterUsage::kLevel); EXPECT_EQ(result.sem->Parameters()[2]->Usage(), ParameterUsage::kLevel);
} }
TEST_F(IntrinsicTableTest, MismatchI32) { TEST_F(IntrinsicTableTest, MismatchI32) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32); auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32);
auto* result = table->Lookup(BuiltinType::kTextureLoad, {tex, f32}, Source{}); auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, f32}, Source{});
ASSERT_EQ(result, nullptr); ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call")); ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
} }
TEST_F(IntrinsicTableTest, MatchIU32AsI32) { TEST_F(IntrinsicTableTest, MatchIU32AsI32) {
auto* i32 = create<sem::I32>(); auto* i32 = create<sem::I32>();
auto* result = table->Lookup(BuiltinType::kCountOneBits, {i32}, Source{}); auto result = table->Lookup(BuiltinType::kCountOneBits, {i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kCountOneBits); EXPECT_EQ(result.sem->Type(), BuiltinType::kCountOneBits);
EXPECT_EQ(result->ReturnType(), i32); EXPECT_EQ(result.sem->ReturnType(), i32);
ASSERT_EQ(result->Parameters().size(), 1u); ASSERT_EQ(result.sem->Parameters().size(), 1u);
EXPECT_EQ(result->Parameters()[0]->Type(), i32); EXPECT_EQ(result.sem->Parameters()[0]->Type(), i32);
} }
TEST_F(IntrinsicTableTest, MatchIU32AsU32) { TEST_F(IntrinsicTableTest, MatchIU32AsU32) {
auto* u32 = create<sem::U32>(); auto* u32 = create<sem::U32>();
auto* result = table->Lookup(BuiltinType::kCountOneBits, {u32}, Source{}); auto result = table->Lookup(BuiltinType::kCountOneBits, {u32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kCountOneBits); EXPECT_EQ(result.sem->Type(), BuiltinType::kCountOneBits);
EXPECT_EQ(result->ReturnType(), u32); EXPECT_EQ(result.sem->ReturnType(), u32);
ASSERT_EQ(result->Parameters().size(), 1u); ASSERT_EQ(result.sem->Parameters().size(), 1u);
EXPECT_EQ(result->Parameters()[0]->Type(), u32); EXPECT_EQ(result.sem->Parameters()[0]->Type(), u32);
} }
TEST_F(IntrinsicTableTest, MismatchIU32) { TEST_F(IntrinsicTableTest, MismatchIU32) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kCountOneBits, {f32}, Source{}); auto result = table->Lookup(BuiltinType::kCountOneBits, {f32}, Source{});
ASSERT_EQ(result, nullptr); ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call")); ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
} }
TEST_F(IntrinsicTableTest, MatchFIU32AsI32) { TEST_F(IntrinsicTableTest, MatchFIU32AsI32) {
auto* i32 = create<sem::I32>(); auto* i32 = create<sem::I32>();
auto* result = table->Lookup(BuiltinType::kClamp, {i32, i32, i32}, Source{}); auto result = table->Lookup(BuiltinType::kClamp, {i32, i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kClamp); EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
EXPECT_EQ(result->ReturnType(), i32); EXPECT_EQ(result.sem->ReturnType(), i32);
ASSERT_EQ(result->Parameters().size(), 3u); ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), i32); EXPECT_EQ(result.sem->Parameters()[0]->Type(), i32);
EXPECT_EQ(result->Parameters()[1]->Type(), i32); EXPECT_EQ(result.sem->Parameters()[1]->Type(), i32);
EXPECT_EQ(result->Parameters()[2]->Type(), i32); EXPECT_EQ(result.sem->Parameters()[2]->Type(), i32);
} }
TEST_F(IntrinsicTableTest, MatchFIU32AsU32) { TEST_F(IntrinsicTableTest, MatchFIU32AsU32) {
auto* u32 = create<sem::U32>(); auto* u32 = create<sem::U32>();
auto* result = table->Lookup(BuiltinType::kClamp, {u32, u32, u32}, Source{}); auto result = table->Lookup(BuiltinType::kClamp, {u32, u32, u32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kClamp); EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
EXPECT_EQ(result->ReturnType(), u32); EXPECT_EQ(result.sem->ReturnType(), u32);
ASSERT_EQ(result->Parameters().size(), 3u); ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), u32); EXPECT_EQ(result.sem->Parameters()[0]->Type(), u32);
EXPECT_EQ(result->Parameters()[1]->Type(), u32); EXPECT_EQ(result.sem->Parameters()[1]->Type(), u32);
EXPECT_EQ(result->Parameters()[2]->Type(), u32); EXPECT_EQ(result.sem->Parameters()[2]->Type(), u32);
} }
TEST_F(IntrinsicTableTest, MatchFIU32AsF32) { TEST_F(IntrinsicTableTest, MatchFIU32AsF32) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kClamp, {f32, f32, f32}, Source{}); auto result = table->Lookup(BuiltinType::kClamp, {f32, f32, f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kClamp); EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
EXPECT_EQ(result->ReturnType(), f32); EXPECT_EQ(result.sem->ReturnType(), f32);
ASSERT_EQ(result->Parameters().size(), 3u); ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), f32); EXPECT_EQ(result.sem->Parameters()[0]->Type(), f32);
EXPECT_EQ(result->Parameters()[1]->Type(), f32); EXPECT_EQ(result.sem->Parameters()[1]->Type(), f32);
EXPECT_EQ(result->Parameters()[2]->Type(), f32); EXPECT_EQ(result.sem->Parameters()[2]->Type(), f32);
} }
TEST_F(IntrinsicTableTest, MismatchFIU32) { TEST_F(IntrinsicTableTest, MismatchFIU32) {
auto* bool_ = create<sem::Bool>(); auto* bool_ = create<sem::Bool>();
auto* result = table->Lookup(BuiltinType::kClamp, {bool_, bool_, bool_}, Source{}); auto result = table->Lookup(BuiltinType::kClamp, {bool_, bool_, bool_}, Source{});
ASSERT_EQ(result, nullptr); ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call")); ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
} }
TEST_F(IntrinsicTableTest, MatchBool) { TEST_F(IntrinsicTableTest, MatchBool) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* bool_ = create<sem::Bool>(); auto* bool_ = create<sem::Bool>();
auto* result = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{}); auto result = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kSelect); EXPECT_EQ(result.sem->Type(), BuiltinType::kSelect);
EXPECT_EQ(result->ReturnType(), f32); EXPECT_EQ(result.sem->ReturnType(), f32);
ASSERT_EQ(result->Parameters().size(), 3u); ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), f32); EXPECT_EQ(result.sem->Parameters()[0]->Type(), f32);
EXPECT_EQ(result->Parameters()[1]->Type(), f32); EXPECT_EQ(result.sem->Parameters()[1]->Type(), f32);
EXPECT_EQ(result->Parameters()[2]->Type(), bool_); EXPECT_EQ(result.sem->Parameters()[2]->Type(), bool_);
} }
TEST_F(IntrinsicTableTest, MismatchBool) { TEST_F(IntrinsicTableTest, MismatchBool) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kSelect, {f32, f32, f32}, Source{}); auto result = table->Lookup(BuiltinType::kSelect, {f32, f32, f32}, Source{});
ASSERT_EQ(result, nullptr); ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call")); ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
} }
@ -217,41 +217,41 @@ TEST_F(IntrinsicTableTest, MatchPointer) {
auto* atomicI32 = create<sem::Atomic>(i32); auto* atomicI32 = create<sem::Atomic>(i32);
auto* ptr = auto* ptr =
create<sem::Pointer>(atomicI32, ast::StorageClass::kWorkgroup, ast::Access::kReadWrite); create<sem::Pointer>(atomicI32, ast::StorageClass::kWorkgroup, ast::Access::kReadWrite);
auto* result = table->Lookup(BuiltinType::kAtomicLoad, {ptr}, Source{}); auto result = table->Lookup(BuiltinType::kAtomicLoad, {ptr}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kAtomicLoad); EXPECT_EQ(result.sem->Type(), BuiltinType::kAtomicLoad);
EXPECT_EQ(result->ReturnType(), i32); EXPECT_EQ(result.sem->ReturnType(), i32);
ASSERT_EQ(result->Parameters().size(), 1u); ASSERT_EQ(result.sem->Parameters().size(), 1u);
EXPECT_EQ(result->Parameters()[0]->Type(), ptr); EXPECT_EQ(result.sem->Parameters()[0]->Type(), ptr);
} }
TEST_F(IntrinsicTableTest, MismatchPointer) { TEST_F(IntrinsicTableTest, MismatchPointer) {
auto* i32 = create<sem::I32>(); auto* i32 = create<sem::I32>();
auto* atomicI32 = create<sem::Atomic>(i32); auto* atomicI32 = create<sem::Atomic>(i32);
auto* result = table->Lookup(BuiltinType::kAtomicLoad, {atomicI32}, Source{}); auto result = table->Lookup(BuiltinType::kAtomicLoad, {atomicI32}, Source{});
ASSERT_EQ(result, nullptr); ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call")); ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
} }
TEST_F(IntrinsicTableTest, MatchArray) { TEST_F(IntrinsicTableTest, MatchArray) {
auto* arr = create<sem::Array>(create<sem::U32>(), 0u, 4u, 4u, 4u, 4u); auto* arr = create<sem::Array>(create<sem::U32>(), 0u, 4u, 4u, 4u, 4u);
auto* arr_ptr = create<sem::Pointer>(arr, ast::StorageClass::kStorage, ast::Access::kReadWrite); auto* arr_ptr = create<sem::Pointer>(arr, ast::StorageClass::kStorage, ast::Access::kReadWrite);
auto* result = table->Lookup(BuiltinType::kArrayLength, {arr_ptr}, Source{}); auto result = table->Lookup(BuiltinType::kArrayLength, {arr_ptr}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kArrayLength); EXPECT_EQ(result.sem->Type(), BuiltinType::kArrayLength);
EXPECT_TRUE(result->ReturnType()->Is<sem::U32>()); EXPECT_TRUE(result.sem->ReturnType()->Is<sem::U32>());
ASSERT_EQ(result->Parameters().size(), 1u); ASSERT_EQ(result.sem->Parameters().size(), 1u);
auto* param_type = result->Parameters()[0]->Type(); auto* param_type = result.sem->Parameters()[0]->Type();
ASSERT_TRUE(param_type->Is<sem::Pointer>()); ASSERT_TRUE(param_type->Is<sem::Pointer>());
EXPECT_TRUE(param_type->As<sem::Pointer>()->StoreType()->Is<sem::Array>()); EXPECT_TRUE(param_type->As<sem::Pointer>()->StoreType()->Is<sem::Array>());
} }
TEST_F(IntrinsicTableTest, MismatchArray) { TEST_F(IntrinsicTableTest, MismatchArray) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kArrayLength, {f32}, Source{}); auto result = table->Lookup(BuiltinType::kArrayLength, {f32}, Source{});
ASSERT_EQ(result, nullptr); ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call")); ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
} }
@ -261,26 +261,26 @@ TEST_F(IntrinsicTableTest, MatchSampler) {
auto* vec4_f32 = create<sem::Vector>(f32, 4u); auto* vec4_f32 = create<sem::Vector>(f32, 4u);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32); auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
auto* sampler = create<sem::Sampler>(ast::SamplerKind::kSampler); auto* sampler = create<sem::Sampler>(ast::SamplerKind::kSampler);
auto* result = table->Lookup(BuiltinType::kTextureSample, {tex, sampler, vec2_f32}, Source{}); auto result = table->Lookup(BuiltinType::kTextureSample, {tex, sampler, vec2_f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureSample); EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureSample);
EXPECT_EQ(result->ReturnType(), vec4_f32); EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
ASSERT_EQ(result->Parameters().size(), 3u); ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex); EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture); EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), sampler); EXPECT_EQ(result.sem->Parameters()[1]->Type(), sampler);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kSampler); EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kSampler);
EXPECT_EQ(result->Parameters()[2]->Type(), vec2_f32); EXPECT_EQ(result.sem->Parameters()[2]->Type(), vec2_f32);
EXPECT_EQ(result->Parameters()[2]->Usage(), ParameterUsage::kCoords); EXPECT_EQ(result.sem->Parameters()[2]->Usage(), ParameterUsage::kCoords);
} }
TEST_F(IntrinsicTableTest, MismatchSampler) { TEST_F(IntrinsicTableTest, MismatchSampler) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u); auto* vec2_f32 = create<sem::Vector>(f32, 2u);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32); auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
auto* result = table->Lookup(BuiltinType::kTextureSample, {tex, f32, vec2_f32}, Source{}); auto result = table->Lookup(BuiltinType::kTextureSample, {tex, f32, vec2_f32}, Source{});
ASSERT_EQ(result, nullptr); ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call")); ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
} }
@ -290,18 +290,18 @@ TEST_F(IntrinsicTableTest, MatchSampledTexture) {
auto* vec2_i32 = create<sem::Vector>(i32, 2u); auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* vec4_f32 = create<sem::Vector>(f32, 4u); auto* vec4_f32 = create<sem::Vector>(f32, 4u);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32); auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
auto* result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{}); auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureLoad); EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result->ReturnType(), vec4_f32); EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
ASSERT_EQ(result->Parameters().size(), 3u); ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex); EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture); EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), vec2_i32); EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kCoords); EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result->Parameters()[2]->Type(), i32); EXPECT_EQ(result.sem->Parameters()[2]->Type(), i32);
EXPECT_EQ(result->Parameters()[2]->Usage(), ParameterUsage::kLevel); EXPECT_EQ(result.sem->Parameters()[2]->Usage(), ParameterUsage::kLevel);
} }
TEST_F(IntrinsicTableTest, MatchMultisampledTexture) { TEST_F(IntrinsicTableTest, MatchMultisampledTexture) {
@ -310,18 +310,18 @@ TEST_F(IntrinsicTableTest, MatchMultisampledTexture) {
auto* vec2_i32 = create<sem::Vector>(i32, 2u); auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* vec4_f32 = create<sem::Vector>(f32, 4u); auto* vec4_f32 = create<sem::Vector>(f32, 4u);
auto* tex = create<sem::MultisampledTexture>(ast::TextureDimension::k2d, f32); auto* tex = create<sem::MultisampledTexture>(ast::TextureDimension::k2d, f32);
auto* result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{}); auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureLoad); EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result->ReturnType(), vec4_f32); EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
ASSERT_EQ(result->Parameters().size(), 3u); ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex); EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture); EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), vec2_i32); EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kCoords); EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result->Parameters()[2]->Type(), i32); EXPECT_EQ(result.sem->Parameters()[2]->Type(), i32);
EXPECT_EQ(result->Parameters()[2]->Usage(), ParameterUsage::kSampleIndex); EXPECT_EQ(result.sem->Parameters()[2]->Usage(), ParameterUsage::kSampleIndex);
} }
TEST_F(IntrinsicTableTest, MatchDepthTexture) { TEST_F(IntrinsicTableTest, MatchDepthTexture) {
@ -329,18 +329,18 @@ TEST_F(IntrinsicTableTest, MatchDepthTexture) {
auto* i32 = create<sem::I32>(); auto* i32 = create<sem::I32>();
auto* vec2_i32 = create<sem::Vector>(i32, 2u); auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* tex = create<sem::DepthTexture>(ast::TextureDimension::k2d); auto* tex = create<sem::DepthTexture>(ast::TextureDimension::k2d);
auto* result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{}); auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureLoad); EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result->ReturnType(), f32); EXPECT_EQ(result.sem->ReturnType(), f32);
ASSERT_EQ(result->Parameters().size(), 3u); ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex); EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture); EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), vec2_i32); EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kCoords); EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result->Parameters()[2]->Type(), i32); EXPECT_EQ(result.sem->Parameters()[2]->Type(), i32);
EXPECT_EQ(result->Parameters()[2]->Usage(), ParameterUsage::kLevel); EXPECT_EQ(result.sem->Parameters()[2]->Usage(), ParameterUsage::kLevel);
} }
TEST_F(IntrinsicTableTest, MatchDepthMultisampledTexture) { TEST_F(IntrinsicTableTest, MatchDepthMultisampledTexture) {
@ -348,18 +348,18 @@ TEST_F(IntrinsicTableTest, MatchDepthMultisampledTexture) {
auto* i32 = create<sem::I32>(); auto* i32 = create<sem::I32>();
auto* vec2_i32 = create<sem::Vector>(i32, 2u); auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* tex = create<sem::DepthMultisampledTexture>(ast::TextureDimension::k2d); auto* tex = create<sem::DepthMultisampledTexture>(ast::TextureDimension::k2d);
auto* result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{}); auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureLoad); EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result->ReturnType(), f32); EXPECT_EQ(result.sem->ReturnType(), f32);
ASSERT_EQ(result->Parameters().size(), 3u); ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex); EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture); EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), vec2_i32); EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kCoords); EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result->Parameters()[2]->Type(), i32); EXPECT_EQ(result.sem->Parameters()[2]->Type(), i32);
EXPECT_EQ(result->Parameters()[2]->Usage(), ParameterUsage::kSampleIndex); EXPECT_EQ(result.sem->Parameters()[2]->Usage(), ParameterUsage::kSampleIndex);
} }
TEST_F(IntrinsicTableTest, MatchExternalTexture) { TEST_F(IntrinsicTableTest, MatchExternalTexture) {
@ -368,16 +368,16 @@ TEST_F(IntrinsicTableTest, MatchExternalTexture) {
auto* vec2_i32 = create<sem::Vector>(i32, 2u); auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* vec4_f32 = create<sem::Vector>(f32, 4u); auto* vec4_f32 = create<sem::Vector>(f32, 4u);
auto* tex = create<sem::ExternalTexture>(); auto* tex = create<sem::ExternalTexture>();
auto* result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32}, Source{}); auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureLoad); EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result->ReturnType(), vec4_f32); EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
ASSERT_EQ(result->Parameters().size(), 2u); ASSERT_EQ(result.sem->Parameters().size(), 2u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex); EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture); EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), vec2_i32); EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kCoords); EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kCoords);
} }
TEST_F(IntrinsicTableTest, MatchWOStorageTexture) { TEST_F(IntrinsicTableTest, MatchWOStorageTexture) {
@ -389,83 +389,83 @@ TEST_F(IntrinsicTableTest, MatchWOStorageTexture) {
auto* tex = create<sem::StorageTexture>(ast::TextureDimension::k2d, ast::TexelFormat::kR32Float, auto* tex = create<sem::StorageTexture>(ast::TextureDimension::k2d, ast::TexelFormat::kR32Float,
ast::Access::kWrite, subtype); ast::Access::kWrite, subtype);
auto* result = table->Lookup(BuiltinType::kTextureStore, {tex, vec2_i32, vec4_f32}, Source{}); auto result = table->Lookup(BuiltinType::kTextureStore, {tex, vec2_i32, vec4_f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureStore); EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureStore);
EXPECT_TRUE(result->ReturnType()->Is<sem::Void>()); EXPECT_TRUE(result.sem->ReturnType()->Is<sem::Void>());
ASSERT_EQ(result->Parameters().size(), 3u); ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex); EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture); EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), vec2_i32); EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kCoords); EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result->Parameters()[2]->Type(), vec4_f32); EXPECT_EQ(result.sem->Parameters()[2]->Type(), vec4_f32);
EXPECT_EQ(result->Parameters()[2]->Usage(), ParameterUsage::kValue); EXPECT_EQ(result.sem->Parameters()[2]->Usage(), ParameterUsage::kValue);
} }
TEST_F(IntrinsicTableTest, MismatchTexture) { TEST_F(IntrinsicTableTest, MismatchTexture) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* i32 = create<sem::I32>(); auto* i32 = create<sem::I32>();
auto* vec2_i32 = create<sem::Vector>(i32, 2u); auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* result = table->Lookup(BuiltinType::kTextureLoad, {f32, vec2_i32}, Source{}); auto result = table->Lookup(BuiltinType::kTextureLoad, {f32, vec2_i32}, Source{});
ASSERT_EQ(result, nullptr); ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call")); ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
} }
TEST_F(IntrinsicTableTest, ImplicitLoadOnReference) { TEST_F(IntrinsicTableTest, ImplicitLoadOnReference) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* result = table->Lookup( auto result = table->Lookup(
BuiltinType::kCos, BuiltinType::kCos,
{create<sem::Reference>(f32, ast::StorageClass::kFunction, ast::Access::kReadWrite)}, {create<sem::Reference>(f32, ast::StorageClass::kFunction, ast::Access::kReadWrite)},
Source{}); Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kCos); EXPECT_EQ(result.sem->Type(), BuiltinType::kCos);
EXPECT_EQ(result->ReturnType(), f32); EXPECT_EQ(result.sem->ReturnType(), f32);
ASSERT_EQ(result->Parameters().size(), 1u); ASSERT_EQ(result.sem->Parameters().size(), 1u);
EXPECT_EQ(result->Parameters()[0]->Type(), f32); EXPECT_EQ(result.sem->Parameters()[0]->Type(), f32);
} }
TEST_F(IntrinsicTableTest, MatchTemplateType) { TEST_F(IntrinsicTableTest, MatchTemplateType) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kClamp, {f32, f32, f32}, Source{}); auto result = table->Lookup(BuiltinType::kClamp, {f32, f32, f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kClamp); EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
EXPECT_EQ(result->ReturnType(), f32); EXPECT_EQ(result.sem->ReturnType(), f32);
EXPECT_EQ(result->Parameters()[0]->Type(), f32); EXPECT_EQ(result.sem->Parameters()[0]->Type(), f32);
EXPECT_EQ(result->Parameters()[1]->Type(), f32); EXPECT_EQ(result.sem->Parameters()[1]->Type(), f32);
EXPECT_EQ(result->Parameters()[2]->Type(), f32); EXPECT_EQ(result.sem->Parameters()[2]->Type(), f32);
} }
TEST_F(IntrinsicTableTest, MismatchTemplateType) { TEST_F(IntrinsicTableTest, MismatchTemplateType) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* u32 = create<sem::U32>(); auto* u32 = create<sem::U32>();
auto* result = table->Lookup(BuiltinType::kClamp, {f32, u32, f32}, Source{}); auto result = table->Lookup(BuiltinType::kClamp, {f32, u32, f32}, Source{});
ASSERT_EQ(result, nullptr); ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call")); ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
} }
TEST_F(IntrinsicTableTest, MatchOpenSizeVector) { TEST_F(IntrinsicTableTest, MatchOpenSizeVector) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u); auto* vec2_f32 = create<sem::Vector>(f32, 2u);
auto* result = table->Lookup(BuiltinType::kClamp, {vec2_f32, vec2_f32, vec2_f32}, Source{}); auto result = table->Lookup(BuiltinType::kClamp, {vec2_f32, vec2_f32, vec2_f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kClamp); EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
EXPECT_EQ(result->ReturnType(), vec2_f32); EXPECT_EQ(result.sem->ReturnType(), vec2_f32);
ASSERT_EQ(result->Parameters().size(), 3u); ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), vec2_f32); EXPECT_EQ(result.sem->Parameters()[0]->Type(), vec2_f32);
EXPECT_EQ(result->Parameters()[1]->Type(), vec2_f32); EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_f32);
EXPECT_EQ(result->Parameters()[2]->Type(), vec2_f32); EXPECT_EQ(result.sem->Parameters()[2]->Type(), vec2_f32);
} }
TEST_F(IntrinsicTableTest, MismatchOpenSizeVector) { TEST_F(IntrinsicTableTest, MismatchOpenSizeVector) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* u32 = create<sem::U32>(); auto* u32 = create<sem::U32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u); auto* vec2_f32 = create<sem::Vector>(f32, 2u);
auto* result = table->Lookup(BuiltinType::kClamp, {vec2_f32, u32, vec2_f32}, Source{}); auto result = table->Lookup(BuiltinType::kClamp, {vec2_f32, u32, vec2_f32}, Source{});
ASSERT_EQ(result, nullptr); ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call")); ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
} }
@ -473,21 +473,21 @@ TEST_F(IntrinsicTableTest, MatchOpenSizeMatrix) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* vec3_f32 = create<sem::Vector>(f32, 3u); auto* vec3_f32 = create<sem::Vector>(f32, 3u);
auto* mat3_f32 = create<sem::Matrix>(vec3_f32, 3u); auto* mat3_f32 = create<sem::Matrix>(vec3_f32, 3u);
auto* result = table->Lookup(BuiltinType::kDeterminant, {mat3_f32}, Source{}); auto result = table->Lookup(BuiltinType::kDeterminant, {mat3_f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str(); ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kDeterminant); EXPECT_EQ(result.sem->Type(), BuiltinType::kDeterminant);
EXPECT_EQ(result->ReturnType(), f32); EXPECT_EQ(result.sem->ReturnType(), f32);
ASSERT_EQ(result->Parameters().size(), 1u); ASSERT_EQ(result.sem->Parameters().size(), 1u);
EXPECT_EQ(result->Parameters()[0]->Type(), mat3_f32); EXPECT_EQ(result.sem->Parameters()[0]->Type(), mat3_f32);
} }
TEST_F(IntrinsicTableTest, MismatchOpenSizeMatrix) { TEST_F(IntrinsicTableTest, MismatchOpenSizeMatrix) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u); auto* vec2_f32 = create<sem::Vector>(f32, 2u);
auto* mat3x2_f32 = create<sem::Matrix>(vec2_f32, 3u); auto* mat3x2_f32 = create<sem::Matrix>(vec2_f32, 3u);
auto* result = table->Lookup(BuiltinType::kDeterminant, {mat3x2_f32}, Source{}); auto result = table->Lookup(BuiltinType::kDeterminant, {mat3x2_f32}, Source{});
ASSERT_EQ(result, nullptr); ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call")); ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
} }
@ -572,20 +572,20 @@ TEST_F(IntrinsicTableTest, SameOverloadReturnsSameBuiltinPointer) {
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
auto* vec2_f32 = create<sem::Vector>(create<sem::F32>(), 2u); auto* vec2_f32 = create<sem::Vector>(create<sem::F32>(), 2u);
auto* bool_ = create<sem::Bool>(); auto* bool_ = create<sem::Bool>();
auto* a = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{}); auto a = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{});
ASSERT_NE(a, nullptr) << Diagnostics().str(); ASSERT_NE(a.sem, nullptr) << Diagnostics().str();
auto* b = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{}); auto b = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{});
ASSERT_NE(b, nullptr) << Diagnostics().str(); ASSERT_NE(b.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
auto* c = table->Lookup(BuiltinType::kSelect, {vec2_f32, vec2_f32, bool_}, Source{}); auto c = table->Lookup(BuiltinType::kSelect, {vec2_f32, vec2_f32, bool_}, Source{});
ASSERT_NE(c, nullptr) << Diagnostics().str(); ASSERT_NE(c.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), ""); ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(a, b); EXPECT_EQ(a.sem, b.sem);
EXPECT_NE(a, c); EXPECT_NE(a.sem, c.sem);
EXPECT_NE(b, c); EXPECT_NE(b.sem, c.sem);
} }
TEST_F(IntrinsicTableTest, MatchUnaryOp) { TEST_F(IntrinsicTableTest, MatchUnaryOp) {
@ -788,8 +788,8 @@ TEST_F(IntrinsicTableTest, MismatchTypeConversion) {
TEST_F(IntrinsicTableTest, Err257Arguments) { // crbug.com/1323605 TEST_F(IntrinsicTableTest, Err257Arguments) { // crbug.com/1323605
auto* f32 = create<sem::F32>(); auto* f32 = create<sem::F32>();
std::vector<const sem::Type*> arg_tys(257, f32); std::vector<const sem::Type*> arg_tys(257, f32);
auto* result = table->Lookup(BuiltinType::kAbs, std::move(arg_tys), Source{}); auto result = table->Lookup(BuiltinType::kAbs, std::move(arg_tys), Source{});
ASSERT_EQ(result, nullptr); ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call")); ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
} }
@ -1025,26 +1025,25 @@ TEST_P(IntrinsicTableAbstractTernaryTest, MatchClamp) {
auto* arg_a = GetParam().arg_a(*this); auto* arg_a = GetParam().arg_a(*this);
auto* arg_b = GetParam().arg_b(*this); auto* arg_b = GetParam().arg_b(*this);
auto* arg_c = GetParam().arg_c(*this); auto* arg_c = GetParam().arg_c(*this);
auto* builtin = auto builtin = table->Lookup(sem::BuiltinType::kClamp, {arg_a, arg_b, arg_c}, Source{{12, 34}});
table->Lookup(sem::BuiltinType::kClamp, {arg_a, arg_b, arg_c}, Source{{12, 34}});
bool matched = builtin != nullptr; bool matched = builtin.sem != nullptr;
bool expected_match = GetParam().expected_match; bool expected_match = GetParam().expected_match;
EXPECT_EQ(matched, expected_match) << Diagnostics().str(); EXPECT_EQ(matched, expected_match) << Diagnostics().str();
auto* result = builtin ? builtin->ReturnType() : nullptr; auto* result = builtin.sem ? builtin.sem->ReturnType() : nullptr;
auto* expected_result = GetParam().expected_result(*this); auto* expected_result = GetParam().expected_result(*this);
EXPECT_TYPE(result, expected_result); EXPECT_TYPE(result, expected_result);
auto* param_a = builtin ? builtin->Parameters()[0]->Type() : nullptr; auto* param_a = builtin.sem ? builtin.sem->Parameters()[0]->Type() : nullptr;
auto* expected_param_a = GetParam().expected_param_a(*this); auto* expected_param_a = GetParam().expected_param_a(*this);
EXPECT_TYPE(param_a, expected_param_a); EXPECT_TYPE(param_a, expected_param_a);
auto* param_b = builtin ? builtin->Parameters()[1]->Type() : nullptr; auto* param_b = builtin.sem ? builtin.sem->Parameters()[1]->Type() : nullptr;
auto* expected_param_b = GetParam().expected_param_b(*this); auto* expected_param_b = GetParam().expected_param_b(*this);
EXPECT_TYPE(param_b, expected_param_b); EXPECT_TYPE(param_b, expected_param_b);
auto* param_c = builtin ? builtin->Parameters()[2]->Type() : nullptr; auto* param_c = builtin.sem ? builtin.sem->Parameters()[2]->Type() : nullptr;
auto* expected_param_c = GetParam().expected_param_c(*this); auto* expected_param_c = GetParam().expected_param_c(*this);
EXPECT_TYPE(param_c, expected_param_c); EXPECT_TYPE(param_c, expected_param_c);
} }

View File

@ -1492,30 +1492,48 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
sem::Call* Resolver::BuiltinCall(const ast::CallExpression* expr, sem::Call* Resolver::BuiltinCall(const ast::CallExpression* expr,
sem::BuiltinType builtin_type, sem::BuiltinType builtin_type,
std::vector<const sem::Expression*> args) { std::vector<const sem::Expression*> args) {
const sem::Builtin* builtin = nullptr; IntrinsicTable::Builtin builtin;
{ {
auto arg_tys = utils::Transform(args, [](auto* arg) { return arg->Type(); }); auto arg_tys = utils::Transform(args, [](auto* arg) { return arg->Type(); });
builtin = intrinsic_table_->Lookup(builtin_type, arg_tys, expr->source); builtin = intrinsic_table_->Lookup(builtin_type, arg_tys, expr->source);
if (!builtin) { if (!builtin.sem) {
return nullptr; return nullptr;
} }
} }
if (!MaterializeArguments(args, builtin)) { if (!MaterializeArguments(args, builtin.sem)) {
return nullptr; return nullptr;
} }
if (builtin->IsDeprecated()) { if (builtin.sem->IsDeprecated()) {
AddWarning("use of deprecated builtin", expr->source); AddWarning("use of deprecated builtin", expr->source);
} }
bool has_side_effects = // If the builtin is @const, and all arguments have constant values, evaluate the builtin now.
builtin->HasSideEffects() || sem::Constant constant;
std::any_of(args.begin(), args.end(), [](auto* e) { return e->HasSideEffects(); }); if (builtin.const_eval_fn) {
auto* call = builder_->create<sem::Call>(expr, builtin, std::move(args), current_statement_, std::vector<sem::Constant> values(args.size());
sem::Constant{}, has_side_effects); bool is_const = true; // all arguments have constant values
for (size_t i = 0; i < values.size(); i++) {
if (auto v = args[i]->ConstantValue()) {
values[i] = std::move(v);
} else {
is_const = false;
break;
}
}
if (is_const) {
constant = builtin.const_eval_fn(*builder_, values.data(), args.size());
}
}
current_function_->AddDirectlyCalledBuiltin(builtin); bool has_side_effects =
builtin.sem->HasSideEffects() ||
std::any_of(args.begin(), args.end(), [](auto* e) { return e->HasSideEffects(); });
auto* call = builder_->create<sem::Call>(expr, builtin.sem, std::move(args), current_statement_,
constant, has_side_effects);
current_function_->AddDirectlyCalledBuiltin(builtin.sem);
if (!validator_.RequiredExtensionForBuiltinFunction(call, enabled_extensions_)) { if (!validator_.RequiredExtensionForBuiltinFunction(call, enabled_extensions_)) {
return nullptr; return nullptr;
@ -1525,7 +1543,7 @@ sem::Call* Resolver::BuiltinCall(const ast::CallExpression* expr,
if (!validator_.TextureBuiltinFunction(call)) { if (!validator_.TextureBuiltinFunction(call)) {
return nullptr; return nullptr;
} }
CollectTextureSamplerPairs(builtin, call->Arguments()); CollectTextureSamplerPairs(builtin.sem, call->Arguments());
} }
if (!validator_.BuiltinCall(call)) { if (!validator_.BuiltinCall(call)) {

View File

@ -105,6 +105,8 @@ type Overload struct {
IsDeprecated bool IsDeprecated bool
// The kind of overload // The kind of overload
Kind string Kind string
// The function name used to evaluate the overload at shader-creation time
ConstEvalFunction string
} }
// Intrinsic is used to create the C++ IntrinsicInfo structure // Intrinsic is used to create the C++ IntrinsicInfo structure
@ -206,6 +208,7 @@ func (b *IntrinsicTableBuilder) buildOverload(o *sem.Overload) (Overload, error)
CanBeUsedInStage: o.CanBeUsedInStage, CanBeUsedInStage: o.CanBeUsedInStage,
IsDeprecated: o.IsDeprecated, IsDeprecated: o.IsDeprecated,
Kind: string(o.Decl.Kind), Kind: string(o.Decl.Kind),
ConstEvalFunction: o.ConstEvalFunction,
}, nil }, nil
} }

View File

@ -326,6 +326,16 @@ func (r *resolver) intrinsic(
Compute: true, Compute: true,
} }
} }
if constEvalFn := a.Attributes.Take("const"); constEvalFn != nil {
switch len(constEvalFn.Values) {
case 0:
overload.ConstEvalFunction = overload.Decl.Name
case 1:
overload.ConstEvalFunction = constEvalFn.Values[0]
default:
return fmt.Errorf("%v too many values for @const attribute", constEvalFn.Source)
}
}
if deprecated := a.Attributes.Take("deprecated"); deprecated != nil { if deprecated := a.Attributes.Take("deprecated"); deprecated != nil {
overload.IsDeprecated = true overload.IsDeprecated = true
if len(deprecated.Values) != 0 { if len(deprecated.Values) != 0 {

View File

@ -143,15 +143,16 @@ type Intrinsic struct {
// Overload describes a single overload of a builtin or operator // Overload describes a single overload of a builtin or operator
type Overload struct { type Overload struct {
Decl ast.IntrinsicDecl Decl ast.IntrinsicDecl
Intrinsic *Intrinsic Intrinsic *Intrinsic
TemplateParams []TemplateParam TemplateParams []TemplateParam
TemplateTypes []*TemplateTypeParam TemplateTypes []*TemplateTypeParam
TemplateNumbers []TemplateParam TemplateNumbers []TemplateParam
ReturnType *FullyQualifiedName ReturnType *FullyQualifiedName
Parameters []Parameter Parameters []Parameter
CanBeUsedInStage StageUses CanBeUsedInStage StageUses
IsDeprecated bool // True if this overload is deprecated IsDeprecated bool // True if this overload is deprecated
ConstEvalFunction string // Name of the function used to evaluate the intrinsic at shader creation time
} }
// StageUses describes the stages an overload can be used in // StageUses describes the stages an overload can be used in