void Program::Dumper::dump()

in gfx/skia/skia/src/sksl/codegen/SkSLRasterPipelineBuilder.cpp [2897:3830]


void Program::Dumper::dump(SkWStream* out, bool writeInstructionCount) {
    using POp = ProgramOp;

    // Allocate memory for the slot and uniform data, even though the program won't ever be
    // executed. The program requires pointer ranges for managing its data, and ASAN will report
    // errors if those pointers are pointing at unallocated memory.
    SkArenaAlloc alloc(/*firstHeapAllocation=*/1000);
    fSlots = fProgram.allocateSlotData(&alloc).value();
    float* uniformPtr = alloc.makeArray<float>(fProgram.fNumUniformSlots);
    fUniforms = SkSpan(uniformPtr, fProgram.fNumUniformSlots);

    // Turn this program into an array of Raster Pipeline stages.
    fProgram.makeStages(&fStages, &alloc, fUniforms, fSlots);

    // Assemble lookup tables for program labels and slot names.
    this->buildLabelToStageMap();
    this->buildUniqueSlotNameList();

    // Emit the program's instruction count.
    if (writeInstructionCount) {
        int invocationCount = 0, instructionCount = 0;
        for (const Stage& stage : fStages) {
            switch (stage.op) {
                case POp::label:
                    // consumes zero instructions
                    break;

                case POp::invoke_shader:
                case POp::invoke_color_filter:
                case POp::invoke_blender:
                case POp::invoke_to_linear_srgb:
                case POp::invoke_from_linear_srgb:
                    ++invocationCount;
                    break;

                default:
                    ++instructionCount;
                    break;
            }
        }

        out->writeText(std::to_string(instructionCount).c_str());
        out->writeText(" instructions");
        if (invocationCount > 0) {
            out->writeText(", ");
            out->writeText(std::to_string(invocationCount).c_str());
            out->writeText(" invocations");
        }
        out->writeText("\n\n");
    }

    // Emit all of the program's immutable data.
    const char* header = "[immutable slots]\n";
    const char* footer = "";
    for (const Instruction& inst : fProgram.fInstructions) {
        if (inst.fOp == BuilderOp::store_immutable_value) {
            out->writeText(header);
            out->writeText("i");
            out->writeText(std::to_string(inst.fSlotA).c_str());
            out->writeText(" = ");
            out->writeText(this->imm(sk_bit_cast<float>(inst.fImmA)).c_str());
            out->writeText("\n");

            header = "";
            footer = "\n";
        }
    }
    out->writeText(footer);

    // Emit the program's instruction list.
    for (int index = 0; index < fStages.size(); ++index) {
        const Stage& stage = fStages[index];

        std::string opArg1, opArg2, opArg3, opSwizzle;
        switch (stage.op) {
            case POp::label:
            case POp::invoke_shader:
            case POp::invoke_color_filter:
            case POp::invoke_blender:
                opArg1 = this->immCtx(stage.ctx, /*showAsFloat=*/false);
                break;

            case POp::case_op: {
                auto ctx =
                        SkRPCtxUtils::Unpack((const SkRasterPipelineContexts::CaseOpCtx*)stage.ctx);
                opArg1 = this->offsetCtx(ctx.offset, 1);
                opArg2 = this->offsetCtx(ctx.offset + sizeof(int32_t) * N, 1);
                opArg3 = this->imm(sk_bit_cast<float>(ctx.expectedValue), /*showAsFloat=*/false);
                break;
            }
            case POp::swizzle_1:
            case POp::swizzle_2:
            case POp::swizzle_3:
            case POp::swizzle_4:
                std::tie(opArg1, opArg2) = this->swizzleCtx(stage.op, stage.ctx);
                break;

            case POp::swizzle_copy_slot_masked:
            case POp::swizzle_copy_2_slots_masked:
            case POp::swizzle_copy_3_slots_masked:
            case POp::swizzle_copy_4_slots_masked:
                std::tie(opArg1, opArg2) = this->swizzleCopyCtx(stage.op, stage.ctx);
                break;

            case POp::refract_4_floats:
                std::tie(opArg1, opArg2) = this->adjacentPtrCtx(stage.ctx, 4);
                opArg3 = this->ptrCtx((const float*)(stage.ctx) + (8 * N), 1);
                break;

            case POp::dot_2_floats:
                opArg1 = this->ptrCtx(stage.ctx, 1);
                std::tie(opArg2, opArg3) = this->adjacentPtrCtx(stage.ctx, 2);
                break;

            case POp::dot_3_floats:
                opArg1 = this->ptrCtx(stage.ctx, 1);
                std::tie(opArg2, opArg3) = this->adjacentPtrCtx(stage.ctx, 3);
                break;

            case POp::dot_4_floats:
                opArg1 = this->ptrCtx(stage.ctx, 1);
                std::tie(opArg2, opArg3) = this->adjacentPtrCtx(stage.ctx, 4);
                break;

            case POp::shuffle:
                std::tie(opArg1, opArg2) = this->shuffleCtx(stage.ctx);
                break;

            case POp::matrix_multiply_2:
            case POp::matrix_multiply_3:
            case POp::matrix_multiply_4:
                std::tie(opArg1, opArg2, opArg3) = this->matrixMultiply(stage.ctx);
                break;

            case POp::load_condition_mask:
            case POp::store_condition_mask:
            case POp::load_loop_mask:
            case POp::store_loop_mask:
            case POp::merge_loop_mask:
            case POp::reenable_loop_mask:
            case POp::load_return_mask:
            case POp::store_return_mask:
            case POp::continue_op:
            case POp::cast_to_float_from_int: case POp::cast_to_float_from_uint:
            case POp::cast_to_int_from_float: case POp::cast_to_uint_from_float:
            case POp::abs_int:
            case POp::acos_float:
            case POp::asin_float:
            case POp::atan_float:
            case POp::ceil_float:
            case POp::cos_float:
            case POp::exp_float:
            case POp::exp2_float:
            case POp::log_float:
            case POp::log2_float:
            case POp::floor_float:
            case POp::invsqrt_float:
            case POp::sin_float:
            case POp::sqrt_float:
            case POp::tan_float:
                opArg1 = this->ptrCtx(stage.ctx, 1);
                break;

            case POp::store_src_rg:
            case POp::cast_to_float_from_2_ints: case POp::cast_to_float_from_2_uints:
            case POp::cast_to_int_from_2_floats: case POp::cast_to_uint_from_2_floats:
            case POp::abs_2_ints:
            case POp::ceil_2_floats:
            case POp::floor_2_floats:
            case POp::invsqrt_2_floats:
                opArg1 = this->ptrCtx(stage.ctx, 2);
                break;

            case POp::cast_to_float_from_3_ints: case POp::cast_to_float_from_3_uints:
            case POp::cast_to_int_from_3_floats: case POp::cast_to_uint_from_3_floats:
            case POp::abs_3_ints:
            case POp::ceil_3_floats:
            case POp::floor_3_floats:
            case POp::invsqrt_3_floats:
                opArg1 = this->ptrCtx(stage.ctx, 3);
                break;

            case POp::load_src:
            case POp::load_dst:
            case POp::exchange_src:
            case POp::store_src:
            case POp::store_dst:
            case POp::store_device_xy01:
            case POp::invoke_to_linear_srgb:
            case POp::invoke_from_linear_srgb:
            case POp::cast_to_float_from_4_ints: case POp::cast_to_float_from_4_uints:
            case POp::cast_to_int_from_4_floats: case POp::cast_to_uint_from_4_floats:
            case POp::abs_4_ints:
            case POp::ceil_4_floats:
            case POp::floor_4_floats:
            case POp::invsqrt_4_floats:
            case POp::inverse_mat2:
                opArg1 = this->ptrCtx(stage.ctx, 4);
                break;

            case POp::inverse_mat3:
                opArg1 = this->ptrCtx(stage.ctx, 9);
                break;

            case POp::inverse_mat4:
                opArg1 = this->ptrCtx(stage.ctx, 16);
                break;

            case POp::copy_constant:
            case POp::add_imm_float:
            case POp::mul_imm_float:
            case POp::cmple_imm_float:
            case POp::cmplt_imm_float:
            case POp::cmpeq_imm_float:
            case POp::cmpne_imm_float:
            case POp::min_imm_float:
            case POp::max_imm_float:
                std::tie(opArg1, opArg2) = this->constantCtx(stage.ctx, 1);
                break;

            case POp::add_imm_int:
            case POp::mul_imm_int:
            case POp::bitwise_and_imm_int:
            case POp::bitwise_xor_imm_int:
            case POp::cmple_imm_int:
            case POp::cmple_imm_uint:
            case POp::cmplt_imm_int:
            case POp::cmplt_imm_uint:
            case POp::cmpeq_imm_int:
            case POp::cmpne_imm_int:
                std::tie(opArg1, opArg2) = this->constantCtx(stage.ctx, 1, /*showAsFloat=*/false);
                break;

            case POp::splat_2_constants:
            case POp::bitwise_and_imm_2_ints:
                std::tie(opArg1, opArg2) = this->constantCtx(stage.ctx, 2);
                break;

            case POp::splat_3_constants:
            case POp::bitwise_and_imm_3_ints:
                std::tie(opArg1, opArg2) = this->constantCtx(stage.ctx, 3);
                break;

            case POp::splat_4_constants:
            case POp::bitwise_and_imm_4_ints:
                std::tie(opArg1, opArg2) = this->constantCtx(stage.ctx, 4);
                break;

            case POp::copy_uniform:
                std::tie(opArg1, opArg2) = this->copyUniformCtx(stage.ctx, 1);
                break;

            case POp::copy_2_uniforms:
                std::tie(opArg1, opArg2) = this->copyUniformCtx(stage.ctx, 2);
                break;

            case POp::copy_3_uniforms:
                std::tie(opArg1, opArg2) = this->copyUniformCtx(stage.ctx, 3);
                break;

            case POp::copy_4_uniforms:
                std::tie(opArg1, opArg2) = this->copyUniformCtx(stage.ctx, 4);
                break;

            case POp::copy_slot_masked:
            case POp::copy_slot_unmasked:
            case POp::copy_immutable_unmasked:
                std::tie(opArg1, opArg2) = this->binaryOpCtx(stage.ctx, 1);
                break;

            case POp::copy_2_slots_masked:
            case POp::copy_2_slots_unmasked:
            case POp::copy_2_immutables_unmasked:
                std::tie(opArg1, opArg2) = this->binaryOpCtx(stage.ctx, 2);
                break;

            case POp::copy_3_slots_masked:
            case POp::copy_3_slots_unmasked:
            case POp::copy_3_immutables_unmasked:
                std::tie(opArg1, opArg2) = this->binaryOpCtx(stage.ctx, 3);
                break;

            case POp::copy_4_slots_masked:
            case POp::copy_4_slots_unmasked:
            case POp::copy_4_immutables_unmasked:
                std::tie(opArg1, opArg2) = this->binaryOpCtx(stage.ctx, 4);
                break;

            case POp::copy_from_indirect_uniform_unmasked:
            case POp::copy_from_indirect_unmasked:
            case POp::copy_to_indirect_masked: {
                const auto* ctx =
                        static_cast<SkRasterPipelineContexts::CopyIndirectCtx*>(stage.ctx);
                // We don't incorporate the indirect-limit in the output
                opArg1 = this->ptrCtx(ctx->dst, ctx->slots);
                opArg2 = this->ptrCtx(ctx->src, ctx->slots);
                opArg3 = this->ptrCtx(ctx->indirectOffset, 1);
                break;
            }
            case POp::swizzle_copy_to_indirect_masked: {
                const auto* ctx =
                        static_cast<SkRasterPipelineContexts::SwizzleCopyIndirectCtx*>(stage.ctx);
                opArg1 = this->ptrCtx(ctx->dst, this->swizzleWidth(SkSpan(ctx->offsets,
                                                                          ctx->slots)));
                opArg2 = this->ptrCtx(ctx->src, ctx->slots);
                opArg3 = this->ptrCtx(ctx->indirectOffset, 1);
                opSwizzle = this->swizzleOffsetSpan(SkSpan(ctx->offsets, ctx->slots));
                break;
            }
            case POp::merge_condition_mask:
            case POp::merge_inv_condition_mask:
            case POp::add_float:   case POp::add_int:
            case POp::sub_float:   case POp::sub_int:
            case POp::mul_float:   case POp::mul_int:
            case POp::div_float:   case POp::div_int:   case POp::div_uint:
                                   case POp::bitwise_and_int:
                                   case POp::bitwise_or_int:
                                   case POp::bitwise_xor_int:
            case POp::mod_float:
            case POp::min_float:   case POp::min_int:   case POp::min_uint:
            case POp::max_float:   case POp::max_int:   case POp::max_uint:
            case POp::cmplt_float: case POp::cmplt_int: case POp::cmplt_uint:
            case POp::cmple_float: case POp::cmple_int: case POp::cmple_uint:
            case POp::cmpeq_float: case POp::cmpeq_int:
            case POp::cmpne_float: case POp::cmpne_int:
                std::tie(opArg1, opArg2) = this->adjacentPtrCtx(stage.ctx, 1);
                break;

            case POp::mix_float:   case POp::mix_int:
                std::tie(opArg1, opArg2, opArg3) = this->adjacent3PtrCtx(stage.ctx, 1);
                break;

            case POp::add_2_floats:   case POp::add_2_ints:
            case POp::sub_2_floats:   case POp::sub_2_ints:
            case POp::mul_2_floats:   case POp::mul_2_ints:
            case POp::div_2_floats:   case POp::div_2_ints:   case POp::div_2_uints:
                                      case POp::bitwise_and_2_ints:
                                      case POp::bitwise_or_2_ints:
                                      case POp::bitwise_xor_2_ints:
            case POp::mod_2_floats:
            case POp::min_2_floats:   case POp::min_2_ints:   case POp::min_2_uints:
            case POp::max_2_floats:   case POp::max_2_ints:   case POp::max_2_uints:
            case POp::cmplt_2_floats: case POp::cmplt_2_ints: case POp::cmplt_2_uints:
            case POp::cmple_2_floats: case POp::cmple_2_ints: case POp::cmple_2_uints:
            case POp::cmpeq_2_floats: case POp::cmpeq_2_ints:
            case POp::cmpne_2_floats: case POp::cmpne_2_ints:
                std::tie(opArg1, opArg2) = this->adjacentPtrCtx(stage.ctx, 2);
                break;

            case POp::mix_2_floats:   case POp::mix_2_ints:
                std::tie(opArg1, opArg2, opArg3) = this->adjacent3PtrCtx(stage.ctx, 2);
                break;

            case POp::add_3_floats:   case POp::add_3_ints:
            case POp::sub_3_floats:   case POp::sub_3_ints:
            case POp::mul_3_floats:   case POp::mul_3_ints:
            case POp::div_3_floats:   case POp::div_3_ints:   case POp::div_3_uints:
                                      case POp::bitwise_and_3_ints:
                                      case POp::bitwise_or_3_ints:
                                      case POp::bitwise_xor_3_ints:
            case POp::mod_3_floats:
            case POp::min_3_floats:   case POp::min_3_ints:   case POp::min_3_uints:
            case POp::max_3_floats:   case POp::max_3_ints:   case POp::max_3_uints:
            case POp::cmplt_3_floats: case POp::cmplt_3_ints: case POp::cmplt_3_uints:
            case POp::cmple_3_floats: case POp::cmple_3_ints: case POp::cmple_3_uints:
            case POp::cmpeq_3_floats: case POp::cmpeq_3_ints:
            case POp::cmpne_3_floats: case POp::cmpne_3_ints:
                std::tie(opArg1, opArg2) = this->adjacentPtrCtx(stage.ctx, 3);
                break;

            case POp::mix_3_floats:   case POp::mix_3_ints:
                std::tie(opArg1, opArg2, opArg3) = this->adjacent3PtrCtx(stage.ctx, 3);
                break;

            case POp::add_4_floats:   case POp::add_4_ints:
            case POp::sub_4_floats:   case POp::sub_4_ints:
            case POp::mul_4_floats:   case POp::mul_4_ints:
            case POp::div_4_floats:   case POp::div_4_ints:   case POp::div_4_uints:
                                      case POp::bitwise_and_4_ints:
                                      case POp::bitwise_or_4_ints:
                                      case POp::bitwise_xor_4_ints:
            case POp::mod_4_floats:
            case POp::min_4_floats:   case POp::min_4_ints:   case POp::min_4_uints:
            case POp::max_4_floats:   case POp::max_4_ints:   case POp::max_4_uints:
            case POp::cmplt_4_floats: case POp::cmplt_4_ints: case POp::cmplt_4_uints:
            case POp::cmple_4_floats: case POp::cmple_4_ints: case POp::cmple_4_uints:
            case POp::cmpeq_4_floats: case POp::cmpeq_4_ints:
            case POp::cmpne_4_floats: case POp::cmpne_4_ints:
                std::tie(opArg1, opArg2) = this->adjacentPtrCtx(stage.ctx, 4);
                break;

            case POp::mix_4_floats:   case POp::mix_4_ints:
                std::tie(opArg1, opArg2, opArg3) = this->adjacent3PtrCtx(stage.ctx, 4);
                break;

            case POp::add_n_floats:   case POp::add_n_ints:
            case POp::sub_n_floats:   case POp::sub_n_ints:
            case POp::mul_n_floats:   case POp::mul_n_ints:
            case POp::div_n_floats:   case POp::div_n_ints:   case POp::div_n_uints:
                                      case POp::bitwise_and_n_ints:
                                      case POp::bitwise_or_n_ints:
                                      case POp::bitwise_xor_n_ints:
            case POp::mod_n_floats:
            case POp::min_n_floats:   case POp::min_n_ints:   case POp::min_n_uints:
            case POp::max_n_floats:   case POp::max_n_ints:   case POp::max_n_uints:
            case POp::cmplt_n_floats: case POp::cmplt_n_ints: case POp::cmplt_n_uints:
            case POp::cmple_n_floats: case POp::cmple_n_ints: case POp::cmple_n_uints:
            case POp::cmpeq_n_floats: case POp::cmpeq_n_ints:
            case POp::cmpne_n_floats: case POp::cmpne_n_ints:
            case POp::atan2_n_floats:
            case POp::pow_n_floats:
                std::tie(opArg1, opArg2) = this->adjacentBinaryOpCtx(stage.ctx);
                break;

            case POp::mix_n_floats:        case POp::mix_n_ints:
            case POp::smoothstep_n_floats:
                std::tie(opArg1, opArg2, opArg3) = this->adjacentTernaryOpCtx(stage.ctx);
                break;

            case POp::jump:
            case POp::branch_if_all_lanes_active:
            case POp::branch_if_any_lanes_active:
            case POp::branch_if_no_lanes_active:
                opArg1 = this->branchOffset(
                        static_cast<SkRasterPipelineContexts::BranchCtx*>(stage.ctx), index);
                break;

            case POp::branch_if_no_active_lanes_eq: {
                const auto* ctx =
                        static_cast<SkRasterPipelineContexts::BranchIfEqualCtx*>(stage.ctx);
                opArg1 = this->branchOffset(ctx, index);
                opArg2 = this->ptrCtx(ctx->ptr, 1);
                opArg3 = this->imm(sk_bit_cast<float>(ctx->value));
                break;
            }
            case POp::trace_var: {
                const auto* ctx = static_cast<SkRasterPipelineContexts::TraceVarCtx*>(stage.ctx);
                opArg1 = this->ptrCtx(ctx->traceMask, 1);
                opArg2 = this->ptrCtx(ctx->data, ctx->numSlots);
                if (ctx->indirectOffset != nullptr) {
                    opArg3 = " + " + this->ptrCtx(ctx->indirectOffset, 1);
                }
                break;
            }
            case POp::trace_line: {
                const auto* ctx = static_cast<SkRasterPipelineContexts::TraceLineCtx*>(stage.ctx);
                opArg1 = this->ptrCtx(ctx->traceMask, 1);
                opArg2 = std::to_string(ctx->lineNumber);
                break;
            }
            case POp::trace_enter:
            case POp::trace_exit: {
                const auto* ctx = static_cast<SkRasterPipelineContexts::TraceFuncCtx*>(stage.ctx);
                opArg1 = this->ptrCtx(ctx->traceMask, 1);
                opArg2 = (fProgram.fDebugTrace &&
                          ctx->funcIdx >= 0 &&
                          ctx->funcIdx < (int)fProgram.fDebugTrace->fFuncInfo.size())
                                 ? fProgram.fDebugTrace->fFuncInfo[ctx->funcIdx].name
                                 : "???";
                break;
            }
            case POp::trace_scope: {
                const auto* ctx = static_cast<SkRasterPipelineContexts::TraceScopeCtx*>(stage.ctx);
                opArg1 = this->ptrCtx(ctx->traceMask, 1);
                opArg2 = SkSL::String::printf("%+d", ctx->delta);
                break;
            }
            default:
                break;
        }

        std::string_view opName;
        switch (stage.op) {
        #define M(x) case POp::x: opName = #x; break;
            SK_RASTER_PIPELINE_OPS_ALL(M)
            SKRP_EXTENDED_OPS(M)
        #undef M
        }

        std::string opText;
        switch (stage.op) {
            case POp::trace_var:
                opText = "TraceVar(" + opArg2 + opArg3 + ") when " + opArg1 + " is true";
                break;

            case POp::trace_line:
                opText = "TraceLine(" + opArg2 + ") when " + opArg1 + " is true";
                break;

            case POp::trace_enter:
                opText = "TraceEnter(" + opArg2 + ") when " + opArg1 + " is true";
                break;

            case POp::trace_exit:
                opText = "TraceExit(" + opArg2 + ") when " + opArg1 + " is true";
                break;

            case POp::trace_scope:
                opText = "TraceScope(" + opArg2 + ") when " + opArg1 + " is true";
                break;

            case POp::init_lane_masks:
                opText = "CondMask = LoopMask = RetMask = true";
                break;

            case POp::load_condition_mask:
                opText = "CondMask = " + opArg1;
                break;

            case POp::store_condition_mask:
                opText = opArg1 + " = CondMask";
                break;

            case POp::merge_condition_mask:
                opText = "CondMask = " + opArg1 + " & " + opArg2;
                break;

            case POp::merge_inv_condition_mask:
                opText = "CondMask = " + opArg1 + " & ~" + opArg2;
                break;

            case POp::load_loop_mask:
                opText = "LoopMask = " + opArg1;
                break;

            case POp::store_loop_mask:
                opText = opArg1 + " = LoopMask";
                break;

            case POp::mask_off_loop_mask:
                opText = "LoopMask &= ~(CondMask & LoopMask & RetMask)";
                break;

            case POp::reenable_loop_mask:
                opText = "LoopMask |= " + opArg1;
                break;

            case POp::merge_loop_mask:
                opText = "LoopMask &= " + opArg1;
                break;

            case POp::load_return_mask:
                opText = "RetMask = " + opArg1;
                break;

            case POp::store_return_mask:
                opText = opArg1 + " = RetMask";
                break;

            case POp::mask_off_return_mask:
                opText = "RetMask &= ~(CondMask & LoopMask & RetMask)";
                break;

            case POp::store_src_rg:
                opText = opArg1 + " = src.rg";
                break;

            case POp::exchange_src:
                opText = "swap(src.rgba, " + opArg1 + ")";
                break;

            case POp::store_src:
                opText = opArg1 + " = src.rgba";
                break;

            case POp::store_dst:
                opText = opArg1 + " = dst.rgba";
                break;

            case POp::store_device_xy01:
                opText = opArg1 + " = DeviceCoords.xy01";
                break;

            case POp::load_src:
                opText = "src.rgba = " + opArg1;
                break;

            case POp::load_dst:
                opText = "dst.rgba = " + opArg1;
                break;

            case POp::bitwise_and_int:
            case POp::bitwise_and_2_ints:
            case POp::bitwise_and_3_ints:
            case POp::bitwise_and_4_ints:
            case POp::bitwise_and_n_ints:
            case POp::bitwise_and_imm_int:
            case POp::bitwise_and_imm_2_ints:
            case POp::bitwise_and_imm_3_ints:
            case POp::bitwise_and_imm_4_ints:
                opText = opArg1 + " &= " + opArg2;
                break;

            case POp::bitwise_or_int:
            case POp::bitwise_or_2_ints:
            case POp::bitwise_or_3_ints:
            case POp::bitwise_or_4_ints:
            case POp::bitwise_or_n_ints:
                opText = opArg1 + " |= " + opArg2;
                break;

            case POp::bitwise_xor_int:
            case POp::bitwise_xor_2_ints:
            case POp::bitwise_xor_3_ints:
            case POp::bitwise_xor_4_ints:
            case POp::bitwise_xor_n_ints:
            case POp::bitwise_xor_imm_int:
                opText = opArg1 + " ^= " + opArg2;
                break;

            case POp::cast_to_float_from_int:
            case POp::cast_to_float_from_2_ints:
            case POp::cast_to_float_from_3_ints:
            case POp::cast_to_float_from_4_ints:
                opText = opArg1 + " = IntToFloat(" + opArg1 + ")";
                break;

            case POp::cast_to_float_from_uint:
            case POp::cast_to_float_from_2_uints:
            case POp::cast_to_float_from_3_uints:
            case POp::cast_to_float_from_4_uints:
                opText = opArg1 + " = UintToFloat(" + opArg1 + ")";
                break;

            case POp::cast_to_int_from_float:
            case POp::cast_to_int_from_2_floats:
            case POp::cast_to_int_from_3_floats:
            case POp::cast_to_int_from_4_floats:
                opText = opArg1 + " = FloatToInt(" + opArg1 + ")";
                break;

            case POp::cast_to_uint_from_float:
            case POp::cast_to_uint_from_2_floats:
            case POp::cast_to_uint_from_3_floats:
            case POp::cast_to_uint_from_4_floats:
                opText = opArg1 + " = FloatToUint(" + opArg1 + ")";
                break;

            case POp::copy_slot_masked:            case POp::copy_2_slots_masked:
            case POp::copy_3_slots_masked:         case POp::copy_4_slots_masked:
            case POp::swizzle_copy_slot_masked:    case POp::swizzle_copy_2_slots_masked:
            case POp::swizzle_copy_3_slots_masked: case POp::swizzle_copy_4_slots_masked:
                opText = opArg1 + " = Mask(" + opArg2 + ")";
                break;

            case POp::copy_uniform:                case POp::copy_2_uniforms:
            case POp::copy_3_uniforms:             case POp::copy_4_uniforms:
            case POp::copy_slot_unmasked:          case POp::copy_2_slots_unmasked:
            case POp::copy_3_slots_unmasked:       case POp::copy_4_slots_unmasked:
            case POp::copy_immutable_unmasked:     case POp::copy_2_immutables_unmasked:
            case POp::copy_3_immutables_unmasked:  case POp::copy_4_immutables_unmasked:
            case POp::copy_constant:               case POp::splat_2_constants:
            case POp::splat_3_constants:           case POp::splat_4_constants:
            case POp::swizzle_1:                   case POp::swizzle_2:
            case POp::swizzle_3:                   case POp::swizzle_4:
            case POp::shuffle:
                opText = opArg1 + " = " + opArg2;
                break;

            case POp::copy_from_indirect_unmasked:
            case POp::copy_from_indirect_uniform_unmasked:
                opText = opArg1 + " = Indirect(" + opArg2 + " + " + opArg3 + ")";
                break;

            case POp::copy_to_indirect_masked:
                opText = "Indirect(" + opArg1 + " + " + opArg3 + ") = Mask(" + opArg2 + ")";
                break;

            case POp::swizzle_copy_to_indirect_masked:
                opText = "Indirect(" + opArg1 + " + " + opArg3 + ")." + opSwizzle + " = Mask(" +
                         opArg2 + ")";
                break;

            case POp::abs_int:
            case POp::abs_2_ints:
            case POp::abs_3_ints:
            case POp::abs_4_ints:
                opText = opArg1 + " = abs(" + opArg1 + ")";
                break;

            case POp::acos_float:
                opText = opArg1 + " = acos(" + opArg1 + ")";
                break;

            case POp::asin_float:
                opText = opArg1 + " = asin(" + opArg1 + ")";
                break;

            case POp::atan_float:
                opText = opArg1 + " = atan(" + opArg1 + ")";
                break;

            case POp::atan2_n_floats:
                opText = opArg1 + " = atan2(" + opArg1 + ", " + opArg2 + ")";
                break;

            case POp::ceil_float:
            case POp::ceil_2_floats:
            case POp::ceil_3_floats:
            case POp::ceil_4_floats:
                opText = opArg1 + " = ceil(" + opArg1 + ")";
                break;

            case POp::cos_float:
                opText = opArg1 + " = cos(" + opArg1 + ")";
                break;

            case POp::refract_4_floats:
                opText = opArg1 + " = refract(" + opArg1 + ", " + opArg2 + ", " + opArg3 + ")";
                break;

            case POp::dot_2_floats:
            case POp::dot_3_floats:
            case POp::dot_4_floats:
                opText = opArg1 + " = dot(" + opArg2 + ", " + opArg3 + ")";
                break;

            case POp::exp_float:
                opText = opArg1 + " = exp(" + opArg1 + ")";
                break;

            case POp::exp2_float:
                opText = opArg1 + " = exp2(" + opArg1 + ")";
                break;

            case POp::log_float:
                opText = opArg1 + " = log(" + opArg1 + ")";
                break;

            case POp::log2_float:
                opText = opArg1 + " = log2(" + opArg1 + ")";
                break;

            case POp::pow_n_floats:
                opText = opArg1 + " = pow(" + opArg1 + ", " + opArg2 + ")";
                break;

            case POp::sin_float:
                opText = opArg1 + " = sin(" + opArg1 + ")";
                break;

            case POp::sqrt_float:
                opText = opArg1 + " = sqrt(" + opArg1 + ")";
                break;

            case POp::tan_float:
                opText = opArg1 + " = tan(" + opArg1 + ")";
                break;

            case POp::floor_float:
            case POp::floor_2_floats:
            case POp::floor_3_floats:
            case POp::floor_4_floats:
                opText = opArg1 + " = floor(" + opArg1 + ")";
                break;

            case POp::invsqrt_float:
            case POp::invsqrt_2_floats:
            case POp::invsqrt_3_floats:
            case POp::invsqrt_4_floats:
                opText = opArg1 + " = inversesqrt(" + opArg1 + ")";
                break;

            case POp::inverse_mat2:
            case POp::inverse_mat3:
            case POp::inverse_mat4:
                opText = opArg1 + " = inverse(" + opArg1 + ")";
                break;

            case POp::add_float:     case POp::add_int:
            case POp::add_2_floats:  case POp::add_2_ints:
            case POp::add_3_floats:  case POp::add_3_ints:
            case POp::add_4_floats:  case POp::add_4_ints:
            case POp::add_n_floats:  case POp::add_n_ints:
            case POp::add_imm_float: case POp::add_imm_int:
                opText = opArg1 + " += " + opArg2;
                break;

            case POp::sub_float:    case POp::sub_int:
            case POp::sub_2_floats: case POp::sub_2_ints:
            case POp::sub_3_floats: case POp::sub_3_ints:
            case POp::sub_4_floats: case POp::sub_4_ints:
            case POp::sub_n_floats: case POp::sub_n_ints:
                opText = opArg1 + " -= " + opArg2;
                break;

            case POp::mul_float:     case POp::mul_int:
            case POp::mul_2_floats:  case POp::mul_2_ints:
            case POp::mul_3_floats:  case POp::mul_3_ints:
            case POp::mul_4_floats:  case POp::mul_4_ints:
            case POp::mul_n_floats:  case POp::mul_n_ints:
            case POp::mul_imm_float: case POp::mul_imm_int:
                opText = opArg1 + " *= " + opArg2;
                break;

            case POp::div_float:    case POp::div_int:    case POp::div_uint:
            case POp::div_2_floats: case POp::div_2_ints: case POp::div_2_uints:
            case POp::div_3_floats: case POp::div_3_ints: case POp::div_3_uints:
            case POp::div_4_floats: case POp::div_4_ints: case POp::div_4_uints:
            case POp::div_n_floats: case POp::div_n_ints: case POp::div_n_uints:
                opText = opArg1 + " /= " + opArg2;
                break;

            case POp::matrix_multiply_2:
            case POp::matrix_multiply_3:
            case POp::matrix_multiply_4:
                opText = opArg1 + " = " + opArg2 + " * " + opArg3;
                break;

            case POp::mod_float:
            case POp::mod_2_floats:
            case POp::mod_3_floats:
            case POp::mod_4_floats:
            case POp::mod_n_floats:
                opText = opArg1 + " = mod(" + opArg1 + ", " + opArg2 + ")";
                break;

            case POp::min_float:        case POp::min_int:          case POp::min_uint:
            case POp::min_2_floats:     case POp::min_2_ints:       case POp::min_2_uints:
            case POp::min_3_floats:     case POp::min_3_ints:       case POp::min_3_uints:
            case POp::min_4_floats:     case POp::min_4_ints:       case POp::min_4_uints:
            case POp::min_n_floats:     case POp::min_n_ints:       case POp::min_n_uints:
            case POp::min_imm_float:
                opText = opArg1 + " = min(" + opArg1 + ", " + opArg2 + ")";
                break;

            case POp::max_float:        case POp::max_int:          case POp::max_uint:
            case POp::max_2_floats:     case POp::max_2_ints:       case POp::max_2_uints:
            case POp::max_3_floats:     case POp::max_3_ints:       case POp::max_3_uints:
            case POp::max_4_floats:     case POp::max_4_ints:       case POp::max_4_uints:
            case POp::max_n_floats:     case POp::max_n_ints:       case POp::max_n_uints:
            case POp::max_imm_float:
                opText = opArg1 + " = max(" + opArg1 + ", " + opArg2 + ")";
                break;

            case POp::cmplt_float:     case POp::cmplt_int:     case POp::cmplt_uint:
            case POp::cmplt_2_floats:  case POp::cmplt_2_ints:  case POp::cmplt_2_uints:
            case POp::cmplt_3_floats:  case POp::cmplt_3_ints:  case POp::cmplt_3_uints:
            case POp::cmplt_4_floats:  case POp::cmplt_4_ints:  case POp::cmplt_4_uints:
            case POp::cmplt_n_floats:  case POp::cmplt_n_ints:  case POp::cmplt_n_uints:
            case POp::cmplt_imm_float: case POp::cmplt_imm_int: case POp::cmplt_imm_uint:
                opText = opArg1 + " = lessThan(" + opArg1 + ", " + opArg2 + ")";
                break;

            case POp::cmple_float:     case POp::cmple_int:     case POp::cmple_uint:
            case POp::cmple_2_floats:  case POp::cmple_2_ints:  case POp::cmple_2_uints:
            case POp::cmple_3_floats:  case POp::cmple_3_ints:  case POp::cmple_3_uints:
            case POp::cmple_4_floats:  case POp::cmple_4_ints:  case POp::cmple_4_uints:
            case POp::cmple_n_floats:  case POp::cmple_n_ints:  case POp::cmple_n_uints:
            case POp::cmple_imm_float: case POp::cmple_imm_int: case POp::cmple_imm_uint:
                opText = opArg1 + " = lessThanEqual(" + opArg1 + ", " + opArg2 + ")";
                break;

            case POp::cmpeq_float:     case POp::cmpeq_int:
            case POp::cmpeq_2_floats:  case POp::cmpeq_2_ints:
            case POp::cmpeq_3_floats:  case POp::cmpeq_3_ints:
            case POp::cmpeq_4_floats:  case POp::cmpeq_4_ints:
            case POp::cmpeq_n_floats:  case POp::cmpeq_n_ints:
            case POp::cmpeq_imm_float: case POp::cmpeq_imm_int:
                opText = opArg1 + " = equal(" + opArg1 + ", " + opArg2 + ")";
                break;

            case POp::cmpne_float:     case POp::cmpne_int:
            case POp::cmpne_2_floats:  case POp::cmpne_2_ints:
            case POp::cmpne_3_floats:  case POp::cmpne_3_ints:
            case POp::cmpne_4_floats:  case POp::cmpne_4_ints:
            case POp::cmpne_n_floats:  case POp::cmpne_n_ints:
            case POp::cmpne_imm_float: case POp::cmpne_imm_int:
                opText = opArg1 + " = notEqual(" + opArg1 + ", " + opArg2 + ")";
                break;

            case POp::mix_float:      case POp::mix_int:
            case POp::mix_2_floats:   case POp::mix_2_ints:
            case POp::mix_3_floats:   case POp::mix_3_ints:
            case POp::mix_4_floats:   case POp::mix_4_ints:
            case POp::mix_n_floats:   case POp::mix_n_ints:
                opText = opArg1 + " = mix(" + opArg2 + ", " + opArg3 + ", " + opArg1 + ")";
                break;

            case POp::smoothstep_n_floats:
                opText = opArg1 + " = smoothstep(" + opArg1 + ", " + opArg2 + ", " + opArg3 + ")";
                break;

            case POp::jump:
            case POp::branch_if_all_lanes_active:
            case POp::branch_if_any_lanes_active:
            case POp::branch_if_no_lanes_active:
            case POp::invoke_shader:
            case POp::invoke_color_filter:
            case POp::invoke_blender:
                opText = std::string(opName) + " " + opArg1;
                break;

            case POp::invoke_to_linear_srgb:
                opText = opArg1 + " = toLinearSrgb(" + opArg1 + ")";
                break;

            case POp::invoke_from_linear_srgb:
                opText = opArg1 + " = fromLinearSrgb(" + opArg1 + ")";
                break;

            case POp::branch_if_no_active_lanes_eq:
                opText = "branch " + opArg1 + " if no lanes of " + opArg2 + " == " + opArg3;
                break;

            case POp::label:
                opText = "label " + opArg1;
                break;

            case POp::case_op:
                opText = "if (" + opArg1 + " == " + opArg3 +
                         ") { LoopMask = true; " + opArg2 + " = false; }";
                break;

            case POp::continue_op:
                opText = opArg1 +
                         " |= Mask(0xFFFFFFFF); LoopMask &= ~(CondMask & LoopMask & RetMask)";
                break;

            default:
                break;
        }

        opName = opName.substr(0, 30);
        if (!opText.empty()) {
            out->writeText(SkSL::String::printf("%-30.*s %s\n",
                                                (int)opName.size(), opName.data(),
                                                opText.c_str()).c_str());
        } else {
            out->writeText(SkSL::String::printf("%.*s\n",
                                                (int)opName.size(), opName.data()).c_str());
        }
    }
}