/*
 * Copyright (c) 2015 Andrew Kelley
 *
 * This file is part of zig, which is MIT licensed.
 * See http://opensource.org/licenses/MIT
 */

#ifndef ZIG_ALL_TYPES_HPP
#define ZIG_ALL_TYPES_HPP

#include "list.hpp"
#include "buffer.hpp"
#include "zig_llvm.h"
#include "hash_map.hpp"
#include "errmsg.hpp"
#include "bigint.hpp"
#include "bigfloat.hpp"
#include "target.hpp"
#include "tokenizer.hpp"

struct AstNode;
struct ZigFn;
struct Scope;
struct ScopeBlock;
struct ScopeFnDef;
struct ScopeExpr;
struct ZigType;
struct ZigVar;
struct ErrorTableEntry;
struct BuiltinFnEntry;
struct TypeStructField;
struct CodeGen;
struct ZigValue;
struct IrInst;
struct IrInstSrc;
struct IrInstGen;
struct IrInstGenCast;
struct IrInstGenAlloca;
struct IrInstGenCall;
struct IrInstGenAwait;
struct IrBasicBlockSrc;
struct IrBasicBlockGen;
struct ScopeDecls;
struct ZigWindowsSDK;
struct Tld;
struct TldExport;
struct IrAnalyze;
struct ResultLoc;
struct ResultLocPeer;
struct ResultLocPeerParent;
struct ResultLocBitCast;
struct ResultLocCast;
struct ResultLocReturn;
struct IrExecutableGen;

enum FileExt {
    FileExtUnknown,
    FileExtAsm,
    FileExtC,
    FileExtCpp,
    FileExtHeader,
    FileExtLLVMIr,
    FileExtLLVMBitCode,
};

enum PtrLen {
    PtrLenUnknown,
    PtrLenSingle,
    PtrLenC,
};

enum CallingConvention {
    CallingConventionUnspecified,
    CallingConventionC,
    CallingConventionNaked,
    CallingConventionAsync,
    CallingConventionInterrupt,
    CallingConventionSignal,
    CallingConventionStdcall,
    CallingConventionFastcall,
    CallingConventionVectorcall,
    CallingConventionThiscall,
    CallingConventionAPCS,
    CallingConventionAAPCS,
    CallingConventionAAPCSVFP,
};

// This one corresponds to the builtin.zig enum.
enum BuiltinPtrSize {
    BuiltinPtrSizeOne,
    BuiltinPtrSizeMany,
    BuiltinPtrSizeSlice,
    BuiltinPtrSizeC,
};

enum UndefAllowed {
    UndefOk,
    UndefBad,
    LazyOkNoUndef,
    LazyOk,
};

enum X64CABIClass {
    X64CABIClass_Unknown,
    X64CABIClass_MEMORY,
    X64CABIClass_MEMORY_nobyval,
    X64CABIClass_INTEGER,
    X64CABIClass_SSE,
};

struct IrExecutableSrc {
    ZigList<IrBasicBlockSrc *> basic_block_list;
    Buf *name;
    ZigFn *name_fn;
    size_t mem_slot_count;
    size_t next_debug_id;
    size_t *backward_branch_count;
    size_t *backward_branch_quota;
    ZigFn *fn_entry;
    Buf *c_import_buf;
    AstNode *source_node;
    IrExecutableGen *parent_exec;
    IrAnalyze *analysis;
    Scope *begin_scope;
    ErrorMsg *first_err_trace_msg;
    ZigList<Tld *> tld_list;

    bool is_inline;
    bool is_generic_instantiation;
    bool need_err_code_spill;

    // This is a function for use in the debugger to print
    // the source location.
    void src();
};

struct IrExecutableGen {
    ZigList<IrBasicBlockGen *> basic_block_list;
    Buf *name;
    ZigFn *name_fn;
    size_t mem_slot_count;
    size_t next_debug_id;
    size_t *backward_branch_count;
    size_t *backward_branch_quota;
    ZigFn *fn_entry;
    Buf *c_import_buf;
    AstNode *source_node;
    IrExecutableGen *parent_exec;
    IrExecutableSrc *source_exec;
    Scope *begin_scope;
    ErrorMsg *first_err_trace_msg;
    ZigList<Tld *> tld_list;

    bool is_inline;
    bool is_generic_instantiation;
    bool need_err_code_spill;

    // This is a function for use in the debugger to print
    // the source location.
    void src();
};

enum OutType {
    OutTypeUnknown,
    OutTypeExe,
    OutTypeLib,
    OutTypeObj,
};

enum ConstParentId {
    ConstParentIdNone,
    ConstParentIdStruct,
    ConstParentIdErrUnionCode,
    ConstParentIdErrUnionPayload,
    ConstParentIdOptionalPayload,
    ConstParentIdArray,
    ConstParentIdUnion,
    ConstParentIdScalar,
};

struct ConstParent {
    ConstParentId id;

    union {
        struct {
            ZigValue *array_val;
            size_t elem_index;
        } p_array;
        struct {
            ZigValue *struct_val;
            size_t field_index;
        } p_struct;
        struct {
            ZigValue *err_union_val;
        } p_err_union_code;
        struct {
            ZigValue *err_union_val;
        } p_err_union_payload;
        struct {
            ZigValue *optional_val;
        } p_optional_payload;
        struct {
            ZigValue *union_val;
        } p_union;
        struct {
            ZigValue *scalar_val;
        } p_scalar;
    } data;
};

struct ConstStructValue {
    ZigValue **fields;
};

struct ConstUnionValue {
    BigInt tag;
    ZigValue *payload;
};

enum ConstArraySpecial {
    ConstArraySpecialNone,
    ConstArraySpecialUndef,
    ConstArraySpecialBuf,
};

struct ConstArrayValue {
    ConstArraySpecial special;
    union {
        struct {
            ZigValue *elements;
        } s_none;
        Buf *s_buf;
    } data;
};

enum ConstPtrSpecial {
    // Enforce explicitly setting this ID by making the zero value invalid.
    ConstPtrSpecialInvalid,
    // The pointer is a reference to a single object.
    ConstPtrSpecialRef,
    // The pointer points to an element in an underlying array.
    // Not to be confused with ConstPtrSpecialSubArray.
    ConstPtrSpecialBaseArray,
    // The pointer points to a field in an underlying struct.
    ConstPtrSpecialBaseStruct,
    // The pointer points to the error set field of an error union
    ConstPtrSpecialBaseErrorUnionCode,
    // The pointer points to the payload field of an error union
    ConstPtrSpecialBaseErrorUnionPayload,
    // The pointer points to the payload field of an optional
    ConstPtrSpecialBaseOptionalPayload,
    // This means that we did a compile-time pointer reinterpret and we cannot
    // understand the value of pointee at compile time. However, we will still
    // emit a binary with a compile time known address.
    // In this case index is the numeric address value.
    ConstPtrSpecialHardCodedAddr,
    // This means that the pointer represents memory of assigning to _.
    // That is, storing discards the data, and loading is invalid.
    ConstPtrSpecialDiscard,
    // This is actually a function.
    ConstPtrSpecialFunction,
    // This means the pointer is null. This is only allowed when the type is ?*T.
    // We use this instead of ConstPtrSpecialHardCodedAddr because often we check
    // for that value to avoid doing comptime work.
    // We need the data layout for ConstCastOnly == true
    // types to be the same, so all optionals of pointer types use x_ptr
    // instead of x_optional.
    ConstPtrSpecialNull,
    // The pointer points to a sub-array (not an individual element).
    // Not to be confused with ConstPtrSpecialBaseArray. However, it uses the same
    // union payload struct (base_array).
    ConstPtrSpecialSubArray,
};

enum ConstPtrMut {
    // The pointer points to memory that is known at compile time and immutable.
    ConstPtrMutComptimeConst,
    // This means that the pointer points to memory used by a comptime variable,
    // so attempting to write a non-compile-time known value is an error
    // But the underlying value is allowed to change at compile time.
    ConstPtrMutComptimeVar,
    // The pointer points to memory that is known only at runtime.
    // For example it may point to the initializer value of a variable.
    ConstPtrMutRuntimeVar,
    // The pointer points to memory for which it must be inferred whether the
    // value is comptime known or not.
    ConstPtrMutInfer,
};

struct ConstPtrValue {
    ConstPtrSpecial special;
    ConstPtrMut mut;

    union {
        struct {
            ZigValue *pointee;
        } ref;
        struct {
            ZigValue *array_val;
            size_t elem_index;
        } base_array;
        struct {
            ZigValue *struct_val;
            size_t field_index;
        } base_struct;
        struct {
            ZigValue *err_union_val;
        } base_err_union_code;
        struct {
            ZigValue *err_union_val;
        } base_err_union_payload;
        struct {
            ZigValue *optional_val;
        } base_optional_payload;
        struct {
            uint64_t addr;
        } hard_coded_addr;
        struct {
            ZigFn *fn_entry;
        } fn;
    } data;
};

struct ConstErrValue {
    ZigValue *error_set;
    ZigValue *payload;
};

struct ConstBoundFnValue {
    ZigFn *fn;
    IrInstGen *first_arg;
    IrInst *first_arg_src;
};

struct ConstArgTuple {
    size_t start_index;
    size_t end_index;
};

enum ConstValSpecial {
    ConstValSpecialRuntime,
    ConstValSpecialStatic,
    ConstValSpecialUndef,
    ConstValSpecialLazy,
};

enum RuntimeHintErrorUnion {
    RuntimeHintErrorUnionUnknown,
    RuntimeHintErrorUnionError,
    RuntimeHintErrorUnionNonError,
};

enum RuntimeHintOptional {
    RuntimeHintOptionalUnknown,
    RuntimeHintOptionalNull, // TODO is this value even possible? if this is the case it might mean the const value is compile time known.
    RuntimeHintOptionalNonNull,
};

enum RuntimeHintPtr {
    RuntimeHintPtrUnknown,
    RuntimeHintPtrStack,
    RuntimeHintPtrNonStack,
};

enum RuntimeHintSliceId {
    RuntimeHintSliceIdUnknown,
    RuntimeHintSliceIdLen,
};

struct RuntimeHintSlice {
    enum RuntimeHintSliceId id;
    uint64_t len;
};

enum LazyValueId {
    LazyValueIdInvalid,
    LazyValueIdAlignOf,
    LazyValueIdSizeOf,
    LazyValueIdPtrType,
    LazyValueIdOptType,
    LazyValueIdSliceType,
    LazyValueIdFnType,
    LazyValueIdErrUnionType,
    LazyValueIdArrayType,
    LazyValueIdTypeInfoDecls,
};

struct LazyValue {
    LazyValueId id;
};

struct LazyValueTypeInfoDecls {
    LazyValue base;

    IrAnalyze *ira;

    ScopeDecls *decls_scope;
    IrInst *source_instr;
};

struct LazyValueAlignOf {
    LazyValue base;

    IrAnalyze *ira;
    IrInstGen *target_type;
};

struct LazyValueSizeOf {
    LazyValue base;

    IrAnalyze *ira;
    IrInstGen *target_type;

    bool bit_size;
};

struct LazyValueSliceType {
    LazyValue base;

    IrAnalyze *ira;
    IrInstGen *sentinel; // can be null
    IrInstGen *elem_type;
    IrInstGen *align_inst; // can be null

    bool is_const;
    bool is_volatile;
    bool is_allowzero;
};

struct LazyValueArrayType {
    LazyValue base;

    IrAnalyze *ira;
    IrInstGen *sentinel; // can be null
    IrInstGen *elem_type;
    uint64_t length;
};

struct LazyValuePtrType {
    LazyValue base;

    IrAnalyze *ira;
    IrInstGen *sentinel; // can be null
    IrInstGen *elem_type;
    IrInstGen *align_inst; // can be null

    PtrLen ptr_len;
    uint32_t bit_offset_in_host;

    uint32_t host_int_bytes;
    bool is_const;
    bool is_volatile;
    bool is_allowzero;
};

struct LazyValueOptType {
    LazyValue base;

    IrAnalyze *ira;
    IrInstGen *payload_type;
};

struct LazyValueFnType {
    LazyValue base;

    IrAnalyze *ira;
    AstNode *proto_node;
    IrInstGen **param_types;
    IrInstGen *align_inst; // can be null
    IrInstGen *return_type;

    CallingConvention cc;
    bool is_generic;
};

struct LazyValueErrUnionType {
    LazyValue base;

    IrAnalyze *ira;
    IrInstGen *err_set_type;
    IrInstGen *payload_type;
    Buf *type_name;
};

struct ZigValue {
    ZigType *type;
    ConstValSpecial special;
    uint32_t llvm_align;
    ConstParent parent;
    LLVMValueRef llvm_value;
    LLVMValueRef llvm_global;

    union {
        // populated if special == ConstValSpecialLazy
        LazyValue *x_lazy;

        // populated if special == ConstValSpecialStatic
        BigInt x_bigint;
        BigFloat x_bigfloat;
        float16_t x_f16;
        float x_f32;
        double x_f64;
        float128_t x_f128;
        bool x_bool;
        ConstBoundFnValue x_bound_fn;
        ZigType *x_type;
        ZigValue *x_optional;
        ConstErrValue x_err_union;
        ErrorTableEntry *x_err_set;
        BigInt x_enum_tag;
        ConstStructValue x_struct;
        ConstUnionValue x_union;
        ConstArrayValue x_array;
        ConstPtrValue x_ptr;
        ConstArgTuple x_arg_tuple;
        Buf *x_enum_literal;

        // populated if special == ConstValSpecialRuntime
        RuntimeHintErrorUnion rh_error_union;
        RuntimeHintOptional rh_maybe;
        RuntimeHintPtr rh_ptr;
        RuntimeHintSlice rh_slice;
    } data;

    // uncomment this to find bugs. can't leave it uncommented because of a gcc-9 warning
    //ZigValue& operator= (const ZigValue &other) = delete; // use copy_const_val

    ZigValue(const ZigValue &other) = delete; // plz zero initialize with ZigValue val = {};

    // for use in debuggers
    void dump();
};

enum ReturnKnowledge {
    ReturnKnowledgeUnknown,
    ReturnKnowledgeKnownError,
    ReturnKnowledgeKnownNonError,
    ReturnKnowledgeKnownNull,
    ReturnKnowledgeKnownNonNull,
    ReturnKnowledgeSkipDefers,
};

enum VisibMod {
    VisibModPrivate,
    VisibModPub,
};

enum GlobalLinkageId {
    GlobalLinkageIdInternal,
    GlobalLinkageIdStrong,
    GlobalLinkageIdWeak,
    GlobalLinkageIdLinkOnce,
};

enum TldId {
    TldIdVar,
    TldIdFn,
    TldIdContainer,
    TldIdCompTime,
    TldIdUsingNamespace,
};

enum TldResolution {
    TldResolutionUnresolved,
    TldResolutionResolving,
    TldResolutionInvalid,
    TldResolutionOkLazy,
    TldResolutionOk,
};

struct Tld {
    TldId id;
    Buf *name;
    VisibMod visib_mod;
    AstNode *source_node;

    ZigType *import;
    Scope *parent_scope;
    TldResolution resolution;
};

struct TldVar {
    Tld base;

    ZigVar *var;
    Buf *extern_lib_name;
    bool analyzing_type; // flag to detect dependency loops
};

struct TldFn {
    Tld base;

    ZigFn *fn_entry;
    Buf *extern_lib_name;
};

struct TldContainer {
    Tld base;

    ScopeDecls *decls_scope;
    ZigType *type_entry;
};

struct TldCompTime {
    Tld base;
};

struct TldUsingNamespace {
    Tld base;

    ZigValue *using_namespace_value;
};

struct TypeEnumField {
    Buf *name;
    BigInt value;
    uint32_t decl_index;
    AstNode *decl_node;
};

struct TypeUnionField {
    Buf *name;
    ZigType *type_entry; // available after ResolveStatusSizeKnown
    ZigValue *type_val; // available after ResolveStatusZeroBitsKnown
    TypeEnumField *enum_field;
    AstNode *decl_node;
    uint32_t gen_index;
    uint32_t align;
};

enum NodeType {
    NodeTypeFnProto,
    NodeTypeFnDef,
    NodeTypeParamDecl,
    NodeTypeBlock,
    NodeTypeGroupedExpr,
    NodeTypeReturnExpr,
    NodeTypeDefer,
    NodeTypeVariableDeclaration,
    NodeTypeTestDecl,
    NodeTypeBinOpExpr,
    NodeTypeCatchExpr,
    NodeTypeFloatLiteral,
    NodeTypeIntLiteral,
    NodeTypeStringLiteral,
    NodeTypeCharLiteral,
    NodeTypeSymbol,
    NodeTypePrefixOpExpr,
    NodeTypePointerType,
    NodeTypeFnCallExpr,
    NodeTypeArrayAccessExpr,
    NodeTypeSliceExpr,
    NodeTypeFieldAccessExpr,
    NodeTypePtrDeref,
    NodeTypeUnwrapOptional,
    NodeTypeUsingNamespace,
    NodeTypeBoolLiteral,
    NodeTypeNullLiteral,
    NodeTypeUndefinedLiteral,
    NodeTypeUnreachable,
    NodeTypeIfBoolExpr,
    NodeTypeWhileExpr,
    NodeTypeForExpr,
    NodeTypeSwitchExpr,
    NodeTypeSwitchProng,
    NodeTypeSwitchRange,
    NodeTypeCompTime,
    NodeTypeNoSuspend,
    NodeTypeBreak,
    NodeTypeContinue,
    NodeTypeAsmExpr,
    NodeTypeContainerDecl,
    NodeTypeStructField,
    NodeTypeContainerInitExpr,
    NodeTypeStructValueField,
    NodeTypeArrayType,
    NodeTypeInferredArrayType,
    NodeTypeErrorType,
    NodeTypeIfErrorExpr,
    NodeTypeIfOptional,
    NodeTypeErrorSetDecl,
    NodeTypeErrorSetField,
    NodeTypeResume,
    NodeTypeAwaitExpr,
    NodeTypeSuspend,
    NodeTypeAnyFrameType,
    NodeTypeEnumLiteral,
    NodeTypeAnyTypeField,
};

enum FnInline {
    FnInlineAuto,
    FnInlineAlways,
    FnInlineNever,
};

struct AstNodeFnProto {
    Buf *name;
    ZigList<AstNode *> params;
    AstNode *return_type;
    Token *return_anytype_token;
    AstNode *fn_def_node;
    // populated if this is an extern declaration
    Buf *lib_name;
    // populated if the "align A" is present
    AstNode *align_expr;
    // populated if the "section(S)" is present
    AstNode *section_expr;
    // populated if the "callconv(S)" is present
    AstNode *callconv_expr;
    Buf doc_comments;

    FnInline fn_inline;

    VisibMod visib_mod;
    bool auto_err_set;
    bool is_var_args;
    bool is_extern;
    bool is_export;
};

struct AstNodeFnDef {
    AstNode *fn_proto;
    AstNode *body;
};

struct AstNodeParamDecl {
    Buf *name;
    AstNode *type;
    Token *anytype_token;
    Buf doc_comments;
    bool is_noalias;
    bool is_comptime;
    bool is_var_args;
};

struct AstNodeBlock {
    Buf *name;
    ZigList<AstNode *> statements;
};

enum ReturnKind {
    ReturnKindUnconditional,
    ReturnKindError,
};

struct AstNodeReturnExpr {
    ReturnKind kind;
    // might be null in case of return void;
    AstNode *expr;
};

struct AstNodeDefer {
    ReturnKind kind;
    AstNode *err_payload;
    AstNode *expr;

    // temporary data used in IR generation
    Scope *child_scope;
    Scope *expr_scope;
};

struct AstNodeVariableDeclaration {
    Buf *symbol;
    // one or both of type and expr will be non null
    AstNode *type;
    AstNode *expr;
    // populated if this is an extern declaration
    Buf *lib_name;
    // populated if the "align(A)" is present
    AstNode *align_expr;
    // populated if the "section(S)" is present
    AstNode *section_expr;
    Token *threadlocal_tok;
    Buf doc_comments;

    VisibMod visib_mod;
    bool is_const;
    bool is_comptime;
    bool is_export;
    bool is_extern;
};

struct AstNodeTestDecl {
    Buf *name;

    AstNode *body;
};

enum BinOpType {
    BinOpTypeInvalid,
    BinOpTypeAssign,
    BinOpTypeAssignTimes,
    BinOpTypeAssignTimesWrap,
    BinOpTypeAssignDiv,
    BinOpTypeAssignMod,
    BinOpTypeAssignPlus,
    BinOpTypeAssignPlusWrap,
    BinOpTypeAssignMinus,
    BinOpTypeAssignMinusWrap,
    BinOpTypeAssignBitShiftLeft,
    BinOpTypeAssignBitShiftRight,
    BinOpTypeAssignBitAnd,
    BinOpTypeAssignBitXor,
    BinOpTypeAssignBitOr,
    BinOpTypeAssignMergeErrorSets,
    BinOpTypeBoolOr,
    BinOpTypeBoolAnd,
    BinOpTypeCmpEq,
    BinOpTypeCmpNotEq,
    BinOpTypeCmpLessThan,
    BinOpTypeCmpGreaterThan,
    BinOpTypeCmpLessOrEq,
    BinOpTypeCmpGreaterOrEq,
    BinOpTypeBinOr,
    BinOpTypeBinXor,
    BinOpTypeBinAnd,
    BinOpTypeBitShiftLeft,
    BinOpTypeBitShiftRight,
    BinOpTypeAdd,
    BinOpTypeAddWrap,
    BinOpTypeSub,
    BinOpTypeSubWrap,
    BinOpTypeMult,
    BinOpTypeMultWrap,
    BinOpTypeDiv,
    BinOpTypeMod,
    BinOpTypeUnwrapOptional,
    BinOpTypeArrayCat,
    BinOpTypeArrayMult,
    BinOpTypeErrorUnion,
    BinOpTypeMergeErrorSets,
};

struct AstNodeBinOpExpr {
    AstNode *op1;
    BinOpType bin_op;
    AstNode *op2;
};

struct AstNodeCatchExpr {
    AstNode *op1;
    AstNode *symbol; // can be null
    AstNode *op2;
};

struct AstNodeUnwrapOptional {
    AstNode *expr;
};

// Must be synchronized with std.builtin.CallOptions.Modifier
enum CallModifier {
    CallModifierNone,
    CallModifierAsync,
    CallModifierNeverTail,
    CallModifierNeverInline,
    CallModifierNoSuspend,
    CallModifierAlwaysTail,
    CallModifierAlwaysInline,
    CallModifierCompileTime,

    // These are additional tags in the compiler, but not exposed in the std lib.
    CallModifierBuiltin,
};

struct AstNodeFnCallExpr {
    AstNode *fn_ref_expr;
    ZigList<AstNode *> params;
    CallModifier modifier;
    bool seen; // used by @compileLog
};

struct AstNodeArrayAccessExpr {
    AstNode *array_ref_expr;
    AstNode *subscript;
};

struct AstNodeSliceExpr {
    AstNode *array_ref_expr;
    AstNode *start;
    AstNode *end;
    AstNode *sentinel; // can be null
};

struct AstNodeFieldAccessExpr {
    AstNode *struct_expr;
    Buf *field_name;
};

struct AstNodePtrDerefExpr {
    AstNode *target;
};

enum PrefixOp {
    PrefixOpInvalid,
    PrefixOpBoolNot,
    PrefixOpBinNot,
    PrefixOpNegation,
    PrefixOpNegationWrap,
    PrefixOpOptional,
    PrefixOpAddrOf,
};

struct AstNodePrefixOpExpr {
    PrefixOp prefix_op;
    AstNode *primary_expr;
};

struct AstNodePointerType {
    Token *star_token;
    AstNode *sentinel;
    AstNode *align_expr;
    BigInt *bit_offset_start;
    BigInt *host_int_bytes;
    AstNode *op_expr;
    Token *allow_zero_token;
    bool is_const;
    bool is_volatile;
};

struct AstNodeInferredArrayType {
    AstNode *sentinel; // can be null
    AstNode *child_type;
};

struct AstNodeArrayType {
    AstNode *size;
    AstNode *sentinel;
    AstNode *child_type;
    AstNode *align_expr;
    Token *allow_zero_token;
    bool is_const;
    bool is_volatile;
};

struct AstNodeUsingNamespace {
    VisibMod visib_mod;
    AstNode *expr;
};

struct AstNodeIfBoolExpr {
    AstNode *condition;
    AstNode *then_block;
    AstNode *else_node; // null, block node, or other if expr node
};

struct AstNodeTryExpr {
    Buf *var_symbol;
    bool var_is_ptr;
    AstNode *target_node;
    AstNode *then_node;
    AstNode *else_node;
    Buf *err_symbol;
};

struct AstNodeTestExpr {
    Buf *var_symbol;
    bool var_is_ptr;
    AstNode *target_node;
    AstNode *then_node;
    AstNode *else_node; // null, block node, or other if expr node
};

struct AstNodeWhileExpr {
    Buf *name;
    AstNode *condition;
    Buf *var_symbol;
    bool var_is_ptr;
    AstNode *continue_expr;
    AstNode *body;
    AstNode *else_node;
    Buf *err_symbol;
    bool is_inline;
};

struct AstNodeForExpr {
    Buf *name;
    AstNode *array_expr;
    AstNode *elem_node; // always a symbol
    AstNode *index_node; // always a symbol, might be null
    AstNode *body;
    AstNode *else_node; // can be null
    bool elem_is_ptr;
    bool is_inline;
};

struct AstNodeSwitchExpr {
    AstNode *expr;
    ZigList<AstNode *> prongs;
};

struct AstNodeSwitchProng {
    ZigList<AstNode *> items;
    AstNode *var_symbol;
    AstNode *expr;
    bool var_is_ptr;
    bool any_items_are_range;
};

struct AstNodeSwitchRange {
    AstNode *start;
    AstNode *end;
};

struct AstNodeCompTime {
    AstNode *expr;
};

struct AstNodeNoSuspend {
    AstNode *expr;
};

struct AsmOutput {
    Buf *asm_symbolic_name;
    Buf *constraint;
    Buf *variable_name;
    AstNode *return_type; // null unless "=r" and return
};

struct AsmInput {
    Buf *asm_symbolic_name;
    Buf *constraint;
    AstNode *expr;
};

struct SrcPos {
    size_t line;
    size_t column;
};

enum AsmTokenId {
    AsmTokenIdTemplate,
    AsmTokenIdPercent,
    AsmTokenIdVar,
    AsmTokenIdUniqueId,
};

struct AsmToken {
    enum AsmTokenId id;
    size_t start;
    size_t end;
};

struct AstNodeAsmExpr {
    Token *volatile_token;
    AstNode *asm_template;
    ZigList<AsmOutput*> output_list;
    ZigList<AsmInput*> input_list;
    ZigList<Buf*> clobber_list;
};

enum ContainerKind {
    ContainerKindStruct,
    ContainerKindEnum,
    ContainerKindUnion,
    ContainerKindOpaque,
};

enum ContainerLayout {
    ContainerLayoutAuto,
    ContainerLayoutExtern,
    ContainerLayoutPacked,
};

struct AstNodeContainerDecl {
    AstNode *init_arg_expr; // enum(T), struct(endianness), or union(T), or union(enum(T))
    ZigList<AstNode *> fields;
    ZigList<AstNode *> decls;
    Buf doc_comments;

    ContainerKind kind;
    ContainerLayout layout;

    bool auto_enum, is_root; // union(enum)
};

struct AstNodeErrorSetField {
    Buf doc_comments;
    AstNode *field_name;
};

struct AstNodeErrorSetDecl {
    // Each AstNode could be AstNodeErrorSetField or just AstNodeSymbolExpr to save memory
    ZigList<AstNode *> decls;
};

struct AstNodeStructField {
    Buf *name;
    AstNode *type;
    AstNode *value;
    // populated if the "align(A)" is present
    AstNode *align_expr;
    Buf doc_comments;
    Token *comptime_token;
};

struct AstNodeStringLiteral {
    Buf *buf;
};

struct AstNodeCharLiteral {
    uint32_t value;
};

struct AstNodeFloatLiteral {
    BigFloat *bigfloat;

    // overflow is true if when parsing the number, we discovered it would not
    // fit without losing data in a double
    bool overflow;
};

struct AstNodeIntLiteral {
    BigInt *bigint;
};

struct AstNodeStructValueField {
    Buf *name;
    AstNode *expr;
};

enum ContainerInitKind {
    ContainerInitKindStruct,
    ContainerInitKindArray,
};

struct AstNodeContainerInitExpr {
    AstNode *type;
    ZigList<AstNode *> entries;
    ContainerInitKind kind;
};

struct AstNodeNullLiteral {
};

struct AstNodeUndefinedLiteral {
};

struct AstNodeThisLiteral {
};

struct AstNodeSymbolExpr {
    Buf *symbol;
};

struct AstNodeBoolLiteral {
    bool value;
};

struct AstNodeBreakExpr {
    Buf *name;
    AstNode *expr; // may be null
};

struct AstNodeResumeExpr {
    AstNode *expr;
};

struct AstNodeContinueExpr {
    Buf *name;
};

struct AstNodeUnreachableExpr {
};


struct AstNodeErrorType {
};

struct AstNodeAwaitExpr {
    AstNode *expr;
};

struct AstNodeSuspend {
    AstNode *block;
};

struct AstNodeAnyFrameType {
    AstNode *payload_type; // can be NULL
};

struct AstNodeEnumLiteral {
    Token *period;
    Token *identifier;
};

struct AstNode {
    enum NodeType type;
    bool already_traced_this_node;
    size_t line;
    size_t column;
    ZigType *owner;
    union {
        AstNodeFnDef fn_def;
        AstNodeFnProto fn_proto;
        AstNodeParamDecl param_decl;
        AstNodeBlock block;
        AstNode * grouped_expr;
        AstNodeReturnExpr return_expr;
        AstNodeDefer defer;
        AstNodeVariableDeclaration variable_declaration;
        AstNodeTestDecl test_decl;
        AstNodeBinOpExpr bin_op_expr;
        AstNodeCatchExpr unwrap_err_expr;
        AstNodeUnwrapOptional unwrap_optional;
        AstNodePrefixOpExpr prefix_op_expr;
        AstNodePointerType pointer_type;
        AstNodeFnCallExpr fn_call_expr;
        AstNodeArrayAccessExpr array_access_expr;
        AstNodeSliceExpr slice_expr;
        AstNodeUsingNamespace using_namespace;
        AstNodeIfBoolExpr if_bool_expr;
        AstNodeTryExpr if_err_expr;
        AstNodeTestExpr test_expr;
        AstNodeWhileExpr while_expr;
        AstNodeForExpr for_expr;
        AstNodeSwitchExpr switch_expr;
        AstNodeSwitchProng switch_prong;
        AstNodeSwitchRange switch_range;
        AstNodeCompTime comptime_expr;
        AstNodeNoSuspend nosuspend_expr;
        AstNodeAsmExpr asm_expr;
        AstNodeFieldAccessExpr field_access_expr;
        AstNodePtrDerefExpr ptr_deref_expr;
        AstNodeContainerDecl container_decl;
        AstNodeStructField struct_field;
        AstNodeStringLiteral string_literal;
        AstNodeCharLiteral char_literal;
        AstNodeFloatLiteral float_literal;
        AstNodeIntLiteral int_literal;
        AstNodeContainerInitExpr container_init_expr;
        AstNodeStructValueField struct_val_field;
        AstNodeNullLiteral null_literal;
        AstNodeUndefinedLiteral undefined_literal;
        AstNodeThisLiteral this_literal;
        AstNodeSymbolExpr symbol_expr;
        AstNodeBoolLiteral bool_literal;
        AstNodeBreakExpr break_expr;
        AstNodeContinueExpr continue_expr;
        AstNodeUnreachableExpr unreachable_expr;
        AstNodeArrayType array_type;
        AstNodeInferredArrayType inferred_array_type;
        AstNodeErrorType error_type;
        AstNodeErrorSetDecl err_set_decl;
        AstNodeErrorSetField err_set_field;
        AstNodeResumeExpr resume_expr;
        AstNodeAwaitExpr await_expr;
        AstNodeSuspend suspend;
        AstNodeAnyFrameType anyframe_type;
        AstNodeEnumLiteral enum_literal;
    } data;

    // This is a function for use in the debugger to print
    // the source location.
    void src();
};

// this struct is allocated with allocate_nonzero
struct FnTypeParamInfo {
    bool is_noalias;
    ZigType *type;
};

struct GenericFnTypeId {
    CodeGen *codegen;
    ZigFn *fn_entry;
    ZigValue *params;
    size_t param_count;
};

uint32_t generic_fn_type_id_hash(GenericFnTypeId *id);
bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b);

struct FnTypeId {
    ZigType *return_type;
    FnTypeParamInfo *param_info;
    size_t param_count;
    size_t next_param_index;
    bool is_var_args;
    CallingConvention cc;
    uint32_t alignment;
};

uint32_t fn_type_id_hash(FnTypeId*);
bool fn_type_id_eql(FnTypeId *a, FnTypeId *b);

static const uint32_t VECTOR_INDEX_NONE = UINT32_MAX;
static const uint32_t VECTOR_INDEX_RUNTIME = UINT32_MAX - 1;

struct InferredStructField {
    ZigType *inferred_struct_type;
    Buf *field_name;
    bool already_resolved;
};

struct ZigTypePointer {
    ZigType *child_type;
    ZigType *slice_parent;

    // Anonymous struct literal syntax uses this when the result location has
    // no type in it. This field is null if this pointer does not refer to
    // a field of a currently-being-inferred struct type.
    // When this is non-null, the pointer is pointing to the base of the inferred
    // struct.
    InferredStructField *inferred_struct_field;

    // This can be null. If it is non-null, it means the pointer is terminated by this
    // sentinel value. This is most commonly used for C-style strings, with a 0 byte
    // to specify the length of the memory pointed to.
    ZigValue *sentinel;

    PtrLen ptr_len;
    uint32_t explicit_alignment; // 0 means use ABI alignment

    uint32_t bit_offset_in_host;
    // size of host integer. 0 means no host integer; this field is aligned
    // when vector_index != VECTOR_INDEX_NONE this is the len of the containing vector
    uint32_t host_int_bytes;

    uint32_t vector_index; // see the VECTOR_INDEX_* constants
    bool is_const;
    bool is_volatile;
    bool allow_zero;
    bool resolve_loop_flag_zero_bits;
};

struct ZigTypeInt {
    uint32_t bit_count;
    bool is_signed;
};

struct ZigTypeFloat {
    size_t bit_count;
};

// Needs to have the same memory layout as ZigTypeVector
struct ZigTypeArray {
    ZigType *child_type;
    uint64_t len;
    ZigValue *sentinel;
};

struct TypeStructField {
    Buf *name;
    ZigType *type_entry; // available after ResolveStatusSizeKnown
    ZigValue *type_val; // available after ResolveStatusZeroBitsKnown
    size_t src_index;
    size_t gen_index;
    size_t offset; // byte offset from beginning of struct
    AstNode *decl_node;
    ZigValue *init_val; // null and then memoized
    uint32_t bit_offset_in_host; // offset from the memory at gen_index
    uint32_t host_int_bytes; // size of host integer
    uint32_t align;
    bool is_comptime;
};

enum ResolveStatus {
    ResolveStatusUnstarted,
    ResolveStatusInvalid,
    ResolveStatusBeingInferred,
    ResolveStatusZeroBitsKnown,
    ResolveStatusAlignmentKnown,
    ResolveStatusSizeKnown,
    ResolveStatusLLVMFwdDecl,
    ResolveStatusLLVMFull,
};

struct ZigPackage {
    Buf root_src_dir;
    Buf root_src_path; // relative to root_src_dir
    Buf pkg_path; // a.b.c.d which follows the package dependency chain from the root package

    // reminder: hash tables must be initialized before use
    HashMap<Buf *, ZigPackage *, buf_hash, buf_eql_buf> package_table;

    bool added_to_cache;
};

// Stuff that only applies to a struct which is the implicit root struct of a file
struct RootStruct {
    ZigPackage *package;
    Buf *path; // relative to root_package->root_src_dir
    ZigList<size_t> *line_offsets;
    Buf *source_code;
    ZigLLVMDIFile *di_file;
};

enum StructSpecial {
    StructSpecialNone,
    StructSpecialSlice,
    StructSpecialInferredTuple,
    StructSpecialInferredStruct,
};

struct ZigTypeStruct {
    AstNode *decl_node;
    TypeStructField **fields;
    ScopeDecls *decls_scope;
    HashMap<Buf *, TypeStructField *, buf_hash, buf_eql_buf> fields_by_name;
    RootStruct *root_struct;
    uint32_t *host_int_bytes; // available for packed structs, indexed by gen_index
    size_t llvm_full_type_queue_index;

    uint32_t src_field_count;
    uint32_t gen_field_count;

    ContainerLayout layout;
    ResolveStatus resolve_status;

    StructSpecial special;
    // whether any of the fields require comptime
    // known after ResolveStatusZeroBitsKnown
    bool requires_comptime;
    bool resolve_loop_flag_zero_bits;
    bool resolve_loop_flag_other;
    bool created_by_at_type;
};

struct ZigTypeOptional {
    ZigType *child_type;
    ResolveStatus resolve_status;
};

struct ZigTypeErrorUnion {
    ZigType *err_set_type;
    ZigType *payload_type;
    size_t pad_bytes;
    LLVMTypeRef pad_llvm_type;
};

struct ZigTypeErrorSet {
    ErrorTableEntry **errors;
    ZigFn *infer_fn;
    uint32_t err_count;
    bool incomplete;
};

struct ZigTypeEnum {
    AstNode *decl_node;
    TypeEnumField *fields;
    ZigType *tag_int_type;

    ScopeDecls *decls_scope;

    LLVMValueRef name_function;

    HashMap<Buf *, TypeEnumField *, buf_hash, buf_eql_buf> fields_by_name;
    uint32_t src_field_count;

    ContainerLayout layout;
    ResolveStatus resolve_status;

    bool has_explicit_tag_type;
    bool non_exhaustive;
    bool resolve_loop_flag;
};

uint32_t type_ptr_hash(const ZigType *ptr);
bool type_ptr_eql(const ZigType *a, const ZigType *b);

uint32_t pkg_ptr_hash(const ZigPackage *ptr);
bool pkg_ptr_eql(const ZigPackage *a, const ZigPackage *b);

uint32_t tld_ptr_hash(const Tld *ptr);
bool tld_ptr_eql(const Tld *a, const Tld *b);

uint32_t node_ptr_hash(const AstNode *ptr);
bool node_ptr_eql(const AstNode *a, const AstNode *b);

uint32_t fn_ptr_hash(const ZigFn *ptr);
bool fn_ptr_eql(const ZigFn *a, const ZigFn *b);

uint32_t err_ptr_hash(const ErrorTableEntry *ptr);
bool err_ptr_eql(const ErrorTableEntry *a, const ErrorTableEntry *b);

struct ZigTypeUnion {
    AstNode *decl_node;
    TypeUnionField *fields;
    ScopeDecls *decls_scope;
    HashMap<Buf *, TypeUnionField *, buf_hash, buf_eql_buf> fields_by_name;
    ZigType *tag_type; // always an enum or null
    LLVMTypeRef union_llvm_type;
    TypeUnionField *most_aligned_union_member;
    size_t gen_union_index;
    size_t gen_tag_index;
    size_t union_abi_size;

    uint32_t src_field_count;
    uint32_t gen_field_count;

    ContainerLayout layout;
    ResolveStatus resolve_status;

    bool have_explicit_tag_type;
    // whether any of the fields require comptime
    // the value is not valid until zero_bits_known == true
    bool requires_comptime;
    bool resolve_loop_flag_zero_bits;
    bool resolve_loop_flag_other;
};

struct FnGenParamInfo {
    size_t src_index;
    size_t gen_index;
    bool is_byval;
    ZigType *type;
};

struct ZigTypeFn {
    FnTypeId fn_type_id;
    bool is_generic;
    ZigType *gen_return_type;
    size_t gen_param_count;
    FnGenParamInfo *gen_param_info;

    LLVMTypeRef raw_type_ref;
    ZigLLVMDIType *raw_di_type;

    ZigType *bound_fn_parent;
};

struct ZigTypeBoundFn {
    ZigType *fn_type;
};

// Needs to have the same memory layout as ZigTypeArray
struct ZigTypeVector {
    // The type must be a pointer, integer, bool, or float
    ZigType *elem_type;
    uint64_t len;
    size_t padding;
};

// A lot of code is relying on ZigTypeArray and ZigTypeVector having the same layout/size
static_assert(sizeof(ZigTypeVector) == sizeof(ZigTypeArray), "Size of ZigTypeVector and ZigTypeArray do not match!");

enum ZigTypeId {
    ZigTypeIdInvalid,
    ZigTypeIdMetaType,
    ZigTypeIdVoid,
    ZigTypeIdBool,
    ZigTypeIdUnreachable,
    ZigTypeIdInt,
    ZigTypeIdFloat,
    ZigTypeIdPointer,
    ZigTypeIdArray,
    ZigTypeIdStruct,
    ZigTypeIdComptimeFloat,
    ZigTypeIdComptimeInt,
    ZigTypeIdUndefined,
    ZigTypeIdNull,
    ZigTypeIdOptional,
    ZigTypeIdErrorUnion,
    ZigTypeIdErrorSet,
    ZigTypeIdEnum,
    ZigTypeIdUnion,
    ZigTypeIdFn,
    ZigTypeIdBoundFn,
    ZigTypeIdOpaque,
    ZigTypeIdFnFrame,
    ZigTypeIdAnyFrame,
    ZigTypeIdVector,
    ZigTypeIdEnumLiteral,
};

enum OnePossibleValue {
    OnePossibleValueInvalid,
    OnePossibleValueNo,
    OnePossibleValueYes,
};

struct ZigTypeOpaque {
    AstNode *decl_node;
    Buf *bare_name;

    ScopeDecls *decls_scope;
};

struct ZigTypeFnFrame {
    ZigFn *fn;
    ZigType *locals_struct;

    // This is set to the type that resolving the frame currently depends on, null if none.
    // It's for generating a helpful error message.
    ZigType *resolve_loop_type;
    AstNode *resolve_loop_src_node;
    bool reported_loop_err;
};

struct ZigTypeAnyFrame {
    ZigType *result_type; // null if `anyframe` instead of `anyframe->T`
};

struct ZigType {
    ZigTypeId id;
    Buf name;

    // These are not supposed to be accessed directly. They're
    // null during semantic analysis, memoized with get_llvm_type
    // and get_llvm_di_type
    LLVMTypeRef llvm_type;
    ZigLLVMDIType *llvm_di_type;

    union {
        ZigTypePointer pointer;
        ZigTypeInt integral;
        ZigTypeFloat floating;
        ZigTypeArray array;
        ZigTypeStruct structure;
        ZigTypeOptional maybe;
        ZigTypeErrorUnion error_union;
        ZigTypeErrorSet error_set;
        ZigTypeEnum enumeration;
        ZigTypeUnion unionation;
        ZigTypeFn fn;
        ZigTypeBoundFn bound_fn;
        ZigTypeVector vector;
        ZigTypeOpaque opaque;
        ZigTypeFnFrame frame;
        ZigTypeAnyFrame any_frame;
    } data;

    // use these fields to make sure we don't duplicate type table entries for the same type
    ZigType *pointer_parent[2]; // [0 - mut, 1 - const]
    ZigType *optional_parent;
    ZigType *any_frame_parent;
    // If we generate a constant name value for this type, we memoize it here.
    // The type of this is array
    ZigValue *cached_const_name_val;

    OnePossibleValue one_possible_value;
    // Known after ResolveStatusAlignmentKnown.
    uint32_t abi_align;
    // The offset in bytes between consecutive array elements of this type. Known
    // after ResolveStatusSizeKnown.
    size_t abi_size;
    // Number of bits of information in this type. Known after ResolveStatusSizeKnown.
    size_t size_in_bits;
};

enum FnAnalState {
    FnAnalStateReady,
    FnAnalStateProbing,
    FnAnalStateComplete,
    FnAnalStateInvalid,
};

struct GlobalExport {
    Buf name;
    GlobalLinkageId linkage;
};

struct ZigFn {
    LLVMValueRef llvm_value;
    const char *llvm_name;
    AstNode *proto_node;
    AstNode *body_node;
    ScopeFnDef *fndef_scope; // parent should be the top level decls or container decls
    Scope *child_scope; // parent is scope for last parameter
    ScopeBlock *def_scope; // parent is child_scope
    Buf symbol_name;
    // This is the function type assuming the function does not suspend.
    // Note that for an async function, this can be shared with non-async functions. So the value here
    // should only be read for things in common between non-async and async function types.
    ZigType *type_entry;
    // For normal functions one could use the type_entry->raw_type_ref and type_entry->raw_di_type.
    // However for functions that suspend, those values could possibly be their non-suspending equivalents.
    // So these values should be preferred.
    LLVMTypeRef raw_type_ref;
    ZigLLVMDIType *raw_di_type;

    ZigType *frame_type;
    // in the case of normal functions this is the implicit return type
    // in the case of async functions this is the implicit return type according to the
    // zig source code, not according to zig ir
    ZigType *src_implicit_return_type;
    IrExecutableSrc *ir_executable;
    IrExecutableGen analyzed_executable;
    size_t prealloc_bbc;
    size_t prealloc_backward_branch_quota;
    AstNode **param_source_nodes;
    Buf **param_names;
    IrInstGen *err_code_spill;
    AstNode *assumed_non_async;

    AstNode *fn_no_inline_set_node;
    AstNode *fn_static_eval_set_node;

    ZigList<IrInstGenAlloca *> alloca_gen_list;
    ZigList<ZigVar *> variable_list;

    Buf *section_name;
    AstNode *set_alignstack_node;

    AstNode *set_cold_node;
    const AstNode *inferred_async_node;
    ZigFn *inferred_async_fn;
    AstNode *non_async_node;

    ZigList<GlobalExport> export_list;
    ZigList<IrInstGenCall *> call_list;
    ZigList<IrInstGenAwait *> await_list;

    LLVMValueRef valgrind_client_request_array;

    FnInline fn_inline;
    FnAnalState anal_state;

    uint32_t align_bytes;
    uint32_t alignstack_value;

    bool calls_or_awaits_errorable_fn;
    bool is_cold;
    bool is_test;
};

uint32_t fn_table_entry_hash(ZigFn*);
bool fn_table_entry_eql(ZigFn *a, ZigFn *b);

enum BuiltinFnId {
    BuiltinFnIdInvalid,
    BuiltinFnIdMemcpy,
    BuiltinFnIdMemset,
    BuiltinFnIdSizeof,
    BuiltinFnIdAlignOf,
    BuiltinFnIdField,
    BuiltinFnIdTypeInfo,
    BuiltinFnIdType,
    BuiltinFnIdHasField,
    BuiltinFnIdTypeof,
    BuiltinFnIdAddWithOverflow,
    BuiltinFnIdSubWithOverflow,
    BuiltinFnIdMulWithOverflow,
    BuiltinFnIdShlWithOverflow,
    BuiltinFnIdMulAdd,
    BuiltinFnIdCInclude,
    BuiltinFnIdCDefine,
    BuiltinFnIdCUndef,
    BuiltinFnIdCompileErr,
    BuiltinFnIdCompileLog,
    BuiltinFnIdCtz,
    BuiltinFnIdClz,
    BuiltinFnIdPopCount,
    BuiltinFnIdBswap,
    BuiltinFnIdBitReverse,
    BuiltinFnIdImport,
    BuiltinFnIdCImport,
    BuiltinFnIdErrName,
    BuiltinFnIdBreakpoint,
    BuiltinFnIdReturnAddress,
    BuiltinFnIdEmbedFile,
    BuiltinFnIdCmpxchgWeak,
    BuiltinFnIdCmpxchgStrong,
    BuiltinFnIdFence,
    BuiltinFnIdDivExact,
    BuiltinFnIdDivTrunc,
    BuiltinFnIdDivFloor,
    BuiltinFnIdRem,
    BuiltinFnIdMod,
    BuiltinFnIdSqrt,
    BuiltinFnIdSin,
    BuiltinFnIdCos,
    BuiltinFnIdExp,
    BuiltinFnIdExp2,
    BuiltinFnIdLog,
    BuiltinFnIdLog2,
    BuiltinFnIdLog10,
    BuiltinFnIdFabs,
    BuiltinFnIdFloor,
    BuiltinFnIdCeil,
    BuiltinFnIdTrunc,
    BuiltinFnIdNearbyInt,
    BuiltinFnIdRound,
    BuiltinFnIdTruncate,
    BuiltinFnIdIntCast,
    BuiltinFnIdFloatCast,
    BuiltinFnIdErrSetCast,
    BuiltinFnIdIntToFloat,
    BuiltinFnIdFloatToInt,
    BuiltinFnIdBoolToInt,
    BuiltinFnIdErrToInt,
    BuiltinFnIdIntToErr,
    BuiltinFnIdEnumToInt,
    BuiltinFnIdIntToEnum,
    BuiltinFnIdVectorType,
    BuiltinFnIdShuffle,
    BuiltinFnIdSplat,
    BuiltinFnIdSetCold,
    BuiltinFnIdSetRuntimeSafety,
    BuiltinFnIdSetFloatMode,
    BuiltinFnIdTypeName,
    BuiltinFnIdPanic,
    BuiltinFnIdPtrCast,
    BuiltinFnIdBitCast,
    BuiltinFnIdIntToPtr,
    BuiltinFnIdPtrToInt,
    BuiltinFnIdTagName,
    BuiltinFnIdTagType,
    BuiltinFnIdFieldParentPtr,
    BuiltinFnIdByteOffsetOf,
    BuiltinFnIdBitOffsetOf,
    BuiltinFnIdAsyncCall,
    BuiltinFnIdShlExact,
    BuiltinFnIdShrExact,
    BuiltinFnIdSetEvalBranchQuota,
    BuiltinFnIdAlignCast,
    BuiltinFnIdThis,
    BuiltinFnIdSetAlignStack,
    BuiltinFnIdExport,
    BuiltinFnIdErrorReturnTrace,
    BuiltinFnIdAtomicRmw,
    BuiltinFnIdAtomicLoad,
    BuiltinFnIdAtomicStore,
    BuiltinFnIdHasDecl,
    BuiltinFnIdUnionInit,
    BuiltinFnIdFrameAddress,
    BuiltinFnIdFrameType,
    BuiltinFnIdFrameHandle,
    BuiltinFnIdFrameSize,
    BuiltinFnIdAs,
    BuiltinFnIdCall,
    BuiltinFnIdBitSizeof,
    BuiltinFnIdWasmMemorySize,
    BuiltinFnIdWasmMemoryGrow,
    BuiltinFnIdSrc,
    BuiltinFnIdReduce,
};

struct BuiltinFnEntry {
    BuiltinFnId id;
    Buf name;
    size_t param_count;
};

enum PanicMsgId {
    PanicMsgIdUnreachable,
    PanicMsgIdBoundsCheckFailure,
    PanicMsgIdCastNegativeToUnsigned,
    PanicMsgIdCastTruncatedData,
    PanicMsgIdIntegerOverflow,
    PanicMsgIdShlOverflowedBits,
    PanicMsgIdShrOverflowedBits,
    PanicMsgIdDivisionByZero,
    PanicMsgIdRemainderDivisionByZero,
    PanicMsgIdExactDivisionRemainder,
    PanicMsgIdUnwrapOptionalFail,
    PanicMsgIdInvalidErrorCode,
    PanicMsgIdIncorrectAlignment,
    PanicMsgIdBadUnionField,
    PanicMsgIdBadEnumValue,
    PanicMsgIdFloatToInt,
    PanicMsgIdPtrCastNull,
    PanicMsgIdBadResume,
    PanicMsgIdBadAwait,
    PanicMsgIdBadReturn,
    PanicMsgIdResumedAnAwaitingFn,
    PanicMsgIdFrameTooSmall,
    PanicMsgIdResumedFnPendingAwait,
    PanicMsgIdBadNoSuspendCall,
    PanicMsgIdResumeNotSuspendedFn,
    PanicMsgIdBadSentinel,
    PanicMsgIdShxTooBigRhs,

    PanicMsgIdCount,
};

uint32_t fn_eval_hash(Scope*);
bool fn_eval_eql(Scope *a, Scope *b);

struct TypeId {
    ZigTypeId id;

    union {
        struct {
            CodeGen *codegen;
            ZigType *child_type;
            InferredStructField *inferred_struct_field;
            ZigValue *sentinel;
            PtrLen ptr_len;
            uint32_t alignment;

            uint32_t bit_offset_in_host;
            uint32_t host_int_bytes;

            uint32_t vector_index;
            bool is_const;
            bool is_volatile;
            bool allow_zero;
        } pointer;
        struct {
            CodeGen *codegen;
            ZigType *child_type;
            uint64_t size;
            ZigValue *sentinel;
        } array;
        struct {
            bool is_signed;
            uint32_t bit_count;
        } integer;
        struct {
            ZigType *err_set_type;
            ZigType *payload_type;
        } error_union;
        struct {
            ZigType *elem_type;
            uint32_t len;
        } vector;
    } data;
};

uint32_t type_id_hash(TypeId);
bool type_id_eql(TypeId a, TypeId b);

enum ZigLLVMFnId {
    ZigLLVMFnIdCtz,
    ZigLLVMFnIdClz,
    ZigLLVMFnIdPopCount,
    ZigLLVMFnIdOverflowArithmetic,
    ZigLLVMFnIdFMA,
    ZigLLVMFnIdFloatOp,
    ZigLLVMFnIdBswap,
    ZigLLVMFnIdBitReverse,
};

// There are a bunch of places in code that rely on these values being in
// exactly this order.
enum AddSubMul {
    AddSubMulAdd = 0,
    AddSubMulSub = 1,
    AddSubMulMul = 2,
};

struct ZigLLVMFnKey {
    ZigLLVMFnId id;

    union {
        struct {
            uint32_t bit_count;
        } ctz;
        struct {
            uint32_t bit_count;
        } clz;
        struct {
            uint32_t bit_count;
        } pop_count;
        struct {
            BuiltinFnId op;
            uint32_t bit_count;
            uint32_t vector_len; // 0 means not a vector
        } floating;
        struct {
            AddSubMul add_sub_mul;
            uint32_t bit_count;
            uint32_t vector_len; // 0 means not a vector
            bool is_signed;
        } overflow_arithmetic;
        struct {
            uint32_t bit_count;
            uint32_t vector_len; // 0 means not a vector
        } bswap;
        struct {
            uint32_t bit_count;
        } bit_reverse;
    } data;
};

uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey);
bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b);

struct TimeEvent {
    double time;
    const char *name;
};

struct CFile {
    ZigList<const char *> args;
    const char *source_path;
    const char *preprocessor_only_basename;
};

struct CodeGen {
    // Other code depends on this being first.
    ZigStage1 stage1;

    // arena allocator destroyed just prior to codegen emit
    heap::ArenaAllocator *pass1_arena;

    //////////////////////////// Runtime State
    LLVMModuleRef module;
    ZigList<ErrorMsg*> errors;
    ErrorMsg *trace_err;
    LLVMBuilderRef builder;
    ZigLLVMDIBuilder *dbuilder;
    ZigLLVMDICompileUnit *compile_unit;
    ZigLLVMDIFile *compile_unit_file;
    LLVMTargetDataRef target_data_ref;
    LLVMTargetMachineRef target_machine;
    ZigLLVMDIFile *dummy_di_file;
    LLVMValueRef cur_ret_ptr;
    LLVMValueRef cur_frame_ptr;
    LLVMValueRef cur_fn_val;
    LLVMValueRef cur_async_switch_instr;
    LLVMValueRef cur_async_resume_index_ptr;
    LLVMValueRef cur_async_awaiter_ptr;
    LLVMBasicBlockRef cur_preamble_llvm_block;
    size_t cur_resume_block_count;
    LLVMValueRef cur_err_ret_trace_val_arg;
    LLVMValueRef cur_err_ret_trace_val_stack;
    LLVMValueRef cur_bad_not_suspended_index;
    LLVMValueRef memcpy_fn_val;
    LLVMValueRef memset_fn_val;
    LLVMValueRef trap_fn_val;
    LLVMValueRef return_address_fn_val;
    LLVMValueRef frame_address_fn_val;
    LLVMValueRef add_error_return_trace_addr_fn_val;
    LLVMValueRef stacksave_fn_val;
    LLVMValueRef stackrestore_fn_val;
    LLVMValueRef write_register_fn_val;
    LLVMValueRef merge_err_ret_traces_fn_val;
    LLVMValueRef sp_md_node;
    LLVMValueRef err_name_table;
    LLVMValueRef safety_crash_err_fn;
    LLVMValueRef return_err_fn;
    LLVMValueRef wasm_memory_size;
    LLVMValueRef wasm_memory_grow;
    LLVMTypeRef anyframe_fn_type;

    // reminder: hash tables must be initialized before use
    HashMap<Buf *, ZigType *, buf_hash, buf_eql_buf> import_table;
    HashMap<Buf *, BuiltinFnEntry *, buf_hash, buf_eql_buf> builtin_fn_table;
    HashMap<Buf *, ZigType *, buf_hash, buf_eql_buf> primitive_type_table;
    HashMap<TypeId, ZigType *, type_id_hash, type_id_eql> type_table;
    HashMap<FnTypeId *, ZigType *, fn_type_id_hash, fn_type_id_eql> fn_type_table;
    HashMap<Buf *, ErrorTableEntry *, buf_hash, buf_eql_buf> error_table;
    HashMap<GenericFnTypeId *, ZigFn *, generic_fn_type_id_hash, generic_fn_type_id_eql> generic_table;
    HashMap<Scope *, ZigValue *, fn_eval_hash, fn_eval_eql> memoized_fn_eval_table;
    HashMap<ZigLLVMFnKey, LLVMValueRef, zig_llvm_fn_key_hash, zig_llvm_fn_key_eql> llvm_fn_table;
    HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> exported_symbol_names;
    HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> external_symbol_names;
    HashMap<Buf *, ZigValue *, buf_hash, buf_eql_buf> string_literals_table;
    HashMap<const ZigType *, ZigValue *, type_ptr_hash, type_ptr_eql> type_info_cache;
    HashMap<const ZigType *, ZigValue *, type_ptr_hash, type_ptr_eql> one_possible_values;

    ZigList<Tld *> resolve_queue;
    size_t resolve_queue_index;
    ZigList<TimeEvent> timing_events;
    ZigList<ZigFn *> inline_fns;
    ZigList<ZigFn *> test_fns;
    ZigList<ErrorTableEntry *> errors_by_index;
    size_t largest_err_name_len;
    ZigList<ZigType *> type_resolve_stack;

    ZigPackage *std_package;
    ZigPackage *test_runner_package;
    ZigPackage *compile_var_package;
    ZigPackage *root_pkg; // @import("root")
    ZigPackage *main_pkg; // usually same as root_pkg, except for `zig test`
    ZigType *compile_var_import;
    ZigType *root_import;
    ZigType *start_import;

    struct {
        ZigType *entry_bool;
        ZigType *entry_c_int[CIntTypeCount];
        ZigType *entry_c_longdouble;
        ZigType *entry_c_void;
        ZigType *entry_u8;
        ZigType *entry_u16;
        ZigType *entry_u32;
        ZigType *entry_u29;
        ZigType *entry_u64;
        ZigType *entry_i8;
        ZigType *entry_i32;
        ZigType *entry_i64;
        ZigType *entry_isize;
        ZigType *entry_usize;
        ZigType *entry_f16;
        ZigType *entry_f32;
        ZigType *entry_f64;
        ZigType *entry_f128;
        ZigType *entry_void;
        ZigType *entry_unreachable;
        ZigType *entry_type;
        ZigType *entry_invalid;
        ZigType *entry_block;
        ZigType *entry_num_lit_int;
        ZigType *entry_num_lit_float;
        ZigType *entry_undef;
        ZigType *entry_null;
        ZigType *entry_anytype;
        ZigType *entry_global_error_set;
        ZigType *entry_enum_literal;
        ZigType *entry_any_frame;
    } builtin_types;

    struct Intern {
        ZigValue x_undefined;
        ZigValue x_void;
        ZigValue x_null;
        ZigValue x_unreachable;
        ZigValue zero_byte;

        ZigValue *for_undefined();
        ZigValue *for_void();
        ZigValue *for_null();
        ZigValue *for_unreachable();
        ZigValue *for_zero_byte();
    } intern;

    ZigType *align_amt_type;
    ZigType *stack_trace_type;
    ZigType *err_tag_type;
    ZigType *test_fn_type;

    Buf llvm_triple_str;
    Buf global_asm;
    Buf o_file_output_path;
    Buf h_file_output_path;
    Buf asm_file_output_path;
    Buf llvm_ir_file_output_path;
    Buf analysis_json_output_path;
    Buf docs_output_path;
    Buf *cache_dir;
    Buf *c_artifact_dir;
    const char **libc_include_dir_list;
    size_t libc_include_dir_len;

    Buf *builtin_zig_path;
    Buf *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir.

    IrInstSrc *invalid_inst_src;
    IrInstGen *invalid_inst_gen;
    IrInstGen *unreach_instruction;

    ZigValue panic_msg_vals[PanicMsgIdCount];

    // The function definitions this module includes.
    ZigList<ZigFn *> fn_defs;
    size_t fn_defs_index;
    ZigList<TldVar *> global_vars;

    ZigFn *cur_fn;
    ZigFn *panic_fn;

    ZigFn *largest_frame_fn;

    Stage2ProgressNode *main_progress_node;
    Stage2ProgressNode *sub_progress_node;

    ErrColor err_color;
    uint32_t next_unresolved_index;
    unsigned pointer_size_bytes;
    bool is_big_endian;
    bool have_err_ret_tracing;
    bool verbose_tokenize;
    bool verbose_ast;
    bool verbose_ir;
    bool verbose_llvm_ir;
    bool verbose_cimport;
    bool verbose_llvm_cpu_features;
    bool error_during_imports;
    bool generate_error_name_table;
    bool enable_time_report;
    bool enable_stack_report;
    bool reported_bad_link_libc_error;
    bool need_frame_size_prefix_data;
    bool link_libc;
    bool link_libcpp;

    BuildMode build_mode;
    const ZigTarget *zig_target;
    TargetSubsystem subsystem; // careful using this directly; see detect_subsystem
    CodeModel code_model;
    bool strip_debug_symbols;
    bool is_test_build;
    bool is_single_threaded;
    bool have_pic;
    bool link_mode_dynamic;
    bool dll_export_fns;
    bool have_stack_probing;
    bool function_sections;
    bool test_is_evented;
    bool valgrind_enabled;

    Buf *root_out_name;
    Buf *test_filter;
    Buf *test_name_prefix;
    Buf *zig_lib_dir;
    Buf *zig_std_dir;
};

struct ZigVar {
    const char *name;
    ZigValue *const_value;
    ZigType *var_type;
    LLVMValueRef value_ref;
    IrInstSrc *is_comptime;
    IrInstGen *ptr_instruction;
    // which node is the declaration of the variable
    AstNode *decl_node;
    ZigLLVMDILocalVariable *di_loc_var;
    size_t src_arg_index;
    Scope *parent_scope;
    Scope *child_scope;
    LLVMValueRef param_value_ref;

    Buf *section_name;

    // In an inline loop, multiple variables may be created,
    // In this case, a reference to a variable should follow
    // this pointer to the redefined variable.
    ZigVar *next_var;

    ZigList<GlobalExport> export_list;

    uint32_t align_bytes;
    uint32_t ref_count;

    bool shadowable;
    bool src_is_const;
    bool gen_is_const;
    bool is_thread_local;
    bool is_comptime_memoized;
    bool is_comptime_memoized_value;
    bool did_the_decl_codegen;
};

struct ErrorTableEntry {
    Buf name;
    uint32_t value;
    AstNode *decl_node;
    ErrorTableEntry *other; // null, or another error decl that was merged into this
    ZigType *set_with_only_this_in_it;
    // If we generate a constant error name value for this error, we memoize it here.
    // The type of this is array
    ZigValue *cached_error_name_val;
};

enum ScopeId {
    ScopeIdDecls,
    ScopeIdBlock,
    ScopeIdDefer,
    ScopeIdDeferExpr,
    ScopeIdVarDecl,
    ScopeIdCImport,
    ScopeIdLoop,
    ScopeIdSuspend,
    ScopeIdFnDef,
    ScopeIdCompTime,
    ScopeIdRuntime,
    ScopeIdTypeOf,
    ScopeIdExpr,
    ScopeIdNoSuspend,
};

struct Scope {
    CodeGen *codegen;
    AstNode *source_node;

    // if the scope has a parent, this is it
    Scope *parent;

    ZigLLVMDIScope *di_scope;
    ScopeId id;
};

// This scope comes from global declarations or from
// declarations in a container declaration
// NodeTypeContainerDecl
struct ScopeDecls {
    Scope base;

    HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> decl_table;
    ZigList<TldUsingNamespace *> use_decls;
    AstNode *safety_set_node;
    AstNode *fast_math_set_node;
    ZigType *import;
    // If this is a scope from a container, this is the type entry, otherwise null
    ZigType *container_type;
    Buf *bare_name;

    bool safety_off;
    bool fast_math_on;
    bool any_imports_failed;
};

enum LVal {
    LValNone,
    LValPtr,
    LValAssign,
};

// This scope comes from a block expression in user code.
// NodeTypeBlock
struct ScopeBlock {
    Scope base;

    Buf *name;
    IrBasicBlockSrc *end_block;
    IrInstSrc *is_comptime;
    ResultLocPeerParent *peer_parent;
    ZigList<IrInstSrc *> *incoming_values;
    ZigList<IrBasicBlockSrc *> *incoming_blocks;

    AstNode *safety_set_node;
    AstNode *fast_math_set_node;

    LVal lval;
    bool safety_off;
    bool fast_math_on;
    bool name_used;
};

// This scope is created from every defer expression.
// It's the code following the defer statement.
// NodeTypeDefer
struct ScopeDefer {
    Scope base;
};

// This scope is created from every defer expression.
// It's the parent of the defer expression itself.
// NodeTypeDefer
struct ScopeDeferExpr {
    Scope base;

    bool reported_err;
};

// This scope is created for every variable declaration inside an IrExecutable
// NodeTypeVariableDeclaration, NodeTypeParamDecl
struct ScopeVarDecl {
    Scope base;

    // The variable that creates this scope
    ZigVar *var;
};

// This scope is created for a @cImport
// NodeTypeFnCallExpr
struct ScopeCImport {
    Scope base;

    Buf buf;
};

// This scope is created for a loop such as for or while in order to
// make break and continue statements work.
// NodeTypeForExpr or NodeTypeWhileExpr
struct ScopeLoop {
    Scope base;

    LVal lval;
    Buf *name;
    IrBasicBlockSrc *break_block;
    IrBasicBlockSrc *continue_block;
    IrInstSrc *is_comptime;
    ZigList<IrInstSrc *> *incoming_values;
    ZigList<IrBasicBlockSrc *> *incoming_blocks;
    ResultLocPeerParent *peer_parent;
    ScopeExpr *spill_scope;

    bool name_used;
};

// This scope blocks certain things from working such as comptime continue
// inside a runtime if expression.
// NodeTypeIfBoolExpr, NodeTypeWhileExpr, NodeTypeForExpr
struct ScopeRuntime {
    Scope base;

    IrInstSrc *is_comptime;
};

// This scope is created for a suspend block in order to have labeled
// suspend for breaking out of a suspend and for detecting if a suspend
// block is inside a suspend block.
struct ScopeSuspend {
    Scope base;

    bool reported_err;
};

// This scope is created for a comptime expression.
// NodeTypeCompTime, NodeTypeSwitchExpr
struct ScopeCompTime {
    Scope base;
};

// This scope is created for a nosuspend expression.
// NodeTypeNoSuspend
struct ScopeNoSuspend {
    Scope base;
};

// This scope is created for a function definition.
// NodeTypeFnDef
struct ScopeFnDef {
    Scope base;

    ZigFn *fn_entry;
};

// This scope is created for a @TypeOf.
// All runtime side-effects are elided within it.
// NodeTypeFnCallExpr
struct ScopeTypeOf {
    Scope base;
};

enum MemoizedBool {
    MemoizedBoolUnknown,
    MemoizedBoolFalse,
    MemoizedBoolTrue,
};

// This scope is created for each expression.
// It's used to identify when an instruction needs to be spilled,
// so that it can be accessed after a suspend point.
struct ScopeExpr {
    Scope base;

    ScopeExpr **children_ptr;
    size_t children_len;

    MemoizedBool need_spill;
    // This is a hack. I apologize for this, I need this to work so that I
    // can make progress on other fronts. I'll pay off this tech debt eventually.
    bool spill_harder;
};

// synchronized with code in define_builtin_compile_vars
enum AtomicOrder {
    AtomicOrderUnordered,
    AtomicOrderMonotonic,
    AtomicOrderAcquire,
    AtomicOrderRelease,
    AtomicOrderAcqRel,
    AtomicOrderSeqCst,
};

// synchronized with code in define_builtin_compile_vars
enum ReduceOp {
    ReduceOp_and,
    ReduceOp_or,
    ReduceOp_xor,
    ReduceOp_min,
    ReduceOp_max,
    ReduceOp_add,
    ReduceOp_mul,
};

// synchronized with the code in define_builtin_compile_vars
enum AtomicRmwOp {
    AtomicRmwOp_xchg,
    AtomicRmwOp_add,
    AtomicRmwOp_sub,
    AtomicRmwOp_and,
    AtomicRmwOp_nand,
    AtomicRmwOp_or,
    AtomicRmwOp_xor,
    AtomicRmwOp_max,
    AtomicRmwOp_min,
};

// A basic block contains no branching. Branches send control flow
// to another basic block.
// Phi instructions must be first in a basic block.
// The last instruction in a basic block must be of type unreachable.
struct IrBasicBlockSrc {
    ZigList<IrInstSrc *> instruction_list;
    IrBasicBlockGen *child;
    Scope *scope;
    const char *name_hint;
    IrInst *suspend_instruction_ref;

    uint32_t ref_count;
    uint32_t index; // index into the basic block list

    uint32_t debug_id;
    bool suspended;
    bool in_resume_stack;
};

struct IrBasicBlockGen {
    ZigList<IrInstGen *> instruction_list;
    Scope *scope;
    const char *name_hint;
    LLVMBasicBlockRef llvm_block;
    LLVMBasicBlockRef llvm_exit_block;
    // The instruction that referenced this basic block and caused us to
    // analyze the basic block. If the same instruction wants us to emit
    // the same basic block, then we re-generate it instead of saving it.
    IrInst *ref_instruction;
    // When this is non-null, a branch to this basic block is only allowed
    // if the branch is comptime. The instruction points to the reason
    // the basic block must be comptime.
    IrInst *must_be_comptime_source_instr;

    uint32_t debug_id;
    bool already_appended;
};

// Src instructions are generated by ir_gen_* functions in ir.cpp from AST.
// ir_analyze_* functions consume Src instructions and produce Gen instructions.
// Src instructions do not have type information; Gen instructions do.
enum IrInstSrcId {
    IrInstSrcIdInvalid,
    IrInstSrcIdDeclVar,
    IrInstSrcIdBr,
    IrInstSrcIdCondBr,
    IrInstSrcIdSwitchBr,
    IrInstSrcIdSwitchVar,
    IrInstSrcIdSwitchElseVar,
    IrInstSrcIdSwitchTarget,
    IrInstSrcIdPhi,
    IrInstSrcIdUnOp,
    IrInstSrcIdBinOp,
    IrInstSrcIdMergeErrSets,
    IrInstSrcIdLoadPtr,
    IrInstSrcIdStorePtr,
    IrInstSrcIdFieldPtr,
    IrInstSrcIdElemPtr,
    IrInstSrcIdVarPtr,
    IrInstSrcIdCall,
    IrInstSrcIdCallArgs,
    IrInstSrcIdCallExtra,
    IrInstSrcIdAsyncCallExtra,
    IrInstSrcIdConst,
    IrInstSrcIdReturn,
    IrInstSrcIdContainerInitList,
    IrInstSrcIdContainerInitFields,
    IrInstSrcIdUnreachable,
    IrInstSrcIdTypeOf,
    IrInstSrcIdSetCold,
    IrInstSrcIdSetRuntimeSafety,
    IrInstSrcIdSetFloatMode,
    IrInstSrcIdArrayType,
    IrInstSrcIdAnyFrameType,
    IrInstSrcIdSliceType,
    IrInstSrcIdAsm,
    IrInstSrcIdSizeOf,
    IrInstSrcIdTestNonNull,
    IrInstSrcIdOptionalUnwrapPtr,
    IrInstSrcIdClz,
    IrInstSrcIdCtz,
    IrInstSrcIdPopCount,
    IrInstSrcIdBswap,
    IrInstSrcIdBitReverse,
    IrInstSrcIdImport,
    IrInstSrcIdCImport,
    IrInstSrcIdCInclude,
    IrInstSrcIdCDefine,
    IrInstSrcIdCUndef,
    IrInstSrcIdRef,
    IrInstSrcIdCompileErr,
    IrInstSrcIdCompileLog,
    IrInstSrcIdErrName,
    IrInstSrcIdEmbedFile,
    IrInstSrcIdCmpxchg,
    IrInstSrcIdFence,
    IrInstSrcIdReduce,
    IrInstSrcIdTruncate,
    IrInstSrcIdIntCast,
    IrInstSrcIdFloatCast,
    IrInstSrcIdIntToFloat,
    IrInstSrcIdFloatToInt,
    IrInstSrcIdBoolToInt,
    IrInstSrcIdVectorType,
    IrInstSrcIdShuffleVector,
    IrInstSrcIdSplat,
    IrInstSrcIdBoolNot,
    IrInstSrcIdMemset,
    IrInstSrcIdMemcpy,
    IrInstSrcIdSlice,
    IrInstSrcIdBreakpoint,
    IrInstSrcIdReturnAddress,
    IrInstSrcIdFrameAddress,
    IrInstSrcIdFrameHandle,
    IrInstSrcIdFrameType,
    IrInstSrcIdFrameSize,
    IrInstSrcIdAlignOf,
    IrInstSrcIdOverflowOp,
    IrInstSrcIdTestErr,
    IrInstSrcIdMulAdd,
    IrInstSrcIdFloatOp,
    IrInstSrcIdUnwrapErrCode,
    IrInstSrcIdUnwrapErrPayload,
    IrInstSrcIdFnProto,
    IrInstSrcIdTestComptime,
    IrInstSrcIdPtrCast,
    IrInstSrcIdBitCast,
    IrInstSrcIdIntToPtr,
    IrInstSrcIdPtrToInt,
    IrInstSrcIdIntToEnum,
    IrInstSrcIdEnumToInt,
    IrInstSrcIdIntToErr,
    IrInstSrcIdErrToInt,
    IrInstSrcIdCheckSwitchProngs,
    IrInstSrcIdCheckStatementIsVoid,
    IrInstSrcIdTypeName,
    IrInstSrcIdDeclRef,
    IrInstSrcIdPanic,
    IrInstSrcIdTagName,
    IrInstSrcIdTagType,
    IrInstSrcIdFieldParentPtr,
    IrInstSrcIdByteOffsetOf,
    IrInstSrcIdBitOffsetOf,
    IrInstSrcIdTypeInfo,
    IrInstSrcIdType,
    IrInstSrcIdHasField,
    IrInstSrcIdSetEvalBranchQuota,
    IrInstSrcIdPtrType,
    IrInstSrcIdAlignCast,
    IrInstSrcIdImplicitCast,
    IrInstSrcIdResolveResult,
    IrInstSrcIdResetResult,
    IrInstSrcIdSetAlignStack,
    IrInstSrcIdArgType,
    IrInstSrcIdExport,
    IrInstSrcIdErrorReturnTrace,
    IrInstSrcIdErrorUnion,
    IrInstSrcIdAtomicRmw,
    IrInstSrcIdAtomicLoad,
    IrInstSrcIdAtomicStore,
    IrInstSrcIdSaveErrRetAddr,
    IrInstSrcIdAddImplicitReturnType,
    IrInstSrcIdErrSetCast,
    IrInstSrcIdCheckRuntimeScope,
    IrInstSrcIdHasDecl,
    IrInstSrcIdUndeclaredIdent,
    IrInstSrcIdAlloca,
    IrInstSrcIdEndExpr,
    IrInstSrcIdUnionInitNamedField,
    IrInstSrcIdSuspendBegin,
    IrInstSrcIdSuspendFinish,
    IrInstSrcIdAwait,
    IrInstSrcIdResume,
    IrInstSrcIdSpillBegin,
    IrInstSrcIdSpillEnd,
    IrInstSrcIdWasmMemorySize,
    IrInstSrcIdWasmMemoryGrow,
    IrInstSrcIdSrc,
};

// ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR.
// Src instructions do not have type information; Gen instructions do.
enum IrInstGenId {
    IrInstGenIdInvalid,
    IrInstGenIdDeclVar,
    IrInstGenIdBr,
    IrInstGenIdCondBr,
    IrInstGenIdSwitchBr,
    IrInstGenIdPhi,
    IrInstGenIdBinaryNot,
    IrInstGenIdNegation,
    IrInstGenIdBinOp,
    IrInstGenIdLoadPtr,
    IrInstGenIdStorePtr,
    IrInstGenIdVectorStoreElem,
    IrInstGenIdStructFieldPtr,
    IrInstGenIdUnionFieldPtr,
    IrInstGenIdElemPtr,
    IrInstGenIdVarPtr,
    IrInstGenIdReturnPtr,
    IrInstGenIdCall,
    IrInstGenIdReturn,
    IrInstGenIdCast,
    IrInstGenIdUnreachable,
    IrInstGenIdAsm,
    IrInstGenIdTestNonNull,
    IrInstGenIdOptionalUnwrapPtr,
    IrInstGenIdOptionalWrap,
    IrInstGenIdUnionTag,
    IrInstGenIdClz,
    IrInstGenIdCtz,
    IrInstGenIdPopCount,
    IrInstGenIdBswap,
    IrInstGenIdBitReverse,
    IrInstGenIdRef,
    IrInstGenIdErrName,
    IrInstGenIdCmpxchg,
    IrInstGenIdFence,
    IrInstGenIdReduce,
    IrInstGenIdTruncate,
    IrInstGenIdShuffleVector,
    IrInstGenIdSplat,
    IrInstGenIdBoolNot,
    IrInstGenIdMemset,
    IrInstGenIdMemcpy,
    IrInstGenIdSlice,
    IrInstGenIdBreakpoint,
    IrInstGenIdReturnAddress,
    IrInstGenIdFrameAddress,
    IrInstGenIdFrameHandle,
    IrInstGenIdFrameSize,
    IrInstGenIdOverflowOp,
    IrInstGenIdTestErr,
    IrInstGenIdMulAdd,
    IrInstGenIdFloatOp,
    IrInstGenIdUnwrapErrCode,
    IrInstGenIdUnwrapErrPayload,
    IrInstGenIdErrWrapCode,
    IrInstGenIdErrWrapPayload,
    IrInstGenIdPtrCast,
    IrInstGenIdBitCast,
    IrInstGenIdWidenOrShorten,
    IrInstGenIdIntToPtr,
    IrInstGenIdPtrToInt,
    IrInstGenIdIntToEnum,
    IrInstGenIdIntToErr,
    IrInstGenIdErrToInt,
    IrInstGenIdPanic,
    IrInstGenIdTagName,
    IrInstGenIdFieldParentPtr,
    IrInstGenIdAlignCast,
    IrInstGenIdErrorReturnTrace,
    IrInstGenIdAtomicRmw,
    IrInstGenIdAtomicLoad,
    IrInstGenIdAtomicStore,
    IrInstGenIdSaveErrRetAddr,
    IrInstGenIdVectorToArray,
    IrInstGenIdArrayToVector,
    IrInstGenIdAssertZero,
    IrInstGenIdAssertNonNull,
    IrInstGenIdPtrOfArrayToSlice,
    IrInstGenIdSuspendBegin,
    IrInstGenIdSuspendFinish,
    IrInstGenIdAwait,
    IrInstGenIdResume,
    IrInstGenIdSpillBegin,
    IrInstGenIdSpillEnd,
    IrInstGenIdVectorExtractElem,
    IrInstGenIdAlloca,
    IrInstGenIdConst,
    IrInstGenIdWasmMemorySize,
    IrInstGenIdWasmMemoryGrow,
};

// Common fields between IrInstSrc and IrInstGen. This allows future passes
// after pass2 to be added to zig.
struct IrInst {
    // if ref_count is zero and the instruction has no side effects,
    // the instruction can be omitted in codegen
    uint32_t ref_count;
    uint32_t debug_id;

    Scope *scope;
    AstNode *source_node;

    // for debugging purposes, these are useful to call to inspect the instruction
    void dump();
    void src();
};

struct IrInstSrc {
    IrInst base;

    IrInstSrcId id;
    // true if this instruction was generated by zig and not from user code
    // this matters for the "unreachable code" compile error
    bool is_gen;
    bool is_noreturn;

    // When analyzing IR, instructions that point to this instruction in the "old ir"
    // can find the instruction that corresponds to this value in the "new ir"
    // with this child field.
    IrInstGen *child;
    IrBasicBlockSrc *owner_bb;

    // for debugging purposes, these are useful to call to inspect the instruction
    void dump();
    void src();
};

struct IrInstGen {
    IrInst base;

    IrInstGenId id;

    LLVMValueRef llvm_value;
    ZigValue *value;
    IrBasicBlockGen *owner_bb;
    // Nearly any instruction can have to be stored as a local variable before suspending
    // and then loaded after resuming, in case there is an expression with a suspend point
    // in it, such as: x + await y
    IrInstGen *spill;

    // for debugging purposes, these are useful to call to inspect the instruction
    void dump();
    void src();
};

struct IrInstSrcDeclVar {
    IrInstSrc base;

    ZigVar *var;
    IrInstSrc *var_type;
    IrInstSrc *align_value;
    IrInstSrc *ptr;
};

struct IrInstGenDeclVar {
    IrInstGen base;

    ZigVar *var;
    IrInstGen *var_ptr;
};

struct IrInstSrcCondBr {
    IrInstSrc base;

    IrInstSrc *condition;
    IrBasicBlockSrc *then_block;
    IrBasicBlockSrc *else_block;
    IrInstSrc *is_comptime;
    ResultLoc *result_loc;
};

struct IrInstGenCondBr {
    IrInstGen base;

    IrInstGen *condition;
    IrBasicBlockGen *then_block;
    IrBasicBlockGen *else_block;
};

struct IrInstSrcBr {
    IrInstSrc base;

    IrBasicBlockSrc *dest_block;
    IrInstSrc *is_comptime;
};

struct IrInstGenBr {
    IrInstGen base;

    IrBasicBlockGen *dest_block;
};

struct IrInstSrcSwitchBrCase {
    IrInstSrc *value;
    IrBasicBlockSrc *block;
};

struct IrInstSrcSwitchBr {
    IrInstSrc base;

    IrInstSrc *target_value;
    IrBasicBlockSrc *else_block;
    size_t case_count;
    IrInstSrcSwitchBrCase *cases;
    IrInstSrc *is_comptime;
    IrInstSrc *switch_prongs_void;
};

struct IrInstGenSwitchBrCase {
    IrInstGen *value;
    IrBasicBlockGen *block;
};

struct IrInstGenSwitchBr {
    IrInstGen base;

    IrInstGen *target_value;
    IrBasicBlockGen *else_block;
    size_t case_count;
    IrInstGenSwitchBrCase *cases;
};

struct IrInstSrcSwitchVar {
    IrInstSrc base;

    IrInstSrc *target_value_ptr;
    IrInstSrc **prongs_ptr;
    size_t prongs_len;
};

struct IrInstSrcSwitchElseVar {
    IrInstSrc base;

    IrInstSrc *target_value_ptr;
    IrInstSrcSwitchBr *switch_br;
};

struct IrInstSrcSwitchTarget {
    IrInstSrc base;

    IrInstSrc *target_value_ptr;
};

struct IrInstSrcPhi {
    IrInstSrc base;

    size_t incoming_count;
    IrBasicBlockSrc **incoming_blocks;
    IrInstSrc **incoming_values;
    ResultLocPeerParent *peer_parent;
};

struct IrInstGenPhi {
    IrInstGen base;

    size_t incoming_count;
    IrBasicBlockGen **incoming_blocks;
    IrInstGen **incoming_values;
};

enum IrUnOp {
    IrUnOpInvalid,
    IrUnOpBinNot,
    IrUnOpNegation,
    IrUnOpNegationWrap,
    IrUnOpDereference,
    IrUnOpOptional,
};

struct IrInstSrcUnOp {
    IrInstSrc base;

    IrUnOp op_id;
    LVal lval;
    IrInstSrc *value;
    ResultLoc *result_loc;
};

struct IrInstGenBinaryNot {
    IrInstGen base;
    IrInstGen *operand;
};

struct IrInstGenNegation {
    IrInstGen base;
    IrInstGen *operand;
    bool wrapping;
};

enum IrBinOp {
    IrBinOpInvalid,
    IrBinOpBoolOr,
    IrBinOpBoolAnd,
    IrBinOpCmpEq,
    IrBinOpCmpNotEq,
    IrBinOpCmpLessThan,
    IrBinOpCmpGreaterThan,
    IrBinOpCmpLessOrEq,
    IrBinOpCmpGreaterOrEq,
    IrBinOpBinOr,
    IrBinOpBinXor,
    IrBinOpBinAnd,
    IrBinOpBitShiftLeftLossy,
    IrBinOpBitShiftLeftExact,
    IrBinOpBitShiftRightLossy,
    IrBinOpBitShiftRightExact,
    IrBinOpAdd,
    IrBinOpAddWrap,
    IrBinOpSub,
    IrBinOpSubWrap,
    IrBinOpMult,
    IrBinOpMultWrap,
    IrBinOpDivUnspecified,
    IrBinOpDivExact,
    IrBinOpDivTrunc,
    IrBinOpDivFloor,
    IrBinOpRemUnspecified,
    IrBinOpRemRem,
    IrBinOpRemMod,
    IrBinOpArrayCat,
    IrBinOpArrayMult,
};

struct IrInstSrcBinOp {
    IrInstSrc base;

    IrInstSrc *op1;
    IrInstSrc *op2;
    IrBinOp op_id;
    bool safety_check_on;
};

struct IrInstGenBinOp {
    IrInstGen base;

    IrInstGen *op1;
    IrInstGen *op2;
    IrBinOp op_id;
    bool safety_check_on;
};

struct IrInstSrcMergeErrSets {
    IrInstSrc base;

    IrInstSrc *op1;
    IrInstSrc *op2;
    Buf *type_name;
};

struct IrInstSrcLoadPtr {
    IrInstSrc base;

    IrInstSrc *ptr;
};

struct IrInstGenLoadPtr {
    IrInstGen base;

    IrInstGen *ptr;
    IrInstGen *result_loc;
};

struct IrInstSrcStorePtr {
    IrInstSrc base;

    IrInstSrc *ptr;
    IrInstSrc *value;

    bool allow_write_through_const;
};

struct IrInstGenStorePtr {
    IrInstGen base;

    IrInstGen *ptr;
    IrInstGen *value;
};

struct IrInstGenVectorStoreElem {
    IrInstGen base;

    IrInstGen *vector_ptr;
    IrInstGen *index;
    IrInstGen *value;
};

struct IrInstSrcFieldPtr {
    IrInstSrc base;

    IrInstSrc *container_ptr;
    Buf *field_name_buffer;
    IrInstSrc *field_name_expr;
    bool initializing;
};

struct IrInstGenStructFieldPtr {
    IrInstGen base;

    IrInstGen *struct_ptr;
    TypeStructField *field;
    bool is_const;
};

struct IrInstGenUnionFieldPtr {
    IrInstGen base;

    IrInstGen *union_ptr;
    TypeUnionField *field;
    bool safety_check_on;
    bool initializing;
};

struct IrInstSrcElemPtr {
    IrInstSrc base;

    IrInstSrc *array_ptr;
    IrInstSrc *elem_index;
    AstNode *init_array_type_source_node;
    PtrLen ptr_len;
    bool safety_check_on;
};

struct IrInstGenElemPtr {
    IrInstGen base;

    IrInstGen *array_ptr;
    IrInstGen *elem_index;
    bool safety_check_on;
};

struct IrInstSrcVarPtr {
    IrInstSrc base;

    ZigVar *var;
    ScopeFnDef *crossed_fndef_scope;
};

struct IrInstGenVarPtr {
    IrInstGen base;

    ZigVar *var;
};

// For functions that have a return type for which handle_is_ptr is true, a
// result location pointer is the secret first parameter ("sret"). This
// instruction returns that pointer.
struct IrInstGenReturnPtr {
    IrInstGen base;
};

struct IrInstSrcCall {
    IrInstSrc base;

    IrInstSrc *fn_ref;
    ZigFn *fn_entry;
    size_t arg_count;
    IrInstSrc **args;
    IrInstSrc *ret_ptr;
    ResultLoc *result_loc;

    IrInstSrc *new_stack;

    CallModifier modifier;
    bool is_async_call_builtin;
};

// This is a pass1 instruction, used by @call when the args node is
// a tuple or struct literal.
struct IrInstSrcCallArgs {
    IrInstSrc base;

    IrInstSrc *options;
    IrInstSrc *fn_ref;
    IrInstSrc **args_ptr;
    size_t args_len;
    ResultLoc *result_loc;
};

// This is a pass1 instruction, used by @call, when the args node
// is not a literal.
// `args` is expected to be either a struct or a tuple.
struct IrInstSrcCallExtra {
    IrInstSrc base;

    IrInstSrc *options;
    IrInstSrc *fn_ref;
    IrInstSrc *args;
    ResultLoc *result_loc;
};

// This is a pass1 instruction, used by @asyncCall, when the args node
// is not a literal.
// `args` is expected to be either a struct or a tuple.
struct IrInstSrcAsyncCallExtra {
    IrInstSrc base;

    CallModifier modifier;
    IrInstSrc *fn_ref;
    IrInstSrc *ret_ptr;
    IrInstSrc *new_stack;
    IrInstSrc *args;
    ResultLoc *result_loc;
};

struct IrInstGenCall {
    IrInstGen base;

    IrInstGen *fn_ref;
    ZigFn *fn_entry;
    size_t arg_count;
    IrInstGen **args;
    IrInstGen *result_loc;
    IrInstGen *frame_result_loc;
    IrInstGen *new_stack;

    CallModifier modifier;

    bool is_async_call_builtin;
};

struct IrInstSrcConst {
    IrInstSrc base;

    ZigValue *value;
};

struct IrInstGenConst {
    IrInstGen base;
};

struct IrInstSrcReturn {
    IrInstSrc base;

    IrInstSrc *operand;
};

// When an IrExecutable is not in a function, a return instruction means that
// the expression returns with that value, even though a return statement from
// an AST perspective is invalid.
struct IrInstGenReturn {
    IrInstGen base;

    IrInstGen *operand;
};

enum CastOp {
    CastOpNoCast, // signifies the function call expression is not a cast
    CastOpNoop, // fn call expr is a cast, but does nothing
    CastOpIntToFloat,
    CastOpFloatToInt,
    CastOpBoolToInt,
    CastOpNumLitToConcrete,
    CastOpErrSet,
    CastOpBitCast,
};

// TODO get rid of this instruction, replace with instructions for each op code
struct IrInstGenCast {
    IrInstGen base;

    IrInstGen *value;
    CastOp cast_op;
};

struct IrInstSrcContainerInitList {
    IrInstSrc base;

    IrInstSrc *elem_type;
    size_t item_count;
    IrInstSrc **elem_result_loc_list;
    IrInstSrc *result_loc;
    AstNode *init_array_type_source_node;
};

struct IrInstSrcContainerInitFieldsField {
    Buf *name;
    AstNode *source_node;
    IrInstSrc *result_loc;
};

struct IrInstSrcContainerInitFields {
    IrInstSrc base;

    size_t field_count;
    IrInstSrcContainerInitFieldsField *fields;
    IrInstSrc *result_loc;
};

struct IrInstSrcUnreachable {
    IrInstSrc base;
};

struct IrInstGenUnreachable {
    IrInstGen base;
};

struct IrInstSrcTypeOf {
    IrInstSrc base;

    union {
        IrInstSrc *scalar; // value_count == 1
        IrInstSrc **list; // value_count > 1
    } value;
    size_t value_count;
};

struct IrInstSrcSetCold {
    IrInstSrc base;

    IrInstSrc *is_cold;
};

struct IrInstSrcSetRuntimeSafety {
    IrInstSrc base;

    IrInstSrc *safety_on;
};

struct IrInstSrcSetFloatMode {
    IrInstSrc base;

    IrInstSrc *scope_value;
    IrInstSrc *mode_value;
};

struct IrInstSrcArrayType {
    IrInstSrc base;

    IrInstSrc *size;
    IrInstSrc *sentinel;
    IrInstSrc *child_type;
};

struct IrInstSrcPtrType {
    IrInstSrc base;

    IrInstSrc *sentinel;
    IrInstSrc *align_value;
    IrInstSrc *child_type;
    uint32_t bit_offset_start;
    uint32_t host_int_bytes;
    PtrLen ptr_len;
    bool is_const;
    bool is_volatile;
    bool is_allow_zero;
};

struct IrInstSrcAnyFrameType {
    IrInstSrc base;

    IrInstSrc *payload_type;
};

struct IrInstSrcSliceType {
    IrInstSrc base;

    IrInstSrc *sentinel;
    IrInstSrc *align_value;
    IrInstSrc *child_type;
    bool is_const;
    bool is_volatile;
    bool is_allow_zero;
};

struct IrInstSrcAsm {
    IrInstSrc base;

    IrInstSrc *asm_template;
    IrInstSrc **input_list;
    IrInstSrc **output_types;
    ZigVar **output_vars;
    size_t return_count;
    bool has_side_effects;
    bool is_global;
};

struct IrInstGenAsm {
    IrInstGen base;

    Buf *asm_template;
    AsmToken *token_list;
    size_t token_list_len;
    IrInstGen **input_list;
    IrInstGen **output_types;
    ZigVar **output_vars;
    size_t return_count;
    bool has_side_effects;
};

struct IrInstSrcSizeOf {
    IrInstSrc base;

    IrInstSrc *type_value;
    bool bit_size;
};

// returns true if nonnull, returns false if null
struct IrInstSrcTestNonNull {
    IrInstSrc base;

    IrInstSrc *value;
};

struct IrInstGenTestNonNull {
    IrInstGen base;

    IrInstGen *value;
};

// Takes a pointer to an optional value, returns a pointer
// to the payload.
struct IrInstSrcOptionalUnwrapPtr {
    IrInstSrc base;

    IrInstSrc *base_ptr;
    bool safety_check_on;
};

struct IrInstGenOptionalUnwrapPtr {
    IrInstGen base;

    IrInstGen *base_ptr;
    bool safety_check_on;
    bool initializing;
};

struct IrInstSrcCtz {
    IrInstSrc base;

    IrInstSrc *type;
    IrInstSrc *op;
};

struct IrInstGenCtz {
    IrInstGen base;

    IrInstGen *op;
};

struct IrInstSrcClz {
    IrInstSrc base;

    IrInstSrc *type;
    IrInstSrc *op;
};

struct IrInstGenClz {
    IrInstGen base;

    IrInstGen *op;
};

struct IrInstSrcPopCount {
    IrInstSrc base;

    IrInstSrc *type;
    IrInstSrc *op;
};

struct IrInstGenPopCount {
    IrInstGen base;

    IrInstGen *op;
};

struct IrInstGenUnionTag {
    IrInstGen base;

    IrInstGen *value;
};

struct IrInstSrcImport {
    IrInstSrc base;

    IrInstSrc *name;
};

struct IrInstSrcRef {
    IrInstSrc base;

    IrInstSrc *value;
};

struct IrInstGenRef {
    IrInstGen base;

    IrInstGen *operand;
    IrInstGen *result_loc;
};

struct IrInstSrcCompileErr {
    IrInstSrc base;

    IrInstSrc *msg;
};

struct IrInstSrcCompileLog {
    IrInstSrc base;

    size_t msg_count;
    IrInstSrc **msg_list;
};

struct IrInstSrcErrName {
    IrInstSrc base;

    IrInstSrc *value;
};

struct IrInstGenErrName {
    IrInstGen base;

    IrInstGen *value;
};

struct IrInstSrcCImport {
    IrInstSrc base;
};

struct IrInstSrcCInclude {
    IrInstSrc base;

    IrInstSrc *name;
};

struct IrInstSrcCDefine {
    IrInstSrc base;

    IrInstSrc *name;
    IrInstSrc *value;
};

struct IrInstSrcCUndef {
    IrInstSrc base;

    IrInstSrc *name;
};

struct IrInstSrcEmbedFile {
    IrInstSrc base;

    IrInstSrc *name;
};

struct IrInstSrcCmpxchg {
    IrInstSrc base;

    bool is_weak;
    IrInstSrc *type_value;
    IrInstSrc *ptr;
    IrInstSrc *cmp_value;
    IrInstSrc *new_value;
    IrInstSrc *success_order_value;
    IrInstSrc *failure_order_value;
    ResultLoc *result_loc;
};

struct IrInstGenCmpxchg {
    IrInstGen base;

    AtomicOrder success_order;
    AtomicOrder failure_order;
    IrInstGen *ptr;
    IrInstGen *cmp_value;
    IrInstGen *new_value;
    IrInstGen *result_loc;
    bool is_weak;
};

struct IrInstSrcFence {
    IrInstSrc base;

    IrInstSrc *order;
};

struct IrInstGenFence {
    IrInstGen base;

    AtomicOrder order;
};

struct IrInstSrcReduce {
    IrInstSrc base;

    IrInstSrc *op;
    IrInstSrc *value;
};

struct IrInstGenReduce {
    IrInstGen base;

    ReduceOp op;
    IrInstGen *value;
};

struct IrInstSrcTruncate {
    IrInstSrc base;

    IrInstSrc *dest_type;
    IrInstSrc *target;
};

struct IrInstGenTruncate {
    IrInstGen base;

    IrInstGen *target;
};

struct IrInstSrcIntCast {
    IrInstSrc base;

    IrInstSrc *dest_type;
    IrInstSrc *target;
};

struct IrInstSrcFloatCast {
    IrInstSrc base;

    IrInstSrc *dest_type;
    IrInstSrc *target;
};

struct IrInstSrcErrSetCast {
    IrInstSrc base;

    IrInstSrc *dest_type;
    IrInstSrc *target;
};

struct IrInstSrcIntToFloat {
    IrInstSrc base;

    IrInstSrc *dest_type;
    IrInstSrc *target;
};

struct IrInstSrcFloatToInt {
    IrInstSrc base;

    IrInstSrc *dest_type;
    IrInstSrc *target;
};

struct IrInstSrcBoolToInt {
    IrInstSrc base;

    IrInstSrc *target;
};

struct IrInstSrcVectorType {
    IrInstSrc base;

    IrInstSrc *len;
    IrInstSrc *elem_type;
};

struct IrInstSrcBoolNot {
    IrInstSrc base;

    IrInstSrc *value;
};

struct IrInstGenBoolNot {
    IrInstGen base;

    IrInstGen *value;
};

struct IrInstSrcMemset {
    IrInstSrc base;

    IrInstSrc *dest_ptr;
    IrInstSrc *byte;
    IrInstSrc *count;
};

struct IrInstGenMemset {
    IrInstGen base;

    IrInstGen *dest_ptr;
    IrInstGen *byte;
    IrInstGen *count;
};

struct IrInstSrcMemcpy {
    IrInstSrc base;

    IrInstSrc *dest_ptr;
    IrInstSrc *src_ptr;
    IrInstSrc *count;
};

struct IrInstGenMemcpy {
    IrInstGen base;

    IrInstGen *dest_ptr;
    IrInstGen *src_ptr;
    IrInstGen *count;
};

struct IrInstSrcWasmMemorySize {
    IrInstSrc base;

    IrInstSrc *index;
};

struct IrInstGenWasmMemorySize {
    IrInstGen base;

    IrInstGen *index;
};

struct IrInstSrcWasmMemoryGrow {
    IrInstSrc base;

    IrInstSrc *index;
    IrInstSrc *delta;
};

struct IrInstGenWasmMemoryGrow {
    IrInstGen base;

    IrInstGen *index;
    IrInstGen *delta;
};

struct IrInstSrcSrc {
    IrInstSrc base;
};

struct IrInstSrcSlice {
    IrInstSrc base;

    IrInstSrc *ptr;
    IrInstSrc *start;
    IrInstSrc *end;
    IrInstSrc *sentinel;
    ResultLoc *result_loc;
    bool safety_check_on;
};

struct IrInstGenSlice {
    IrInstGen base;

    IrInstGen *ptr;
    IrInstGen *start;
    IrInstGen *end;
    IrInstGen *result_loc;
    ZigValue *sentinel;
    bool safety_check_on;
};

struct IrInstSrcBreakpoint {
    IrInstSrc base;
};

struct IrInstGenBreakpoint {
    IrInstGen base;
};

struct IrInstSrcReturnAddress {
    IrInstSrc base;
};

struct IrInstGenReturnAddress {
    IrInstGen base;
};

struct IrInstSrcFrameAddress {
    IrInstSrc base;
};

struct IrInstGenFrameAddress {
    IrInstGen base;
};

struct IrInstSrcFrameHandle {
    IrInstSrc base;
};

struct IrInstGenFrameHandle {
    IrInstGen base;
};

struct IrInstSrcFrameType {
    IrInstSrc base;

    IrInstSrc *fn;
};

struct IrInstSrcFrameSize {
    IrInstSrc base;

    IrInstSrc *fn;
};

struct IrInstGenFrameSize {
    IrInstGen base;

    IrInstGen *fn;
};

enum IrOverflowOp {
    IrOverflowOpAdd,
    IrOverflowOpSub,
    IrOverflowOpMul,
    IrOverflowOpShl,
};

struct IrInstSrcOverflowOp {
    IrInstSrc base;

    IrOverflowOp op;
    IrInstSrc *type_value;
    IrInstSrc *op1;
    IrInstSrc *op2;
    IrInstSrc *result_ptr;
};

struct IrInstGenOverflowOp {
    IrInstGen base;

    IrOverflowOp op;
    IrInstGen *op1;
    IrInstGen *op2;
    IrInstGen *result_ptr;

    // TODO can this field be removed?
    ZigType *result_ptr_type;
};

struct IrInstSrcMulAdd {
    IrInstSrc base;

    IrInstSrc *type_value;
    IrInstSrc *op1;
    IrInstSrc *op2;
    IrInstSrc *op3;
};

struct IrInstGenMulAdd {
    IrInstGen base;

    IrInstGen *op1;
    IrInstGen *op2;
    IrInstGen *op3;
};

struct IrInstSrcAlignOf {
    IrInstSrc base;

    IrInstSrc *type_value;
};

// returns true if error, returns false if not error
struct IrInstSrcTestErr {
    IrInstSrc base;

    IrInstSrc *base_ptr;
    bool resolve_err_set;
    bool base_ptr_is_payload;
};

struct IrInstGenTestErr {
    IrInstGen base;

    IrInstGen *err_union;
};

// Takes an error union pointer, returns a pointer to the error code.
struct IrInstSrcUnwrapErrCode {
    IrInstSrc base;

    IrInstSrc *err_union_ptr;
    bool initializing;
};

struct IrInstGenUnwrapErrCode {
    IrInstGen base;

    IrInstGen *err_union_ptr;
    bool initializing;
};

struct IrInstSrcUnwrapErrPayload {
    IrInstSrc base;

    IrInstSrc *value;
    bool safety_check_on;
    bool initializing;
};

struct IrInstGenUnwrapErrPayload {
    IrInstGen base;

    IrInstGen *value;
    bool safety_check_on;
    bool initializing;
};

struct IrInstGenOptionalWrap {
    IrInstGen base;

    IrInstGen *operand;
    IrInstGen *result_loc;
};

struct IrInstGenErrWrapPayload {
    IrInstGen base;

    IrInstGen *operand;
    IrInstGen *result_loc;
};

struct IrInstGenErrWrapCode {
    IrInstGen base;

    IrInstGen *operand;
    IrInstGen *result_loc;
};

struct IrInstSrcFnProto {
    IrInstSrc base;

    IrInstSrc **param_types;
    IrInstSrc *align_value;
    IrInstSrc *callconv_value;
    IrInstSrc *return_type;
    bool is_var_args;
};

// true if the target value is compile time known, false otherwise
struct IrInstSrcTestComptime {
    IrInstSrc base;

    IrInstSrc *value;
};

struct IrInstSrcPtrCast {
    IrInstSrc base;

    IrInstSrc *dest_type;
    IrInstSrc *ptr;
    bool safety_check_on;
};

struct IrInstGenPtrCast {
    IrInstGen base;

    IrInstGen *ptr;
    bool safety_check_on;
};

struct IrInstSrcImplicitCast {
    IrInstSrc base;

    IrInstSrc *operand;
    ResultLocCast *result_loc_cast;
};

struct IrInstSrcBitCast {
    IrInstSrc base;

    IrInstSrc *operand;
    ResultLocBitCast *result_loc_bit_cast;
};

struct IrInstGenBitCast {
    IrInstGen base;

    IrInstGen *operand;
};

struct IrInstGenWidenOrShorten {
    IrInstGen base;

    IrInstGen *target;
};

struct IrInstSrcPtrToInt {
    IrInstSrc base;

    IrInstSrc *target;
};

struct IrInstGenPtrToInt {
    IrInstGen base;

    IrInstGen *target;
};

struct IrInstSrcIntToPtr {
    IrInstSrc base;

    IrInstSrc *dest_type;
    IrInstSrc *target;
};

struct IrInstGenIntToPtr {
    IrInstGen base;

    IrInstGen *target;
};

struct IrInstSrcIntToEnum {
    IrInstSrc base;

    IrInstSrc *dest_type;
    IrInstSrc *target;
};

struct IrInstGenIntToEnum {
    IrInstGen base;

    IrInstGen *target;
};

struct IrInstSrcEnumToInt {
    IrInstSrc base;

    IrInstSrc *target;
};

struct IrInstSrcIntToErr {
    IrInstSrc base;

    IrInstSrc *target;
};

struct IrInstGenIntToErr {
    IrInstGen base;

    IrInstGen *target;
};

struct IrInstSrcErrToInt {
    IrInstSrc base;

    IrInstSrc *target;
};

struct IrInstGenErrToInt {
    IrInstGen base;

    IrInstGen *target;
};

struct IrInstSrcCheckSwitchProngsRange {
    IrInstSrc *start;
    IrInstSrc *end;
};

struct IrInstSrcCheckSwitchProngs {
    IrInstSrc base;

    IrInstSrc *target_value;
    IrInstSrcCheckSwitchProngsRange *ranges;
    size_t range_count;
    AstNode* else_prong;
    bool have_underscore_prong;
};

struct IrInstSrcCheckStatementIsVoid {
    IrInstSrc base;

    IrInstSrc *statement_value;
};

struct IrInstSrcTypeName {
    IrInstSrc base;

    IrInstSrc *type_value;
};

struct IrInstSrcDeclRef {
    IrInstSrc base;

    LVal lval;
    Tld *tld;
};

struct IrInstSrcPanic {
    IrInstSrc base;

    IrInstSrc *msg;
};

struct IrInstGenPanic {
    IrInstGen base;

    IrInstGen *msg;
};

struct IrInstSrcTagName {
    IrInstSrc base;

    IrInstSrc *target;
};

struct IrInstGenTagName {
    IrInstGen base;

    IrInstGen *target;
};

struct IrInstSrcTagType {
    IrInstSrc base;

    IrInstSrc *target;
};

struct IrInstSrcFieldParentPtr {
    IrInstSrc base;

    IrInstSrc *type_value;
    IrInstSrc *field_name;
    IrInstSrc *field_ptr;
};

struct IrInstGenFieldParentPtr {
    IrInstGen base;

    IrInstGen *field_ptr;
    TypeStructField *field;
};

struct IrInstSrcByteOffsetOf {
    IrInstSrc base;

    IrInstSrc *type_value;
    IrInstSrc *field_name;
};

struct IrInstSrcBitOffsetOf {
    IrInstSrc base;

    IrInstSrc *type_value;
    IrInstSrc *field_name;
};

struct IrInstSrcTypeInfo {
    IrInstSrc base;

    IrInstSrc *type_value;
};

struct IrInstSrcType {
    IrInstSrc base;

    IrInstSrc *type_info;
};

struct IrInstSrcHasField {
    IrInstSrc base;

    IrInstSrc *container_type;
    IrInstSrc *field_name;
};

struct IrInstSrcSetEvalBranchQuota {
    IrInstSrc base;

    IrInstSrc *new_quota;
};

struct IrInstSrcAlignCast {
    IrInstSrc base;

    IrInstSrc *align_bytes;
    IrInstSrc *target;
};

struct IrInstGenAlignCast {
    IrInstGen base;

    IrInstGen *target;
};

struct IrInstSrcSetAlignStack {
    IrInstSrc base;

    IrInstSrc *align_bytes;
};

struct IrInstSrcArgType {
    IrInstSrc base;

    IrInstSrc *fn_type;
    IrInstSrc *arg_index;
    bool allow_var;
};

struct IrInstSrcExport {
    IrInstSrc base;

    IrInstSrc *target;
    IrInstSrc *options;
};

enum IrInstErrorReturnTraceOptional {
    IrInstErrorReturnTraceNull,
    IrInstErrorReturnTraceNonNull,
};

struct IrInstSrcErrorReturnTrace {
    IrInstSrc base;

    IrInstErrorReturnTraceOptional optional;
};

struct IrInstGenErrorReturnTrace {
    IrInstGen base;

    IrInstErrorReturnTraceOptional optional;
};

struct IrInstSrcErrorUnion {
    IrInstSrc base;

    IrInstSrc *err_set;
    IrInstSrc *payload;
    Buf *type_name;
};

struct IrInstSrcAtomicRmw {
    IrInstSrc base;

    IrInstSrc *operand_type;
    IrInstSrc *ptr;
    IrInstSrc *op;
    IrInstSrc *operand;
    IrInstSrc *ordering;
};

struct IrInstGenAtomicRmw {
    IrInstGen base;

    IrInstGen *ptr;
    IrInstGen *operand;
    AtomicRmwOp op;
    AtomicOrder ordering;
};

struct IrInstSrcAtomicLoad {
    IrInstSrc base;

    IrInstSrc *operand_type;
    IrInstSrc *ptr;
    IrInstSrc *ordering;
};

struct IrInstGenAtomicLoad {
    IrInstGen base;

    IrInstGen *ptr;
    AtomicOrder ordering;
};

struct IrInstSrcAtomicStore {
    IrInstSrc base;

    IrInstSrc *operand_type;
    IrInstSrc *ptr;
    IrInstSrc *value;
    IrInstSrc *ordering;
};

struct IrInstGenAtomicStore {
    IrInstGen base;

    IrInstGen *ptr;
    IrInstGen *value;
    AtomicOrder ordering;
};

struct IrInstSrcSaveErrRetAddr {
    IrInstSrc base;
};

struct IrInstGenSaveErrRetAddr {
    IrInstGen base;
};

struct IrInstSrcAddImplicitReturnType {
    IrInstSrc base;

    IrInstSrc *value;
    ResultLocReturn *result_loc_ret;
};

// For float ops that take a single argument
struct IrInstSrcFloatOp {
    IrInstSrc base;

    IrInstSrc *operand;
    BuiltinFnId fn_id;
};

struct IrInstGenFloatOp {
    IrInstGen base;

    IrInstGen *operand;
    BuiltinFnId fn_id;
};

struct IrInstSrcCheckRuntimeScope {
    IrInstSrc base;

    IrInstSrc *scope_is_comptime;
    IrInstSrc *is_comptime;
};

struct IrInstSrcBswap {
    IrInstSrc base;

    IrInstSrc *type;
    IrInstSrc *op;
};

struct IrInstGenBswap {
    IrInstGen base;

    IrInstGen *op;
};

struct IrInstSrcBitReverse {
    IrInstSrc base;

    IrInstSrc *type;
    IrInstSrc *op;
};

struct IrInstGenBitReverse {
    IrInstGen base;

    IrInstGen *op;
};

struct IrInstGenArrayToVector {
    IrInstGen base;

    IrInstGen *array;
};

struct IrInstGenVectorToArray {
    IrInstGen base;

    IrInstGen *vector;
    IrInstGen *result_loc;
};

struct IrInstSrcShuffleVector {
    IrInstSrc base;

    IrInstSrc *scalar_type;
    IrInstSrc *a;
    IrInstSrc *b;
    IrInstSrc *mask; // This is in zig-format, not llvm format
};

struct IrInstGenShuffleVector {
    IrInstGen base;

    IrInstGen *a;
    IrInstGen *b;
    IrInstGen *mask; // This is in zig-format, not llvm format
};

struct IrInstSrcSplat {
    IrInstSrc base;

    IrInstSrc *len;
    IrInstSrc *scalar;
};

struct IrInstGenSplat {
    IrInstGen base;

    IrInstGen *scalar;
};

struct IrInstGenAssertZero {
    IrInstGen base;

    IrInstGen *target;
};

struct IrInstGenAssertNonNull {
    IrInstGen base;

    IrInstGen *target;
};

struct IrInstSrcUnionInitNamedField {
    IrInstSrc base;

    IrInstSrc *union_type;
    IrInstSrc *field_name;
    IrInstSrc *field_result_loc;
    IrInstSrc *result_loc;
};

struct IrInstSrcHasDecl {
    IrInstSrc base;

    IrInstSrc *container;
    IrInstSrc *name;
};

struct IrInstSrcUndeclaredIdent {
    IrInstSrc base;

    Buf *name;
};

struct IrInstSrcAlloca {
    IrInstSrc base;

    IrInstSrc *align;
    IrInstSrc *is_comptime;
    const char *name_hint;
};

struct IrInstGenAlloca {
    IrInstGen base;

    uint32_t align;
    const char *name_hint;
    size_t field_index;
};

struct IrInstSrcEndExpr {
    IrInstSrc base;

    IrInstSrc *value;
    ResultLoc *result_loc;
};

// This one is for writing through the result pointer.
struct IrInstSrcResolveResult {
    IrInstSrc base;

    ResultLoc *result_loc;
    IrInstSrc *ty;
};

struct IrInstSrcResetResult {
    IrInstSrc base;

    ResultLoc *result_loc;
};

struct IrInstGenPtrOfArrayToSlice {
    IrInstGen base;

    IrInstGen *operand;
    IrInstGen *result_loc;
};

struct IrInstSrcSuspendBegin {
    IrInstSrc base;
};

struct IrInstGenSuspendBegin {
    IrInstGen base;

    LLVMBasicBlockRef resume_bb;
};

struct IrInstSrcSuspendFinish {
    IrInstSrc base;

    IrInstSrcSuspendBegin *begin;
};

struct IrInstGenSuspendFinish {
    IrInstGen base;

    IrInstGenSuspendBegin *begin;
};

struct IrInstSrcAwait {
    IrInstSrc base;

    IrInstSrc *frame;
    ResultLoc *result_loc;
    bool is_nosuspend;
};

struct IrInstGenAwait {
    IrInstGen base;

    IrInstGen *frame;
    IrInstGen *result_loc;
    ZigFn *target_fn;
    bool is_nosuspend;
};

struct IrInstSrcResume {
    IrInstSrc base;

    IrInstSrc *frame;
};

struct IrInstGenResume {
    IrInstGen base;

    IrInstGen *frame;
};

enum SpillId {
    SpillIdInvalid,
    SpillIdRetErrCode,
};

struct IrInstSrcSpillBegin {
    IrInstSrc base;

    IrInstSrc *operand;
    SpillId spill_id;
};

struct IrInstGenSpillBegin {
    IrInstGen base;

    SpillId spill_id;
    IrInstGen *operand;
};

struct IrInstSrcSpillEnd {
    IrInstSrc base;

    IrInstSrcSpillBegin *begin;
};

struct IrInstGenSpillEnd {
    IrInstGen base;

    IrInstGenSpillBegin *begin;
};

struct IrInstGenVectorExtractElem {
    IrInstGen base;

    IrInstGen *vector;
    IrInstGen *index;
};

enum ResultLocId {
    ResultLocIdInvalid,
    ResultLocIdNone,
    ResultLocIdVar,
    ResultLocIdReturn,
    ResultLocIdPeer,
    ResultLocIdPeerParent,
    ResultLocIdInstruction,
    ResultLocIdBitCast,
    ResultLocIdCast,
};

// Additions to this struct may need to be handled in
// ir_reset_result
struct ResultLoc {
    ResultLocId id;
    bool written;
    bool allow_write_through_const;
    IrInstGen *resolved_loc; // result ptr
    IrInstSrc *source_instruction;
    IrInstGen *gen_instruction; // value to store to the result loc
    ZigType *implicit_elem_type;
};

struct ResultLocNone {
    ResultLoc base;
};

struct ResultLocVar {
    ResultLoc base;

    ZigVar *var;
};

struct ResultLocReturn {
    ResultLoc base;

    bool implicit_return_type_done;
};

struct IrSuspendPosition {
    size_t basic_block_index;
    size_t instruction_index;
};

struct ResultLocPeerParent {
    ResultLoc base;

    bool skipped;
    bool done_resuming;
    IrBasicBlockSrc *end_bb;
    ResultLoc *parent;
    ZigList<ResultLocPeer *> peers;
    ZigType *resolved_type;
    IrInstSrc *is_comptime;
};

struct ResultLocPeer {
    ResultLoc base;

    ResultLocPeerParent *parent;
    IrBasicBlockSrc *next_bb;
    IrSuspendPosition suspend_pos;
};

// The result location is the source instruction
struct ResultLocInstruction {
    ResultLoc base;
};

// The source_instruction is the destination type
struct ResultLocBitCast {
    ResultLoc base;

    ResultLoc *parent;
};

// The source_instruction is the destination type
struct ResultLocCast {
    ResultLoc base;

    ResultLoc *parent;
};

static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;

static const size_t maybe_child_index = 0;
static const size_t maybe_null_index = 1;

static const size_t err_union_payload_index = 0;
static const size_t err_union_err_index = 1;

// label (grep this): [fn_frame_struct_layout]
static const size_t frame_fn_ptr_index = 0;
static const size_t frame_resume_index = 1;
static const size_t frame_awaiter_index = 2;
static const size_t frame_ret_start = 3;

// TODO https://github.com/ziglang/zig/issues/3056
// We require this to be a power of 2 so that we can use shifting rather than
// remainder division.
static const size_t stack_trace_ptr_count = 32; // Must be a power of 2.

#define NAMESPACE_SEP_CHAR '.'
#define NAMESPACE_SEP_STR "."

#define CACHE_OUT_SUBDIR "o"
#define CACHE_HASH_SUBDIR "h"

enum FloatMode {
    FloatModeStrict,
    FloatModeOptimized,
};

enum FnWalkId {
    FnWalkIdAttrs,
    FnWalkIdCall,
    FnWalkIdTypes,
    FnWalkIdVars,
    FnWalkIdInits,
};

struct FnWalkAttrs {
    ZigFn *fn;
    LLVMValueRef llvm_fn;
    unsigned gen_i;
};

struct FnWalkCall {
    ZigList<LLVMValueRef> *gen_param_values;
    ZigList<ZigType *> *gen_param_types;
    IrInstGenCall *inst;
    bool is_var_args;
};

struct FnWalkTypes {
    ZigList<ZigLLVMDIType *> *param_di_types;
    ZigList<LLVMTypeRef> *gen_param_types;
};

struct FnWalkVars {
    ZigType *import;
    LLVMValueRef llvm_fn;
    ZigFn *fn;
    ZigVar *var;
    unsigned gen_i;
};

struct FnWalkInits {
    LLVMValueRef llvm_fn;
    ZigFn *fn;
    unsigned gen_i;
};

struct FnWalk {
    FnWalkId id;
    union {
        FnWalkAttrs attrs;
        FnWalkCall call;
        FnWalkTypes types;
        FnWalkVars vars;
        FnWalkInits inits;
    } data;
};

#endif
