Raw File
slang-ir-insts.h
// 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-compiler.h"
#include "slang-ir.h"
#include "slang-syntax.h"
#include "slang-type-layout.h"

namespace Slang {

class Decl;

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,
};

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 IRTargetSpecificDecoration : IRDecoration
{
    IR_PARENT_ISA(TargetSpecificDecoration)

    IRStringLit* getTargetNameOperand() { return cast<IRStringLit>(getOperand(0)); }

    UnownedStringSlice getTargetName()
    {
        return getTargetNameOperand()->getStringSlice();
    }
};

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 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,
};

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();
    }
};

#define IR_SIMPLE_DECORATION(NAME)      \
    struct IR##NAME : IRDecoration      \
    {                                   \
        enum { kOp = kIROp_##NAME };    \
    IR_LEAF_ISA(NAME)                   \
    };                                  \
    /**/

/// 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)

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 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(EarlyDepthStencilDecoration)
IR_SIMPLE_DECORATION(GloballyCoherentDecoration)
IR_SIMPLE_DECORATION(PreciseDecoration)


struct IROutputControlPointsDecoration : IRDecoration
{
    enum { kOp = kIROp_OutputControlPointsDecoration };
    IR_LEAF_ISA(OutputControlPointsDecoration)

    IRIntLit* getControlPointCount() { return cast<IRIntLit>(getOperand(0)); }
};

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)); }
};

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)); }
};

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 IRFormatDecoration : IRDecoration
{
    enum { kOp = kIROp_FormatDecoration };
    IR_LEAF_ISA(FormatDecoration)

    IRConstant* getFormatOperand() { return cast<IRConstant>(getOperand(0)); }

    ImageFormat getFormat()
    {
        return ImageFormat(getFormatOperand()->value.intVal);
    }
};

// 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(); }
};

struct IRLookupWitnessTable : IRInst
{
    IRUse sourceType;
    IRUse interfaceType;
};

// 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())); }
};

    /// 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 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)

    IRStructKey* getFieldKey()
    {
        return cast<IRStructKey>(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(IRStructKey* 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
        {
            IRStructKey* 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;
    };
};

    /// 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];
    };
};

    /// 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; }
    IRInst* getArg(UInt index) { return getOperand(index + 1); }
};

struct IRLoad : IRInst
{
    IRUse ptr;
};

struct IRStore : IRInst
{
    IRUse ptr;
    IRUse val;
};

struct IRFieldExtract : IRInst
{
    IRUse   base;
    IRUse   field;

    IRInst* getBase() { return base.get(); }
    IRInst* getField() { return field.get(); }
};

struct IRFieldAddress : IRInst
{
    IRUse   base;
    IRUse   field;

    IRInst* getBase() { return base.get(); }
    IRInst* getField() { return field.get(); }
};

// Terminators

struct IRReturn : IRTerminatorInst
{};

struct IRReturnVal : IRReturn
{
    IRUse val;

    IRInst* getVal() { return val.get(); }
};

struct IRReturnVoid : IRReturn
{};

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);

    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
{
    // 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 represent the test inside a loop
struct IRLoopTest : IRConditionalBranch
{
};

// A conditional branch that represents a one-sided `if`:
//
//     if( <condition> ) { <trueBlock> }
//     <falseBlock>
struct IRIf : IRConditionalBranch
{
    IRBlock* getAfterBlock() { return getFalseBlock(); }
};

// 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 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
{
    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());
    }

    IR_LEAF_ISA(WitnessTable)
};

// 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 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)
};

    /// 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)
};


// Description of an instruction to be used for global value numbering
struct IRInstKey
{
    IRInst* inst;

    int GetHashCode();
};

bool operator==(IRInstKey const& left, IRInstKey const& right);

struct IRConstantKey
{
    IRConstant* inst;

    bool operator==(const IRConstantKey& rhs) const { return inst->equal(rhs.inst); }
    int GetHashCode() const { return inst->getHashCode(); }
};

struct SharedIRBuilder
{
    // The parent compilation session
    Session* session;
    Session* getSession()
    {
        return session;
    }

    // The module that will own all of the IR
    IRModule*       module;

    Dictionary<IRInstKey,       IRInst*>    globalValueNumberingMap;
    Dictionary<IRConstantKey,   IRConstant*>    constantMap;

    // TODO: We probably shouldn't use this in the long run.
    Dictionary<void*,           IRLayout*>        layoutMap;
};

struct IRBuilderSourceLocRAII;

struct IRBuilder
{
    // Shared state for all IR builders working on the same module
    SharedIRBuilder*    sharedBuilder;

    Session* getSession()
    {
        return sharedBuilder->getSession();
    }

    IRModule* getModule() { return sharedBuilder->module; }

    // The current parent being inserted into (this might
    // be the global scope, a function, a block inside
    // a function, etc.)
    IRInst*   insertIntoParent = nullptr;
    //
    // An instruction in the current parent that we should insert before
    IRInst*         insertBeforeInst = nullptr;

    // Get the current basic block we are inserting into (if any)
    IRBlock*                getBlock();

    // Get the current function (or other value with code)
    // that we are inserting into (if any).
    IRGlobalValueWithCode*  getFunc();

    void setInsertInto(IRInst* insertInto);
    void setInsertBefore(IRInst* insertBefore);

    IRBuilderSourceLocRAII* sourceLocInfo = nullptr;

    void addInst(IRInst* inst);

    IRInst* getBoolValue(bool value);
    IRInst* getIntValue(IRType* type, IRIntegerValue value);
    IRInst* getFloatValue(IRType* type, IRFloatingPointValue value);
    IRStringLit* getStringValue(const UnownedStringSlice& slice);
    IRPtrLit* getPtrValue(void* value);

    IRBasicType* getBasicType(BaseType baseType);
    IRBasicType* getVoidType();
    IRBasicType* getBoolType();
    IRBasicType* getIntType();
    IRStringType* getStringType();

    IRBasicBlockType*   getBasicBlockType();
    IRType* getWitnessTableType() { return nullptr; }
    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);

    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);

    IRFuncType* getFuncType(
        UInt            paramCount,
        IRType* const*  paramTypes,
        IRType*         resultType);

    IRFuncType* getFuncType(
        List<IRType*> const&    paramTypes,
        IRType*                 resultType)
    {
        return getFuncType(paramTypes.getCount(), paramTypes.getBuffer(), resultType);
    }

    IRConstantBufferType* getConstantBufferType(
        IRType* elementType);

    IRConstExprRate* getConstExprRate();
    IRGroupSharedRate* getGroupSharedRate();

    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);

    // Set the data type of an instruction, while preserving
    // its rate, if any.
    void setDataType(IRInst* inst, IRType* dataType);

        /// 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* emitSpecializeInst(
        IRType*         type,
        IRInst*         genericVal,
        UInt            argCount,
        IRInst* const*  args);

    IRInst* emitLookupInterfaceMethodInst(
        IRType* type,
        IRInst* witnessTableVal,
        IRInst* interfaceMethodVal);

    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* createIntrinsicInst(
        IRType*         type,
        IROp            op,
        UInt            argCount,
        IRInst* const*  args);

    IRInst* emitIntrinsicInst(
        IRType*         type,
        IROp            op,
        UInt            argCount,
        IRInst* const*  args);

    IRInst* emitConstructorInst(
        IRType*         type,
        UInt            argCount,
        IRInst* const* args);

    IRInst* emitMakeVector(
        IRType*         type,
        UInt            argCount,
        IRInst* const* args);

    IRInst* emitMakeVector(
        IRType*                 type,
        List<IRInst*> const&    args)
    {
        return emitMakeVector(type, args.getCount(), args.getBuffer());
    }

    IRInst* emitMakeMatrix(
        IRType*         type,
        UInt            argCount,
        IRInst* const* args);

    IRInst* emitMakeArray(
        IRType*         type,
        UInt            argCount,
        IRInst* const* args);

    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* emitMakeExistential(
        IRType* type,
        IRInst* value,
        IRInst* witnessTable);

    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());
    }

    IRUndefined* emitUndefined(IRType* type);

    IRInst* findOrAddInst(
         IRType*                 type,
         IROp                    op,
         UInt                    operandListCount,
         UInt const*             listOperandCounts,
         IRInst* const* const*   listOperands);

    IRInst* findOrEmitHoistableInst(
        IRType*                 type,
        IROp                    op,
        UInt                    operandListCount,
        UInt const*             listOperandCounts,
        IRInst* const* const*   listOperands);
    IRInst* findOrEmitHoistableInst(
        IRType*         type,
        IROp            op,
        UInt            operandCount,
        IRInst* const*  operands);
    IRInst* findOrEmitHoistableInst(
        IRType*         type,
        IROp            op,
        IRInst*         operand,
        UInt            operandCount,
        IRInst* const*  operands);

    IRModule* createModule();

    IRFunc* createFunc();
    IRGlobalVar* createGlobalVar(
        IRType* valueType);
    IRGlobalParam* createGlobalParam(
        IRType* valueType);
    IRWitnessTable* createWitnessTable();
    IRWitnessTableEntry* createWitnessTableEntry(
        IRWitnessTable* witnessTable,
        IRInst*        requirementKey,
        IRInst*        satisfyingVal);

    // Create an initially empty `struct` type.
    IRStructType*   createStructType();

    // Create an empty `interface` type.
    IRInterfaceType* createInterfaceType();

    // 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(
        IRStructType*   structType,
        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);

        /// 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();

    

    IRParam* createParam(
        IRType* type);
    IRParam* emitParam(
        IRType* type);

    IRVar* emitVar(
        IRType* type);

    IRInst* emitLoad(
        IRType* type,
        IRInst* ptr);

    IRInst* emitLoad(
        IRInst*    ptr);

    IRInst* emitStore(
        IRInst*    dstPtr,
        IRInst*    srcVal);

    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* emitElementAddress(
        IRType*     type,
        IRInst*    basePtr,
        IRInst*    index);

    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* emitDiscard();

    IRInst* emitUnreachable();
    IRInst* emitMissingReturn();

    IRInst* emitBranch(
        IRBlock*    block);

    IRInst* emitBreak(
        IRBlock*    target);

    IRInst* emitContinue(
        IRBlock*    target);

    IRInst* emitLoop(
        IRBlock*    target,
        IRBlock*    breakBlock,
        IRBlock*    continueBlock);

    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);

    IRInst* emitLoopTest(
        IRInst*    val,
        IRBlock*    bodyBlock,
        IRBlock*    breakBlock);

    IRInst* emitSwitch(
        IRInst*        val,
        IRBlock*        breakLabel,
        IRBlock*        defaultLabel,
        UInt            caseArgCount,
        IRInst* const* caseArgs);

    IRGlobalGenericParam* emitGlobalGenericParam();

    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);

    IRGlobalConstant* emitGlobalConstant(
        IRType* type);

    IRGlobalConstant* emitGlobalConstant(
        IRType* type,
        IRInst* val);

    //
    // 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>
    T* addRefObjectToFree(T* ptr)
    {
        getModule()->getObjectScopeManager()->addMaybeNull(ptr);
        return ptr;
    }

    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(
        IRStructKey*    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);

    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 addSemanticDecoration(IRInst* value, UnownedStringSlice const& text, int index = 0)
    {
        addDecoration(value, kIROp_SemanticDecoration, getStringValue(text), getIntValue(getIntType(), index));
    }

    void addTargetIntrinsicDecoration(IRInst* value, UnownedStringSlice const& target, UnownedStringSlice const& definition)
    {
        addDecoration(value, kIROp_TargetIntrinsicDecoration, getStringValue(target), getStringValue(definition));
    }

    void addTargetDecoration(IRInst* value, UnownedStringSlice const& target)
    {
        addDecoration(value, kIROp_TargetDecoration, getStringValue(target));
    }

    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 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 addEntryPointDecoration(IRInst* value, Profile profile, UnownedStringSlice const& name)
    {
        addDecoration(value, kIROp_EntryPointDecoration, getIntValue(getIntType(), profile.raw), getStringValue(name));
    }

    void addKeepAliveDecoration(IRInst* value)
    {
        addDecoration(value, kIROp_KeepAliveDecoration);
    }

        /// 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 addHoistableInst(
    IRBuilder*  builder,
    IRInst*     inst);

// 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->sourceLocInfo;
        builder->sourceLocInfo = this;
    }

    ~IRBuilderSourceLocRAII()
    {
        SLANG_ASSERT(builder->sourceLocInfo == this);
        builder->sourceLocInfo = next;
    }
};

//

void markConstExpr(
    IRBuilder*  builder,
    IRInst*     irValue);

//

IRTargetIntrinsicDecoration* findTargetIntrinsicDecoration(
        IRInst*        val,
        String const&   targetName);

}

#endif
back to top