Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/gfx/skia/skia/src/sksl/ir/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 21 kB image not shown  

Quelle  SkSLVarDeclarations.cpp   Sprache: C

 
/*
 * Copyright 2021 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */


#include "src/sksl/ir/SkSLVarDeclarations.h"

#include "include/core/SkSpan.h"
#include "include/private/base/SkTo.h"
#include "src/base/SkEnumBitMask.h"
#include "src/sksl/SkSLAnalysis.h"
#include "src/sksl/SkSLBuiltinTypes.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLContext.h"
#include "src/sksl/SkSLErrorReporter.h"
#include "src/sksl/SkSLPosition.h"
#include "src/sksl/SkSLProgramKind.h"
#include "src/sksl/SkSLProgramSettings.h"
#include "src/sksl/SkSLString.h"
#include "src/sksl/ir/SkSLLayout.h"
#include "src/sksl/ir/SkSLModifierFlags.h"
#include "src/sksl/ir/SkSLModifiers.h"
#include "src/sksl/ir/SkSLSymbolTable.h"
#include "src/sksl/ir/SkSLType.h"

#include <string_view>

namespace SkSL {
namespace {

static bool check_valid_uniform_type(Position pos,
                                     const Type* t,
                                     const Context& context,
                                     bool topLevel = true) {
    auto reportError = [&]() {
        context.fErrors->error(pos, "variables of type '" + t->displayName() +
                                    "' may not be uniform");
    };

    // In Runtime Effects we only allow a restricted set of types: shader, blender, colorFilter,
    // 32-bit signed integers, 16-bit and 32-bit floats, and their vector/square-matrix composites.
    if (ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
        // `shader`, `blender`, `colorFilter`
        if (t->isEffectChild()) {
            return true;
        }

        // `int`, `int2`, `int3`, `int4`
        const Type& ct = t->componentType();
        if (ct.isSigned() && ct.bitWidth() == 32 && (t->isScalar() || t->isVector())) {
            return true;
        }

        // `float`, `float2`, `float3`, `float4`, `float2x2`, `float3x3`, `float4x4`
        // `half`, `half2`, `half3`, `half4`, `half2x2`, `half3x3`, `half4x4`
        if (ct.isFloat() &&
            (t->isScalar() || t->isVector() || (t->isMatrix() && t->rows() == t->columns()))) {
            return true;
        }

        // Everything else is an error.
        reportError();
        return false;
    }

    Position errorPosition = {};
    if (!t->isAllowedInUniform(&errorPosition)) {
        reportError();
        if (errorPosition.valid()) {
            context.fErrors->error(errorPosition, "caused by:");
        }
        return false;
    }

    return true;
}

}  // namespace

std::string VarDeclaration::description() const {
    std::string result = this->var()->layout().paddedDescription() +
                         this->var()->modifierFlags().paddedDescription() +
                         this->baseType().description() + ' ' + std::string(this->var()->name());
    if (this->arraySize() > 0) {
        String::appendf(&result, "[%d]", this->arraySize());
    }
    if (this->value()) {
        result += " = " + this->value()->description();
    }
    result += ";";
    return result;
}

void VarDeclaration::ErrorCheck(const Context& context,
                                Position pos,
                                Position modifiersPosition,
                                const Layout& layout,
                                ModifierFlags modifierFlags,
                                const Type* type,
                                const Type* baseType,
                                Variable::Storage storage) {
    SkASSERT(type->isArray() ? baseType->matches(type->componentType())
                             : baseType->matches(*type));

    if (baseType->componentType().isOpaque() && !baseType->componentType().isAtomic() &&
        storage != Variable::Storage::kGlobal) {
        context.fErrors->error(pos, "variables of type '" + baseType->displayName() +
                                    "' must be global");
    }
    if ((modifierFlags & ModifierFlag::kIn) && baseType->isMatrix()) {
        context.fErrors->error(pos, "'in' variables may not have matrix type");
    }
    if ((modifierFlags & ModifierFlag::kIn) && type->isUnsizedArray()) {
        context.fErrors->error(pos, "'in' variables may not have unsized array type");
    }
    if ((modifierFlags & ModifierFlag::kOut) && type->isUnsizedArray()) {
        context.fErrors->error(pos, "'out' variables may not have unsized array type");
    }
    if ((modifierFlags & ModifierFlag::kIn) && modifierFlags.isUniform()) {
        context.fErrors->error(pos, "'in uniform' variables not permitted");
    }
    if (modifierFlags.isReadOnly() && modifierFlags.isWriteOnly()) {
        context.fErrors->error(pos, "'readonly' and 'writeonly' qualifiers cannot be combined");
    }
    if (modifierFlags.isUniform() && modifierFlags.isBuffer()) {
        context.fErrors->error(pos, "'uniform buffer' variables not permitted");
    }
    if (modifierFlags.isWorkgroup() && (modifierFlags & (ModifierFlag::kIn |
                                                         ModifierFlag::kOut))) {
        context.fErrors->error(pos, "in / out variables may not be declared workgroup");
    }
    if (modifierFlags.isUniform()) {
        check_valid_uniform_type(pos, baseType, context);
    }
    if (baseType->isEffectChild() && !modifierFlags.isUniform()) {
        context.fErrors->error(pos, "variables of type '" + baseType->displayName() +
                                    "' must be uniform");
    }
    if (baseType->isEffectChild() && context.fConfig->fKind == ProgramKind::kMeshVertex) {
        context.fErrors->error(pos, "effects are not permitted in mesh vertex shaders");
    }
    if (baseType->isOrContainsAtomic()) {
        // An atomic variable (or a struct or an array that contains an atomic member) must be
        // either:
        //   a. Declared as a workgroup-shared variable, OR
        //   b. Declared as the member of writable storage buffer block (i.e. has no readonly
        //   restriction).
        //
        // The checks below will enforce these two rules on all declarations. If the variable is not
        // declared with the workgroup modifier, then it must be declared in the interface block
        // storage. If this is the declaration for an interface block that contains an atomic
        // member, then it must have the `buffer` modifier and no `readonly` modifier.
        bool isBlockMember = (storage == Variable::Storage::kInterfaceBlock);
        bool isWritableStorageBuffer = modifierFlags.isBuffer() && !modifierFlags.isReadOnly();

        if (!modifierFlags.isWorkgroup() &&
            !(baseType->isInterfaceBlock() ? isWritableStorageBuffer : isBlockMember)) {
            context.fErrors->error(pos, "atomics are only permitted in workgroup variables and "
                                        "writable storage blocks");
        }
    }
    if (layout.fFlags & LayoutFlag::kColor) {
        if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
            context.fErrors->error(pos, "'layout(color)' is only permitted in runtime effects");
        }
        if (!modifierFlags.isUniform()) {
            context.fErrors->error(pos, "'layout(color)' is only permitted on 'uniform' variables");
        }
        auto validColorXformType = [](const Type& t) {
            return t.isVector() && t.componentType().isFloat() &&
                   (t.columns() == 3 || t.columns() == 4);
        };
        if (!validColorXformType(*baseType)) {
            context.fErrors->error(pos, "'layout(color)' is not permitted on variables of type '" +
                                        baseType->displayName() + "'");
        }
    }

    ModifierFlags permitted = ModifierFlag::kConst | ModifierFlag::kHighp | ModifierFlag::kMediump |
                              ModifierFlag::kLowp;
    if (storage == Variable::Storage::kGlobal) {
        // Uniforms are allowed in all programs
        permitted |= ModifierFlag::kUniform;

        // No other modifiers are allowed in runtime effects.
        if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
            if (baseType->isInterfaceBlock()) {
                // Interface blocks allow `buffer`.
                permitted |= ModifierFlag::kBuffer;

                if (modifierFlags.isBuffer()) {
                    // Only storage blocks allow `readonly` and `writeonly`.
                    // (`readonly` and `writeonly` textures are converted to separate types via
                    // applyAccessQualifiers.)
                    permitted |= ModifierFlag::kReadOnly | ModifierFlag::kWriteOnly;
                }

                // It is an error for an unsized array to appear anywhere but the last member of a
                // "buffer" block.
                const auto& fields = baseType->fields();
                const int illegalRangeEnd = SkToInt(fields.size()) -
                                            (modifierFlags.isBuffer() ? 1 : 0);
                for (int i = 0; i < illegalRangeEnd; ++i) {
                    if (fields[i].fType->isUnsizedArray()) {
                        context.fErrors->error(
                                fields[i].fPosition,
                                "unsized array must be the last member of a storage block");
                    }
                }
            }

            if (!baseType->isOpaque()) {
                // Only non-opaque types allow `in` and `out`.
                permitted |= ModifierFlag::kIn | ModifierFlag::kOut;
            }
            if (ProgramConfig::IsFragment(context.fConfig->fKind) && baseType->isStruct() &&
                !baseType->isInterfaceBlock()) {
                // Only structs in fragment shaders allow `pixel_local`.
                permitted |= ModifierFlag::kPixelLocal;
            }
            if (ProgramConfig::IsCompute(context.fConfig->fKind)) {
                // Only compute shaders allow `workgroup`.
                if (!baseType->isOpaque() || baseType->isAtomic()) {
                    permitted |= ModifierFlag::kWorkgroup;
                }
            } else {
                // Only vertex/fragment shaders allow `flat` and `noperspective`.
                permitted |= ModifierFlag::kFlat | ModifierFlag::kNoPerspective;
            }
        }
    }

    LayoutFlags permittedLayoutFlags = LayoutFlag::kAll;

    // Pixel format modifiers are required on storage textures, and forbidden on other types.
    if (baseType->isStorageTexture()) {
        if (!(layout.fFlags & LayoutFlag::kAllPixelFormats)) {
            context.fErrors->error(pos, "storage textures must declare a pixel format");
        }
    } else {
        permittedLayoutFlags &= ~LayoutFlag::kAllPixelFormats;
    }

    // The `texture` and `sampler` modifiers can be present respectively on a texture and sampler or
    // simultaneously on a combined image-sampler but they are not permitted on any other type.
    switch (baseType->typeKind()) {
        case Type::TypeKind::kSampler:
            // Both texture and sampler flags are permitted
            break;
        case Type::TypeKind::kTexture:
            permittedLayoutFlags &= ~LayoutFlag::kSampler;
            break;
        case Type::TypeKind::kSeparateSampler:
            permittedLayoutFlags &= ~LayoutFlag::kTexture;
            break;
        default:
            permittedLayoutFlags &= ~(LayoutFlag::kTexture | LayoutFlag::kSampler);
            break;
    }

    // We don't allow 'binding' or 'set' on normal uniform variables, only on textures, samplers,
    // and interface blocks (holding uniform variables). They're also only allowed at global scope,
    // not on interface block fields (or locals/parameters).
    bool permitBindingAndSet = baseType->typeKind() == Type::TypeKind::kSampler ||
                               baseType->typeKind() == Type::TypeKind::kSeparateSampler ||
                               baseType->typeKind() == Type::TypeKind::kTexture ||
                               baseType->isInterfaceBlock();
    if (storage != Variable::Storage::kGlobal || (modifierFlags.isUniform() &&
                                                  !permitBindingAndSet)) {
        permittedLayoutFlags &= ~LayoutFlag::kBinding;
        permittedLayoutFlags &= ~LayoutFlag::kSet;
        permittedLayoutFlags &= ~LayoutFlag::kAllBackends;
    }
    if (ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
        // Disallow all layout flags except 'color' in runtime effects
        permittedLayoutFlags &= LayoutFlag::kColor;
    }

    // The `push_constant` flag isn't allowed on in-variables, out-variables, bindings or sets.
    if ((layout.fFlags & (LayoutFlag::kSet | LayoutFlag::kBinding)) ||
        (modifierFlags & (ModifierFlag::kIn | ModifierFlag::kOut))) {
        permittedLayoutFlags &= ~LayoutFlag::kPushConstant;
    }
    // The `builtin` layout flag is only allowed in modules.
    if (!context.fConfig->isBuiltinCode()) {
        permittedLayoutFlags &= ~LayoutFlag::kBuiltin;
    }

    modifierFlags.checkPermittedFlags(context, modifiersPosition, permitted);
    layout.checkPermittedLayout(context, modifiersPosition, permittedLayoutFlags);
}

bool VarDeclaration::ErrorCheckAndCoerce(const Context& context,
                                         const Variable& var,
                                         const Type* baseType,
                                         std::unique_ptr<Expression>& value) {
    if (baseType->matches(*context.fTypes.fInvalid)) {
        context.fErrors->error(var.fPosition, "invalid type");
        return false;
    }
    if (baseType->isVoid()) {
        context.fErrors->error(var.fPosition, "variables of type 'void' are not allowed");
        return false;
    }

    ErrorCheck(context, var.fPosition, var.modifiersPosition(), var.layout(), var.modifierFlags(),
               &var.type(), baseType, var.storage());
    if (value) {
        if (var.type().isOpaque() || var.type().isOrContainsAtomic()) {
            context.fErrors->error(value->fPosition, "opaque type '" + var.type().displayName() +
                                                     "' cannot use initializer expressions");
            return false;
        }
        if (var.modifierFlags() & ModifierFlag::kIn) {
            context.fErrors->error(value->fPosition,
                                   "'in' variables cannot use initializer expressions");
            return false;
        }
        if (var.modifierFlags().isUniform()) {
            context.fErrors->error(value->fPosition,
                                   "'uniform' variables cannot use initializer expressions");
            return false;
        }
        if (var.storage() == Variable::Storage::kInterfaceBlock) {
            context.fErrors->error(value->fPosition,
                                   "initializers are not permitted on interface block fields");
            return false;
        }
        if (context.fConfig->strictES2Mode() && var.type().isOrContainsArray()) {
            context.fErrors->error(value->fPosition, "initializers are not permitted on arrays "
                                                     "(or structs containing arrays)");
            return false;
        }
        value = var.type().coerceExpression(std::move(value), context);
        if (!value) {
            return false;
        }
    }
    if (var.modifierFlags().isConst()) {
        if (!value) {
            context.fErrors->error(var.fPosition, "'const' variables must be initialized");
            return false;
        }
        if (!Analysis::IsConstantExpression(*value)) {
            context.fErrors->error(value->fPosition,
                                   "'const' variable initializer must be a constant expression");
            return false;
        }
    }
    if (var.storage() == Variable::Storage::kInterfaceBlock) {
        if (var.type().isOpaque()) {
            context.fErrors->error(var.fPosition, "opaque type '" + var.type().displayName() +
                                                  "' is not permitted in an interface block");
            return false;
        }
    }
    if (var.storage() == Variable::Storage::kGlobal) {
        if (value && !Analysis::IsConstantExpression(*value)) {
            context.fErrors->error(value->fPosition,
                                   "global variable initializer must be a constant expression");
            return false;
        }
    }
    return true;
}

std::unique_ptr<VarDeclaration> VarDeclaration::Convert(const Context& context,
                                                        Position overallPos,
                                                        const Modifiers& modifiers,
                                                        const Type& type,
                                                        Position namePos,
                                                        std::string_view name,
                                                        VariableStorage storage,
                                                        std::unique_ptr<Expression> value) {
    // Parameter declaration-statements do not exist in the grammar (unlike, say, K&R C).
    SkASSERT(storage != VariableStorage::kParameter);

    std::unique_ptr<Variable> var = Variable::Convert(context,
                                                      overallPos,
                                                      modifiers.fPosition,
                                                      modifiers.fLayout,
                                                      modifiers.fFlags,
                                                      &type,
                                                      namePos,
                                                      name,
                                                      storage);
    if (!var) {
        return nullptr;
    }
    return VarDeclaration::Convert(context, std::move(var), std::move(value));
}

std::unique_ptr<VarDeclaration> VarDeclaration::Convert(const Context& context,
                                                        std::unique_ptr<Variable> var,
                                                        std::unique_ptr<Expression> value) {
    const Type* baseType = &var->type();
    int arraySize = 0;
    if (baseType->isArray()) {
        arraySize = baseType->columns();
        baseType = &baseType->componentType();
    }
    if (!ErrorCheckAndCoerce(context, *var, baseType, value)) {
        return nullptr;
    }
    std::unique_ptr<VarDeclaration> varDecl = VarDeclaration::Make(context, var.get(), baseType,
                                                                   arraySize, std::move(value));
    if (!varDecl) {
        return nullptr;
    }

    if (var->storage() == Variable::Storage::kGlobal ||
        var->storage() == Variable::Storage::kInterfaceBlock) {
        // Check if this globally-scoped variable name overlaps an existing symbol name.
        if (context.fSymbolTable->find(var->name())) {
            context.fErrors->error(var->fPosition,
                                   "symbol '" + std::string(var->name()) + "' was already defined");
            return nullptr;
        }

        // `sk_RTAdjust` is special, and makes the IR generator emit position-fixup expressions.
        if (var->name() == Compiler::RTADJUST_NAME) {
            if (!var->type().matches(*context.fTypes.fFloat4)) {
                context.fErrors->error(var->fPosition, "sk_RTAdjust must have type 'float4'");
                return nullptr;
            }
        }
    }

    context.fSymbolTable->add(context, std::move(var));
    return varDecl;
}

std::unique_ptr<VarDeclaration> VarDeclaration::Make(const Context& context,
                                                     Variable* var,
                                                     const Type* baseType,
                                                     int arraySize,
                                                     std::unique_ptr<Expression> value) {
    SkASSERT(!baseType->isArray());
    // function parameters cannot have variable declarations
    SkASSERT(var->storage() != Variable::Storage::kParameter);
    // 'const' variables must be initialized
    SkASSERT(!var->modifierFlags().isConst() || value);
    // 'const' variable initializer must be a constant expression
    SkASSERT(!var->modifierFlags().isConst() || Analysis::IsConstantExpression(*value));
    // global variable initializer must be a constant expression
    SkASSERT(!(value && var->storage() == Variable::Storage::kGlobal &&
               !Analysis::IsConstantExpression(*value)));
    // opaque type not permitted on an interface block
    SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && var->type().isOpaque()));
    // initializers are not permitted on interface block fields
    SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && value));
    // opaque type cannot use initializer expressions
    SkASSERT(!(value && var->type().isOpaque()));
    // 'in' variables cannot use initializer expressions
    SkASSERT(!(value && (var->modifierFlags() & ModifierFlag::kIn)));
    // 'uniform' variables cannot use initializer expressions
    SkASSERT(!(value && var->modifierFlags().isUniform()));
    // in strict-ES2 mode, is-or-contains-array types cannot use initializer expressions
    SkASSERT(!(value && var->type().isOrContainsArray() && context.fConfig->strictES2Mode()));

    auto result = std::make_unique<VarDeclaration>(var, baseType, arraySize, std::move(value));
    var->setVarDeclaration(result.get());
    return result;
}

}  // namespace SkSL

Messung V0.5
C=90 H=92 G=90

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.