blob: 9f3be47e451763b6a0238d1b138c23443cd274d4 [file] [log] [blame]
* Copyright 2016 Google Inc.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
#include <set>
#include <unordered_set>
#include <vector>
#include "src/sksl/SkSLASTFile.h"
#include "src/sksl/SkSLAnalysis.h"
#include "src/sksl/SkSLCFGGenerator.h"
#include "src/sksl/SkSLContext.h"
#include "src/sksl/SkSLErrorReporter.h"
#include "src/sksl/SkSLInliner.h"
#include "src/sksl/SkSLLexer.h"
#include "src/sksl/ir/SkSLProgram.h"
#include "src/sksl/ir/SkSLSymbolTable.h"
#include "src/gpu/GrShaderVar.h"
#define SK_IN_BUILTIN 10002
#define SK_OUT_BUILTIN 10007
#define SK_WIDTH_BUILTIN 10011
#define SK_HEIGHT_BUILTIN 10012
class SkBitSet;
class SkSLCompileBench;
namespace SkSL {
class ByteCode;
class ExternalValue;
class IRGenerator;
class IRIntrinsicMap;
struct PipelineStageArgs;
class ProgramUsage;
struct LoadedModule {
Program::Kind fKind;
std::shared_ptr<SymbolTable> fSymbols;
std::vector<std::unique_ptr<ProgramElement>> fElements;
struct ParsedModule {
std::shared_ptr<SymbolTable> fSymbols;
std::shared_ptr<IRIntrinsicMap> fIntrinsics;
* Main compiler entry point. This is a traditional compiler design which first parses the .sksl
* file into an abstract syntax tree (a tree of ASTNodes), then performs semantic analysis to
* produce a Program (a tree of IRNodes), then feeds the Program into a CodeGenerator to produce
* compiled output.
* See the README for information about SkSL.
class SK_API Compiler : public ErrorReporter {
static constexpr const char* RTADJUST_NAME = "sk_RTAdjust";
static constexpr const char* PERVERTEX_NAME = "sk_PerVertex";
enum Flags {
kNone_Flags = 0,
// permits static if/switch statements to be used with non-constant tests. This is used when
// producing H and CPP code; the static tests don't have to have constant values *yet*, but
// the generated code will contain a static test which then does have to be a constant.
kPermitInvalidStaticTests_Flag = 1,
// An invalid (otherwise unused) character to mark where FormatArgs are inserted
static constexpr char kFormatArgPlaceholder = '\001';
static constexpr const char* kFormatArgPlaceholderStr = "\001";
struct FormatArg {
enum class Kind {
FormatArg(Kind kind)
: fKind(kind) {}
FormatArg(Kind kind, int index)
: fKind(kind)
, fIndex(index) {}
Kind fKind;
int fIndex;
String fCoords;
struct OptimizationContext {
// nodes we have already reported errors for and should not error on again
std::unordered_set<const IRNode*> fSilences;
// true if we have updated the CFG during this pass
bool fUpdated = false;
// true if we need to completely regenerate the CFG
bool fNeedsRescan = false;
// Metadata about function and variable usage within the program
ProgramUsage* fUsage = nullptr;
* Represents the arguments to GrGLSLShaderBuilder::emitFunction.
struct GLSLFunction {
GrSLType fReturnType;
SkString fName;
std::vector<GrShaderVar> fParameters;
String fBody;
std::vector<Compiler::FormatArg> fFormatArgs;
Compiler(const ShaderCapsClass* caps, Flags flags = kNone_Flags);
~Compiler() override;
Compiler(const Compiler&) = delete;
Compiler& operator=(const Compiler&) = delete;
* If externalValues is supplied, those values are registered in the symbol table of the
* Program, but ownership is *not* transferred. It is up to the caller to keep them alive.
std::unique_ptr<Program> convertProgram(
Program::Kind kind,
String text,
const Program::Settings& settings,
const std::vector<std::unique_ptr<ExternalValue>>* externalValues = nullptr);
bool toSPIRV(Program& program, OutputStream& out);
bool toSPIRV(Program& program, String* out);
bool toGLSL(Program& program, OutputStream& out);
bool toGLSL(Program& program, String* out);
bool toHLSL(Program& program, String* out);
bool toMetal(Program& program, OutputStream& out);
bool toMetal(Program& program, String* out);
bool toCPP(Program& program, String name, OutputStream& out);
bool toH(Program& program, String name, OutputStream& out);
std::unique_ptr<ByteCode> toByteCode(Program& program);
bool toPipelineStage(Program& program, PipelineStageArgs* outArgs);
void error(int offset, String msg) override;
String errorText();
void writeErrorCount();
int errorCount() override {
return fErrorCount;
Context& context() {
return *fContext;
static const char* OperatorName(Token::Kind op);
// Returns true if op is '=' or any compound assignment operator ('+=', '-=', etc.)
static bool IsAssignment(Token::Kind op);
// Given a compound assignment operator, returns the non-assignment version of the operator
// (e.g. '+=' becomes '+')
static Token::Kind RemoveAssignment(Token::Kind op);
// When SKSL_STANDALONE, fPath is used. (fData, fSize) will be (nullptr, 0)
// When !SKSL_STANDALONE, fData and fSize are used. fPath will be nullptr.
struct ModuleData {
const char* fPath;
const uint8_t* fData;
size_t fSize;
static ModuleData MakeModulePath(const char* path) {
return ModuleData{path, /*fData=*/nullptr, /*fSize=*/0};
static ModuleData MakeModuleData(const uint8_t* data, size_t size) {
return ModuleData{/*fPath=*/nullptr, data, size};
LoadedModule loadModule(Program::Kind kind, ModuleData data, std::shared_ptr<SymbolTable> base);
ParsedModule parseModule(Program::Kind kind, ModuleData data, const ParsedModule& base);
IRGenerator& irGenerator() {
return *fIRGenerator;
const ParsedModule& moduleForProgramKind(Program::Kind kind);
const ParsedModule& loadGPUModule();
const ParsedModule& loadFragmentModule();
const ParsedModule& loadVertexModule();
const ParsedModule& loadFPModule();
const ParsedModule& loadGeometryModule();
const ParsedModule& loadPublicModule();
const ParsedModule& loadInterpreterModule();
const ParsedModule& loadPipelineModule();
void addDefinition(const Expression* lvalue, std::unique_ptr<Expression>* expr,
DefinitionMap* definitions);
void addDefinitions(const BasicBlock::Node& node, DefinitionMap* definitions);
void scanCFG(CFG* cfg, BlockId block, SkBitSet* processedSet);
void computeDataFlow(CFG* cfg);
* Simplifies the expression pointed to by iter (in both the IR and CFG structures), if
* possible.
void simplifyExpression(DefinitionMap& definitions,
BasicBlock& b,
std::vector<BasicBlock::Node>::iterator* iter,
OptimizationContext* context);
* Simplifies the statement pointed to by iter (in both the IR and CFG structures), if
* possible.
void simplifyStatement(DefinitionMap& definitions,
BasicBlock& b,
std::vector<BasicBlock::Node>::iterator* iter,
OptimizationContext* context);
* Optimizes a function based on control flow analysis. Returns true if changes were made.
bool scanCFG(FunctionDefinition& f, ProgramUsage* usage);
* Optimize every function in the program.
bool optimize(Program& program);
bool optimize(LoadedModule& module);
Position position(int offset);
std::shared_ptr<Context> fContext;
const ShaderCapsClass* fCaps = nullptr;
std::shared_ptr<SymbolTable> fRootSymbolTable;
std::shared_ptr<SymbolTable> fPrivateSymbolTable;
ParsedModule fRootModule; // Core types
ParsedModule fPrivateModule; // [Root] + Internal types
ParsedModule fGPUModule; // [Private] + GPU intrinsics, helper functions
ParsedModule fVertexModule; // [GPU] + Vertex stage decls
ParsedModule fFragmentModule; // [GPU] + Fragment stage decls
ParsedModule fGeometryModule; // [GPU] + Geometry stage decls
ParsedModule fFPModule; // [GPU] + FP features
ParsedModule fPublicModule; // [Root] + Public features
ParsedModule fInterpreterModule; // [Public] + Interpreter-only decls
ParsedModule fPipelineModule; // [Public] + Runtime effect decls
// holds ModifiersPools belonging to the core includes for lifetime purposes
std::vector<std::unique_ptr<ModifiersPool>> fModifiers;
Inliner fInliner;
std::unique_ptr<IRGenerator> fIRGenerator;
int fFlags;
const String* fSource;
int fErrorCount;
String fErrorText;
friend class AutoSource;
friend class ::SkSLCompileBench;
struct PipelineStageArgs {
String fCode;
std::vector<Compiler::FormatArg> fFormatArgs;
std::vector<Compiler::GLSLFunction> fFunctions;
} // namespace SkSL