src: Reimplement IntrinsicTable from intrisics.def

Add a template file to generate intrinsic_table.inl and
include this from intrinsic_table.cc.

Speeds up execution of the unittests by 20 - 30%, and
reduces the executable size by a couple of percent.

Bug: tint:832
Change-Id: Ifa7f3c42202c92e97b46ed1716ece48660dd29dd
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/52504
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: David Neto <dneto@google.com>
This commit is contained in:
Ben Clayton
2021-06-01 19:06:31 +00:00
committed by Tint LUCI CQ
parent ae5437cc87
commit b29a59de6e
13 changed files with 9411 additions and 1636 deletions

View File

@@ -436,6 +436,7 @@ libtint_source_set("libtint_core_all_src") {
"inspector/scalar.h",
"intrinsic_table.cc",
"intrinsic_table.h",
"intrinsic_table.inl",
"program.cc",
"program.h",
"program_builder.cc",

View File

@@ -221,6 +221,7 @@ set(TINT_LIB_SRCS
inspector/scalar.h
intrinsic_table.cc
intrinsic_table.h
intrinsic_table.inl
program_builder.cc
program_builder.h
program_id.cc

File diff suppressed because it is too large Load Diff

View File

@@ -29,30 +29,23 @@ class ProgramBuilder;
/// IntrinsicTable is a lookup table of all the WGSL intrinsic functions
class IntrinsicTable {
public:
/// @param builder the program builder
/// @return a pointer to a newly created IntrinsicTable
static std::unique_ptr<IntrinsicTable> Create();
static std::unique_ptr<IntrinsicTable> Create(ProgramBuilder& builder);
/// Destructor
virtual ~IntrinsicTable();
/// Result is returned by Lookup
struct Result {
/// The intrinsic, if the lookup succeeded, otherwise nullptr
sem::Intrinsic* intrinsic;
/// Diagnostic messages
diag::List diagnostics;
};
/// Lookup looks for the intrinsic overload with the given signature.
/// @param builder the program builder
/// Lookup looks for the intrinsic overload with the given signature, raising
/// an error diagnostic if the intrinsic was not found.
/// @param type the intrinsic type
/// @param args the argument types passed to the intrinsic function
/// @param source the source of the intrinsic call
/// @return the semantic intrinsic if found, otherwise nullptr
virtual Result Lookup(ProgramBuilder& builder,
sem::IntrinsicType type,
const std::vector<const sem::Type*>& args,
const Source& source) const = 0;
virtual const sem::Intrinsic* Lookup(
sem::IntrinsicType type,
const std::vector<const sem::Type*>& args,
const Source& source) const = 0;
};
} // namespace tint

7551
src/intrinsic_table.inl Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,447 @@
{{- /*
--------------------------------------------------------------------------------
Template file for use with tools/intrinsic-gen to generate intrinsic_table.inl
Used by IntrinsicTable.cc for intrinsic overload resolution.
See:
* tools/cmd/intrinsic-gen/gen for structures used by this template
* https://golang.org/pkg/text/template/ for documentation on the template syntax
--------------------------------------------------------------------------------
*/ -}}
// clang-format off
/// ParameterInfo describes a parameter
struct ParameterInfo {
/// The parameter usage (parameter name in definition file)
ParameterUsage const usage;
/// Pointer to a list of indices that are used to match the parameter type.
/// The matcher indices index on Matchers::type and / or Matchers::number.
/// These indices are consumed by the matchers themselves.
/// The first index is always a TypeMatcher.
MatcherIndex const* const matcher_indices;
};
/// OpenTypeInfo describes an open type
struct OpenTypeInfo {
/// Name of the open type (e.g. 'T')
const char* name;
/// Optional type matcher constraint.
/// Either an index in Matchers::type, or kNoMatcher
MatcherIndex const matcher_index;
};
/// OpenNumberInfo describes an open number
struct OpenNumberInfo {
/// Name of the open number (e.g. 'N')
const char* name;
/// Optional number matcher constraint.
/// Either an index in Matchers::number, or kNoMatcher
MatcherIndex const matcher_index;
};
/// OverloadInfo describes a single function overload
struct OverloadInfo {
/// Total number of parameters for the overload
uint8_t const num_parameters;
/// Total number of open types for the overload
uint8_t const num_open_types;
/// Total number of open numbers for the overload
uint8_t const num_open_numbers;
/// Pointer to the first open type
OpenTypeInfo const* const open_types;
/// Pointer to the first open number
OpenNumberInfo const* const open_numbers;
/// Pointer to the first parameter
ParameterInfo const* const parameters;
/// Pointer to a list of matcher indices that index on Matchers::type and
/// Matchers::number, used to build the return type. If the function has no
/// return type then this is null.
MatcherIndex const* const return_matcher_indices;
};
/// IntrinsicInfo describes an intrinsic function
struct IntrinsicInfo {
/// Number of overloads of the intrinsic function
uint8_t const num_overloads;
/// Pointer to the start of the overloads for the function
OverloadInfo const* const overloads;
};
{{ with .Sem -}}
{{ range .Types -}}
{{ template "Type" . }}
{{ end -}}
{{ range .TypeMatchers -}}
{{ template "TypeMatcher" . }}
{{ end -}}
{{ range .EnumMatchers -}}
{{ template "EnumMatcher" . }}
{{ end -}}
{{- end -}}
{{- with IntrinsicTable -}}
{{- template "Matchers" . }}
constexpr MatcherIndex kMatcherIndices[] = {
{{- range $i, $idx := .MatcherIndices }}
/* [{{$i}}] */ {{$idx}},
{{- end }}
};
// Assert that the MatcherIndex is big enough to index all the matchers, plus
// kNoMatcher.
static_assert(static_cast<int>(sizeof(kMatcherIndices) / sizeof(kMatcherIndices[0])) <
static_cast<int>(std::numeric_limits<MatcherIndex>::max() - 1),
"MatcherIndex is not large enough to index kMatcherIndices");
constexpr ParameterInfo kParameters[] = {
{{- range $i, $p := .Parameters }}
{
/* [{{$i}}] */
/* usage */ ParameterUsage::
{{- if $p.Usage }}k{{PascalCase $p.Usage}}
{{- else }}kNone
{{- end }},
/* matcher indices */ &kMatcherIndices[{{$p.MatcherIndicesOffset}}],
},
{{- end }}
};
constexpr OpenTypeInfo kOpenTypes[] = {
{{- range $i, $o := .OpenTypes }}
{
/* [{{$i}}] */
/* name */ "{{$o.Name}}",
/* matcher index */
{{- if ge $o.MatcherIndex 0 }} {{$o.MatcherIndex}}
{{- else }} kNoMatcher
{{- end }},
},
{{- end }}
};
constexpr OpenNumberInfo kOpenNumbers[] = {
{{- range $i, $o := .OpenNumbers }}
{
/* [{{$i}}] */
/* name */ "{{$o.Name}}",
/* matcher index */
{{- if ge $o.MatcherIndex 0 }} {{$o.MatcherIndex}}
{{- else }} kNoMatcher
{{- end }},
},
{{- end }}
};
constexpr OverloadInfo kOverloads[] = {
{{- range $i, $o := .Overloads }}
{
/* [{{$i}}] */
/* num parameters */ {{$o.NumParameters}},
/* num open types */ {{$o.NumOpenTypes}},
/* num open numbers */ {{$o.NumOpenNumbers}},
/* open types */
{{- if $o.OpenTypesOffset }} &kOpenTypes[{{$o.OpenTypesOffset}}],
{{- else }} nullptr,
{{- end }}
/* open numbers */
{{- if $o.OpenNumbersOffset }} &kOpenNumbers[{{$o.OpenNumbersOffset}}]
{{- else }} nullptr
{{- end }},
/* parameters */ &kParameters[{{$o.ParametersOffset}}],
/* return matcher indices */
{{- if $o.ReturnMatcherIndicesOffset }} &kMatcherIndices[{{$o.ReturnMatcherIndicesOffset}}]
{{- else }} nullptr
{{- end }},
},
{{- end }}
};
constexpr IntrinsicInfo kIntrinsics[] = {
{{- range $i, $f := .Functions }}
{
/* [{{$i}}] */
{{- range $f.OverloadDescriptions }}
/* {{.}} */
{{- end }}
/* num overloads */ {{$f.NumOverloads}},
/* overloads */ &kOverloads[{{$f.OverloadsOffset}}],
},
{{- end }}
};
// clang-format on
{{ end -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- define "Type" -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- $class := PascalCase .Name -}}
/// TypeMatcher for 'type {{.Name}}'
{{- if .Decl.Source.S.Filepath }}
/// @see {{.Decl.Source}}
{{- end }}
class {{$class}} : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
/// Match may close open types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
const sem::Type* Match(MatchState& state,
const sem::Type* type) const override;
/// @param state the MatchState
/// @return a string representation of the matcher.
std::string String(MatchState& state) const override;
};
const sem::Type* {{$class}}::Match(MatchState& state, const sem::Type* ty) const {
{{- range .TemplateParams }}
{{- template "DeclareLocalTemplateParam" . }}
{{- end }}
if (!match_{{.Name}}(ty{{range .TemplateParams}}, {{.GetName}}{{end}})) {
return nullptr;
}
{{- range .TemplateParams }}
{{.Name}} = {{ template "MatchTemplateParam" .}}({{.Name}});
if ({{ template "IsTemplateParamInvalid" .}}) {
return nullptr;
}
{{- end }}
return build_{{.Name}}(state{{range .TemplateParams}}, {{.GetName}}{{end}});
}
std::string {{$class}}::String(MatchState&{{if .TemplateParams}} state{{end}}) const {
{{- range .TemplateParams }}
{{- template "DeclareLocalTemplateParamName" . }}
{{- end }}
{{- if .DisplayName }}
std::stringstream ss;
ss{{range SplitDisplayName .DisplayName}} << {{.}}{{end}};
return ss.str();
{{- else if .TemplateParams }}
return "{{.Name}}<"{{template "AppendTemplateParamNames" .TemplateParams}} + ">";
{{- else }}
return "{{.Name}}";
{{- end }}
}
{{ end -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- define "TypeMatcher" -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- $class := PascalCase .Name -}}
/// TypeMatcher for 'match {{.Name}}'
{{- if .Decl.Source.S.Filepath }}
/// @see {{.Decl.Source}}
{{- end }}
class {{$class}} : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
/// Match may close open types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
const sem::Type* Match(MatchState& state,
const sem::Type* type) const override;
/// @param state the MatchState
/// @return a string representation of the matcher.
std::string String(MatchState& state) const override;
};
const sem::Type* {{$class}}::Match(MatchState& state, const sem::Type* ty) const {
{{- range .Types }}
if (match_{{.Name}}(ty)) {
return build_{{.Name}}(state);
}
{{- end }}
return nullptr;
}
std::string {{$class}}::String(MatchState&) const {
return "
{{- range .Types -}}
{{- if IsFirstIn . $.Types }}{{.Name}}
{{- else if IsLastIn . $.Types }} or {{.Name}}
{{- else }}, {{.Name}}
{{- end -}}
{{- end -}}
";
}
{{ end -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- define "EnumMatcher" -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- $class := PascalCase .Name -}}
{{- $enum := PascalCase .Enum.Name -}}
/// EnumMatcher for 'match {{.Name}}'
{{- if .Decl.Source.S.Filepath }}
/// @see {{.Decl.Source}}
{{- end }}
class {{$class}} : public NumberMatcher {
public:
/// Checks whether the given number matches the enum matcher rules.
/// Match may close open types and numbers in state.
/// @param state the MatchState
/// @param number the enum value as a Number
/// @return true if the enum value matches the set
Number Match(MatchState& state, Number number) const override;
/// @param state the MatchState
/// @return a string representation of the matcher.
std::string String(MatchState& state) const override;
};
{{ if eq 1 (len .Options) -}}
{{- $option := index .Options 0 }}
{{- $entry := printf "k%v" (PascalCase $option.Name) -}}
Number {{$class}}::Match(MatchState&, Number number) const {
if (number.IsAny() || number.Value() == static_cast<uint32_t>({{$enum}}::{{$entry}})) {
return Number({{$enum}}::{{$entry}});
}
return Number::invalid;
}
{{- else -}}
Number {{$class}}::Match(MatchState&, Number number) const {
switch (static_cast<{{$enum}}>(number.Value())) {
{{- range .Options }}
case {{$enum}}::k{{PascalCase .Name}}:
{{- end }}
return number;
default:
return Number::invalid;
}
}
{{- end }}
std::string {{$class}}::String(MatchState&) const {
return "{{.Name}}";
}
{{ end -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- define "Matchers" -}}
{{- /* ------------------------------------------------------------------ */ -}}
/// Matchers holds type and number matchers
class Matchers {
private:
{{- $t_names := Map -}}
{{- $n_names := Map -}}
{{- range Iterate .Sem.MaxOpenTypes -}}
{{- $name := printf "open_type_%v" . -}}
{{- $t_names.Put . $name }}
OpenTypeMatcher {{$name}}_{ {{- . -}} };
{{- end }}
{{- range Iterate .Sem.MaxOpenNumbers -}}
{{- $name := printf "open_number_%v" . -}}
{{- $n_names.Put . $name }}
OpenNumberMatcher {{$name}}_{ {{- . -}} };
{{- end }}
{{- range .Sem.Types -}}
{{- $name := PascalCase .Name -}}
{{- $t_names.Put . $name }}
{{$name}} {{$name}}_;
{{- end }}
{{- range .Sem.TypeMatchers -}}
{{- $name := PascalCase .Name -}}
{{- $t_names.Put . $name }}
{{$name}} {{$name}}_;
{{- end }}
{{- range .Sem.EnumMatchers -}}
{{- $name := PascalCase .Name -}}
{{- $n_names.Put . $name }}
{{$name}} {{$name}}_;
{{- end }}
public:
/// Constructor
Matchers();
/// Destructor
~Matchers();
/// The open-types, types, and type matchers
TypeMatcher const* const type[{{len .TMatchers}}] = {
{{- range $i, $m := .TMatchers }}
/* [{{$i}}] */
{{- if $m }} &{{$t_names.Get $m}}_,
{{- else }} &{{$t_names.Get $i}}_,
{{- end }}
{{- end }}
};
/// The open-numbers, and number matchers
NumberMatcher const* const number[{{len .NMatchers}}] = {
{{- range $i, $m := .NMatchers }}
/* [{{$i}}] */
{{- if $m }} &{{$n_names.Get $m}}_,
{{- else }} &{{$n_names.Get $i}}_,
{{- end }}
{{- end }}
};
};
Matchers::Matchers() = default;
Matchers::~Matchers() = default;
{{- end -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- define "DeclareLocalTemplateParam" -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- if IsTemplateTypeParam . }}
const sem::Type* {{.Name}} = nullptr;
{{- else if IsTemplateNumberParam . }}
Number {{.Name}} = Number::invalid;
{{- else if IsTemplateEnumParam . }}
Number {{.Name}} = Number::invalid;
{{- end -}}
{{- end -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- define "DeclareLocalTemplateParamName" -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- if IsTemplateTypeParam . }}
const std::string {{.Name}} = state.TypeName();
{{- else if IsTemplateNumberParam . }}
const std::string {{.Name}} = state.NumName();
{{- else if IsTemplateEnumParam . }}
const std::string {{.Name}} = state.NumName();
{{- end -}}
{{- end -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- define "MatchTemplateParam" -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- if IsTemplateTypeParam . -}}
state.Type
{{- else if IsTemplateNumberParam . -}}
state.Num
{{- else if IsTemplateEnumParam . -}}
state.Num
{{- end -}}
{{- end -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- define "IsTemplateParamInvalid" -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- if IsTemplateTypeParam . -}}
{{.Name}} == nullptr
{{- else if IsTemplateNumberParam . -}}
!{{.Name}}.IsValid()
{{- else if IsTemplateEnumParam . -}}
!{{.Name}}.IsValid()
{{- end -}}
{{- end -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- define "AppendTemplateParamNames" -}}
{{- /* ------------------------------------------------------------------ */ -}}
{{- range $i, $ := . -}}
{{- if $i }} + ", " + {{.Name}}
{{- else }} + {{.Name}}
{{- end -}}
{{- end -}}
{{- end -}}

View File

@@ -35,45 +35,45 @@ using ParameterUsage = sem::ParameterUsage;
class IntrinsicTableTest : public testing::Test, public ProgramBuilder {
public:
std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create();
std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create(*this);
};
TEST_F(IntrinsicTableTest, MatchF32) {
auto* f32 = create<sem::F32>();
auto result = table->Lookup(*this, IntrinsicType::kCos, {f32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{f32}));
auto* result = table->Lookup(IntrinsicType::kCos, {f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kCos);
EXPECT_THAT(result->ReturnType(), f32);
EXPECT_THAT(result->Parameters(), ElementsAre(Parameter{f32}));
}
TEST_F(IntrinsicTableTest, MismatchF32) {
auto* i32 = create<sem::I32>();
auto result = table->Lookup(*this, IntrinsicType::kCos, {i32}, Source{});
ASSERT_EQ(result.intrinsic, nullptr);
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
auto* result = table->Lookup(IntrinsicType::kCos, {i32}, Source{});
ASSERT_EQ(result, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchU32) {
auto* f32 = create<sem::F32>();
auto* u32 = create<sem::U32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2);
auto result =
table->Lookup(*this, IntrinsicType::kUnpack2x16float, {u32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kUnpack2x16float);
EXPECT_THAT(result.intrinsic->ReturnType(), vec2_f32);
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{u32}));
auto* result =
table->Lookup(IntrinsicType::kUnpack2x16float, {u32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kUnpack2x16float);
EXPECT_THAT(result->ReturnType(), vec2_f32);
EXPECT_THAT(result->Parameters(), ElementsAre(Parameter{u32}));
}
TEST_F(IntrinsicTableTest, MismatchU32) {
auto* f32 = create<sem::F32>();
auto result =
table->Lookup(*this, IntrinsicType::kUnpack2x16float, {f32}, Source{});
ASSERT_EQ(result.intrinsic, nullptr);
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
auto* result =
table->Lookup(IntrinsicType::kUnpack2x16float, {f32}, Source{});
ASSERT_EQ(result, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchI32) {
@@ -81,13 +81,13 @@ TEST_F(IntrinsicTableTest, MatchI32) {
auto* i32 = create<sem::I32>();
auto* vec4_f32 = create<sem::Vector>(f32, 4);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32);
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
{tex, i32, i32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
EXPECT_THAT(result.intrinsic->ReturnType(), vec4_f32);
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result =
table->Lookup(IntrinsicType::kTextureLoad, {tex, i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kTextureLoad);
EXPECT_THAT(result->ReturnType(), vec4_f32);
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
Parameter{i32, ParameterUsage::kCoords},
Parameter{i32, ParameterUsage::kLevel}));
@@ -96,146 +96,139 @@ TEST_F(IntrinsicTableTest, MatchI32) {
TEST_F(IntrinsicTableTest, MismatchI32) {
auto* f32 = create<sem::F32>();
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32);
auto result =
table->Lookup(*this, IntrinsicType::kTextureLoad, {tex, f32}, Source{});
ASSERT_EQ(result.intrinsic, nullptr);
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
auto* result =
table->Lookup(IntrinsicType::kTextureLoad, {tex, f32}, Source{});
ASSERT_EQ(result, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchIU32AsI32) {
auto* i32 = create<sem::I32>();
auto result =
table->Lookup(*this, IntrinsicType::kCountOneBits, {i32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCountOneBits);
EXPECT_THAT(result.intrinsic->ReturnType(), i32);
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{i32}));
auto* result = table->Lookup(IntrinsicType::kCountOneBits, {i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kCountOneBits);
EXPECT_THAT(result->ReturnType(), i32);
EXPECT_THAT(result->Parameters(), ElementsAre(Parameter{i32}));
}
TEST_F(IntrinsicTableTest, MatchIU32AsU32) {
auto* u32 = create<sem::U32>();
auto result =
table->Lookup(*this, IntrinsicType::kCountOneBits, {u32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCountOneBits);
EXPECT_THAT(result.intrinsic->ReturnType(), u32);
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{u32}));
auto* result = table->Lookup(IntrinsicType::kCountOneBits, {u32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kCountOneBits);
EXPECT_THAT(result->ReturnType(), u32);
EXPECT_THAT(result->Parameters(), ElementsAre(Parameter{u32}));
}
TEST_F(IntrinsicTableTest, MismatchIU32) {
auto* f32 = create<sem::F32>();
auto result =
table->Lookup(*this, IntrinsicType::kCountOneBits, {f32}, Source{});
ASSERT_EQ(result.intrinsic, nullptr);
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
auto* result = table->Lookup(IntrinsicType::kCountOneBits, {f32}, Source{});
ASSERT_EQ(result, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchFIU32AsI32) {
auto* i32 = create<sem::I32>();
auto result =
table->Lookup(*this, IntrinsicType::kClamp, {i32, i32, i32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
EXPECT_THAT(result.intrinsic->ReturnType(), i32);
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result =
table->Lookup(IntrinsicType::kClamp, {i32, i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kClamp);
EXPECT_THAT(result->ReturnType(), i32);
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{i32}, Parameter{i32}, Parameter{i32}));
}
TEST_F(IntrinsicTableTest, MatchFIU32AsU32) {
auto* u32 = create<sem::U32>();
auto result =
table->Lookup(*this, IntrinsicType::kClamp, {u32, u32, u32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
EXPECT_THAT(result.intrinsic->ReturnType(), u32);
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result =
table->Lookup(IntrinsicType::kClamp, {u32, u32, u32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kClamp);
EXPECT_THAT(result->ReturnType(), u32);
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{u32}, Parameter{u32}, Parameter{u32}));
}
TEST_F(IntrinsicTableTest, MatchFIU32AsF32) {
auto* f32 = create<sem::F32>();
auto result =
table->Lookup(*this, IntrinsicType::kClamp, {f32, f32, f32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result =
table->Lookup(IntrinsicType::kClamp, {f32, f32, f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kClamp);
EXPECT_THAT(result->ReturnType(), f32);
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{f32}, Parameter{f32}, Parameter{f32}));
}
TEST_F(IntrinsicTableTest, MismatchFIU32) {
auto* bool_ = create<sem::Bool>();
auto result = table->Lookup(*this, IntrinsicType::kClamp,
{bool_, bool_, bool_}, Source{});
ASSERT_EQ(result.intrinsic, nullptr);
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
auto* result =
table->Lookup(IntrinsicType::kClamp, {bool_, bool_, bool_}, Source{});
ASSERT_EQ(result, 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(*this, IntrinsicType::kSelect, {f32, f32, bool_}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kSelect);
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result =
table->Lookup(IntrinsicType::kSelect, {f32, f32, bool_}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kSelect);
EXPECT_THAT(result->ReturnType(), f32);
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{f32}, Parameter{f32}, Parameter{bool_}));
}
TEST_F(IntrinsicTableTest, MismatchBool) {
auto* f32 = create<sem::F32>();
auto result =
table->Lookup(*this, IntrinsicType::kSelect, {f32, f32, f32}, Source{});
ASSERT_EQ(result.intrinsic, nullptr);
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
auto* result =
table->Lookup(IntrinsicType::kSelect, {f32, f32, f32}, Source{});
ASSERT_EQ(result, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchPointer) {
auto* f32 = create<sem::F32>();
auto* ptr = create<sem::Pointer>(f32, ast::StorageClass::kNone);
auto result =
table->Lookup(*this, IntrinsicType::kModf, {f32, ptr}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kModf);
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result = table->Lookup(IntrinsicType::kModf, {f32, ptr}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kModf);
EXPECT_THAT(result->ReturnType(), f32);
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{f32}, Parameter{ptr}));
}
TEST_F(IntrinsicTableTest, MismatchPointer) {
auto* f32 = create<sem::F32>();
auto result =
table->Lookup(*this, IntrinsicType::kModf, {f32, f32}, Source{});
ASSERT_EQ(result.intrinsic, nullptr);
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
auto* result = table->Lookup(IntrinsicType::kModf, {f32, f32}, Source{});
ASSERT_EQ(result, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchArray) {
auto* arr = create<sem::Array>(create<sem::U32>(), 0, 4, 4, 4, true);
auto result =
table->Lookup(*this, IntrinsicType::kArrayLength, {arr}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kArrayLength);
EXPECT_TRUE(result.intrinsic->ReturnType()->Is<sem::U32>());
ASSERT_EQ(result.intrinsic->Parameters().size(), 1u);
EXPECT_TRUE(result.intrinsic->Parameters()[0].type->Is<sem::Array>());
auto* result = table->Lookup(IntrinsicType::kArrayLength, {arr}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kArrayLength);
EXPECT_TRUE(result->ReturnType()->Is<sem::U32>());
ASSERT_EQ(result->Parameters().size(), 1u);
EXPECT_TRUE(result->Parameters()[0].type->Is<sem::Array>());
}
TEST_F(IntrinsicTableTest, MismatchArray) {
auto* f32 = create<sem::F32>();
auto result =
table->Lookup(*this, IntrinsicType::kArrayLength, {f32}, Source{});
ASSERT_EQ(result.intrinsic, nullptr);
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
auto* result = table->Lookup(IntrinsicType::kArrayLength, {f32}, Source{});
ASSERT_EQ(result, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchSampler) {
@@ -244,13 +237,13 @@ TEST_F(IntrinsicTableTest, MatchSampler) {
auto* vec4_f32 = create<sem::Vector>(f32, 4);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
auto* sampler = create<sem::Sampler>(ast::SamplerKind::kSampler);
auto result = table->Lookup(*this, IntrinsicType::kTextureSample,
{tex, sampler, vec2_f32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureSample);
EXPECT_THAT(result.intrinsic->ReturnType(), vec4_f32);
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result = table->Lookup(IntrinsicType::kTextureSample,
{tex, sampler, vec2_f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kTextureSample);
EXPECT_THAT(result->ReturnType(), vec4_f32);
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
Parameter{sampler, ParameterUsage::kSampler},
Parameter{vec2_f32, ParameterUsage::kCoords}));
@@ -260,10 +253,10 @@ TEST_F(IntrinsicTableTest, MismatchSampler) {
auto* f32 = create<sem::F32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
auto result = table->Lookup(*this, IntrinsicType::kTextureSample,
{tex, f32, vec2_f32}, Source{});
ASSERT_EQ(result.intrinsic, nullptr);
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
auto* result = table->Lookup(IntrinsicType::kTextureSample,
{tex, f32, vec2_f32}, Source{});
ASSERT_EQ(result, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchSampledTexture) {
@@ -272,13 +265,13 @@ TEST_F(IntrinsicTableTest, MatchSampledTexture) {
auto* vec2_i32 = create<sem::Vector>(i32, 2);
auto* vec4_f32 = create<sem::Vector>(f32, 4);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
{tex, vec2_i32, i32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
EXPECT_THAT(result.intrinsic->ReturnType(), vec4_f32);
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result = table->Lookup(IntrinsicType::kTextureLoad,
{tex, vec2_i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kTextureLoad);
EXPECT_THAT(result->ReturnType(), vec4_f32);
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
Parameter{vec2_i32, ParameterUsage::kCoords},
Parameter{i32, ParameterUsage::kLevel}));
@@ -290,13 +283,13 @@ TEST_F(IntrinsicTableTest, MatchMultisampledTexture) {
auto* vec2_i32 = create<sem::Vector>(i32, 2);
auto* vec4_f32 = create<sem::Vector>(f32, 4);
auto* tex = create<sem::MultisampledTexture>(ast::TextureDimension::k2d, f32);
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
{tex, vec2_i32, i32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
EXPECT_THAT(result.intrinsic->ReturnType(), vec4_f32);
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result = table->Lookup(IntrinsicType::kTextureLoad,
{tex, vec2_i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kTextureLoad);
EXPECT_THAT(result->ReturnType(), vec4_f32);
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
Parameter{vec2_i32, ParameterUsage::kCoords},
Parameter{i32, ParameterUsage::kSampleIndex}));
@@ -307,13 +300,13 @@ TEST_F(IntrinsicTableTest, MatchDepthTexture) {
auto* i32 = create<sem::I32>();
auto* vec2_i32 = create<sem::Vector>(i32, 2);
auto* tex = create<sem::DepthTexture>(ast::TextureDimension::k2d);
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
{tex, vec2_i32, i32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result = table->Lookup(IntrinsicType::kTextureLoad,
{tex, vec2_i32, i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kTextureLoad);
EXPECT_THAT(result->ReturnType(), f32);
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
Parameter{vec2_i32, ParameterUsage::kCoords},
Parameter{i32, ParameterUsage::kLevel}));
@@ -325,13 +318,13 @@ TEST_F(IntrinsicTableTest, MatchExternalTexture) {
auto* vec2_i32 = create<sem::Vector>(i32, 2);
auto* vec4_f32 = create<sem::Vector>(f32, 4);
auto* tex = create<sem::ExternalTexture>();
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
{tex, vec2_i32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
EXPECT_THAT(result.intrinsic->ReturnType(), vec4_f32);
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result =
table->Lookup(IntrinsicType::kTextureLoad, {tex, vec2_i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kTextureLoad);
EXPECT_THAT(result->ReturnType(), vec4_f32);
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
Parameter{vec2_i32, ParameterUsage::kCoords}));
}
@@ -342,18 +335,18 @@ TEST_F(IntrinsicTableTest, MatchROStorageTexture) {
auto* vec2_i32 = create<sem::Vector>(i32, 2);
auto* vec4_f32 = create<sem::Vector>(f32, 4);
auto* subtype =
sem::StorageTexture::SubtypeFor(ast::ImageFormat::kR16Float, Types());
sem::StorageTexture::SubtypeFor(ast::ImageFormat::kR32Float, Types());
auto* tex = create<sem::StorageTexture>(ast::TextureDimension::k2d,
ast::ImageFormat::kR16Float,
ast::ImageFormat::kR32Float,
ast::AccessControl::kRead, subtype);
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
{tex, vec2_i32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
EXPECT_THAT(result.intrinsic->ReturnType(), vec4_f32);
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result =
table->Lookup(IntrinsicType::kTextureLoad, {tex, vec2_i32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kTextureLoad);
EXPECT_THAT(result->ReturnType(), vec4_f32);
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
Parameter{vec2_i32, ParameterUsage::kCoords}));
}
@@ -364,18 +357,18 @@ TEST_F(IntrinsicTableTest, MatchWOStorageTexture) {
auto* vec2_i32 = create<sem::Vector>(i32, 2);
auto* vec4_f32 = create<sem::Vector>(f32, 4);
auto* subtype =
sem::StorageTexture::SubtypeFor(ast::ImageFormat::kR16Float, Types());
sem::StorageTexture::SubtypeFor(ast::ImageFormat::kR32Float, Types());
auto* tex = create<sem::StorageTexture>(ast::TextureDimension::k2d,
ast::ImageFormat::kR16Float,
ast::ImageFormat::kR32Float,
ast::AccessControl::kWrite, subtype);
auto result = table->Lookup(*this, IntrinsicType::kTextureStore,
{tex, vec2_i32, vec4_f32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureStore);
EXPECT_TRUE(result.intrinsic->ReturnType()->Is<sem::Void>());
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result = table->Lookup(IntrinsicType::kTextureStore,
{tex, vec2_i32, vec4_f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kTextureStore);
EXPECT_TRUE(result->ReturnType()->Is<sem::Void>());
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
Parameter{vec2_i32, ParameterUsage::kCoords},
Parameter{vec4_f32, ParameterUsage::kValue}));
@@ -385,55 +378,55 @@ TEST_F(IntrinsicTableTest, MismatchTexture) {
auto* f32 = create<sem::F32>();
auto* i32 = create<sem::I32>();
auto* vec2_i32 = create<sem::Vector>(i32, 2);
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
{f32, vec2_i32}, Source{});
ASSERT_EQ(result.intrinsic, nullptr);
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
auto* result =
table->Lookup(IntrinsicType::kTextureLoad, {f32, vec2_i32}, Source{});
ASSERT_EQ(result, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, ImplicitLoadOnReference) {
auto* f32 = create<sem::F32>();
auto result = table->Lookup(
*this, IntrinsicType::kCos,
auto* result = table->Lookup(
IntrinsicType::kCos,
{create<sem::Reference>(f32, ast::StorageClass::kNone)}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{f32}));
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kCos);
EXPECT_THAT(result->ReturnType(), f32);
EXPECT_THAT(result->Parameters(), ElementsAre(Parameter{f32}));
}
TEST_F(IntrinsicTableTest, MatchOpenType) {
auto* f32 = create<sem::F32>();
auto result =
table->Lookup(*this, IntrinsicType::kClamp, {f32, f32, f32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result =
table->Lookup(IntrinsicType::kClamp, {f32, f32, f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kClamp);
EXPECT_THAT(result->ReturnType(), f32);
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{f32}, Parameter{f32}, Parameter{f32}));
}
TEST_F(IntrinsicTableTest, MismatchOpenType) {
auto* f32 = create<sem::F32>();
auto* u32 = create<sem::U32>();
auto result =
table->Lookup(*this, IntrinsicType::kClamp, {f32, u32, f32}, Source{});
ASSERT_EQ(result.intrinsic, nullptr);
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
auto* result =
table->Lookup(IntrinsicType::kClamp, {f32, u32, f32}, Source{});
ASSERT_EQ(result, 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, 2);
auto result = table->Lookup(*this, IntrinsicType::kClamp,
{vec2_f32, vec2_f32, vec2_f32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
EXPECT_THAT(result.intrinsic->ReturnType(), vec2_f32);
EXPECT_THAT(result.intrinsic->Parameters(),
auto* result = table->Lookup(IntrinsicType::kClamp,
{vec2_f32, vec2_f32, vec2_f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kClamp);
EXPECT_THAT(result->ReturnType(), vec2_f32);
EXPECT_THAT(result->Parameters(),
ElementsAre(Parameter{vec2_f32}, Parameter{vec2_f32},
Parameter{vec2_f32}));
}
@@ -442,110 +435,106 @@ TEST_F(IntrinsicTableTest, MismatchOpenSizeVector) {
auto* f32 = create<sem::F32>();
auto* u32 = create<sem::U32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2);
auto result = table->Lookup(*this, IntrinsicType::kClamp,
{vec2_f32, u32, vec2_f32}, Source{});
ASSERT_EQ(result.intrinsic, nullptr);
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
auto* result =
table->Lookup(IntrinsicType::kClamp, {vec2_f32, u32, vec2_f32}, Source{});
ASSERT_EQ(result, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchOpenSizeMatrix) {
auto* f32 = create<sem::F32>();
auto* vec3_f32 = create<sem::Vector>(f32, 3);
auto* mat3_f32 = create<sem::Matrix>(vec3_f32, 3);
auto result =
table->Lookup(*this, IntrinsicType::kDeterminant, {mat3_f32}, Source{});
ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kDeterminant);
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{mat3_f32}));
auto* result =
table->Lookup(IntrinsicType::kDeterminant, {mat3_f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_THAT(result->Type(), IntrinsicType::kDeterminant);
EXPECT_THAT(result->ReturnType(), f32);
EXPECT_THAT(result->Parameters(), ElementsAre(Parameter{mat3_f32}));
}
TEST_F(IntrinsicTableTest, MismatchOpenSizeMatrix) {
auto* f32 = create<sem::F32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2);
auto* mat3x2_f32 = create<sem::Matrix>(vec2_f32, 3);
auto result =
table->Lookup(*this, IntrinsicType::kDeterminant, {mat3x2_f32}, Source{});
ASSERT_EQ(result.intrinsic, nullptr);
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
auto* result =
table->Lookup(IntrinsicType::kDeterminant, {mat3x2_f32}, Source{});
ASSERT_EQ(result, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, OverloadOrderByNumberOfParameters) {
// None of the arguments match, so expect the overloads with 2 parameters to
// come first
auto* bool_ = create<sem::Bool>();
auto result = table->Lookup(*this, IntrinsicType::kTextureDimensions,
{bool_, bool_}, Source{});
ASSERT_EQ(result.diagnostics.str(),
table->Lookup(IntrinsicType::kTextureDimensions, {bool_, bool_}, Source{});
ASSERT_EQ(Diagnostics().str(),
R"(error: no matching call to textureDimensions(bool, bool)
26 candidate functions:
textureDimensions(texture : texture_2d<T>, level : i32) -> vec2<i32>
textureDimensions(texture : texture_2d_array<T>, level : i32) -> vec2<i32>
textureDimensions(texture : texture_3d<T>, level : i32) -> vec3<i32>
textureDimensions(texture : texture_cube<T>, level : i32) -> vec3<i32>
textureDimensions(texture : texture_cube_array<T>, level : i32) -> vec3<i32>
textureDimensions(texture : texture_depth_2d, level : i32) -> vec2<i32>
textureDimensions(texture : texture_depth_2d_array, level : i32) -> vec2<i32>
textureDimensions(texture : texture_depth_cube, level : i32) -> vec3<i32>
textureDimensions(texture : texture_depth_cube_array, level : i32) -> vec3<i32>
textureDimensions(texture : texture_1d<T>) -> i32
textureDimensions(texture : texture_2d<T>) -> vec2<i32>
textureDimensions(texture : texture_2d_array<T>) -> vec2<i32>
textureDimensions(texture : texture_3d<T>) -> vec3<i32>
textureDimensions(texture : texture_cube<T>) -> vec3<i32>
textureDimensions(texture : texture_cube_array<T>) -> vec3<i32>
textureDimensions(texture : texture_multisampled_2d<T>) -> vec2<i32>
textureDimensions(texture : texture_multisampled_2d_array<T>) -> vec2<i32>
textureDimensions(texture : texture_depth_2d) -> vec2<i32>
textureDimensions(texture : texture_depth_2d_array) -> vec2<i32>
textureDimensions(texture : texture_depth_cube) -> vec3<i32>
textureDimensions(texture : texture_depth_cube_array) -> vec3<i32>
textureDimensions(texture : texture_storage_1d<F, A>) -> i32
textureDimensions(texture : texture_storage_2d<F, A>) -> vec2<i32>
textureDimensions(texture : texture_storage_2d_array<F, A>) -> vec2<i32>
textureDimensions(texture : texture_storage_3d<F, A>) -> vec3<i32>
textureDimensions(texture : texture_external) -> vec2<i32>
25 candidate functions:
textureDimensions(texture: texture_2d<T>, level: i32) -> vec2<i32>
textureDimensions(texture: texture_2d_array<T>, level: i32) -> vec2<i32>
textureDimensions(texture: texture_3d<T>, level: i32) -> vec3<i32>
textureDimensions(texture: texture_cube<T>, level: i32) -> vec3<i32>
textureDimensions(texture: texture_cube_array<T>, level: i32) -> vec3<i32>
textureDimensions(texture: texture_depth_2d, level: i32) -> vec2<i32>
textureDimensions(texture: texture_depth_2d_array, level: i32) -> vec2<i32>
textureDimensions(texture: texture_depth_cube, level: i32) -> vec3<i32>
textureDimensions(texture: texture_depth_cube_array, level: i32) -> vec3<i32>
textureDimensions(texture: texture_1d<T>) -> i32
textureDimensions(texture: texture_2d<T>) -> vec2<i32>
textureDimensions(texture: texture_2d_array<T>) -> vec2<i32>
textureDimensions(texture: texture_3d<T>) -> vec3<i32>
textureDimensions(texture: texture_cube<T>) -> vec3<i32>
textureDimensions(texture: texture_cube_array<T>) -> vec3<i32>
textureDimensions(texture: texture_multisampled_2d<T>) -> vec2<i32>
textureDimensions(texture: texture_depth_2d) -> vec2<i32>
textureDimensions(texture: texture_depth_2d_array) -> vec2<i32>
textureDimensions(texture: texture_depth_cube) -> vec3<i32>
textureDimensions(texture: texture_depth_cube_array) -> vec3<i32>
textureDimensions(texture: texture_storage_1d<F, A>) -> i32
textureDimensions(texture: texture_storage_2d<F, A>) -> vec2<i32>
textureDimensions(texture: texture_storage_2d_array<F, A>) -> vec2<i32>
textureDimensions(texture: texture_storage_3d<F, A>) -> vec3<i32>
textureDimensions(texture: texture_external) -> vec2<i32>
)");
}
TEST_F(IntrinsicTableTest, OverloadOrderByMatchingParameter) {
auto* tex = create<sem::DepthTexture>(ast::TextureDimension::k2d);
auto* bool_ = create<sem::Bool>();
auto result = table->Lookup(*this, IntrinsicType::kTextureDimensions,
{tex, bool_}, Source{});
table->Lookup(IntrinsicType::kTextureDimensions, {tex, bool_}, Source{});
ASSERT_EQ(
result.diagnostics.str(),
Diagnostics().str(),
R"(error: no matching call to textureDimensions(texture_depth_2d, bool)
26 candidate functions:
textureDimensions(texture : texture_depth_2d, level : i32) -> vec2<i32>
textureDimensions(texture : texture_depth_2d) -> vec2<i32>
textureDimensions(texture : texture_2d<T>, level : i32) -> vec2<i32>
textureDimensions(texture : texture_2d_array<T>, level : i32) -> vec2<i32>
textureDimensions(texture : texture_3d<T>, level : i32) -> vec3<i32>
textureDimensions(texture : texture_cube<T>, level : i32) -> vec3<i32>
textureDimensions(texture : texture_cube_array<T>, level : i32) -> vec3<i32>
textureDimensions(texture : texture_depth_2d_array, level : i32) -> vec2<i32>
textureDimensions(texture : texture_depth_cube, level : i32) -> vec3<i32>
textureDimensions(texture : texture_depth_cube_array, level : i32) -> vec3<i32>
textureDimensions(texture : texture_1d<T>) -> i32
textureDimensions(texture : texture_2d<T>) -> vec2<i32>
textureDimensions(texture : texture_2d_array<T>) -> vec2<i32>
textureDimensions(texture : texture_3d<T>) -> vec3<i32>
textureDimensions(texture : texture_cube<T>) -> vec3<i32>
textureDimensions(texture : texture_cube_array<T>) -> vec3<i32>
textureDimensions(texture : texture_multisampled_2d<T>) -> vec2<i32>
textureDimensions(texture : texture_multisampled_2d_array<T>) -> vec2<i32>
textureDimensions(texture : texture_depth_2d_array) -> vec2<i32>
textureDimensions(texture : texture_depth_cube) -> vec3<i32>
textureDimensions(texture : texture_depth_cube_array) -> vec3<i32>
textureDimensions(texture : texture_storage_1d<F, A>) -> i32
textureDimensions(texture : texture_storage_2d<F, A>) -> vec2<i32>
textureDimensions(texture : texture_storage_2d_array<F, A>) -> vec2<i32>
textureDimensions(texture : texture_storage_3d<F, A>) -> vec3<i32>
textureDimensions(texture : texture_external) -> vec2<i32>
25 candidate functions:
textureDimensions(texture: texture_depth_2d, level: i32) -> vec2<i32>
textureDimensions(texture: texture_depth_2d) -> vec2<i32>
textureDimensions(texture: texture_2d<T>, level: i32) -> vec2<i32>
textureDimensions(texture: texture_2d_array<T>, level: i32) -> vec2<i32>
textureDimensions(texture: texture_3d<T>, level: i32) -> vec3<i32>
textureDimensions(texture: texture_cube<T>, level: i32) -> vec3<i32>
textureDimensions(texture: texture_cube_array<T>, level: i32) -> vec3<i32>
textureDimensions(texture: texture_depth_2d_array, level: i32) -> vec2<i32>
textureDimensions(texture: texture_depth_cube, level: i32) -> vec3<i32>
textureDimensions(texture: texture_depth_cube_array, level: i32) -> vec3<i32>
textureDimensions(texture: texture_1d<T>) -> i32
textureDimensions(texture: texture_2d<T>) -> vec2<i32>
textureDimensions(texture: texture_2d_array<T>) -> vec2<i32>
textureDimensions(texture: texture_3d<T>) -> vec3<i32>
textureDimensions(texture: texture_cube<T>) -> vec3<i32>
textureDimensions(texture: texture_cube_array<T>) -> vec3<i32>
textureDimensions(texture: texture_multisampled_2d<T>) -> vec2<i32>
textureDimensions(texture: texture_depth_2d_array) -> vec2<i32>
textureDimensions(texture: texture_depth_cube) -> vec3<i32>
textureDimensions(texture: texture_depth_cube_array) -> vec3<i32>
textureDimensions(texture: texture_storage_1d<F, A>) -> i32
textureDimensions(texture: texture_storage_2d<F, A>) -> vec2<i32>
textureDimensions(texture: texture_storage_2d_array<F, A>) -> vec2<i32>
textureDimensions(texture: texture_storage_3d<F, A>) -> vec3<i32>
textureDimensions(texture: texture_external) -> vec2<i32>
)");
}

View File

@@ -468,8 +468,8 @@ TEST_F(ResolverIntrinsicTest, Select_Error_NoParams) {
R"(error: no matching call to select()
2 candidate functions:
select(T, T, bool) -> T where: T is scalar
select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
select(T, T, bool) -> T where: T is f32, i32, u32 or bool
select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is f32, i32, u32 or bool
)");
}
@@ -483,8 +483,8 @@ TEST_F(ResolverIntrinsicTest, Select_Error_SelectorInt) {
R"(error: no matching call to select(i32, i32, i32)
2 candidate functions:
select(T, T, bool) -> T where: T is scalar
select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
select(T, T, bool) -> T where: T is f32, i32, u32 or bool
select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is f32, i32, u32 or bool
)");
}
@@ -500,8 +500,8 @@ TEST_F(ResolverIntrinsicTest, Select_Error_Matrix) {
R"(error: no matching call to select(mat2x2<f32>, mat2x2<f32>, bool)
2 candidate functions:
select(T, T, bool) -> T where: T is scalar
select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
select(T, T, bool) -> T where: T is f32, i32, u32 or bool
select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is f32, i32, u32 or bool
)");
}
@@ -515,8 +515,8 @@ TEST_F(ResolverIntrinsicTest, Select_Error_MismatchTypes) {
R"(error: no matching call to select(f32, vec2<f32>, bool)
2 candidate functions:
select(T, T, bool) -> T where: T is scalar
select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
select(T, T, bool) -> T where: T is f32, i32, u32 or bool
select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is f32, i32, u32 or bool
)");
}
@@ -531,8 +531,8 @@ TEST_F(ResolverIntrinsicTest, Select_Error_MismatchVectorSize) {
R"(error: no matching call to select(vec2<f32>, vec3<f32>, bool)
2 candidate functions:
select(T, T, bool) -> T where: T is scalar
select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
select(T, T, bool) -> T where: T is f32, i32, u32 or bool
select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is f32, i32, u32 or bool
)");
}

View File

@@ -126,7 +126,7 @@ bool IsValidationDisabled(const ast::DecorationList& decorations,
Resolver::Resolver(ProgramBuilder* builder)
: builder_(builder),
diagnostics_(builder->Diagnostics()),
intrinsic_table_(IntrinsicTable::Create()) {}
intrinsic_table_(IntrinsicTable::Create(*builder)) {}
Resolver::~Resolver() = default;
@@ -1694,17 +1694,15 @@ bool Resolver::IntrinsicCall(ast::CallExpression* call,
arg_tys.emplace_back(TypeOf(expr));
}
auto result = intrinsic_table_->Lookup(*builder_, intrinsic_type, arg_tys,
call->source());
if (!result.intrinsic) {
// Intrinsic lookup failed.
diagnostics_.add(result.diagnostics);
auto* result =
intrinsic_table_->Lookup(intrinsic_type, arg_tys, call->source());
if (!result) {
return false;
}
builder_->Sem().Add(call, builder_->create<sem::Call>(call, result.intrinsic,
current_statement_));
SetType(call, result.intrinsic->ReturnType());
builder_->Sem().Add(
call, builder_->create<sem::Call>(call, result, current_statement_));
SetType(call, result->ReturnType());
return true;
}