code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <utility>

#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
#include "shader_recompiler/frontend/ir/value.h"

namespace Shader::Backend::GLASM {
namespace {
struct ScopedRegister {
    ScopedRegister() = default;
    ScopedRegister(RegAlloc& reg_alloc_) : reg_alloc{&reg_alloc_}, reg{reg_alloc->AllocReg()} {}

    ~ScopedRegister() {
        if (reg_alloc) {
            reg_alloc->FreeReg(reg);
        }
    }

    ScopedRegister& operator=(ScopedRegister&& rhs) noexcept {
        if (reg_alloc) {
            reg_alloc->FreeReg(reg);
        }
        reg_alloc = std::exchange(rhs.reg_alloc, nullptr);
        reg = rhs.reg;
        return *this;
    }

    ScopedRegister(ScopedRegister&& rhs) noexcept
        : reg_alloc{std::exchange(rhs.reg_alloc, nullptr)}, reg{rhs.reg} {}

    ScopedRegister& operator=(const ScopedRegister&) = delete;
    ScopedRegister(const ScopedRegister&) = delete;

    RegAlloc* reg_alloc{};
    Register reg;
};

std::string Texture(EmitContext& ctx, IR::TextureInstInfo info,
                    [[maybe_unused]] const IR::Value& index) {
    // FIXME: indexed reads
    if (info.type == TextureType::Buffer) {
        return fmt::format("texture[{}]", ctx.texture_buffer_bindings.at(info.descriptor_index));
    } else {
        return fmt::format("texture[{}]", ctx.texture_bindings.at(info.descriptor_index));
    }
}

std::string Image(EmitContext& ctx, IR::TextureInstInfo info,
                  [[maybe_unused]] const IR::Value& index) {
    // FIXME: indexed reads
    if (info.type == TextureType::Buffer) {
        return fmt::format("image[{}]", ctx.image_buffer_bindings.at(info.descriptor_index));
    } else {
        return fmt::format("image[{}]", ctx.image_bindings.at(info.descriptor_index));
    }
}

bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
    if (info.type == TextureType::Buffer) {
        return false;
    }
    return ctx.info.texture_descriptors.at(info.descriptor_index).is_multisample;
}

std::string_view TextureType(IR::TextureInstInfo info, bool is_ms = false) {
    if (info.is_depth) {
        switch (info.type) {
        case TextureType::Color1D:
            return "SHADOW1D";
        case TextureType::ColorArray1D:
            return "SHADOWARRAY1D";
        case TextureType::Color2D:
        case TextureType::Color2DRect:
            return "SHADOW2D";
        case TextureType::ColorArray2D:
            return "SHADOWARRAY2D";
        case TextureType::Color3D:
            return "SHADOW3D";
        case TextureType::ColorCube:
            return "SHADOWCUBE";
        case TextureType::ColorArrayCube:
            return "SHADOWARRAYCUBE";
        case TextureType::Buffer:
            return "SHADOWBUFFER";
        }
    } else {
        switch (info.type) {
        case TextureType::Color1D:
            return "1D";
        case TextureType::ColorArray1D:
            return "ARRAY1D";
        case TextureType::Color2D:
        case TextureType::Color2DRect:
            return is_ms ? "2DMS" : "2D";
        case TextureType::ColorArray2D:
            return is_ms ? "ARRAY2DMS" : "ARRAY2D";
        case TextureType::Color3D:
            return "3D";
        case TextureType::ColorCube:
            return "CUBE";
        case TextureType::ColorArrayCube:
            return "ARRAYCUBE";
        case TextureType::Buffer:
            return "BUFFER";
        }
    }
    throw InvalidArgument("Invalid texture type {}", info.type.Value());
}

std::string Offset(EmitContext& ctx, const IR::Value& offset) {
    if (offset.IsEmpty()) {
        return "";
    }
    return fmt::format(",offset({})", Register{ctx.reg_alloc.Consume(offset)});
}

std::pair<ScopedRegister, ScopedRegister> AllocOffsetsRegs(EmitContext& ctx,
                                                           const IR::Value& offset2) {
    if (offset2.IsEmpty()) {
        return {};
    } else {
        return {ctx.reg_alloc, ctx.reg_alloc};
    }
}

void SwizzleOffsets(EmitContext& ctx, Register off_x, Register off_y, const IR::Value& offset1,
                    const IR::Value& offset2) {
    const Register offsets_a{ctx.reg_alloc.Consume(offset1)};
    const Register offsets_b{ctx.reg_alloc.Consume(offset2)};
    // Input swizzle:  [XYXY] [XYXY]
    // Output swizzle: [XXXX] [YYYY]
    ctx.Add("MOV {}.x,{}.x;"
            "MOV {}.y,{}.z;"
            "MOV {}.z,{}.x;"
            "MOV {}.w,{}.z;"
            "MOV {}.x,{}.y;"
            "MOV {}.y,{}.w;"
            "MOV {}.z,{}.y;"
            "MOV {}.w,{}.w;",
            off_x, offsets_a, off_x, offsets_a, off_x, offsets_b, off_x, offsets_b, off_y,
            offsets_a, off_y, offsets_a, off_y, offsets_b, off_y, offsets_b);
}

std::string GradOffset(const IR::Value& offset) {
    if (offset.IsImmediate()) {
        LOG_WARNING(Shader_GLASM, "Gradient offset is a scalar immediate");
        return "";
    }
    IR::Inst* const vector{offset.InstRecursive()};
    if (!vector->AreAllArgsImmediates()) {
        LOG_WARNING(Shader_GLASM, "Gradient offset vector is not immediate");
        return "";
    }
    switch (vector->NumArgs()) {
    case 1:
        return fmt::format(",({})", static_cast<s32>(vector->Arg(0).U32()));
    case 2:
        return fmt::format(",({},{})", static_cast<s32>(vector->Arg(0).U32()),
                           static_cast<s32>(vector->Arg(1).U32()));
    default:
        throw LogicError("Invalid number of gradient offsets {}", vector->NumArgs());
    }
}

std::pair<std::string, ScopedRegister> Coord(EmitContext& ctx, const IR::Value& coord) {
    if (coord.IsImmediate()) {
        ScopedRegister scoped_reg(ctx.reg_alloc);
        ctx.Add("MOV.U {}.x,{};", scoped_reg.reg, ScalarU32{ctx.reg_alloc.Consume(coord)});
        return {fmt::to_string(scoped_reg.reg), std::move(scoped_reg)};
    }
    std::string coord_vec{fmt::to_string(Register{ctx.reg_alloc.Consume(coord)})};
    if (coord.InstRecursive()->HasUses()) {
        // Move non-dead coords to a separate register, although this should never happen because
        // vectors are only assembled for immediate texture instructions
        ctx.Add("MOV.F RC,{};", coord_vec);
        coord_vec = "RC";
    }
    return {std::move(coord_vec), ScopedRegister{}};
}

void StoreSparse(EmitContext& ctx, IR::Inst* sparse_inst) {
    if (!sparse_inst) {
        return;
    }
    const Register sparse_ret{ctx.reg_alloc.Define(*sparse_inst)};
    ctx.Add("MOV.S {},-1;"
            "MOV.S {}(NONRESIDENT),0;",
            sparse_ret, sparse_ret);
}

std::string_view FormatStorage(ImageFormat format) {
    switch (format) {
    case ImageFormat::Typeless:
        return "U";
    case ImageFormat::R8_UINT:
        return "U8";
    case ImageFormat::R8_SINT:
        return "S8";
    case ImageFormat::R16_UINT:
        return "U16";
    case ImageFormat::R16_SINT:
        return "S16";
    case ImageFormat::R32_UINT:
        return "U32";
    case ImageFormat::R32G32_UINT:
        return "U32X2";
    case ImageFormat::R32G32B32A32_UINT:
        return "U32X4";
    }
    throw InvalidArgument("Invalid image format {}", format);
}

template <typename T>
void ImageAtomic(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord, T value,
                 std::string_view op) {
    const auto info{inst.Flags<IR::TextureInstInfo>()};
    const std::string_view type{TextureType(info)};
    const std::string image{Image(ctx, info, index)};
    const Register ret{ctx.reg_alloc.Define(inst)};
    ctx.Add("ATOMIM.{} {},{},{},{},{};", op, ret, value, coord, image, type);
}

IR::Inst* PrepareSparse(IR::Inst& inst) {
    const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
    if (sparse_inst) {
        sparse_inst->Invalidate();
    }
    return sparse_inst;
}
} // Anonymous namespace

void EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                                const IR::Value& coord, Register bias_lc, const IR::Value& offset) {
    const auto info{inst.Flags<IR::TextureInstInfo>()};
    const auto sparse_inst{PrepareSparse(inst)};
    const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
    const std::string_view lod_clamp_mod{info.has_lod_clamp ? ".LODCLAMP" : ""};
    const std::string_view type{TextureType(info)};
    const std::string texture{Texture(ctx, info, index)};
    const std::string offset_vec{Offset(ctx, offset)};
    const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
    const Register ret{ctx.reg_alloc.Define(inst)};
    if (info.has_bias) {
        if (info.type == TextureType::ColorArrayCube) {
            ctx.Add("TXB.F{}{} {},{},{},{},ARRAYCUBE{};", lod_clamp_mod, sparse_mod, ret, coord_vec,
                    bias_lc, texture, offset_vec);
        } else {
            if (info.has_lod_clamp) {
                ctx.Add("MOV.F {}.w,{}.x;"
                        "TXB.F.LODCLAMP{} {},{},{}.y,{},{}{};",
                        coord_vec, bias_lc, sparse_mod, ret, coord_vec, bias_lc, texture, type,
                        offset_vec);
            } else {
                ctx.Add("MOV.F {}.w,{}.x;"
                        "TXB.F{} {},{},{},{}{};",
                        coord_vec, bias_lc, sparse_mod, ret, coord_vec, texture, type, offset_vec);
            }
        }
    } else {
        if (info.has_lod_clamp && info.type == TextureType::ColorArrayCube) {
            ctx.Add("TEX.F.LODCLAMP{} {},{},{},{},ARRAYCUBE{};", sparse_mod, ret, coord_vec,
                    bias_lc, texture, offset_vec);
        } else {
            ctx.Add("TEX.F{}{} {},{},{},{}{};", lod_clamp_mod, sparse_mod, ret, coord_vec, texture,
                    type, offset_vec);
        }
    }
    StoreSparse(ctx, sparse_inst);
}

void EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                                const IR::Value& coord, ScalarF32 lod, const IR::Value& offset) {
    const auto info{inst.Flags<IR::TextureInstInfo>()};
    const auto sparse_inst{PrepareSparse(inst)};
    const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
    const std::string_view type{TextureType(info)};
    const std::string texture{Texture(ctx, info, index)};
    const std::string offset_vec{Offset(ctx, offset)};
    const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
    const Register ret{ctx.reg_alloc.Define(inst)};
    if (info.type == TextureType::ColorArrayCube) {
        ctx.Add("TXL.F{} {},{},{},{},ARRAYCUBE{};", sparse_mod, ret, coord_vec, lod, texture,
                offset_vec);
    } else {
        ctx.Add("MOV.F {}.w,{};"
                "TXL.F{} {},{},{},{}{};",
                coord_vec, lod, sparse_mod, ret, coord_vec, texture, type, offset_vec);
    }
    StoreSparse(ctx, sparse_inst);
}

void EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                                    const IR::Value& coord, const IR::Value& dref,
                                    const IR::Value& bias_lc, const IR::Value& offset) {
    // Allocate early to avoid aliases
    const auto info{inst.Flags<IR::TextureInstInfo>()};
    ScopedRegister staging;
    if (info.type == TextureType::ColorArrayCube) {
        staging = ScopedRegister{ctx.reg_alloc};
    }
    const ScalarF32 dref_val{ctx.reg_alloc.Consume(dref)};
    const Register bias_lc_vec{ctx.reg_alloc.Consume(bias_lc)};
    const auto sparse_inst{PrepareSparse(inst)};
    const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
    const std::string_view type{TextureType(info)};
    const std::string texture{Texture(ctx, info, index)};
    const std::string offset_vec{Offset(ctx, offset)};
    const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
    const Register ret{ctx.reg_alloc.Define(inst)};
    if (info.has_bias) {
        if (info.has_lod_clamp) {
            switch (info.type) {
            case TextureType::Color1D:
            case TextureType::ColorArray1D:
            case TextureType::Color2D:
                ctx.Add("MOV.F {}.z,{};"
                        "MOV.F {}.w,{}.x;"
                        "TXB.F.LODCLAMP{} {},{},{}.y,{},{}{};",
                        coord_vec, dref_val, coord_vec, bias_lc_vec, sparse_mod, ret, coord_vec,
                        bias_lc_vec, texture, type, offset_vec);
                break;
            case TextureType::ColorArray2D:
            case TextureType::ColorCube:
                ctx.Add("MOV.F {}.w,{};"
                        "TXB.F.LODCLAMP{} {},{},{},{},{}{};",
                        coord_vec, dref_val, sparse_mod, ret, coord_vec, bias_lc_vec, texture, type,
                        offset_vec);
                break;
            default:
                throw NotImplementedException("Invalid type {} with bias and lod clamp",
                                              info.type.Value());
            }
        } else {
            switch (info.type) {
            case TextureType::Color1D:
            case TextureType::ColorArray1D:
            case TextureType::Color2D:
                ctx.Add("MOV.F {}.z,{};"
                        "MOV.F {}.w,{}.x;"
                        "TXB.F{} {},{},{},{}{};",
                        coord_vec, dref_val, coord_vec, bias_lc_vec, sparse_mod, ret, coord_vec,
                        texture, type, offset_vec);
                break;
            case TextureType::ColorArray2D:
            case TextureType::ColorCube:
                ctx.Add("MOV.F {}.w,{};"
                        "TXB.F{} {},{},{},{},{}{};",
                        coord_vec, dref_val, sparse_mod, ret, coord_vec, bias_lc_vec, texture, type,
                        offset_vec);
                break;
            case TextureType::ColorArrayCube:
                ctx.Add("MOV.F {}.x,{};"
                        "MOV.F {}.y,{}.x;"
                        "TXB.F{} {},{},{},{},{}{};",
                        staging.reg, dref_val, staging.reg, bias_lc_vec, sparse_mod, ret, coord_vec,
                        staging.reg, texture, type, offset_vec);
                break;
            default:
                throw NotImplementedException("Invalid type {}", info.type.Value());
            }
        }
    } else {
        if (info.has_lod_clamp) {
            if (info.type != TextureType::ColorArrayCube) {
                const bool w_swizzle{info.type == TextureType::ColorArray2D ||
                                     info.type == TextureType::ColorCube};
                const char dref_swizzle{w_swizzle ? 'w' : 'z'};
                ctx.Add("MOV.F {}.{},{};"
                        "TEX.F.LODCLAMP{} {},{},{},{},{}{};",
                        coord_vec, dref_swizzle, dref_val, sparse_mod, ret, coord_vec, bias_lc_vec,
                        texture, type, offset_vec);
            } else {
                ctx.Add("MOV.F {}.x,{};"
                        "MOV.F {}.y,{};"
                        "TEX.F.LODCLAMP{} {},{},{},{},{}{};",
                        staging.reg, dref_val, staging.reg, bias_lc_vec, sparse_mod, ret, coord_vec,
                        staging.reg, texture, type, offset_vec);
            }
        } else {
            if (info.type != TextureType::ColorArrayCube) {
                const bool w_swizzle{info.type == TextureType::ColorArray2D ||
                                     info.type == TextureType::ColorCube};
                const char dref_swizzle{w_swizzle ? 'w' : 'z'};
                ctx.Add("MOV.F {}.{},{};"
                        "TEX.F{} {},{},{},{}{};",
                        coord_vec, dref_swizzle, dref_val, sparse_mod, ret, coord_vec, texture,
                        type, offset_vec);
            } else {
                ctx.Add("TEX.F{} {},{},{},{},{}{};", sparse_mod, ret, coord_vec, dref_val, texture,
                        type, offset_vec);
            }
        }
    }
    StoreSparse(ctx, sparse_inst);
}

void EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                                    const IR::Value& coord, const IR::Value& dref,
                                    const IR::Value& lod, const IR::Value& offset) {
    // Allocate early to avoid aliases
    const auto info{inst.Flags<IR::TextureInstInfo>()};
    ScopedRegister staging;
    if (info.type == TextureType::ColorArrayCube) {
        staging = ScopedRegister{ctx.reg_alloc};
    }
    const ScalarF32 dref_val{ctx.reg_alloc.Consume(dref)};
    const ScalarF32 lod_val{ctx.reg_alloc.Consume(lod)};
    const auto sparse_inst{PrepareSparse(inst)};
    const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
    const std::string_view type{TextureType(info)};
    const std::string texture{Texture(ctx, info, index)};
    const std::string offset_vec{Offset(ctx, offset)};
    const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
    const Register ret{ctx.reg_alloc.Define(inst)};
    switch (info.type) {
    case TextureType::Color1D:
    case TextureType::ColorArray1D:
    case TextureType::Color2D:
        ctx.Add("MOV.F {}.z,{};"
                "MOV.F {}.w,{};"
                "TXL.F{} {},{},{},{}{};",
                coord_vec, dref_val, coord_vec, lod_val, sparse_mod, ret, coord_vec, texture, type,
                offset_vec);
        break;
    case TextureType::ColorArray2D:
    case TextureType::ColorCube:
        ctx.Add("MOV.F {}.w,{};"
                "TXL.F{} {},{},{},{},{}{};",
                coord_vec, dref_val, sparse_mod, ret, coord_vec, lod_val, texture, type,
                offset_vec);
        break;
    case TextureType::ColorArrayCube:
        ctx.Add("MOV.F {}.x,{};"
                "MOV.F {}.y,{};"
                "TXL.F{} {},{},{},{},{}{};",
                staging.reg, dref_val, staging.reg, lod_val, sparse_mod, ret, coord_vec,
                staging.reg, texture, type, offset_vec);
        break;
    default:
        throw NotImplementedException("Invalid type {}", info.type.Value());
    }
    StoreSparse(ctx, sparse_inst);
}

void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                     const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2) {
    // Allocate offsets early so they don't overwrite any consumed register
    const auto [off_x, off_y]{AllocOffsetsRegs(ctx, offset2)};
    const auto info{inst.Flags<IR::TextureInstInfo>()};
    const char comp{"xyzw"[info.gather_component]};
    const auto sparse_inst{PrepareSparse(inst)};
    const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
    const std::string_view type{TextureType(info)};
    const std::string texture{Texture(ctx, info, index)};
    const Register coord_vec{ctx.reg_alloc.Consume(coord)};
    const Register ret{ctx.reg_alloc.Define(inst)};
    if (offset2.IsEmpty()) {
        const std::string offset_vec{Offset(ctx, offset)};
        ctx.Add("TXG.F{} {},{},{}.{},{}{};", sparse_mod, ret, coord_vec, texture, comp, type,
                offset_vec);
    } else {
        SwizzleOffsets(ctx, off_x.reg, off_y.reg, offset, offset2);
        ctx.Add("TXGO.F{} {},{},{},{},{}.{},{};", sparse_mod, ret, coord_vec, off_x.reg, off_y.reg,
                texture, comp, type);
    }
    StoreSparse(ctx, sparse_inst);
}

void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                         const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2,
                         const IR::Value& dref) {
    // FIXME: This instruction is not working as expected

    // Allocate offsets early so they don't overwrite any consumed register
    const auto [off_x, off_y]{AllocOffsetsRegs(ctx, offset2)};
    const auto info{inst.Flags<IR::TextureInstInfo>()};
    const auto sparse_inst{PrepareSparse(inst)};
    const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
    const std::string_view type{TextureType(info)};
    const std::string texture{Texture(ctx, info, index)};
    const Register coord_vec{ctx.reg_alloc.Consume(coord)};
    const ScalarF32 dref_value{ctx.reg_alloc.Consume(dref)};
    const Register ret{ctx.reg_alloc.Define(inst)};
    std::string args;
    switch (info.type) {
    case TextureType::Color2D:
        ctx.Add("MOV.F {}.z,{};", coord_vec, dref_value);
        args = fmt::to_string(coord_vec);
        break;
    case TextureType::ColorArray2D:
    case TextureType::ColorCube:
        ctx.Add("MOV.F {}.w,{};", coord_vec, dref_value);
        args = fmt::to_string(coord_vec);
        break;
    case TextureType::ColorArrayCube:
        args = fmt::format("{},{}", coord_vec, dref_value);
        break;
    default:
        throw NotImplementedException("Invalid type {}", info.type.Value());
    }
    if (offset2.IsEmpty()) {
        const std::string offset_vec{Offset(ctx, offset)};
        ctx.Add("TXG.F{} {},{},{},{}{};", sparse_mod, ret, args, texture, type, offset_vec);
    } else {
        SwizzleOffsets(ctx, off_x.reg, off_y.reg, offset, offset2);
        ctx.Add("TXGO.F{} {},{},{},{},{},{};", sparse_mod, ret, args, off_x.reg, off_y.reg, texture,
                type);
    }
    StoreSparse(ctx, sparse_inst);
}

void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                    const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms) {
    const auto info{inst.Flags<IR::TextureInstInfo>()};
    const auto sparse_inst{PrepareSparse(inst)};
    const bool is_multisample{ms.type != Type::Void};
    const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
    const std::string_view type{TextureType(info, is_multisample)};
    const std::string texture{Texture(ctx, info, index)};
    const std::string offset_vec{Offset(ctx, offset)};
    const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
    const Register ret{ctx.reg_alloc.Define(inst)};
    if (info.type == TextureType::Buffer) {
        ctx.Add("TXF.F{} {},{},{},{}{};", sparse_mod, ret, coord_vec, texture, type, offset_vec);
    } else if (is_multisample) {
        ctx.Add("MOV.S {}.w,{};"
                "TXFMS.F{} {},{},{},{}{};",
                coord_vec, ms, sparse_mod, ret, coord_vec, texture, type, offset_vec);
    } else {
        ctx.Add("MOV.S {}.w,{};"
                "TXF.F{} {},{},{},{}{};",
                coord_vec, lod, sparse_mod, ret, coord_vec, texture, type, offset_vec);
    }
    StoreSparse(ctx, sparse_inst);
}

void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                              ScalarS32 lod, [[maybe_unused]] const IR::Value& skip_mips) {
    const auto info{inst.Flags<IR::TextureInstInfo>()};
    const std::string texture{Texture(ctx, info, index)};
    const bool is_msaa{IsTextureMsaa(ctx, info)};
    const std::string_view type{TextureType(info, is_msaa)};
    ctx.Add("TXQ {},{},{},{};", inst, lod, texture, type);
}

void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord) {
    const auto info{inst.Flags<IR::TextureInstInfo>()};
    const std::string texture{Texture(ctx, info, index)};
    const std::string_view type{TextureType(info)};
    ctx.Add("LOD.F {},{},{},{};", inst, coord, texture, type);
}

void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                       const IR::Value& coord, const IR::Value& derivatives,
                       const IR::Value& offset, const IR::Value& lod_clamp) {
    const auto info{inst.Flags<IR::TextureInstInfo>()};
    ScopedRegister dpdx, dpdy, coords;
    const bool multi_component{info.num_derivatives > 1 || info.has_lod_clamp};
    if (multi_component) {
        // Allocate this early to avoid aliasing other registers
        dpdx = ScopedRegister{ctx.reg_alloc};
        dpdy = ScopedRegister{ctx.reg_alloc};
        if (info.num_derivatives >= 3) {
            coords = ScopedRegister{ctx.reg_alloc};
        }
    }
    const auto sparse_inst{PrepareSparse(inst)};
    const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
    const std::string_view type{TextureType(info)};
    const std::string texture{Texture(ctx, info, index)};
    const std::string offset_vec{GradOffset(offset)};
    const Register coord_vec{ctx.reg_alloc.Consume(coord)};
    const Register derivatives_vec{ctx.reg_alloc.Consume(derivatives)};
    const Register ret{ctx.reg_alloc.Define(inst)};
    if (multi_component) {
        ctx.Add("MOV.F {}.x,{}.x;"
                "MOV.F {}.y,{}.z;"
                "MOV.F {}.x,{}.y;"
                "MOV.F {}.y,{}.w;",
                dpdx.reg, derivatives_vec, dpdx.reg, derivatives_vec, dpdy.reg, derivatives_vec,
                dpdy.reg, derivatives_vec);
        Register final_coord;
        if (info.num_derivatives >= 3) {
            ctx.Add("MOV.F {}.z,{}.x;"
                    "MOV.F {}.z,{}.y;",
                    dpdx.reg, coord_vec, dpdy.reg, coord_vec);
            ctx.Add("MOV.F {}.x,0;"
                    "MOV.F {}.y,0;",
                    "MOV.F {}.z,0;", coords.reg, coords.reg, coords.reg);
            final_coord = coords.reg;
        } else {
            final_coord = coord_vec;
        }
        if (info.has_lod_clamp) {
            const ScalarF32 lod_clamp_value{ctx.reg_alloc.Consume(lod_clamp)};
            ctx.Add("MOV.F {}.w,{};"
                    "TXD.F.LODCLAMP{} {},{},{},{},{},{}{};",
                    dpdy.reg, lod_clamp_value, sparse_mod, ret, final_coord, dpdx.reg, dpdy.reg,
                    texture, type, offset_vec);
        } else {
            ctx.Add("TXD.F{} {},{},{},{},{},{}{};", sparse_mod, ret, final_coord, dpdx.reg,
                    dpdy.reg, texture, type, offset_vec);
        }
    } else {
        ctx.Add("TXD.F{} {},{},{}.x,{}.y,{},{}{};", sparse_mod, ret, coord_vec, derivatives_vec,
                derivatives_vec, texture, type, offset_vec);
    }
    StoreSparse(ctx, sparse_inst);
}

void EmitImageRead(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord) {
    const auto info{inst.Flags<IR::TextureInstInfo>()};
    const auto sparse_inst{PrepareSparse(inst)};
    const std::string_view format{FormatStorage(info.image_format)};
    const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
    const std::string_view type{TextureType(info)};
    const std::string image{Image(ctx, info, index)};
    const Register ret{ctx.reg_alloc.Define(inst)};
    ctx.Add("LOADIM.{}{} {},{},{},{};", format, sparse_mod, ret, coord, image, type);
    StoreSparse(ctx, sparse_inst);
}

void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                    Register color) {
    const auto info{inst.Flags<IR::TextureInstInfo>()};
    const std::string_view format{FormatStorage(info.image_format)};
    const std::string_view type{TextureType(info)};
    const std::string image{Image(ctx, info, index)};
    ctx.Add("STOREIM.{} {},{},{},{};", format, image, color, coord, type);
}

void EmitIsTextureScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& index) {
    if (!index.IsImmediate()) {
        throw NotImplementedException("Non-constant texture rescaling");
    }
    ctx.Add("AND.U RC.x,scaling[0].x,{};"
            "SNE.S {},RC.x,0;",
            1u << index.U32(), ctx.reg_alloc.Define(inst));
}

void EmitIsImageScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& index) {
    if (!index.IsImmediate()) {
        throw NotImplementedException("Non-constant texture rescaling");
    }
    ctx.Add("AND.U RC.x,scaling[0].y,{};"
            "SNE.S {},RC.x,0;",
            1u << index.U32(), ctx.reg_alloc.Define(inst));
}

void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                           ScalarU32 value) {
    ImageAtomic(ctx, inst, index, coord, value, "ADD.U32");
}

void EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                           ScalarS32 value) {
    ImageAtomic(ctx, inst, index, coord, value, "MIN.S32");
}

void EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                           ScalarU32 value) {
    ImageAtomic(ctx, inst, index, coord, value, "MIN.U32");
}

void EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                           ScalarS32 value) {
    ImageAtomic(ctx, inst, index, coord, value, "MAX.S32");
}

void EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                           ScalarU32 value) {
    ImageAtomic(ctx, inst, index, coord, value, "MAX.U32");
}

void EmitImageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                          ScalarU32 value) {
    ImageAtomic(ctx, inst, index, coord, value, "IWRAP.U32");
}

void EmitImageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                          ScalarU32 value) {
    ImageAtomic(ctx, inst, index, coord, value, "DWRAP.U32");
}

void EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                          ScalarU32 value) {
    ImageAtomic(ctx, inst, index, coord, value, "AND.U32");
}

void EmitImageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                         ScalarU32 value) {
    ImageAtomic(ctx, inst, index, coord, value, "OR.U32");
}

void EmitImageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                          ScalarU32 value) {
    ImageAtomic(ctx, inst, index, coord, value, "XOR.U32");
}

void EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                               Register coord, ScalarU32 value) {
    ImageAtomic(ctx, inst, index, coord, value, "EXCH.U32");
}

void EmitBindlessImageSampleImplicitLod(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageSampleExplicitLod(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageSampleDrefImplicitLod(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageSampleDrefExplicitLod(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageGather(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageGatherDref(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageFetch(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageQueryDimensions(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageQueryLod(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageGradient(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageRead(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageWrite(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageSampleImplicitLod(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageSampleExplicitLod(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageSampleDrefImplicitLod(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageSampleDrefExplicitLod(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageGather(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageGatherDref(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageFetch(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageQueryDimensions(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageQueryLod(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageGradient(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageRead(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageWrite(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageAtomicIAdd32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageAtomicSMin32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageAtomicUMin32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageAtomicSMax32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageAtomicUMax32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageAtomicInc32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageAtomicDec32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageAtomicAnd32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageAtomicOr32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageAtomicXor32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBindlessImageAtomicExchange32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageAtomicIAdd32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageAtomicSMin32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageAtomicUMin32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageAtomicSMax32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageAtomicUMax32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageAtomicInc32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageAtomicDec32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageAtomicAnd32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageAtomicOr32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageAtomicXor32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

void EmitBoundImageAtomicExchange32(EmitContext&) {
    throw LogicError("Unreachable instruction");
}

} // namespace Shader::Backend::GLASM


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/common_types.h"
#include "shader_recompiler/backend/glasm/reg_alloc.h"

namespace Shader::IR {
enum class Attribute : u64;
enum class Patch : u64;
class Inst;
class Value;
} // namespace Shader::IR

namespace Shader::Backend::GLASM {

class EmitContext;

// Microinstruction emitters
void EmitPhi(EmitContext& ctx, IR::Inst& inst);
void EmitVoid(EmitContext& ctx);
void EmitIdentity(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
void EmitReference(EmitContext&, const IR::Value& value);
void EmitPhiMove(EmitContext& ctx, const IR::Value& phi, const IR::Value& value);
void EmitJoin(EmitContext& ctx);
void EmitDemoteToHelperInvocation(EmitContext& ctx);
void EmitBarrier(EmitContext& ctx);
void EmitWorkgroupMemoryBarrier(EmitContext& ctx);
void EmitDeviceMemoryBarrier(EmitContext& ctx);
void EmitPrologue(EmitContext& ctx);
void EmitEpilogue(EmitContext& ctx);
void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream);
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream);
void EmitGetRegister(EmitContext& ctx);
void EmitSetRegister(EmitContext& ctx);
void EmitGetPred(EmitContext& ctx);
void EmitSetPred(EmitContext& ctx);
void EmitSetGotoVariable(EmitContext& ctx);
void EmitGetGotoVariable(EmitContext& ctx);
void EmitSetIndirectBranchVariable(EmitContext& ctx);
void EmitGetIndirectBranchVariable(EmitContext& ctx);
void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex);
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex);
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value, ScalarU32 vertex);
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset, ScalarU32 vertex);
void EmitSetAttributeIndexed(EmitContext& ctx, ScalarU32 offset, ScalarF32 value, ScalarU32 vertex);
void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch);
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, ScalarF32 value);
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, ScalarF32 value);
void EmitSetSampleMask(EmitContext& ctx, ScalarS32 value);
void EmitSetFragDepth(EmitContext& ctx, ScalarF32 value);
void EmitGetZFlag(EmitContext& ctx);
void EmitGetSFlag(EmitContext& ctx);
void EmitGetCFlag(EmitContext& ctx);
void EmitGetOFlag(EmitContext& ctx);
void EmitSetZFlag(EmitContext& ctx);
void EmitSetSFlag(EmitContext& ctx);
void EmitSetCFlag(EmitContext& ctx);
void EmitSetOFlag(EmitContext& ctx);
void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst);
void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst);
void EmitInvocationId(EmitContext& ctx, IR::Inst& inst);
void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst);
void EmitSampleId(EmitContext& ctx, IR::Inst& inst);
void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
void EmitSR_WScaleFactorXY(EmitContext& ctx, IR::Inst& inst);
void EmitSR_WScaleFactorZ(EmitContext& ctx, IR::Inst& inst);
void EmitYDirection(EmitContext& ctx, IR::Inst& inst);
void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst);
void EmitRenderArea(EmitContext& ctx, IR::Inst& inst);
void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset);
void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value);
void EmitUndefU1(EmitContext& ctx, IR::Inst& inst);
void EmitUndefU8(EmitContext& ctx, IR::Inst& inst);
void EmitUndefU16(EmitContext& ctx, IR::Inst& inst);
void EmitUndefU32(EmitContext& ctx, IR::Inst& inst);
void EmitUndefU64(EmitContext& ctx, IR::Inst& inst);
void EmitLoadGlobalU8(EmitContext& ctx, IR::Inst& inst, Register address);
void EmitLoadGlobalS8(EmitContext& ctx, IR::Inst& inst, Register address);
void EmitLoadGlobalU16(EmitContext& ctx, IR::Inst& inst, Register address);
void EmitLoadGlobalS16(EmitContext& ctx, IR::Inst& inst, Register address);
void EmitLoadGlobal32(EmitContext& ctx, IR::Inst& inst, Register address);
void EmitLoadGlobal64(EmitContext& ctx, IR::Inst& inst, Register address);
void EmitLoadGlobal128(EmitContext& ctx, IR::Inst& inst, Register address);
void EmitWriteGlobalU8(EmitContext& ctx, Register address, Register value);
void EmitWriteGlobalS8(EmitContext& ctx, Register address, Register value);
void EmitWriteGlobalU16(EmitContext& ctx, Register address, Register value);
void EmitWriteGlobalS16(EmitContext& ctx, Register address, Register value);
void EmitWriteGlobal32(EmitContext& ctx, Register address, ScalarU32 value);
void EmitWriteGlobal64(EmitContext& ctx, Register address, Register value);
void EmitWriteGlobal128(EmitContext& ctx, Register address, Register value);
void EmitLoadStorageU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                       ScalarU32 offset);
void EmitLoadStorageS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                       ScalarU32 offset);
void EmitLoadStorageU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                        ScalarU32 offset);
void EmitLoadStorageS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                        ScalarU32 offset);
void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                       ScalarU32 offset);
void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                       ScalarU32 offset);
void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                        ScalarU32 offset);
void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
                        ScalarU32 value);
void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
                        ScalarS32 value);
void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
                         ScalarU32 value);
void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
                         ScalarS32 value);
void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
                        ScalarU32 value);
void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
                        Register value);
void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
                         Register value);
void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
void EmitWriteSharedU8(EmitContext& ctx, ScalarU32 offset, ScalarU32 value);
void EmitWriteSharedU16(EmitContext& ctx, ScalarU32 offset, ScalarU32 value);
void EmitWriteSharedU32(EmitContext& ctx, ScalarU32 offset, ScalarU32 value);
void EmitWriteSharedU64(EmitContext& ctx, ScalarU32 offset, Register value);
void EmitWriteSharedU128(EmitContext& ctx, ScalarU32 offset, Register value);
void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
                                 const IR::Value& e2);
void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
                                 const IR::Value& e2, const IR::Value& e3);
void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
                                 const IR::Value& e2, const IR::Value& e3, const IR::Value& e4);
void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
void EmitCompositeInsertU32x2(EmitContext& ctx, Register composite, ScalarU32 object, u32 index);
void EmitCompositeInsertU32x3(EmitContext& ctx, Register composite, ScalarU32 object, u32 index);
void EmitCompositeInsertU32x4(EmitContext& ctx, Register composite, ScalarU32 object, u32 index);
void EmitCompositeConstructF16x2(EmitContext& ctx, Register e1, Register e2);
void EmitCompositeConstructF16x3(EmitContext& ctx, Register e1, Register e2, Register e3);
void EmitCompositeConstructF16x4(EmitContext& ctx, Register e1, Register e2, Register e3,
                                 Register e4);
void EmitCompositeExtractF16x2(EmitContext& ctx, Register composite, u32 index);
void EmitCompositeExtractF16x3(EmitContext& ctx, Register composite, u32 index);
void EmitCompositeExtractF16x4(EmitContext& ctx, Register composite, u32 index);
void EmitCompositeInsertF16x2(EmitContext& ctx, Register composite, Register object, u32 index);
void EmitCompositeInsertF16x3(EmitContext& ctx, Register composite, Register object, u32 index);
void EmitCompositeInsertF16x4(EmitContext& ctx, Register composite, Register object, u32 index);
void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
                                 const IR::Value& e2);
void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
                                 const IR::Value& e2, const IR::Value& e3);
void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
                                 const IR::Value& e2, const IR::Value& e3, const IR::Value& e4);
void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, Register composite,
                              ScalarF32 object, u32 index);
void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, Register composite,
                              ScalarF32 object, u32 index);
void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, Register composite,
                              ScalarF32 object, u32 index);
void EmitCompositeConstructF64x2(EmitContext& ctx);
void EmitCompositeConstructF64x3(EmitContext& ctx);
void EmitCompositeConstructF64x4(EmitContext& ctx);
void EmitCompositeExtractF64x2(EmitContext& ctx);
void EmitCompositeExtractF64x3(EmitContext& ctx);
void EmitCompositeExtractF64x4(EmitContext& ctx);
void EmitCompositeInsertF64x2(EmitContext& ctx, Register composite, Register object, u32 index);
void EmitCompositeInsertF64x3(EmitContext& ctx, Register composite, Register object, u32 index);
void EmitCompositeInsertF64x4(EmitContext& ctx, Register composite, Register object, u32 index);
void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
                  ScalarS32 false_value);
void EmitSelectU8(EmitContext& ctx, ScalarS32 cond, ScalarS32 true_value, ScalarS32 false_value);
void EmitSelectU16(EmitContext& ctx, ScalarS32 cond, ScalarS32 true_value, ScalarS32 false_value);
void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
                   ScalarS32 false_value);
void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, Register true_value,
                   Register false_value);
void EmitSelectF16(EmitContext& ctx, ScalarS32 cond, Register true_value, Register false_value);
void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
                   ScalarS32 false_value);
void EmitSelectF64(EmitContext& ctx, ScalarS32 cond, Register true_value, Register false_value);
void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitPackFloat2x16(EmitContext& ctx, Register value);
void EmitUnpackFloat2x16(EmitContext& ctx, Register value);
void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitGetZeroFromOp(EmitContext& ctx);
void EmitGetSignFromOp(EmitContext& ctx);
void EmitGetCarryFromOp(EmitContext& ctx);
void EmitGetOverflowFromOp(EmitContext& ctx);
void EmitGetSparseFromOp(EmitContext& ctx);
void EmitGetInBoundsFromOp(EmitContext& ctx);
void EmitFPAbs16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
void EmitFPAdd16(EmitContext& ctx, IR::Inst& inst, Register a, Register b);
void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b);
void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b);
void EmitFPFma16(EmitContext& ctx, IR::Inst& inst, Register a, Register b, Register c);
void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b, ScalarF32 c);
void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b, ScalarF64 c);
void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b);
void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b);
void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b);
void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b);
void EmitFPMul16(EmitContext& ctx, IR::Inst& inst, Register a, Register b);
void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b);
void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b);
void EmitFPNeg16(EmitContext& ctx, Register value);
void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, ScalarRegister value);
void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitFPSin(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitFPCos(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitFPRecip64(EmitContext& ctx, Register value);
void EmitFPRecipSqrt32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitFPRecipSqrt64(EmitContext& ctx, Register value);
void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitFPSaturate16(EmitContext& ctx, Register value);
void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitFPSaturate64(EmitContext& ctx, Register value);
void EmitFPClamp16(EmitContext& ctx, Register value, Register min_value, Register max_value);
void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value, ScalarF32 min_value,
                   ScalarF32 max_value);
void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value, ScalarF64 min_value,
                   ScalarF64 max_value);
void EmitFPRoundEven16(EmitContext& ctx, Register value);
void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
void EmitFPFloor16(EmitContext& ctx, Register value);
void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
void EmitFPCeil16(EmitContext& ctx, Register value);
void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
void EmitFPTrunc16(EmitContext& ctx, Register value);
void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
void EmitFPOrdEqual16(EmitContext& ctx, Register lhs, Register rhs);
void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
void EmitFPUnordEqual16(EmitContext& ctx, Register lhs, Register rhs);
void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
void EmitFPOrdNotEqual16(EmitContext& ctx, Register lhs, Register rhs);
void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
void EmitFPUnordNotEqual16(EmitContext& ctx, Register lhs, Register rhs);
void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
void EmitFPOrdLessThan16(EmitContext& ctx, Register lhs, Register rhs);
void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
void EmitFPUnordLessThan16(EmitContext& ctx, Register lhs, Register rhs);
void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
void EmitFPOrdGreaterThan16(EmitContext& ctx, Register lhs, Register rhs);
void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
void EmitFPUnordGreaterThan16(EmitContext& ctx, Register lhs, Register rhs);
void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
void EmitFPOrdLessThanEqual16(EmitContext& ctx, Register lhs, Register rhs);
void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
void EmitFPUnordLessThanEqual16(EmitContext& ctx, Register lhs, Register rhs);
void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
void EmitFPOrdGreaterThanEqual16(EmitContext& ctx, Register lhs, Register rhs);
void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
void EmitFPUnordGreaterThanEqual16(EmitContext& ctx, Register lhs, Register rhs);
void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
void EmitFPIsNan16(EmitContext& ctx, Register value);
void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, Register a, Register b);
void EmitISub32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
void EmitISub64(EmitContext& ctx, IR::Inst& inst, Register a, Register b);
void EmitIMul32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
void EmitSDiv32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
void EmitUDiv32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b);
void EmitINeg32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
void EmitINeg64(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift);
void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base, ScalarU32 shift);
void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift);
void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
                             ScalarU32 shift);
void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 shift);
void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
                                ScalarS32 shift);
void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 insert,
                        ScalarS32 offset, ScalarS32 count);
void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 offset,
                          ScalarS32 count);
void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 offset,
                          ScalarU32 count);
void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
void EmitSMin32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
void EmitUMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b);
void EmitSMax32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
void EmitUMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b);
void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value, ScalarS32 min, ScalarS32 max);
void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 min, ScalarU32 max);
void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
void EmitULessThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs);
void EmitIEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs);
void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs);
void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs);
void EmitSharedAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
                            ScalarU32 value);
void EmitSharedAtomicSMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
                            ScalarS32 value);
void EmitSharedAtomicUMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
                            ScalarU32 value);
void EmitSharedAtomicSMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
                            ScalarS32 value);
void EmitSharedAtomicUMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
                            ScalarU32 value);
void EmitSharedAtomicInc32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
                           ScalarU32 value);
void EmitSharedAtomicDec32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
                           ScalarU32 value);
void EmitSharedAtomicAnd32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
                           ScalarU32 value);
void EmitSharedAtomicOr32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
                          ScalarU32 value);
void EmitSharedAtomicXor32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
                           ScalarU32 value);
void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
                                ScalarU32 value);
void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
                                Register value);
void EmitSharedAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
                                  Register value);
void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                             ScalarU32 offset, ScalarU32 value);
void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                             ScalarU32 offset, ScalarS32 value);
void EmitStorageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                             ScalarU32 offset, ScalarU32 value);
void EmitStorageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                             ScalarU32 offset, ScalarS32 value);
void EmitStorageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                             ScalarU32 offset, ScalarU32 value);
void EmitStorageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                            ScalarU32 offset, ScalarU32 value);
void EmitStorageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                            ScalarU32 offset, ScalarU32 value);
void EmitStorageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                            ScalarU32 offset, ScalarU32 value);
void EmitStorageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                           ScalarU32 offset, ScalarU32 value);
void EmitStorageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                            ScalarU32 offset, ScalarU32 value);
void EmitStorageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                                 ScalarU32 offset, ScalarU32 value);
void EmitStorageAtomicIAdd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                             ScalarU32 offset, Register value);
void EmitStorageAtomicSMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                             ScalarU32 offset, Register value);
void EmitStorageAtomicUMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                             ScalarU32 offset, Register value);
void EmitStorageAtomicSMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                             ScalarU32 offset, Register value);
void EmitStorageAtomicUMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                             ScalarU32 offset, Register value);
void EmitStorageAtomicAnd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                            ScalarU32 offset, Register value);
void EmitStorageAtomicOr64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                           ScalarU32 offset, Register value);
void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                            ScalarU32 offset, Register value);
void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                                 ScalarU32 offset, Register value);
void EmitStorageAtomicIAdd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                               ScalarU32 offset, Register value);
void EmitStorageAtomicSMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                               ScalarU32 offset, Register value);
void EmitStorageAtomicUMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                               ScalarU32 offset, Register value);
void EmitStorageAtomicSMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                               ScalarU32 offset, Register value);
void EmitStorageAtomicUMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                               ScalarU32 offset, Register value);
void EmitStorageAtomicAnd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                              ScalarU32 offset, Register value);
void EmitStorageAtomicOr32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                             ScalarU32 offset, Register value);
void EmitStorageAtomicXor32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                              ScalarU32 offset, Register value);
void EmitStorageAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                                   ScalarU32 offset, Register value);
void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                             ScalarU32 offset, ScalarF32 value);
void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                               ScalarU32 offset, Register value);
void EmitStorageAtomicAddF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                               ScalarU32 offset, Register value);
void EmitStorageAtomicMinF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                               ScalarU32 offset, Register value);
void EmitStorageAtomicMinF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                               ScalarU32 offset, Register value);
void EmitStorageAtomicMaxF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                               ScalarU32 offset, Register value);
void EmitStorageAtomicMaxF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
                               ScalarU32 offset, Register value);
void EmitGlobalAtomicIAdd32(EmitContext& ctx);
void EmitGlobalAtomicSMin32(EmitContext& ctx);
void EmitGlobalAtomicUMin32(EmitContext& ctx);
void EmitGlobalAtomicSMax32(EmitContext& ctx);
void EmitGlobalAtomicUMax32(EmitContext& ctx);
void EmitGlobalAtomicInc32(EmitContext& ctx);
void EmitGlobalAtomicDec32(EmitContext& ctx);
void EmitGlobalAtomicAnd32(EmitContext& ctx);
void EmitGlobalAtomicOr32(EmitContext& ctx);
void EmitGlobalAtomicXor32(EmitContext& ctx);
void EmitGlobalAtomicExchange32(EmitContext& ctx);
void EmitGlobalAtomicIAdd64(EmitContext& ctx);
void EmitGlobalAtomicSMin64(EmitContext& ctx);
void EmitGlobalAtomicUMin64(EmitContext& ctx);
void EmitGlobalAtomicSMax64(EmitContext& ctx);
void EmitGlobalAtomicUMax64(EmitContext& ctx);
void EmitGlobalAtomicInc64(EmitContext& ctx);
void EmitGlobalAtomicDec64(EmitContext& ctx);
void EmitGlobalAtomicAnd64(EmitContext& ctx);
void EmitGlobalAtomicOr64(EmitContext& ctx);
void EmitGlobalAtomicXor64(EmitContext& ctx);
void EmitGlobalAtomicExchange64(EmitContext& ctx);
void EmitGlobalAtomicIAdd32x2(EmitContext& ctx);
void EmitGlobalAtomicSMin32x2(EmitContext& ctx);
void EmitGlobalAtomicUMin32x2(EmitContext& ctx);
void EmitGlobalAtomicSMax32x2(EmitContext& ctx);
void EmitGlobalAtomicUMax32x2(EmitContext& ctx);
void EmitGlobalAtomicInc32x2(EmitContext& ctx);
void EmitGlobalAtomicDec32x2(EmitContext& ctx);
void EmitGlobalAtomicAnd32x2(EmitContext& ctx);
void EmitGlobalAtomicOr32x2(EmitContext& ctx);
void EmitGlobalAtomicXor32x2(EmitContext& ctx);
void EmitGlobalAtomicExchange32x2(EmitContext& ctx);
void EmitGlobalAtomicAddF32(EmitContext& ctx);
void EmitGlobalAtomicAddF16x2(EmitContext& ctx);
void EmitGlobalAtomicAddF32x2(EmitContext& ctx);
void EmitGlobalAtomicMinF16x2(EmitContext& ctx);
void EmitGlobalAtomicMinF32x2(EmitContext& ctx);
void EmitGlobalAtomicMaxF16x2(EmitContext& ctx);
void EmitGlobalAtomicMaxF32x2(EmitContext& ctx);
void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
void EmitConvertS16F16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitConvertS16F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
void EmitConvertS32F16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
void EmitConvertS64F16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
void EmitConvertU16F16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertU16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitConvertU16F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
void EmitConvertU32F16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
void EmitConvertU64F16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitConvertF32F16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
void EmitConvertF16S8(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF16S16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF16S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
void EmitConvertF16S64(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF16U8(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF16U16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF16U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
void EmitConvertF16U64(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF32S8(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF32S16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF32U8(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF64S8(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF64S16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF64U8(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF64U16(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, Register value);
void EmitBindlessImageSampleImplicitLod(EmitContext&);
void EmitBindlessImageSampleExplicitLod(EmitContext&);
void EmitBindlessImageSampleDrefImplicitLod(EmitContext&);
void EmitBindlessImageSampleDrefExplicitLod(EmitContext&);
void EmitBindlessImageGather(EmitContext&);
void EmitBindlessImageGatherDref(EmitContext&);
void EmitBindlessImageFetch(EmitContext&);
void EmitBindlessImageQueryDimensions(EmitContext&);
void EmitBindlessImageQueryLod(EmitContext&);
void EmitBindlessImageGradient(EmitContext&);
void EmitBindlessImageRead(EmitContext&);
void EmitBindlessImageWrite(EmitContext&);
void EmitBoundImageSampleImplicitLod(EmitContext&);
void EmitBoundImageSampleExplicitLod(EmitContext&);
void EmitBoundImageSampleDrefImplicitLod(EmitContext&);
void EmitBoundImageSampleDrefExplicitLod(EmitContext&);
void EmitBoundImageGather(EmitContext&);
void EmitBoundImageGatherDref(EmitContext&);
void EmitBoundImageFetch(EmitContext&);
void EmitBoundImageQueryDimensions(EmitContext&);
void EmitBoundImageQueryLod(EmitContext&);
void EmitBoundImageGradient(EmitContext&);
void EmitBoundImageRead(EmitContext&);
void EmitBoundImageWrite(EmitContext&);
void EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                                const IR::Value& coord, Register bias_lc, const IR::Value& offset);
void EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                                const IR::Value& coord, ScalarF32 lod, const IR::Value& offset);
void EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                                    const IR::Value& coord, const IR::Value& dref,
                                    const IR::Value& bias_lc, const IR::Value& offset);
void EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                                    const IR::Value& coord, const IR::Value& dref,
                                    const IR::Value& lod, const IR::Value& offset);
void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                     const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2);
void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                         const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2,
                         const IR::Value& dref);
void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                    const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms);
void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                              ScalarS32 lod, const IR::Value& skip_mips);
void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord);
void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                       const IR::Value& coord, const IR::Value& derivatives,
                       const IR::Value& offset, const IR::Value& lod_clamp);
void EmitImageRead(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord);
void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                    Register color);
void EmitIsTextureScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& index);
void EmitIsImageScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& index);
void EmitBindlessImageAtomicIAdd32(EmitContext&);
void EmitBindlessImageAtomicSMin32(EmitContext&);
void EmitBindlessImageAtomicUMin32(EmitContext&);
void EmitBindlessImageAtomicSMax32(EmitContext&);
void EmitBindlessImageAtomicUMax32(EmitContext&);
void EmitBindlessImageAtomicInc32(EmitContext&);
void EmitBindlessImageAtomicDec32(EmitContext&);
void EmitBindlessImageAtomicAnd32(EmitContext&);
void EmitBindlessImageAtomicOr32(EmitContext&);
void EmitBindlessImageAtomicXor32(EmitContext&);
void EmitBindlessImageAtomicExchange32(EmitContext&);
void EmitBoundImageAtomicIAdd32(EmitContext&);
void EmitBoundImageAtomicSMin32(EmitContext&);
void EmitBoundImageAtomicUMin32(EmitContext&);
void EmitBoundImageAtomicSMax32(EmitContext&);
void EmitBoundImageAtomicUMax32(EmitContext&);
void EmitBoundImageAtomicInc32(EmitContext&);
void EmitBoundImageAtomicDec32(EmitContext&);
void EmitBoundImageAtomicAnd32(EmitContext&);
void EmitBoundImageAtomicOr32(EmitContext&);
void EmitBoundImageAtomicXor32(EmitContext&);
void EmitBoundImageAtomicExchange32(EmitContext&);
void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                           ScalarU32 value);
void EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                           ScalarS32 value);
void EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                           ScalarU32 value);
void EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                           ScalarS32 value);
void EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                           ScalarU32 value);
void EmitImageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                          ScalarU32 value);
void EmitImageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                          ScalarU32 value);
void EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                          ScalarU32 value);
void EmitImageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                         ScalarU32 value);
void EmitImageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
                          ScalarU32 value);
void EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
                               Register coord, ScalarU32 value);
void EmitLaneId(EmitContext& ctx, IR::Inst& inst);
void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred);
void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred);
void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred);
void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred);
void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst);
void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst);
void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst);
void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst);
void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst);
void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
                      const IR::Value& clamp, const IR::Value& segmentation_mask);
void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
                   const IR::Value& clamp, const IR::Value& segmentation_mask);
void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
                     const IR::Value& clamp, const IR::Value& segmentation_mask);
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
                          const IR::Value& clamp, const IR::Value& segmentation_mask);
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
                     ScalarU32 swizzle);
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);

} // namespace Shader::Backend::GLASM

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"

namespace Shader::Backend::GLASM {
namespace {
void BitwiseLogicalOp(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b,
                      std::string_view lop) {
    const auto zero = inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp);
    const auto sign = inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp);
    if (zero) {
        zero->Invalidate();
    }
    if (sign) {
        sign->Invalidate();
    }
    if (zero || sign) {
        ctx.reg_alloc.InvalidateConditionCodes();
    }
    const auto ret{ctx.reg_alloc.Define(inst)};
    ctx.Add("{}.S {}.x,{},{};", lop, ret, a, b);
    if (zero) {
        ctx.Add("SEQ.S {},{},0;", *zero, ret);
    }
    if (sign) {
        ctx.Add("SLT.S {},{},0;", *sign, ret);
    }
}
} // Anonymous namespace

void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
    const std::array flags{
        inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp),
        inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp),
        inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp),
        inst.GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp),
    };
    for (IR::Inst* const flag_inst : flags) {
        if (flag_inst) {
            flag_inst->Invalidate();
        }
    }
    const bool cc{inst.HasAssociatedPseudoOperation()};
    const std::string_view cc_mod{cc ? ".CC" : ""};
    if (cc) {
        ctx.reg_alloc.InvalidateConditionCodes();
    }
    const auto ret{ctx.reg_alloc.Define(inst)};
    ctx.Add("ADD.S{} {}.x,{},{};", cc_mod, ret, a, b);
    if (!cc) {
        return;
    }
    static constexpr std::array<std::string_view, 4> masks{"", "SF", "CF", "OF"};
    for (size_t flag_index = 0; flag_index < flags.size(); ++flag_index) {
        if (!flags[flag_index]) {
            continue;
        }
        const auto flag_ret{ctx.reg_alloc.Define(*flags[flag_index])};
        if (flag_index == 0) {
            ctx.Add("SEQ.S {}.x,{}.x,0;", flag_ret, ret);
        } else {
            // We could use conditional execution here, but it's broken on Nvidia's compiler
            ctx.Add("IF {}.x;"
                    "MOV.S {}.x,-1;"
                    "ELSE;"
                    "MOV.S {}.x,0;"
                    "ENDIF;",
                    masks[flag_index], flag_ret, flag_ret);
        }
    }
}

void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, Register a, Register b) {
    ctx.LongAdd("ADD.S64 {}.x,{}.x,{}.x;", inst, a, b);
}

void EmitISub32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
    ctx.Add("SUB.S {}.x,{},{};", inst, a, b);
}

void EmitISub64(EmitContext& ctx, IR::Inst& inst, Register a, Register b) {
    ctx.LongAdd("SUB.S64 {}.x,{}.x,{}.x;", inst, a, b);
}

void EmitIMul32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
    ctx.Add("MUL.S {}.x,{},{};", inst, a, b);
}

void EmitSDiv32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
    ctx.Add("DIV.S {}.x,{},{};", inst, a, b);
}

void EmitUDiv32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b) {
    ctx.Add("DIV.U {}.x,{},{};", inst, a, b);
}

void EmitINeg32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
    if (value.type != Type::Register && static_cast<s32>(value.imm_u32) < 0) {
        ctx.Add("MOV.S {},{};", inst, -static_cast<s32>(value.imm_u32));
    } else {
        ctx.Add("MOV.S {},-{};", inst, value);
    }
}

void EmitINeg64(EmitContext& ctx, IR::Inst& inst, Register value) {
    ctx.LongAdd("MOV.S64 {},-{};", inst, value);
}

void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
    ctx.Add("ABS.S {},{};", inst, value);
}

void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift) {
    ctx.Add("SHL.U {}.x,{},{};", inst, base, shift);
}

void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
                            ScalarU32 shift) {
    ctx.LongAdd("SHL.U64 {}.x,{},{};", inst, base, shift);
}

void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift) {
    ctx.Add("SHR.U {}.x,{},{};", inst, base, shift);
}

void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
                             ScalarU32 shift) {
    ctx.LongAdd("SHR.U64 {}.x,{},{};", inst, base, shift);
}

void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 shift) {
    ctx.Add("SHR.S {}.x,{},{};", inst, base, shift);
}

void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
                                ScalarS32 shift) {
    ctx.LongAdd("SHR.S64 {}.x,{},{};", inst, base, shift);
}

void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
    BitwiseLogicalOp(ctx, inst, a, b, "AND");
}

void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
    BitwiseLogicalOp(ctx, inst, a, b, "OR");
}

void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
    BitwiseLogicalOp(ctx, inst, a, b, "XOR");
}

void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 insert,
                        ScalarS32 offset, ScalarS32 count) {
    const Register ret{ctx.reg_alloc.Define(inst)};
    if (count.type != Type::Register && offset.type != Type::Register) {
        ctx.Add("BFI.S {},{{{},{},0,0}},{},{};", ret, count, offset, insert, base);
    } else {
        ctx.Add("MOV.S RC.x,{};"
                "MOV.S RC.y,{};"
                "BFI.S {},RC,{},{};",
                count, offset, ret, insert, base);
    }
}

void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 offset,
                          ScalarS32 count) {
    const Register ret{ctx.reg_alloc.Define(inst)};
    if (count.type != Type::Register && offset.type != Type::Register) {
        ctx.Add("BFE.S {},{{{},{},0,0}},{};", ret, count, offset, base);
    } else {
        ctx.Add("MOV.S RC.x,{};"
                "MOV.S RC.y,{};"
                "BFE.S {},RC,{};",
                count, offset, ret, base);
    }
}

void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 offset,
                          ScalarU32 count) {
    const auto zero = inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp);
    const auto sign = inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp);
    if (zero) {
        zero->Invalidate();
    }
    if (sign) {
        sign->Invalidate();
    }
    if (zero || sign) {
        ctx.reg_alloc.InvalidateConditionCodes();
    }
    const Register ret{ctx.reg_alloc.Define(inst)};
    if (count.type != Type::Register && offset.type != Type::Register) {
        ctx.Add("BFE.U {},{{{},{},0,0}},{};", ret, count, offset, base);
    } else {
        ctx.Add("MOV.U RC.x,{};"
                "MOV.U RC.y,{};"
                "BFE.U {},RC,{};",
                count, offset, ret, base);
    }
    if (zero) {
        ctx.Add("SEQ.S {},{},0;", *zero, ret);
    }
    if (sign) {
        ctx.Add("SLT.S {},{},0;", *sign, ret);
    }
}

void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
    ctx.Add("BFR {},{};", inst, value);
}

void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
    ctx.Add("BTC {},{};", inst, value);
}

void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
    ctx.Add("NOT.S {},{};", inst, value);
}

void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
    ctx.Add("BTFM.S {},{};", inst, value);
}

void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
    ctx.Add("BTFM.U {},{};", inst, value);
}

void EmitSMin32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
    ctx.Add("MIN.S {},{},{};", inst, a, b);
}

void EmitUMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b) {
    ctx.Add("MIN.U {},{},{};", inst, a, b);
}

void EmitSMax32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
    ctx.Add("MAX.S {},{},{};", inst, a, b);
}

void EmitUMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b) {
    ctx.Add("MAX.U {},{},{};", inst, a, b);
}

void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value, ScalarS32 min, ScalarS32 max) {
    const Register ret{ctx.reg_alloc.Define(inst)};
    ctx.Add("MIN.S RC.x,{},{};"
            "MAX.S {}.x,RC.x,{};",
            max, value, ret, min);
}

void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 min, ScalarU32 max) {
    const Register ret{ctx.reg_alloc.Define(inst)};
    ctx.Add("MIN.U RC.x,{},{};"
            "MAX.U {}.x,RC.x,{};",
            max, value, ret, min);
}

void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
    ctx.Add("SLT.S {}.x,{},{};", inst, lhs, rhs);
}

void EmitULessThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
    ctx.Add("SLT.U {}.x,{},{};", inst, lhs, rhs);
}

void EmitIEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
    ctx.Add("SEQ.S {}.x,{},{};", inst, lhs, rhs);
}

void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
    ctx.Add("SLE.S {}.x,{},{};", inst, lhs, rhs);
}

void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
    ctx.Add("SLE.U {}.x,{},{};", inst, lhs, rhs);
}

void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
    ctx.Add("SGT.S {}.x,{},{};", inst, lhs, rhs);
}

void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
    ctx.Add("SGT.U {}.x,{},{};", inst, lhs, rhs);
}

void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
    ctx.Add("SNE.U {}.x,{},{};", inst, lhs, rhs);
}

void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
    ctx.Add("SGE.S {}.x,{},{};", inst, lhs, rhs);
}

void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
    ctx.Add("SGE.U {}.x,{},{};", inst, lhs, rhs);
}

} // namespace Shader::Backend::GLASM


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"

namespace Shader::Backend::GLASM {

void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
    ctx.Add("OR.S {},{},{};", inst, a, b);
}

void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
    ctx.Add("AND.S {},{},{};", inst, a, b);
}

void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
    ctx.Add("XOR.S {},{},{};", inst, a, b);
}

void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
    ctx.Add("SEQ.S {},{},0;", inst, value);
}

} // namespace Shader::Backend::GLASM