| /* |
| * 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/private/SkStringView.h" |
| #include "include/sksl/SkSLErrorReporter.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 <type_traits> |
| #include <utility> |
| |
| 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<VarDeclaration>() || |
| fDeclaringElement->is<GlobalVarDeclaration>()); |
| return fDeclaringElement->is<GlobalVarDeclaration>() |
| ? &fDeclaringElement->as<GlobalVarDeclaration>().varDeclaration() |
| : &fDeclaringElement->as<VarDeclaration>(); |
| } |
| |
| GlobalVarDeclaration* Variable::globalVarDeclaration() const { |
| if (!fDeclaringElement) { |
| return nullptr; |
| } |
| SkASSERT(fDeclaringElement->is<VarDeclaration>() || |
| fDeclaringElement->is<GlobalVarDeclaration>()); |
| return fDeclaringElement->is<GlobalVarDeclaration>() |
| ? &fDeclaringElement->as<GlobalVarDeclaration>() |
| : 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> 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<Expression> 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> Variable::Make(const Context& context, |
| Position pos, |
| Position modifiersPos, |
| const Modifiers& modifiers, |
| const Type* baseType, |
| std::string_view name, |
| bool isArray, |
| std::unique_ptr<Expression> 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<InterfaceBlockVariable>(pos, |
| modifiersPos, |
| context.fModifiersPool->add(modifiers), |
| name, |
| type, |
| context.fConfig->fIsBuiltinCode, |
| storage); |
| } else { |
| return std::make_unique<Variable>(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<Expression> 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<Variable>(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 |