/* * 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/SkSLVariable.h" #include "include/private/SkSLIRNode.h" #include "include/private/SkSLLayout.h" #include "include/sksl/SkSLErrorReporter.h" #include "src/base/SkStringView.h" #include "src/sksl/SkSLCompiler.h" #include "src/sksl/SkSLContext.h" #include "src/sksl/SkSLMangler.h" #include "src/sksl/SkSLModifiersPool.h" #include "src/sksl/SkSLProgramSettings.h" #include "src/sksl/SkSLThreadContext.h" #include "src/sksl/ir/SkSLExpression.h" #include "src/sksl/ir/SkSLInterfaceBlock.h" #include "src/sksl/ir/SkSLSymbolTable.h" #include "src/sksl/ir/SkSLVarDeclarations.h" #include #include namespace SkSL { Variable::~Variable() { // Unhook this Variable from its associated VarDeclaration, since we're being deleted. if (VarDeclaration* declaration = this->varDeclaration()) { declaration->detachDeadVariable(); } } InterfaceBlockVariable::~InterfaceBlockVariable() { // Unhook this Variable from its associated InterfaceBlock, since we're being deleted. if (fInterfaceBlockElement) { fInterfaceBlockElement->detachDeadVariable(); } } const Expression* Variable::initialValue() const { VarDeclaration* declaration = this->varDeclaration(); return declaration ? declaration->value().get() : nullptr; } VarDeclaration* Variable::varDeclaration() const { if (!fDeclaringElement) { return nullptr; } SkASSERT(fDeclaringElement->is() || fDeclaringElement->is()); return fDeclaringElement->is() ? &fDeclaringElement->as().varDeclaration() : &fDeclaringElement->as(); } GlobalVarDeclaration* Variable::globalVarDeclaration() const { if (!fDeclaringElement) { return nullptr; } SkASSERT(fDeclaringElement->is() || fDeclaringElement->is()); return fDeclaringElement->is() ? &fDeclaringElement->as() : nullptr; } void Variable::setVarDeclaration(VarDeclaration* declaration) { SkASSERT(!fDeclaringElement || this == declaration->var()); if (!fDeclaringElement) { fDeclaringElement = declaration; } } void Variable::setGlobalVarDeclaration(GlobalVarDeclaration* global) { SkASSERT(!fDeclaringElement || this == global->varDeclaration().var()); fDeclaringElement = global; } std::string Variable::mangledName() const { // Only private variables need to use name mangling. std::string_view name = this->name(); if (!skstd::starts_with(name, '$')) { return std::string(name); } // The $ prefix will fail to compile in GLSL, so replace it with `sk_Priv`. name.remove_prefix(1); return "sk_Priv" + std::string(name); } std::unique_ptr Variable::Convert(const Context& context, Position pos, Position modifiersPos, const Modifiers& modifiers, const Type* baseType, Position namePos, std::string_view name, bool isArray, std::unique_ptr arraySize, Variable::Storage storage) { if (modifiers.fLayout.fLocation == 0 && modifiers.fLayout.fIndex == 0 && (modifiers.fFlags & Modifiers::kOut_Flag) && ProgramConfig::IsFragment(context.fConfig->fKind) && name != Compiler::FRAGCOLOR_NAME) { context.fErrors->error(modifiersPos, "out location=0, index=0 is reserved for sk_FragColor"); } if (baseType->isUnsizedArray() && storage != Variable::Storage::kInterfaceBlock) { context.fErrors->error(pos, "unsized arrays are not permitted here"); } if (ProgramConfig::IsCompute(ThreadContext::Context().fConfig->fKind) && modifiers.fLayout.fBuiltin == -1) { if (storage == Variable::Storage::kGlobal) { if (modifiers.fFlags & Modifiers::kIn_Flag) { context.fErrors->error(pos, "pipeline inputs not permitted in compute shaders"); } else if (modifiers.fFlags & Modifiers::kOut_Flag) { context.fErrors->error(pos, "pipeline outputs not permitted in compute shaders"); } } } return Make(context, pos, modifiersPos, modifiers, baseType, name, isArray, std::move(arraySize), storage); } std::unique_ptr Variable::Make(const Context& context, Position pos, Position modifiersPos, const Modifiers& modifiers, const Type* baseType, std::string_view name, bool isArray, std::unique_ptr arraySize, Variable::Storage storage) { const Type* type = baseType; int arraySizeValue = 0; if (isArray) { SkASSERT(arraySize); arraySizeValue = type->convertArraySize(context, pos, std::move(arraySize)); if (!arraySizeValue) { return nullptr; } type = ThreadContext::SymbolTable()->addArrayDimension(type, arraySizeValue); } if (type->componentType().isInterfaceBlock()) { return std::make_unique(pos, modifiersPos, context.fModifiersPool->add(modifiers), name, type, context.fConfig->fIsBuiltinCode, storage); } else { return std::make_unique(pos, modifiersPos, context.fModifiersPool->add(modifiers), name, type, context.fConfig->fIsBuiltinCode, storage); } } Variable::ScratchVariable Variable::MakeScratchVariable(const Context& context, Mangler& mangler, std::string_view baseName, const Type* type, const Modifiers& modifiers, SymbolTable* symbolTable, std::unique_ptr initialValue) { // $floatLiteral or $intLiteral aren't real types that we can use for scratch variables, so // replace them if they ever appear here. If this happens, we likely forgot to coerce a type // somewhere during compilation. if (type->isLiteral()) { SkDEBUGFAIL("found a $literal type in MakeScratchVariable"); type = &type->scalarTypeForLiteral(); } // Out-parameters aren't supported. SkASSERT(!(modifiers.fFlags & Modifiers::kOut_Flag)); // Provide our new variable with a unique name, and add it to our symbol table. const std::string* name = symbolTable->takeOwnershipOfString(mangler.uniqueName(baseName, symbolTable)); // Create our new variable and add it to the symbol table. ScratchVariable result; auto var = std::make_unique(initialValue ? initialValue->fPosition : Position(), /*modifiersPosition=*/Position(), context.fModifiersPool->add(Modifiers{}), name->c_str(), type, symbolTable->isBuiltin(), Variable::Storage::kLocal); // If we are creating an array type, reduce it to base type plus array-size. int arraySize = 0; if (type->isArray()) { arraySize = type->columns(); type = &type->componentType(); } // Create our variable declaration. result.fVarDecl = VarDeclaration::Make(context, var.get(), type, arraySize, std::move(initialValue)); result.fVarSymbol = symbolTable->add(std::move(var)); return result; } } // namespace SkSL