// slang-ir-insts.h
#ifndef SLANG_IR_INSTS_H_INCLUDED
#define SLANG_IR_INSTS_H_INCLUDED
// This file extends the core definitions in `ir.h`
// with a wider variety of concrete instructions,
// and a "builder" abstraction.
//
// TODO: the builder probably needs its own file.
#include "slang-capability.h"
#include "slang-compiler.h"
#include "slang-ir.h"
#include "slang-syntax.h"
#include "slang-type-layout.h"
namespace Slang {
class Decl;
struct IRCapabilitySet : IRInst
{
IR_LEAF_ISA(CapabilitySet);
CapabilitySet getCaps();
};
struct IRDecoration : IRInst
{
IR_PARENT_ISA(Decoration)
IRDecoration* getNextDecoration()
{
return as<IRDecoration>(getNextInst());
}
};
// Associates an IR-level decoration with a source declaration
// in the high-level AST, that can be used to extract
// additional information that informs code emission.
struct IRHighLevelDeclDecoration : IRDecoration
{
enum { kOp = kIROp_HighLevelDeclDecoration };
IR_LEAF_ISA(HighLevelDeclDecoration)
IRPtrLit* getDeclOperand() { return cast<IRPtrLit>(getOperand(0)); }
Decl* getDecl() { return (Decl*) getDeclOperand()->getValue(); }
};
enum IRLoopControl
{
kIRLoopControl_Unroll,
kIRLoopControl_Loop,
};
struct IRLoopControlDecoration : IRDecoration
{
enum { kOp = kIROp_LoopControlDecoration };
IR_LEAF_ISA(LoopControlDecoration)
IRConstant* getModeOperand() { return cast<IRConstant>(getOperand(0)); }
IRLoopControl getMode()
{
return IRLoopControl(getModeOperand()->value.intVal);
}
};
struct IRLoopMaxItersDecoration : IRDecoration
{
enum { kOp = kIROp_LoopMaxItersDecoration };
IR_LEAF_ISA(LoopMaxItersDecoration)
IRConstant* getMaxItersInst() { return cast<IRConstant>(getOperand(0)); }
IRIntegerValue getMaxIters() { return as<IRIntLit>(getOperand(0))->getValue(); }
};
struct IRTargetSpecificDecoration : IRDecoration
{
IR_PARENT_ISA(TargetSpecificDecoration)
IRCapabilitySet* getTargetCapsOperand() { return cast<IRCapabilitySet>(getOperand(0)); }
CapabilitySet getTargetCaps() { return getTargetCapsOperand()->getCaps(); }
};
struct IRTargetDecoration : IRTargetSpecificDecoration
{
enum { kOp = kIROp_TargetDecoration };
IR_LEAF_ISA(TargetDecoration)
};
struct IRTargetIntrinsicDecoration : IRTargetSpecificDecoration
{
enum { kOp = kIROp_TargetIntrinsicDecoration };
IR_LEAF_ISA(TargetIntrinsicDecoration)
IRStringLit* getDefinitionOperand() { return cast<IRStringLit>(getOperand(1)); }
UnownedStringSlice getDefinition()
{
return getDefinitionOperand()->getStringSlice();
}
};
struct IRIntrinsicOpDecoration : IRDecoration
{
enum { kOp = kIROp_IntrinsicOpDecoration };
IR_LEAF_ISA(IntrinsicOpDecoration)
IRIntLit* getIntrinsicOpOperand() { return cast<IRIntLit>(getOperand(0)); }
IROp getIntrinsicOp()
{
return (IROp)getIntrinsicOpOperand()->getValue();
}
};
struct IRGLSLOuterArrayDecoration : IRDecoration
{
enum { kOp = kIROp_GLSLOuterArrayDecoration };
IR_LEAF_ISA(GLSLOuterArrayDecoration)
IRStringLit* getOuterArraynameOperand() { return cast<IRStringLit>(getOperand(0)); }
UnownedStringSlice getOuterArrayName()
{
return getOuterArraynameOperand()->getStringSlice();
}
};
enum class IRInterpolationMode
{
Linear,
NoPerspective,
NoInterpolation,
Centroid,
Sample,
PerVertex,
};
struct IRInterpolationModeDecoration : IRDecoration
{
enum { kOp = kIROp_InterpolationModeDecoration };
IR_LEAF_ISA(InterpolationModeDecoration)
IRConstant* getModeOperand() { return cast<IRConstant>(getOperand(0)); }
IRInterpolationMode getMode()
{
return IRInterpolationMode(getModeOperand()->value.intVal);
}
};
/// A decoration that provides a desired name to be used
/// in conjunction with the given instruction. Back-end
/// code generation may use this to help derive symbol
/// names, emit debug information, etc.
struct IRNameHintDecoration : IRDecoration
{
enum { kOp = kIROp_NameHintDecoration };
IR_LEAF_ISA(NameHintDecoration)
IRStringLit* getNameOperand() { return cast<IRStringLit>(getOperand(0)); }
UnownedStringSlice getName()
{
return getNameOperand()->getStringSlice();
}
};
/// A decoration on a RTTIObject providing type size information.
struct IRRTTITypeSizeDecoration : IRDecoration
{
enum { kOp = kIROp_RTTITypeSizeDecoration };
IR_LEAF_ISA(RTTITypeSizeDecoration)
IRIntLit* getTypeSizeOperand() { return cast<IRIntLit>(getOperand(0)); }
IRIntegerValue getTypeSize()
{
return getTypeSizeOperand()->getValue();
}
};
/// A decoration on `IRInterfaceType` that marks the size of `AnyValue` that should
/// be used to represent a polymorphic value of the interface.
struct IRAnyValueSizeDecoration : IRDecoration
{
enum { kOp = kIROp_AnyValueSizeDecoration };
IR_LEAF_ISA(AnyValueSizeDecoration)
IRIntLit* getSizeOperand() { return cast<IRIntLit>(getOperand(0)); }
IRIntegerValue getSize()
{
return getSizeOperand()->getValue();
}
};
struct IRSpecializeDecoration : IRDecoration
{
enum { kOp = kIROp_SpecializeDecoration };
IR_LEAF_ISA(SpecializeDecoration)
};
struct IRComInterfaceDecoration : IRDecoration
{
enum
{
kOp = kIROp_ComInterfaceDecoration
};
IR_LEAF_ISA(ComInterfaceDecoration)
};
struct IRCOMWitnessDecoration : IRDecoration
{
enum
{
kOp = kIROp_COMWitnessDecoration
};
IR_LEAF_ISA(COMWitnessDecoration)
IRInst* getWitnessTable() { return getOperand(0); }
};
/// A decoration on `IRParam`s that represent generic parameters,
/// marking the interface type that the generic parameter conforms to.
/// A generic parameter can have more than one `IRTypeConstraintDecoration`s
struct IRTypeConstraintDecoration : IRDecoration
{
enum { kOp = kIROp_TypeConstraintDecoration };
IR_LEAF_ISA(TypeConstraintDecoration)
IRInst* getConstraintType() { return getOperand(0); }
};
#define IR_SIMPLE_DECORATION(NAME) \
struct IR##NAME : IRDecoration \
{ \
enum { kOp = kIROp_##NAME }; \
IR_LEAF_ISA(NAME) \
}; \
/**/
bool isSimpleDecoration(IROp op);
/// A decoration that indicates that a variable represents
/// a vulkan ray payload, and should have a location assigned
/// to it.
IR_SIMPLE_DECORATION(VulkanRayPayloadDecoration)
/// A decoration that indicates that a variable represents
/// a vulkan callable shader payload, and should have a location assigned
/// to it.
IR_SIMPLE_DECORATION(VulkanCallablePayloadDecoration)
/// A decoration that indicates that a variable represents
/// vulkan hit attributes, and should have a location assigned
/// to it.
IR_SIMPLE_DECORATION(VulkanHitAttributesDecoration)
/// A decoration that indicates that a variable represents
/// vulkan hit object attributes, and should have a location assigned
/// to it.
IR_SIMPLE_DECORATION(VulkanHitObjectAttributesDecoration)
struct IRRequireGLSLVersionDecoration : IRDecoration
{
enum { kOp = kIROp_RequireGLSLVersionDecoration };
IR_LEAF_ISA(RequireGLSLVersionDecoration)
IRConstant* getLanguageVersionOperand() { return cast<IRConstant>(getOperand(0)); }
Int getLanguageVersion()
{
return Int(getLanguageVersionOperand()->value.intVal);
}
};
struct IRRequireSPIRVVersionDecoration : IRDecoration
{
enum { kOp = kIROp_RequireSPIRVVersionDecoration };
IR_LEAF_ISA(RequireGLSLVersionDecoration)
IRConstant* getSPIRVVersionOperand() { return cast<IRConstant>(getOperand(0)); }
IntegerLiteralValue getSPIRVVersion()
{
return getSPIRVVersionOperand()->value.intVal;
}
};
struct IRRequireCUDASMVersionDecoration : IRDecoration
{
enum { kOp = kIROp_RequireCUDASMVersionDecoration };
IR_LEAF_ISA(RequireCUDASMVersionDecoration)
IRConstant* getCUDASMVersionOperand() { return cast<IRConstant>(getOperand(0)); }
IntegerLiteralValue getCUDASMVersion()
{
return getCUDASMVersionOperand()->value.intVal;
}
};
struct IRRequireGLSLExtensionDecoration : IRDecoration
{
enum { kOp = kIROp_RequireGLSLExtensionDecoration };
IR_LEAF_ISA(RequireGLSLExtensionDecoration)
IRStringLit* getExtensionNameOperand() { return cast<IRStringLit>(getOperand(0)); }
UnownedStringSlice getExtensionName()
{
return getExtensionNameOperand()->getStringSlice();
}
};
IR_SIMPLE_DECORATION(ReadNoneDecoration)
IR_SIMPLE_DECORATION(NoSideEffectDecoration)
IR_SIMPLE_DECORATION(EarlyDepthStencilDecoration)
IR_SIMPLE_DECORATION(GloballyCoherentDecoration)
IR_SIMPLE_DECORATION(PreciseDecoration)
IR_SIMPLE_DECORATION(PublicDecoration)
IR_SIMPLE_DECORATION(HLSLExportDecoration)
IR_SIMPLE_DECORATION(KeepAliveDecoration)
IR_SIMPLE_DECORATION(RequiresNVAPIDecoration)
IR_SIMPLE_DECORATION(NoInlineDecoration)
IR_SIMPLE_DECORATION(AlwaysFoldIntoUseSiteDecoration)
struct IRNVAPIMagicDecoration : IRDecoration
{
enum { kOp = kIROp_NVAPIMagicDecoration };
IR_LEAF_ISA(NVAPIMagicDecoration)
IRStringLit* getNameOperand() { return cast<IRStringLit>(getOperand(0)); }
UnownedStringSlice getName() { return getNameOperand()->getStringSlice(); }
};
struct IRNVAPISlotDecoration : IRDecoration
{
enum { kOp = kIROp_NVAPISlotDecoration };
IR_LEAF_ISA(NVAPISlotDecoration)
IRStringLit* getRegisterNameOperand() { return cast<IRStringLit>(getOperand(0)); }
UnownedStringSlice getRegisterName() { return getRegisterNameOperand()->getStringSlice(); }
IRStringLit* getSpaceNameOperand() { return cast<IRStringLit>(getOperand(1)); }
UnownedStringSlice getSpaceName() { return getSpaceNameOperand()->getStringSlice(); }
};
struct IROutputControlPointsDecoration : IRDecoration
{
enum { kOp = kIROp_OutputControlPointsDecoration };
IR_LEAF_ISA(OutputControlPointsDecoration)
IRIntLit* getControlPointCount() { return cast<IRIntLit>(getOperand(0)); }
};
// This is used for mesh shaders too
struct IROutputTopologyDecoration : IRDecoration
{
enum { kOp = kIROp_OutputTopologyDecoration };
IR_LEAF_ISA(OutputTopologyDecoration)
IRStringLit* getTopology() { return cast<IRStringLit>(getOperand(0)); }
};
struct IRPartitioningDecoration : IRDecoration
{
enum { kOp = kIROp_PartitioningDecoration };
IR_LEAF_ISA(PartitioningDecoration)
IRStringLit* getPartitioning() { return cast<IRStringLit>(getOperand(0)); }
};
struct IRDomainDecoration : IRDecoration
{
enum { kOp = kIROp_DomainDecoration };
IR_LEAF_ISA(DomainDecoration)
IRStringLit* getDomain() { return cast<IRStringLit>(getOperand(0)); }
};
struct IRMaxVertexCountDecoration : IRDecoration
{
enum { kOp = kIROp_MaxVertexCountDecoration };
IR_LEAF_ISA(MaxVertexCountDecoration)
IRIntLit* getCount() { return cast<IRIntLit>(getOperand(0)); }
};
struct IRInstanceDecoration : IRDecoration
{
enum { kOp = kIROp_InstanceDecoration };
IR_LEAF_ISA(InstanceDecoration)
IRIntLit* getCount() { return cast<IRIntLit>(getOperand(0)); }
};
struct IRNumThreadsDecoration : IRDecoration
{
enum { kOp = kIROp_NumThreadsDecoration };
IR_LEAF_ISA(NumThreadsDecoration)
IRIntLit* getX() { return cast<IRIntLit>(getOperand(0)); }
IRIntLit* getY() { return cast<IRIntLit>(getOperand(1)); }
IRIntLit* getZ() { return cast<IRIntLit>(getOperand(2)); }
IRIntLit* getExtentAlongAxis(int axis) { return cast<IRIntLit>(getOperand(axis)); }
};
struct IREntryPointDecoration : IRDecoration
{
enum { kOp = kIROp_EntryPointDecoration };
IR_LEAF_ISA(EntryPointDecoration)
IRIntLit* getProfileInst() { return cast<IRIntLit>(getOperand(0)); }
Profile getProfile() { return Profile(Profile::RawVal(getIntVal(getProfileInst()))); }
IRStringLit* getName() { return cast<IRStringLit>(getOperand(1)); }
IRStringLit* getModuleName() { return cast<IRStringLit>(getOperand(2)); }
};
IR_SIMPLE_DECORATION(CudaHostDecoration)
IR_SIMPLE_DECORATION(CudaKernelDecoration)
struct IRGeometryInputPrimitiveTypeDecoration: IRDecoration
{
IR_PARENT_ISA(GeometryInputPrimitiveTypeDecoration)
};
IR_SIMPLE_DECORATION(PointInputPrimitiveTypeDecoration)
IR_SIMPLE_DECORATION(LineInputPrimitiveTypeDecoration)
IR_SIMPLE_DECORATION(TriangleInputPrimitiveTypeDecoration)
IR_SIMPLE_DECORATION(LineAdjInputPrimitiveTypeDecoration)
IR_SIMPLE_DECORATION(TriangleAdjInputPrimitiveTypeDecoration)
/// This is a bit of a hack. The problem is that when GLSL legalization takes place
/// the parameters from the entry point are globalized *and* potentially split
/// So even if we did copy a suitable decoration onto the globalized parameters,
/// it would potentially output multiple times without extra logic.
/// Using this decoration we can copy the StreamOut type to the entry point, and then
/// emit as part of entry point attribute emitting.
struct IRStreamOutputTypeDecoration : IRDecoration
{
enum { kOp = kIROp_StreamOutputTypeDecoration };
IR_LEAF_ISA(StreamOutputTypeDecoration)
IRHLSLStreamOutputType* getStreamType() { return cast<IRHLSLStreamOutputType>(getOperand(0)); }
};
/// A decoration that marks a value as having linkage.
/// A value with linkage is either exported from its module,
/// or will have a definition imported from another module.
/// In either case, it requires a mangled name to use when
/// matching imports and exports.
struct IRLinkageDecoration : IRDecoration
{
IR_PARENT_ISA(LinkageDecoration)
IRStringLit* getMangledNameOperand() { return cast<IRStringLit>(getOperand(0)); }
UnownedStringSlice getMangledName()
{
return getMangledNameOperand()->getStringSlice();
}
};
struct IRImportDecoration : IRLinkageDecoration
{
enum { kOp = kIROp_ImportDecoration };
IR_LEAF_ISA(ImportDecoration)
};
struct IRExportDecoration : IRLinkageDecoration
{
enum { kOp = kIROp_ExportDecoration };
IR_LEAF_ISA(ExportDecoration)
};
struct IRExternCppDecoration : IRDecoration
{
enum
{
kOp = kIROp_ExternCppDecoration
};
IR_LEAF_ISA(ExternCppDecoration)
IRStringLit* getNameOperand() { return cast<IRStringLit>(getOperand(0)); }
UnownedStringSlice getName() { return getNameOperand()->getStringSlice(); }
};
struct IRDllImportDecoration : IRDecoration
{
enum
{
kOp = kIROp_DllImportDecoration
};
IR_LEAF_ISA(DllImportDecoration)
IRStringLit* getLibraryNameOperand() { return cast<IRStringLit>(getOperand(0)); }
UnownedStringSlice getLibraryName() { return getLibraryNameOperand()->getStringSlice(); }
IRStringLit* getFunctionNameOperand() { return cast<IRStringLit>(getOperand(1)); }
UnownedStringSlice getFunctionName() { return getFunctionNameOperand()->getStringSlice(); }
};
struct IRDllExportDecoration : IRDecoration
{
enum
{
kOp = kIROp_DllExportDecoration
};
IR_LEAF_ISA(DllExportDecoration)
IRStringLit* getFunctionNameOperand() { return cast<IRStringLit>(getOperand(0)); }
UnownedStringSlice getFunctionName() { return getFunctionNameOperand()->getStringSlice(); }
};
struct IRTorchEntryPointDecoration : IRDecoration
{
enum
{
kOp = kIROp_TorchEntryPointDecoration
};
IR_LEAF_ISA(TorchEntryPointDecoration)
IRStringLit* getFunctionNameOperand() { return cast<IRStringLit>(getOperand(0)); }
UnownedStringSlice getFunctionName() { return getFunctionNameOperand()->getStringSlice(); }
};
struct IRFormatDecoration : IRDecoration
{
enum { kOp = kIROp_FormatDecoration };
IR_LEAF_ISA(FormatDecoration)
IRConstant* getFormatOperand() { return cast<IRConstant>(getOperand(0)); }
ImageFormat getFormat()
{
return ImageFormat(getFormatOperand()->value.intVal);
}
};
IR_SIMPLE_DECORATION(UnsafeForceInlineEarlyDecoration)
IR_SIMPLE_DECORATION(ForceInlineDecoration)
IR_SIMPLE_DECORATION(ForceUnrollDecoration)
struct IRNaturalSizeAndAlignmentDecoration : IRDecoration
{
enum { kOp = kIROp_NaturalSizeAndAlignmentDecoration };
IR_LEAF_ISA(NaturalSizeAndAlignmentDecoration)
IRIntLit* getSizeOperand() { return cast<IRIntLit>(getOperand(0)); }
IRIntLit* getAlignmentOperand() { return cast<IRIntLit>(getOperand(1)); }
IRIntegerValue getSize() { return getSizeOperand()->getValue(); }
IRIntegerValue getAlignment() { return getAlignmentOperand()->getValue(); }
};
struct IRNaturalOffsetDecoration : IRDecoration
{
enum { kOp = kIROp_NaturalOffsetDecoration };
IR_LEAF_ISA(NaturalOffsetDecoration)
IRIntLit* getOffsetOperand() { return cast<IRIntLit>(getOperand(0)); }
IRIntegerValue getOffset() { return getOffsetOperand()->getValue(); }
};
struct IRBuiltinDecoration : IRDecoration
{
enum
{
kOp = kIROp_BuiltinDecoration
};
IR_LEAF_ISA(BuiltinDecoration)
};
struct IRSequentialIDDecoration : IRDecoration
{
enum
{
kOp = kIROp_SequentialIDDecoration
};
IR_LEAF_ISA(SequentialIDDecoration)
IRIntLit* getSequentialIDOperand() { return cast<IRIntLit>(getOperand(0)); }
IRIntegerValue getSequentialID() { return getSequentialIDOperand()->getValue(); }
};
struct IRAutoDiffOriginalValueDecoration : IRDecoration
{
enum
{
kOp = kIROp_AutoDiffOriginalValueDecoration
};
IR_LEAF_ISA(AutoDiffOriginalValueDecoration)
IRInst* getOriginalValue() { return getOperand(0); }
};
struct IRForwardDifferentiableDecoration : IRDecoration
{
enum
{
kOp = kIROp_ForwardDifferentiableDecoration
};
IR_LEAF_ISA(ForwardDifferentiableDecoration)
};
struct IRForwardDerivativeDecoration : IRDecoration
{
enum
{
kOp = kIROp_ForwardDerivativeDecoration
};
IR_LEAF_ISA(ForwardDerivativeDecoration)
IRInst* getForwardDerivativeFunc() { return getOperand(0); }
};
struct IRPrimalSubstituteDecoration : IRDecoration
{
enum
{
kOp = kIROp_PrimalSubstituteDecoration
};
IR_LEAF_ISA(PrimalSubstituteDecoration)
IRInst* getPrimalSubstituteFunc() { return getOperand(0); }
};
struct IRBackwardDerivativeIntermediateTypeDecoration : IRDecoration
{
enum
{
kOp = kIROp_BackwardDerivativeIntermediateTypeDecoration
};
IR_LEAF_ISA(BackwardDerivativeIntermediateTypeDecoration)
IRInst* getBackwardDerivativeIntermediateType() { return getOperand(0); }
};
struct IRBackwardDerivativePrimalDecoration : IRDecoration
{
enum
{
kOp = kIROp_BackwardDerivativePrimalDecoration
};
IR_LEAF_ISA(BackwardDerivativePrimalDecoration)
IRInst* getBackwardDerivativePrimalFunc() { return getOperand(0); }
};
// Used to associate the restore context var to use in a call to splitted backward propgate function.
struct IRBackwardDerivativePrimalContextDecoration : IRDecoration
{
enum
{
kOp = kIROp_BackwardDerivativePrimalContextDecoration
};
IR_LEAF_ISA(BackwardDerivativePrimalContextDecoration)
IRUse primalContextVar;
IRInst* getBackwardDerivativePrimalContextVar() { return getOperand(0); }
};
struct IRBackwardDerivativePrimalReturnDecoration : IRDecoration
{
enum
{
kOp = kIROp_BackwardDerivativePrimalReturnDecoration
};
IR_LEAF_ISA(BackwardDerivativePrimalReturnDecoration)
IRInst* getBackwardDerivativePrimalReturnValue() { return getOperand(0); }
};
struct IRBackwardDerivativePropagateDecoration : IRDecoration
{
enum
{
kOp = kIROp_BackwardDerivativePropagateDecoration
};
IR_LEAF_ISA(BackwardDerivativePropagateDecoration)
IRInst* getBackwardDerivativePropagateFunc() { return getOperand(0); }
};
struct IRBackwardDerivativeDecoration : IRDecoration
{
enum
{
kOp = kIROp_BackwardDerivativeDecoration
};
IR_LEAF_ISA(BackwardDerivativeDecoration)
IRInst* getBackwardDerivativeFunc() { return getOperand(0); }
};
struct IRLoopCounterDecoration : IRDecoration
{
enum
{
kOp = kIROp_LoopCounterDecoration
};
IR_LEAF_ISA(LoopCounterDecoration)
};
struct IRLoopExitPrimalValueDecoration : IRDecoration
{
enum
{
kOp = kIROp_LoopExitPrimalValueDecoration
};
IR_LEAF_ISA(LoopExitPrimalValueDecoration)
IRUse target;
IRUse exitVal;
IRInst* getTargetInst() { return getOperand(0); }
IRInst* getLoopExitValInst() { return getOperand(1); }
};
struct IRAutodiffInstDecoration : IRDecoration
{
IR_PARENT_ISA(AutodiffInstDecoration)
};
struct IRDifferentialInstDecoration : IRAutodiffInstDecoration
{
enum
{
kOp = kIROp_DifferentialInstDecoration
};
IRUse primalType;
IR_LEAF_ISA(DifferentialInstDecoration)
IRType* getPrimalType() { return (IRType*)(getOperand(0)); }
IRInst* getPrimalInst() { return getOperand(1); }
};
struct IRPrimalInstDecoration : IRAutodiffInstDecoration
{
enum
{
kOp = kIROp_PrimalInstDecoration
};
IR_LEAF_ISA(PrimalInstDecoration)
};
struct IRMixedDifferentialInstDecoration : IRAutodiffInstDecoration
{
enum
{
kOp = kIROp_MixedDifferentialInstDecoration
};
IRUse pairType;
IR_LEAF_ISA(MixedDifferentialInstDecoration)
IRType* getPairType() { return as<IRType>(getOperand(0)); }
};
struct IRPrimalValueAccessDecoration : IRAutodiffInstDecoration
{
enum
{
kOp = kIROp_PrimalValueAccessDecoration
};
IR_LEAF_ISA(PrimalValueAccessDecoration)
};
struct IRPrimalValueStructKeyDecoration : IRDecoration
{
enum
{
kOp = kIROp_PrimalValueStructKeyDecoration
};
IR_LEAF_ISA(PrimalValueStructKeyDecoration)
IRStructKey* getStructKey() { return as<IRStructKey>(getOperand(0)); }
};
struct IRPrimalElementTypeDecoration : IRDecoration
{
enum
{
kOp = kIROp_PrimalElementTypeDecoration
};
IR_LEAF_ISA(PrimalElementTypeDecoration)
IRInst* getPrimalElementType() { return getOperand(0); }
};
struct IRIntermediateContextFieldDifferentialTypeDecoration : IRDecoration
{
enum
{
kOp = kIROp_IntermediateContextFieldDifferentialTypeDecoration
};
IR_LEAF_ISA(IntermediateContextFieldDifferentialTypeDecoration)
IRInst* getDifferentialWitness() { return getOperand(0); }
};
struct IRBackwardDifferentiableDecoration : IRDecoration
{
enum
{
kOp = kIROp_BackwardDifferentiableDecoration
};
IR_LEAF_ISA(BackwardDifferentiableDecoration)
};
struct IRUserDefinedBackwardDerivativeDecoration : IRDecoration
{
enum
{
kOp = kIROp_UserDefinedBackwardDerivativeDecoration
};
IR_LEAF_ISA(UserDefinedBackwardDerivativeDecoration)
IRInst* getBackwardDerivativeFunc() { return getOperand(0); }
};
struct IRTreatAsDifferentiableDecoration : IRDecoration
{
enum
{
kOp = kIROp_TreatAsDifferentiableDecoration
};
IR_LEAF_ISA(TreatAsDifferentiableDecoration)
};
struct IRDerivativeMemberDecoration : IRDecoration
{
enum
{
kOp = kIROp_DerivativeMemberDecoration
};
IR_LEAF_ISA(DerivativeMemberDecoration)
IRInst* getDerivativeMemberStructKey() { return getOperand(0); }
};
// An instruction that replaces the function symbol
// with it's derivative function.
struct IRForwardDifferentiate : IRInst
{
enum
{
kOp = kIROp_ForwardDifferentiate
};
// The base function for the call.
IRUse base;
IRInst* getBaseFn() { return getOperand(0); }
IR_LEAF_ISA(ForwardDifferentiate)
};
// An instruction that replaces the function symbol
// with its backward derivative primal function.
// A backward derivative primal function is the first pass
// of backward derivative computation. It performs the primal
// computations and returns the intermediates that will be used
// by the actual backward derivative function.
struct IRBackwardDifferentiatePrimal : IRInst
{
enum
{
kOp = kIROp_BackwardDifferentiatePrimal
};
// The base function for the call.
IRUse base;
IRInst* getBaseFn() { return getOperand(0); }
IR_LEAF_ISA(BackwardDifferentiatePrimal)
};
// An instruction that replaces the function symbol with its backward derivative propagate function.
// A backward derivative propagate function is the second pass of backward derivative computation. It uses the
// intermediates computed in the bacward derivative primal function to perform the actual backward
// derivative propagation.
struct IRBackwardDifferentiatePropagate : IRInst
{
enum
{
kOp = kIROp_BackwardDifferentiatePropagate
};
// The base function for the call.
IRUse base;
IRInst* getBaseFn() { return getOperand(0); }
IR_LEAF_ISA(BackwardDifferentiatePropagate)
};
// An instruction that replaces the function symbol with its backward derivative function.
// A backward derivative function is a concept that combines both passes of backward derivative
// computation. This inst should only be produced by lower-to-ir, and will be replaced with calls to
// the primal function followed by the propagate function in the auto-diff pass.
struct IRBackwardDifferentiate : IRInst
{
enum
{
kOp = kIROp_BackwardDifferentiate
};
// The base function for the call.
IRUse base;
IRInst* getBaseFn() { return getOperand(0); }
IR_LEAF_ISA(BackwardDifferentiate)
};
// Retrieves the primal substitution function for the given function.
struct IRPrimalSubstitute : IRInst
{
enum
{
kOp = kIROp_PrimalSubstitute
};
IRInst* getBaseFn() { return getOperand(0); }
IR_LEAF_ISA(PrimalSubstitute)
};
struct IRDispatchKernel : IRInst
{
enum
{
kOp = kIROp_DispatchKernel
};
IRInst* getBaseFn() { return getOperand(0); }
IRInst* getThreadGroupSize() { return getOperand(1); }
IRInst* getDispatchSize() { return getOperand(2); }
UInt getArgCount() { return getOperandCount() - 3; }
IRInst* getArg(UInt i) { return getOperand(3 + i); }
IR_LEAF_ISA(DispatchKernel)
};
struct IRTorchTensorGetView : IRInst
{
enum
{
kOp = kIROp_TorchTensorGetView
};
IR_LEAF_ISA(TorchTensorGetView)
};
// Dictionary item mapping a type with a corresponding
// IDifferentiable witness table
//
struct IRDifferentiableTypeDictionaryItem : IRInst
{
IR_LEAF_ISA(DifferentiableTypeDictionaryItem)
IRInst* getConcreteType() { return getOperand(0); }
IRInst* getWitness() { return getOperand(1); }
};
struct IRDifferentiableTypeDictionaryDecoration : IRDecoration
{
IR_LEAF_ISA(DifferentiableTypeDictionaryDecoration)
};
struct IRFloatingModeOverrideDecoration : IRDecoration
{
IR_LEAF_ISA(FloatingPointModeOverrideDecoration)
FloatingPointMode getFloatingPointMode() { return (FloatingPointMode)cast<IRIntLit>(getOperand(0))->getValue(); }
};
// An instruction that specializes another IR value
// (representing a generic) to a particular set of generic arguments
// (instructions representing types, witness tables, etc.)
struct IRSpecialize : IRInst
{
// The "base" for the call is the generic to be specialized
IRUse base;
IRInst* getBase() { return getOperand(0); }
// after the generic value come the arguments
UInt getArgCount() { return getOperandCount() - 1; }
IRInst* getArg(UInt index) { return getOperand(index + 1); }
IR_LEAF_ISA(Specialize)
};
// An instruction that looks up the implementation
// of an interface operation identified by `requirementDeclRef`
// in the witness table `witnessTable` which should
// hold the conformance information for a specific type.
struct IRLookupWitnessMethod : IRInst
{
IRUse witnessTable;
IRUse requirementKey;
IRInst* getWitnessTable() { return witnessTable.get(); }
IRInst* getRequirementKey() { return requirementKey.get(); }
IR_LEAF_ISA(LookupWitness)
};
// Returns the sequential ID of an RTTI object.
struct IRGetSequentialID : IRInst
{
IR_LEAF_ISA(GetSequentialID)
IRInst* getRTTIOperand() { return getOperand(0); }
};
struct IRLookupWitnessTable : IRInst
{
IRUse sourceType;
IRUse interfaceType;
};
/// Allocates space from local stack.
///
struct IRAlloca : IRInst
{
IR_LEAF_ISA(Alloca)
IRInst* getAllocSize() { return getOperand(0); }
};
/// Packs a value into an `AnyValue`.
/// Return type is `IRAnyValueType`.
struct IRPackAnyValue : IRInst
{
IR_LEAF_ISA(PackAnyValue)
IRInst* getValue() { return getOperand(0); }
};
/// Unpacks a `AnyValue` value into a concrete type.
/// Operand must have `IRAnyValueType`.
struct IRUnpackAnyValue : IRInst
{
IR_LEAF_ISA(UnpackAnyValue)
IRInst* getValue() { return getOperand(0); }
};
// Layout decorations
/// A decoration that marks a field key as having been associated
/// with a particular simple semantic (e.g., `COLOR` or `SV_Position`,
/// but not a `register` semantic).
///
/// This is currently needed so that we can round-trip HLSL `struct`
/// types that get used for varying input/output. This is an unfortunate
/// case where some amount of "layout" information can't just come
/// in via the `TypeLayout` part of things.
///
struct IRSemanticDecoration : public IRDecoration
{
IR_LEAF_ISA(SemanticDecoration)
IRStringLit* getSemanticNameOperand() { return cast<IRStringLit>(getOperand(0)); }
UnownedStringSlice getSemanticName() { return getSemanticNameOperand()->getStringSlice(); }
IRIntLit* getSemanticIndexOperand() { return cast<IRIntLit>(getOperand(1)); }
int getSemanticIndex() { return int(getIntVal(getSemanticIndexOperand())); }
};
struct IRStageAccessDecoration : public IRDecoration
{
IR_PARENT_ISA(StageAccessDecoration)
Int getStageCount() { return (Int) getOperandCount(); }
IRStringLit* getStageOperand(Int index) { return cast<IRStringLit>(getOperand(index)); }
UnownedStringSlice getStageName(Int index) { return getStageOperand(index)->getStringSlice(); }
};
struct IRStageReadAccessDecoration : public IRStageAccessDecoration
{
IR_LEAF_ISA(StageReadAccessDecoration)
};
struct IRStageWriteAccessDecoration : public IRStageAccessDecoration
{
IR_LEAF_ISA(StageWriteAccessDecoration)
};
struct IRPayloadDecoration : public IRDecoration
{
IR_LEAF_ISA(PayloadDecoration)
};
// Mesh shader decorations
struct IRMeshOutputDecoration : public IRDecoration
{
IR_PARENT_ISA(MeshOutputDecoration)
IRIntLit* getMaxSize() { return cast<IRIntLit>(getOperand(0)); }
};
struct IRVerticesDecoration : public IRMeshOutputDecoration
{
IR_LEAF_ISA(VerticesDecoration)
};
struct IRIndicesDecoration : public IRMeshOutputDecoration
{
IR_LEAF_ISA(IndicesDecoration)
};
struct IRPrimitivesDecoration : public IRMeshOutputDecoration
{
IR_LEAF_ISA(PrimitivesDecoration)
};
struct IRGLSLPrimitivesRateDecoration : public IRDecoration
{
IR_LEAF_ISA(GLSLPrimitivesRateDecoration)
};
struct IRMeshOutputRef : public IRInst
{
enum { kOp = kIROp_MeshOutputRef };
IR_LEAF_ISA(MeshOutputRef)
IRInst* getIndex() { return getOperand(1); }
IRInst* getOutputType() { return cast<IRPtrTypeBase>(getFullType())->getValueType(); }
};
/// An attribute that can be attached to another instruction as an operand.
///
/// Attributes serve a similar role to decorations, in that both are ways
/// to attach additional information to an instruction, where the operand
/// of the attribute/decoration identifies the purpose of the additional
/// information.
///
/// The key difference between decorations and attributes is that decorations
/// are stored as children of an instruction (in terms of the ownership
/// hierarchy), while attributes are referenced as operands.
///
/// The key benefit of having attributes be operands is that they must
/// be present at the time an instruction is created, which means that
/// they can affect the conceptual value/identity of an instruction
/// in cases where we deduplicate/hash instructions by value.
///
struct IRAttr : public IRInst
{
IR_PARENT_ISA(Attr);
};
/// An attribute that specifies layout information for a single resource kind.
struct IRLayoutResourceInfoAttr : public IRAttr
{
IR_PARENT_ISA(LayoutResourceInfoAttr);
IRIntLit* getResourceKindInst() { return cast<IRIntLit>(getOperand(0)); }
LayoutResourceKind getResourceKind() { return LayoutResourceKind(getIntVal(getResourceKindInst())); }
};
/// An attribute that specifies offset information for a single resource kind.
///
/// This operation can appear as `varOffset(kind, offset)` or
/// `varOffset(kind, offset, space)`. The latter form is only
/// used when `space` is non-zero.
///
struct IRVarOffsetAttr : public IRLayoutResourceInfoAttr
{
IR_LEAF_ISA(VarOffsetAttr);
IRIntLit* getOffsetInst() { return cast<IRIntLit>(getOperand(1)); }
UInt getOffset() { return UInt(getIntVal(getOffsetInst())); }
IRIntLit* getSpaceInst()
{
if(getOperandCount() > 2)
return cast<IRIntLit>(getOperand(2));
return nullptr;
}
UInt getSpace()
{
if(auto spaceInst = getSpaceInst())
return UInt(getIntVal(spaceInst));
return 0;
}
};
/// An attribute that specifies the error type a function is throwing
struct IRFuncThrowTypeAttr : IRAttr
{
IR_LEAF_ISA(FuncThrowTypeAttr)
IRType* getErrorType() { return (IRType*)getOperand(0); }
};
struct IRNoDiffAttr : IRAttr
{
IR_LEAF_ISA(NoDiffAttr)
};
/// An attribute that specifies size information for a single resource kind.
struct IRTypeSizeAttr : public IRLayoutResourceInfoAttr
{
IR_LEAF_ISA(TypeSizeAttr);
IRIntLit* getSizeInst() { return cast<IRIntLit>(getOperand(1)); }
LayoutSize getSize() { return LayoutSize::fromRaw(LayoutSize::RawValue(getIntVal(getSizeInst()))); }
size_t getFiniteSize() { return getSize().getFiniteValue(); }
};
// Layout
/// Base type for instructions that represent layout information.
///
/// Layout instructions are effectively just meta-data constants.
///
struct IRLayout : IRInst
{
IR_PARENT_ISA(Layout)
};
struct IRVarLayout;
/// An attribute to specify that a layout has another layout attached for "pending" data.
///
/// "Pending" data refers to the parts of a type or variable that
/// couldn't be laid out until the concrete types for existential
/// type slots were filled in. The layout of pending data may not
/// be contiguous with the layout of the original type/variable.
///
struct IRPendingLayoutAttr : IRAttr
{
IR_LEAF_ISA(PendingLayoutAttr);
IRLayout* getLayout() { return cast<IRLayout>(getOperand(0)); }
};
/// Layout information for a type.
///
/// The most important thing this instruction provides is the
/// resource usage (aka "size") of the type for each of the
/// resource kinds it consumes.
///
/// Subtypes of `IRTypeLayout` will include additional type-specific
/// operands or attributes. For example, a type layout for a
/// `struct` type will include offset information for its fields.
///
struct IRTypeLayout : IRLayout
{
IR_PARENT_ISA(TypeLayout);
/// Find the attribute that stores offset information for `kind`.
///
/// Returns null if no attribute is found, indicating that this
/// type does not consume any resources of `kind`.
///
IRTypeSizeAttr* findSizeAttr(LayoutResourceKind kind);
/// Get all the attributes representing size information.
IROperandList<IRTypeSizeAttr> getSizeAttrs();
/// Unwrap any layers of array-ness and return the outer-most non-array type.
IRTypeLayout* unwrapArray();
/// Get the layout for pending data, if present.
IRTypeLayout* getPendingDataTypeLayout();
/// A builder for constructing `IRTypeLayout`s
struct Builder
{
/// Begin building.
///
/// The `irBuilder` will be used to construct the
/// type layout and any additional instructions required.
///
Builder(IRBuilder* irBuilder);
/// Add `size` units of resource `kind` to the resource usage of this type.
void addResourceUsage(
LayoutResourceKind kind,
LayoutSize size);
/// Add the resource usage specified by `sizeAttr`.
void addResourceUsage(IRTypeSizeAttr* sizeAttr);
/// Add all resource usage from `typeLayout`.
void addResourceUsageFrom(IRTypeLayout* typeLayout);
/// Set the (optional) layout for pending data.
void setPendingTypeLayout(
IRTypeLayout* typeLayout)
{
m_pendingTypeLayout = typeLayout;
}
/// Build a type layout according to the information specified so far.
IRTypeLayout* build();
protected:
// The following services are provided so that
// subtypes of `IRTypeLayout` can provide their
// own `Builder` subtypes that construct appropriate
// layouts.
/// Override to customize the opcode of the generated layout.
virtual IROp getOp() { return kIROp_TypeLayoutBase; }
/// Override to add additional operands to the generated layout.
virtual void addOperandsImpl(List<IRInst*>&) {}
/// Override to add additional attributes to the generated layout.
virtual void addAttrsImpl(List<IRInst*>&) {}
/// Use to access the underlying IR builder.
IRBuilder* getIRBuilder() { return m_irBuilder; };
private:
void addOperands(List<IRInst*>&);
void addAttrs(List<IRInst*>& ioOperands);
IRBuilder* m_irBuilder = nullptr;
IRTypeLayout* m_pendingTypeLayout = nullptr;
struct ResInfo
{
LayoutResourceKind kind = LayoutResourceKind::None;
LayoutSize size = 0;
};
ResInfo m_resInfos[SLANG_PARAMETER_CATEGORY_COUNT];
};
};
/// Type layout for parameter groups (constant buffers and parameter blocks)
struct IRParameterGroupTypeLayout : IRTypeLayout
{
private:
typedef IRTypeLayout Super;
public:
IR_LEAF_ISA(ParameterGroupTypeLayout)
IRVarLayout* getContainerVarLayout()
{
return cast<IRVarLayout>(getOperand(0));
}
IRVarLayout* getElementVarLayout()
{
return cast<IRVarLayout>(getOperand(1));
}
// TODO: There shouldn't be a need for the IR to store an "offset" element type layout,
// but there are just enough places that currently use that information so that removing
// it would require some careful refactoring.
//
IRTypeLayout* getOffsetElementTypeLayout()
{
return cast<IRTypeLayout>(getOperand(2));
}
/// Specialized builder for parameter group type layouts.
struct Builder : Super::Builder
{
public:
Builder(IRBuilder* irBuilder)
: Super::Builder(irBuilder)
{}
void setContainerVarLayout(IRVarLayout* varLayout)
{
m_containerVarLayout = varLayout;
}
void setElementVarLayout(IRVarLayout* varLayout)
{
m_elementVarLayout = varLayout;
}
void setOffsetElementTypeLayout(IRTypeLayout* typeLayout)
{
m_offsetElementTypeLayout = typeLayout;
}
IRParameterGroupTypeLayout* build();
protected:
IROp getOp() SLANG_OVERRIDE { return kIROp_ParameterGroupTypeLayout; }
void addOperandsImpl(List<IRInst*>& ioOperands) SLANG_OVERRIDE;
IRVarLayout* m_containerVarLayout;
IRVarLayout* m_elementVarLayout;
IRTypeLayout* m_offsetElementTypeLayout;
};
};
/// Specialized layout information for array types
struct IRArrayTypeLayout : IRTypeLayout
{
typedef IRTypeLayout Super;
IR_LEAF_ISA(ArrayTypeLayout)
IRTypeLayout* getElementTypeLayout()
{
return cast<IRTypeLayout>(getOperand(0));
}
struct Builder : Super::Builder
{
Builder(IRBuilder* irBuilder, IRTypeLayout* elementTypeLayout)
: Super::Builder(irBuilder)
, m_elementTypeLayout(elementTypeLayout)
{}
IRArrayTypeLayout* build()
{
return cast<IRArrayTypeLayout>(Super::Builder::build());
}
protected:
IROp getOp() SLANG_OVERRIDE { return kIROp_ArrayTypeLayout; }
void addOperandsImpl(List<IRInst*>& ioOperands) SLANG_OVERRIDE;
IRTypeLayout* m_elementTypeLayout;
};
};
/// Specialized layout information for stream-output types
struct IRStreamOutputTypeLayout : IRTypeLayout
{
typedef IRTypeLayout Super;
IR_LEAF_ISA(StreamOutputTypeLayout)
IRTypeLayout* getElementTypeLayout()
{
return cast<IRTypeLayout>(getOperand(0));
}
struct Builder : Super::Builder
{
Builder(IRBuilder* irBuilder, IRTypeLayout* elementTypeLayout)
: Super::Builder(irBuilder)
, m_elementTypeLayout(elementTypeLayout)
{}
IRArrayTypeLayout* build()
{
return cast<IRArrayTypeLayout>(Super::Builder::build());
}
protected:
IROp getOp() SLANG_OVERRIDE { return kIROp_StreamOutputTypeLayout; }
void addOperandsImpl(List<IRInst*>& ioOperands) SLANG_OVERRIDE;
IRTypeLayout* m_elementTypeLayout;
};
};
/// Specialized layout information for matrix types
struct IRMatrixTypeLayout : IRTypeLayout
{
typedef IRTypeLayout Super;
IR_LEAF_ISA(MatrixTypeLayout)
MatrixLayoutMode getMode()
{
return MatrixLayoutMode(getIntVal(cast<IRIntLit>(getOperand(0))));
}
struct Builder : Super::Builder
{
Builder(IRBuilder* irBuilder, MatrixLayoutMode mode);
IRMatrixTypeLayout* build()
{
return cast<IRMatrixTypeLayout>(Super::Builder::build());
}
protected:
IROp getOp() SLANG_OVERRIDE { return kIROp_MatrixTypeLayout; }
void addOperandsImpl(List<IRInst*>& ioOperands) SLANG_OVERRIDE;
IRInst* m_modeInst = nullptr;
};
};
/// Attribute that specifies the layout for one field of a structure type.
struct IRStructFieldLayoutAttr : IRAttr
{
IR_LEAF_ISA(StructFieldLayoutAttr)
IRInst* getFieldKey()
{
return getOperand(0);
}
IRVarLayout* getLayout()
{
return cast<IRVarLayout>(getOperand(1));
}
};
/// Specialized layout information for structure types.
struct IRStructTypeLayout : IRTypeLayout
{
IR_LEAF_ISA(StructTypeLayout)
typedef IRTypeLayout Super;
/// Get all of the attributes that represent field layouts.
IROperandList<IRStructFieldLayoutAttr> getFieldLayoutAttrs()
{
return findAttrs<IRStructFieldLayoutAttr>();
}
/// Get the number of fields for which layout information is stored.
UInt getFieldCount()
{
return getFieldLayoutAttrs().getCount();
}
/// Get the layout information for a field by `index`
IRVarLayout* getFieldLayout(UInt index)
{
return getFieldLayoutAttrs()[index]->getLayout();
}
/// Specialized builder for structure type layouts.
struct Builder : Super::Builder
{
Builder(IRBuilder* irBuilder)
: Super::Builder(irBuilder)
{}
void addField(IRInst* key, IRVarLayout* layout)
{
FieldInfo info;
info.key = key;
info.layout = layout;
m_fields.add(info);
}
IRStructTypeLayout* build()
{
return cast<IRStructTypeLayout>(Super::Builder::build());
}
protected:
IROp getOp() SLANG_OVERRIDE { return kIROp_StructTypeLayout; }
void addAttrsImpl(List<IRInst*>& ioOperands) SLANG_OVERRIDE;
struct FieldInfo
{
IRInst* key;
IRVarLayout* layout;
};
List<FieldInfo> m_fields;
};
};
/// Attribute that represents the layout for one case of a union type
struct IRCaseTypeLayoutAttr : IRAttr
{
IR_LEAF_ISA(CaseTypeLayoutAttr);
IRTypeLayout* getTypeLayout()
{
return cast<IRTypeLayout>(getOperand(0));
}
};
/// Specialized layout information for tagged union types
struct IRTaggedUnionTypeLayout : IRTypeLayout
{
typedef IRTypeLayout Super;
IR_LEAF_ISA(TaggedUnionTypeLayout)
/// Get the (byte) offset of the tagged union's tag (aka "discriminator") field
LayoutSize getTagOffset()
{
return LayoutSize::fromRaw(LayoutSize::RawValue(getIntVal(cast<IRIntLit>(getOperand(0)))));
}
/// Get all the attributes representing layouts for the difference cases
IROperandList<IRCaseTypeLayoutAttr> getCaseTypeLayoutAttrs()
{
return findAttrs<IRCaseTypeLayoutAttr>();
}
/// Get the number of cases for which layout information is stored
UInt getCaseCount()
{
return getCaseTypeLayoutAttrs().getCount();
}
/// Get the layout information for the case at the given `index`
IRTypeLayout* getCaseTypeLayout(UInt index)
{
return getCaseTypeLayoutAttrs()[index]->getTypeLayout();
}
/// Specialized builder for tagged union type layouts
struct Builder : Super::Builder
{
Builder(IRBuilder* irBuilder, LayoutSize tagOffset);
void addCaseTypeLayout(IRTypeLayout* typeLayout);
IRTaggedUnionTypeLayout* build()
{
return cast<IRTaggedUnionTypeLayout>(Super::Builder::build());
}
protected:
IROp getOp() SLANG_OVERRIDE { return kIROp_TaggedUnionTypeLayout; }
void addOperandsImpl(List<IRInst*>& ioOperands) SLANG_OVERRIDE;
void addAttrsImpl(List<IRInst*>& ioOperands) SLANG_OVERRIDE;
IRInst* m_tagOffset = nullptr;
List<IRAttr*> m_caseTypeLayoutAttrs;
};
};
/// Type layout for an existential/interface type.
struct IRExistentialTypeLayout : IRTypeLayout
{
typedef IRTypeLayout Super;
IR_LEAF_ISA(ExistentialTypeLayout)
struct Builder : Super::Builder
{
Builder(IRBuilder* irBuilder)
: Super::Builder(irBuilder)
{}
IRExistentialTypeLayout* build()
{
return cast<IRExistentialTypeLayout>(Super::Builder::build());
}
protected:
IROp getOp() SLANG_OVERRIDE { return kIROp_ExistentialTypeLayout; }
};
};
/// Layout information for an entry point
struct IREntryPointLayout : IRLayout
{
IR_LEAF_ISA(EntryPointLayout)
/// Get the layout information for the entry point parameters.
///
/// The parameters layout will either be a structure type layout
/// with one field per parameter, or a parameter group type
/// layout wrapping such a structure, if the entry point parameters
/// needed to be allocated into a constant buffer.
///
IRVarLayout* getParamsLayout()
{
return cast<IRVarLayout>(getOperand(0));
}
/// Get the layout information for the entry point result.
///
/// This represents the return value of the entry point.
/// Note that it does *not* represent all of the entry
/// point outputs, because the parameter list may also
/// contain `out` or `inout` parameters.
///
IRVarLayout* getResultLayout()
{
return cast<IRVarLayout>(getOperand(1));
}
};
/// Given an entry-point layout, extract the layout for the parameters struct.
IRStructTypeLayout* getScopeStructLayout(IREntryPointLayout* scopeLayout);
/// Attribute that associates a variable layout with a known stage.
struct IRStageAttr : IRAttr
{
IR_LEAF_ISA(StageAttr);
IRIntLit* getStageOperand() { return cast<IRIntLit>(getOperand(0)); }
Stage getStage() { return Stage(getIntVal(getStageOperand())); }
};
/// Base type for attributes that associate a variable layout with a semantic name and index.
struct IRSemanticAttr : IRAttr
{
IR_PARENT_ISA(SemanticAttr);
IRStringLit* getNameOperand() { return cast<IRStringLit>(getOperand(0)); }
UnownedStringSlice getName() { return getNameOperand()->getStringSlice(); }
IRIntLit* getIndexOperand() { return cast<IRIntLit>(getOperand(1)); }
UInt getIndex() { return UInt(getIntVal(getIndexOperand())); }
};
/// Attribute that associates a variable with a system-value semantic name and index
struct IRSystemValueSemanticAttr : IRSemanticAttr
{
IR_LEAF_ISA(SystemValueSemanticAttr);
};
/// Attribute that associates a variable with a user-defined semantic name and index
struct IRUserSemanticAttr : IRSemanticAttr
{
IR_LEAF_ISA(UserSemanticAttr);
};
/// Layout infromation for a single parameter/field
struct IRVarLayout : IRLayout
{
IR_LEAF_ISA(VarLayout)
/// Get the type layout information for this variable
IRTypeLayout* getTypeLayout() { return cast<IRTypeLayout>(getOperand(0)); }
/// Get all the attributes representing resource-kind-specific offsets
IROperandList<IRVarOffsetAttr> getOffsetAttrs();
/// Find the offset information (if present) for the given resource `kind`
IRVarOffsetAttr* findOffsetAttr(LayoutResourceKind kind);
/// Does this variable use any resources of the given `kind`?
bool usesResourceKind(LayoutResourceKind kind);
/// Get the fixed/known stage that this variable is associated with.
///
/// This will be a specific stage for entry-point parameters, but
/// will be `Stage::Unknown` for any parameter that is not bound
/// solely to one entry point.
///
Stage getStage();
/// Find the system-value semantic attribute for this variable, if any.
IRSystemValueSemanticAttr* findSystemValueSemanticAttr();
/// Get the (optional) layout for any "pending" data assocaited with this variable.
IRVarLayout* getPendingVarLayout();
/// Builder for construction `IRVarLayout`s in a stateful fashion
struct Builder
{
/// Begin building a variable layout with the given `typeLayout`
///
/// The result layout and any instructions needed along the way
/// will be allocated with `irBuilder`.
///
Builder(
IRBuilder* irBuilder,
IRTypeLayout* typeLayout);
/// Represents resource-kind-specific offset information
struct ResInfo
{
LayoutResourceKind kind = LayoutResourceKind::None;
UInt offset = 0;
UInt space = 0;
};
/// Has any resource usage/offset been registered for the given resource `kind`?
bool usesResourceKind(LayoutResourceKind kind);
/// Either fetch or add a `ResInfo` record for `kind` and return it
ResInfo* findOrAddResourceInfo(LayoutResourceKind kind);
/// Set the (optional) variable layout for pending data.
void setPendingVarLayout(IRVarLayout* varLayout)
{
m_pendingVarLayout = varLayout;
}
/// Set the (optional) system-valeu semantic for this variable.
void setSystemValueSemantic(String const& name, UInt index);
/// Set the (optional) user-defined semantic for this variable.
void setUserSemantic(String const& name, UInt index);
/// Set the (optional) known stage for this variable.
void setStage(Stage stage);
/// Clone all of the layout information from the `other` layout, except for offsets.
///
/// This is convenience when one wants to build a variable layout "like that other one, but..."
void cloneEverythingButOffsetsFrom(
IRVarLayout* other);
/// Build a variable layout using the current state that has been set.
IRVarLayout* build();
private:
IRBuilder* m_irBuilder;
IRBuilder* getIRBuilder() { return m_irBuilder; };
IRTypeLayout* m_typeLayout = nullptr;
IRVarLayout* m_pendingVarLayout = nullptr;
IRSystemValueSemanticAttr* m_systemValueSemantic = nullptr;
IRUserSemanticAttr* m_userSemantic = nullptr;
IRStageAttr* m_stageAttr = nullptr;
ResInfo m_resInfos[SLANG_PARAMETER_CATEGORY_COUNT];
};
};
bool isVaryingResourceKind(LayoutResourceKind kind);
bool isVaryingParameter(IRTypeLayout* typeLayout);
bool isVaryingParameter(IRVarLayout* varLayout);
/// Associate layout information with an instruction.
///
/// This decoration is used in three main ways:
///
/// * To attach an `IRVarLayout` to an `IRGlobalParam` or entry-point `IRParam` representing a shader parameter
/// * To attach an `IREntryPointLayout` to an `IRFunc` representing an entry point
/// * To attach an `IRTaggedUnionTypeLayout` to an `IRTaggedUnionType`
///
struct IRLayoutDecoration : IRDecoration
{
enum { kOp = kIROp_LayoutDecoration };
IR_LEAF_ISA(LayoutDecoration)
/// Get the layout that is being attached to the parent instruction
IRLayout* getLayout() { return cast<IRLayout>(getOperand(0)); }
};
//
struct IRCall : IRInst
{
IR_LEAF_ISA(Call)
IRInst* getCallee() { return getOperand(0); }
UInt getArgCount() { return getOperandCount() - 1; }
IRUse* getArgs() { return getOperands() + 1; }
IRInst* getArg(UInt index) { return getOperand(index + 1); }
};
struct IRLoad : IRInst
{
IRUse ptr;
IR_LEAF_ISA(Load)
IRInst* getPtr() { return ptr.get(); }
};
struct IRStore : IRInst
{
IRUse ptr;
IRUse val;
IR_LEAF_ISA(Store)
IRInst* getPtr() { return ptr.get(); }
IRInst* getVal() { return val.get(); }
};
struct IRFieldExtract : IRInst
{
IRUse base;
IRUse field;
IRInst* getBase() { return base.get(); }
IRInst* getField() { return field.get(); }
IR_LEAF_ISA(FieldExtract)
};
struct IRFieldAddress : IRInst
{
IRUse base;
IRUse field;
IRInst* getBase() { return base.get(); }
IRInst* getField() { return field.get(); }
IR_LEAF_ISA(FieldAddress)
};
struct IRGetElement : IRInst
{
IR_LEAF_ISA(GetElement);
IRInst* getBase() { return getOperand(0); }
IRInst* getIndex() { return getOperand(1); }
};
struct IRGetElementPtr : IRInst
{
IR_LEAF_ISA(GetElementPtr);
IRInst* getBase() { return getOperand(0); }
IRInst* getIndex() { return getOperand(1); }
};
struct IRLoadReverseGradient : IRInst
{
IR_LEAF_ISA(LoadReverseGradient)
IRInst* getValue() { return getOperand(0); }
};
struct IRReverseGradientDiffPairRef : IRInst
{
IR_LEAF_ISA(ReverseGradientDiffPairRef)
IRInst* getPrimal() { return getOperand(0); }
IRInst* getDiff() { return getOperand(1); }
};
struct IRPrimalParamRef : IRInst
{
IR_LEAF_ISA(PrimalParamRef)
IRInst* getReferencedParam() { return getOperand(0); }
};
struct IRDiffParamRef : IRInst
{
IR_LEAF_ISA(DiffParamRef)
IRInst* getReferencedParam() { return getOperand(0); }
};
struct IRGetNativePtr : IRInst
{
IR_LEAF_ISA(GetNativePtr);
IRInst* getElementType() { return getOperand(0); }
};
struct IRGetManagedPtrWriteRef : IRInst
{
IR_LEAF_ISA(GetManagedPtrWriteRef);
IRInst* getPtrToManagedPtr() { return getOperand(0); }
};
struct IRGetAddress : IRInst
{
IR_LEAF_ISA(GetAddr);
};
struct IRImageSubscript : IRInst
{
IR_LEAF_ISA(ImageSubscript);
IRInst* getImage() { return getOperand(0); }
IRInst* getCoord() { return getOperand(1); }
};
struct IRImageLoad : IRInst
{
IR_LEAF_ISA(ImageLoad);
IRInst* getImage() { return getOperand(0); }
IRInst* getCoord() { return getOperand(1); }
};
struct IRImageStore : IRInst
{
IR_LEAF_ISA(ImageStore);
IRInst* getImage() { return getOperand(0); }
IRInst* getCoord() { return getOperand(1); }
IRInst* getValue() { return getOperand(2); }
};
// Terminators
struct IRReturn : IRTerminatorInst
{
IR_LEAF_ISA(Return);
IRInst* getVal() { return getOperand(0); }
};
struct IRDiscard : IRTerminatorInst
{};
// Signals that this point in the code should be unreachable.
// We can/should emit a dataflow error if we can ever determine
// that a block ending in one of these can actually be
// executed.
struct IRUnreachable : IRTerminatorInst
{
IR_PARENT_ISA(Unreachable);
};
struct IRMissingReturn : IRUnreachable
{
IR_LEAF_ISA(MissingReturn);
};
struct IRBlock;
struct IRUnconditionalBranch : IRTerminatorInst
{
IRUse block;
IRBlock* getTargetBlock() { return (IRBlock*)block.get(); }
UInt getArgCount();
IRUse* getArgs();
IRInst* getArg(UInt index);
void removeArgument(UInt index);
IR_PARENT_ISA(UnconditionalBranch);
};
// Special cases of unconditional branch, to handle
// structured control flow:
struct IRBreak : IRUnconditionalBranch {};
struct IRContinue : IRUnconditionalBranch {};
// The start of a loop is a special control-flow
// instruction, that records relevant information
// about the loop structure:
struct IRLoop : IRUnconditionalBranch
{
IR_LEAF_ISA(loop);
// The next block after the loop, which
// is where we expect control flow to
// re-converge, and also where a
// `break` will target.
IRUse breakBlock;
// The block where control flow will go
// on a `continue`.
IRUse continueBlock;
IRBlock* getBreakBlock() { return (IRBlock*)breakBlock.get(); }
IRBlock* getContinueBlock() { return (IRBlock*)continueBlock.get(); }
};
struct IRConditionalBranch : IRTerminatorInst
{
IR_PARENT_ISA(ConditionalBranch)
IRUse condition;
IRUse trueBlock;
IRUse falseBlock;
IRInst* getCondition() { return condition.get(); }
IRBlock* getTrueBlock() { return (IRBlock*)trueBlock.get(); }
IRBlock* getFalseBlock() { return (IRBlock*)falseBlock.get(); }
};
// A conditional branch that represents a two-sided `if`:
//
// if( <condition> ) { <trueBlock> }
// else { <falseBlock> }
// <afterBlock>
//
struct IRIfElse : IRConditionalBranch
{
IRUse afterBlock;
IRBlock* getAfterBlock() { return (IRBlock*)afterBlock.get(); }
};
// A multi-way branch that represents a source-level `switch`
struct IRSwitch : IRTerminatorInst
{
IR_LEAF_ISA(Switch);
IRUse condition;
IRUse breakLabel;
IRUse defaultLabel;
IRInst* getCondition() { return condition.get(); }
IRBlock* getBreakLabel() { return (IRBlock*) breakLabel.get(); }
IRBlock* getDefaultLabel() { return (IRBlock*) defaultLabel.get(); }
// remaining args are: caseVal, caseLabel, ...
UInt getCaseCount() { return (getOperandCount() - 3) / 2; }
IRInst* getCaseValue(UInt index) { return getOperand(3 + index*2 + 0); }
IRBlock* getCaseLabel(UInt index) { return (IRBlock*) getOperand(3 + index*2 + 1); }
};
struct IRThrow : IRTerminatorInst
{
IR_LEAF_ISA(Throw);
IRInst* getValue() { return getOperand(0); }
};
struct IRTryCall : IRTerminatorInst
{
IR_LEAF_ISA(TryCall);
IRBlock* getSuccessBlock() { return cast<IRBlock>(getOperand(0)); }
IRBlock* getFailureBlock() { return cast<IRBlock>(getOperand(1)); }
IRInst* getCallee() { return getOperand(2); }
UInt getArgCount() { return getOperandCount() - 3; }
IRUse* getArgs() { return getOperands() + 3; }
IRInst* getArg(UInt index) { return getOperand(index + 3); }
};
struct IRSwizzle : IRInst
{
IRUse base;
IRInst* getBase() { return base.get(); }
UInt getElementCount()
{
return getOperandCount() - 1;
}
IRInst* getElementIndex(UInt index)
{
return getOperand(index + 1);
}
};
struct IRSwizzleSet : IRInst
{
IRUse base;
IRUse source;
IRInst* getBase() { return base.get(); }
IRInst* getSource() { return source.get(); }
UInt getElementCount()
{
return getOperandCount() - 2;
}
IRInst* getElementIndex(UInt index)
{
return getOperand(index + 2);
}
};
struct IRSwizzledStore : IRInst
{
IRInst* getDest() { return getOperand(0); }
IRInst* getSource() { return getOperand(1); }
UInt getElementCount()
{
return getOperandCount() - 2;
}
IRInst* getElementIndex(UInt index)
{
return getOperand(index + 2);
}
IR_LEAF_ISA(SwizzledStore)
};
struct IRPatchConstantFuncDecoration : IRDecoration
{
enum { kOp = kIROp_PatchConstantFuncDecoration };
IR_LEAF_ISA(PatchConstantFuncDecoration)
IRInst* getFunc() { return getOperand(0); }
};
// An IR `var` instruction conceptually represents
// a stack allocation of some memory.
struct IRVar : IRInst
{
IRPtrType* getDataType()
{
return cast<IRPtrType>(IRInst::getDataType());
}
static bool isaImpl(IROp op) { return op == kIROp_Var; }
};
/// @brief A global variable.
///
/// Represents a global variable in the IR.
/// If the variable has an initializer, then
/// it is represented by the code in the basic
/// blocks nested inside this value.
struct IRGlobalVar : IRGlobalValueWithCode
{
IR_LEAF_ISA(GlobalVar)
IRPtrType* getDataType()
{
return cast<IRPtrType>(IRInst::getDataType());
}
};
/// @brief A global shader parameter.
///
/// Represents a uniform (as opposed to varying) shader parameter
/// passed at the global scope (entry-point `uniform` parameters
/// are encoded as ordinary function parameters.
///
/// Note that an `IRGlobalParam` directly represents the value of
/// the parameter, unlike an `IRGlobalVar`, which represents the
/// *address* of the value. As a result, global parameters are
/// immutable, and subject to various SSA simplifications that
/// do not work for global variables.
///
struct IRGlobalParam : IRInst
{
IR_LEAF_ISA(GlobalParam)
};
/// @brief A global constnat.
///
/// Represents a global constant that may have a name and linkage.
/// If it has an operand, then this operand is the value of
/// the constants. If there is no operand, then the instruction
/// represents an "extern" constant that will be defined in another
/// module, and which is thus expected to have linkage.
///
struct IRGlobalConstant : IRInst
{
IR_LEAF_ISA(GlobalConstant);
/// Get the value of this global constant, or null if the value is not known.
IRInst* getValue()
{
return getOperandCount() != 0 ? getOperand(0) : nullptr;
}
};
// An entry in a witness table (see below)
struct IRWitnessTableEntry : IRInst
{
// The AST-level requirement
IRUse requirementKey;
// The IR-level value that satisfies the requirement
IRUse satisfyingVal;
IRInst* getRequirementKey() { return getOperand(0); }
IRInst* getSatisfyingVal() { return getOperand(1); }
IR_LEAF_ISA(WitnessTableEntry)
};
// A witness table is a global value that stores
// information about how a type conforms to some
// interface. It basically takes the form of a
// map from the required members of the interface
// to the IR values that satisfy those requirements.
struct IRWitnessTable : IRInst
{
IRInstList<IRWitnessTableEntry> getEntries()
{
return IRInstList<IRWitnessTableEntry>(getChildren());
}
IRInst* getConformanceType()
{
return cast<IRWitnessTableType>(getDataType())->getConformanceType();
}
IRType* getConcreteType()
{
return (IRType*) getOperand(0);
}
void setConcreteType(IRType* t)
{
return setOperand(0, t);
}
IR_LEAF_ISA(WitnessTable)
};
/// Represents an RTTI object.
/// An IRRTTIObject has 1 operand, specifying the type
/// this RTTI object provides info for.
/// All type info are encapsualted as `IRRTTI*Decoration`s attached
/// to the object.
struct IRRTTIObject : IRInst
{
IR_LEAF_ISA(RTTIObject)
};
// An instruction that yields an undefined value.
//
// Note that we make this an instruction rather than a value,
// so that we will be able to identify a variable that is
// used when undefined.
struct IRUndefined : IRInst
{
};
// A global-scope generic parameter (a type parameter, a
// constraint parameter, etc.)
struct IRGlobalGenericParam : IRInst
{
IR_LEAF_ISA(GlobalGenericParam)
};
// An instruction that binds a global generic parameter
// to a particular value.
struct IRBindGlobalGenericParam : IRInst
{
IRGlobalGenericParam* getParam() { return cast<IRGlobalGenericParam>(getOperand(0)); }
IRInst* getVal() { return getOperand(1); }
IR_LEAF_ISA(BindGlobalGenericParam)
};
// An Instruction that creates a tuple value.
struct IRMakeTuple : IRInst
{
IR_LEAF_ISA(MakeTuple)
};
struct IRGetTupleElement : IRInst
{
IR_LEAF_ISA(GetTupleElement)
IRInst* getTuple() { return getOperand(0); }
IRInst* getElementIndex() { return getOperand(1); }
};
struct IRGetTargetTupleElement : IRInst
{
IR_LEAF_ISA(GetTargetTupleElement)
IRInst* getTuple() { return getOperand(0); }
IRInst* getElementIndex() { return getOperand(1); }
};
// An Instruction that creates a differential pair value from a
// primal and differential.
struct IRMakeDifferentialPairBase : IRInst
{
IR_PARENT_ISA(MakeDifferentialPairBase)
IRInst* getPrimalValue() { return getOperand(0); }
IRInst* getDifferentialValue() { return getOperand(1); }
};
struct IRMakeDifferentialPair : IRMakeDifferentialPairBase
{
IR_LEAF_ISA(MakeDifferentialPair)
};
struct IRMakeDifferentialPairUserCode : IRMakeDifferentialPairBase
{
IR_LEAF_ISA(MakeDifferentialPairUserCode)
};
struct IRDifferentialPairGetDifferentialBase : IRInst
{
IR_PARENT_ISA(DifferentialPairGetDifferentialBase)
IRInst* getBase() { return getOperand(0); }
};
struct IRDifferentialPairGetDifferential : IRDifferentialPairGetDifferentialBase
{
IR_LEAF_ISA(DifferentialPairGetDifferential)
};
struct IRDifferentialPairGetDifferentialUserCode : IRDifferentialPairGetDifferentialBase
{
IR_LEAF_ISA(DifferentialPairGetDifferentialUserCode)
};
struct IRDifferentialPairGetPrimalBase : IRInst
{
IR_PARENT_ISA(DifferentialPairGetPrimalBase)
IRInst* getBase() { return getOperand(0); }
};
struct IRDifferentialPairGetPrimal : IRDifferentialPairGetPrimalBase
{
IR_LEAF_ISA(DifferentialPairGetPrimal)
};
struct IRDifferentialPairGetPrimalUserCode : IRDifferentialPairGetPrimalBase
{
IR_LEAF_ISA(DifferentialPairGetPrimalUserCode)
};
struct IRDetachDerivative : IRInst
{
IR_LEAF_ISA(DetachDerivative)
IRInst* getBase() { return getOperand(0); }
};
struct IRUpdateElement : IRInst
{
IR_LEAF_ISA(UpdateElement)
IRInst* getOldValue() { return getOperand(0); }
IRInst* getElementValue() { return getOperand(1); }
IRInst* getAccessKey(UInt index) { return getOperand(2 + index); }
UInt getAccessKeyCount() { return getOperandCount() - 2; }
List<IRInst*> getAccessChain()
{
List<IRInst*> result;
for (UInt i = 0; i < getAccessKeyCount(); i++)
result.add(getAccessKey(i));
return result;
}
};
// Constructs an `Result<T,E>` value from an error code.
struct IRMakeResultError : IRInst
{
IR_LEAF_ISA(MakeResultError)
IRInst* getErrorValue() { return getOperand(0); }
};
// Constructs an `Result<T,E>` value from an valid value.
struct IRMakeResultValue : IRInst
{
IR_LEAF_ISA(MakeResultValue)
IRInst* getValue() { return getOperand(0); }
};
// Determines if a `Result` value represents an error.
struct IRIsResultError : IRInst
{
IR_LEAF_ISA(IsResultError)
IRInst* getResultOperand() { return getOperand(0); }
};
// Extract the value from a `Result`.
struct IRGetResultValue : IRInst
{
IR_LEAF_ISA(GetResultValue)
IRInst* getResultOperand() { return getOperand(0); }
};
// Extract the error code from a `Result`.
struct IRGetResultError : IRInst
{
IR_LEAF_ISA(GetResultError)
IRInst* getResultOperand() { return getOperand(0); }
};
struct IROptionalHasValue : IRInst
{
IR_LEAF_ISA(OptionalHasValue)
IRInst* getOptionalOperand() { return getOperand(0); }
};
struct IRGetOptionalValue : IRInst
{
IR_LEAF_ISA(GetOptionalValue)
IRInst* getOptionalOperand() { return getOperand(0); }
};
struct IRMakeOptionalValue : IRInst
{
IR_LEAF_ISA(MakeOptionalValue)
IRInst* getValue() { return getOperand(0); }
};
struct IRMakeOptionalNone : IRInst
{
IR_LEAF_ISA(MakeOptionalNone)
IRInst* getDefaultValue() { return getOperand(0); }
};
/// An instruction that packs a concrete value into an existential-type "box"
struct IRMakeExistential : IRInst
{
IRInst* getWrappedValue() { return getOperand(0); }
IRInst* getWitnessTable() { return getOperand(1); }
IR_LEAF_ISA(MakeExistential)
};
struct IRMakeExistentialWithRTTI : IRInst
{
IRInst* getWrappedValue() { return getOperand(0); }
IRInst* getWitnessTable() { return getOperand(1); }
IRInst* getRTTI() { return getOperand(2); }
IR_LEAF_ISA(MakeExistentialWithRTTI)
};
struct IRCreateExistentialObject : IRInst
{
IRInst* getTypeID() { return getOperand(0); }
IRInst* getValue() { return getOperand(1); }
IR_LEAF_ISA(CreateExistentialObject)
};
/// Generalizes `IRMakeExistential` by allowing a type with existential sub-fields to be boxed
struct IRWrapExistential : IRInst
{
IRInst* getWrappedValue() { return getOperand(0); }
UInt getSlotOperandCount() { return getOperandCount() - 1; }
IRInst* getSlotOperand(UInt index) { return getOperand(index + 1); }
IRUse* getSlotOperands() { return getOperands() + 1; }
IR_LEAF_ISA(WrapExistential)
};
struct IRGetValueFromBoundInterface : IRInst
{
IR_LEAF_ISA(GetValueFromBoundInterface);
};
struct IRExtractExistentialValue : IRInst
{
IR_LEAF_ISA(ExtractExistentialValue);
};
struct IRExtractExistentialType : IRInst
{
IR_LEAF_ISA(ExtractExistentialType);
};
struct IRExtractExistentialWitnessTable : IRInst
{
IR_LEAF_ISA(ExtractExistentialWitnessTable);
};
/* Base class for instructions that track liveness */
struct IRLiveRangeMarker : IRInst
{
IR_PARENT_ISA(LiveRangeMarker)
// TODO(JS): It might be useful to track how many bytes are live in the item referenced.
// It's not entirely clear how that will work across different targets, or even what such a
// size means on some targets.
//
// Here we assume the size is the size of the type being referenced (whatever that means on a target)
//
// Potentially we could have a count, for defining (say) a range of an array. It's not clear this is
// needed, so we just have the item referenced.
/// The referenced item whose liveness starts after this instruction
IRInst* getReferenced() { return getOperand(0); }
};
/// Identifies then the item references starts being live.
struct IRLiveRangeStart : IRLiveRangeMarker
{
IR_LEAF_ISA(LiveRangeStart);
};
struct IRIsType : IRInst
{
IR_LEAF_ISA(IsType);
IRInst* getValue() { return getOperand(0); }
IRInst* getValueWitness() { return getOperand(1); }
IRInst* getTypeOperand() { return getOperand(2); }
IRInst* getTargetWitness() { return getOperand(3); }
};
/// Demarks where the referenced item is no longer live, optimimally (although not
/// necessarily) at the previous instruction.
///
/// There *can* be acceses to the referenced item after the end, if those accesses
/// can never be seen. For example if there is a store, without any subsequent loads,
/// the store will never be seen (by a load) and so can be ignored.
///
/// In general there can be one or more 'ends' for every start.
struct IRLiveRangeEnd : IRLiveRangeMarker
{
IR_LEAF_ISA(LiveRangeEnd);
};
struct IRBuilderSourceLocRAII;
struct IRBuilder
{
private:
/// Deduplication context from the module.
IRDeduplicationContext* m_dedupContext = nullptr;
IRModule* m_module = nullptr;
/// Default location for inserting new instructions as they are emitted
IRInsertLoc m_insertLoc;
/// Information that controls how source locations are associatd with instructions that get emitted
IRBuilderSourceLocRAII* m_sourceLocInfo = nullptr;
public:
IRBuilder()
{}
explicit IRBuilder(IRModule* module)
: m_module(module)
, m_dedupContext(module->getDeduplicationContext())
{}
explicit IRBuilder(IRInst* inst)
: m_module(inst->getModule())
, m_dedupContext(inst->getModule()->getDeduplicationContext())
{}
Session* getSession() const
{
return m_module->getSession();
}
IRModule* getModule() const
{
return m_module;
}
IRInsertLoc const& getInsertLoc() const { return m_insertLoc; }
void setInsertLoc(IRInsertLoc const& loc) { m_insertLoc = loc; }
// Get the current basic block we are inserting into (if any)
IRBlock* getBlock() { return m_insertLoc.getBlock(); }
// Get the current function (or other value with code)
// that we are inserting into (if any).
IRGlobalValueWithCode* getFunc() { return m_insertLoc.getFunc(); }
void setInsertInto(IRInst* insertInto) { setInsertLoc(IRInsertLoc::atEnd(insertInto)); }
void setInsertBefore(IRInst* insertBefore) { setInsertLoc(IRInsertLoc::before(insertBefore)); }
// TODO: Ellie, contrary to IRInsertLoc::after, this inserts instructions in the order they are emitted, should it have a better name (setInsertBeforeNext)?
void setInsertAfter(IRInst* insertAfter);
void setInsertInto(IRModule* module) { setInsertInto(module->getModuleInst()); }
IRBuilderSourceLocRAII* getSourceLocInfo() const { return m_sourceLocInfo; }
void setSourceLocInfo(IRBuilderSourceLocRAII* sourceLocInfo) { m_sourceLocInfo = sourceLocInfo; }
//
// Low-level interface for instruction creation/insertion.
//
/// Either find or create an `IRConstant` that matches the value of `keyInst`.
///
/// This operation will re-use an existing constant with the same type and
/// value if one can be found (currently identified through the `SharedIRBuilder`).
/// Otherwise it will create a new `IRConstant` with the given value and register it.
///
IRConstant* _findOrEmitConstant(
IRConstant& keyInst);
/// Implements a special case of inst creation (intended only for calling from `_createInst`)
/// that returns an matching existing hoistable inst if it exists, otherwise it creates the inst and
/// add it to the global numbering map.
IRInst* _findOrEmitHoistableInst(
IRType* type,
IROp op,
Int fixedArgCount,
IRInst* const* fixedArgs,
Int varArgListCount,
Int const* listArgCounts,
IRInst* const* const* listArgs);
/// Create a new instruction with the given `type` and `op`, with an allocated
/// size of at least `minSizeInBytes`, and with its operand list initialized
/// from the provided lists of "fixed" and "variable" operands.
///
/// The `fixedArgs` array must contain `fixedArgCount` operands, and will be
/// the initial operands in the operand list of the instruction.
///
/// After the fixed arguments, the instruction may have zero or more additional
/// lists of "variable" operands, which are all concatenated. The total number
/// of such additional lists is given by `varArgsListCount`. The number of
/// operands in list `i` is given by `listArgCounts[i]`, and the arguments in
/// list `i` are pointed to by `listArgs[i]`.
///
/// The allocation for the instruction created will be at least `minSizeInBytes`,
/// but may be larger if the total number of operands provided implies a larger
/// size.
///
/// Note: This is an extremely low-level operation and clients of an `IRBuilder`
/// should not be using it when other options are available. This is also where
/// all insts creation are bottlenecked through.
///
IRInst* _createInst(
size_t minSizeInBytes,
IRType* type,
IROp op,
Int fixedArgCount,
IRInst* const* fixedArgs,
Int varArgListCount,
Int const* listArgCounts,
IRInst* const* const* listArgs);
/// Create a new instruction with the given `type` and `op`, with an allocated
/// size of at least `minSizeInBytes`, and with zero operands.
///
IRInst* _createInst(
size_t minSizeInBytes,
IRType* type,
IROp op)
{
return _createInst(minSizeInBytes, type, op, 0, nullptr, 0, nullptr, nullptr);
}
/// Attempt to attach a useful source location to `inst`.
///
/// This operation looks at the source location information that has been
/// attached to the builder. If it finds a valid source location, it will
/// attach that location to `inst`.
///
void _maybeSetSourceLoc(
IRInst* inst);
//
void addInst(IRInst* inst);
// Replace the operand of a potentially hoistable inst.
// If the hoistable inst become duplicate of an existing inst,
// all uses of the original user will be replaced with the existing inst.
// The function returns the new user after any potential updates.
IRInst* replaceOperand(IRUse* use, IRInst* newValue);
IRInst* getBoolValue(bool value);
IRInst* getIntValue(IRType* type, IRIntegerValue value);
IRInst* getFloatValue(IRType* type, IRFloatingPointValue value);
IRStringLit* getStringValue(const UnownedStringSlice& slice);
IRPtrLit* _getPtrValue(void* ptr);
IRPtrLit* getNullPtrValue(IRType* type);
IRPtrLit* getNullVoidPtrValue() { return getNullPtrValue(getPtrType(getVoidType())); }
IRVoidLit* getVoidValue();
IRInst* getCapabilityValue(CapabilitySet const& caps);
IRBasicType* getBasicType(BaseType baseType);
IRBasicType* getVoidType();
IRBasicType* getBoolType();
IRBasicType* getIntType();
IRBasicType* getUIntType();
IRBasicType* getUInt64Type();
IRBasicType* getCharType();
IRStringType* getStringType();
IRNativeStringType* getNativeStringType();
IRNativePtrType* getNativePtrType(IRType* valueType);
IRType* getCapabilitySetType();
IRAssociatedType* getAssociatedType(ArrayView<IRInterfaceType*> constraintTypes);
IRThisType* getThisType(IRInterfaceType* interfaceType);
IRRawPointerType* getRawPointerType();
IRRTTIPointerType* getRTTIPointerType(IRInst* rttiPtr);
IRRTTIType* getRTTIType();
IRRTTIHandleType* getRTTIHandleType();
IRAnyValueType* getAnyValueType(IRIntegerValue size);
IRAnyValueType* getAnyValueType(IRInst* size);
IRDynamicType* getDynamicType();
IRTargetTupleType* getTargetTupleType(UInt count, IRType* const* types);
IRTupleType* getTupleType(UInt count, IRType* const* types);
IRTupleType* getTupleType(List<IRType*> const& types)
{
return getTupleType(types.getCount(), types.getBuffer());
}
IRTupleType* getTupleType(IRType* type0, IRType* type1);
IRTupleType* getTupleType(IRType* type0, IRType* type1, IRType* type2);
IRTupleType* getTupleType(IRType* type0, IRType* type1, IRType* type2, IRType* type3);
IRResultType* getResultType(IRType* valueType, IRType* errorType);
IROptionalType* getOptionalType(IRType* valueType);
IRBasicBlockType* getBasicBlockType();
IRWitnessTableType* getWitnessTableType(IRType* baseType);
IRWitnessTableIDType* getWitnessTableIDType(IRType* baseType);
IRType* getTypeType() { return getType(IROp::kIROp_TypeType); }
IRType* getKeyType() { return nullptr; }
IRTypeKind* getTypeKind();
IRGenericKind* getGenericKind();
IRPtrType* getPtrType(IRType* valueType);
IROutType* getOutType(IRType* valueType);
IRInOutType* getInOutType(IRType* valueType);
IRRefType* getRefType(IRType* valueType);
IRPtrTypeBase* getPtrType(IROp op, IRType* valueType);
IRPtrType* getPtrType(IROp op, IRType* valueType, IRIntegerValue addressSpace);
IRComPtrType* getComPtrType(IRType* valueType);
/// Get a 'SPIRV literal'
IRSPIRVLiteralType* getSPIRVLiteralType(IRType* type);
IRArrayTypeBase* getArrayTypeBase(
IROp op,
IRType* elementType,
IRInst* elementCount);
IRArrayType* getArrayType(
IRType* elementType,
IRInst* elementCount);
IRUnsizedArrayType* getUnsizedArrayType(
IRType* elementType);
IRVectorType* getVectorType(
IRType* elementType,
IRInst* elementCount);
IRMatrixType* getMatrixType(
IRType* elementType,
IRInst* rowCount,
IRInst* columnCount);
IRArrayListType* getArrayListType(IRType* elementType);
IRTensorViewType* getTensorViewType(IRType* elementType);
IRTorchTensorType* getTorchTensorType();
IRDifferentialPairType* getDifferentialPairType(
IRType* valueType,
IRInst* witnessTable);
IRDifferentialPairUserCodeType* getDifferentialPairUserCodeType(
IRType* valueType,
IRInst* witnessTable);
IRBackwardDiffIntermediateContextType* getBackwardDiffIntermediateContextType(IRInst* func);
IRFuncType* getFuncType(
UInt paramCount,
IRType* const* paramTypes,
IRType* resultType);
IRFuncType* getFuncType(
UInt paramCount, IRType* const* paramTypes, IRType* resultType, IRAttr* attribute);
IRFuncType* getFuncType(
List<IRType*> const& paramTypes,
IRType* resultType)
{
return getFuncType(paramTypes.getCount(), paramTypes.getBuffer(), resultType);
}
IRConstantBufferType* getConstantBufferType(
IRType* elementType);
IRGLSLOutputParameterGroupType* getGLSLOutputParameterGroupType(IRType* valueType);
IRConstExprRate* getConstExprRate();
IRGroupSharedRate* getGroupSharedRate();
IRActualGlobalRate* getActualGlobalRate();
IRRateQualifiedType* getRateQualifiedType(
IRRate* rate,
IRType* dataType);
IRType* getTaggedUnionType(
UInt caseCount,
IRType* const* caseTypes);
IRType* getTaggedUnionType(
List<IRType*> const& caseTypes)
{
return getTaggedUnionType(caseTypes.getCount(), caseTypes.getBuffer());
}
IRType* getBindExistentialsType(
IRInst* baseType,
UInt slotArgCount,
IRInst* const* slotArgs);
IRType* getBindExistentialsType(
IRInst* baseType,
UInt slotArgCount,
IRUse const* slotArgs);
IRType* getBoundInterfaceType(
IRType* interfaceType,
IRType* concreteType,
IRInst* witnessTable);
IRType* getPseudoPtrType(
IRType* concreteType);
IRType* getConjunctionType(
UInt typeCount,
IRType* const* types);
IRType* getConjunctionType(
IRType* type0,
IRType* type1)
{
IRType* types[] = { type0, type1 };
return getConjunctionType(2, types);
}
IRType* getAttributedType(
IRType* baseType,
UInt attributeCount,
IRAttr* const* attributes);
IRType* getAttributedType(
IRType* baseType,
List<IRAttr*> attributes)
{
return getAttributedType(baseType, attributes.getCount(), attributes.getBuffer());
}
/// Emit an LiveRangeStart instruction indicating the referenced item is live following this instruction
IRLiveRangeStart* emitLiveRangeStart(IRInst* referenced);
/// Emit a LiveRangeEnd instruction indicating the referenced item is no longer live when this instruction is reached.
IRLiveRangeEnd* emitLiveRangeEnd(IRInst* referenced);
// Set the data type of an instruction, while preserving
// its rate, if any.
void setDataType(IRInst* inst, IRType* dataType);
/// Extract the value wrapped inside an existential box.
IRInst* emitGetValueFromBoundInterface(IRType* type, IRInst* boundInterfaceValue);
/// Given an existential value, extract the underlying "real" value
IRInst* emitExtractExistentialValue(
IRType* type,
IRInst* existentialValue);
/// Given an existential value, extract the underlying "real" type
IRType* emitExtractExistentialType(
IRInst* existentialValue);
/// Given an existential value, extract the witness table showing how the value conforms to the existential type.
IRInst* emitExtractExistentialWitnessTable(
IRInst* existentialValue);
IRInst* emitForwardDifferentiateInst(IRType* type, IRInst* baseFn);
IRInst* emitBackwardDifferentiateInst(IRType* type, IRInst* baseFn);
IRInst* emitBackwardDifferentiatePrimalInst(IRType* type, IRInst* baseFn);
IRInst* emitBackwardDifferentiatePropagateInst(IRType* type, IRInst* baseFn);
IRInst* emitPrimalSubstituteInst(IRType* type, IRInst* baseFn);
IRInst* emitDispatchKernelInst(IRType* type, IRInst* baseFn, IRInst* threadGroupSize, IRInst* dispatchSize, Int argCount, IRInst* const* inArgs);
IRInst* emitCudaKernelLaunch(IRInst* baseFn, IRInst* gridDim, IRInst* blockDim, IRInst* argsArray, IRInst* cudaStream);
IRInst* emitGetTorchCudaStream();
IRInst* emitMakeDifferentialPair(IRType* type, IRInst* primal, IRInst* differential);
IRInst* emitMakeDifferentialPairUserCode(IRType* type, IRInst* primal, IRInst* differential);
IRInst* addDifferentiableTypeDictionaryDecoration(IRInst* target);
IRInst* addPrimalValueStructKeyDecoration(IRInst* target, IRStructKey* key);
IRInst* addPrimalElementTypeDecoration(IRInst* target, IRInst* type);
IRInst* addIntermediateContextFieldDifferentialTypeDecoration(IRInst* target, IRInst* witness);
// Add a differentiable type entry to the appropriate dictionary.
IRInst* addDifferentiableTypeEntry(IRInst* dictDecoration, IRInst* irType, IRInst* conformanceWitness);
IRInst* addFloatingModeOverrideDecoration(IRInst* dest, FloatingPointMode mode);
IRInst* emitSpecializeInst(
IRType* type,
IRInst* genericVal,
UInt argCount,
IRInst* const* args);
IRInst* emitLookupInterfaceMethodInst(
IRType* type,
IRInst* witnessTableVal,
IRInst* interfaceMethodVal);
IRInst* emitGetSequentialIDInst(IRInst* rttiObj);
IRInst* emitAlloca(IRInst* type, IRInst* rttiObjPtr);
IRInst* emitPackAnyValue(IRType* type, IRInst* value);
IRInst* emitUnpackAnyValue(IRType* type, IRInst* value);
IRInst* emitCallInst(
IRType* type,
IRInst* func,
UInt argCount,
IRInst* const* args);
IRInst* emitCallInst(
IRType* type,
IRInst* func,
List<IRInst*> const& args)
{
return emitCallInst(type, func, args.getCount(), args.getBuffer());
}
IRInst* emitTryCallInst(
IRType* type,
IRBlock* successBlock,
IRBlock* failureBlock,
IRInst* func,
UInt argCount,
IRInst* const* args);
IRInst* createIntrinsicInst(
IRType* type,
IROp op,
UInt argCount,
IRInst* const* args);
IRInst* createIntrinsicInst(
IRType* type,
IROp op,
IRInst* operand,
UInt operandCount,
IRInst* const* operands);
IRInst* createIntrinsicInst(
IRType* type,
IROp op,
UInt operandListCount,
UInt const* listOperandCounts,
IRInst* const* const* listOperands);
IRInst* emitIntrinsicInst(
IRType* type,
IROp op,
UInt argCount,
IRInst* const* args);
/// Emits appropriate inst for constructing a default value of `type`.
/// If `fallback` is true, will emit `DefaultConstruct` inst on unknown types.
/// Otherwise, returns nullptr if we can't materialize the inst.
IRInst* emitDefaultConstruct(IRType* type, bool fallback = true);
/// Emits a raw `DefaultConstruct` opcode without attempting to fold/materialize
/// the inst.
IRInst* emitDefaultConstructRaw(IRType* type);
IRInst* emitCast(
IRType* type,
IRInst* value);
IRInst* emitVectorReshape(IRType* type, IRInst* value);
IRInst* emitMakeUInt64(IRInst* low, IRInst* high);
// Creates an RTTI object. Result is of `IRRTTIType`.
IRInst* emitMakeRTTIObject(IRInst* typeInst);
IRInst* emitMakeTargetTuple(IRType* type, UInt count, IRInst* const* args);
IRInst* emitTargetTupleGetElement(IRType* elementType, IRInst* targetTupleVal, IRInst* indexVal);
IRInst* emitMakeTuple(IRType* type, UInt count, IRInst* const* args);
IRInst* emitMakeTuple(UInt count, IRInst* const* args);
IRInst* emitMakeTuple(IRType* type, List<IRInst*> const& args)
{
return emitMakeTuple(type, args.getCount(), args.getBuffer());
}
IRInst* emitMakeTuple(List<IRInst*> const& args)
{
return emitMakeTuple(args.getCount(), args.getBuffer());
}
IRInst* emitMakeTuple(IRInst* arg0, IRInst* arg1)
{
IRInst* args[] = { arg0, arg1 };
return emitMakeTuple(SLANG_COUNT_OF(args), args);
}
IRInst* emitMakeString(IRInst* nativeStr);
IRInst* emitGetNativeString(IRInst* str);
IRInst* emitGetTupleElement(IRType* type, IRInst* tuple, UInt element);
IRInst* emitMakeResultError(IRType* resultType, IRInst* errorVal);
IRInst* emitMakeResultValue(IRType* resultType, IRInst* val);
IRInst* emitIsResultError(IRInst* result);
IRInst* emitGetResultError(IRInst* result);
IRInst* emitGetResultValue(IRInst* result);
IRInst* emitOptionalHasValue(IRInst* optValue);
IRInst* emitGetOptionalValue(IRInst* optValue);
IRInst* emitMakeOptionalValue(IRInst* optType, IRInst* value);
IRInst* emitMakeOptionalNone(IRInst* optType, IRInst* defaultValue);
IRInst* emitDifferentialPairGetDifferential(IRType* diffType, IRInst* diffPair);
IRInst* emitDifferentialPairGetPrimal(IRInst* diffPair);
IRInst* emitDifferentialPairGetDifferentialUserCode(IRType* diffType, IRInst* diffPair);
IRInst* emitDifferentialPairGetPrimalUserCode(IRInst* diffPair);
IRInst* emitMakeVector(
IRType* type,
UInt argCount,
IRInst* const* args);
IRInst* emitMakeVectorFromScalar(
IRType* type,
IRInst* scalarValue);
IRInst* emitMakeVector(
IRType* type,
List<IRInst*> const& args)
{
return emitMakeVector(type, args.getCount(), args.getBuffer());
}
IRInst* emitMatrixReshape(IRType* type, IRInst* inst);
IRInst* emitMakeMatrix(
IRType* type,
UInt argCount,
IRInst* const* args);
IRInst* emitMakeMatrixFromScalar(
IRType* type,
IRInst* scalarValue);
IRInst* emitMakeArray(
IRType* type,
UInt argCount,
IRInst* const* args);
IRInst* emitMakeArrayList(
IRType* type,
UInt argCount,
IRInst* const* args);
IRInst* emitMakeArrayFromElement(
IRType* type,
IRInst* element);
IRInst* emitMakeStruct(
IRType* type,
UInt argCount,
IRInst* const* args);
IRInst* emitMakeStruct(
IRType* type,
List<IRInst*> const& args)
{
return emitMakeStruct(type, args.getCount(), args.getBuffer());
}
IRInst* emitMakeTensorView(IRType* type, IRInst* val);
IRInst* emitMakeExistential(
IRType* type,
IRInst* value,
IRInst* witnessTable);
IRInst* emitMakeExistentialWithRTTI(
IRType* type,
IRInst* value,
IRInst* witnessTable,
IRInst* rtti);
IRInst* emitWrapExistential(
IRType* type,
IRInst* value,
UInt slotArgCount,
IRInst* const* slotArgs);
IRInst* emitWrapExistential(
IRType* type,
IRInst* value,
UInt slotArgCount,
IRUse const* slotArgs)
{
List<IRInst*> slotArgVals;
for(UInt ii = 0; ii < slotArgCount; ++ii)
slotArgVals.add(slotArgs[ii].get());
return emitWrapExistential(type, value, slotArgCount, slotArgVals.getBuffer());
}
IRInst* emitManagedPtrAttach(IRInst* managedPtrVar, IRInst* value);
IRInst* emitManagedPtrDetach(IRType* type, IRInst* managedPtrVal);
IRInst* emitGetNativePtr(IRInst* value);
IRInst* emitGetManagedPtrWriteRef(IRInst* ptrToManagedPtr);
IRInst* emitGpuForeach(List<IRInst*> args);
IRUndefined* emitUndefined(IRType* type);
IRInst* emitReinterpret(IRInst* type, IRInst* value);
IRFunc* createFunc();
IRGlobalVar* createGlobalVar(
IRType* valueType);
IRGlobalParam* createGlobalParam(
IRType* valueType);
/// Creates an IRWitnessTable value.
/// @param baseType: The comformant-to type of this witness.
/// @param subType: The type that is doing the conforming.
IRWitnessTable* createWitnessTable(IRType* baseType, IRType* subType);
IRWitnessTableEntry* createWitnessTableEntry(
IRWitnessTable* witnessTable,
IRInst* requirementKey,
IRInst* satisfyingVal);
IRInterfaceRequirementEntry* createInterfaceRequirementEntry(
IRInst* requirementKey,
IRInst* requirementVal);
// Create an initially empty `struct` type.
IRStructType* createStructType();
// Create an initially empty `class` type.
IRClassType* createClassType();
// Create an empty `interface` type.
IRInterfaceType* createInterfaceType(UInt operandCount, IRInst* const* operands);
// Create a global "key" to use for indexing into a `struct` type.
IRStructKey* createStructKey();
// Create a field nested in a struct type, declaring that
// the specified field key maps to a field with the specified type.
IRStructField* createStructField(
IRType* aggType,
IRStructKey* fieldKey,
IRType* fieldType);
IRGeneric* createGeneric();
IRGeneric* emitGeneric();
// Low-level operation for creating a type.
IRType* getType(
IROp op,
UInt operandCount,
IRInst* const* operands);
IRType* getType(
IROp op);
IRType* getType(
IROp op,
IRInst* operand0);
/// Create an empty basic block.
///
/// The created block will not be inserted into the current
/// function; call `insertBlock()` to attach the block
/// at an appropriate point.
///
IRBlock* createBlock();
/// Insert a block into the current function.
///
/// This attaches the given `block` to the current function,
/// and makes it the current block for
/// new instructions that get emitted.
///
void insertBlock(IRBlock* block);
/// Emit a new block into the current function.
///
/// This function is equivalent to using `createBlock()`
/// and then `insertBlock()`.
///
IRBlock* emitBlock();
static void insertBlockAlongEdge(IRModule* module, IREdge const& edge);
IRParam* createParam(
IRType* type);
IRParam* emitParam(
IRType* type);
IRParam* emitParamAtHead(
IRType* type);
IRInst* emitAllocObj(IRType* type);
IRVar* emitVar(
IRType* type);
IRInst* emitLoad(
IRType* type,
IRInst* ptr);
IRInst* emitLoad(
IRInst* ptr);
IRInst* emitLoadReverseGradient(IRType* type, IRInst* diffValue);
IRInst* emitReverseGradientDiffPairRef(IRType* type, IRInst* primalVar, IRInst* diffVar);
IRInst* emitPrimalParamRef(IRInst* param);
IRInst* emitDiffParamRef(IRType* type, IRInst* param);
IRInst* emitStore(
IRInst* dstPtr,
IRInst* srcVal);
IRInst* emitImageLoad(
IRType* type,
IRInst* image,
IRInst* coord);
IRInst* emitImageStore(
IRType* type,
IRInst* image,
IRInst* coord,
IRInst* value);
IRInst* emitIsType(IRInst* value, IRInst* witness, IRInst* typeOperand, IRInst* targetWitness);
IRInst* emitFieldExtract(
IRType* type,
IRInst* base,
IRInst* field);
IRInst* emitFieldAddress(
IRType* type,
IRInst* basePtr,
IRInst* field);
IRInst* emitElementExtract(
IRType* type,
IRInst* base,
IRInst* index);
IRInst* emitElementExtract(
IRInst* base,
IRInst* index);
IRInst* emitElementExtract(
IRInst* base,
const ArrayView<IRInst*>& accessChain);
IRInst* emitElementAddress(
IRType* type,
IRInst* basePtr,
IRInst* index);
IRInst* emitElementAddress(
IRInst* basePtr,
IRInst* index);
IRInst* emitElementAddress(
IRInst* basePtr,
const ArrayView<IRInst*>& accessChain);
IRInst* emitUpdateElement(IRInst* base, IRInst* index, IRInst* newElement);
IRInst* emitUpdateElement(IRInst* base, const List<IRInst*>& accessChain, IRInst* newElement);
IRInst* emitGetAddress(
IRType* type,
IRInst* value);
IRInst* emitSwizzle(
IRType* type,
IRInst* base,
UInt elementCount,
IRInst* const* elementIndices);
IRInst* emitSwizzle(
IRType* type,
IRInst* base,
UInt elementCount,
UInt const* elementIndices);
IRInst* emitSwizzleSet(
IRType* type,
IRInst* base,
IRInst* source,
UInt elementCount,
IRInst* const* elementIndices);
IRInst* emitSwizzleSet(
IRType* type,
IRInst* base,
IRInst* source,
UInt elementCount,
UInt const* elementIndices);
IRInst* emitSwizzledStore(
IRInst* dest,
IRInst* source,
UInt elementCount,
IRInst* const* elementIndices);
IRInst* emitSwizzledStore(
IRInst* dest,
IRInst* source,
UInt elementCount,
UInt const* elementIndices);
IRInst* emitReturn(
IRInst* val);
IRInst* emitReturn();
IRInst* emitThrow(IRInst* val);
IRInst* emitDiscard();
IRInst* emitUnreachable();
IRInst* emitMissingReturn();
IRInst* emitBranch(
IRBlock* block);
IRInst* emitBranch(IRBlock* block, Int argCount, IRInst*const* args);
IRInst* emitBreak(
IRBlock* target);
IRInst* emitContinue(
IRBlock* target);
IRInst* emitLoop(
IRBlock* target,
IRBlock* breakBlock,
IRBlock* continueBlock);
IRInst* emitLoop(
IRBlock* target,
IRBlock* breakBlock,
IRBlock* continueBlock,
Int argCount,
IRInst*const* args);
IRInst* emitBranch(
IRInst* val,
IRBlock* trueBlock,
IRBlock* falseBlock);
IRInst* emitIf(
IRInst* val,
IRBlock* trueBlock,
IRBlock* afterBlock);
IRInst* emitIfElse(
IRInst* val,
IRBlock* trueBlock,
IRBlock* falseBlock,
IRBlock* afterBlock);
// Create basic blocks and insert an `IfElse` inst at current position that jumps into the blocks.
// The current insert position is changed to inside `outTrueBlock` after the call.
IRInst* emitIfElseWithBlocks(
IRInst* val, IRBlock*& outTrueBlock, IRBlock*& outFalseBlock, IRBlock*& outAfterBlock);
// Create basic blocks and insert an `If` inst at current position that jumps into the blocks.
// The current insert position is changed to inside `outTrueBlock` after the call.
IRInst* emitIfWithBlocks(
IRInst* val, IRBlock*& outTrueBlock, IRBlock*& outAfterBlock);
IRInst* emitLoopTest(
IRInst* val,
IRBlock* bodyBlock,
IRBlock* breakBlock);
IRInst* emitSwitch(
IRInst* val,
IRBlock* breakLabel,
IRBlock* defaultLabel,
UInt caseArgCount,
IRInst* const* caseArgs);
IRGlobalGenericParam* emitGlobalGenericParam(
IRType* type);
IRGlobalGenericParam* emitGlobalGenericTypeParam()
{
return emitGlobalGenericParam(getTypeKind());
}
IRGlobalGenericParam* emitGlobalGenericWitnessTableParam(IRType* comformanceType)
{
return emitGlobalGenericParam(getWitnessTableType(comformanceType));
}
IRBindGlobalGenericParam* emitBindGlobalGenericParam(
IRInst* param,
IRInst* val);
IRDecoration* addBindExistentialSlotsDecoration(
IRInst* value,
UInt argCount,
IRInst* const* args);
IRInst* emitExtractTaggedUnionTag(
IRInst* val);
IRInst* emitExtractTaggedUnionPayload(
IRType* type,
IRInst* val,
IRInst* tag);
IRInst* emitBitCast(
IRType* type,
IRInst* val);
IRInst* emitCastPtrToBool(IRInst* val);
IRGlobalConstant* emitGlobalConstant(
IRType* type);
IRGlobalConstant* emitGlobalConstant(
IRType* type,
IRInst* val);
IRInst* emitWaveMaskBallot(IRType* type, IRInst* mask, IRInst* condition);
IRInst* emitWaveMaskMatch(IRType* type, IRInst* mask, IRInst* value);
IRInst* emitBitAnd(IRType* type, IRInst* left, IRInst* right);
IRInst* emitBitOr(IRType* type, IRInst* left, IRInst* right);
IRInst* emitBitNot(IRType* type, IRInst* value);
IRInst* emitNeg(IRType* type, IRInst* value);
IRInst* emitNot(IRType* type, IRInst* value);
IRInst* emitAdd(IRType* type, IRInst* left, IRInst* right);
IRInst* emitSub(IRType* type, IRInst* left, IRInst* right);
IRInst* emitMul(IRType* type, IRInst* left, IRInst* right);
IRInst* emitDiv(IRType* type, IRInst* numerator, IRInst* denominator);
IRInst* emitEql(IRInst* left, IRInst* right);
IRInst* emitNeq(IRInst* left, IRInst* right);
IRInst* emitLess(IRInst* left, IRInst* right);
IRInst* emitShr(IRType* type, IRInst* op0, IRInst* op1);
IRInst* emitShl(IRType* type, IRInst* op0, IRInst* op1);
//
// Decorations
//
IRDecoration* addDecoration(IRInst* value, IROp op, IRInst* const* operands, Int operandCount);
IRDecoration* addDecoration(IRInst* value, IROp op)
{
return addDecoration(value, op, (IRInst* const*) nullptr, 0);
}
IRDecoration* addDecoration(IRInst* value, IROp op, IRInst* operand)
{
return addDecoration(value, op, &operand, 1);
}
IRDecoration* addDecoration(IRInst* value, IROp op, IRInst* operand0, IRInst* operand1)
{
IRInst* operands[] = { operand0, operand1 };
return addDecoration(value, op, operands, SLANG_COUNT_OF(operands));
}
template<typename T>
void addSimpleDecoration(IRInst* value)
{
addDecoration(value, IROp(T::kOp), (IRInst* const*) nullptr, 0);
}
void addHighLevelDeclDecoration(IRInst* value, Decl* decl);
// void addLayoutDecoration(IRInst* value, Layout* layout);
void addLayoutDecoration(IRInst* value, IRLayout* layout);
// IRLayout* getLayout(Layout* astLayout);
IRTypeSizeAttr* getTypeSizeAttr(
LayoutResourceKind kind,
LayoutSize size);
IRVarOffsetAttr* getVarOffsetAttr(
LayoutResourceKind kind,
UInt offset,
UInt space = 0);
IRPendingLayoutAttr* getPendingLayoutAttr(
IRLayout* pendingLayout);
IRStructFieldLayoutAttr* getFieldLayoutAttr(
IRInst* key,
IRVarLayout* layout);
IRCaseTypeLayoutAttr* getCaseTypeLayoutAttr(
IRTypeLayout* layout);
IRSemanticAttr* getSemanticAttr(
IROp op,
String const& name,
UInt index);
IRSystemValueSemanticAttr* getSystemValueSemanticAttr(
String const& name,
UInt index)
{
return cast<IRSystemValueSemanticAttr>(getSemanticAttr(
kIROp_SystemValueSemanticAttr,
name,
index));
}
IRUserSemanticAttr* getUserSemanticAttr(
String const& name,
UInt index)
{
return cast<IRUserSemanticAttr>(getSemanticAttr(
kIROp_UserSemanticAttr,
name,
index));
}
IRStageAttr* getStageAttr(Stage stage);
IRAttr* getAttr(IROp op, UInt operandCount, IRInst* const* operands);
IRAttr* getAttr(IROp op, List<IRInst*> const& operands)
{
return getAttr(op, operands.getCount(), operands.getBuffer());
}
IRAttr* getAttr(IROp op)
{
return getAttr(op, 0, nullptr);
}
IRTypeLayout* getTypeLayout(IROp op, List<IRInst*> const& operands);
IRVarLayout* getVarLayout(List<IRInst*> const& operands);
IREntryPointLayout* getEntryPointLayout(
IRVarLayout* paramsLayout,
IRVarLayout* resultLayout);
void addNameHintDecoration(IRInst* value, IRStringLit* name)
{
addDecoration(value, kIROp_NameHintDecoration, name);
}
void addNameHintDecoration(IRInst* value, UnownedStringSlice const& text)
{
addNameHintDecoration(value, getStringValue(text));
}
void addGLSLOuterArrayDecoration(IRInst* value, UnownedStringSlice const& text)
{
addDecoration(value, kIROp_GLSLOuterArrayDecoration, getStringValue(text));
}
void addInterpolationModeDecoration(IRInst* value, IRInterpolationMode mode)
{
addDecoration(value, kIROp_InterpolationModeDecoration, getIntValue(getIntType(), IRIntegerValue(mode)));
}
void addLoopControlDecoration(IRInst* value, IRLoopControl mode)
{
addDecoration(value, kIROp_LoopControlDecoration, getIntValue(getIntType(), IRIntegerValue(mode)));
}
void addLoopMaxItersDecoration(IRInst* value, IntegerLiteralValue iters)
{
addDecoration(value, kIROp_LoopMaxItersDecoration, getIntValue(getIntType(), iters));
}
void addLoopForceUnrollDecoration(IRInst* value, IntegerLiteralValue iters)
{
addDecoration(value, kIROp_ForceUnrollDecoration, getIntValue(getIntType(), iters));
}
void addSemanticDecoration(IRInst* value, UnownedStringSlice const& text, int index = 0)
{
addDecoration(value, kIROp_SemanticDecoration, getStringValue(text), getIntValue(getIntType(), index));
}
void addTargetIntrinsicDecoration(IRInst* value, IRInst* caps, UnownedStringSlice const& definition)
{
addDecoration(value, kIROp_TargetIntrinsicDecoration, caps, getStringValue(definition));
}
void addTargetIntrinsicDecoration(IRInst* value, CapabilitySet const& caps, UnownedStringSlice const& definition)
{
addTargetIntrinsicDecoration(value, getCapabilityValue(caps), definition);
}
void addTargetDecoration(IRInst* value, IRInst* caps)
{
addDecoration(value, kIROp_TargetDecoration, caps);
}
void addTargetDecoration(IRInst* value, CapabilitySet const& caps)
{
addTargetDecoration(value, getCapabilityValue(caps));
}
void addRequireGLSLExtensionDecoration(IRInst* value, UnownedStringSlice const& extensionName)
{
addDecoration(value, kIROp_RequireGLSLExtensionDecoration, getStringValue(extensionName));
}
void addRequireGLSLVersionDecoration(IRInst* value, Int version)
{
addDecoration(value, kIROp_RequireGLSLVersionDecoration, getIntValue(getIntType(), IRIntegerValue(version)));
}
void addRequireSPIRVVersionDecoration(IRInst* value, const SemanticVersion& version)
{
SemanticVersion::IntegerType intValue = version.toInteger();
addDecoration(value, kIROp_RequireSPIRVVersionDecoration, getIntValue(getBasicType(BaseType::UInt64), intValue));
}
void addRequireCUDASMVersionDecoration(IRInst* value, const SemanticVersion& version)
{
SemanticVersion::IntegerType intValue = version.toInteger();
addDecoration(value, kIROp_RequireCUDASMVersionDecoration, getIntValue(getBasicType(BaseType::UInt64), intValue));
}
void addPatchConstantFuncDecoration(IRInst* value, IRInst* patchConstantFunc)
{
addDecoration(value, kIROp_PatchConstantFuncDecoration, patchConstantFunc);
}
void addImportDecoration(IRInst* value, UnownedStringSlice const& mangledName)
{
addDecoration(value, kIROp_ImportDecoration, getStringValue(mangledName));
}
void addExportDecoration(IRInst* value, UnownedStringSlice const& mangledName)
{
addDecoration(value, kIROp_ExportDecoration, getStringValue(mangledName));
}
void addExternCppDecoration(IRInst* value, UnownedStringSlice const& mangledName)
{
addDecoration(value, kIROp_ExternCppDecoration, getStringValue(mangledName));
}
void addForceInlineDecoration(IRInst* value)
{
addDecoration(value, kIROp_ForceInlineDecoration);
}
void addAutoDiffOriginalValueDecoration(IRInst* value, IRInst* originalVal)
{
addDecoration(value, kIROp_AutoDiffOriginalValueDecoration, originalVal);
}
void addForwardDifferentiableDecoration(IRInst* value)
{
addDecoration(value, kIROp_ForwardDifferentiableDecoration);
}
void addBackwardDifferentiableDecoration(IRInst* value)
{
addDecoration(value, kIROp_BackwardDifferentiableDecoration);
}
void addForwardDerivativeDecoration(IRInst* value, IRInst* fwdFunc)
{
addDecoration(value, kIROp_ForwardDerivativeDecoration, fwdFunc);
}
void addUserDefinedBackwardDerivativeDecoration(IRInst* value, IRInst* fwdFunc)
{
addDecoration(value, kIROp_UserDefinedBackwardDerivativeDecoration, fwdFunc);
}
void addBackwardDerivativePrimalDecoration(IRInst* value, IRInst* jvpFn)
{
addDecoration(value, kIROp_BackwardDerivativePrimalDecoration, jvpFn);
}
void addBackwardDerivativePrimalReturnDecoration(IRInst* value, IRInst* retVal)
{
addDecoration(value, kIROp_BackwardDerivativePrimalReturnDecoration, retVal);
}
void addBackwardDerivativePropagateDecoration(IRInst* value, IRInst* jvpFn)
{
addDecoration(value, kIROp_BackwardDerivativePropagateDecoration, jvpFn);
}
void addBackwardDerivativeDecoration(IRInst* value, IRInst* jvpFn)
{
addDecoration(value, kIROp_BackwardDerivativeDecoration, jvpFn);
}
void addBackwardDerivativeIntermediateTypeDecoration(IRInst* value, IRInst* jvpFn)
{
addDecoration(value, kIROp_BackwardDerivativeIntermediateTypeDecoration, jvpFn);
}
void addBackwardDerivativePrimalContextDecoration(IRInst* value, IRInst* ctx)
{
addDecoration(value, kIROp_BackwardDerivativePrimalContextDecoration, ctx);
}
void addPrimalSubstituteDecoration(IRInst* value, IRInst* jvpFn)
{
addDecoration(value, kIROp_PrimalSubstituteDecoration, jvpFn);
}
void addLoopCounterDecoration(IRInst* value)
{
addDecoration(value, kIROp_LoopCounterDecoration);
}
void addLoopExitPrimalValueDecoration(IRInst* value, IRInst* primalInst, IRInst* exitValue)
{
addDecoration(value, kIROp_LoopExitPrimalValueDecoration, primalInst, exitValue);
}
void addPrimalValueAccessDecoration(IRInst* value)
{
addDecoration(value, kIROp_PrimalValueAccessDecoration);
}
void markInstAsPrimal(IRInst* value)
{
addDecoration(value, kIROp_PrimalInstDecoration);
}
void markInstAsDifferential(IRInst* value)
{
addDecoration(value, kIROp_DifferentialInstDecoration, nullptr);
}
void markInstAsMixedDifferential(IRInst* value)
{
addDecoration(value, kIROp_MixedDifferentialInstDecoration, nullptr);
}
void markInstAsMixedDifferential(IRInst* value, IRType* pairType)
{
addDecoration(value, kIROp_MixedDifferentialInstDecoration, pairType);
}
void markInstAsDifferential(IRInst* value, IRType* primalType)
{
addDecoration(value, kIROp_DifferentialInstDecoration, primalType);
}
void markInstAsDifferential(IRInst* value, IRType* primalType, IRInst* primalInst)
{
addDecoration(value, kIROp_DifferentialInstDecoration, primalType, primalInst);
}
void addCOMWitnessDecoration(IRInst* value, IRInst* witnessTable)
{
addDecoration(value, kIROp_COMWitnessDecoration, &witnessTable, 1);
}
void addDllImportDecoration(IRInst* value, UnownedStringSlice const& libraryName, UnownedStringSlice const& functionName)
{
addDecoration(value, kIROp_DllImportDecoration, getStringValue(libraryName), getStringValue(functionName));
}
void addDllExportDecoration(IRInst* value, UnownedStringSlice const& functionName)
{
addDecoration(value, kIROp_DllExportDecoration, getStringValue(functionName));
}
void addTorchEntryPointDecoration(IRInst* value, UnownedStringSlice const& functionName)
{
addDecoration(value, kIROp_TorchEntryPointDecoration, getStringValue(functionName));
}
void addCudaDeviceExportDecoration(IRInst* value, UnownedStringSlice const& functionName)
{
addDecoration(value, kIROp_CudaDeviceExportDecoration, getStringValue(functionName));
}
void addCudaHostDecoration(IRInst* value)
{
addDecoration(value, kIROp_CudaHostDecoration);
}
void addCudaKernelDecoration(IRInst* value)
{
addDecoration(value, kIROp_CudaKernelDecoration);
}
void addEntryPointDecoration(IRInst* value, Profile profile, UnownedStringSlice const& name, UnownedStringSlice const& moduleName)
{
IRInst* operands[] = { getIntValue(getIntType(), profile.raw), getStringValue(name), getStringValue(moduleName) };
addDecoration(value, kIROp_EntryPointDecoration, operands, SLANG_COUNT_OF(operands));
}
void addKeepAliveDecoration(IRInst* value)
{
addDecoration(value, kIROp_KeepAliveDecoration);
}
void addPublicDecoration(IRInst* value)
{
addDecoration(value, kIROp_PublicDecoration);
}
void addHLSLExportDecoration(IRInst* value)
{
addDecoration(value, kIROp_HLSLExportDecoration);
}
void addNVAPIMagicDecoration(IRInst* value, UnownedStringSlice const& name)
{
addDecoration(value, kIROp_NVAPIMagicDecoration, getStringValue(name));
}
void addNVAPISlotDecoration(IRInst* value, UnownedStringSlice const& registerName, UnownedStringSlice const& spaceName)
{
addDecoration(value, kIROp_NVAPISlotDecoration, getStringValue(registerName), getStringValue(spaceName));
}
/// Add a decoration that indicates that the given `inst` depends on the given `dependency`.
///
/// This decoration can be used to ensure that a value that an instruction
/// implicitly depends on cannot be eliminated so long as the instruction
/// itself is kept alive.
///
void addDependsOnDecoration(IRInst* inst, IRInst* dependency)
{
addDecoration(inst, kIROp_DependsOnDecoration, dependency);
}
void addFormatDecoration(IRInst* inst, ImageFormat format)
{
addFormatDecoration(inst, getIntValue(getIntType(), IRIntegerValue(format)));
}
void addFormatDecoration(IRInst* inst, IRInst* format)
{
addDecoration(inst, kIROp_FormatDecoration, format);
}
void addRTTITypeSizeDecoration(IRInst* inst, IRIntegerValue value)
{
addDecoration(inst, kIROp_RTTITypeSizeDecoration, getIntValue(getIntType(), value));
}
void addAnyValueSizeDecoration(IRInst* inst, IRIntegerValue value)
{
addDecoration(inst, kIROp_AnyValueSizeDecoration, getIntValue(getIntType(), value));
}
void addSpecializeDecoration(IRInst* inst)
{
addDecoration(inst, kIROp_SpecializeDecoration);
}
void addComInterfaceDecoration(IRInst* inst, UnownedStringSlice guid)
{
addDecoration(inst, kIROp_ComInterfaceDecoration, getStringValue(guid));
}
void addTypeConstraintDecoration(IRInst* inst, IRInst* constraintType)
{
addDecoration(inst, kIROp_TypeConstraintDecoration, constraintType);
}
void addBuiltinDecoration(IRInst* inst)
{
addDecoration(inst, kIROp_BuiltinDecoration);
}
void addSequentialIDDecoration(IRInst* inst, IRIntegerValue id)
{
addDecoration(inst, kIROp_SequentialIDDecoration, getIntValue(getUIntType(), id));
}
void addVulkanRayPayloadDecoration(IRInst* inst, int location)
{
addDecoration(inst, kIROp_VulkanRayPayloadDecoration, getIntValue(getIntType(), location));
}
void addVulkanCallablePayloadDecoration(IRInst* inst, int location)
{
addDecoration(inst, kIROp_VulkanCallablePayloadDecoration, getIntValue(getIntType(), location));
}
void addVulkanHitObjectAttributesDecoration(IRInst* inst, int location)
{
addDecoration(inst, kIROp_VulkanHitObjectAttributesDecoration, getIntValue(getIntType(), location));
}
void addMeshOutputDecoration(IROp d, IRInst* value, IRInst* maxCount)
{
SLANG_ASSERT(IRMeshOutputDecoration::isaImpl(d));
// TODO: Ellie, correct int type here?
addDecoration(value, d, maxCount);
}
};
// Helper to establish the source location that will be used
// by an IRBuilder.
struct IRBuilderSourceLocRAII
{
IRBuilder* builder;
SourceLoc sourceLoc;
IRBuilderSourceLocRAII* next;
IRBuilderSourceLocRAII(
IRBuilder* builder,
SourceLoc sourceLoc)
: builder(builder)
, sourceLoc(sourceLoc)
, next(nullptr)
{
next = builder->getSourceLocInfo();
builder->setSourceLocInfo(this);
}
~IRBuilderSourceLocRAII()
{
SLANG_ASSERT(builder->getSourceLocInfo() == this);
builder->setSourceLocInfo(next);
}
};
// A helper to restore the builder's insert location on destruction
struct IRBuilderInsertLocScope
{
IRBuilder* builder;
IRInsertLoc insertLoc;
IRBuilderInsertLocScope(IRBuilder* b)
: builder(b), insertLoc(builder->getInsertLoc())
{}
~IRBuilderInsertLocScope()
{
builder->setInsertLoc(insertLoc);
}
};
//
void markConstExpr(
IRBuilder* builder,
IRInst* irValue);
//
IRTargetIntrinsicDecoration* findAnyTargetIntrinsicDecoration(
IRInst* val);
IRTargetSpecificDecoration* findBestTargetDecoration(
IRInst* val,
CapabilitySet const& targetCaps);
IRTargetSpecificDecoration* findBestTargetDecoration(
IRInst* val,
CapabilityAtom targetCapabilityAtom);
inline IRTargetIntrinsicDecoration* findBestTargetIntrinsicDecoration(
IRInst* inInst,
CapabilitySet const& targetCaps)
{
return as<IRTargetIntrinsicDecoration>(findBestTargetDecoration(inInst, targetCaps));
}
void addHoistableInst(
IRBuilder* builder,
IRInst* inst);
}
#endif