summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/src/sksl/ir/SkSLType.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/skia/skia/src/sksl/ir/SkSLType.cpp')
-rw-r--r--gfx/skia/skia/src/sksl/ir/SkSLType.cpp1208
1 files changed, 1208 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/sksl/ir/SkSLType.cpp b/gfx/skia/skia/src/sksl/ir/SkSLType.cpp
new file mode 100644
index 0000000000..265d49dcc8
--- /dev/null
+++ b/gfx/skia/skia/src/sksl/ir/SkSLType.cpp
@@ -0,0 +1,1208 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/sksl/ir/SkSLType.h"
+
+#include "include/private/SkSLLayout.h"
+#include "include/private/SkSLString.h"
+#include "include/sksl/SkSLErrorReporter.h"
+#include "src/base/SkMathPriv.h"
+#include "src/base/SkSafeMath.h"
+#include "src/sksl/SkSLBuiltinTypes.h"
+#include "src/sksl/SkSLConstantFolder.h"
+#include "src/sksl/SkSLContext.h"
+#include "src/sksl/SkSLProgramSettings.h"
+#include "src/sksl/ir/SkSLConstructorArrayCast.h"
+#include "src/sksl/ir/SkSLConstructorCompoundCast.h"
+#include "src/sksl/ir/SkSLConstructorScalarCast.h"
+#include "src/sksl/ir/SkSLExpression.h"
+#include "src/sksl/ir/SkSLSymbolTable.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <limits>
+#include <optional>
+#include <string_view>
+#include <utility>
+
+namespace SkSL {
+
+static constexpr int kMaxStructDepth = 8;
+
+class AliasType final : public Type {
+public:
+ AliasType(std::string_view name, const Type& targetType)
+ : INHERITED(name, targetType.abbreviatedName(), targetType.typeKind())
+ , fTargetType(targetType) {}
+
+ const Type& resolve() const override {
+ return fTargetType;
+ }
+
+ const Type& componentType() const override {
+ return fTargetType.componentType();
+ }
+
+ NumberKind numberKind() const override {
+ return fTargetType.numberKind();
+ }
+
+ int priority() const override {
+ return fTargetType.priority();
+ }
+
+ int columns() const override {
+ return fTargetType.columns();
+ }
+
+ int rows() const override {
+ return fTargetType.rows();
+ }
+
+ int bitWidth() const override {
+ return fTargetType.bitWidth();
+ }
+
+ bool isAllowedInES2() const override {
+ return fTargetType.isAllowedInES2();
+ }
+
+ size_t slotCount() const override {
+ return fTargetType.slotCount();
+ }
+
+ SpvDim_ dimensions() const override {
+ return fTargetType.dimensions();
+ }
+
+ bool isDepth() const override {
+ return fTargetType.isDepth();
+ }
+
+ bool isArrayedTexture() const override {
+ return fTargetType.isArrayedTexture();
+ }
+
+ bool isScalar() const override {
+ return fTargetType.isScalar();
+ }
+
+ bool isLiteral() const override {
+ return fTargetType.isLiteral();
+ }
+
+ bool isVector() const override {
+ return fTargetType.isVector();
+ }
+
+ bool isMatrix() const override {
+ return fTargetType.isMatrix();
+ }
+
+ bool isArray() const override {
+ return fTargetType.isArray();
+ }
+
+ bool isUnsizedArray() const override {
+ return fTargetType.isUnsizedArray();
+ }
+
+ bool isStruct() const override {
+ return fTargetType.isStruct();
+ }
+
+ bool isInterfaceBlock() const override {
+ return fTargetType.isInterfaceBlock();
+ }
+
+ bool isMultisampled() const override {
+ return fTargetType.isMultisampled();
+ }
+
+ TextureAccess textureAccess() const override {
+ return fTargetType.textureAccess();
+ }
+
+ SkSpan<const Type* const> coercibleTypes() const override {
+ return fTargetType.coercibleTypes();
+ }
+
+private:
+ using INHERITED = Type;
+
+ const Type& fTargetType;
+};
+
+class ArrayType final : public Type {
+public:
+ inline static constexpr TypeKind kTypeKind = TypeKind::kArray;
+
+ ArrayType(std::string_view name, const char* abbrev, const Type& componentType, int count)
+ : INHERITED(name, abbrev, kTypeKind)
+ , fComponentType(componentType)
+ , fCount(count) {
+ SkASSERT(count > 0 || count == kUnsizedArray);
+ // Disallow multi-dimensional arrays.
+ SkASSERT(!componentType.is<ArrayType>());
+ }
+
+ bool isArray() const override {
+ return true;
+ }
+
+ bool isUnsizedArray() const override {
+ return fCount == kUnsizedArray;
+ }
+
+ const Type& componentType() const override {
+ return fComponentType;
+ }
+
+ int columns() const override {
+ return fCount;
+ }
+
+ int bitWidth() const override {
+ return this->componentType().bitWidth();
+ }
+
+ bool isAllowedInES2() const override {
+ return fComponentType.isAllowedInES2();
+ }
+
+ size_t slotCount() const override {
+ SkASSERT(fCount != kUnsizedArray);
+ SkASSERT(fCount > 0);
+ return fCount * fComponentType.slotCount();
+ }
+
+private:
+ using INHERITED = Type;
+
+ const Type& fComponentType;
+ int fCount;
+};
+
+class GenericType final : public Type {
+public:
+ inline static constexpr TypeKind kTypeKind = TypeKind::kGeneric;
+
+ GenericType(const char* name, SkSpan<const Type* const> coercibleTypes)
+ : INHERITED(name, "G", kTypeKind) {
+ fNumTypes = coercibleTypes.size();
+ SkASSERT(fNumTypes <= std::size(fCoercibleTypes));
+ std::copy(coercibleTypes.begin(), coercibleTypes.end(), fCoercibleTypes);
+ }
+
+ SkSpan<const Type* const> coercibleTypes() const override {
+ return SkSpan(fCoercibleTypes, fNumTypes);
+ }
+
+private:
+ using INHERITED = Type;
+
+ const Type* fCoercibleTypes[9];
+ size_t fNumTypes;
+};
+
+class LiteralType : public Type {
+public:
+ inline static constexpr TypeKind kTypeKind = TypeKind::kLiteral;
+
+ LiteralType(const char* name, const Type& scalarType, int8_t priority)
+ : INHERITED(name, "L", kTypeKind)
+ , fScalarType(scalarType)
+ , fPriority(priority) {}
+
+ const Type& scalarTypeForLiteral() const override {
+ return fScalarType;
+ }
+
+ int priority() const override {
+ return fPriority;
+ }
+
+ int columns() const override {
+ return 1;
+ }
+
+ int rows() const override {
+ return 1;
+ }
+
+ NumberKind numberKind() const override {
+ return fScalarType.numberKind();
+ }
+
+ int bitWidth() const override {
+ return fScalarType.bitWidth();
+ }
+
+ double minimumValue() const override {
+ return fScalarType.minimumValue();
+ }
+
+ double maximumValue() const override {
+ return fScalarType.maximumValue();
+ }
+
+ bool isScalar() const override {
+ return true;
+ }
+
+ bool isLiteral() const override {
+ return true;
+ }
+
+ size_t slotCount() const override {
+ return 1;
+ }
+
+private:
+ using INHERITED = Type;
+
+ const Type& fScalarType;
+ int8_t fPriority;
+};
+
+
+class ScalarType final : public Type {
+public:
+ inline static constexpr TypeKind kTypeKind = TypeKind::kScalar;
+
+ ScalarType(std::string_view name, const char* abbrev, NumberKind numberKind, int8_t priority,
+ int8_t bitWidth)
+ : INHERITED(name, abbrev, kTypeKind)
+ , fNumberKind(numberKind)
+ , fPriority(priority)
+ , fBitWidth(bitWidth) {}
+
+ NumberKind numberKind() const override {
+ return fNumberKind;
+ }
+
+ int priority() const override {
+ return fPriority;
+ }
+
+ int bitWidth() const override {
+ return fBitWidth;
+ }
+
+ int columns() const override {
+ return 1;
+ }
+
+ int rows() const override {
+ return 1;
+ }
+
+ bool isScalar() const override {
+ return true;
+ }
+
+ bool isAllowedInES2() const override {
+ return fNumberKind != NumberKind::kUnsigned;
+ }
+
+ size_t slotCount() const override {
+ return 1;
+ }
+
+ using int_limits = std::numeric_limits<int32_t>;
+ using short_limits = std::numeric_limits<int16_t>;
+ using uint_limits = std::numeric_limits<uint32_t>;
+ using ushort_limits = std::numeric_limits<uint16_t>;
+ using float_limits = std::numeric_limits<float>;
+
+ /** Returns the maximum value that can fit in the type. */
+ double minimumValue() const override {
+ switch (this->numberKind()) {
+ case NumberKind::kSigned:
+ return this->highPrecision() ? int_limits::lowest()
+ : short_limits::lowest();
+
+ case NumberKind::kUnsigned:
+ return 0;
+
+ case NumberKind::kFloat:
+ default:
+ return float_limits::lowest();
+ }
+ }
+
+ /** Returns the maximum value that can fit in the type. */
+ double maximumValue() const override {
+ switch (this->numberKind()) {
+ case NumberKind::kSigned:
+ return this->highPrecision() ? int_limits::max()
+ : short_limits::max();
+
+ case NumberKind::kUnsigned:
+ return this->highPrecision() ? uint_limits::max()
+ : ushort_limits::max();
+
+ case NumberKind::kFloat:
+ default:
+ return float_limits::max();
+ }
+ }
+
+private:
+ using INHERITED = Type;
+
+ NumberKind fNumberKind;
+ int8_t fPriority;
+ int8_t fBitWidth;
+};
+
+class AtomicType final : public Type {
+public:
+ inline static constexpr TypeKind kTypeKind = TypeKind::kAtomic;
+
+ AtomicType(std::string_view name, const char* abbrev) : INHERITED(name, abbrev, kTypeKind) {}
+
+ bool isAllowedInES2() const override { return false; }
+
+private:
+ using INHERITED = Type;
+};
+
+class MatrixType final : public Type {
+public:
+ inline static constexpr TypeKind kTypeKind = TypeKind::kMatrix;
+
+ MatrixType(std::string_view name, const char* abbrev, const Type& componentType,
+ int8_t columns, int8_t rows)
+ : INHERITED(name, abbrev, kTypeKind)
+ , fComponentType(componentType.as<ScalarType>())
+ , fColumns(columns)
+ , fRows(rows) {
+ SkASSERT(columns >= 2 && columns <= 4);
+ SkASSERT(rows >= 2 && rows <= 4);
+ }
+
+ const ScalarType& componentType() const override {
+ return fComponentType;
+ }
+
+ int columns() const override {
+ return fColumns;
+ }
+
+ int rows() const override {
+ return fRows;
+ }
+
+ int bitWidth() const override {
+ return this->componentType().bitWidth();
+ }
+
+ bool isMatrix() const override {
+ return true;
+ }
+
+ bool isAllowedInES2() const override {
+ return fColumns == fRows;
+ }
+
+ size_t slotCount() const override {
+ return fColumns * fRows;
+ }
+
+private:
+ using INHERITED = Type;
+
+ const ScalarType& fComponentType;
+ int8_t fColumns;
+ int8_t fRows;
+};
+
+class TextureType final : public Type {
+public:
+ inline static constexpr TypeKind kTypeKind = TypeKind::kTexture;
+
+ TextureType(const char* name, SpvDim_ dimensions, bool isDepth, bool isArrayed,
+ bool isMultisampled, TextureAccess textureAccess)
+ : INHERITED(name, "T", kTypeKind)
+ , fDimensions(dimensions)
+ , fIsDepth(isDepth)
+ , fIsArrayed(isArrayed)
+ , fIsMultisampled(isMultisampled)
+ , fTextureAccess(textureAccess) {}
+
+ SpvDim_ dimensions() const override {
+ return fDimensions;
+ }
+
+ bool isDepth() const override {
+ return fIsDepth;
+ }
+
+ bool isArrayedTexture() const override {
+ return fIsArrayed;
+ }
+
+ bool isMultisampled() const override {
+ return fIsMultisampled;
+ }
+
+ TextureAccess textureAccess() const override {
+ return fTextureAccess;
+ }
+
+private:
+ using INHERITED = Type;
+
+ SpvDim_ fDimensions;
+ bool fIsDepth;
+ bool fIsArrayed;
+ bool fIsMultisampled;
+ TextureAccess fTextureAccess;
+};
+
+class SamplerType final : public Type {
+public:
+ inline static constexpr TypeKind kTypeKind = TypeKind::kSampler;
+
+ SamplerType(const char* name, const Type& textureType)
+ : INHERITED(name, "Z", kTypeKind)
+ , fTextureType(textureType.as<TextureType>()) {}
+
+ const TextureType& textureType() const override {
+ return fTextureType;
+ }
+
+ SpvDim_ dimensions() const override {
+ return fTextureType.dimensions();
+ }
+
+ bool isDepth() const override {
+ return fTextureType.isDepth();
+ }
+
+ bool isArrayedTexture() const override {
+ return fTextureType.isArrayedTexture();
+ }
+
+ bool isMultisampled() const override {
+ return fTextureType.isMultisampled();
+ }
+
+ TextureAccess textureAccess() const override {
+ return fTextureType.textureAccess();
+ }
+
+private:
+ using INHERITED = Type;
+
+ const TextureType& fTextureType;
+};
+
+class StructType final : public Type {
+public:
+ inline static constexpr TypeKind kTypeKind = TypeKind::kStruct;
+
+ StructType(Position pos, std::string_view name, std::vector<Field> fields, bool interfaceBlock)
+ : INHERITED(std::move(name), "S", kTypeKind, pos)
+ , fFields(std::move(fields))
+ , fInterfaceBlock(interfaceBlock) {}
+
+ const std::vector<Field>& fields() const override {
+ return fFields;
+ }
+
+ bool isStruct() const override {
+ return true;
+ }
+
+ bool isInterfaceBlock() const override {
+ return fInterfaceBlock;
+ }
+
+ bool isAllowedInES2() const override {
+ return std::all_of(fFields.begin(), fFields.end(), [](const Field& f) {
+ return f.fType->isAllowedInES2();
+ });
+ }
+
+ size_t slotCount() const override {
+ size_t slots = 0;
+ for (const Field& field : fFields) {
+ slots += field.fType->slotCount();
+ }
+ return slots;
+ }
+
+private:
+ using INHERITED = Type;
+
+ std::vector<Field> fFields;
+ bool fInterfaceBlock;
+};
+
+class VectorType final : public Type {
+public:
+ inline static constexpr TypeKind kTypeKind = TypeKind::kVector;
+
+ VectorType(std::string_view name, const char* abbrev, const Type& componentType,
+ int8_t columns)
+ : INHERITED(name, abbrev, kTypeKind)
+ , fComponentType(componentType.as<ScalarType>())
+ , fColumns(columns) {
+ SkASSERT(columns >= 2 && columns <= 4);
+ }
+
+ const ScalarType& componentType() const override {
+ return fComponentType;
+ }
+
+ int columns() const override {
+ return fColumns;
+ }
+
+ int rows() const override {
+ return 1;
+ }
+
+ int bitWidth() const override {
+ return this->componentType().bitWidth();
+ }
+
+ bool isVector() const override {
+ return true;
+ }
+
+ bool isAllowedInES2() const override {
+ return fComponentType.isAllowedInES2();
+ }
+
+ size_t slotCount() const override {
+ return fColumns;
+ }
+
+private:
+ using INHERITED = Type;
+
+ const ScalarType& fComponentType;
+ int8_t fColumns;
+};
+
+std::string Type::getArrayName(int arraySize) const {
+ std::string_view name = this->name();
+ if (arraySize == kUnsizedArray) {
+ return String::printf("%.*s[]", (int)name.size(), name.data());
+ }
+ return String::printf("%.*s[%d]", (int)name.size(), name.data(), arraySize);
+}
+
+std::unique_ptr<Type> Type::MakeAliasType(std::string_view name, const Type& targetType) {
+ return std::make_unique<AliasType>(std::move(name), targetType);
+}
+
+std::unique_ptr<Type> Type::MakeArrayType(std::string_view name, const Type& componentType,
+ int columns) {
+ return std::make_unique<ArrayType>(std::move(name), componentType.abbreviatedName(),
+ componentType, columns);
+}
+
+std::unique_ptr<Type> Type::MakeGenericType(const char* name, SkSpan<const Type* const> types) {
+ return std::make_unique<GenericType>(name, types);
+}
+
+std::unique_ptr<Type> Type::MakeLiteralType(const char* name, const Type& scalarType,
+ int8_t priority) {
+ return std::make_unique<LiteralType>(name, scalarType, priority);
+}
+
+std::unique_ptr<Type> Type::MakeMatrixType(std::string_view name, const char* abbrev,
+ const Type& componentType, int columns, int8_t rows) {
+ return std::make_unique<MatrixType>(name, abbrev, componentType, columns, rows);
+}
+
+std::unique_ptr<Type> Type::MakeSamplerType(const char* name, const Type& textureType) {
+ return std::make_unique<SamplerType>(name, textureType);
+}
+
+std::unique_ptr<Type> Type::MakeSpecialType(const char* name, const char* abbrev,
+ Type::TypeKind typeKind) {
+ return std::unique_ptr<Type>(new Type(name, abbrev, typeKind));
+}
+
+std::unique_ptr<Type> Type::MakeScalarType(std::string_view name, const char* abbrev,
+ Type::NumberKind numberKind, int8_t priority,
+ int8_t bitWidth) {
+ return std::make_unique<ScalarType>(name, abbrev, numberKind, priority, bitWidth);
+}
+
+std::unique_ptr<Type> Type::MakeAtomicType(std::string_view name, const char* abbrev) {
+ return std::make_unique<AtomicType>(name, abbrev);
+}
+
+static bool is_too_deeply_nested(const Type* t, int limit) {
+ if (limit <= 0) {
+ return true;
+ }
+
+ if (t->isStruct()) {
+ for (const Type::Field& f : t->fields()) {
+ if (is_too_deeply_nested(f.fType, limit - 1)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+std::unique_ptr<Type> Type::MakeStructType(const Context& context,
+ Position pos,
+ std::string_view name,
+ std::vector<Field> fields,
+ bool interfaceBlock) {
+ for (const Field& field : fields) {
+ if (field.fModifiers.fFlags != Modifiers::kNo_Flag) {
+ std::string desc = field.fModifiers.description();
+ desc.pop_back(); // remove trailing space
+ context.fErrors->error(field.fPosition,
+ "modifier '" + desc + "' is not permitted on a struct field");
+ }
+ if (field.fModifiers.fLayout.fFlags & Layout::kBinding_Flag) {
+ context.fErrors->error(field.fPosition,
+ "layout qualifier 'binding' is not permitted on a struct field");
+ }
+ if (field.fModifiers.fLayout.fFlags & Layout::kSet_Flag) {
+ context.fErrors->error(field.fPosition,
+ "layout qualifier 'set' is not permitted on a struct field");
+ }
+
+ if (field.fType->isVoid()) {
+ context.fErrors->error(field.fPosition, "type 'void' is not permitted in a struct");
+ }
+ if (field.fType->isOpaque() && !field.fType->isAtomic()) {
+ context.fErrors->error(field.fPosition, "opaque type '" + field.fType->displayName() +
+ "' is not permitted in a struct");
+ }
+ }
+ for (const Field& field : fields) {
+ if (is_too_deeply_nested(field.fType, kMaxStructDepth)) {
+ context.fErrors->error(pos, "struct '" + std::string(name) + "' is too deeply nested");
+ break;
+ }
+ }
+ size_t slots = 0;
+ for (const Field& field : fields) {
+ if (field.fType->isUnsizedArray()) {
+ continue;
+ }
+ slots = SkSafeMath::Add(slots, field.fType->slotCount());
+ if (slots >= kVariableSlotLimit) {
+ context.fErrors->error(pos, "struct is too large");
+ break;
+ }
+ }
+ return std::make_unique<StructType>(pos, name, std::move(fields), interfaceBlock);
+}
+
+std::unique_ptr<Type> Type::MakeTextureType(const char* name, SpvDim_ dimensions, bool isDepth,
+ bool isArrayedTexture, bool isMultisampled,
+ TextureAccess textureAccess) {
+ return std::make_unique<TextureType>(name, dimensions, isDepth, isArrayedTexture,
+ isMultisampled, textureAccess);
+}
+
+std::unique_ptr<Type> Type::MakeVectorType(std::string_view name, const char* abbrev,
+ const Type& componentType, int columns) {
+ return std::make_unique<VectorType>(name, abbrev, componentType, columns);
+}
+
+CoercionCost Type::coercionCost(const Type& other) const {
+ if (this->matches(other)) {
+ return CoercionCost::Free();
+ }
+ if (this->typeKind() == other.typeKind() &&
+ (this->isVector() || this->isMatrix() || this->isArray())) {
+ // Vectors/matrices/arrays of the same size can be coerced if their component type can be.
+ if (this->isMatrix() && (this->rows() != other.rows())) {
+ return CoercionCost::Impossible();
+ }
+ if (this->columns() != other.columns()) {
+ return CoercionCost::Impossible();
+ }
+ return this->componentType().coercionCost(other.componentType());
+ }
+ if (this->isNumber() && other.isNumber()) {
+ if (this->isLiteral() && this->isInteger()) {
+ return CoercionCost::Free();
+ } else if (this->numberKind() != other.numberKind()) {
+ return CoercionCost::Impossible();
+ } else if (other.priority() >= this->priority()) {
+ return CoercionCost::Normal(other.priority() - this->priority());
+ } else {
+ return CoercionCost::Narrowing(this->priority() - other.priority());
+ }
+ }
+ if (fTypeKind == TypeKind::kGeneric) {
+ SkSpan<const Type* const> types = this->coercibleTypes();
+ for (size_t i = 0; i < types.size(); i++) {
+ if (types[i]->matches(other)) {
+ return CoercionCost::Normal((int) i + 1);
+ }
+ }
+ }
+ return CoercionCost::Impossible();
+}
+
+const Type* Type::applyQualifiers(const Context& context,
+ Modifiers* modifiers,
+ SymbolTable* symbols,
+ Position pos) const {
+ const Type* type;
+ type = this->applyPrecisionQualifiers(context, modifiers, symbols, pos);
+ type = type->applyAccessQualifiers(context, modifiers, symbols, pos);
+ return type;
+}
+
+const Type* Type::applyPrecisionQualifiers(const Context& context,
+ Modifiers* modifiers,
+ SymbolTable* symbols,
+ Position pos) const {
+ int precisionQualifiers = modifiers->fFlags & (Modifiers::kHighp_Flag |
+ Modifiers::kMediump_Flag |
+ Modifiers::kLowp_Flag);
+ if (!precisionQualifiers) {
+ // No precision qualifiers here. Return the type as-is.
+ return this;
+ }
+
+ if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
+ // We want to discourage precision modifiers internally. Instead, use the type that
+ // corresponds to the precision you need. (e.g. half vs float, short vs int)
+ context.fErrors->error(pos, "precision qualifiers are not allowed");
+ return context.fTypes.fPoison.get();
+ }
+
+ if (SkPopCount(precisionQualifiers) > 1) {
+ context.fErrors->error(pos, "only one precision qualifier can be used");
+ return context.fTypes.fPoison.get();
+ }
+
+ // We're going to return a whole new type, so the modifier bits can be cleared out.
+ modifiers->fFlags &= ~(Modifiers::kHighp_Flag |
+ Modifiers::kMediump_Flag |
+ Modifiers::kLowp_Flag);
+
+ const Type& component = this->componentType();
+ if (component.highPrecision()) {
+ if (precisionQualifiers & Modifiers::kHighp_Flag) {
+ // Type is already high precision, and we are requesting high precision. Return as-is.
+ return this;
+ }
+
+ // SkSL doesn't support low precision, so `lowp` is interpreted as medium precision.
+ // Ascertain the mediump equivalent type for this type, if any.
+ const Type* mediumpType;
+ switch (component.numberKind()) {
+ case Type::NumberKind::kFloat:
+ mediumpType = context.fTypes.fHalf.get();
+ break;
+
+ case Type::NumberKind::kSigned:
+ mediumpType = context.fTypes.fShort.get();
+ break;
+
+ case Type::NumberKind::kUnsigned:
+ mediumpType = context.fTypes.fUShort.get();
+ break;
+
+ default:
+ mediumpType = context.fTypes.fPoison.get();
+ break;
+ }
+
+ if (mediumpType) {
+ // Convert the mediump component type into the final vector/matrix/array type as needed.
+ return this->isArray()
+ ? symbols->addArrayDimension(mediumpType, this->columns())
+ : &mediumpType->toCompound(context, this->columns(), this->rows());
+ }
+ }
+
+ context.fErrors->error(pos, "type '" + this->displayName() +
+ "' does not support precision qualifiers");
+ return context.fTypes.fPoison.get();
+}
+
+const Type* Type::applyAccessQualifiers(const Context& context,
+ Modifiers* modifiers,
+ SymbolTable* symbols,
+ Position pos) const {
+ int accessQualifiers = modifiers->fFlags & (Modifiers::kReadOnly_Flag |
+ Modifiers::kWriteOnly_Flag);
+ if (!accessQualifiers) {
+ // No access qualifiers here. Return the type as-is.
+ return this;
+ }
+
+ // We're going to return a whole new type, so the modifier bits can be cleared out.
+ modifiers->fFlags &= ~(Modifiers::kReadOnly_Flag |
+ Modifiers::kWriteOnly_Flag);
+
+ if (this->matches(*context.fTypes.fReadWriteTexture2D)) {
+ switch (accessQualifiers) {
+ case Modifiers::kReadOnly_Flag:
+ return context.fTypes.fReadOnlyTexture2D.get();
+
+ case Modifiers::kWriteOnly_Flag:
+ return context.fTypes.fWriteOnlyTexture2D.get();
+
+ default:
+ context.fErrors->error(pos, "'readonly' and 'writeonly' qualifiers "
+ "cannot be combined");
+ return this;
+ }
+ }
+
+ context.fErrors->error(pos, "type '" + this->displayName() + "' does not support qualifier '" +
+ Modifiers::DescribeFlags(accessQualifiers) + "'");
+ return this;
+}
+
+const Type& Type::toCompound(const Context& context, int columns, int rows) const {
+ SkASSERT(this->isScalar());
+ if (columns == 1 && rows == 1) {
+ return *this;
+ }
+ if (this->matches(*context.fTypes.fFloat) || this->matches(*context.fTypes.fFloatLiteral)) {
+ switch (rows) {
+ case 1:
+ switch (columns) {
+ case 1: return *context.fTypes.fFloat;
+ case 2: return *context.fTypes.fFloat2;
+ case 3: return *context.fTypes.fFloat3;
+ case 4: return *context.fTypes.fFloat4;
+ default: SK_ABORT("unsupported vector column count (%d)", columns);
+ }
+ case 2:
+ switch (columns) {
+ case 2: return *context.fTypes.fFloat2x2;
+ case 3: return *context.fTypes.fFloat3x2;
+ case 4: return *context.fTypes.fFloat4x2;
+ default: SK_ABORT("unsupported matrix column count (%d)", columns);
+ }
+ case 3:
+ switch (columns) {
+ case 2: return *context.fTypes.fFloat2x3;
+ case 3: return *context.fTypes.fFloat3x3;
+ case 4: return *context.fTypes.fFloat4x3;
+ default: SK_ABORT("unsupported matrix column count (%d)", columns);
+ }
+ case 4:
+ switch (columns) {
+ case 2: return *context.fTypes.fFloat2x4;
+ case 3: return *context.fTypes.fFloat3x4;
+ case 4: return *context.fTypes.fFloat4x4;
+ default: SK_ABORT("unsupported matrix column count (%d)", columns);
+ }
+ default: SK_ABORT("unsupported row count (%d)", rows);
+ }
+ } else if (this->matches(*context.fTypes.fHalf)) {
+ switch (rows) {
+ case 1:
+ switch (columns) {
+ case 1: return *context.fTypes.fHalf;
+ case 2: return *context.fTypes.fHalf2;
+ case 3: return *context.fTypes.fHalf3;
+ case 4: return *context.fTypes.fHalf4;
+ default: SK_ABORT("unsupported vector column count (%d)", columns);
+ }
+ case 2:
+ switch (columns) {
+ case 2: return *context.fTypes.fHalf2x2;
+ case 3: return *context.fTypes.fHalf3x2;
+ case 4: return *context.fTypes.fHalf4x2;
+ default: SK_ABORT("unsupported matrix column count (%d)", columns);
+ }
+ case 3:
+ switch (columns) {
+ case 2: return *context.fTypes.fHalf2x3;
+ case 3: return *context.fTypes.fHalf3x3;
+ case 4: return *context.fTypes.fHalf4x3;
+ default: SK_ABORT("unsupported matrix column count (%d)", columns);
+ }
+ case 4:
+ switch (columns) {
+ case 2: return *context.fTypes.fHalf2x4;
+ case 3: return *context.fTypes.fHalf3x4;
+ case 4: return *context.fTypes.fHalf4x4;
+ default: SK_ABORT("unsupported matrix column count (%d)", columns);
+ }
+ default: SK_ABORT("unsupported row count (%d)", rows);
+ }
+ } else if (this->matches(*context.fTypes.fInt) || this->matches(*context.fTypes.fIntLiteral)) {
+ switch (rows) {
+ case 1:
+ switch (columns) {
+ case 1: return *context.fTypes.fInt;
+ case 2: return *context.fTypes.fInt2;
+ case 3: return *context.fTypes.fInt3;
+ case 4: return *context.fTypes.fInt4;
+ default: SK_ABORT("unsupported vector column count (%d)", columns);
+ }
+ default: SK_ABORT("unsupported row count (%d)", rows);
+ }
+ } else if (this->matches(*context.fTypes.fShort)) {
+ switch (rows) {
+ case 1:
+ switch (columns) {
+ case 1: return *context.fTypes.fShort;
+ case 2: return *context.fTypes.fShort2;
+ case 3: return *context.fTypes.fShort3;
+ case 4: return *context.fTypes.fShort4;
+ default: SK_ABORT("unsupported vector column count (%d)", columns);
+ }
+ default: SK_ABORT("unsupported row count (%d)", rows);
+ }
+ } else if (this->matches(*context.fTypes.fUInt)) {
+ switch (rows) {
+ case 1:
+ switch (columns) {
+ case 1: return *context.fTypes.fUInt;
+ case 2: return *context.fTypes.fUInt2;
+ case 3: return *context.fTypes.fUInt3;
+ case 4: return *context.fTypes.fUInt4;
+ default: SK_ABORT("unsupported vector column count (%d)", columns);
+ }
+ default: SK_ABORT("unsupported row count (%d)", rows);
+ }
+ } else if (this->matches(*context.fTypes.fUShort)) {
+ switch (rows) {
+ case 1:
+ switch (columns) {
+ case 1: return *context.fTypes.fUShort;
+ case 2: return *context.fTypes.fUShort2;
+ case 3: return *context.fTypes.fUShort3;
+ case 4: return *context.fTypes.fUShort4;
+ default: SK_ABORT("unsupported vector column count (%d)", columns);
+ }
+ default: SK_ABORT("unsupported row count (%d)", rows);
+ }
+ } else if (this->matches(*context.fTypes.fBool)) {
+ switch (rows) {
+ case 1:
+ switch (columns) {
+ case 1: return *context.fTypes.fBool;
+ case 2: return *context.fTypes.fBool2;
+ case 3: return *context.fTypes.fBool3;
+ case 4: return *context.fTypes.fBool4;
+ default: SK_ABORT("unsupported vector column count (%d)", columns);
+ }
+ default: SK_ABORT("unsupported row count (%d)", rows);
+ }
+ }
+ SkDEBUGFAILF("unsupported toCompound type %s", this->description().c_str());
+ return *context.fTypes.fVoid;
+}
+
+const Type* Type::clone(SymbolTable* symbolTable) const {
+ // Many types are built-ins, and exist in every SymbolTable by default.
+ if (this->isInBuiltinTypes()) {
+ return this;
+ }
+ // Even if the type isn't a built-in, it might already exist in the SymbolTable.
+ const Symbol* clonedSymbol = symbolTable->find(this->name());
+ if (clonedSymbol != nullptr) {
+ const Type& clonedType = clonedSymbol->as<Type>();
+ SkASSERT(clonedType.typeKind() == this->typeKind());
+ return &clonedType;
+ }
+ // This type actually needs to be cloned into the destination SymbolTable.
+ switch (this->typeKind()) {
+ case TypeKind::kArray: {
+ return symbolTable->addArrayDimension(&this->componentType(), this->columns());
+ }
+ case TypeKind::kStruct: {
+ // We are cloning an existing struct, so there's no need to call MakeStructType and
+ // fully error-check it again.
+ const std::string* name = symbolTable->takeOwnershipOfString(std::string(this->name()));
+ return symbolTable->add(std::make_unique<StructType>(
+ this->fPosition, *name, this->fields(), this->isInterfaceBlock()));
+ }
+ default:
+ SkDEBUGFAILF("don't know how to clone type '%s'", this->description().c_str());
+ return nullptr;
+ }
+}
+
+std::unique_ptr<Expression> Type::coerceExpression(std::unique_ptr<Expression> expr,
+ const Context& context) const {
+ if (!expr || expr->isIncomplete(context)) {
+ return nullptr;
+ }
+ if (expr->type().matches(*this)) {
+ return expr;
+ }
+
+ const Position pos = expr->fPosition;
+ const ProgramSettings& settings = context.fConfig->fSettings;
+ if (!expr->coercionCost(*this).isPossible(settings.fAllowNarrowingConversions)) {
+ context.fErrors->error(pos, "expected '" + this->displayName() + "', but found '" +
+ expr->type().displayName() + "'");
+ return nullptr;
+ }
+
+ if (this->isScalar()) {
+ return ConstructorScalarCast::Make(context, pos, *this, std::move(expr));
+ }
+ if (this->isVector() || this->isMatrix()) {
+ return ConstructorCompoundCast::Make(context, pos, *this, std::move(expr));
+ }
+ if (this->isArray()) {
+ return ConstructorArrayCast::Make(context, pos, *this, std::move(expr));
+ }
+ context.fErrors->error(pos, "cannot construct '" + this->displayName() + "'");
+ return nullptr;
+}
+
+static bool is_or_contains_array(const Type* type, bool onlyMatchUnsizedArrays) {
+ if (type->isStruct()) {
+ for (const Type::Field& f : type->fields()) {
+ if (is_or_contains_array(f.fType, onlyMatchUnsizedArrays)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ if (type->isArray()) {
+ return onlyMatchUnsizedArrays
+ ? (type->isUnsizedArray() || is_or_contains_array(&type->componentType(), true))
+ : true;
+ }
+
+ return false;
+}
+
+bool Type::isOrContainsArray() const {
+ return is_or_contains_array(this, /*onlyMatchUnsizedArrays=*/false);
+}
+
+bool Type::isOrContainsUnsizedArray() const {
+ return is_or_contains_array(this, /*onlyMatchUnsizedArrays=*/true);
+}
+
+bool Type::isOrContainsAtomic() const {
+ if (this->isAtomic()) {
+ return true;
+ }
+
+ if (this->isArray() && this->componentType().isOrContainsAtomic()) {
+ return true;
+ }
+
+ if (this->isStruct()) {
+ for (const Field& f : this->fields()) {
+ if (f.fType->isOrContainsAtomic()) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool Type::isAllowedInES2(const Context& context) const {
+ return !context.fConfig->strictES2Mode() || this->isAllowedInES2();
+}
+
+bool Type::checkForOutOfRangeLiteral(const Context& context, const Expression& expr) const {
+ bool foundError = false;
+ const Type& baseType = this->componentType();
+ if (baseType.isNumber()) {
+ // Replace constant expressions with their corresponding values.
+ const Expression* valueExpr = ConstantFolder::GetConstantValueForVariable(expr);
+ if (valueExpr->supportsConstantValues()) {
+ // Iterate over every constant subexpression in the value.
+ int numSlots = valueExpr->type().slotCount();
+ for (int slot = 0; slot < numSlots; ++slot) {
+ std::optional<double> slotVal = valueExpr->getConstantValue(slot);
+ // Check for Literal values that are out of range for the base type.
+ if (slotVal.has_value() &&
+ baseType.checkForOutOfRangeLiteral(context, *slotVal, valueExpr->fPosition)) {
+ foundError = true;
+ }
+ }
+ }
+ }
+
+ // We don't need range checks for floats or booleans; any matched-type value is acceptable.
+ return foundError;
+}
+
+bool Type::checkForOutOfRangeLiteral(const Context& context, double value, Position pos) const {
+ SkASSERT(this->isScalar());
+ if (!this->isNumber()) {
+ return false;
+ }
+ if (value >= this->minimumValue() && value <= this->maximumValue()) {
+ return false;
+ }
+ // We found a value that can't fit in our type. Flag it as an error.
+ context.fErrors->error(pos, SkSL::String::printf("value is out of range for type '%s': %.0f",
+ this->displayName().c_str(),
+ value));
+ return true;
+}
+
+bool Type::checkIfUsableInArray(const Context& context, Position arrayPos) const {
+ if (this->isArray()) {
+ context.fErrors->error(arrayPos, "multi-dimensional arrays are not supported");
+ return false;
+ }
+ if (this->isVoid()) {
+ context.fErrors->error(arrayPos, "type 'void' may not be used in an array");
+ return false;
+ }
+ if (this->isOpaque() && !this->isAtomic()) {
+ context.fErrors->error(arrayPos, "opaque type '" + std::string(this->name()) +
+ "' may not be used in an array");
+ return false;
+ }
+ return true;
+}
+
+SKSL_INT Type::convertArraySize(const Context& context,
+ Position arrayPos,
+ std::unique_ptr<Expression> size) const {
+ size = context.fTypes.fInt->coerceExpression(std::move(size), context);
+ if (!size) {
+ return 0;
+ }
+ if (!this->checkIfUsableInArray(context, arrayPos)) {
+ return 0;
+ }
+ SKSL_INT count;
+ if (!ConstantFolder::GetConstantInt(*size, &count)) {
+ context.fErrors->error(size->fPosition, "array size must be an integer");
+ return 0;
+ }
+ if (count <= 0) {
+ context.fErrors->error(size->fPosition, "array size must be positive");
+ return 0;
+ }
+ if (SkSafeMath::Mul(this->slotCount(), count) > kVariableSlotLimit) {
+ context.fErrors->error(size->fPosition, "array size is too large");
+ return 0;
+ }
+ return static_cast<int>(count);
+}
+
+std::string Type::Field::description() const {
+ return fModifiers.description() + fType->displayName() + " " + std::string(fName) + ";";
+}
+
+} // namespace SkSL