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",
|
"inspector/scalar.h",
|
||||||
"intrinsic_table.cc",
|
"intrinsic_table.cc",
|
||||||
"intrinsic_table.h",
|
"intrinsic_table.h",
|
||||||
|
"intrinsic_table.inl",
|
||||||
"program.cc",
|
"program.cc",
|
||||||
"program.h",
|
"program.h",
|
||||||
"program_builder.cc",
|
"program_builder.cc",
|
||||||
|
|
|
@ -221,6 +221,7 @@ set(TINT_LIB_SRCS
|
||||||
inspector/scalar.h
|
inspector/scalar.h
|
||||||
intrinsic_table.cc
|
intrinsic_table.cc
|
||||||
intrinsic_table.h
|
intrinsic_table.h
|
||||||
|
intrinsic_table.inl
|
||||||
program_builder.cc
|
program_builder.cc
|
||||||
program_builder.h
|
program_builder.h
|
||||||
program_id.cc
|
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
|
/// IntrinsicTable is a lookup table of all the WGSL intrinsic functions
|
||||||
class IntrinsicTable {
|
class IntrinsicTable {
|
||||||
public:
|
public:
|
||||||
|
/// @param builder the program builder
|
||||||
/// @return a pointer to a newly created IntrinsicTable
|
/// @return a pointer to a newly created IntrinsicTable
|
||||||
static std::unique_ptr<IntrinsicTable> Create();
|
static std::unique_ptr<IntrinsicTable> Create(ProgramBuilder& builder);
|
||||||
|
|
||||||
/// Destructor
|
/// Destructor
|
||||||
virtual ~IntrinsicTable();
|
virtual ~IntrinsicTable();
|
||||||
|
|
||||||
/// Result is returned by Lookup
|
/// Lookup looks for the intrinsic overload with the given signature, raising
|
||||||
struct Result {
|
/// an error diagnostic if the intrinsic was not found.
|
||||||
/// 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
|
|
||||||
/// @param type the intrinsic type
|
/// @param type the intrinsic type
|
||||||
/// @param args the argument types passed to the intrinsic function
|
/// @param args the argument types passed to the intrinsic function
|
||||||
/// @param source the source of the intrinsic call
|
/// @param source the source of the intrinsic call
|
||||||
/// @return the semantic intrinsic if found, otherwise nullptr
|
/// @return the semantic intrinsic if found, otherwise nullptr
|
||||||
virtual Result Lookup(ProgramBuilder& builder,
|
virtual const sem::Intrinsic* Lookup(
|
||||||
sem::IntrinsicType type,
|
sem::IntrinsicType type,
|
||||||
const std::vector<const sem::Type*>& args,
|
const std::vector<const sem::Type*>& args,
|
||||||
const Source& source) const = 0;
|
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 {
|
class IntrinsicTableTest : public testing::Test, public ProgramBuilder {
|
||||||
public:
|
public:
|
||||||
std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create();
|
std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create(*this);
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchF32) {
|
TEST_F(IntrinsicTableTest, MatchF32) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kCos, {f32}, Source{});
|
auto* result = table->Lookup(IntrinsicType::kCos, {f32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
|
EXPECT_THAT(result->Type(), IntrinsicType::kCos);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
|
EXPECT_THAT(result->ReturnType(), f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{f32}));
|
EXPECT_THAT(result->Parameters(), ElementsAre(Parameter{f32}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchF32) {
|
TEST_F(IntrinsicTableTest, MismatchF32) {
|
||||||
auto* i32 = create<sem::I32>();
|
auto* i32 = create<sem::I32>();
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kCos, {i32}, Source{});
|
auto* result = table->Lookup(IntrinsicType::kCos, {i32}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result, nullptr);
|
||||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchU32) {
|
TEST_F(IntrinsicTableTest, MatchU32) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto* u32 = create<sem::U32>();
|
auto* u32 = create<sem::U32>();
|
||||||
auto* vec2_f32 = create<sem::Vector>(f32, 2);
|
auto* vec2_f32 = create<sem::Vector>(f32, 2);
|
||||||
auto result =
|
auto* result =
|
||||||
table->Lookup(*this, IntrinsicType::kUnpack2x16float, {u32}, Source{});
|
table->Lookup(IntrinsicType::kUnpack2x16float, {u32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kUnpack2x16float);
|
EXPECT_THAT(result->Type(), IntrinsicType::kUnpack2x16float);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), vec2_f32);
|
EXPECT_THAT(result->ReturnType(), vec2_f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{u32}));
|
EXPECT_THAT(result->Parameters(), ElementsAre(Parameter{u32}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchU32) {
|
TEST_F(IntrinsicTableTest, MismatchU32) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto result =
|
auto* result =
|
||||||
table->Lookup(*this, IntrinsicType::kUnpack2x16float, {f32}, Source{});
|
table->Lookup(IntrinsicType::kUnpack2x16float, {f32}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result, nullptr);
|
||||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchI32) {
|
TEST_F(IntrinsicTableTest, MatchI32) {
|
||||||
|
@ -81,13 +81,13 @@ TEST_F(IntrinsicTableTest, MatchI32) {
|
||||||
auto* i32 = create<sem::I32>();
|
auto* i32 = create<sem::I32>();
|
||||||
auto* vec4_f32 = create<sem::Vector>(f32, 4);
|
auto* vec4_f32 = create<sem::Vector>(f32, 4);
|
||||||
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32);
|
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32);
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
auto* result =
|
||||||
{tex, i32, i32}, Source{});
|
table->Lookup(IntrinsicType::kTextureLoad, {tex, i32, i32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
EXPECT_THAT(result->Type(), IntrinsicType::kTextureLoad);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), vec4_f32);
|
EXPECT_THAT(result->ReturnType(), vec4_f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result->Parameters(),
|
||||||
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
||||||
Parameter{i32, ParameterUsage::kCoords},
|
Parameter{i32, ParameterUsage::kCoords},
|
||||||
Parameter{i32, ParameterUsage::kLevel}));
|
Parameter{i32, ParameterUsage::kLevel}));
|
||||||
|
@ -96,146 +96,139 @@ TEST_F(IntrinsicTableTest, MatchI32) {
|
||||||
TEST_F(IntrinsicTableTest, MismatchI32) {
|
TEST_F(IntrinsicTableTest, MismatchI32) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32);
|
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32);
|
||||||
auto result =
|
auto* result =
|
||||||
table->Lookup(*this, IntrinsicType::kTextureLoad, {tex, f32}, Source{});
|
table->Lookup(IntrinsicType::kTextureLoad, {tex, f32}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result, nullptr);
|
||||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchIU32AsI32) {
|
TEST_F(IntrinsicTableTest, MatchIU32AsI32) {
|
||||||
auto* i32 = create<sem::I32>();
|
auto* i32 = create<sem::I32>();
|
||||||
auto result =
|
auto* result = table->Lookup(IntrinsicType::kCountOneBits, {i32}, Source{});
|
||||||
table->Lookup(*this, IntrinsicType::kCountOneBits, {i32}, Source{});
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
EXPECT_THAT(result->Type(), IntrinsicType::kCountOneBits);
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCountOneBits);
|
EXPECT_THAT(result->ReturnType(), i32);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), i32);
|
EXPECT_THAT(result->Parameters(), ElementsAre(Parameter{i32}));
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{i32}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchIU32AsU32) {
|
TEST_F(IntrinsicTableTest, MatchIU32AsU32) {
|
||||||
auto* u32 = create<sem::U32>();
|
auto* u32 = create<sem::U32>();
|
||||||
auto result =
|
auto* result = table->Lookup(IntrinsicType::kCountOneBits, {u32}, Source{});
|
||||||
table->Lookup(*this, IntrinsicType::kCountOneBits, {u32}, Source{});
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
EXPECT_THAT(result->Type(), IntrinsicType::kCountOneBits);
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCountOneBits);
|
EXPECT_THAT(result->ReturnType(), u32);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), u32);
|
EXPECT_THAT(result->Parameters(), ElementsAre(Parameter{u32}));
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{u32}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchIU32) {
|
TEST_F(IntrinsicTableTest, MismatchIU32) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto result =
|
auto* result = table->Lookup(IntrinsicType::kCountOneBits, {f32}, Source{});
|
||||||
table->Lookup(*this, IntrinsicType::kCountOneBits, {f32}, Source{});
|
ASSERT_EQ(result, nullptr);
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
|
||||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchFIU32AsI32) {
|
TEST_F(IntrinsicTableTest, MatchFIU32AsI32) {
|
||||||
auto* i32 = create<sem::I32>();
|
auto* i32 = create<sem::I32>();
|
||||||
auto result =
|
auto* result =
|
||||||
table->Lookup(*this, IntrinsicType::kClamp, {i32, i32, i32}, Source{});
|
table->Lookup(IntrinsicType::kClamp, {i32, i32, i32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
EXPECT_THAT(result->Type(), IntrinsicType::kClamp);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), i32);
|
EXPECT_THAT(result->ReturnType(), i32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result->Parameters(),
|
||||||
ElementsAre(Parameter{i32}, Parameter{i32}, Parameter{i32}));
|
ElementsAre(Parameter{i32}, Parameter{i32}, Parameter{i32}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchFIU32AsU32) {
|
TEST_F(IntrinsicTableTest, MatchFIU32AsU32) {
|
||||||
auto* u32 = create<sem::U32>();
|
auto* u32 = create<sem::U32>();
|
||||||
auto result =
|
auto* result =
|
||||||
table->Lookup(*this, IntrinsicType::kClamp, {u32, u32, u32}, Source{});
|
table->Lookup(IntrinsicType::kClamp, {u32, u32, u32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
EXPECT_THAT(result->Type(), IntrinsicType::kClamp);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), u32);
|
EXPECT_THAT(result->ReturnType(), u32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result->Parameters(),
|
||||||
ElementsAre(Parameter{u32}, Parameter{u32}, Parameter{u32}));
|
ElementsAre(Parameter{u32}, Parameter{u32}, Parameter{u32}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchFIU32AsF32) {
|
TEST_F(IntrinsicTableTest, MatchFIU32AsF32) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto result =
|
auto* result =
|
||||||
table->Lookup(*this, IntrinsicType::kClamp, {f32, f32, f32}, Source{});
|
table->Lookup(IntrinsicType::kClamp, {f32, f32, f32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
EXPECT_THAT(result->Type(), IntrinsicType::kClamp);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
|
EXPECT_THAT(result->ReturnType(), f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result->Parameters(),
|
||||||
ElementsAre(Parameter{f32}, Parameter{f32}, Parameter{f32}));
|
ElementsAre(Parameter{f32}, Parameter{f32}, Parameter{f32}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchFIU32) {
|
TEST_F(IntrinsicTableTest, MismatchFIU32) {
|
||||||
auto* bool_ = create<sem::Bool>();
|
auto* bool_ = create<sem::Bool>();
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
auto* result =
|
||||||
{bool_, bool_, bool_}, Source{});
|
table->Lookup(IntrinsicType::kClamp, {bool_, bool_, bool_}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result, nullptr);
|
||||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchBool) {
|
TEST_F(IntrinsicTableTest, MatchBool) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto* bool_ = create<sem::Bool>();
|
auto* bool_ = create<sem::Bool>();
|
||||||
auto result =
|
auto* result =
|
||||||
table->Lookup(*this, IntrinsicType::kSelect, {f32, f32, bool_}, Source{});
|
table->Lookup(IntrinsicType::kSelect, {f32, f32, bool_}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kSelect);
|
EXPECT_THAT(result->Type(), IntrinsicType::kSelect);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
|
EXPECT_THAT(result->ReturnType(), f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result->Parameters(),
|
||||||
ElementsAre(Parameter{f32}, Parameter{f32}, Parameter{bool_}));
|
ElementsAre(Parameter{f32}, Parameter{f32}, Parameter{bool_}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchBool) {
|
TEST_F(IntrinsicTableTest, MismatchBool) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto result =
|
auto* result =
|
||||||
table->Lookup(*this, IntrinsicType::kSelect, {f32, f32, f32}, Source{});
|
table->Lookup(IntrinsicType::kSelect, {f32, f32, f32}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result, nullptr);
|
||||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchPointer) {
|
TEST_F(IntrinsicTableTest, MatchPointer) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto* ptr = create<sem::Pointer>(f32, ast::StorageClass::kNone);
|
auto* ptr = create<sem::Pointer>(f32, ast::StorageClass::kNone);
|
||||||
auto result =
|
auto* result = table->Lookup(IntrinsicType::kModf, {f32, ptr}, Source{});
|
||||||
table->Lookup(*this, IntrinsicType::kModf, {f32, ptr}, Source{});
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
EXPECT_THAT(result->Type(), IntrinsicType::kModf);
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kModf);
|
EXPECT_THAT(result->ReturnType(), f32);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
|
EXPECT_THAT(result->Parameters(),
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
|
||||||
ElementsAre(Parameter{f32}, Parameter{ptr}));
|
ElementsAre(Parameter{f32}, Parameter{ptr}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchPointer) {
|
TEST_F(IntrinsicTableTest, MismatchPointer) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto result =
|
auto* result = table->Lookup(IntrinsicType::kModf, {f32, f32}, Source{});
|
||||||
table->Lookup(*this, IntrinsicType::kModf, {f32, f32}, Source{});
|
ASSERT_EQ(result, nullptr);
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
|
||||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchArray) {
|
TEST_F(IntrinsicTableTest, MatchArray) {
|
||||||
auto* arr = create<sem::Array>(create<sem::U32>(), 0, 4, 4, 4, true);
|
auto* arr = create<sem::Array>(create<sem::U32>(), 0, 4, 4, 4, true);
|
||||||
auto result =
|
auto* result = table->Lookup(IntrinsicType::kArrayLength, {arr}, Source{});
|
||||||
table->Lookup(*this, IntrinsicType::kArrayLength, {arr}, Source{});
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
EXPECT_THAT(result->Type(), IntrinsicType::kArrayLength);
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kArrayLength);
|
EXPECT_TRUE(result->ReturnType()->Is<sem::U32>());
|
||||||
EXPECT_TRUE(result.intrinsic->ReturnType()->Is<sem::U32>());
|
ASSERT_EQ(result->Parameters().size(), 1u);
|
||||||
ASSERT_EQ(result.intrinsic->Parameters().size(), 1u);
|
EXPECT_TRUE(result->Parameters()[0].type->Is<sem::Array>());
|
||||||
EXPECT_TRUE(result.intrinsic->Parameters()[0].type->Is<sem::Array>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchArray) {
|
TEST_F(IntrinsicTableTest, MismatchArray) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto result =
|
auto* result = table->Lookup(IntrinsicType::kArrayLength, {f32}, Source{});
|
||||||
table->Lookup(*this, IntrinsicType::kArrayLength, {f32}, Source{});
|
ASSERT_EQ(result, nullptr);
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
|
||||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchSampler) {
|
TEST_F(IntrinsicTableTest, MatchSampler) {
|
||||||
|
@ -244,13 +237,13 @@ TEST_F(IntrinsicTableTest, MatchSampler) {
|
||||||
auto* vec4_f32 = create<sem::Vector>(f32, 4);
|
auto* vec4_f32 = create<sem::Vector>(f32, 4);
|
||||||
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
|
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
|
||||||
auto* sampler = create<sem::Sampler>(ast::SamplerKind::kSampler);
|
auto* sampler = create<sem::Sampler>(ast::SamplerKind::kSampler);
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureSample,
|
auto* result = table->Lookup(IntrinsicType::kTextureSample,
|
||||||
{tex, sampler, vec2_f32}, Source{});
|
{tex, sampler, vec2_f32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureSample);
|
EXPECT_THAT(result->Type(), IntrinsicType::kTextureSample);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), vec4_f32);
|
EXPECT_THAT(result->ReturnType(), vec4_f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result->Parameters(),
|
||||||
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
||||||
Parameter{sampler, ParameterUsage::kSampler},
|
Parameter{sampler, ParameterUsage::kSampler},
|
||||||
Parameter{vec2_f32, ParameterUsage::kCoords}));
|
Parameter{vec2_f32, ParameterUsage::kCoords}));
|
||||||
|
@ -260,10 +253,10 @@ TEST_F(IntrinsicTableTest, MismatchSampler) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto* vec2_f32 = create<sem::Vector>(f32, 2);
|
auto* vec2_f32 = create<sem::Vector>(f32, 2);
|
||||||
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
|
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{});
|
{tex, f32, vec2_f32}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result, nullptr);
|
||||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchSampledTexture) {
|
TEST_F(IntrinsicTableTest, MatchSampledTexture) {
|
||||||
|
@ -272,13 +265,13 @@ TEST_F(IntrinsicTableTest, MatchSampledTexture) {
|
||||||
auto* vec2_i32 = create<sem::Vector>(i32, 2);
|
auto* vec2_i32 = create<sem::Vector>(i32, 2);
|
||||||
auto* vec4_f32 = create<sem::Vector>(f32, 4);
|
auto* vec4_f32 = create<sem::Vector>(f32, 4);
|
||||||
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
|
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{});
|
{tex, vec2_i32, i32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
EXPECT_THAT(result->Type(), IntrinsicType::kTextureLoad);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), vec4_f32);
|
EXPECT_THAT(result->ReturnType(), vec4_f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result->Parameters(),
|
||||||
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
||||||
Parameter{vec2_i32, ParameterUsage::kCoords},
|
Parameter{vec2_i32, ParameterUsage::kCoords},
|
||||||
Parameter{i32, ParameterUsage::kLevel}));
|
Parameter{i32, ParameterUsage::kLevel}));
|
||||||
|
@ -290,13 +283,13 @@ TEST_F(IntrinsicTableTest, MatchMultisampledTexture) {
|
||||||
auto* vec2_i32 = create<sem::Vector>(i32, 2);
|
auto* vec2_i32 = create<sem::Vector>(i32, 2);
|
||||||
auto* vec4_f32 = create<sem::Vector>(f32, 4);
|
auto* vec4_f32 = create<sem::Vector>(f32, 4);
|
||||||
auto* tex = create<sem::MultisampledTexture>(ast::TextureDimension::k2d, f32);
|
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{});
|
{tex, vec2_i32, i32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
EXPECT_THAT(result->Type(), IntrinsicType::kTextureLoad);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), vec4_f32);
|
EXPECT_THAT(result->ReturnType(), vec4_f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result->Parameters(),
|
||||||
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
||||||
Parameter{vec2_i32, ParameterUsage::kCoords},
|
Parameter{vec2_i32, ParameterUsage::kCoords},
|
||||||
Parameter{i32, ParameterUsage::kSampleIndex}));
|
Parameter{i32, ParameterUsage::kSampleIndex}));
|
||||||
|
@ -307,13 +300,13 @@ TEST_F(IntrinsicTableTest, MatchDepthTexture) {
|
||||||
auto* i32 = create<sem::I32>();
|
auto* i32 = create<sem::I32>();
|
||||||
auto* vec2_i32 = create<sem::Vector>(i32, 2);
|
auto* vec2_i32 = create<sem::Vector>(i32, 2);
|
||||||
auto* tex = create<sem::DepthTexture>(ast::TextureDimension::k2d);
|
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{});
|
{tex, vec2_i32, i32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
EXPECT_THAT(result->Type(), IntrinsicType::kTextureLoad);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
|
EXPECT_THAT(result->ReturnType(), f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result->Parameters(),
|
||||||
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
||||||
Parameter{vec2_i32, ParameterUsage::kCoords},
|
Parameter{vec2_i32, ParameterUsage::kCoords},
|
||||||
Parameter{i32, ParameterUsage::kLevel}));
|
Parameter{i32, ParameterUsage::kLevel}));
|
||||||
|
@ -325,13 +318,13 @@ TEST_F(IntrinsicTableTest, MatchExternalTexture) {
|
||||||
auto* vec2_i32 = create<sem::Vector>(i32, 2);
|
auto* vec2_i32 = create<sem::Vector>(i32, 2);
|
||||||
auto* vec4_f32 = create<sem::Vector>(f32, 4);
|
auto* vec4_f32 = create<sem::Vector>(f32, 4);
|
||||||
auto* tex = create<sem::ExternalTexture>();
|
auto* tex = create<sem::ExternalTexture>();
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
auto* result =
|
||||||
{tex, vec2_i32}, Source{});
|
table->Lookup(IntrinsicType::kTextureLoad, {tex, vec2_i32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
EXPECT_THAT(result->Type(), IntrinsicType::kTextureLoad);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), vec4_f32);
|
EXPECT_THAT(result->ReturnType(), vec4_f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result->Parameters(),
|
||||||
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
||||||
Parameter{vec2_i32, ParameterUsage::kCoords}));
|
Parameter{vec2_i32, ParameterUsage::kCoords}));
|
||||||
}
|
}
|
||||||
|
@ -342,18 +335,18 @@ TEST_F(IntrinsicTableTest, MatchROStorageTexture) {
|
||||||
auto* vec2_i32 = create<sem::Vector>(i32, 2);
|
auto* vec2_i32 = create<sem::Vector>(i32, 2);
|
||||||
auto* vec4_f32 = create<sem::Vector>(f32, 4);
|
auto* vec4_f32 = create<sem::Vector>(f32, 4);
|
||||||
auto* subtype =
|
auto* subtype =
|
||||||
sem::StorageTexture::SubtypeFor(ast::ImageFormat::kR16Float, Types());
|
sem::StorageTexture::SubtypeFor(ast::ImageFormat::kR32Float, Types());
|
||||||
auto* tex = create<sem::StorageTexture>(ast::TextureDimension::k2d,
|
auto* tex = create<sem::StorageTexture>(ast::TextureDimension::k2d,
|
||||||
ast::ImageFormat::kR16Float,
|
ast::ImageFormat::kR32Float,
|
||||||
ast::AccessControl::kRead, subtype);
|
ast::AccessControl::kRead, subtype);
|
||||||
|
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
auto* result =
|
||||||
{tex, vec2_i32}, Source{});
|
table->Lookup(IntrinsicType::kTextureLoad, {tex, vec2_i32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
EXPECT_THAT(result->Type(), IntrinsicType::kTextureLoad);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), vec4_f32);
|
EXPECT_THAT(result->ReturnType(), vec4_f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result->Parameters(),
|
||||||
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
||||||
Parameter{vec2_i32, ParameterUsage::kCoords}));
|
Parameter{vec2_i32, ParameterUsage::kCoords}));
|
||||||
}
|
}
|
||||||
|
@ -364,18 +357,18 @@ TEST_F(IntrinsicTableTest, MatchWOStorageTexture) {
|
||||||
auto* vec2_i32 = create<sem::Vector>(i32, 2);
|
auto* vec2_i32 = create<sem::Vector>(i32, 2);
|
||||||
auto* vec4_f32 = create<sem::Vector>(f32, 4);
|
auto* vec4_f32 = create<sem::Vector>(f32, 4);
|
||||||
auto* subtype =
|
auto* subtype =
|
||||||
sem::StorageTexture::SubtypeFor(ast::ImageFormat::kR16Float, Types());
|
sem::StorageTexture::SubtypeFor(ast::ImageFormat::kR32Float, Types());
|
||||||
auto* tex = create<sem::StorageTexture>(ast::TextureDimension::k2d,
|
auto* tex = create<sem::StorageTexture>(ast::TextureDimension::k2d,
|
||||||
ast::ImageFormat::kR16Float,
|
ast::ImageFormat::kR32Float,
|
||||||
ast::AccessControl::kWrite, subtype);
|
ast::AccessControl::kWrite, subtype);
|
||||||
|
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureStore,
|
auto* result = table->Lookup(IntrinsicType::kTextureStore,
|
||||||
{tex, vec2_i32, vec4_f32}, Source{});
|
{tex, vec2_i32, vec4_f32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureStore);
|
EXPECT_THAT(result->Type(), IntrinsicType::kTextureStore);
|
||||||
EXPECT_TRUE(result.intrinsic->ReturnType()->Is<sem::Void>());
|
EXPECT_TRUE(result->ReturnType()->Is<sem::Void>());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result->Parameters(),
|
||||||
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
ElementsAre(Parameter{tex, ParameterUsage::kTexture},
|
||||||
Parameter{vec2_i32, ParameterUsage::kCoords},
|
Parameter{vec2_i32, ParameterUsage::kCoords},
|
||||||
Parameter{vec4_f32, ParameterUsage::kValue}));
|
Parameter{vec4_f32, ParameterUsage::kValue}));
|
||||||
|
@ -385,55 +378,55 @@ TEST_F(IntrinsicTableTest, MismatchTexture) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto* i32 = create<sem::I32>();
|
auto* i32 = create<sem::I32>();
|
||||||
auto* vec2_i32 = create<sem::Vector>(i32, 2);
|
auto* vec2_i32 = create<sem::Vector>(i32, 2);
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
auto* result =
|
||||||
{f32, vec2_i32}, Source{});
|
table->Lookup(IntrinsicType::kTextureLoad, {f32, vec2_i32}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result, nullptr);
|
||||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, ImplicitLoadOnReference) {
|
TEST_F(IntrinsicTableTest, ImplicitLoadOnReference) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto result = table->Lookup(
|
auto* result = table->Lookup(
|
||||||
*this, IntrinsicType::kCos,
|
IntrinsicType::kCos,
|
||||||
{create<sem::Reference>(f32, ast::StorageClass::kNone)}, Source{});
|
{create<sem::Reference>(f32, ast::StorageClass::kNone)}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
|
EXPECT_THAT(result->Type(), IntrinsicType::kCos);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
|
EXPECT_THAT(result->ReturnType(), f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{f32}));
|
EXPECT_THAT(result->Parameters(), ElementsAre(Parameter{f32}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchOpenType) {
|
TEST_F(IntrinsicTableTest, MatchOpenType) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto result =
|
auto* result =
|
||||||
table->Lookup(*this, IntrinsicType::kClamp, {f32, f32, f32}, Source{});
|
table->Lookup(IntrinsicType::kClamp, {f32, f32, f32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
EXPECT_THAT(result->Type(), IntrinsicType::kClamp);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
|
EXPECT_THAT(result->ReturnType(), f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result->Parameters(),
|
||||||
ElementsAre(Parameter{f32}, Parameter{f32}, Parameter{f32}));
|
ElementsAre(Parameter{f32}, Parameter{f32}, Parameter{f32}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchOpenType) {
|
TEST_F(IntrinsicTableTest, MismatchOpenType) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto* u32 = create<sem::U32>();
|
auto* u32 = create<sem::U32>();
|
||||||
auto result =
|
auto* result =
|
||||||
table->Lookup(*this, IntrinsicType::kClamp, {f32, u32, f32}, Source{});
|
table->Lookup(IntrinsicType::kClamp, {f32, u32, f32}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result, nullptr);
|
||||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchOpenSizeVector) {
|
TEST_F(IntrinsicTableTest, MatchOpenSizeVector) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto* vec2_f32 = create<sem::Vector>(f32, 2);
|
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{});
|
{vec2_f32, vec2_f32, vec2_f32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
EXPECT_THAT(result->Type(), IntrinsicType::kClamp);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), vec2_f32);
|
EXPECT_THAT(result->ReturnType(), vec2_f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result->Parameters(),
|
||||||
ElementsAre(Parameter{vec2_f32}, Parameter{vec2_f32},
|
ElementsAre(Parameter{vec2_f32}, Parameter{vec2_f32},
|
||||||
Parameter{vec2_f32}));
|
Parameter{vec2_f32}));
|
||||||
}
|
}
|
||||||
|
@ -442,110 +435,106 @@ TEST_F(IntrinsicTableTest, MismatchOpenSizeVector) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto* u32 = create<sem::U32>();
|
auto* u32 = create<sem::U32>();
|
||||||
auto* vec2_f32 = create<sem::Vector>(f32, 2);
|
auto* vec2_f32 = create<sem::Vector>(f32, 2);
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
auto* result =
|
||||||
{vec2_f32, u32, vec2_f32}, Source{});
|
table->Lookup(IntrinsicType::kClamp, {vec2_f32, u32, vec2_f32}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result, nullptr);
|
||||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchOpenSizeMatrix) {
|
TEST_F(IntrinsicTableTest, MatchOpenSizeMatrix) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto* vec3_f32 = create<sem::Vector>(f32, 3);
|
auto* vec3_f32 = create<sem::Vector>(f32, 3);
|
||||||
auto* mat3_f32 = create<sem::Matrix>(vec3_f32, 3);
|
auto* mat3_f32 = create<sem::Matrix>(vec3_f32, 3);
|
||||||
auto result =
|
auto* result =
|
||||||
table->Lookup(*this, IntrinsicType::kDeterminant, {mat3_f32}, Source{});
|
table->Lookup(IntrinsicType::kDeterminant, {mat3_f32}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result, nullptr) << Diagnostics().str();
|
||||||
ASSERT_EQ(result.diagnostics.str(), "");
|
ASSERT_EQ(Diagnostics().str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kDeterminant);
|
EXPECT_THAT(result->Type(), IntrinsicType::kDeterminant);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), f32);
|
EXPECT_THAT(result->ReturnType(), f32);
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{mat3_f32}));
|
EXPECT_THAT(result->Parameters(), ElementsAre(Parameter{mat3_f32}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchOpenSizeMatrix) {
|
TEST_F(IntrinsicTableTest, MismatchOpenSizeMatrix) {
|
||||||
auto* f32 = create<sem::F32>();
|
auto* f32 = create<sem::F32>();
|
||||||
auto* vec2_f32 = create<sem::Vector>(f32, 2);
|
auto* vec2_f32 = create<sem::Vector>(f32, 2);
|
||||||
auto* mat3x2_f32 = create<sem::Matrix>(vec2_f32, 3);
|
auto* mat3x2_f32 = create<sem::Matrix>(vec2_f32, 3);
|
||||||
auto result =
|
auto* result =
|
||||||
table->Lookup(*this, IntrinsicType::kDeterminant, {mat3x2_f32}, Source{});
|
table->Lookup(IntrinsicType::kDeterminant, {mat3x2_f32}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result, nullptr);
|
||||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, OverloadOrderByNumberOfParameters) {
|
TEST_F(IntrinsicTableTest, OverloadOrderByNumberOfParameters) {
|
||||||
// None of the arguments match, so expect the overloads with 2 parameters to
|
// None of the arguments match, so expect the overloads with 2 parameters to
|
||||||
// come first
|
// come first
|
||||||
auto* bool_ = create<sem::Bool>();
|
auto* bool_ = create<sem::Bool>();
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureDimensions,
|
table->Lookup(IntrinsicType::kTextureDimensions, {bool_, bool_}, Source{});
|
||||||
{bool_, bool_}, Source{});
|
ASSERT_EQ(Diagnostics().str(),
|
||||||
ASSERT_EQ(result.diagnostics.str(),
|
|
||||||
R"(error: no matching call to textureDimensions(bool, bool)
|
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<T>, level: i32) -> vec2<i32>
|
||||||
textureDimensions(texture : texture_2d_array<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_3d<T>, level: i32) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_cube<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_cube_array<T>, level: i32) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_depth_2d, level : i32) -> vec2<i32>
|
textureDimensions(texture: texture_depth_2d, level: i32) -> vec2<i32>
|
||||||
textureDimensions(texture : texture_depth_2d_array, 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, level: i32) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_depth_cube_array, level : i32) -> vec3<i32>
|
textureDimensions(texture: texture_depth_cube_array, level: i32) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_1d<T>) -> i32
|
textureDimensions(texture: texture_1d<T>) -> i32
|
||||||
textureDimensions(texture : texture_2d<T>) -> vec2<i32>
|
textureDimensions(texture: texture_2d<T>) -> vec2<i32>
|
||||||
textureDimensions(texture : texture_2d_array<T>) -> vec2<i32>
|
textureDimensions(texture: texture_2d_array<T>) -> vec2<i32>
|
||||||
textureDimensions(texture : texture_3d<T>) -> vec3<i32>
|
textureDimensions(texture: texture_3d<T>) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_cube<T>) -> vec3<i32>
|
textureDimensions(texture: texture_cube<T>) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_cube_array<T>) -> vec3<i32>
|
textureDimensions(texture: texture_cube_array<T>) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_multisampled_2d<T>) -> vec2<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) -> vec2<i32>
|
textureDimensions(texture: texture_depth_2d_array) -> vec2<i32>
|
||||||
textureDimensions(texture : texture_depth_2d_array) -> vec2<i32>
|
textureDimensions(texture: texture_depth_cube) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_depth_cube) -> vec3<i32>
|
textureDimensions(texture: texture_depth_cube_array) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_depth_cube_array) -> vec3<i32>
|
textureDimensions(texture: texture_storage_1d<F, A>) -> i32
|
||||||
textureDimensions(texture : texture_storage_1d<F, A>) -> i32
|
textureDimensions(texture: texture_storage_2d<F, A>) -> vec2<i32>
|
||||||
textureDimensions(texture : texture_storage_2d<F, A>) -> vec2<i32>
|
textureDimensions(texture: texture_storage_2d_array<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_storage_3d<F, A>) -> vec3<i32>
|
textureDimensions(texture: texture_external) -> vec2<i32>
|
||||||
textureDimensions(texture : texture_external) -> vec2<i32>
|
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, OverloadOrderByMatchingParameter) {
|
TEST_F(IntrinsicTableTest, OverloadOrderByMatchingParameter) {
|
||||||
auto* tex = create<sem::DepthTexture>(ast::TextureDimension::k2d);
|
auto* tex = create<sem::DepthTexture>(ast::TextureDimension::k2d);
|
||||||
auto* bool_ = create<sem::Bool>();
|
auto* bool_ = create<sem::Bool>();
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureDimensions,
|
table->Lookup(IntrinsicType::kTextureDimensions, {tex, bool_}, Source{});
|
||||||
{tex, bool_}, Source{});
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
result.diagnostics.str(),
|
Diagnostics().str(),
|
||||||
R"(error: no matching call to textureDimensions(texture_depth_2d, bool)
|
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, level: i32) -> vec2<i32>
|
||||||
textureDimensions(texture : texture_depth_2d) -> vec2<i32>
|
textureDimensions(texture: texture_depth_2d) -> vec2<i32>
|
||||||
textureDimensions(texture : texture_2d<T>, level : i32) -> vec2<i32>
|
textureDimensions(texture: texture_2d<T>, level: i32) -> vec2<i32>
|
||||||
textureDimensions(texture : texture_2d_array<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_3d<T>, level: i32) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_cube<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_cube_array<T>, level: i32) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_depth_2d_array, 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, level: i32) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_depth_cube_array, level : i32) -> vec3<i32>
|
textureDimensions(texture: texture_depth_cube_array, level: i32) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_1d<T>) -> i32
|
textureDimensions(texture: texture_1d<T>) -> i32
|
||||||
textureDimensions(texture : texture_2d<T>) -> vec2<i32>
|
textureDimensions(texture: texture_2d<T>) -> vec2<i32>
|
||||||
textureDimensions(texture : texture_2d_array<T>) -> vec2<i32>
|
textureDimensions(texture: texture_2d_array<T>) -> vec2<i32>
|
||||||
textureDimensions(texture : texture_3d<T>) -> vec3<i32>
|
textureDimensions(texture: texture_3d<T>) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_cube<T>) -> vec3<i32>
|
textureDimensions(texture: texture_cube<T>) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_cube_array<T>) -> vec3<i32>
|
textureDimensions(texture: texture_cube_array<T>) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_multisampled_2d<T>) -> vec2<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_2d_array) -> vec2<i32>
|
textureDimensions(texture: texture_depth_cube) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_depth_cube) -> vec3<i32>
|
textureDimensions(texture: texture_depth_cube_array) -> vec3<i32>
|
||||||
textureDimensions(texture : texture_depth_cube_array) -> vec3<i32>
|
textureDimensions(texture: texture_storage_1d<F, A>) -> i32
|
||||||
textureDimensions(texture : texture_storage_1d<F, A>) -> i32
|
textureDimensions(texture: texture_storage_2d<F, A>) -> vec2<i32>
|
||||||
textureDimensions(texture : texture_storage_2d<F, A>) -> vec2<i32>
|
textureDimensions(texture: texture_storage_2d_array<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_storage_3d<F, A>) -> vec3<i32>
|
textureDimensions(texture: texture_external) -> vec2<i32>
|
||||||
textureDimensions(texture : texture_external) -> vec2<i32>
|
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -468,8 +468,8 @@ TEST_F(ResolverIntrinsicTest, Select_Error_NoParams) {
|
||||||
R"(error: no matching call to select()
|
R"(error: no matching call to select()
|
||||||
|
|
||||||
2 candidate functions:
|
2 candidate functions:
|
||||||
select(T, T, bool) -> 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 scalar
|
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)
|
R"(error: no matching call to select(i32, i32, i32)
|
||||||
|
|
||||||
2 candidate functions:
|
2 candidate functions:
|
||||||
select(T, T, bool) -> 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 scalar
|
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)
|
R"(error: no matching call to select(mat2x2<f32>, mat2x2<f32>, bool)
|
||||||
|
|
||||||
2 candidate functions:
|
2 candidate functions:
|
||||||
select(T, T, bool) -> 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 scalar
|
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)
|
R"(error: no matching call to select(f32, vec2<f32>, bool)
|
||||||
|
|
||||||
2 candidate functions:
|
2 candidate functions:
|
||||||
select(T, T, bool) -> 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 scalar
|
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)
|
R"(error: no matching call to select(vec2<f32>, vec3<f32>, bool)
|
||||||
|
|
||||||
2 candidate functions:
|
2 candidate functions:
|
||||||
select(T, T, bool) -> 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 scalar
|
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)
|
Resolver::Resolver(ProgramBuilder* builder)
|
||||||
: builder_(builder),
|
: builder_(builder),
|
||||||
diagnostics_(builder->Diagnostics()),
|
diagnostics_(builder->Diagnostics()),
|
||||||
intrinsic_table_(IntrinsicTable::Create()) {}
|
intrinsic_table_(IntrinsicTable::Create(*builder)) {}
|
||||||
|
|
||||||
Resolver::~Resolver() = default;
|
Resolver::~Resolver() = default;
|
||||||
|
|
||||||
|
@ -1694,17 +1694,15 @@ bool Resolver::IntrinsicCall(ast::CallExpression* call,
|
||||||
arg_tys.emplace_back(TypeOf(expr));
|
arg_tys.emplace_back(TypeOf(expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = intrinsic_table_->Lookup(*builder_, intrinsic_type, arg_tys,
|
auto* result =
|
||||||
call->source());
|
intrinsic_table_->Lookup(intrinsic_type, arg_tys, call->source());
|
||||||
if (!result.intrinsic) {
|
if (!result) {
|
||||||
// Intrinsic lookup failed.
|
|
||||||
diagnostics_.add(result.diagnostics);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
builder_->Sem().Add(call, builder_->create<sem::Call>(call, result.intrinsic,
|
builder_->Sem().Add(
|
||||||
current_statement_));
|
call, builder_->create<sem::Call>(call, result, current_statement_));
|
||||||
SetType(call, result.intrinsic->ReturnType());
|
SetType(call, result->ReturnType());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,7 @@ type Parameter struct {
|
||||||
// Format implements the fmt.Formatter interface
|
// Format implements the fmt.Formatter interface
|
||||||
func (p Parameter) Format(w fmt.State, verb rune) {
|
func (p Parameter) Format(w fmt.State, verb rune) {
|
||||||
if p.Name != "" {
|
if p.Name != "" {
|
||||||
fmt.Fprintf(w, "%v ", p.Name)
|
fmt.Fprintf(w, "%v: ", p.Name)
|
||||||
}
|
}
|
||||||
p.Type.Format(w, verb)
|
p.Type.Format(w, verb)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,9 @@ import (
|
||||||
|
|
||||||
type generator struct {
|
type generator struct {
|
||||||
s *sem.Sem
|
s *sem.Sem
|
||||||
|
cached struct {
|
||||||
|
intrinsicTable *IntrinsicTable // lazily built by intrinsicTable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate executes the template tmpl using the provided semantic
|
// 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{}),
|
"IsTemplateEnumParam": is(&sem.TemplateEnumParam{}),
|
||||||
"IsFirstIn": isFirstIn,
|
"IsFirstIn": isFirstIn,
|
||||||
"IsLastIn": isLastIn,
|
"IsLastIn": isLastIn,
|
||||||
|
"IntrinsicTable": g.intrinsicTable,
|
||||||
}).
|
}).
|
||||||
Option("missingkey=error").
|
Option("missingkey=error").
|
||||||
Parse(tmpl)
|
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
|
// Map is a simple generic key-value map, which can be used in the template
|
||||||
type Map map[interface{}]interface{}
|
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"
|
"dawn.googlesource.com/tint/tools/src/glob"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defProjectRelPath = "src/intrinsics.def"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := run(); err != nil {
|
if err := run(); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
@ -58,7 +60,7 @@ optional flags:`)
|
||||||
func run() error {
|
func run() error {
|
||||||
// Load the intrinsics definition file
|
// Load the intrinsics definition file
|
||||||
projectRoot := fileutils.ProjectRoot()
|
projectRoot := fileutils.ProjectRoot()
|
||||||
defPath := filepath.Join(projectRoot, "src/intrinsics.def")
|
defPath := filepath.Join(projectRoot, defProjectRelPath)
|
||||||
|
|
||||||
defSource, err := ioutil.ReadFile(defPath)
|
defSource, err := ioutil.ReadFile(defPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -66,7 +68,7 @@ func run() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the definition file to produce an AST
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue