mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-04 20:25:56 +00:00
All includes from .cc to .h are preserved, even when transitively included. It's clear that there are far too many includes in header files, and we should be more aggressive with forward declarations. tint:532 will continue to track this work. There are, however, plenty of includes that have accumulated over time which are no longer required directly or transitively, so this change starts with a clean slate of *required* includes. Bug: tint:532 Change-Id: Ie1718dad565f8309fa180ef91bcf3920e76dba18 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/44042 Commit-Queue: Ben Clayton <bclayton@google.com> Reviewed-by: Antonio Maiorano <amaiorano@google.com>
1471 lines
72 KiB
C++
1471 lines
72 KiB
C++
// 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.
|
|
|
|
#include "src/intrinsic_table.h"
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
|
|
#include "src/program_builder.h"
|
|
#include "src/type/access_control_type.h"
|
|
#include "src/type/depth_texture_type.h"
|
|
#include "src/type/multisampled_texture_type.h"
|
|
#include "src/type/sampled_texture_type.h"
|
|
#include "src/type/storage_texture_type.h"
|
|
|
|
namespace tint {
|
|
namespace {
|
|
|
|
/// OpenTypes are the symbols used for templated types in overload signatures
|
|
enum class OpenType {
|
|
T,
|
|
Count, // Number of entries in the enum. Not a usable symbol.
|
|
};
|
|
|
|
/// OpenNumber are the symbols used for templated integers in overload
|
|
/// signatures
|
|
enum class OpenNumber {
|
|
N, // Typically used for vecN
|
|
M, // Typically used for matNxM
|
|
F, // Typically used for texture_storage_2d<F>
|
|
};
|
|
|
|
/// @return a string of the OpenType symbol `ty`
|
|
const char* str(OpenType ty) {
|
|
switch (ty) {
|
|
case OpenType::T:
|
|
return "T";
|
|
|
|
case OpenType::Count:
|
|
break;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/// @return a string of the OpenNumber symbol `num`
|
|
const char* str(OpenNumber num) {
|
|
switch (num) {
|
|
case OpenNumber::N:
|
|
return "N";
|
|
case OpenNumber::M:
|
|
return "M";
|
|
case OpenNumber::F:
|
|
return "F";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/// A Matcher is an interface of a class used to match an overload parameter,
|
|
/// return type, or open type.
|
|
class Matcher {
|
|
public:
|
|
/// Current state passed to Match()
|
|
struct MatchState {
|
|
/// The map of open types. A new entry is assigned the first time an
|
|
/// OpenType is encountered. If the OpenType is encountered again, a
|
|
/// comparison is made to see if the type is consistent.
|
|
std::unordered_map<OpenType, type::Type*> open_types;
|
|
/// The map of open numbers. A new entry is assigned the first time an
|
|
/// OpenNumber is encountered. If the OpenNumber is encountered again, a
|
|
/// comparison is made to see if the number is consistent.
|
|
std::unordered_map<OpenNumber, uint32_t> open_numbers;
|
|
};
|
|
|
|
/// Destructor
|
|
virtual ~Matcher() = default;
|
|
|
|
/// Checks whether the given argument type matches.
|
|
/// Aliases are automatically unwrapped before matching.
|
|
/// Match may add to, or compare against the open types and numbers in state.
|
|
/// @returns true if the argument type is as expected.
|
|
bool Match(MatchState& state, type::Type* argument_type) const {
|
|
auto* unwrapped = argument_type->UnwrapAliasIfNeeded();
|
|
return MatchUnwrapped(state, unwrapped);
|
|
}
|
|
|
|
/// @return true if the matcher is expecting a pointer. If this method returns
|
|
/// false and the argument is a pointer type, then the argument should be
|
|
/// dereferenced before calling.
|
|
virtual bool ExpectsPointer() const { return false; }
|
|
|
|
/// @return a string representation of the matcher. Used for printing error
|
|
/// messages when no overload is found.
|
|
virtual std::string str() const = 0;
|
|
|
|
protected:
|
|
/// Checks whether the given alias-unwrapped argument type matches.
|
|
/// Match may add to, or compare against the open types and numbers in state.
|
|
/// @returns true if the argument type is as expected.
|
|
virtual bool MatchUnwrapped(MatchState& state,
|
|
type::Type* argument_type) const = 0;
|
|
|
|
/// Checks `state.open_type` to see if the OpenType `t` is equal to the type
|
|
/// `ty`. If `state.open_type` does not contain an entry for `t`, then `ty`
|
|
/// is added and returns true.
|
|
bool MatchOpenType(MatchState& state, OpenType t, type::Type* ty) const {
|
|
auto it = state.open_types.find(t);
|
|
if (it != state.open_types.end()) {
|
|
return it->second == ty;
|
|
}
|
|
state.open_types[t] = ty;
|
|
return true;
|
|
}
|
|
|
|
/// Checks `state.open_numbers` to see if the OpenNumber `n` is equal to
|
|
/// `val`. If `state.open_numbers` does not contain an entry for `n`, then
|
|
/// `val` is added and returns true.
|
|
bool MatchOpenNumber(MatchState& state, OpenNumber n, uint32_t val) const {
|
|
auto it = state.open_numbers.find(n);
|
|
if (it != state.open_numbers.end()) {
|
|
return it->second == val;
|
|
}
|
|
state.open_numbers[n] = val;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/// Builder is an extension of the Matcher interface that can also build the
|
|
/// expected type. Builders are used to generate the parameter and return types
|
|
/// on successful overload match.
|
|
class Builder : public Matcher {
|
|
public:
|
|
/// Final matched state passed to Build()
|
|
struct BuildState {
|
|
/// The type manager used to construct new types
|
|
type::Manager& ty_mgr;
|
|
/// The final resolved list of open types
|
|
std::unordered_map<OpenType, type::Type*> const open_types;
|
|
/// The final resolved list of open numbers
|
|
std::unordered_map<OpenNumber, uint32_t> const open_numbers;
|
|
};
|
|
|
|
/// Destructor
|
|
~Builder() override = default;
|
|
|
|
/// Constructs and returns the expected type
|
|
virtual type::Type* Build(BuildState& state) const = 0;
|
|
};
|
|
|
|
/// OpenTypeBuilder is a Matcher / Builder for an open type (T etc).
|
|
/// The OpenTypeBuilder will match against any type (so long as it is consistent
|
|
/// for the overload), and Build() will build the type it matched against.
|
|
class OpenTypeBuilder : public Builder {
|
|
public:
|
|
explicit OpenTypeBuilder(OpenType open_type) : open_type_(open_type) {}
|
|
|
|
bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
|
|
return MatchOpenType(state, open_type_, ty);
|
|
}
|
|
|
|
type::Type* Build(BuildState& state) const override {
|
|
return state.open_types.at(open_type_);
|
|
}
|
|
|
|
std::string str() const override { return tint::str(open_type_); }
|
|
|
|
private:
|
|
OpenType open_type_;
|
|
};
|
|
|
|
/// VoidBuilder is a Matcher / Builder for void types.
|
|
class VoidBuilder : public Builder {
|
|
public:
|
|
bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
|
|
return ty->Is<type::Void>();
|
|
}
|
|
type::Type* Build(BuildState& state) const override {
|
|
return state.ty_mgr.Get<type::Void>();
|
|
}
|
|
std::string str() const override { return "void"; }
|
|
};
|
|
|
|
/// BoolBuilder is a Matcher / Builder for boolean types.
|
|
class BoolBuilder : public Builder {
|
|
public:
|
|
bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
|
|
return ty->Is<type::Bool>();
|
|
}
|
|
type::Type* Build(BuildState& state) const override {
|
|
return state.ty_mgr.Get<type::Bool>();
|
|
}
|
|
std::string str() const override { return "bool"; }
|
|
};
|
|
|
|
/// F32Builder is a Matcher / Builder for f32 types.
|
|
class F32Builder : public Builder {
|
|
public:
|
|
bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
|
|
return ty->Is<type::F32>();
|
|
}
|
|
type::Type* Build(BuildState& state) const override {
|
|
return state.ty_mgr.Get<type::F32>();
|
|
}
|
|
std::string str() const override { return "f32"; }
|
|
};
|
|
|
|
/// U32Builder is a Matcher / Builder for u32 types.
|
|
class U32Builder : public Builder {
|
|
public:
|
|
bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
|
|
return ty->Is<type::U32>();
|
|
}
|
|
type::Type* Build(BuildState& state) const override {
|
|
return state.ty_mgr.Get<type::U32>();
|
|
}
|
|
std::string str() const override { return "u32"; }
|
|
};
|
|
|
|
/// I32Builder is a Matcher / Builder for i32 types.
|
|
class I32Builder : public Builder {
|
|
public:
|
|
bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
|
|
return ty->Is<type::I32>();
|
|
}
|
|
type::Type* Build(BuildState& state) const override {
|
|
return state.ty_mgr.Get<type::I32>();
|
|
}
|
|
std::string str() const override { return "i32"; }
|
|
};
|
|
|
|
/// IU32Matcher is a Matcher for i32 or u32 types.
|
|
class IU32Matcher : public Matcher {
|
|
public:
|
|
bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
|
|
return ty->Is<type::I32>() || ty->Is<type::U32>();
|
|
}
|
|
std::string str() const override { return "i32 or u32"; }
|
|
};
|
|
|
|
/// FIU32Matcher is a Matcher for f32, i32 or u32 types.
|
|
class FIU32Matcher : public Matcher {
|
|
public:
|
|
bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
|
|
return ty->Is<type::F32>() || ty->Is<type::I32>() || ty->Is<type::U32>();
|
|
}
|
|
std::string str() const override { return "f32, i32 or u32"; }
|
|
};
|
|
|
|
/// ScalarMatcher is a Matcher for f32, i32, u32 or boolean types.
|
|
class ScalarMatcher : public Matcher {
|
|
public:
|
|
bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
|
|
return ty->is_scalar();
|
|
}
|
|
std::string str() const override { return "scalar"; }
|
|
};
|
|
|
|
/// OpenSizeVecBuilder is a Matcher / Builder for vector types of an open number
|
|
/// size.
|
|
class OpenSizeVecBuilder : public Builder {
|
|
public:
|
|
OpenSizeVecBuilder(OpenNumber size, Builder* element_builder)
|
|
: size_(size), element_builder_(element_builder) {}
|
|
|
|
bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
|
|
if (auto* vec = ty->As<type::Vector>()) {
|
|
if (!MatchOpenNumber(state, size_, vec->size())) {
|
|
return false;
|
|
}
|
|
return element_builder_->Match(state, vec->type());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
type::Type* Build(BuildState& state) const override {
|
|
auto* el = element_builder_->Build(state);
|
|
auto n = state.open_numbers.at(size_);
|
|
return state.ty_mgr.Get<type::Vector>(el, n);
|
|
}
|
|
|
|
std::string str() const override {
|
|
return "vec" + std::string(tint::str(size_)) + "<" +
|
|
element_builder_->str() + ">";
|
|
}
|
|
|
|
protected:
|
|
OpenNumber const size_;
|
|
Builder* const element_builder_;
|
|
};
|
|
|
|
/// VecBuilder is a Matcher / Builder for vector types of a fixed size.
|
|
class VecBuilder : public Builder {
|
|
public:
|
|
VecBuilder(uint32_t size, Builder* element_builder)
|
|
: size_(size), element_builder_(element_builder) {}
|
|
|
|
bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
|
|
if (auto* vec = ty->As<type::Vector>()) {
|
|
if (vec->size() == size_) {
|
|
return element_builder_->Match(state, vec->type());
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
type::Type* Build(BuildState& state) const override {
|
|
auto* el = element_builder_->Build(state);
|
|
return state.ty_mgr.Get<type::Vector>(el, size_);
|
|
}
|
|
|
|
std::string str() const override {
|
|
return "vec" + std::to_string(size_) + "<" + element_builder_->str() + ">";
|
|
}
|
|
|
|
protected:
|
|
const uint32_t size_;
|
|
Builder* const element_builder_;
|
|
};
|
|
|
|
/// OpenSizeVecBuilder is a Matcher / Builder for matrix types of an open number
|
|
/// column and row size.
|
|
class OpenSizeMatBuilder : public Builder {
|
|
public:
|
|
OpenSizeMatBuilder(OpenNumber columns,
|
|
OpenNumber rows,
|
|
Builder* element_builder)
|
|
: columns_(columns), rows_(rows), element_builder_(element_builder) {}
|
|
|
|
bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
|
|
if (auto* mat = ty->As<type::Matrix>()) {
|
|
if (!MatchOpenNumber(state, columns_, mat->columns())) {
|
|
return false;
|
|
}
|
|
if (!MatchOpenNumber(state, rows_, mat->rows())) {
|
|
return false;
|
|
}
|
|
return element_builder_->Match(state, mat->type());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
type::Type* Build(BuildState& state) const override {
|
|
auto* el = element_builder_->Build(state);
|
|
auto columns = state.open_numbers.at(columns_);
|
|
auto rows = state.open_numbers.at(rows_);
|
|
return state.ty_mgr.Get<type::Matrix>(el, rows, columns);
|
|
}
|
|
|
|
std::string str() const override {
|
|
return "mat" + std::string(tint::str(columns_)) + "x" +
|
|
std::string(tint::str(rows_)) + "<" + element_builder_->str() + ">";
|
|
}
|
|
|
|
protected:
|
|
OpenNumber const columns_;
|
|
OpenNumber const rows_;
|
|
Builder* const element_builder_;
|
|
};
|
|
|
|
/// PtrBuilder is a Matcher / Builder for pointer types.
|
|
class PtrBuilder : public Builder {
|
|
public:
|
|
explicit PtrBuilder(Builder* element_builder)
|
|
: element_builder_(element_builder) {}
|
|
|
|
bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
|
|
if (auto* ptr = ty->As<type::Pointer>()) {
|
|
return element_builder_->Match(state, ptr->type());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
type::Type* Build(BuildState& state) const override {
|
|
auto* el = element_builder_->Build(state);
|
|
return state.ty_mgr.Get<type::Pointer>(el, ast::StorageClass::kNone);
|
|
}
|
|
|
|
bool ExpectsPointer() const override { return true; }
|
|
|
|
std::string str() const override {
|
|
return "ptr<" + element_builder_->str() + ">";
|
|
}
|
|
|
|
private:
|
|
Builder* const element_builder_;
|
|
};
|
|
|
|
/// ArrayBuilder is a Matcher / Builder for runtime sized array types.
|
|
class ArrayBuilder : public Builder {
|
|
public:
|
|
explicit ArrayBuilder(Builder* element_builder)
|
|
: element_builder_(element_builder) {}
|
|
|
|
bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
|
|
if (auto* arr = ty->As<type::Array>()) {
|
|
if (arr->size() == 0) {
|
|
return element_builder_->Match(state, arr->type());
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
type::Type* Build(BuildState& state) const override {
|
|
auto* el = element_builder_->Build(state);
|
|
return state.ty_mgr.Get<type::Array>(el, 0, ast::ArrayDecorationList{});
|
|
}
|
|
|
|
std::string str() const override {
|
|
return "array<" + element_builder_->str() + ">";
|
|
}
|
|
|
|
private:
|
|
Builder* const element_builder_;
|
|
};
|
|
|
|
/// SampledTextureBuilder is a Matcher / Builder for sampled texture types.
|
|
class SampledTextureBuilder : public Builder {
|
|
public:
|
|
explicit SampledTextureBuilder(type::TextureDimension dimensions,
|
|
Builder* type_builder)
|
|
: dimensions_(dimensions), type_builder_(type_builder) {}
|
|
|
|
bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
|
|
if (auto* tex = ty->As<type::SampledTexture>()) {
|
|
if (tex->dim() == dimensions_) {
|
|
return type_builder_->Match(state, tex->type());
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
type::Type* Build(BuildState& state) const override {
|
|
auto* type = type_builder_->Build(state);
|
|
return state.ty_mgr.Get<type::SampledTexture>(dimensions_, type);
|
|
}
|
|
|
|
std::string str() const override {
|
|
std::stringstream ss;
|
|
ss << "texture_" << dimensions_ << "<" << type_builder_->str() << ">";
|
|
return ss.str();
|
|
}
|
|
|
|
private:
|
|
type::TextureDimension const dimensions_;
|
|
Builder* const type_builder_;
|
|
};
|
|
|
|
/// MultisampledTextureBuilder is a Matcher / Builder for multisampled texture
|
|
/// types.
|
|
class MultisampledTextureBuilder : public Builder {
|
|
public:
|
|
explicit MultisampledTextureBuilder(type::TextureDimension dimensions,
|
|
Builder* type_builder)
|
|
: dimensions_(dimensions), type_builder_(type_builder) {}
|
|
|
|
bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
|
|
if (auto* tex = ty->As<type::MultisampledTexture>()) {
|
|
if (tex->dim() == dimensions_) {
|
|
return type_builder_->Match(state, tex->type());
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
type::Type* Build(BuildState& state) const override {
|
|
auto* type = type_builder_->Build(state);
|
|
return state.ty_mgr.Get<type::MultisampledTexture>(dimensions_, type);
|
|
}
|
|
|
|
std::string str() const override {
|
|
std::stringstream ss;
|
|
ss << "texture_multisampled_" << dimensions_ << "<" << type_builder_->str()
|
|
<< ">";
|
|
return ss.str();
|
|
}
|
|
|
|
private:
|
|
type::TextureDimension const dimensions_;
|
|
Builder* const type_builder_;
|
|
};
|
|
|
|
/// DepthTextureBuilder is a Matcher / Builder for depth texture types.
|
|
class DepthTextureBuilder : public Builder {
|
|
public:
|
|
explicit DepthTextureBuilder(type::TextureDimension dimensions)
|
|
: dimensions_(dimensions) {}
|
|
|
|
bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
|
|
if (auto* tex = ty->As<type::DepthTexture>()) {
|
|
return tex->dim() == dimensions_;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
type::Type* Build(BuildState& state) const override {
|
|
return state.ty_mgr.Get<type::DepthTexture>(dimensions_);
|
|
}
|
|
|
|
std::string str() const override {
|
|
std::stringstream ss;
|
|
ss << "texture_depth_" << dimensions_;
|
|
return ss.str();
|
|
}
|
|
|
|
private:
|
|
type::TextureDimension const dimensions_;
|
|
};
|
|
|
|
/// StorageTextureBuilder is a Matcher / Builder for storage texture types of
|
|
/// the given texel and channel formats.
|
|
class StorageTextureBuilder : public Builder {
|
|
public:
|
|
explicit StorageTextureBuilder(
|
|
type::TextureDimension dimensions,
|
|
OpenNumber texel_format, // a.k.a "image format"
|
|
OpenType channel_format) // a.k.a "storage subtype"
|
|
: dimensions_(dimensions),
|
|
texel_format_(texel_format),
|
|
channel_format_(channel_format) {}
|
|
|
|
bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
|
|
if (auto* ac = ty->As<type::AccessControl>()) {
|
|
// If we have an storage texture argument that's got an access control
|
|
// type wrapped around it, accept it. Signatures that don't include an
|
|
// access control imply any access. Example:
|
|
// textureDimensions(t : texture_storage_1d<F>) -> i32
|
|
ty = ac->type();
|
|
}
|
|
|
|
if (auto* tex = ty->As<type::StorageTexture>()) {
|
|
if (MatchOpenNumber(state, texel_format_,
|
|
static_cast<uint32_t>(tex->image_format()))) {
|
|
if (MatchOpenType(state, channel_format_, tex->type())) {
|
|
return tex->dim() == dimensions_;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
type::Type* Build(BuildState& state) const override {
|
|
auto texel_format =
|
|
static_cast<type::ImageFormat>(state.open_numbers.at(texel_format_));
|
|
auto* channel_format = state.open_types.at(channel_format_);
|
|
return state.ty_mgr.Get<type::StorageTexture>(dimensions_, texel_format,
|
|
channel_format);
|
|
}
|
|
|
|
std::string str() const override {
|
|
std::stringstream ss;
|
|
ss << "texture_storage_" << dimensions_ << "<F>";
|
|
return ss.str();
|
|
}
|
|
|
|
private:
|
|
type::TextureDimension const dimensions_;
|
|
OpenNumber const texel_format_;
|
|
OpenType const channel_format_;
|
|
};
|
|
|
|
/// SamplerBuilder is a Matcher / Builder for sampler types of the given kind.
|
|
class SamplerBuilder : public Builder {
|
|
public:
|
|
explicit SamplerBuilder(type::SamplerKind kind) : kind_(kind) {}
|
|
|
|
bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
|
|
if (auto* sampler = ty->As<type::Sampler>()) {
|
|
return sampler->kind() == kind_;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
type::Type* Build(BuildState& state) const override {
|
|
return state.ty_mgr.Get<type::Sampler>(kind_);
|
|
}
|
|
|
|
std::string str() const override {
|
|
switch (kind_) {
|
|
case type::SamplerKind::kSampler:
|
|
return "sampler";
|
|
case type::SamplerKind::kComparisonSampler:
|
|
return "sampler_comparison";
|
|
}
|
|
return "sampler";
|
|
}
|
|
|
|
private:
|
|
type::SamplerKind const kind_;
|
|
};
|
|
|
|
/// AccessControlBuilder is a Matcher / Builder for AccessControl types
|
|
class AccessControlBuilder : public Builder {
|
|
public:
|
|
explicit AccessControlBuilder(ast::AccessControl access_control,
|
|
Builder* type)
|
|
: access_control_(access_control), type_(type) {}
|
|
|
|
bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
|
|
if (auto* ac = ty->As<type::AccessControl>()) {
|
|
if (ac->access_control() == access_control_) {
|
|
return type_->Match(state, ty);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
type::Type* Build(BuildState& state) const override {
|
|
auto* ty = type_->Build(state);
|
|
return state.ty_mgr.Get<type::AccessControl>(access_control_, ty);
|
|
}
|
|
|
|
std::string str() const override {
|
|
std::stringstream ss;
|
|
ss << "[[access(" << access_control_ << ")]] " << type_->str();
|
|
return ss.str();
|
|
}
|
|
|
|
private:
|
|
ast::AccessControl const access_control_;
|
|
Builder* const type_;
|
|
};
|
|
|
|
/// Impl is the private implementation of the IntrinsicTable interface.
|
|
class Impl : public IntrinsicTable {
|
|
public:
|
|
Impl();
|
|
|
|
IntrinsicTable::Result Lookup(ProgramBuilder& builder,
|
|
semantic::IntrinsicType type,
|
|
const std::vector<type::Type*>& args,
|
|
const Source& source) const override;
|
|
|
|
/// Holds the information about a single overload parameter used for matching
|
|
struct Parameter {
|
|
Parameter(
|
|
Builder* m) // NOLINT - implicit constructor required for Register()
|
|
: matcher(m) {}
|
|
Parameter(semantic::Parameter::Usage u, Builder* m)
|
|
: matcher(m), usage(u) {}
|
|
|
|
Builder* const matcher;
|
|
semantic::Parameter::Usage const usage = semantic::Parameter::Usage::kNone;
|
|
};
|
|
|
|
/// A single overload definition.
|
|
struct Overload {
|
|
/// Attempts to match this overload given the IntrinsicType and argument
|
|
/// types. If a match is made, the build intrinsic is returned, otherwise
|
|
/// `match_score` is assigned a score of how closely the overload matched
|
|
/// (positive representing a greater match), and nullptr is returned.
|
|
semantic::Intrinsic* Match(ProgramBuilder& builder,
|
|
semantic::IntrinsicType type,
|
|
const std::vector<type::Type*>& arg_types,
|
|
diag::List& diagnostics,
|
|
int& match_score) const;
|
|
|
|
semantic::IntrinsicType type;
|
|
Builder* return_type;
|
|
std::vector<Parameter> parameters;
|
|
std::unordered_map<OpenType, Matcher*> open_type_matchers;
|
|
};
|
|
|
|
private:
|
|
/// Allocator for the built Matcher / Builders
|
|
BlockAllocator<Matcher> matcher_allocator_;
|
|
|
|
/// Commonly used Matcher / Builders
|
|
struct {
|
|
VoidBuilder void_;
|
|
BoolBuilder bool_;
|
|
F32Builder f32;
|
|
I32Builder i32;
|
|
IU32Matcher iu32;
|
|
FIU32Matcher fiu32;
|
|
ScalarMatcher scalar;
|
|
U32Builder u32;
|
|
OpenTypeBuilder T{OpenType::T};
|
|
} matchers_;
|
|
|
|
// TODO(bclayton): Sort by type, or array these by IntrinsicType
|
|
std::vector<Overload> overloads_;
|
|
|
|
/// @returns a Matcher / Builder that matches a pointer with the given element
|
|
/// type
|
|
Builder* ptr(Builder* element_builder) {
|
|
return matcher_allocator_.Create<PtrBuilder>(element_builder);
|
|
}
|
|
|
|
/// @returns a Matcher / Builder that matches a vector of size OpenNumber::N
|
|
/// with the given element type
|
|
Builder* vecN(Builder* element_builder) {
|
|
return matcher_allocator_.Create<OpenSizeVecBuilder>(OpenNumber::N,
|
|
element_builder);
|
|
}
|
|
|
|
/// @returns a Matcher / Builder that matches a vector of the given size and
|
|
/// element type
|
|
Builder* vec(uint32_t size, Builder* element_builder) {
|
|
return matcher_allocator_.Create<VecBuilder>(size, element_builder);
|
|
}
|
|
|
|
/// @returns a Matcher / Builder that matches a runtime sized array with the
|
|
/// given element type
|
|
Builder* array(Builder* element_builder) {
|
|
return matcher_allocator_.Create<ArrayBuilder>(element_builder);
|
|
}
|
|
|
|
/// @returns a Matcher / Builder that matches a matrix with the given size and
|
|
/// element type
|
|
Builder* mat(OpenNumber columns, OpenNumber rows, Builder* element_builder) {
|
|
return matcher_allocator_.Create<OpenSizeMatBuilder>(columns, rows,
|
|
element_builder);
|
|
}
|
|
|
|
/// @returns a Matcher / Builder that matches a square matrix with the column
|
|
/// / row count of OpenNumber::N
|
|
template <typename T>
|
|
auto matNxN(T&& in) {
|
|
return mat(OpenNumber::N, OpenNumber::N, std::forward<T>(in));
|
|
}
|
|
|
|
/// @returns a Matcher / Builder that matches a sampled texture with the given
|
|
/// dimensions and type
|
|
Builder* sampled_texture(type::TextureDimension dimensions, Builder* type) {
|
|
return matcher_allocator_.Create<SampledTextureBuilder>(dimensions, type);
|
|
}
|
|
|
|
/// @returns a Matcher / Builder that matches a multisampled texture with the
|
|
/// given dimensions and type
|
|
Builder* multisampled_texture(type::TextureDimension dimensions,
|
|
Builder* type) {
|
|
return matcher_allocator_.Create<MultisampledTextureBuilder>(dimensions,
|
|
type);
|
|
}
|
|
|
|
/// @returns a Matcher / Builder that matches a depth texture with the
|
|
/// given dimensions
|
|
Builder* depth_texture(type::TextureDimension dimensions) {
|
|
return matcher_allocator_.Create<DepthTextureBuilder>(dimensions);
|
|
}
|
|
|
|
/// @returns a Matcher / Builder that matches a storage texture of the given
|
|
/// format with the given dimensions
|
|
Builder* storage_texture(type::TextureDimension dimensions,
|
|
OpenNumber texel_format,
|
|
OpenType channel_format) {
|
|
return matcher_allocator_.Create<StorageTextureBuilder>(
|
|
dimensions, texel_format, channel_format);
|
|
}
|
|
|
|
/// @returns a Matcher / Builder that matches a sampler type
|
|
Builder* sampler(type::SamplerKind kind) {
|
|
return matcher_allocator_.Create<SamplerBuilder>(kind);
|
|
}
|
|
|
|
/// @returns a Matcher / Builder that matches an access control type
|
|
Builder* access_control(ast::AccessControl access_control, Builder* type) {
|
|
return matcher_allocator_.Create<AccessControlBuilder>(access_control,
|
|
type);
|
|
}
|
|
|
|
/// Registers an overload with the given intrinsic type, return type Matcher /
|
|
/// Builder, and parameter Matcher / Builders.
|
|
/// This overload of Register does not constrain any OpenTypes.
|
|
void Register(semantic::IntrinsicType type,
|
|
Builder* return_type,
|
|
std::vector<Parameter> parameters) {
|
|
Overload overload{type, return_type, std::move(parameters), {}};
|
|
overloads_.emplace_back(overload);
|
|
}
|
|
|
|
/// Registers an overload with the given intrinsic type, return type Matcher /
|
|
/// Builder, and parameter Matcher / Builders.
|
|
/// A single OpenType is contained with the given Matcher in
|
|
/// open_type_matcher.
|
|
void Register(semantic::IntrinsicType type,
|
|
Builder* return_type,
|
|
std::vector<Parameter> parameters,
|
|
std::pair<OpenType, Matcher*> open_type_matcher) {
|
|
Overload overload{
|
|
type, return_type, std::move(parameters), {open_type_matcher}};
|
|
overloads_.emplace_back(overload);
|
|
}
|
|
};
|
|
|
|
Impl::Impl() {
|
|
using I = semantic::IntrinsicType;
|
|
using Dim = type::TextureDimension;
|
|
|
|
auto* void_ = &matchers_.void_; // void
|
|
auto* bool_ = &matchers_.bool_; // bool
|
|
auto* f32 = &matchers_.f32; // f32
|
|
auto* i32 = &matchers_.i32; // i32
|
|
auto* u32 = &matchers_.u32; // u32
|
|
auto* iu32 = &matchers_.iu32; // i32 or u32
|
|
auto* fiu32 = &matchers_.fiu32; // f32, i32 or u32
|
|
auto* scalar = &matchers_.scalar; // f32, i32, u32 or bool
|
|
auto* T = &matchers_.T; // Any T type
|
|
auto* array_T = array(T); // array<T>
|
|
auto* vec2_f32 = vec(2, f32); // vec2<f32>
|
|
auto* vec3_f32 = vec(3, f32); // vec3<f32>
|
|
auto* vec4_f32 = vec(4, f32); // vec4<f32>
|
|
auto* vec4_T = vec(4, T); // vec4<T>
|
|
auto* vec2_i32 = vec(2, i32); // vec2<i32>
|
|
auto* vec3_i32 = vec(3, i32); // vec3<i32>
|
|
auto* vecN_f32 = vecN(f32); // vecN<f32>
|
|
auto* vecN_T = vecN(T); // vecN<T>
|
|
auto* vecN_bool = vecN(bool_); // vecN<bool>
|
|
auto* matNxN_f32 = matNxN(f32); // matNxN<f32>
|
|
auto* ptr_T = ptr(T); // ptr<T>
|
|
auto* ptr_f32 = ptr(f32); // ptr<f32>
|
|
auto* ptr_vecN_T = ptr(vecN_T); // ptr<vecN<T>>
|
|
auto* ptr_vecN_f32 = ptr(vecN_f32); // ptr<vecN<f32>>
|
|
|
|
// Intrinsic overloads are registered with a call to the Register().
|
|
//
|
|
// The best way to explain Register() and the lookup process is by example.
|
|
//
|
|
// Let's begin with a simple overload declaration:
|
|
//
|
|
// Register(I::kIsInf, bool_, {f32});
|
|
//
|
|
// I - is an alias to semantic::IntrinsicType.
|
|
// I::kIsInf is shorthand for semantic::IntrinsicType::kIsInf.
|
|
// bool_ - is a pointer to a pre-constructed BoolBuilder which matches and
|
|
// builds type::Bool types.
|
|
// {f32} - is the list of parameter Builders for the overload.
|
|
// Builders are a type of Matcher that can also build the the type.
|
|
// All Builders are Matchers, not all Matchers are Builders.
|
|
// f32 is a pointer to a pre-constructed F32Builder which matches and
|
|
// builds type::F32 types.
|
|
//
|
|
// This call registers the overload for the `isInf(f32) -> bool` intrinsic.
|
|
//
|
|
// Let's now see the process of Overload::Match() when passed a single f32
|
|
// argument:
|
|
//
|
|
// (1) Overload::Match() begins by attempting to match the argument types
|
|
// from left to right.
|
|
// F32Builder::Match() is called with the type::F32 argument type.
|
|
// F32Builder (only) matches the type::F32 type, so F32Builder::Match()
|
|
// returns true.
|
|
// (2) All the parameters have had their Matcher::Match() methods return
|
|
// true, there are no open-types (more about these later), so the
|
|
// overload has matched.
|
|
// (3) The semantic::Intrinsic now needs to be built, so we begin by
|
|
// building the overload's parameter types (these may not exactly match
|
|
// the argument types). Build() is called for each parameter Builder,
|
|
// returning the parameter type.
|
|
// (4) Finally, Builder::Build() is called for the return_type, and the
|
|
// semantic::Intrinsic is constructed and returned.
|
|
// Job done.
|
|
//
|
|
// Overload resolution also supports basic pattern matching through the use of
|
|
// open-types and open-numbers.
|
|
//
|
|
// OpenTypeBuilder is a Matcher that matches a single open-type.
|
|
//
|
|
// An 'open-type' can be thought as a template type that is determined by the
|
|
// arguments to the intrinsic.
|
|
//
|
|
// At the beginning of Overload::Match(), all open-types are undefined.
|
|
// Open-types are closed (pinned to a fixed type) on the first attempt to
|
|
// match against that open-type (e.g. via OpenTypeBuilder::Match()).
|
|
// Once open-types are closed, they remain that type, and
|
|
// OpenTypeBuilder::Match() will only ever return true if the queried type
|
|
// matches the closed type.
|
|
//
|
|
// To better understand, let's consider the following hypothetical overload
|
|
// declaration:
|
|
//
|
|
// Register(I::kFoo, T, {T, T}, {OpenType::T, scalar});
|
|
//
|
|
// T - is the matcher for the open-type OpenType::T.
|
|
// scalar - is a pointer to a pre-constructed ScalarMatcher
|
|
// which matches scalar types (f32, i32, u32, bool).
|
|
// {OpenType::T, scalar} - is a constraint on the open-type OpenType::T that
|
|
// it needs to resolve to a scalar.
|
|
//
|
|
// This call to Register() declares the foo intrinsic which accepts the
|
|
// identical scalar type for both arguments, and returns that scalar type.
|
|
//
|
|
// The process for resolving this overload is as follows:
|
|
//
|
|
// (1) Overload::Match() begins by attempting to match the argument types
|
|
// from left to right.
|
|
// OpenTypeBuilder::Match() is called for the first parameter, being
|
|
// passed the type of the first argument.
|
|
// The OpenType::T has not been closed yet, so the OpenType::T is closed
|
|
// as the type of the first argument.
|
|
// There's no verification that the T type is a scalar at this stage.
|
|
// (2) OpenTypeBuilder::Match() is called again for the second parameter
|
|
// with the type of the second argument.
|
|
// As the OpenType::T is now closed, the argument type is compared
|
|
// against the value of the closed-type of OpenType::T.
|
|
// OpenTypeBuilder::Match() returns true if these type match, otherwise
|
|
// false and the overload match fails.
|
|
// (3) If all the parameters have had their Matcher::Match() methods return
|
|
// true, then the open-type constraints need to be checked next.
|
|
// The Matcher::Match() is called for each closed type. If any return
|
|
// false then the overload match fails.
|
|
// (4) Overload::Match() now needs to build and return the output
|
|
// semantic::Intrinsic holding the matched overload signature.
|
|
// (5) The parameter types are built by calling OpenTypeBuilder::Build().
|
|
// This simply returns the closed type.
|
|
// (6) OpenTypeBuilder::Build() is called again for the return_type, and the
|
|
// semantic::Intrinsic is constructed and returned.
|
|
// Job done.
|
|
//
|
|
// Open-numbers are very similar to open-types, except they match against
|
|
// integers instead of types. The rules for open-numbers are almost identical
|
|
// to open-types, except open-numbers do not support constraints.
|
|
//
|
|
// vecN(f32) is an example of a Matcher that uses open-numbers.
|
|
// vecN() constructs a OpenSizeVecBuilder that will match a vector of size
|
|
// OpenNumber::N and of element type f32. As vecN() always uses the
|
|
// OpenNumber::N, using vecN() multiple times in the same overload signature
|
|
// will ensure that the vector size is identical for all vector types.
|
|
//
|
|
// Some Matcher implementations accept other Matchers for matching sub-types.
|
|
// Consider:
|
|
//
|
|
// Register(I::kClamp, vecN(T), {vecN(T), vecN(T), vecN(T)},
|
|
// {OpenType::T, fiu32});
|
|
//
|
|
// vecN(T) is a OpenSizeVecBuilder that matches a vector of size OpenNumber::N
|
|
// and of element type OpenType::T, where T must be either a f32, i32, or u32.
|
|
|
|
// clang-format off
|
|
|
|
// name return type parameter types open type constraints // NOLINT
|
|
Register(I::kAbs, T, {T}, {OpenType::T, fiu32} ); // NOLINT
|
|
Register(I::kAbs, vecN_T, {vecN_T}, {OpenType::T, fiu32} ); // NOLINT
|
|
Register(I::kAcos, f32, {f32} ); // NOLINT
|
|
Register(I::kAcos, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kAll, bool_, {vecN_bool} ); // NOLINT
|
|
Register(I::kAny, bool_, {vecN_bool} ); // NOLINT
|
|
Register(I::kArrayLength, u32, {array_T} ); // NOLINT
|
|
Register(I::kAsin, f32, {f32} ); // NOLINT
|
|
Register(I::kAsin, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kAtan, f32, {f32} ); // NOLINT
|
|
Register(I::kAtan, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kAtan2, f32, {f32, f32} ); // NOLINT
|
|
Register(I::kAtan2, vecN_f32, {vecN_f32, vecN_f32} ); // NOLINT
|
|
Register(I::kCeil, f32, {f32} ); // NOLINT
|
|
Register(I::kCeil, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kClamp, T, {T, T, T}, {OpenType::T, fiu32} ); // NOLINT
|
|
Register(I::kClamp, vecN_T, {vecN_T, vecN_T, vecN_T}, {OpenType::T, fiu32} ); // NOLINT
|
|
Register(I::kCos, f32, {f32} ); // NOLINT
|
|
Register(I::kCos, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kCosh, f32, {f32} ); // NOLINT
|
|
Register(I::kCosh, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kCountOneBits, T, {T}, {OpenType::T, iu32} ); // NOLINT
|
|
Register(I::kCountOneBits, vecN_T, {vecN_T}, {OpenType::T, iu32} ); // NOLINT
|
|
Register(I::kCross, vec3_f32, {vec3_f32, vec3_f32} ); // NOLINT
|
|
Register(I::kDeterminant, f32, {matNxN_f32} ); // NOLINT
|
|
Register(I::kDistance, f32, {f32, f32} ); // NOLINT
|
|
Register(I::kDistance, f32, {vecN_f32, vecN_f32} ); // NOLINT
|
|
Register(I::kDot, f32, {vecN_f32, vecN_f32} ); // NOLINT
|
|
Register(I::kDpdx, f32, {f32} ); // NOLINT
|
|
Register(I::kDpdx, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kDpdxCoarse, f32, {f32} ); // NOLINT
|
|
Register(I::kDpdxCoarse, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kDpdxFine, f32, {f32} ); // NOLINT
|
|
Register(I::kDpdxFine, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kDpdy, f32, {f32} ); // NOLINT
|
|
Register(I::kDpdy, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kDpdyCoarse, f32, {f32} ); // NOLINT
|
|
Register(I::kDpdyCoarse, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kDpdyFine, f32, {f32} ); // NOLINT
|
|
Register(I::kDpdyFine, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kExp, f32, {f32} ); // NOLINT
|
|
Register(I::kExp, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kExp2, f32, {f32} ); // NOLINT
|
|
Register(I::kExp2, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kFaceForward, f32, {f32, f32, f32} ); // NOLINT
|
|
Register(I::kFaceForward, vecN_f32, {vecN_f32, vecN_f32, vecN_f32} ); // NOLINT
|
|
Register(I::kFloor, f32, {f32} ); // NOLINT
|
|
Register(I::kFloor, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kFma, f32, {f32, f32, f32} ); // NOLINT
|
|
Register(I::kFma, vecN_f32, {vecN_f32, vecN_f32, vecN_f32} ); // NOLINT
|
|
Register(I::kFract, f32, {f32} ); // NOLINT
|
|
Register(I::kFract, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kFrexp, f32, {f32, ptr_T}, {OpenType::T, iu32} ); // NOLINT
|
|
Register(I::kFrexp, vecN_f32, {vecN_f32, ptr_vecN_T}, {OpenType::T, iu32} ); // NOLINT
|
|
Register(I::kFwidth, f32, {f32} ); // NOLINT
|
|
Register(I::kFwidth, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kFwidthCoarse, f32, {f32} ); // NOLINT
|
|
Register(I::kFwidthCoarse, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kFwidthFine, f32, {f32} ); // NOLINT
|
|
Register(I::kFwidthFine, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kInverseSqrt, f32, {f32} ); // NOLINT
|
|
Register(I::kInverseSqrt, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kIsFinite, bool_, {f32} ); // NOLINT
|
|
Register(I::kIsFinite, vecN_bool, {vecN_f32} ); // NOLINT
|
|
Register(I::kIsInf, bool_, {f32} ); // NOLINT
|
|
Register(I::kIsInf, vecN_bool, {vecN_f32} ); // NOLINT
|
|
Register(I::kIsNan, bool_, {f32} ); // NOLINT
|
|
Register(I::kIsNan, vecN_bool, {vecN_f32} ); // NOLINT
|
|
Register(I::kIsNormal, bool_, {f32} ); // NOLINT
|
|
Register(I::kIsNormal, vecN_bool, {vecN_f32} ); // NOLINT
|
|
Register(I::kLdexp, f32, {f32, T}, {OpenType::T, iu32} ); // NOLINT
|
|
Register(I::kLdexp, vecN_f32, {vecN_f32, vecN_T}, {OpenType::T, iu32} ); // NOLINT
|
|
Register(I::kLength, f32, {f32} ); // NOLINT
|
|
Register(I::kLength, f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kLog, f32, {f32} ); // NOLINT
|
|
Register(I::kLog, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kLog2, f32, {f32} ); // NOLINT
|
|
Register(I::kLog2, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kMax, T, {T, T}, {OpenType::T, fiu32} ); // NOLINT
|
|
Register(I::kMax, vecN_T, {vecN_T, vecN_T}, {OpenType::T, fiu32} ); // NOLINT
|
|
Register(I::kMin, T, {T, T}, {OpenType::T, fiu32} ); // NOLINT
|
|
Register(I::kMin, vecN_T, {vecN_T, vecN_T}, {OpenType::T, fiu32} ); // NOLINT
|
|
Register(I::kMix, f32, {f32, f32, f32} ); // NOLINT
|
|
Register(I::kMix, vecN_f32, {vecN_f32, vecN_f32, vecN_f32} ); // NOLINT
|
|
Register(I::kModf, f32, {f32, ptr_f32} ); // NOLINT
|
|
Register(I::kModf, vecN_f32, {vecN_f32, ptr_vecN_f32} ); // NOLINT
|
|
Register(I::kNormalize, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kPack2x16Float, u32, {vec2_f32} ); // NOLINT
|
|
Register(I::kPack2x16Snorm, u32, {vec2_f32} ); // NOLINT
|
|
Register(I::kPack2x16Unorm, u32, {vec2_f32} ); // NOLINT
|
|
Register(I::kPack4x8Snorm, u32, {vec4_f32} ); // NOLINT
|
|
Register(I::kPack4x8Unorm, u32, {vec4_f32} ); // NOLINT
|
|
Register(I::kPow, f32, {f32, f32} ); // NOLINT
|
|
Register(I::kPow, vecN_f32, {vecN_f32, vecN_f32} ); // NOLINT
|
|
Register(I::kReflect, f32, {f32, f32} ); // NOLINT
|
|
Register(I::kReflect, vecN_f32, {vecN_f32, vecN_f32} ); // NOLINT
|
|
Register(I::kReverseBits, T, {T}, {OpenType::T, iu32} ); // NOLINT
|
|
Register(I::kReverseBits, vecN_T, {vecN_T}, {OpenType::T, iu32} ); // NOLINT
|
|
Register(I::kRound, f32, {f32} ); // NOLINT
|
|
Register(I::kRound, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kSelect, T, {T, T, bool_}, {OpenType::T, scalar} ); // NOLINT
|
|
Register(I::kSelect, vecN_T, {vecN_T, vecN_T, vecN_bool}, {OpenType::T, scalar} ); // NOLINT
|
|
Register(I::kSign, f32, {f32} ); // NOLINT
|
|
Register(I::kSign, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kSin, f32, {f32} ); // NOLINT
|
|
Register(I::kSin, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kSinh, f32, {f32} ); // NOLINT
|
|
Register(I::kSinh, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kSmoothStep, f32, {f32, f32, f32} ); // NOLINT
|
|
Register(I::kSmoothStep, vecN_f32, {vecN_f32, vecN_f32, vecN_f32} ); // NOLINT
|
|
Register(I::kSqrt, f32, {f32} ); // NOLINT
|
|
Register(I::kSqrt, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kStep, f32, {f32, f32} ); // NOLINT
|
|
Register(I::kStep, vecN_f32, {vecN_f32, vecN_f32} ); // NOLINT
|
|
Register(I::kTan, f32, {f32} ); // NOLINT
|
|
Register(I::kTan, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kTanh, f32, {f32} ); // NOLINT
|
|
Register(I::kTanh, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kTrunc, f32, {f32} ); // NOLINT
|
|
Register(I::kTrunc, vecN_f32, {vecN_f32} ); // NOLINT
|
|
Register(I::kUnpack2x16Float, vec2_f32, {u32} ); // NOLINT
|
|
Register(I::kUnpack2x16Snorm, vec2_f32, {u32} ); // NOLINT
|
|
Register(I::kUnpack2x16Unorm, vec2_f32, {u32} ); // NOLINT
|
|
Register(I::kUnpack4x8Snorm, vec4_f32, {u32} ); // NOLINT
|
|
Register(I::kUnpack4x8Unorm, vec4_f32, {u32} ); // NOLINT
|
|
// clang-format on
|
|
|
|
auto* tex_1d_f32 = sampled_texture(Dim::k1d, f32);
|
|
auto* tex_1d_T = sampled_texture(Dim::k1d, T);
|
|
auto* tex_2d_f32 = sampled_texture(Dim::k2d, f32);
|
|
auto* tex_2d_T = sampled_texture(Dim::k2d, T);
|
|
auto* tex_2d_array_f32 = sampled_texture(Dim::k2dArray, f32);
|
|
auto* tex_2d_array_T = sampled_texture(Dim::k2dArray, T);
|
|
auto* tex_3d_f32 = sampled_texture(Dim::k3d, f32);
|
|
auto* tex_3d_T = sampled_texture(Dim::k3d, T);
|
|
auto* tex_cube_f32 = sampled_texture(Dim::kCube, f32);
|
|
auto* tex_cube_T = sampled_texture(Dim::kCube, T);
|
|
auto* tex_cube_array_f32 = sampled_texture(Dim::kCubeArray, f32);
|
|
auto* tex_cube_array_T = sampled_texture(Dim::kCubeArray, T);
|
|
auto* tex_ms_2d_T = multisampled_texture(Dim::k2d, T);
|
|
auto* tex_ms_2d_array_T = multisampled_texture(Dim::k2dArray, T);
|
|
auto* tex_depth_2d = depth_texture(Dim::k2d);
|
|
auto* tex_depth_2d_array = depth_texture(Dim::k2dArray);
|
|
auto* tex_depth_cube = depth_texture(Dim::kCube);
|
|
auto* tex_depth_cube_array = depth_texture(Dim::kCubeArray);
|
|
auto* tex_storage_1d_FT =
|
|
storage_texture(Dim::k1d, OpenNumber::F, OpenType::T);
|
|
auto* tex_storage_2d_FT =
|
|
storage_texture(Dim::k2d, OpenNumber::F, OpenType::T);
|
|
auto* tex_storage_2d_array_FT =
|
|
storage_texture(Dim::k2dArray, OpenNumber::F, OpenType::T);
|
|
auto* tex_storage_3d_FT =
|
|
storage_texture(Dim::k3d, OpenNumber::F, OpenType::T);
|
|
auto* tex_storage_ro_1d_FT =
|
|
access_control(ast::AccessControl::kReadOnly, tex_storage_1d_FT);
|
|
auto* tex_storage_ro_2d_FT =
|
|
access_control(ast::AccessControl::kReadOnly, tex_storage_2d_FT);
|
|
auto* tex_storage_ro_2d_array_FT =
|
|
access_control(ast::AccessControl::kReadOnly, tex_storage_2d_array_FT);
|
|
auto* tex_storage_ro_3d_FT =
|
|
access_control(ast::AccessControl::kReadOnly, tex_storage_3d_FT);
|
|
auto* tex_storage_wo_1d_FT =
|
|
access_control(ast::AccessControl::kWriteOnly, tex_storage_1d_FT);
|
|
auto* tex_storage_wo_2d_FT =
|
|
access_control(ast::AccessControl::kWriteOnly, tex_storage_2d_FT);
|
|
auto* tex_storage_wo_2d_array_FT =
|
|
access_control(ast::AccessControl::kWriteOnly, tex_storage_2d_array_FT);
|
|
auto* tex_storage_wo_3d_FT =
|
|
access_control(ast::AccessControl::kWriteOnly, tex_storage_3d_FT);
|
|
auto* sampler = this->sampler(type::SamplerKind::kSampler);
|
|
auto* sampler_comparison =
|
|
this->sampler(type::SamplerKind::kComparisonSampler);
|
|
auto t = semantic::Parameter::Usage::kTexture;
|
|
auto s = semantic::Parameter::Usage::kSampler;
|
|
auto coords = semantic::Parameter::Usage::kCoords;
|
|
auto array_index = semantic::Parameter::Usage::kArrayIndex;
|
|
auto ddx = semantic::Parameter::Usage::kDdx;
|
|
auto ddy = semantic::Parameter::Usage::kDdy;
|
|
auto depth_ref = semantic::Parameter::Usage::kDepthRef;
|
|
auto bias = semantic::Parameter::Usage::kBias;
|
|
auto level = semantic::Parameter::Usage::kLevel;
|
|
auto offset = semantic::Parameter::Usage::kOffset;
|
|
auto value = semantic::Parameter::Usage::kValue;
|
|
auto sample_index = semantic::Parameter::Usage::kSampleIndex;
|
|
|
|
// clang-format off
|
|
|
|
// name return type parameter types
|
|
Register(I::kTextureDimensions, i32, {{t, tex_1d_T}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec2_i32, {{t, tex_2d_T}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec2_i32, {{t, tex_2d_T}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec2_i32, {{t, tex_2d_array_T}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec2_i32, {{t, tex_2d_array_T}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec3_i32, {{t, tex_3d_T}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec3_i32, {{t, tex_3d_T}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec3_i32, {{t, tex_cube_T}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec3_i32, {{t, tex_cube_T}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec3_i32, {{t, tex_cube_array_T}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec3_i32, {{t, tex_cube_array_T}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec2_i32, {{t, tex_ms_2d_T}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec2_i32, {{t, tex_ms_2d_array_T}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec2_i32, {{t, tex_depth_2d}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec2_i32, {{t, tex_depth_2d}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec2_i32, {{t, tex_depth_2d_array}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec2_i32, {{t, tex_depth_2d_array}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec3_i32, {{t, tex_depth_cube}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec3_i32, {{t, tex_depth_cube}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec3_i32, {{t, tex_depth_cube_array}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec3_i32, {{t, tex_depth_cube_array}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureDimensions, i32, {{t, tex_storage_1d_FT}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec2_i32, {{t, tex_storage_2d_FT}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec2_i32, {{t, tex_storage_2d_array_FT}, }); // NOLINT
|
|
Register(I::kTextureDimensions, vec3_i32, {{t, tex_storage_3d_FT}, }); // NOLINT
|
|
|
|
Register(I::kTextureNumLayers, i32, {{t, tex_2d_array_T}, });
|
|
Register(I::kTextureNumLayers, i32, {{t, tex_cube_array_T}, });
|
|
Register(I::kTextureNumLayers, i32, {{t, tex_ms_2d_array_T}, });
|
|
Register(I::kTextureNumLayers, i32, {{t, tex_depth_2d_array}, });
|
|
Register(I::kTextureNumLayers, i32, {{t, tex_depth_cube_array}, });
|
|
Register(I::kTextureNumLayers, i32, {{t, tex_storage_2d_array_FT}, });
|
|
|
|
Register(I::kTextureNumLevels, i32, {{t, tex_2d_T}, });
|
|
Register(I::kTextureNumLevels, i32, {{t, tex_2d_array_T}, });
|
|
Register(I::kTextureNumLevels, i32, {{t, tex_3d_T}, });
|
|
Register(I::kTextureNumLevels, i32, {{t, tex_cube_T}, });
|
|
Register(I::kTextureNumLevels, i32, {{t, tex_cube_array_T}, });
|
|
Register(I::kTextureNumLevels, i32, {{t, tex_depth_2d}, });
|
|
Register(I::kTextureNumLevels, i32, {{t, tex_depth_2d_array}, });
|
|
Register(I::kTextureNumLevels, i32, {{t, tex_depth_cube}, });
|
|
Register(I::kTextureNumLevels, i32, {{t, tex_depth_cube_array}, });
|
|
|
|
Register(I::kTextureNumSamples, i32, {{t, tex_ms_2d_T}, });
|
|
Register(I::kTextureNumSamples, i32, {{t, tex_ms_2d_array_T}, });
|
|
|
|
Register(I::kTextureSample, vec4_f32, {{t, tex_1d_f32}, {s, sampler}, {coords, f32}, }); // NOLINT
|
|
Register(I::kTextureSample, vec4_f32, {{t, tex_2d_f32}, {s, sampler}, {coords, vec2_f32}, }); // NOLINT
|
|
Register(I::kTextureSample, vec4_f32, {{t, tex_2d_f32}, {s, sampler}, {coords, vec2_f32}, {offset, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureSample, vec4_f32, {{t, tex_2d_array_f32}, {s, sampler}, {coords, vec2_f32}, {array_index, i32}, }); // NOLINT
|
|
Register(I::kTextureSample, vec4_f32, {{t, tex_2d_array_f32}, {s, sampler}, {coords, vec2_f32}, {array_index, i32}, {offset, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureSample, vec4_f32, {{t, tex_3d_f32}, {s, sampler}, {coords, vec3_f32}, }); // NOLINT
|
|
Register(I::kTextureSample, vec4_f32, {{t, tex_3d_f32}, {s, sampler}, {coords, vec3_f32}, {offset, vec3_i32}, }); // NOLINT
|
|
Register(I::kTextureSample, vec4_f32, {{t, tex_cube_f32}, {s, sampler}, {coords, vec3_f32}, }); // NOLINT
|
|
Register(I::kTextureSample, vec4_f32, {{t, tex_cube_array_f32}, {s, sampler}, {coords, vec3_f32}, {array_index, i32}, }); // NOLINT
|
|
Register(I::kTextureSample, f32, {{t, tex_depth_2d}, {s, sampler}, {coords, vec2_f32}, }); // NOLINT
|
|
Register(I::kTextureSample, f32, {{t, tex_depth_2d}, {s, sampler}, {coords, vec2_f32}, {offset, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureSample, f32, {{t, tex_depth_2d_array}, {s, sampler}, {coords, vec2_f32}, {array_index, i32}, }); // NOLINT
|
|
Register(I::kTextureSample, f32, {{t, tex_depth_2d_array}, {s, sampler}, {coords, vec2_f32}, {array_index, i32}, {offset, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureSample, f32, {{t, tex_depth_cube}, {s, sampler}, {coords, vec3_f32}, }); // NOLINT
|
|
Register(I::kTextureSample, f32, {{t, tex_depth_cube_array}, {s, sampler}, {coords, vec3_f32}, {array_index, i32}, }); // NOLINT
|
|
|
|
Register(I::kTextureSampleBias, vec4_f32, {{t, tex_2d_f32}, {s, sampler}, {coords, vec2_f32}, {bias, f32}, }); // NOLINT
|
|
Register(I::kTextureSampleBias, vec4_f32, {{t, tex_2d_f32}, {s, sampler}, {coords, vec2_f32}, {bias, f32}, {offset, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureSampleBias, vec4_f32, {{t, tex_2d_array_f32}, {s, sampler}, {coords, vec2_f32}, {array_index, i32}, {bias, f32}, }); // NOLINT
|
|
Register(I::kTextureSampleBias, vec4_f32, {{t, tex_2d_array_f32}, {s, sampler}, {coords, vec2_f32}, {array_index, i32}, {bias, f32}, {offset, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureSampleBias, vec4_f32, {{t, tex_3d_f32}, {s, sampler}, {coords, vec3_f32}, {bias, f32}, }); // NOLINT
|
|
Register(I::kTextureSampleBias, vec4_f32, {{t, tex_3d_f32}, {s, sampler}, {coords, vec3_f32}, {bias, f32}, {offset, vec3_i32}, }); // NOLINT
|
|
Register(I::kTextureSampleBias, vec4_f32, {{t, tex_cube_f32}, {s, sampler}, {coords, vec3_f32}, {bias, f32}, }); // NOLINT
|
|
Register(I::kTextureSampleBias, vec4_f32, {{t, tex_cube_array_f32}, {s, sampler}, {coords, vec3_f32}, {array_index, i32}, {bias, f32}, }); // NOLINT
|
|
|
|
Register(I::kTextureSampleCompare, f32, {{t, tex_depth_2d}, {s, sampler_comparison}, {coords, vec2_f32}, {depth_ref, f32}, }); // NOLINT
|
|
Register(I::kTextureSampleCompare, f32, {{t, tex_depth_2d}, {s, sampler_comparison}, {coords, vec2_f32}, {depth_ref, f32}, {offset, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureSampleCompare, f32, {{t, tex_depth_2d_array}, {s, sampler_comparison}, {coords, vec2_f32}, {array_index, i32}, {depth_ref, f32}, }); // NOLINT
|
|
Register(I::kTextureSampleCompare, f32, {{t, tex_depth_2d_array}, {s, sampler_comparison}, {coords, vec2_f32}, {array_index, i32}, {depth_ref, f32}, {offset, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureSampleCompare, f32, {{t, tex_depth_cube}, {s, sampler_comparison}, {coords, vec3_f32}, {depth_ref, f32}, }); // NOLINT
|
|
Register(I::kTextureSampleCompare, f32, {{t, tex_depth_cube_array}, {s, sampler_comparison}, {coords, vec3_f32}, {array_index, i32}, {depth_ref, f32}, }); // NOLINT
|
|
|
|
Register(I::kTextureSampleGrad, vec4_f32, {{t, tex_2d_f32}, {s, sampler}, {coords, vec2_f32}, {ddx, vec2_f32}, {ddy, vec2_f32}, }); // NOLINT
|
|
Register(I::kTextureSampleGrad, vec4_f32, {{t, tex_2d_f32}, {s, sampler}, {coords, vec2_f32}, {ddx, vec2_f32}, {ddy, vec2_f32}, {offset, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureSampleGrad, vec4_f32, {{t, tex_2d_array_f32}, {s, sampler}, {coords, vec2_f32}, {array_index, i32}, {ddx, vec2_f32}, {ddy, vec2_f32}, }); // NOLINT
|
|
Register(I::kTextureSampleGrad, vec4_f32, {{t, tex_2d_array_f32}, {s, sampler}, {coords, vec2_f32}, {array_index, i32}, {ddx, vec2_f32}, {ddy, vec2_f32}, {offset, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureSampleGrad, vec4_f32, {{t, tex_3d_f32}, {s, sampler}, {coords, vec3_f32}, {ddx, vec3_f32}, {ddy, vec3_f32}, }); // NOLINT
|
|
Register(I::kTextureSampleGrad, vec4_f32, {{t, tex_3d_f32}, {s, sampler}, {coords, vec3_f32}, {ddx, vec3_f32}, {ddy, vec3_f32}, {offset, vec3_i32}, }); // NOLINT
|
|
Register(I::kTextureSampleGrad, vec4_f32, {{t, tex_cube_f32}, {s, sampler}, {coords, vec3_f32}, {ddx, vec3_f32}, {ddy, vec3_f32}, }); // NOLINT
|
|
Register(I::kTextureSampleGrad, vec4_f32, {{t, tex_cube_array_f32}, {s, sampler}, {coords, vec3_f32}, {array_index, i32}, {ddx, vec3_f32}, {ddy, vec3_f32}, }); // NOLINT
|
|
|
|
Register(I::kTextureSampleLevel, vec4_f32, {{t, tex_2d_f32}, {s, sampler}, {coords, vec2_f32}, {level, f32}, }); // NOLINT
|
|
Register(I::kTextureSampleLevel, vec4_f32, {{t, tex_2d_f32}, {s, sampler}, {coords, vec2_f32}, {level, f32}, {offset, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureSampleLevel, vec4_f32, {{t, tex_2d_array_f32}, {s, sampler}, {coords, vec2_f32}, {array_index, i32}, {level, f32}, }); // NOLINT
|
|
Register(I::kTextureSampleLevel, vec4_f32, {{t, tex_2d_array_f32}, {s, sampler}, {coords, vec2_f32}, {array_index, i32}, {level, f32}, {offset, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureSampleLevel, vec4_f32, {{t, tex_3d_f32}, {s, sampler}, {coords, vec3_f32}, {level, f32}, }); // NOLINT
|
|
Register(I::kTextureSampleLevel, vec4_f32, {{t, tex_3d_f32}, {s, sampler}, {coords, vec3_f32}, {level, f32}, {offset, vec3_i32}, }); // NOLINT
|
|
Register(I::kTextureSampleLevel, vec4_f32, {{t, tex_cube_f32}, {s, sampler}, {coords, vec3_f32}, {level, f32}, }); // NOLINT
|
|
Register(I::kTextureSampleLevel, vec4_f32, {{t, tex_cube_array_f32}, {s, sampler}, {coords, vec3_f32}, {array_index, i32}, {level, f32}, }); // NOLINT
|
|
Register(I::kTextureSampleLevel, f32, {{t, tex_depth_2d}, {s, sampler}, {coords, vec2_f32}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureSampleLevel, f32, {{t, tex_depth_2d}, {s, sampler}, {coords, vec2_f32}, {level, i32}, {offset, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureSampleLevel, f32, {{t, tex_depth_2d_array}, {s, sampler}, {coords, vec2_f32}, {array_index, i32}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureSampleLevel, f32, {{t, tex_depth_2d_array}, {s, sampler}, {coords, vec2_f32}, {array_index, i32}, {level, i32}, {offset, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureSampleLevel, f32, {{t, tex_depth_cube}, {s, sampler}, {coords, vec3_f32}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureSampleLevel, f32, {{t, tex_depth_cube_array},{s, sampler}, {coords, vec3_f32}, {array_index, i32}, {level, i32}, }); // NOLINT
|
|
|
|
Register(I::kTextureStore, void_, {{t, tex_storage_wo_1d_FT}, {coords, i32}, {value, vec4_T}, }); // NOLINT
|
|
Register(I::kTextureStore, void_, {{t, tex_storage_wo_2d_FT}, {coords, vec2_i32}, {value, vec4_T}, }); // NOLINT
|
|
Register(I::kTextureStore, void_, {{t, tex_storage_wo_2d_array_FT},{coords, vec2_i32}, {array_index, i32}, {value, vec4_T}, }); // NOLINT
|
|
Register(I::kTextureStore, void_, {{t, tex_storage_wo_3d_FT}, {coords, vec3_i32}, {value, vec4_T}, }); // NOLINT
|
|
|
|
Register(I::kTextureLoad, vec4_T, {{t, tex_1d_T}, {coords, i32}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureLoad, vec4_T, {{t, tex_2d_T}, {coords, vec2_i32}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureLoad, vec4_T, {{t, tex_2d_array_T}, {coords, vec2_i32}, {array_index, i32}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureLoad, vec4_T, {{t, tex_3d_T}, {coords, vec3_i32}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureLoad, vec4_T, {{t, tex_ms_2d_T}, {coords, vec2_i32}, {sample_index, i32}, }); // NOLINT
|
|
Register(I::kTextureLoad, vec4_T, {{t, tex_ms_2d_array_T}, {coords, vec2_i32}, {array_index, i32}, {sample_index, i32}, }); // NOLINT
|
|
Register(I::kTextureLoad, f32, {{t, tex_depth_2d}, {coords, vec2_i32}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureLoad, f32, {{t, tex_depth_2d_array}, {coords, vec2_i32}, {array_index, i32}, {level, i32}, }); // NOLINT
|
|
Register(I::kTextureLoad, vec4_T, {{t, tex_storage_ro_1d_FT}, {coords, i32}, }); // NOLINT
|
|
Register(I::kTextureLoad, vec4_T, {{t, tex_storage_ro_2d_FT}, {coords, vec2_i32}, }); // NOLINT
|
|
Register(I::kTextureLoad, vec4_T, {{t, tex_storage_ro_2d_array_FT},{coords, vec2_i32}, {array_index, i32}, }); // NOLINT
|
|
Register(I::kTextureLoad, vec4_T, {{t, tex_storage_ro_3d_FT}, {coords, vec3_i32}, }); // NOLINT
|
|
|
|
// clang-format on
|
|
}
|
|
|
|
/// @returns a human readable string representation of the overload
|
|
std::string str(const Impl::Overload& overload) {
|
|
std::stringstream ss;
|
|
ss << overload.type << "(";
|
|
{
|
|
bool first = true;
|
|
for (auto param : overload.parameters) {
|
|
if (!first) {
|
|
ss << ", ";
|
|
}
|
|
first = false;
|
|
if (param.usage != semantic::Parameter::Usage::kNone) {
|
|
ss << semantic::str(param.usage) << " : ";
|
|
}
|
|
ss << param.matcher->str();
|
|
}
|
|
}
|
|
ss << ") -> ";
|
|
ss << overload.return_type->str();
|
|
|
|
if (!overload.open_type_matchers.empty()) {
|
|
ss << " where: ";
|
|
|
|
for (uint32_t i = 0; i < static_cast<uint32_t>(OpenType::Count); i++) {
|
|
auto open_type = static_cast<OpenType>(i);
|
|
auto it = overload.open_type_matchers.find(open_type);
|
|
if (it != overload.open_type_matchers.end()) {
|
|
if (i > 0) {
|
|
ss << ", ";
|
|
}
|
|
ss << tint::str(open_type) << " is " << it->second->str();
|
|
}
|
|
}
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
/// @return a string representing a call to an intrinsic with the given argument
|
|
/// types.
|
|
std::string CallSignature(ProgramBuilder& builder,
|
|
semantic::IntrinsicType type,
|
|
const std::vector<type::Type*>& args) {
|
|
std::stringstream ss;
|
|
ss << semantic::str(type) << "(";
|
|
{
|
|
bool first = true;
|
|
for (auto* arg : args) {
|
|
if (!first) {
|
|
ss << ", ";
|
|
}
|
|
first = false;
|
|
ss << arg->FriendlyName(builder.Symbols());
|
|
}
|
|
}
|
|
ss << ")";
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
IntrinsicTable::Result Impl::Lookup(ProgramBuilder& builder,
|
|
semantic::IntrinsicType type,
|
|
const std::vector<type::Type*>& args,
|
|
const Source& source) const {
|
|
diag::List diagnostics;
|
|
// Candidate holds information about a mismatched overload that could be what
|
|
// the user intended to call.
|
|
struct Candidate {
|
|
const Overload* overload;
|
|
int score;
|
|
};
|
|
|
|
// The list of failed matches that had promise.
|
|
std::vector<Candidate> candidates;
|
|
|
|
// TODO(bclayton) Sort overloads_, or place them into a map keyed by intrinsic
|
|
// type. This is horribly inefficient.
|
|
for (auto& overload : overloads_) {
|
|
int match_score = 0;
|
|
if (auto* intrinsic =
|
|
overload.Match(builder, type, args, diagnostics, match_score)) {
|
|
return Result{intrinsic, {}}; // Match found
|
|
}
|
|
if (match_score > 0) {
|
|
candidates.emplace_back(Candidate{&overload, match_score});
|
|
}
|
|
}
|
|
|
|
// Sort the candidates with the most promising first
|
|
std::stable_sort(
|
|
candidates.begin(), candidates.end(),
|
|
[](const Candidate& a, const Candidate& b) { return a.score > b.score; });
|
|
|
|
// Generate an error message
|
|
std::stringstream ss;
|
|
ss << "no matching call to " << CallSignature(builder, type, args)
|
|
<< std::endl;
|
|
if (!candidates.empty()) {
|
|
ss << std::endl;
|
|
ss << candidates.size() << " candidate function"
|
|
<< (candidates.size() > 1 ? "s:" : ":") << std::endl;
|
|
for (auto& candidate : candidates) {
|
|
ss << " " << str(*candidate.overload) << std::endl;
|
|
}
|
|
}
|
|
diagnostics.add_error(ss.str(), source);
|
|
|
|
return Result{nullptr, std::move(diagnostics)};
|
|
}
|
|
|
|
semantic::Intrinsic* Impl::Overload::Match(ProgramBuilder& builder,
|
|
semantic::IntrinsicType intrinsic,
|
|
const std::vector<type::Type*>& args,
|
|
diag::List& diagnostics,
|
|
int& match_score) const {
|
|
if (type != intrinsic) {
|
|
match_score = std::numeric_limits<int>::min();
|
|
return nullptr; // Incorrect function
|
|
}
|
|
|
|
// Penalize argument <-> parameter count mismatches
|
|
match_score = 1000;
|
|
match_score -= std::max(parameters.size(), args.size()) -
|
|
std::min(parameters.size(), args.size());
|
|
|
|
bool matched = parameters.size() == args.size();
|
|
|
|
Matcher::MatchState matcher_state;
|
|
|
|
// Check that each of the parameters match.
|
|
// This stage also populates the open_types and open_numbers.
|
|
auto count = std::min(parameters.size(), args.size());
|
|
for (size_t i = 0; i < count; i++) {
|
|
assert(args[i]);
|
|
auto* arg_ty = args[i];
|
|
if (auto* ptr = arg_ty->As<type::Pointer>()) {
|
|
if (!parameters[i].matcher->ExpectsPointer()) {
|
|
// Argument is a pointer, but the matcher isn't expecting one.
|
|
// Perform an implicit dereference.
|
|
arg_ty = ptr->type();
|
|
}
|
|
}
|
|
if (parameters[i].matcher->Match(matcher_state, arg_ty)) {
|
|
// A correct parameter match is scored higher than number of parameters to
|
|
// arguments.
|
|
match_score += 2;
|
|
} else {
|
|
matched = false;
|
|
}
|
|
}
|
|
if (!matched) {
|
|
return nullptr;
|
|
}
|
|
|
|
// If any of the open-types are constrained, check that they match.
|
|
for (auto matcher_it : open_type_matchers) {
|
|
OpenType open_type = matcher_it.first;
|
|
auto* matcher = matcher_it.second;
|
|
auto type_it = matcher_state.open_types.find(open_type);
|
|
if (type_it == matcher_state.open_types.end()) {
|
|
// We have an overload that claims to have matched, but didn't actually
|
|
// resolve the open type. This is a bug that needs fixing.
|
|
TINT_ICE(diagnostics)
|
|
<< "IntrinsicTable overload matched for "
|
|
<< CallSignature(builder, intrinsic, args)
|
|
<< ", but didn't resolve the open type " << str(open_type);
|
|
return nullptr;
|
|
}
|
|
auto* resolved_type = type_it->second;
|
|
if (resolved_type == nullptr) {
|
|
// We have an overload that claims to have matched, but has a nullptr
|
|
// resolved open type. This is a bug that needs fixing.
|
|
TINT_ICE(diagnostics)
|
|
<< "IntrinsicTable overload matched for "
|
|
<< CallSignature(builder, intrinsic, args) << ", but open type "
|
|
<< str(open_type) << " is nullptr";
|
|
return nullptr;
|
|
}
|
|
if (!matcher->Match(matcher_state, resolved_type)) {
|
|
matched = false;
|
|
continue;
|
|
}
|
|
match_score++;
|
|
}
|
|
if (!matched) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Overload matched!
|
|
|
|
// Build the return type
|
|
Builder::BuildState builder_state{builder.Types(), matcher_state.open_types,
|
|
matcher_state.open_numbers};
|
|
auto* ret = return_type->Build(builder_state);
|
|
assert(ret); // Build() must return a type
|
|
|
|
// Build the semantic parameters
|
|
semantic::ParameterList params;
|
|
params.reserve(parameters.size());
|
|
for (size_t i = 0; i < args.size(); i++) {
|
|
auto& parameter = parameters[i];
|
|
auto* ty = parameter.matcher->Build(builder_state);
|
|
params.emplace_back(semantic::Parameter{ty, parameter.usage});
|
|
}
|
|
|
|
return builder.create<semantic::Intrinsic>(intrinsic, ret, params);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::unique_ptr<IntrinsicTable> IntrinsicTable::Create() {
|
|
return std::make_unique<Impl>();
|
|
}
|
|
|
|
IntrinsicTable::~IntrinsicTable() = default;
|
|
|
|
} // namespace tint
|