tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SkSLFinalizationChecks.cpp (9108B)


      1 /*
      2 * Copyright 2022 Google LLC
      3 *
      4 * Use of this source code is governed by a BSD-style license that can be
      5 * found in the LICENSE file.
      6 */
      7 
      8 #include "include/core/SkSpan.h"
      9 #include "include/core/SkTypes.h"
     10 #include "src/base/SkEnumBitMask.h"
     11 #include "src/base/SkSafeMath.h"
     12 #include "src/core/SkTHash.h"
     13 #include "src/sksl/SkSLAnalysis.h"
     14 #include "src/sksl/SkSLBuiltinTypes.h"
     15 #include "src/sksl/SkSLContext.h"
     16 #include "src/sksl/SkSLDefines.h"
     17 #include "src/sksl/SkSLErrorReporter.h"
     18 #include "src/sksl/SkSLPosition.h"
     19 #include "src/sksl/SkSLProgramSettings.h"
     20 #include "src/sksl/analysis/SkSLProgramUsage.h"
     21 #include "src/sksl/analysis/SkSLProgramVisitor.h"
     22 #include "src/sksl/ir/SkSLExpression.h"
     23 #include "src/sksl/ir/SkSLFunctionCall.h"
     24 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
     25 #include "src/sksl/ir/SkSLFunctionDefinition.h"
     26 #include "src/sksl/ir/SkSLIRNode.h"
     27 #include "src/sksl/ir/SkSLInterfaceBlock.h"
     28 #include "src/sksl/ir/SkSLLayout.h"
     29 #include "src/sksl/ir/SkSLModifierFlags.h"
     30 #include "src/sksl/ir/SkSLModifiersDeclaration.h"
     31 #include "src/sksl/ir/SkSLProgram.h"
     32 #include "src/sksl/ir/SkSLProgramElement.h"
     33 #include "src/sksl/ir/SkSLType.h"
     34 #include "src/sksl/ir/SkSLVarDeclarations.h"
     35 #include "src/sksl/ir/SkSLVariable.h"
     36 
     37 #include <cstddef>
     38 #include <cstdint>
     39 #include <memory>
     40 #include <string>
     41 #include <vector>
     42 
     43 using namespace skia_private;
     44 
     45 namespace SkSL {
     46 namespace {
     47 
     48 class FinalizationVisitor : public ProgramVisitor {
     49 public:
     50    FinalizationVisitor(const Context& c, const ProgramUsage& u) : fContext(c), fUsage(u) {}
     51 
     52    bool visitProgramElement(const ProgramElement& pe) override {
     53        switch (pe.kind()) {
     54            case ProgramElement::Kind::kGlobalVar:
     55                this->checkGlobalVariableSizeLimit(pe.as<GlobalVarDeclaration>());
     56                break;
     57            case ProgramElement::Kind::kInterfaceBlock:
     58                // TODO(skbug.com/40044753): Enforce duplicate checks universally. This is currently not
     59                // possible without changes to the binding index assignment logic in graphite.
     60                this->checkBindUniqueness(pe.as<InterfaceBlock>());
     61                break;
     62            case ProgramElement::Kind::kFunction:
     63                this->checkOutParamsAreAssigned(pe.as<FunctionDefinition>());
     64                break;
     65            case ProgramElement::Kind::kModifiers:
     66                this->checkWorkgroupLocalSize(pe.as<ModifiersDeclaration>());
     67                break;
     68            default:
     69                break;
     70        }
     71        return INHERITED::visitProgramElement(pe);
     72    }
     73 
     74    void checkGlobalVariableSizeLimit(const GlobalVarDeclaration& globalDecl) {
     75        if (!ProgramConfig::IsRuntimeEffect(fContext.fConfig->fKind)) {
     76            return;
     77        }
     78        const VarDeclaration& decl = globalDecl.varDeclaration();
     79 
     80        size_t prevSlotsUsed = fGlobalSlotsUsed;
     81        fGlobalSlotsUsed = SkSafeMath::Add(fGlobalSlotsUsed, decl.var()->type().slotCount());
     82        // To avoid overzealous error reporting, only trigger the error at the first place where the
     83        // global limit is exceeded.
     84        if (prevSlotsUsed < kVariableSlotLimit && fGlobalSlotsUsed >= kVariableSlotLimit) {
     85            fContext.fErrors->error(decl.fPosition,
     86                                    "global variable '" + std::string(decl.var()->name()) +
     87                                    "' exceeds the size limit");
     88        }
     89    }
     90 
     91    void checkBindUniqueness(const InterfaceBlock& block) {
     92        const Variable* var = block.var();
     93        int32_t set = var->layout().fSet;
     94        int32_t binding = var->layout().fBinding;
     95        if (binding != -1) {
     96            // TODO(skbug.com/40044753): This should map a `set` value of -1 to the default settings value
     97            // used by codegen backends to prevent duplicates that may arise from the effective
     98            // default set value.
     99            uint64_t key = ((uint64_t)set << 32) + binding;
    100            if (!fBindings.contains(key)) {
    101                fBindings.add(key);
    102            } else {
    103                if (set != -1) {
    104                    fContext.fErrors->error(block.fPosition,
    105                                            "layout(set=" + std::to_string(set) +
    106                                                    ", binding=" + std::to_string(binding) +
    107                                                    ") has already been defined");
    108                } else {
    109                    fContext.fErrors->error(block.fPosition,
    110                                            "layout(binding=" + std::to_string(binding) +
    111                                                    ") has already been defined");
    112                }
    113            }
    114        }
    115    }
    116 
    117    void checkOutParamsAreAssigned(const FunctionDefinition& funcDef) {
    118        const FunctionDeclaration& funcDecl = funcDef.declaration();
    119 
    120        // Searches for `out` parameters that are not written to. According to the GLSL spec,
    121        // the value of an out-param that's never assigned to is unspecified, so report it.
    122        for (const Variable* param : funcDecl.parameters()) {
    123            const ModifierFlags paramInout = param->modifierFlags() & (ModifierFlag::kIn |
    124                                                                       ModifierFlag::kOut);
    125            if (paramInout == ModifierFlag::kOut) {
    126                ProgramUsage::VariableCounts counts = fUsage.get(*param);
    127                if (counts.fWrite <= 0) {
    128                    fContext.fErrors->error(param->fPosition,
    129                                            "function '" + std::string(funcDecl.name()) +
    130                                            "' never assigns a value to out parameter '" +
    131                                            std::string(param->name()) + "'");
    132                }
    133            }
    134        }
    135    }
    136 
    137    void checkWorkgroupLocalSize(const ModifiersDeclaration& d) {
    138        if (d.layout().fLocalSizeX >= 0) {
    139            if (fLocalSizeX >= 0) {
    140                fContext.fErrors->error(d.fPosition, "'local_size_x' was specified more than once");
    141            } else {
    142                fLocalSizeX = d.layout().fLocalSizeX;
    143            }
    144        }
    145        if (d.layout().fLocalSizeY >= 0) {
    146            if (fLocalSizeY >= 0) {
    147                fContext.fErrors->error(d.fPosition, "'local_size_y' was specified more than once");
    148            } else {
    149                fLocalSizeY = d.layout().fLocalSizeY;
    150            }
    151        }
    152        if (d.layout().fLocalSizeZ >= 0) {
    153            if (fLocalSizeZ >= 0) {
    154                fContext.fErrors->error(d.fPosition, "'local_size_z' was specified more than once");
    155            } else {
    156                fLocalSizeZ = d.layout().fLocalSizeZ;
    157            }
    158        }
    159    }
    160 
    161    bool visitExpression(const Expression& expr) override {
    162        switch (expr.kind()) {
    163            case Expression::Kind::kFunctionCall: {
    164                const FunctionDeclaration& decl = expr.as<FunctionCall>().function();
    165                if (!decl.isBuiltin() && !decl.definition()) {
    166                    fContext.fErrors->error(expr.fPosition, "function '" + decl.description() +
    167                                                            "' is not defined");
    168                }
    169                break;
    170            }
    171            case Expression::Kind::kFunctionReference:
    172            case Expression::Kind::kMethodReference:
    173            case Expression::Kind::kTypeReference:
    174                SkDEBUGFAIL("invalid reference-expr, should have been reported by coerce()");
    175                fContext.fErrors->error(expr.fPosition, "invalid expression");
    176                break;
    177            default:
    178                if (expr.type().matches(*fContext.fTypes.fInvalid)) {
    179                    fContext.fErrors->error(expr.fPosition, "invalid expression");
    180                }
    181                break;
    182        }
    183        return INHERITED::visitExpression(expr);
    184    }
    185 
    186    bool definesLocalSize() const {
    187        return fLocalSizeX >= 0 || fLocalSizeY >= 0 || fLocalSizeZ >= 0;
    188    }
    189 
    190 private:
    191    using INHERITED = ProgramVisitor;
    192    size_t fGlobalSlotsUsed = 0;
    193    const Context& fContext;
    194    const ProgramUsage& fUsage;
    195    // we pack the set/binding pair into a single 64 bit int
    196    THashSet<uint64_t> fBindings;
    197 
    198    // Compute programs must at least specify the X dimension of the local size. The other
    199    // dimensions have a default value of "1".
    200    int fLocalSizeX = -1;
    201    int fLocalSizeY = -1;
    202    int fLocalSizeZ = -1;
    203 };
    204 
    205 }  // namespace
    206 
    207 void Analysis::DoFinalizationChecks(const Program& program) {
    208    // Check all of the program's owned elements. (Built-in elements are assumed to be valid.)
    209    FinalizationVisitor visitor{*program.fContext, *program.usage()};
    210    for (const std::unique_ptr<ProgramElement>& element : program.fOwnedElements) {
    211        visitor.visitProgramElement(*element);
    212    }
    213    if (ProgramConfig::IsCompute(program.fConfig->fKind) && !visitor.definesLocalSize()) {
    214        program.fContext->fErrors->error(Position(),
    215                                         "compute programs must specify a workgroup size");
    216    }
    217 }
    218 
    219 }  // namespace SkSL