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:
parent
ae5437cc87
commit
b29a59de6e
|
@ -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",
|
||||
|
|
|
@ -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
|
@ -29,27 +29,20 @@ 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,
|
||||
virtual const sem::Intrinsic* Lookup(
|
||||
sem::IntrinsicType type,
|
||||
const std::vector<const sem::Type*>& args,
|
||||
const Source& source) const = 0;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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 -}}
|
|
@ -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,
|
||||
auto* result = table->Lookup(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(),
|
||||
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,
|
||||
auto* result = table->Lookup(IntrinsicType::kTextureSample,
|
||||
{tex, f32, vec2_f32}, Source{});
|
||||
ASSERT_EQ(result.intrinsic, nullptr);
|
||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||
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,
|
||||
auto* result = table->Lookup(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(),
|
||||
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,
|
||||
auto* result = table->Lookup(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(),
|
||||
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,
|
||||
auto* result = table->Lookup(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(),
|
||||
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,
|
||||
auto* result = table->Lookup(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(),
|
||||
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,
|
||||
auto* result = table->Lookup(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(),
|
||||
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,45 +435,44 @@ 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:
|
||||
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>
|
||||
|
@ -497,7 +489,6 @@ TEST_F(IntrinsicTableTest, OverloadOrderByNumberOfParameters) {
|
|||
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>
|
||||
|
@ -513,13 +504,12 @@ TEST_F(IntrinsicTableTest, OverloadOrderByNumberOfParameters) {
|
|||
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:
|
||||
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>
|
||||
|
@ -537,7 +527,6 @@ TEST_F(IntrinsicTableTest, OverloadOrderByMatchingParameter) {
|
|||
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>
|
||||
|
|
|
@ -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
|
||||
)");
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ type Parameter struct {
|
|||
// Format implements the fmt.Formatter interface
|
||||
func (p Parameter) Format(w fmt.State, verb rune) {
|
||||
if p.Name != "" {
|
||||
fmt.Fprintf(w, "%v ", p.Name)
|
||||
fmt.Fprintf(w, "%v: ", p.Name)
|
||||
}
|
||||
p.Type.Format(w, verb)
|
||||
}
|
||||
|
|
|
@ -27,6 +27,9 @@ import (
|
|||
|
||||
type generator struct {
|
||||
s *sem.Sem
|
||||
cached struct {
|
||||
intrinsicTable *IntrinsicTable // lazily built by intrinsicTable()
|
||||
}
|
||||
}
|
||||
|
||||
// Generate executes the template tmpl using the provided semantic
|
||||
|
@ -52,6 +55,7 @@ func (g *generator) generate(tmpl string, w io.Writer) error {
|
|||
"IsTemplateEnumParam": is(&sem.TemplateEnumParam{}),
|
||||
"IsFirstIn": isFirstIn,
|
||||
"IsLastIn": isLastIn,
|
||||
"IntrinsicTable": g.intrinsicTable,
|
||||
}).
|
||||
Option("missingkey=error").
|
||||
Parse(tmpl)
|
||||
|
@ -63,6 +67,19 @@ func (g *generator) generate(tmpl string, w io.Writer) error {
|
|||
})
|
||||
}
|
||||
|
||||
// intrinsicTable lazily calls and returns the result of buildIntrinsicTable(),
|
||||
// caching the result for repeated calls.
|
||||
func (g *generator) intrinsicTable() (*IntrinsicTable, error) {
|
||||
if g.cached.intrinsicTable == nil {
|
||||
var err error
|
||||
g.cached.intrinsicTable, err = buildIntrinsicTable(g.s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return g.cached.intrinsicTable, nil
|
||||
}
|
||||
|
||||
// Map is a simple generic key-value map, which can be used in the template
|
||||
type Map map[interface{}]interface{}
|
||||
|
||||
|
|
|
@ -0,0 +1,381 @@
|
|||
// Copyright 2021 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.
|
||||
|
||||
package gen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"dawn.googlesource.com/tint/tools/src/cmd/intrinsic-gen/sem"
|
||||
"dawn.googlesource.com/tint/tools/src/list"
|
||||
"dawn.googlesource.com/tint/tools/src/lut"
|
||||
)
|
||||
|
||||
// IntrinsicTable holds data specific to the intrinsic_table.inl.tmpl template
|
||||
type IntrinsicTable struct {
|
||||
// The semantic info
|
||||
Sem *sem.Sem
|
||||
|
||||
// TMatchers are all the sem.OpenType, sem.Type and sem.TypeMatchers.
|
||||
// These are all implemented by classes deriving from tint::TypeMatcher
|
||||
TMatchers []sem.Named
|
||||
TMatcherIndex map[sem.Named]int // [object -> index] in TMatcher
|
||||
|
||||
// NMatchers are all the sem.OpenNumber and sem.EnumMatchers.
|
||||
// These are all implemented by classes deriving from tint::NumberMatcher
|
||||
NMatchers []sem.Named
|
||||
NMatcherIndex map[sem.Named]int // [object -> index] in NMatchers
|
||||
|
||||
MatcherIndices []int // kMatcherIndices table content
|
||||
OpenTypes []OpenType // kOpenTypes table content
|
||||
OpenNumbers []OpenNumber // kOpenNumbers table content
|
||||
Parameters []Parameter // kParameters table content
|
||||
Overloads []Overload // kOverloads table content
|
||||
Functions []Function // kIntrinsics table content
|
||||
}
|
||||
|
||||
// OpenType is used to create the C++ OpenTypeInfo structure
|
||||
type OpenType struct {
|
||||
// Name of the open type (e.g. 'T')
|
||||
Name string
|
||||
// Optional type matcher constraint.
|
||||
// Either an index in Matchers::type, or -1
|
||||
MatcherIndex int
|
||||
}
|
||||
|
||||
// OpenNumber is used to create the C++ OpenNumberInfo structure
|
||||
type OpenNumber struct {
|
||||
// Name of the open number (e.g. 'N')
|
||||
Name string
|
||||
// Optional type matcher constraint.
|
||||
// Either an index in Matchers::type, or -1
|
||||
MatcherIndex int
|
||||
}
|
||||
|
||||
// Parameter is used to create the C++ ParameterInfo structure
|
||||
type Parameter struct {
|
||||
// The parameter usage (parameter name)
|
||||
Usage string
|
||||
|
||||
// Index into IntrinsicTable.MatcherIndices, beginning the list of matchers
|
||||
// required to match the parameter type. The matcher indices index
|
||||
// into IntrinsicTable::TMatchers and / or IntrinsicTable::NMatchers.
|
||||
// These indices are consumed by the matchers themselves.
|
||||
// The first index is always a TypeMatcher.
|
||||
MatcherIndicesOffset *int
|
||||
}
|
||||
|
||||
// Overload is used to create the C++ OverloadInfo structure
|
||||
type Overload struct {
|
||||
// Total number of parameters for the overload
|
||||
NumParameters int
|
||||
// Total number of open types for the overload
|
||||
NumOpenTypes int
|
||||
// Total number of open numbers for the overload
|
||||
NumOpenNumbers int
|
||||
// Index to the first open type in IntrinsicTable.OpenTypes
|
||||
OpenTypesOffset *int
|
||||
// Index to the first open number in IntrinsicTable.OpenNumbers
|
||||
OpenNumbersOffset *int
|
||||
// Index to the first parameter in IntrinsicTable.Parameters
|
||||
ParametersOffset *int
|
||||
// Index into IntrinsicTable.MatcherIndices, beginning the list of matchers
|
||||
// required to match the return type. The matcher indices index
|
||||
// into IntrinsicTable::TMatchers and / or IntrinsicTable::NMatchers.
|
||||
// These indices are consumed by the matchers themselves.
|
||||
// The first index is always a TypeMatcher.
|
||||
ReturnMatcherIndicesOffset *int
|
||||
}
|
||||
|
||||
// Function is used to create the C++ IntrinsicInfo structure
|
||||
type Function struct {
|
||||
OverloadDescriptions []string
|
||||
NumOverloads int
|
||||
OverloadsOffset *int
|
||||
}
|
||||
|
||||
// Helper for building the IntrinsicTable
|
||||
type intrinsicTableBuilder struct {
|
||||
// The output of the builder
|
||||
IntrinsicTable
|
||||
|
||||
// Lookup tables.
|
||||
// These are packed (compressed) once all the entries have been added.
|
||||
lut struct {
|
||||
matcherIndices lut.LUT
|
||||
openTypes lut.LUT
|
||||
openNumbers lut.LUT
|
||||
parameters lut.LUT
|
||||
overloads lut.LUT
|
||||
}
|
||||
}
|
||||
|
||||
// Helper for building a single overload
|
||||
type overloadBuilder struct {
|
||||
*intrinsicTableBuilder
|
||||
// Maps TemplateParam to index in openTypes
|
||||
openTypeIndex map[sem.TemplateParam]int
|
||||
// Maps TemplateParam to index in openNumbers
|
||||
openNumberIndex map[sem.TemplateParam]int
|
||||
// Open types used by the overload
|
||||
openTypes []OpenType
|
||||
// Open numbers used by the overload
|
||||
openNumbers []OpenNumber
|
||||
// All parameters declared by the overload
|
||||
parameters []Parameter
|
||||
// Index into IntrinsicTable.MatcherIndices, beginning the list of matchers
|
||||
// required to match the return type. The matcher indices index
|
||||
// into IntrinsicTable::TMatchers and / or IntrinsicTable::NMatchers.
|
||||
// These indices are consumed by the matchers themselves.
|
||||
// The first index is always a TypeMatcher.
|
||||
returnTypeMatcherIndicesOffset *int
|
||||
}
|
||||
|
||||
// layoutMatchers assigns each of the TMatchers and NMatchers a unique index
|
||||
// in the C++ Matchers::type and Matchers::number arrays, respectively.
|
||||
func (b *intrinsicTableBuilder) layoutMatchers(s *sem.Sem) {
|
||||
// First MaxOpenTypes of TMatchers are open types
|
||||
b.TMatchers = make([]sem.Named, s.MaxOpenTypes)
|
||||
for _, m := range s.Types {
|
||||
b.TMatcherIndex[m] = len(b.TMatchers)
|
||||
b.TMatchers = append(b.TMatchers, m)
|
||||
}
|
||||
for _, m := range s.TypeMatchers {
|
||||
b.TMatcherIndex[m] = len(b.TMatchers)
|
||||
b.TMatchers = append(b.TMatchers, m)
|
||||
}
|
||||
|
||||
// First MaxOpenNumbers of NMatchers are open numbers
|
||||
b.NMatchers = make([]sem.Named, s.MaxOpenNumbers)
|
||||
for _, m := range s.EnumMatchers {
|
||||
b.NMatcherIndex[m] = len(b.NMatchers)
|
||||
b.NMatchers = append(b.NMatchers, m)
|
||||
}
|
||||
}
|
||||
|
||||
// buildOverload constructs an Overload for a sem.Overload
|
||||
func (b *intrinsicTableBuilder) buildOverload(o *sem.Overload) (Overload, error) {
|
||||
ob := overloadBuilder{
|
||||
intrinsicTableBuilder: b,
|
||||
openTypeIndex: map[sem.TemplateParam]int{},
|
||||
openNumberIndex: map[sem.TemplateParam]int{},
|
||||
}
|
||||
|
||||
if err := ob.buildOpenTypes(o); err != nil {
|
||||
return Overload{}, err
|
||||
}
|
||||
if err := ob.buildOpenNumbers(o); err != nil {
|
||||
return Overload{}, err
|
||||
}
|
||||
if err := ob.buildParameters(o); err != nil {
|
||||
return Overload{}, err
|
||||
}
|
||||
if err := ob.buildReturnType(o); err != nil {
|
||||
return Overload{}, err
|
||||
}
|
||||
|
||||
return Overload{
|
||||
NumParameters: len(ob.parameters),
|
||||
NumOpenTypes: len(ob.openTypes),
|
||||
NumOpenNumbers: len(ob.openNumbers),
|
||||
OpenTypesOffset: b.lut.openTypes.Add(ob.openTypes),
|
||||
OpenNumbersOffset: b.lut.openNumbers.Add(ob.openNumbers),
|
||||
ParametersOffset: b.lut.parameters.Add(ob.parameters),
|
||||
ReturnMatcherIndicesOffset: ob.returnTypeMatcherIndicesOffset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// buildOpenTypes constructs the OpenTypes used by the overload, populating
|
||||
// b.openTypes
|
||||
func (b *overloadBuilder) buildOpenTypes(o *sem.Overload) error {
|
||||
b.openTypes = make([]OpenType, len(o.OpenTypes))
|
||||
for i, t := range o.OpenTypes {
|
||||
b.openTypeIndex[t] = i
|
||||
matcherIndex := -1
|
||||
if t.Type != nil {
|
||||
var err error
|
||||
matcherIndex, err = b.matcherIndex(t.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
b.openTypes[i] = OpenType{
|
||||
Name: t.Name,
|
||||
MatcherIndex: matcherIndex,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildOpenNumbers constructs the OpenNumbers used by the overload, populating
|
||||
// b.openNumbers
|
||||
func (b *overloadBuilder) buildOpenNumbers(o *sem.Overload) error {
|
||||
b.openNumbers = make([]OpenNumber, len(o.OpenNumbers))
|
||||
for i, t := range o.OpenNumbers {
|
||||
b.openNumberIndex[t] = i
|
||||
matcherIndex := -1
|
||||
if e, ok := t.(*sem.TemplateEnumParam); ok && e.Matcher != nil {
|
||||
var err error
|
||||
matcherIndex, err = b.matcherIndex(e.Matcher)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
b.openNumbers[i] = OpenNumber{
|
||||
Name: t.GetName(),
|
||||
MatcherIndex: matcherIndex,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildParameters constructs the Parameters used by the overload, populating
|
||||
// b.parameters
|
||||
func (b *overloadBuilder) buildParameters(o *sem.Overload) error {
|
||||
b.parameters = make([]Parameter, len(o.Parameters))
|
||||
for i, p := range o.Parameters {
|
||||
indices, err := b.collectMatcherIndices(p.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.parameters[i] = Parameter{
|
||||
Usage: p.Name,
|
||||
MatcherIndicesOffset: b.lut.matcherIndices.Add(indices),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildParameters calculates the matcher indices required to match the
|
||||
// overload's return type (if the overload has a return value), possibly
|
||||
// populating b.returnTypeMatcherIndicesOffset
|
||||
func (b *overloadBuilder) buildReturnType(o *sem.Overload) error {
|
||||
if o.ReturnType != nil {
|
||||
indices, err := b.collectMatcherIndices(*o.ReturnType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.returnTypeMatcherIndicesOffset = b.lut.matcherIndices.Add(indices)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// matcherIndex returns the index of TMatcher or NMatcher in
|
||||
// IntrinsicTable.TMatcher or IntrinsicTable.NMatcher, respectively.
|
||||
func (b *overloadBuilder) matcherIndex(n sem.Named) (int, error) {
|
||||
switch n := n.(type) {
|
||||
case *sem.Type, *sem.TypeMatcher:
|
||||
if i, ok := b.TMatcherIndex[n]; ok {
|
||||
return i, nil
|
||||
}
|
||||
return 0, fmt.Errorf("matcherIndex missing entry for %v %T", n.GetName(), n)
|
||||
case *sem.TemplateTypeParam:
|
||||
if i, ok := b.openTypeIndex[n]; ok {
|
||||
return i, nil
|
||||
}
|
||||
return 0, fmt.Errorf("openTypeIndex missing entry for %v %T", n.Name, n)
|
||||
case *sem.EnumMatcher:
|
||||
if i, ok := b.NMatcherIndex[n]; ok {
|
||||
return i, nil
|
||||
}
|
||||
return 0, fmt.Errorf("matcherIndex missing entry for %v %T", n.GetName(), n)
|
||||
case *sem.TemplateEnumParam:
|
||||
if i, ok := b.openNumberIndex[n]; ok {
|
||||
return i, nil
|
||||
}
|
||||
return 0, fmt.Errorf("openNumberIndex missing entry for %v %T", n, n)
|
||||
case *sem.TemplateNumberParam:
|
||||
if i, ok := b.openNumberIndex[n]; ok {
|
||||
return i, nil
|
||||
}
|
||||
return 0, fmt.Errorf("openNumberIndex missing entry for %v %T", n, n)
|
||||
default:
|
||||
return 0, fmt.Errorf("overload.matcherIndex() does not handle %v %T", n, n)
|
||||
}
|
||||
}
|
||||
|
||||
// collectMatcherIndices returns the full list of matcher indices required to
|
||||
// match the fully-qualified-name. For names that have do not have templated
|
||||
// arguments, collectMatcherIndices() will return a single TMatcher index.
|
||||
// For names that do have templated arguments, collectMatcherIndices() returns
|
||||
// a list of type matcher indices, starting with the target of the fully
|
||||
// qualified name, then followed by each of the template arguments from left to
|
||||
// right. Note that template arguments may themselves have template arguments,
|
||||
// and so collectMatcherIndices() may call itself.
|
||||
// The order of returned matcher indices is always the order of the fully
|
||||
// qualified name as read from left to right.
|
||||
// For example, calling collectMatcherIndices() for the fully qualified name:
|
||||
// A<B<C, D>, E<F, G<H>, I>
|
||||
// Would return the matcher indices:
|
||||
// A, B, C, D, E, F, G, H, I
|
||||
func (b *overloadBuilder) collectMatcherIndices(fqn sem.FullyQualifiedName) ([]int, error) {
|
||||
idx, err := b.matcherIndex(fqn.Target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := []int{idx}
|
||||
for _, arg := range fqn.TemplateArguments {
|
||||
indices, err := b.collectMatcherIndices(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, indices...)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// buildIntrinsicTable builds the IntrinsicTable from the semantic info
|
||||
func buildIntrinsicTable(s *sem.Sem) (*IntrinsicTable, error) {
|
||||
b := intrinsicTableBuilder{
|
||||
IntrinsicTable: IntrinsicTable{
|
||||
Sem: s,
|
||||
TMatcherIndex: map[sem.Named]int{},
|
||||
NMatcherIndex: map[sem.Named]int{},
|
||||
},
|
||||
}
|
||||
b.lut.matcherIndices = lut.New(list.Wrap(&b.MatcherIndices))
|
||||
b.lut.openTypes = lut.New(list.Wrap(&b.OpenTypes))
|
||||
b.lut.openNumbers = lut.New(list.Wrap(&b.OpenNumbers))
|
||||
b.lut.parameters = lut.New(list.Wrap(&b.Parameters))
|
||||
b.lut.overloads = lut.New(list.Wrap(&b.Overloads))
|
||||
|
||||
b.layoutMatchers(s)
|
||||
|
||||
for _, f := range s.Functions {
|
||||
overloads := make([]Overload, len(f.Overloads))
|
||||
overloadDescriptions := make([]string, len(f.Overloads))
|
||||
for i, o := range f.Overloads {
|
||||
overloadDescriptions[i] = fmt.Sprint(o.Decl)
|
||||
var err error
|
||||
if overloads[i], err = b.buildOverload(o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
b.Functions = append(b.Functions, Function{
|
||||
OverloadDescriptions: overloadDescriptions,
|
||||
NumOverloads: len(overloads),
|
||||
OverloadsOffset: b.lut.overloads.Add(overloads),
|
||||
})
|
||||
}
|
||||
|
||||
b.lut.matcherIndices.Compact()
|
||||
b.lut.openTypes.Compact()
|
||||
b.lut.openNumbers.Compact()
|
||||
b.lut.parameters.Compact()
|
||||
b.lut.overloads.Compact()
|
||||
|
||||
return &b.IntrinsicTable, nil
|
||||
}
|
|
@ -32,6 +32,8 @@ import (
|
|||
"dawn.googlesource.com/tint/tools/src/glob"
|
||||
)
|
||||
|
||||
const defProjectRelPath = "src/intrinsics.def"
|
||||
|
||||
func main() {
|
||||
if err := run(); err != nil {
|
||||
fmt.Println(err)
|
||||
|
@ -58,7 +60,7 @@ optional flags:`)
|
|||
func run() error {
|
||||
// Load the intrinsics definition file
|
||||
projectRoot := fileutils.ProjectRoot()
|
||||
defPath := filepath.Join(projectRoot, "src/intrinsics.def")
|
||||
defPath := filepath.Join(projectRoot, defProjectRelPath)
|
||||
|
||||
defSource, err := ioutil.ReadFile(defPath)
|
||||
if err != nil {
|
||||
|
@ -66,7 +68,7 @@ func run() error {
|
|||
}
|
||||
|
||||
// Parse the definition file to produce an AST
|
||||
ast, err := parser.Parse(string(defSource), defPath)
|
||||
ast, err := parser.Parse(string(defSource), defProjectRelPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue