// These flags allow tools like Viewer or Nanobench to override the compiler's ProgramSettings.
Compiler::OverrideFlag Compiler::sOptimizer = OverrideFlag::kDefault;
Compiler::OverrideFlag Compiler::sInliner = OverrideFlag::kDefault;
const Module* Compiler::moduleForProgramKind(ProgramKind kind) { auto m = ModuleLoader::Get(); switch (kind) { case ProgramKind::kFragment: return m.loadFragmentModule(this); case ProgramKind::kVertex: return m.loadVertexModule(this); case ProgramKind::kCompute: return m.loadComputeModule(this); case ProgramKind::kGraphiteFragment: return m.loadGraphiteFragmentModule(this); case ProgramKind::kGraphiteVertex: return m.loadGraphiteVertexModule(this); case ProgramKind::kGraphiteFragmentES2: return m.loadGraphiteFragmentES2Module(this); case ProgramKind::kGraphiteVertexES2: return m.loadGraphiteVertexES2Module(this); case ProgramKind::kPrivateRuntimeBlender: case ProgramKind::kPrivateRuntimeColorFilter: case ProgramKind::kPrivateRuntimeShader: return m.loadPrivateRTShaderModule(this); case ProgramKind::kRuntimeColorFilter: case ProgramKind::kRuntimeShader: case ProgramKind::kRuntimeBlender: case ProgramKind::kMeshVertex: case ProgramKind::kMeshFragment: return m.loadPublicModule(this);
}
SkUNREACHABLE;
}
void Compiler::FinalizeSettings(ProgramSettings* settings, ProgramKind kind) { // Honor our optimization-override flags. switch (sOptimizer) { case OverrideFlag::kDefault: break; case OverrideFlag::kOff:
settings->fOptimize = false; break; case OverrideFlag::kOn:
settings->fOptimize = true; break;
}
switch (sInliner) { case OverrideFlag::kDefault: break; case OverrideFlag::kOff:
settings->fInlineThreshold = 0; break; case OverrideFlag::kOn: if (settings->fInlineThreshold == 0) {
settings->fInlineThreshold = kDefaultInlineThreshold;
} break;
}
// Disable optimization settings that depend on a parent setting which has been disabled.
settings->fInlineThreshold *= (int)settings->fOptimize;
settings->fRemoveDeadFunctions &= settings->fOptimize;
settings->fRemoveDeadVariables &= settings->fOptimize;
// Set up a clean symbol table atop the parent module's symbols.
fGlobalSymbols = std::make_unique<SymbolTable>(module->fSymbols.get(),
moduleType != ModuleType::program);
fGlobalSymbols->markModuleBoundary();
fContext->fSymbolTable = fGlobalSymbols.get();
}
void Compiler::cleanupContext() { // Clear out the fields we initialized above.
fContext->fConfig = nullptr;
fContext->fModule = nullptr;
fContext->fErrors->setSource(std::string_view());
fContext->fSymbolTable = nullptr;
fConfig = nullptr;
fGlobalSymbols = nullptr;
if (fPool) {
fPool->detachFromThread();
fPool = nullptr;
}
}
// Wrap the program source in a pointer so it is guaranteed to be stable across moves. auto sourcePtr = std::make_unique<std::string>(std::move(moduleSource));
// Compile the module from source, using default program settings (but no memory pooling).
ProgramSettings settings;
settings.fUseMemoryPool = false;
this->initializeContext(parentModule, kind, settings, *sourcePtr, moduleType);
// Wrap the program source in a pointer so it is guaranteed to be stable across moves. auto sourcePtr = std::make_unique<std::string>(std::move(programSource));
// Load the module used by this ProgramKind. const SkSL::Module* module = this->moduleForProgramKind(kind);
if (shrinkSymbols) { // Assign shorter names to symbols as long as it won't change the external meaning of the // code.
Transform::RenamePrivateSymbols(this->context(), module, usage.get(), kind);
// Replace constant variables with their literal values to save space.
Transform::ReplaceConstVarsWithLiterals(module, usage.get());
}
// Remove any unreachable code.
Transform::EliminateUnreachableCode(module, usage.get());
// We can only remove dead functions from runtime shaders, since runtime-effect helper functions // are isolated from other parts of the program. In a module, an unreferenced function is // intended to be called by the code that includes the module. if (kind == ProgramKind::kRuntimeShader) { while (Transform::EliminateDeadFunctions(this->context(), module, usage.get())) { // Removing dead functions may cause more functions to become unreferenced. Try again.
}
}
while (Transform::EliminateDeadLocalVariables(this->context(), module, usage.get())) { // Removing dead variables may cause more variables to become unreferenced. Try again.
}
// Runtime shaders are isolated from other parts of the program via name mangling, so we can // eliminate public globals if they aren't referenced. Otherwise, we only eliminate private // globals (prefixed with `$`) to avoid changing the meaning of the module code. bool onlyPrivateGlobals = !ProgramConfig::IsRuntimeEffect(kind); while (Transform::EliminateDeadGlobalVariables(this->context(), module, usage.get(),
onlyPrivateGlobals)) { // Repeat until no changes occur.
}
// We eliminate empty statements to avoid runs of `;;;;;;` caused by the previous passes.
SkSL::Transform::EliminateEmptyStatements(module);
// We can eliminate `{}` around single-statement blocks.
SkSL::Transform::EliminateUnnecessaryBraces(this->context(), module);
// We can convert `float4(myFloat)` with `myFloat.xxxx` to save a few characters.
SkSL::Transform::ReplaceSplatCastsWithSwizzles(this->context(), module);
// Make sure that program usage is still correct after the optimization pass is complete.
SkASSERT(*usage == *Analysis::GetUsage(module));
// Perform inline-candidate analysis and inline any functions deemed suitable.
Inliner inliner(fContext.get()); while (this->errorCount() == 0) { if (!this->runInliner(&inliner, module.fElements, module.fSymbols.get(), usage.get())) { break;
}
} // Make sure that program usage is still correct after the optimization pass is complete.
SkASSERT(*usage == *Analysis::GetUsage(module)); #endif
return this->errorCount() == 0;
}
bool Compiler::optimize(Program& program) { // The optimizer only needs to run when it is enabled. if (!program.fConfig->fSettings.fOptimize) { returntrue;
}
SkASSERT(!this->errorCount()); if (this->errorCount() == 0) { #ifndef SK_ENABLE_OPTIMIZE_SIZE // Run the inliner only once; it is expensive! Multiple passes can occasionally shake out // more wins, but it's diminishing returns.
Inliner inliner(fContext.get());
this->runInliner(&inliner, program.fOwnedElements, program.fSymbols.get(),
program.fUsage.get()); #endif
// Unreachable code can confuse some drivers, so it's worth removing. (skia:12012)
Transform::EliminateUnreachableCode(program);
while (Transform::EliminateDeadFunctions(program)) { // Removing dead functions may cause more functions to become unreferenced. Try again.
} while (Transform::EliminateDeadLocalVariables(program)) { // Removing dead variables may cause more variables to become unreferenced. Try again.
} while (Transform::EliminateDeadGlobalVariables(program)) { // Repeat until no changes occur.
} // Make sure that program usage is still correct after the optimization pass is complete.
SkASSERT(*program.usage() == *Analysis::GetUsage(program));
// Make sure that variables are still declared in the correct symbol tables.
SkDEBUGCODE(Analysis::CheckSymbolTableCorrectness(program));
}
bool Compiler::runInliner(Inliner* inliner, const std::vector<std::unique_ptr<ProgramElement>>& elements,
SymbolTable* symbols,
ProgramUsage* usage) { #ifdef SK_ENABLE_OPTIMIZE_SIZE returntrue; #else // The program's SymbolTable was taken out of the context when the program was bundled, but // the inliner creates IR objects which may expect the context to hold a valid SymbolTable.
SkASSERT(!fContext->fSymbolTable);
fContext->fSymbolTable = symbols;
bool result = inliner->analyze(elements, symbols, usage);
bool Compiler::finalize(Program& program) { // Copy all referenced built-in functions into the Program.
Transform::FindAndDeclareBuiltinFunctions(program);
// Variables defined in modules need their declaring elements added to the program.
Transform::FindAndDeclareBuiltinVariables(program);
// Structs from module code need to be added to the program's shared elements.
Transform::FindAndDeclareBuiltinStructs(program);
// Do one last correctness-check pass. This looks for dangling FunctionReference/TypeReference // expressions, and reports them as errors.
Analysis::DoFinalizationChecks(program);
if (fContext->fConfig->strictES2Mode() && this->errorCount() == 0) { // Enforce Appendix A, Section 5 of the GLSL ES 1.00 spec -- Indexing. This logic assumes // that all loops meet the criteria of Section 4, and if they don't, could crash. for (constauto& pe : program.fOwnedElements) {
Analysis::ValidateIndexingForES2(*pe, this->errorReporter());
}
} if (this->errorCount() == 0) {
Analysis::CheckProgramStructure(program);
// Make sure that variables are declared in the symbol tables that immediately enclose them.
SkDEBUGCODE(Analysis::CheckSymbolTableCorrectness(program));
}
// Make sure that program usage is still correct after finalization is complete.
SkASSERT(*program.usage() == *Analysis::GetUsage(program));
return this->errorCount() == 0;
}
void Compiler::handleError(std::string_view msg, Position pos) {
fErrorText += "error: "; bool printLocation = false;
std::string_view src = this->errorReporter().source(); int line = -1; if (pos.valid()) {
line = pos.line(src);
printLocation = pos.startOffset() < (int)src.length();
fErrorText += std::to_string(line) + ": ";
}
fErrorText += std::string(msg) + "\n"; if (printLocation) { constint kMaxSurroundingChars = 100;
// Find the beginning of the line. int lineStart = pos.startOffset(); while (lineStart > 0) { if (src[lineStart - 1] == '\n') { break;
}
--lineStart;
}
// We don't want to show more than 100 characters surrounding the error, so push the line // start forward and add a leading ellipsis if there would be more than this.
std::string lineText;
std::string caretText; if ((pos.startOffset() - lineStart) > kMaxSurroundingChars) {
lineStart = pos.startOffset() - kMaxSurroundingChars;
lineText = "...";
caretText = " ";
}
// Echo the line. Again, we don't want to show more than 100 characters after the end of the // error, so truncate with a trailing ellipsis if needed. constchar* lineSuffix = "...\n"; int lineStop = pos.endOffset() + kMaxSurroundingChars; if (lineStop >= (int)src.length()) {
lineStop = src.length() - 1;
lineSuffix = "\n"; // no ellipsis if we reach end-of-file
} for (int i = lineStart; i < lineStop; ++i) { char c = src[i]; if (c == '\n') {
lineSuffix = "\n"; // no ellipsis if we reach end-of-line break;
} switch (c) { case'\t': lineText += " "; break; case'\0': lineText += " "; break; default: lineText += src[i]; break;
}
}
fErrorText += lineText + lineSuffix;
// print the carets underneath it, pointing to the range in question for (int i = lineStart; i < (int)src.length(); i++) { if (i >= pos.endOffset()) { break;
} switch (src[i]) { case'\t':
caretText += (i >= pos.startOffset()) ? "^^^^" : " "; break; case'\n':
SkASSERT(i >= pos.startOffset()); // use an ellipsis if the error continues past the end of the line
caretText += (pos.endOffset() > i + 1) ? "..." : "^";
i = src.length(); break; default:
caretText += (i >= pos.startOffset()) ? '^' : ' '; break;
}
}
fErrorText += caretText + '\n';
}
}
std::string Compiler::errorText(bool showCount) { if (showCount) {
this->writeErrorCount();
}
std::string result = fErrorText;
this->resetErrors(); return result;
}
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.