1/* 2 * Copyright 2016 Google Inc. 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 "src/sksl/SkSLCompiler.h" 9 10#include <memory> 11#include <unordered_set> 12 13#include "include/sksl/DSLCore.h" 14#include "src/core/SkTraceEvent.h" 15#include "src/sksl/SkSLConstantFolder.h" 16#include "src/sksl/SkSLDSLParser.h" 17#include "src/sksl/SkSLIntrinsicMap.h" 18#include "src/sksl/SkSLOperators.h" 19#include "src/sksl/SkSLProgramSettings.h" 20#include "src/sksl/SkSLRehydrator.h" 21#include "src/sksl/SkSLThreadContext.h" 22#include "src/sksl/codegen/SkSLGLSLCodeGenerator.h" 23#include "src/sksl/codegen/SkSLMetalCodeGenerator.h" 24#include "src/sksl/codegen/SkSLSPIRVCodeGenerator.h" 25#include "src/sksl/codegen/SkSLSPIRVtoHLSL.h" 26#include "src/sksl/dsl/priv/DSLWriter.h" 27#include "src/sksl/dsl/priv/DSL_priv.h" 28#include "src/sksl/ir/SkSLExpression.h" 29#include "src/sksl/ir/SkSLExpressionStatement.h" 30#include "src/sksl/ir/SkSLExternalFunctionReference.h" 31#include "src/sksl/ir/SkSLField.h" 32#include "src/sksl/ir/SkSLFieldAccess.h" 33#include "src/sksl/ir/SkSLFunctionCall.h" 34#include "src/sksl/ir/SkSLFunctionDefinition.h" 35#include "src/sksl/ir/SkSLFunctionReference.h" 36#include "src/sksl/ir/SkSLInterfaceBlock.h" 37#include "src/sksl/ir/SkSLLiteral.h" 38#include "src/sksl/ir/SkSLModifiersDeclaration.h" 39#include "src/sksl/ir/SkSLNop.h" 40#include "src/sksl/ir/SkSLSymbolTable.h" 41#include "src/sksl/ir/SkSLTernaryExpression.h" 42#include "src/sksl/ir/SkSLTypeReference.h" 43#include "src/sksl/ir/SkSLUnresolvedFunction.h" 44#include "src/sksl/ir/SkSLVarDeclarations.h" 45#include "src/sksl/transform/SkSLProgramWriter.h" 46#include "src/sksl/transform/SkSLTransform.h" 47#include "src/utils/SkBitSet.h" 48 49#include <fstream> 50 51#if !defined(SKSL_STANDALONE) & SK_SUPPORT_GPU 52#include "include/gpu/GrContextOptions.h" 53#include "src/gpu/GrShaderCaps.h" 54#endif 55 56#ifdef SK_ENABLE_SPIRV_VALIDATION 57#include "spirv-tools/libspirv.hpp" 58#endif 59 60#if defined(SKSL_STANDALONE) 61 62// In standalone mode, we load the textual sksl source files. GN generates or copies these files 63// to the skslc executable directory. The "data" in this mode is just the filename. 64#define MODULE_DATA(name) MakeModulePath("sksl_" #name ".sksl") 65 66#else 67 68// At runtime, we load the dehydrated sksl data files. The data is a (pointer, size) pair. 69#include "src/sksl/generated/sksl_frag.dehydrated.sksl" 70#include "src/sksl/generated/sksl_gpu.dehydrated.sksl" 71#include "src/sksl/generated/sksl_public.dehydrated.sksl" 72#include "src/sksl/generated/sksl_rt_shader.dehydrated.sksl" 73#include "src/sksl/generated/sksl_vert.dehydrated.sksl" 74 75#define MODULE_DATA(name) MakeModuleData(SKSL_INCLUDE_sksl_##name,\ 76 SKSL_INCLUDE_sksl_##name##_LENGTH) 77 78#endif 79 80namespace SkSL { 81 82// These flags allow tools like Viewer or Nanobench to override the compiler's ProgramSettings. 83Compiler::OverrideFlag Compiler::sOptimizer = OverrideFlag::kDefault; 84Compiler::OverrideFlag Compiler::sInliner = OverrideFlag::kDefault; 85 86using RefKind = VariableReference::RefKind; 87 88class AutoSource { 89public: 90 AutoSource(Compiler* compiler, const char* source) 91 : fCompiler(compiler) { 92 SkASSERT(!fCompiler->errorReporter().source()); 93 fCompiler->errorReporter().setSource(source); 94 } 95 96 ~AutoSource() { 97 fCompiler->errorReporter().setSource(nullptr); 98 } 99 100 Compiler* fCompiler; 101}; 102 103class AutoProgramConfig { 104public: 105 AutoProgramConfig(std::shared_ptr<Context>& context, ProgramConfig* config) 106 : fContext(context.get()) 107 , fOldConfig(fContext->fConfig) { 108 fContext->fConfig = config; 109 } 110 111 ~AutoProgramConfig() { 112 fContext->fConfig = fOldConfig; 113 } 114 115 Context* fContext; 116 ProgramConfig* fOldConfig; 117}; 118 119class AutoModifiersPool { 120public: 121 AutoModifiersPool(std::shared_ptr<Context>& context, ModifiersPool* modifiersPool) 122 : fContext(context.get()) { 123 SkASSERT(!fContext->fModifiersPool); 124 fContext->fModifiersPool = modifiersPool; 125 } 126 127 ~AutoModifiersPool() { 128 fContext->fModifiersPool = nullptr; 129 } 130 131 Context* fContext; 132}; 133 134Compiler::Compiler(const ShaderCapsClass* caps) 135 : fErrorReporter(this) 136 , fContext(std::make_shared<Context>(fErrorReporter, *caps, fMangler)) 137 , fInliner(fContext.get()) { 138 SkASSERT(caps); 139 fRootModule.fSymbols = this->makeRootSymbolTable(); 140 fPrivateModule.fSymbols = this->makePrivateSymbolTable(fRootModule.fSymbols); 141} 142 143Compiler::~Compiler() {} 144 145#define TYPE(t) &BuiltinTypes::f ## t 146 147using BuiltinTypePtr = const std::unique_ptr<Type> BuiltinTypes::*; 148 149inline static constexpr BuiltinTypePtr kRootTypes[] = { 150 TYPE(Void), 151 152 TYPE( Float), TYPE( Float2), TYPE( Float3), TYPE( Float4), 153 TYPE( Half), TYPE( Half2), TYPE( Half3), TYPE( Half4), 154 TYPE( Int), TYPE( Int2), TYPE( Int3), TYPE( Int4), 155 TYPE( UInt), TYPE( UInt2), TYPE( UInt3), TYPE( UInt4), 156 TYPE( Short), TYPE( Short2), TYPE( Short3), TYPE( Short4), 157 TYPE(UShort), TYPE(UShort2), TYPE(UShort3), TYPE(UShort4), 158 TYPE( Bool), TYPE( Bool2), TYPE( Bool3), TYPE( Bool4), 159 160 TYPE(Float2x2), TYPE(Float2x3), TYPE(Float2x4), 161 TYPE(Float3x2), TYPE(Float3x3), TYPE(Float3x4), 162 TYPE(Float4x2), TYPE(Float4x3), TYPE(Float4x4), 163 164 TYPE(Half2x2), TYPE(Half2x3), TYPE(Half2x4), 165 TYPE(Half3x2), TYPE(Half3x3), TYPE(Half3x4), 166 TYPE(Half4x2), TYPE(Half4x3), TYPE(Half4x4), 167 168 TYPE(SquareMat), TYPE(SquareHMat), 169 TYPE(Mat), TYPE(HMat), 170 171 // TODO(skia:12349): generic short/ushort 172 TYPE(GenType), TYPE(GenIType), TYPE(GenUType), 173 TYPE(GenHType), /* (GenSType) (GenUSType) */ 174 TYPE(GenBType), 175 176 TYPE(Vec), TYPE(IVec), TYPE(UVec), 177 TYPE(HVec), TYPE(SVec), TYPE(USVec), 178 TYPE(BVec), 179 180 TYPE(ColorFilter), 181 TYPE(Shader), 182 TYPE(Blender), 183}; 184 185inline static constexpr BuiltinTypePtr kPrivateTypes[] = { 186 TYPE(Sampler1D), TYPE(Sampler2D), TYPE(Sampler3D), 187 TYPE(SamplerExternalOES), 188 TYPE(Sampler2DRect), 189 190 TYPE(ISampler2D), 191 TYPE(SubpassInput), TYPE(SubpassInputMS), 192 193 TYPE(Sampler), 194 TYPE(Texture2D), 195}; 196 197#undef TYPE 198 199std::shared_ptr<SymbolTable> Compiler::makeRootSymbolTable() { 200 auto rootSymbolTable = std::make_shared<SymbolTable>(*fContext, /*builtin=*/true); 201 202 for (BuiltinTypePtr rootType : kRootTypes) { 203 rootSymbolTable->addWithoutOwnership((fContext->fTypes.*rootType).get()); 204 } 205 206 return rootSymbolTable; 207} 208 209std::shared_ptr<SymbolTable> Compiler::makePrivateSymbolTable(std::shared_ptr<SymbolTable> parent) { 210 auto privateSymbolTable = std::make_shared<SymbolTable>(parent, /*builtin=*/true); 211 212 for (BuiltinTypePtr privateType : kPrivateTypes) { 213 privateSymbolTable->addWithoutOwnership((fContext->fTypes.*privateType).get()); 214 } 215 216 // sk_Caps is "builtin", but all references to it are resolved to Settings, so we don't need to 217 // treat it as builtin (ie, no need to clone it into the Program). 218 privateSymbolTable->add(std::make_unique<Variable>(/*line=*/-1, 219 fCoreModifiers.add(Modifiers{}), 220 "sk_Caps", 221 fContext->fTypes.fSkCaps.get(), 222 /*builtin=*/false, 223 Variable::Storage::kGlobal)); 224 return privateSymbolTable; 225} 226 227const ParsedModule& Compiler::loadGPUModule() { 228 if (!fGPUModule.fSymbols) { 229 fGPUModule = this->parseModule(ProgramKind::kFragment, MODULE_DATA(gpu), fPrivateModule); 230 } 231 return fGPUModule; 232} 233 234const ParsedModule& Compiler::loadFragmentModule() { 235 if (!fFragmentModule.fSymbols) { 236 fFragmentModule = this->parseModule(ProgramKind::kFragment, MODULE_DATA(frag), 237 this->loadGPUModule()); 238 } 239 return fFragmentModule; 240} 241 242const ParsedModule& Compiler::loadVertexModule() { 243 if (!fVertexModule.fSymbols) { 244 fVertexModule = this->parseModule(ProgramKind::kVertex, MODULE_DATA(vert), 245 this->loadGPUModule()); 246 } 247 return fVertexModule; 248} 249 250static void add_glsl_type_aliases(SkSL::SymbolTable* symbols, const SkSL::BuiltinTypes& types) { 251 // Add some aliases to the runtime effect modules so that it's friendlier, and more like GLSL. 252 symbols->addAlias("vec2", types.fFloat2.get()); 253 symbols->addAlias("vec3", types.fFloat3.get()); 254 symbols->addAlias("vec4", types.fFloat4.get()); 255 256 symbols->addAlias("ivec2", types.fInt2.get()); 257 symbols->addAlias("ivec3", types.fInt3.get()); 258 symbols->addAlias("ivec4", types.fInt4.get()); 259 260 symbols->addAlias("bvec2", types.fBool2.get()); 261 symbols->addAlias("bvec3", types.fBool3.get()); 262 symbols->addAlias("bvec4", types.fBool4.get()); 263 264 symbols->addAlias("mat2", types.fFloat2x2.get()); 265 symbols->addAlias("mat3", types.fFloat3x3.get()); 266 symbols->addAlias("mat4", types.fFloat4x4.get()); 267 268 // Alias every private type to "invalid". This will prevent code from using built-in names like 269 // `sampler2D` as variable names. 270 for (BuiltinTypePtr privateType : kPrivateTypes) { 271 symbols->addAlias((types.*privateType)->name(), types.fInvalid.get()); 272 } 273} 274 275const ParsedModule& Compiler::loadPublicModule() { 276 if (!fPublicModule.fSymbols) { 277 fPublicModule = this->parseModule(ProgramKind::kGeneric, MODULE_DATA(public), fRootModule); 278 add_glsl_type_aliases(fPublicModule.fSymbols.get(), fContext->fTypes); 279 } 280 return fPublicModule; 281} 282 283const ParsedModule& Compiler::loadRuntimeShaderModule() { 284 if (!fRuntimeShaderModule.fSymbols) { 285 fRuntimeShaderModule = this->parseModule( 286 ProgramKind::kRuntimeShader, MODULE_DATA(rt_shader), this->loadPublicModule()); 287 } 288 return fRuntimeShaderModule; 289} 290 291const ParsedModule& Compiler::moduleForProgramKind(ProgramKind kind) { 292 switch (kind) { 293 case ProgramKind::kVertex: return this->loadVertexModule(); break; 294 case ProgramKind::kFragment: return this->loadFragmentModule(); break; 295 case ProgramKind::kRuntimeColorFilter: return this->loadPublicModule(); break; 296 case ProgramKind::kRuntimeShader: return this->loadRuntimeShaderModule(); break; 297 case ProgramKind::kRuntimeBlender: return this->loadPublicModule(); break; 298 case ProgramKind::kGeneric: return this->loadPublicModule(); break; 299 } 300 SkUNREACHABLE; 301} 302 303LoadedModule Compiler::loadModule(ProgramKind kind, 304 ModuleData data, 305 std::shared_ptr<SymbolTable> base, 306 bool dehydrate) { 307 if (dehydrate) { 308 // NOTE: This is a workaround. When dehydrating includes, skslc doesn't know which module 309 // it's preparing, nor what the correct base module is. We can't use 'Root', because many 310 // GPU intrinsics reference private types, like samplers or textures. Today, 'Private' does 311 // contain the union of all known types, so this is safe. If we ever have types that only 312 // exist in 'Public' (for example), this logic needs to be smarter (by choosing the correct 313 // base for the module we're compiling). 314 base = fPrivateModule.fSymbols; 315 } 316 SkASSERT(base); 317 318 // Put the core-module modifier pool into the context. 319 AutoModifiersPool autoPool(fContext, &fCoreModifiers); 320 321 // Built-in modules always use default program settings. 322 Program::Settings settings; 323 settings.fReplaceSettings = !dehydrate; 324 325#if defined(SKSL_STANDALONE) 326 SkASSERT(this->errorCount() == 0); 327 SkASSERT(data.fPath); 328 std::ifstream in(data.fPath); 329 String text{std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>()}; 330 if (in.rdstate()) { 331 printf("error reading %s\n", data.fPath); 332 abort(); 333 } 334 ParsedModule baseModule = {base, /*fIntrinsics=*/nullptr}; 335 LoadedModule result = DSLParser(this, settings, kind, 336 std::move(text)).moduleInheritingFrom(std::move(baseModule)); 337 if (this->errorCount()) { 338 printf("Unexpected errors: %s\n", this->fErrorText.c_str()); 339 SkDEBUGFAILF("%s %s\n", data.fPath, this->fErrorText.c_str()); 340 } 341#else 342 ProgramConfig config; 343 config.fIsBuiltinCode = true; 344 config.fKind = kind; 345 config.fSettings = settings; 346 AutoProgramConfig autoConfig(fContext, &config); 347 SkASSERT(data.fData && (data.fSize != 0)); 348 Rehydrator rehydrator(fContext.get(), base, data.fData, data.fSize); 349 LoadedModule result = { kind, rehydrator.symbolTable(), rehydrator.elements() }; 350#endif 351 352 return result; 353} 354 355ParsedModule Compiler::parseModule(ProgramKind kind, ModuleData data, const ParsedModule& base) { 356 LoadedModule module = this->loadModule(kind, data, base.fSymbols, /*dehydrate=*/false); 357 this->optimize(module); 358 359 // For modules that just declare (but don't define) intrinsic functions, there will be no new 360 // program elements. In that case, we can share our parent's intrinsic map: 361 if (module.fElements.empty()) { 362 return ParsedModule{module.fSymbols, base.fIntrinsics}; 363 } 364 365 auto intrinsics = std::make_shared<IntrinsicMap>(base.fIntrinsics.get()); 366 367 // Now, transfer all of the program elements to an intrinsic map. This maps certain types of 368 // global objects to the declaring ProgramElement. 369 for (std::unique_ptr<ProgramElement>& element : module.fElements) { 370 switch (element->kind()) { 371 case ProgramElement::Kind::kFunction: { 372 const FunctionDefinition& f = element->as<FunctionDefinition>(); 373 SkASSERT(f.declaration().isBuiltin()); 374 intrinsics->insertOrDie(f.declaration().description(), std::move(element)); 375 break; 376 } 377 case ProgramElement::Kind::kFunctionPrototype: { 378 // These are already in the symbol table. 379 break; 380 } 381 case ProgramElement::Kind::kGlobalVar: { 382 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>(); 383 const Variable& var = global.declaration()->as<VarDeclaration>().var(); 384 SkASSERT(var.isBuiltin()); 385 intrinsics->insertOrDie(String(var.name()), std::move(element)); 386 break; 387 } 388 case ProgramElement::Kind::kInterfaceBlock: { 389 const Variable& var = element->as<InterfaceBlock>().variable(); 390 SkASSERT(var.isBuiltin()); 391 intrinsics->insertOrDie(String(var.name()), std::move(element)); 392 break; 393 } 394 default: 395 printf("Unsupported element: %s\n", element->description().c_str()); 396 SkASSERT(false); 397 break; 398 } 399 } 400 401 return ParsedModule{module.fSymbols, std::move(intrinsics)}; 402} 403 404std::unique_ptr<Program> Compiler::convertProgram(ProgramKind kind, 405 String text, 406 Program::Settings settings) { 407 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::convertProgram"); 408 409 SkASSERT(!settings.fExternalFunctions || (kind == ProgramKind::kGeneric)); 410 411 // Honor our optimization-override flags. 412 switch (sOptimizer) { 413 case OverrideFlag::kDefault: 414 break; 415 case OverrideFlag::kOff: 416 settings.fOptimize = false; 417 break; 418 case OverrideFlag::kOn: 419 settings.fOptimize = true; 420 break; 421 } 422 423 switch (sInliner) { 424 case OverrideFlag::kDefault: 425 break; 426 case OverrideFlag::kOff: 427 settings.fInlineThreshold = 0; 428 break; 429 case OverrideFlag::kOn: 430 if (settings.fInlineThreshold == 0) { 431 settings.fInlineThreshold = kDefaultInlineThreshold; 432 } 433 break; 434 } 435 436 // Disable optimization settings that depend on a parent setting which has been disabled. 437 settings.fInlineThreshold *= (int)settings.fOptimize; 438 settings.fRemoveDeadFunctions &= settings.fOptimize; 439 settings.fRemoveDeadVariables &= settings.fOptimize; 440 441 // Runtime effects always allow narrowing conversions. 442 if (ProgramConfig::IsRuntimeEffect(kind)) { 443 settings.fAllowNarrowingConversions = true; 444 } 445 446 this->resetErrors(); 447 fInliner.reset(); 448 449 settings.fDSLMangling = false; 450 return DSLParser(this, settings, kind, std::move(text)).program(); 451} 452 453std::unique_ptr<Expression> Compiler::convertIdentifier(int line, skstd::string_view name) { 454 const Symbol* result = (*fSymbolTable)[name]; 455 if (!result) { 456 this->errorReporter().error(line, "unknown identifier '" + name + "'"); 457 return nullptr; 458 } 459 switch (result->kind()) { 460 case Symbol::Kind::kFunctionDeclaration: { 461 std::vector<const FunctionDeclaration*> f = { 462 &result->as<FunctionDeclaration>() 463 }; 464 return std::make_unique<FunctionReference>(*fContext, line, f); 465 } 466 case Symbol::Kind::kUnresolvedFunction: { 467 const UnresolvedFunction* f = &result->as<UnresolvedFunction>(); 468 return std::make_unique<FunctionReference>(*fContext, line, f->functions()); 469 } 470 case Symbol::Kind::kVariable: { 471 const Variable* var = &result->as<Variable>(); 472 const Modifiers& modifiers = var->modifiers(); 473 switch (modifiers.fLayout.fBuiltin) { 474 case SK_FRAGCOORD_BUILTIN: 475 if (fContext->fCaps.canUseFragCoord()) { 476 ThreadContext::Inputs().fUseFlipRTUniform = true; 477 } 478 break; 479 case SK_CLOCKWISE_BUILTIN: 480 ThreadContext::Inputs().fUseFlipRTUniform = true; 481 break; 482 } 483 // default to kRead_RefKind; this will be corrected later if the variable is written to 484 return VariableReference::Make(line, var, VariableReference::RefKind::kRead); 485 } 486 case Symbol::Kind::kField: { 487 const Field* field = &result->as<Field>(); 488 auto base = VariableReference::Make(line, &field->owner(), 489 VariableReference::RefKind::kRead); 490 return FieldAccess::Make(*fContext, std::move(base), field->fieldIndex(), 491 FieldAccess::OwnerKind::kAnonymousInterfaceBlock); 492 } 493 case Symbol::Kind::kType: { 494 return TypeReference::Convert(*fContext, line, &result->as<Type>()); 495 } 496 case Symbol::Kind::kExternal: { 497 const ExternalFunction* r = &result->as<ExternalFunction>(); 498 return std::make_unique<ExternalFunctionReference>(line, r); 499 } 500 default: 501 SK_ABORT("unsupported symbol type %d\n", (int) result->kind()); 502 } 503} 504 505bool Compiler::optimize(LoadedModule& module) { 506 SkASSERT(!this->errorCount()); 507 508 // Create a temporary program configuration with default settings. 509 ProgramConfig config; 510 config.fIsBuiltinCode = true; 511 config.fKind = module.fKind; 512 AutoProgramConfig autoConfig(fContext, &config); 513 AutoModifiersPool autoPool(fContext, &fCoreModifiers); 514 515 // Reset the Inliner. 516 fInliner.reset(); 517 518 std::unique_ptr<ProgramUsage> usage = Analysis::GetUsage(module); 519 520 while (this->errorCount() == 0) { 521 // Perform inline-candidate analysis and inline any functions deemed suitable. 522 if (!this->runInliner(module.fElements, module.fSymbols, usage.get())) { 523 break; 524 } 525 } 526 return this->errorCount() == 0; 527} 528 529bool Compiler::optimize(Program& program) { 530 // The optimizer only needs to run when it is enabled. 531 if (!program.fConfig->fSettings.fOptimize) { 532 return true; 533 } 534 535 SkASSERT(!this->errorCount()); 536 ProgramUsage* usage = program.fUsage.get(); 537 538 if (this->errorCount() == 0) { 539 // Run the inliner only once; it is expensive! Multiple passes can occasionally shake out 540 // more wins, but it's diminishing returns. 541 this->runInliner(program.fOwnedElements, program.fSymbols, usage); 542 543 // Unreachable code can confuse some drivers, so it's worth removing. (skia:12012) 544 Transform::EliminateUnreachableCode(program, usage); 545 546 while (Transform::EliminateDeadFunctions(program, usage)) { 547 // Removing dead functions may cause more functions to become unreferenced. Try again. 548 } 549 while (Transform::EliminateDeadLocalVariables(program, usage)) { 550 // Removing dead variables may cause more variables to become unreferenced. Try again. 551 } 552 553 Transform::EliminateDeadGlobalVariables(program, usage); 554 } 555 556 return this->errorCount() == 0; 557} 558 559bool Compiler::runInliner(const std::vector<std::unique_ptr<ProgramElement>>& elements, 560 std::shared_ptr<SymbolTable> symbols, 561 ProgramUsage* usage) { 562 // The program's SymbolTable was taken out of fSymbolTable when the program was bundled, but 563 // the inliner relies (indirectly) on having a valid SymbolTable. 564 // In particular, inlining can turn a non-optimizable expression like `normalize(myVec)` into 565 // `normalize(vec2(7))`, which is now optimizable. The optimizer can use DSL to simplify this 566 // expression--e.g., in the case of normalize, using DSL's Length(). The DSL relies on 567 // convertIdentifier() to look up `length`. convertIdentifier() needs a valid symbol table to 568 // find the declaration of `length`. To allow this chain of events to succeed, we re-insert the 569 // program's symbol table temporarily. 570 SkASSERT(!fSymbolTable); 571 fSymbolTable = symbols; 572 573 bool result = fInliner.analyze(elements, symbols, usage); 574 575 fSymbolTable = nullptr; 576 return result; 577} 578 579bool Compiler::finalize(Program& program) { 580 // Do a pass looking for @if/@switch statements that didn't optimize away, or dangling 581 // FunctionReference or TypeReference expressions. Report these as errors. 582 Analysis::VerifyStaticTestsAndExpressions(program); 583 584 // Verify that the program conforms to ES2 limitations. 585 if (fContext->fConfig->strictES2Mode() && this->errorCount() == 0) { 586 // Enforce Appendix A, Section 5 of the GLSL ES 1.00 spec -- Indexing. This logic assumes 587 // that all loops meet the criteria of Section 4, and if they don't, could crash. 588 for (const auto& pe : program.fOwnedElements) { 589 Analysis::ValidateIndexingForES2(*pe, this->errorReporter()); 590 } 591 // Verify that the program size is reasonable after unrolling and inlining. This also 592 // issues errors for static recursion and overly-deep function-call chains. 593 Analysis::CheckProgramUnrolledSize(program); 594 } 595 596 return this->errorCount() == 0; 597} 598 599#if defined(SKSL_STANDALONE) || SK_SUPPORT_GPU 600 601bool Compiler::toSPIRV(Program& program, OutputStream& out) { 602 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toSPIRV"); 603 AutoSource as(this, program.fSource->c_str()); 604 ProgramSettings settings; 605 settings.fDSLUseMemoryPool = false; 606 dsl::Start(this, program.fConfig->fKind, settings); 607 dsl::SetErrorReporter(&fErrorReporter); 608 fSymbolTable = program.fSymbols; 609#ifdef SK_ENABLE_SPIRV_VALIDATION 610 StringStream buffer; 611 SPIRVCodeGenerator cg(fContext.get(), &program, &buffer); 612 bool result = cg.generateCode(); 613 if (result && program.fConfig->fSettings.fValidateSPIRV) { 614 spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0); 615 const String& data = buffer.str(); 616 SkASSERT(0 == data.size() % 4); 617 String errors; 618 auto dumpmsg = [&errors](spv_message_level_t, const char*, const spv_position_t&, 619 const char* m) { 620 errors.appendf("SPIR-V validation error: %s\n", m); 621 }; 622 tools.SetMessageConsumer(dumpmsg); 623 624 // Verify that the SPIR-V we produced is valid. At runtime, we will abort() with a message 625 // explaining the error. In standalone mode (skslc), we will send the message, plus the 626 // entire disassembled SPIR-V (for easier context & debugging) as *our* error message. 627 result = tools.Validate((const uint32_t*) data.c_str(), data.size() / 4); 628 629 if (!result) { 630#if defined(SKSL_STANDALONE) 631 // Convert the string-stream to a SPIR-V disassembly. 632 std::string disassembly; 633 if (tools.Disassemble((const uint32_t*)data.data(), data.size() / 4, &disassembly)) { 634 errors.append(disassembly); 635 } 636 this->errorReporter().error(-1, errors); 637 this->errorReporter().reportPendingErrors(PositionInfo()); 638#else 639 SkDEBUGFAILF("%s", errors.c_str()); 640#endif 641 } 642 out.write(data.c_str(), data.size()); 643 } 644#else 645 SPIRVCodeGenerator cg(fContext.get(), &program, &out); 646 bool result = cg.generateCode(); 647#endif 648 dsl::End(); 649 return result; 650} 651 652bool Compiler::toSPIRV(Program& program, String* out) { 653 StringStream buffer; 654 bool result = this->toSPIRV(program, buffer); 655 if (result) { 656 *out = buffer.str(); 657 } 658 return result; 659} 660 661bool Compiler::toGLSL(Program& program, OutputStream& out) { 662 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toGLSL"); 663 AutoSource as(this, program.fSource->c_str()); 664 GLSLCodeGenerator cg(fContext.get(), &program, &out); 665 bool result = cg.generateCode(); 666 return result; 667} 668 669bool Compiler::toGLSL(Program& program, String* out) { 670 StringStream buffer; 671 bool result = this->toGLSL(program, buffer); 672 if (result) { 673 *out = buffer.str(); 674 } 675 return result; 676} 677 678bool Compiler::toHLSL(Program& program, String* out) { 679 String spirv; 680 if (!this->toSPIRV(program, &spirv)) { 681 return false; 682 } 683 684 return SPIRVtoHLSL(spirv, out); 685} 686 687bool Compiler::toMetal(Program& program, OutputStream& out) { 688 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toMetal"); 689 AutoSource as(this, program.fSource->c_str()); 690 MetalCodeGenerator cg(fContext.get(), &program, &out); 691 bool result = cg.generateCode(); 692 return result; 693} 694 695bool Compiler::toMetal(Program& program, String* out) { 696 StringStream buffer; 697 bool result = this->toMetal(program, buffer); 698 if (result) { 699 *out = buffer.str(); 700 } 701 return result; 702} 703 704#endif // defined(SKSL_STANDALONE) || SK_SUPPORT_GPU 705 706void Compiler::handleError(skstd::string_view msg, PositionInfo pos) { 707 fErrorText += "error: " + (pos.line() >= 1 ? to_string(pos.line()) + ": " : "") + msg + "\n"; 708} 709 710String Compiler::errorText(bool showCount) { 711 if (showCount) { 712 this->writeErrorCount(); 713 } 714 String result = fErrorText; 715 this->resetErrors(); 716 return result; 717} 718 719void Compiler::writeErrorCount() { 720 int count = this->errorCount(); 721 if (count) { 722 fErrorText += to_string(count) + " error"; 723 if (count > 1) { 724 fErrorText += "s"; 725 } 726 fErrorText += "\n"; 727 } 728} 729 730} // namespace SkSL 731