https://github.com/shader-slang/slang
Tip revision: ebf0e524d61d81a845daaf244b7ceef6c279f95e authored by jsmall-nvidia on 29 July 2021, 14:39:46 UTC
Improvements around glslang optimization (#1916)
Improvements around glslang optimization (#1916)
Tip revision: ebf0e52
slang-emit-c-like.h
// slang-emit-c-like.h
#ifndef SLANG_EMIT_C_LIKE_H
#define SLANG_EMIT_C_LIKE_H
#include "../core/slang-basic.h"
#include "slang-compiler.h"
#include "slang-emit-precedence.h"
#include "slang-emit-source-writer.h"
#include "slang-ir.h"
#include "slang-ir-insts.h"
#include "slang-ir-restructure.h"
namespace Slang
{
class CLikeSourceEmitter: public RefObject
{
public:
struct Desc
{
BackEndCompileRequest* compileRequest = nullptr;
/// The target language we want to generate code for
CodeGenTarget target = CodeGenTarget::Unknown;
/// The stage for the entry point we are being asked to compile
Stage entryPointStage = Stage::Unknown;
/// The "effective" profile that is being used to emit code,
/// combining information from the target and entry point.
Profile effectiveProfile = Profile::RawEnum::Unknown;
/// The capabilities of the target
CapabilitySet targetCaps;
/// The associated extension tracker
ExtensionTracker* extensionTracker = nullptr;
SourceWriter* sourceWriter = nullptr;
};
enum
{
kThreadGroupAxisCount = 3,
};
typedef unsigned int ESemanticMask;
enum
{
kESemanticMask_None = 0,
kESemanticMask_NoPackOffset = 1 << 0,
kESemanticMask_Default = kESemanticMask_NoPackOffset,
};
/// A C-style declarator, used for emitting types and declarations.
///
/// A C-style declaration typically has a *type specifier* (like
/// `int` or `MyType`) and a *declarator* (like `myVar` or
/// `myArray[]` or `*myPtr`).
///
/// The type of a declaration depends on both the type specifier
/// the declarator, and we already have logic to "unwrap" the
/// syntax of a declarator as part of the parser.
///
/// A `DeclaratorInfo` is used for the inverse process: taking
/// a complete type and splitting out the parts that need to be
/// handled as declarators when emitting code in a C-like language.
///
struct DeclaratorInfo
{
public:
enum class Flavor
{
Name,
Ptr,
SizedArray,
UnsizedArray,
LiteralSizedArray,
};
Flavor flavor;
protected:
DeclaratorInfo(Flavor flavor)
: flavor(flavor)
{}
};
/// A simple declarator that only includes a name
struct NameDeclaratorInfo : DeclaratorInfo
{
const StringSliceLoc* nameAndLoc;
NameDeclaratorInfo(StringSliceLoc const* nameAndLoc)
: DeclaratorInfo(Flavor::Name), nameAndLoc(nameAndLoc)
{}
};
/// A "chained" declarator that may a nested declarator.
struct ChainedDeclaratorInfo : DeclaratorInfo
{
DeclaratorInfo* next = nullptr;
protected:
ChainedDeclaratorInfo(Flavor flavor, DeclaratorInfo* next)
: DeclaratorInfo(flavor)
, next(next)
{}
};
struct PtrDeclaratorInfo : ChainedDeclaratorInfo
{
PtrDeclaratorInfo(DeclaratorInfo* next)
: ChainedDeclaratorInfo(Flavor::Ptr, next)
{}
};
struct SizedArrayDeclaratorInfo : ChainedDeclaratorInfo
{
IRInst* elementCount;
SizedArrayDeclaratorInfo(DeclaratorInfo* next, IRInst* elementCount)
: ChainedDeclaratorInfo(Flavor::SizedArray, next)
, elementCount(elementCount)
{}
};
struct UnsizedArrayDeclaratorInfo : ChainedDeclaratorInfo
{
UnsizedArrayDeclaratorInfo(DeclaratorInfo* next)
: ChainedDeclaratorInfo(Flavor::UnsizedArray, next)
{}
};
struct LiteralSizedArrayDeclaratorInfo : ChainedDeclaratorInfo
{
IRIntegerValue elementCount;
LiteralSizedArrayDeclaratorInfo(DeclaratorInfo* next, IRIntegerValue elementCount)
: ChainedDeclaratorInfo(Flavor::LiteralSizedArray, next)
, elementCount(elementCount)
{}
};
struct ComputeEmitActionsContext;
// An action to be performed during code emit.
struct EmitAction
{
enum Level
{
ForwardDeclaration,
Definition,
};
Level level;
IRInst* inst;
};
// A chain of variables to use for emitting semantic/layout info
struct EmitVarChain
{
IRVarLayout* varLayout;
EmitVarChain* next;
EmitVarChain()
: varLayout(nullptr)
, next(nullptr)
{}
EmitVarChain(IRVarLayout* varLayout)
: varLayout(varLayout)
, next(nullptr)
{}
EmitVarChain(IRVarLayout* varLayout, EmitVarChain* next)
: varLayout(varLayout)
, next(next)
{}
};
/// Must be called before used
virtual SlangResult init();
/// Ctor
CLikeSourceEmitter(const Desc& desc);
/// Get the source manager
SourceManager* getSourceManager() { return m_compileRequest->getSourceManager(); }
/// Get the source writer used
SourceWriter* getSourceWriter() const { return m_writer; }
/// Get the diagnostic sink
DiagnosticSink* getSink() { return m_compileRequest->getSink();}
/// Get the code gen target
CodeGenTarget getTarget() { return m_target; }
/// Get the source style
SLANG_FORCE_INLINE SourceLanguage getSourceLanguage() const { return m_sourceLanguage; }
void noteInternalErrorLoc(SourceLoc loc) { return getSink()->noteInternalErrorLoc(loc); }
CapabilitySet getTargetCaps() { return m_targetCaps; }
//
// Types
//
void emitDeclarator(DeclaratorInfo* declarator);
void emitType(IRType* type, const StringSliceLoc* nameLoc) { emitTypeImpl(type, nameLoc); }
void emitType(IRType* type, Name* name);
void emitType(IRType* type, String const& name);
void emitType(IRType* type);
void emitType(IRType* type, Name* name, SourceLoc const& nameLoc);
void emitType(IRType* type, NameLoc const& nameAndLoc);
//
// Expressions
//
bool maybeEmitParens(EmitOpInfo& outerPrec, const EmitOpInfo& prec);
void maybeCloseParens(bool needClose);
void emitStringLiteral(const String& value);
void emitVal(IRInst* val, const EmitOpInfo& outerPrec);
UInt getBindingOffset(EmitVarChain* chain, LayoutResourceKind kind);
UInt getBindingSpace(EmitVarChain* chain, LayoutResourceKind kind);
/// Emit directives to control overall layout computation for the emitted code.
void emitLayoutDirectives(TargetRequest* targetReq);
// Utility code for generating unique IDs as needed
// during the emit process (e.g., for declarations
// that didn't originally have names, but now need to).
UInt allocateUniqueID();
// IR-level emit logic
UInt getID(IRInst* value);
/// "Scrub" a name so that it complies with restrictions of the target language.
void appendScrubbedName(const UnownedStringSlice& name, StringBuilder& out);
String generateName(IRInst* inst);
virtual String generateEntryPointNameImpl(IREntryPointDecoration* entryPointDecor);
String getName(IRInst* inst);
void emitSimpleValue(IRInst* inst) { emitSimpleValueImpl(inst); }
bool shouldFoldInstIntoUseSites(IRInst* inst);
void emitOperand(IRInst* inst, EmitOpInfo const& outerPrec) { emitOperandImpl(inst, outerPrec); }
void emitArgs(IRInst* inst);
void emitRateQualifiers(IRInst* value);
void emitInstResultDecl(IRInst* inst);
IRTargetSpecificDecoration* findBestTargetDecoration(IRInst* inst);
IRTargetIntrinsicDecoration* findBestTargetIntrinsicDecoration(IRInst* inst);
// Check if the string being used to define a target intrinsic
// is an "ordinary" name, such that we can simply emit a call
// to the new name with the arguments of the old operation.
static bool isOrdinaryName(const UnownedStringSlice& name);
void emitIntrinsicCallExpr(
IRCall* inst,
IRTargetIntrinsicDecoration* targetIntrinsic,
EmitOpInfo const& inOuterPrec);
void emitCallExpr(IRCall* inst, EmitOpInfo outerPrec);
void emitInstExpr(IRInst* inst, EmitOpInfo const& inOuterPrec);
void defaultEmitInstExpr(IRInst* inst, EmitOpInfo const& inOuterPrec);
void diagnoseUnhandledInst(IRInst* inst);
BaseType extractBaseType(IRType* inType);
void emitInst(IRInst* inst);
void emitSemantics(IRInst* inst);
void emitSemanticsUsingVarLayout(IRVarLayout* varLayout);
static IRVarLayout* getVarLayout(IRInst* var);
void emitLayoutSemantics(IRInst* inst, char const* uniformSemanticSpelling = "register");
// When we are about to traverse an edge from one block to another,
// we need to emit the assignments that conceptually occur "along"
// the edge. In traditional SSA these are the phi nodes in the
// target block, while in our representation these use the arguments
// to the branch instruction to fill in the parameters of the target.
void emitPhiVarAssignments(UInt argCount, IRUse* args, IRBlock* targetBlock);
/// Emit high-level language statements from a structured region.
void emitRegion(Region* inRegion);
/// Emit high-level language statements from a structured region tree.
void emitRegionTree(RegionTree* regionTree);
// Is an IR function a definition? (otherwise it is a declaration)
bool isDefinition(IRFunc* func);
void emitEntryPointAttributes(IRFunc* irFunc, IREntryPointDecoration* entryPointDecor);
void emitPhiVarDecls(IRFunc* func);
/// Emit high-level statements for the body of a function.
void emitFunctionBody(IRGlobalValueWithCode* code);
void emitSimpleFunc(IRFunc* func) { emitSimpleFuncImpl(func); }
void emitParamType(IRType* type, String const& name) { emitParamTypeImpl(type, name); }
IRInst* getSpecializedValue(IRSpecialize* specInst);
void emitFuncDecl(IRFunc* func);
IREntryPointLayout* getEntryPointLayout(IRFunc* func);
IREntryPointLayout* asEntryPoint(IRFunc* func);
// Detect if the given IR function represents a
// declaration of an intrinsic/builtin for the
// current code-generation target.
bool isTargetIntrinsic(IRFunc* func);
void emitFunc(IRFunc* func);
void emitFuncDecorations(IRFunc* func);
void emitStruct(IRStructType* structType);
/// Emit type attributes that should appear after, e.g., a `struct` keyword
void emitPostKeywordTypeAttributes(IRInst* inst) { emitPostKeywordTypeAttributesImpl(inst); }
void emitInterpolationModifiers(IRInst* varInst, IRType* valueType, IRVarLayout* layout);
UInt getRayPayloadLocation(IRInst* inst);
UInt getCallablePayloadLocation(IRInst* inst);
/// Emit modifiers that should apply even for a declaration of an SSA temporary.
virtual void emitTempModifiers(IRInst* temp);
void emitVarModifiers(IRVarLayout* layout, IRInst* varDecl, IRType* varType);
/// Emit the array brackets that go on the end of a declaration of the given type.
void emitArrayBrackets(IRType* inType);
void emitParameterGroup(IRGlobalParam* varDecl, IRUniformParameterGroupType* type);
void emitVar(IRVar* varDecl);
void emitDereferenceOperand(IRInst* inst, EmitOpInfo const& outerPrec);
void emitGlobalVar(IRGlobalVar* varDecl);
void emitGlobalParam(IRGlobalParam* varDecl);
void emitGlobalInst(IRInst* inst);
virtual void emitGlobalInstImpl(IRInst* inst);
void ensureInstOperand(ComputeEmitActionsContext* ctx, IRInst* inst, EmitAction::Level requiredLevel = EmitAction::Level::Definition);
void ensureInstOperandsRec(ComputeEmitActionsContext* ctx, IRInst* inst);
void ensureGlobalInst(ComputeEmitActionsContext* ctx, IRInst* inst, EmitAction::Level requiredLevel);
void computeEmitActions(IRModule* module, List<EmitAction>& ioActions);
void executeEmitActions(List<EmitAction> const& actions);
void emitModule(IRModule* module, DiagnosticSink* sink)
{ m_irModule = module; emitModuleImpl(module, sink); }
/// Emit any preprocessor directives that should come *before* the prelude code
///
/// These are directives that are intended to customize some aspect(s) of the
/// prelude's behavior.
///
void emitPreludeDirectives() { emitPreludeDirectivesImpl(); }
void emitPreprocessorDirectives() { emitPreprocessorDirectivesImpl(); }
void emitSimpleType(IRType* type);
void emitVectorTypeName(IRType* elementType, IRIntegerValue elementCount) { emitVectorTypeNameImpl(elementType, elementCount); }
void emitTextureOrTextureSamplerType(IRTextureTypeBase* type, char const* baseName) { emitTextureOrTextureSamplerTypeImpl(type, baseName); }
virtual RefObject* getExtensionTracker() { return nullptr; }
/// Gets a source language for a target for a target. Returns Unknown if not a known target
static SourceLanguage getSourceLanguage(CodeGenTarget target);
/// Gets the default type name for built in scalar types. Different impls may require something different.
/// Returns an empty slice if not a built in type
static UnownedStringSlice getDefaultBuiltinTypeName(IROp op);
/// Finds the IRNumThreadsDecoration and gets the size from that or sets all dimensions to 1
static IRNumThreadsDecoration* getComputeThreadGroupSize(IRFunc* func, Int outNumThreads[kThreadGroupAxisCount]);
protected:
virtual bool doesTargetSupportPtrTypes() { return false; }
virtual void emitLayoutSemanticsImpl(IRInst* inst, char const* uniformSemanticSpelling = "register") { SLANG_UNUSED(inst); SLANG_UNUSED(uniformSemanticSpelling); }
virtual void emitParameterGroupImpl(IRGlobalParam* varDecl, IRUniformParameterGroupType* type) = 0;
virtual void emitEntryPointAttributesImpl(IRFunc* irFunc, IREntryPointDecoration* entryPointDecor) = 0;
virtual void emitImageFormatModifierImpl(IRInst* varDecl, IRType* varType) { SLANG_UNUSED(varDecl); SLANG_UNUSED(varType); }
virtual void emitLayoutQualifiersImpl(IRVarLayout* layout) { SLANG_UNUSED(layout); }
virtual void emitPreludeDirectivesImpl() {}
virtual void emitPreprocessorDirectivesImpl() {}
virtual void emitLayoutDirectivesImpl(TargetRequest* targetReq) { SLANG_UNUSED(targetReq); }
virtual void emitRateQualifiersImpl(IRRate* rate) { SLANG_UNUSED(rate); }
virtual void emitSemanticsImpl(IRInst* inst) { SLANG_UNUSED(inst); }
virtual void emitSimpleFuncParamImpl(IRParam* param);
virtual void emitSimpleFuncParamsImpl(IRFunc* func);
virtual void emitInterpolationModifiersImpl(IRInst* varInst, IRType* valueType, IRVarLayout* layout) { SLANG_UNUSED(varInst); SLANG_UNUSED(valueType); SLANG_UNUSED(layout); }
virtual void emitSimpleTypeImpl(IRType* type) = 0;
virtual void emitVarDecorationsImpl(IRInst* varDecl) { SLANG_UNUSED(varDecl); }
virtual void emitMatrixLayoutModifiersImpl(IRVarLayout* layout) { SLANG_UNUSED(layout); }
virtual void emitTypeImpl(IRType* type, const StringSliceLoc* nameLoc);
virtual void emitSimpleValueImpl(IRInst* inst);
virtual void emitModuleImpl(IRModule* module, DiagnosticSink* sink);
virtual void emitSimpleFuncImpl(IRFunc* func);
virtual void emitVarExpr(IRInst* inst, EmitOpInfo const& outerPrec);
virtual void emitOperandImpl(IRInst* inst, EmitOpInfo const& outerPrec);
virtual void emitParamTypeImpl(IRType* type, String const& name);
virtual void emitIntrinsicCallExprImpl(IRCall* inst, IRTargetIntrinsicDecoration* targetIntrinsic, EmitOpInfo const& inOuterPrec);
virtual void emitFunctionPreambleImpl(IRInst* inst) { SLANG_UNUSED(inst); }
virtual void emitLoopControlDecorationImpl(IRLoopControlDecoration* decl) { SLANG_UNUSED(decl); }
virtual void emitFuncDecorationImpl(IRDecoration* decoration) { SLANG_UNUSED(decoration); }
// Only needed for glsl output with $ prefix intrinsics - so perhaps removable in the future
virtual void emitTextureOrTextureSamplerTypeImpl(IRTextureTypeBase* type, char const* baseName) { SLANG_UNUSED(type); SLANG_UNUSED(baseName); }
// Again necessary for & prefix intrinsics. May be removable in the future
virtual void emitVectorTypeNameImpl(IRType* elementType, IRIntegerValue elementCount) = 0;
virtual void emitWitnessTable(IRWitnessTable* witnessTable);
virtual void emitInterface(IRInterfaceType* interfaceType);
virtual void emitRTTIObject(IRRTTIObject* rttiObject);
virtual bool tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) { SLANG_UNUSED(varDecl); SLANG_UNUSED(varType); return false; }
virtual bool tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOuterPrec) { SLANG_UNUSED(inst); SLANG_UNUSED(inOuterPrec); return false; }
/// Inspect the capabilities required by `inst` (according to its decorations),
/// and ensure that those capabilities have been detected and stored in the
/// target-specific extension tracker.
void handleRequiredCapabilities(IRInst* inst);
virtual void handleRequiredCapabilitiesImpl(IRInst* inst) { SLANG_UNUSED(inst); }
virtual void emitPostKeywordTypeAttributesImpl(IRInst* inst) { SLANG_UNUSED(inst); }
void _emitArrayType(IRArrayType* arrayType, DeclaratorInfo* declarator);
void _emitUnsizedArrayType(IRUnsizedArrayType* arrayType, DeclaratorInfo* declarator);
void _emitType(IRType* type, DeclaratorInfo* declarator);
void _emitInst(IRInst* inst);
// Emit the argument list (including paranthesis) in a `CallInst`
void _emitCallArgList(IRCall* call);
String _generateUniqueName(const UnownedStringSlice& slice);
// Sort witnessTable entries according to the order defined in the witnessed interface type.
List<IRWitnessTableEntry*> getSortedWitnessTableEntries(IRWitnessTable* witnessTable);
BackEndCompileRequest* m_compileRequest = nullptr;
IRModule* m_irModule = nullptr;
// The stage for which we are emitting code.
//
// TODO: We should support emitting code that includes multiple
// entry points for different stages, but this value is used
// in some very specific cases to determine how a construct
// should map to GLSL.
//
Stage m_entryPointStage = Stage::Unknown;
// The target language we want to generate code for
CodeGenTarget m_target;
/// The capabilities of the target
CapabilitySet m_targetCaps;
// Source language (based on the more nuanced m_target)
SourceLanguage m_sourceLanguage;
// Where source is written to
SourceWriter* m_writer;
UInt m_uniqueIDCounter = 1;
Dictionary<IRInst*, UInt> m_mapIRValueToID;
HashSet<String> m_irDeclsVisited;
HashSet<String> m_irTupleTypes;
// The "effective" profile that is being used to emit code,
// combining information from the target and entry point.
Profile m_effectiveProfile;
// Map a string name to the number of times we have seen this
// name used so far during code emission.
Dictionary<String, UInt> m_uniqueNameCounters;
// Map an IR instruction to the name that we've decided
// to use for it when emitting code.
Dictionary<IRInst*, String> m_mapInstToName;
Dictionary<IRInst*, UInt> m_mapIRValueToRayPayloadLocation;
Dictionary<IRInst*, UInt> m_mapIRValueToCallablePayloadLocation;
};
}
#endif