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",
"reader/reader.cc",
"reader/reader.h",
"resolver/const_eval.cc",
"resolver/const_eval.h",
"resolver/ctor_conv_intrinsic.cc",
"resolver/ctor_conv_intrinsic.h",
"resolver/dependency_graph.cc",

View File

@ -249,6 +249,8 @@ set(TINT_LIB_SRCS
program.h
reader/reader.cc
reader/reader.h
resolver/const_eval.cc
resolver/const_eval.h
resolver/ctor_conv_intrinsic.cc
resolver/ctor_conv_intrinsic.h
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;
/// The flags for the overload
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
@ -905,7 +907,7 @@ class Impl : public IntrinsicTable {
public:
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 Source& source) override;
@ -1064,7 +1066,7 @@ std::string TemplateNumberMatcher::String(MatchState* state) const {
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 Source& source) {
const char* intrinsic_name = sem::str(builtin_type);
@ -1090,7 +1092,7 @@ const sem::Builtin* Impl::Lookup(sem::BuiltinType builtin_type,
}
// De-duplicate builtins that are identical.
return utils::GetOrCreate(builtins, match, [&] {
auto* sem = utils::GetOrCreate(builtins, match, [&] {
std::vector<sem::Parameter*> params;
params.reserve(match.parameters.size());
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,
match.overload->flags.Contains(OverloadFlag::kIsDeprecated));
});
return Builtin{sem, match.overload->const_eval_fn};
}
IntrinsicTable::UnaryOperator Impl::Lookup(ast::UnaryOp op,

View File

@ -19,6 +19,7 @@
#include <string>
#include <vector>
#include "src/tint/resolver/const_eval.h"
#include "src/tint/resolver/ctor_conv_intrinsic.h"
#include "src/tint/sem/builtin.h"
@ -39,22 +40,30 @@ class IntrinsicTable {
/// Destructor
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
struct UnaryOperator {
/// 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
const sem::Type* parameter;
const sem::Type* parameter = nullptr;
};
/// BinaryOperator describes a resolved binary operator
struct BinaryOperator {
/// 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
const sem::Type* lhs;
const sem::Type* lhs = nullptr;
/// 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
@ -63,7 +72,7 @@ class IntrinsicTable {
/// @param args the argument types passed to the builtin function
/// @param source the source of the builtin call
/// @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 Source& source) = 0;

File diff suppressed because it is too large Load Diff

View File

@ -102,6 +102,10 @@ constexpr OverloadInfo kOverloads[] = {
, OverloadFlag::kSupports{{Title $u}}Pipeline
{{- end }}
{{- if $o.IsDeprecated}}, OverloadFlag::kIsDeprecated{{end }}),
/* const eval */
{{- if $o.ConstEvalFunction }} const_eval::{{$o.ConstEvalFunction}},
{{- else }} nullptr,
{{- end }}
},
{{- end }}
};

View File

@ -53,19 +53,19 @@ class IntrinsicTableTest : public testing::Test, public ProgramBuilder {
TEST_F(IntrinsicTableTest, MatchF32) {
auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kCos, {f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kCos, {f32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kCos);
EXPECT_EQ(result->ReturnType(), f32);
ASSERT_EQ(result->Parameters().size(), 1u);
EXPECT_EQ(result->Parameters()[0]->Type(), f32);
EXPECT_EQ(result.sem->Type(), BuiltinType::kCos);
EXPECT_EQ(result.sem->ReturnType(), f32);
ASSERT_EQ(result.sem->Parameters().size(), 1u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), f32);
}
TEST_F(IntrinsicTableTest, MismatchF32) {
auto* i32 = create<sem::I32>();
auto* result = table->Lookup(BuiltinType::kCos, {i32}, Source{});
ASSERT_EQ(result, nullptr);
auto result = table->Lookup(BuiltinType::kCos, {i32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@ -73,19 +73,19 @@ TEST_F(IntrinsicTableTest, MatchU32) {
auto* f32 = create<sem::F32>();
auto* u32 = create<sem::U32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u);
auto* result = table->Lookup(BuiltinType::kUnpack2x16float, {u32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kUnpack2x16float, {u32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kUnpack2x16float);
EXPECT_EQ(result->ReturnType(), vec2_f32);
ASSERT_EQ(result->Parameters().size(), 1u);
EXPECT_EQ(result->Parameters()[0]->Type(), u32);
EXPECT_EQ(result.sem->Type(), BuiltinType::kUnpack2x16float);
EXPECT_EQ(result.sem->ReturnType(), vec2_f32);
ASSERT_EQ(result.sem->Parameters().size(), 1u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), u32);
}
TEST_F(IntrinsicTableTest, MismatchU32) {
auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kUnpack2x16float, {f32}, Source{});
ASSERT_EQ(result, nullptr);
auto result = table->Lookup(BuiltinType::kUnpack2x16float, {f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@ -94,121 +94,121 @@ TEST_F(IntrinsicTableTest, MatchI32) {
auto* i32 = create<sem::I32>();
auto* vec4_f32 = create<sem::Vector>(f32, 4u);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32);
auto* result = table->Lookup(BuiltinType::kTextureLoad, {tex, i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, i32, i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result->ReturnType(), vec4_f32);
ASSERT_EQ(result->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), i32);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result->Parameters()[2]->Type(), i32);
EXPECT_EQ(result->Parameters()[2]->Usage(), ParameterUsage::kLevel);
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), i32);
EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), i32);
EXPECT_EQ(result.sem->Parameters()[2]->Usage(), ParameterUsage::kLevel);
}
TEST_F(IntrinsicTableTest, MismatchI32) {
auto* f32 = create<sem::F32>();
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32);
auto* result = table->Lookup(BuiltinType::kTextureLoad, {tex, f32}, Source{});
ASSERT_EQ(result, nullptr);
auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchIU32AsI32) {
auto* i32 = create<sem::I32>();
auto* result = table->Lookup(BuiltinType::kCountOneBits, {i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kCountOneBits, {i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kCountOneBits);
EXPECT_EQ(result->ReturnType(), i32);
ASSERT_EQ(result->Parameters().size(), 1u);
EXPECT_EQ(result->Parameters()[0]->Type(), i32);
EXPECT_EQ(result.sem->Type(), BuiltinType::kCountOneBits);
EXPECT_EQ(result.sem->ReturnType(), i32);
ASSERT_EQ(result.sem->Parameters().size(), 1u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), i32);
}
TEST_F(IntrinsicTableTest, MatchIU32AsU32) {
auto* u32 = create<sem::U32>();
auto* result = table->Lookup(BuiltinType::kCountOneBits, {u32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kCountOneBits, {u32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kCountOneBits);
EXPECT_EQ(result->ReturnType(), u32);
ASSERT_EQ(result->Parameters().size(), 1u);
EXPECT_EQ(result->Parameters()[0]->Type(), u32);
EXPECT_EQ(result.sem->Type(), BuiltinType::kCountOneBits);
EXPECT_EQ(result.sem->ReturnType(), u32);
ASSERT_EQ(result.sem->Parameters().size(), 1u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), u32);
}
TEST_F(IntrinsicTableTest, MismatchIU32) {
auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kCountOneBits, {f32}, Source{});
ASSERT_EQ(result, nullptr);
auto result = table->Lookup(BuiltinType::kCountOneBits, {f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchFIU32AsI32) {
auto* i32 = create<sem::I32>();
auto* result = table->Lookup(BuiltinType::kClamp, {i32, i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kClamp, {i32, i32, i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kClamp);
EXPECT_EQ(result->ReturnType(), i32);
ASSERT_EQ(result->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), i32);
EXPECT_EQ(result->Parameters()[1]->Type(), i32);
EXPECT_EQ(result->Parameters()[2]->Type(), i32);
EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
EXPECT_EQ(result.sem->ReturnType(), i32);
ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), i32);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), i32);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), i32);
}
TEST_F(IntrinsicTableTest, MatchFIU32AsU32) {
auto* u32 = create<sem::U32>();
auto* result = table->Lookup(BuiltinType::kClamp, {u32, u32, u32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kClamp, {u32, u32, u32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kClamp);
EXPECT_EQ(result->ReturnType(), u32);
ASSERT_EQ(result->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), u32);
EXPECT_EQ(result->Parameters()[1]->Type(), u32);
EXPECT_EQ(result->Parameters()[2]->Type(), u32);
EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
EXPECT_EQ(result.sem->ReturnType(), u32);
ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), u32);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), u32);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), u32);
}
TEST_F(IntrinsicTableTest, MatchFIU32AsF32) {
auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kClamp, {f32, f32, f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kClamp, {f32, f32, f32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kClamp);
EXPECT_EQ(result->ReturnType(), f32);
ASSERT_EQ(result->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), f32);
EXPECT_EQ(result->Parameters()[1]->Type(), f32);
EXPECT_EQ(result->Parameters()[2]->Type(), f32);
EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
EXPECT_EQ(result.sem->ReturnType(), f32);
ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), f32);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), f32);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), f32);
}
TEST_F(IntrinsicTableTest, MismatchFIU32) {
auto* bool_ = create<sem::Bool>();
auto* result = table->Lookup(BuiltinType::kClamp, {bool_, bool_, bool_}, Source{});
ASSERT_EQ(result, nullptr);
auto result = table->Lookup(BuiltinType::kClamp, {bool_, bool_, bool_}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchBool) {
auto* f32 = create<sem::F32>();
auto* bool_ = create<sem::Bool>();
auto* result = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kSelect);
EXPECT_EQ(result->ReturnType(), f32);
ASSERT_EQ(result->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), f32);
EXPECT_EQ(result->Parameters()[1]->Type(), f32);
EXPECT_EQ(result->Parameters()[2]->Type(), bool_);
EXPECT_EQ(result.sem->Type(), BuiltinType::kSelect);
EXPECT_EQ(result.sem->ReturnType(), f32);
ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), f32);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), f32);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), bool_);
}
TEST_F(IntrinsicTableTest, MismatchBool) {
auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kSelect, {f32, f32, f32}, Source{});
ASSERT_EQ(result, nullptr);
auto result = table->Lookup(BuiltinType::kSelect, {f32, f32, f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@ -217,41 +217,41 @@ TEST_F(IntrinsicTableTest, MatchPointer) {
auto* atomicI32 = create<sem::Atomic>(i32);
auto* ptr =
create<sem::Pointer>(atomicI32, ast::StorageClass::kWorkgroup, ast::Access::kReadWrite);
auto* result = table->Lookup(BuiltinType::kAtomicLoad, {ptr}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kAtomicLoad, {ptr}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kAtomicLoad);
EXPECT_EQ(result->ReturnType(), i32);
ASSERT_EQ(result->Parameters().size(), 1u);
EXPECT_EQ(result->Parameters()[0]->Type(), ptr);
EXPECT_EQ(result.sem->Type(), BuiltinType::kAtomicLoad);
EXPECT_EQ(result.sem->ReturnType(), i32);
ASSERT_EQ(result.sem->Parameters().size(), 1u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), ptr);
}
TEST_F(IntrinsicTableTest, MismatchPointer) {
auto* i32 = create<sem::I32>();
auto* atomicI32 = create<sem::Atomic>(i32);
auto* result = table->Lookup(BuiltinType::kAtomicLoad, {atomicI32}, Source{});
ASSERT_EQ(result, nullptr);
auto result = table->Lookup(BuiltinType::kAtomicLoad, {atomicI32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchArray) {
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* result = table->Lookup(BuiltinType::kArrayLength, {arr_ptr}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kArrayLength, {arr_ptr}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kArrayLength);
EXPECT_TRUE(result->ReturnType()->Is<sem::U32>());
ASSERT_EQ(result->Parameters().size(), 1u);
auto* param_type = result->Parameters()[0]->Type();
EXPECT_EQ(result.sem->Type(), BuiltinType::kArrayLength);
EXPECT_TRUE(result.sem->ReturnType()->Is<sem::U32>());
ASSERT_EQ(result.sem->Parameters().size(), 1u);
auto* param_type = result.sem->Parameters()[0]->Type();
ASSERT_TRUE(param_type->Is<sem::Pointer>());
EXPECT_TRUE(param_type->As<sem::Pointer>()->StoreType()->Is<sem::Array>());
}
TEST_F(IntrinsicTableTest, MismatchArray) {
auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kArrayLength, {f32}, Source{});
ASSERT_EQ(result, nullptr);
auto result = table->Lookup(BuiltinType::kArrayLength, {f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
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* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
auto* sampler = create<sem::Sampler>(ast::SamplerKind::kSampler);
auto* result = table->Lookup(BuiltinType::kTextureSample, {tex, sampler, vec2_f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kTextureSample, {tex, sampler, vec2_f32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureSample);
EXPECT_EQ(result->ReturnType(), vec4_f32);
ASSERT_EQ(result->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), sampler);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kSampler);
EXPECT_EQ(result->Parameters()[2]->Type(), vec2_f32);
EXPECT_EQ(result->Parameters()[2]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureSample);
EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), sampler);
EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kSampler);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), vec2_f32);
EXPECT_EQ(result.sem->Parameters()[2]->Usage(), ParameterUsage::kCoords);
}
TEST_F(IntrinsicTableTest, MismatchSampler) {
auto* f32 = create<sem::F32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
auto* result = table->Lookup(BuiltinType::kTextureSample, {tex, f32, vec2_f32}, Source{});
ASSERT_EQ(result, nullptr);
auto result = table->Lookup(BuiltinType::kTextureSample, {tex, f32, vec2_f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
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* vec4_f32 = create<sem::Vector>(f32, 4u);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
auto* result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result->ReturnType(), vec4_f32);
ASSERT_EQ(result->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result->Parameters()[2]->Type(), i32);
EXPECT_EQ(result->Parameters()[2]->Usage(), ParameterUsage::kLevel);
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), i32);
EXPECT_EQ(result.sem->Parameters()[2]->Usage(), ParameterUsage::kLevel);
}
TEST_F(IntrinsicTableTest, MatchMultisampledTexture) {
@ -310,18 +310,18 @@ TEST_F(IntrinsicTableTest, MatchMultisampledTexture) {
auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* vec4_f32 = create<sem::Vector>(f32, 4u);
auto* tex = create<sem::MultisampledTexture>(ast::TextureDimension::k2d, f32);
auto* result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result->ReturnType(), vec4_f32);
ASSERT_EQ(result->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result->Parameters()[2]->Type(), i32);
EXPECT_EQ(result->Parameters()[2]->Usage(), ParameterUsage::kSampleIndex);
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), i32);
EXPECT_EQ(result.sem->Parameters()[2]->Usage(), ParameterUsage::kSampleIndex);
}
TEST_F(IntrinsicTableTest, MatchDepthTexture) {
@ -329,18 +329,18 @@ TEST_F(IntrinsicTableTest, MatchDepthTexture) {
auto* i32 = create<sem::I32>();
auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* tex = create<sem::DepthTexture>(ast::TextureDimension::k2d);
auto* result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result->ReturnType(), f32);
ASSERT_EQ(result->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result->Parameters()[2]->Type(), i32);
EXPECT_EQ(result->Parameters()[2]->Usage(), ParameterUsage::kLevel);
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result.sem->ReturnType(), f32);
ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), i32);
EXPECT_EQ(result.sem->Parameters()[2]->Usage(), ParameterUsage::kLevel);
}
TEST_F(IntrinsicTableTest, MatchDepthMultisampledTexture) {
@ -348,18 +348,18 @@ TEST_F(IntrinsicTableTest, MatchDepthMultisampledTexture) {
auto* i32 = create<sem::I32>();
auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* tex = create<sem::DepthMultisampledTexture>(ast::TextureDimension::k2d);
auto* result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result->ReturnType(), f32);
ASSERT_EQ(result->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result->Parameters()[2]->Type(), i32);
EXPECT_EQ(result->Parameters()[2]->Usage(), ParameterUsage::kSampleIndex);
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result.sem->ReturnType(), f32);
ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), i32);
EXPECT_EQ(result.sem->Parameters()[2]->Usage(), ParameterUsage::kSampleIndex);
}
TEST_F(IntrinsicTableTest, MatchExternalTexture) {
@ -368,16 +368,16 @@ TEST_F(IntrinsicTableTest, MatchExternalTexture) {
auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* vec4_f32 = create<sem::Vector>(f32, 4u);
auto* tex = create<sem::ExternalTexture>();
auto* result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result->ReturnType(), vec4_f32);
ASSERT_EQ(result->Parameters().size(), 2u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
ASSERT_EQ(result.sem->Parameters().size(), 2u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kCoords);
}
TEST_F(IntrinsicTableTest, MatchWOStorageTexture) {
@ -389,83 +389,83 @@ TEST_F(IntrinsicTableTest, MatchWOStorageTexture) {
auto* tex = create<sem::StorageTexture>(ast::TextureDimension::k2d, ast::TexelFormat::kR32Float,
ast::Access::kWrite, subtype);
auto* result = table->Lookup(BuiltinType::kTextureStore, {tex, vec2_i32, vec4_f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kTextureStore, {tex, vec2_i32, vec4_f32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kTextureStore);
EXPECT_TRUE(result->ReturnType()->Is<sem::Void>());
ASSERT_EQ(result->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), tex);
EXPECT_EQ(result->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result->Parameters()[2]->Type(), vec4_f32);
EXPECT_EQ(result->Parameters()[2]->Usage(), ParameterUsage::kValue);
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureStore);
EXPECT_TRUE(result.sem->ReturnType()->Is<sem::Void>());
ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
EXPECT_EQ(result.sem->Parameters()[1]->Usage(), ParameterUsage::kCoords);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), vec4_f32);
EXPECT_EQ(result.sem->Parameters()[2]->Usage(), ParameterUsage::kValue);
}
TEST_F(IntrinsicTableTest, MismatchTexture) {
auto* f32 = create<sem::F32>();
auto* i32 = create<sem::I32>();
auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* result = table->Lookup(BuiltinType::kTextureLoad, {f32, vec2_i32}, Source{});
ASSERT_EQ(result, nullptr);
auto result = table->Lookup(BuiltinType::kTextureLoad, {f32, vec2_i32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, ImplicitLoadOnReference) {
auto* f32 = create<sem::F32>();
auto* result = table->Lookup(
auto result = table->Lookup(
BuiltinType::kCos,
{create<sem::Reference>(f32, ast::StorageClass::kFunction, ast::Access::kReadWrite)},
Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kCos);
EXPECT_EQ(result->ReturnType(), f32);
ASSERT_EQ(result->Parameters().size(), 1u);
EXPECT_EQ(result->Parameters()[0]->Type(), f32);
EXPECT_EQ(result.sem->Type(), BuiltinType::kCos);
EXPECT_EQ(result.sem->ReturnType(), f32);
ASSERT_EQ(result.sem->Parameters().size(), 1u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), f32);
}
TEST_F(IntrinsicTableTest, MatchTemplateType) {
auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kClamp, {f32, f32, f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kClamp, {f32, f32, f32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kClamp);
EXPECT_EQ(result->ReturnType(), f32);
EXPECT_EQ(result->Parameters()[0]->Type(), f32);
EXPECT_EQ(result->Parameters()[1]->Type(), f32);
EXPECT_EQ(result->Parameters()[2]->Type(), f32);
EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
EXPECT_EQ(result.sem->ReturnType(), f32);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), f32);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), f32);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), f32);
}
TEST_F(IntrinsicTableTest, MismatchTemplateType) {
auto* f32 = create<sem::F32>();
auto* u32 = create<sem::U32>();
auto* result = table->Lookup(BuiltinType::kClamp, {f32, u32, f32}, Source{});
ASSERT_EQ(result, nullptr);
auto result = table->Lookup(BuiltinType::kClamp, {f32, u32, f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchOpenSizeVector) {
auto* f32 = create<sem::F32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u);
auto* result = table->Lookup(BuiltinType::kClamp, {vec2_f32, vec2_f32, vec2_f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kClamp, {vec2_f32, vec2_f32, vec2_f32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kClamp);
EXPECT_EQ(result->ReturnType(), vec2_f32);
ASSERT_EQ(result->Parameters().size(), 3u);
EXPECT_EQ(result->Parameters()[0]->Type(), vec2_f32);
EXPECT_EQ(result->Parameters()[1]->Type(), vec2_f32);
EXPECT_EQ(result->Parameters()[2]->Type(), vec2_f32);
EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
EXPECT_EQ(result.sem->ReturnType(), vec2_f32);
ASSERT_EQ(result.sem->Parameters().size(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), vec2_f32);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_f32);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), vec2_f32);
}
TEST_F(IntrinsicTableTest, MismatchOpenSizeVector) {
auto* f32 = create<sem::F32>();
auto* u32 = create<sem::U32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u);
auto* result = table->Lookup(BuiltinType::kClamp, {vec2_f32, u32, vec2_f32}, Source{});
ASSERT_EQ(result, nullptr);
auto result = table->Lookup(BuiltinType::kClamp, {vec2_f32, u32, vec2_f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@ -473,21 +473,21 @@ TEST_F(IntrinsicTableTest, MatchOpenSizeMatrix) {
auto* f32 = create<sem::F32>();
auto* vec3_f32 = create<sem::Vector>(f32, 3u);
auto* mat3_f32 = create<sem::Matrix>(vec3_f32, 3u);
auto* result = table->Lookup(BuiltinType::kDeterminant, {mat3_f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
auto result = table->Lookup(BuiltinType::kDeterminant, {mat3_f32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result->Type(), BuiltinType::kDeterminant);
EXPECT_EQ(result->ReturnType(), f32);
ASSERT_EQ(result->Parameters().size(), 1u);
EXPECT_EQ(result->Parameters()[0]->Type(), mat3_f32);
EXPECT_EQ(result.sem->Type(), BuiltinType::kDeterminant);
EXPECT_EQ(result.sem->ReturnType(), f32);
ASSERT_EQ(result.sem->Parameters().size(), 1u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), mat3_f32);
}
TEST_F(IntrinsicTableTest, MismatchOpenSizeMatrix) {
auto* f32 = create<sem::F32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u);
auto* mat3x2_f32 = create<sem::Matrix>(vec2_f32, 3u);
auto* result = table->Lookup(BuiltinType::kDeterminant, {mat3x2_f32}, Source{});
ASSERT_EQ(result, nullptr);
auto result = table->Lookup(BuiltinType::kDeterminant, {mat3x2_f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@ -572,20 +572,20 @@ TEST_F(IntrinsicTableTest, SameOverloadReturnsSameBuiltinPointer) {
auto* f32 = create<sem::F32>();
auto* vec2_f32 = create<sem::Vector>(create<sem::F32>(), 2u);
auto* bool_ = create<sem::Bool>();
auto* a = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{});
ASSERT_NE(a, nullptr) << Diagnostics().str();
auto a = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{});
ASSERT_NE(a.sem, nullptr) << Diagnostics().str();
auto* b = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{});
ASSERT_NE(b, nullptr) << Diagnostics().str();
auto b = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{});
ASSERT_NE(b.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
auto* c = table->Lookup(BuiltinType::kSelect, {vec2_f32, vec2_f32, bool_}, Source{});
ASSERT_NE(c, nullptr) << Diagnostics().str();
auto c = table->Lookup(BuiltinType::kSelect, {vec2_f32, vec2_f32, bool_}, Source{});
ASSERT_NE(c.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(a, b);
EXPECT_NE(a, c);
EXPECT_NE(b, c);
EXPECT_EQ(a.sem, b.sem);
EXPECT_NE(a.sem, c.sem);
EXPECT_NE(b.sem, c.sem);
}
TEST_F(IntrinsicTableTest, MatchUnaryOp) {
@ -788,8 +788,8 @@ TEST_F(IntrinsicTableTest, MismatchTypeConversion) {
TEST_F(IntrinsicTableTest, Err257Arguments) { // crbug.com/1323605
auto* f32 = create<sem::F32>();
std::vector<const sem::Type*> arg_tys(257, f32);
auto* result = table->Lookup(BuiltinType::kAbs, std::move(arg_tys), Source{});
ASSERT_EQ(result, nullptr);
auto result = table->Lookup(BuiltinType::kAbs, std::move(arg_tys), Source{});
ASSERT_EQ(result.sem, nullptr);
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_b = GetParam().arg_b(*this);
auto* arg_c = GetParam().arg_c(*this);
auto* builtin =
table->Lookup(sem::BuiltinType::kClamp, {arg_a, arg_b, arg_c}, Source{{12, 34}});
auto builtin = 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;
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);
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);
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);
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);
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::BuiltinType builtin_type,
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(); });
builtin = intrinsic_table_->Lookup(builtin_type, arg_tys, expr->source);
if (!builtin) {
if (!builtin.sem) {
return nullptr;
}
}
if (!MaterializeArguments(args, builtin)) {
if (!MaterializeArguments(args, builtin.sem)) {
return nullptr;
}
if (builtin->IsDeprecated()) {
if (builtin.sem->IsDeprecated()) {
AddWarning("use of deprecated builtin", expr->source);
}
bool has_side_effects =
builtin->HasSideEffects() ||
std::any_of(args.begin(), args.end(), [](auto* e) { return e->HasSideEffects(); });
auto* call = builder_->create<sem::Call>(expr, builtin, std::move(args), current_statement_,
sem::Constant{}, has_side_effects);
// If the builtin is @const, and all arguments have constant values, evaluate the builtin now.
sem::Constant constant;
if (builtin.const_eval_fn) {
std::vector<sem::Constant> values(args.size());
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_)) {
return nullptr;
@ -1525,7 +1543,7 @@ sem::Call* Resolver::BuiltinCall(const ast::CallExpression* expr,
if (!validator_.TextureBuiltinFunction(call)) {
return nullptr;
}
CollectTextureSamplerPairs(builtin, call->Arguments());
CollectTextureSamplerPairs(builtin.sem, call->Arguments());
}
if (!validator_.BuiltinCall(call)) {

View File

@ -105,6 +105,8 @@ type Overload struct {
IsDeprecated bool
// The kind of overload
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
@ -206,6 +208,7 @@ func (b *IntrinsicTableBuilder) buildOverload(o *sem.Overload) (Overload, error)
CanBeUsedInStage: o.CanBeUsedInStage,
IsDeprecated: o.IsDeprecated,
Kind: string(o.Decl.Kind),
ConstEvalFunction: o.ConstEvalFunction,
}, nil
}

View File

@ -326,6 +326,16 @@ func (r *resolver) intrinsic(
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 {
overload.IsDeprecated = true
if len(deprecated.Values) != 0 {

View File

@ -152,6 +152,7 @@ type Overload struct {
Parameters []Parameter
CanBeUsedInStage StageUses
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