diff --git a/BUILD.gn b/BUILD.gn index 26717c2d7c..7be33e8e3f 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -656,21 +656,17 @@ if (skia_compile_sksl_tests) { "src/utils/SkParse.cpp", "src/utils/SkShaderUtils.cpp", "src/utils/SkUTF.cpp", - "src/utils/SkVMVisualizer.cpp", ] - libs = [] if (is_win) { sources += [ "src/ports/SkOSFile_win.cpp" ] } else { sources += [ "src/ports/SkOSFile_posix.cpp" ] - libs += [ "dl" ] } sources += skia_sksl_sources sources += skia_sksl_gpu_sources include_dirs = [ "." ] deps = [ ":run_sksllex", - ":skvm_jit", "//third_party/externals/spirv-tools:spvtools", "//third_party/externals/spirv-tools:spvtools_val", "//third_party/spirv-cross:spirv_cross", diff --git a/gn/utils.gni b/gn/utils.gni index 96fd884d09..046ef9143d 100644 --- a/gn/utils.gni +++ b/gn/utils.gni @@ -80,8 +80,6 @@ skia_utils_sources = [ "$_src/utils/SkThreadUtils_win.cpp", "$_src/utils/SkUTF.cpp", "$_src/utils/SkUTF.h", - "$_src/utils/SkVMVisualizer.cpp", - "$_src/utils/SkVMVisualizer.h", #mac "$_src/utils/mac/SkCGBase.h", diff --git a/src/core/SkVM.cpp b/src/core/SkVM.cpp index d6e4ae411c..e9dfcd2ad7 100644 --- a/src/core/SkVM.cpp +++ b/src/core/SkVM.cpp @@ -17,7 +17,6 @@ #include "src/core/SkOpts.h" #include "src/core/SkStreamPriv.h" #include "src/core/SkVM.h" -#include "src/utils/SkVMVisualizer.h" #include #include #include @@ -160,13 +159,13 @@ namespace skvm { Builder::Builder(Features features, bool createDuplicates) : fFeatures(features ), fCreateDuplicates(createDuplicates) {} + struct Program::Impl { std::vector instructions; int regs = 0; int loop = 0; std::vector strides; std::vector traceHooks; - std::unique_ptr visualizer; std::atomic jit_entry{nullptr}; // TODO: minimal std::memory_orders size_t jit_size = 0; @@ -370,13 +369,6 @@ namespace skvm { } } - void Program::visualize(SkWStream* output, const char* code) const { - if (fImpl->visualizer) { - fImpl->visualizer->dump(output, code); - } - } - - viz::Visualizer* Program::visualizer() { return fImpl->visualizer.get(); } void Program::dump(SkWStream* o) const { SkDebugfStream debug; if (!o) { o = &debug; } @@ -486,8 +478,8 @@ namespace skvm { write(o, "\n"); } } - std::vector eliminate_dead_code(std::vector program, - viz::Visualizer* visualizer) { + + std::vector eliminate_dead_code(std::vector program) { // Determine which Instructions are live by working back from side effects. std::vector live(program.size(), false); for (Val id = program.size(); id--;) { @@ -517,11 +509,6 @@ namespace skvm { } } - if (visualizer) { - visualizer->addInstructions(program); - visualizer->markAsDeadCode(live, new_id); - } - // Eliminate any non-live ops. auto it = std::remove_if(program.begin(), program.end(), [&](const Instruction& inst) { Val id = (Val)(&inst - program.data()); @@ -532,8 +519,7 @@ namespace skvm { return program; } - std::vector finalize(const std::vector program, - viz::Visualizer* visualizer) { + std::vector finalize(const std::vector program) { std::vector optimized(program.size()); for (Val id = 0; id < (Val)program.size(); id++) { Instruction inst = program[id]; @@ -577,38 +563,23 @@ namespace skvm { } } - if (visualizer) { - visualizer->finalize(program, optimized); - } - return optimized; } - std::vector Builder::optimize(viz::Visualizer* visualizer) const { + std::vector Builder::optimize() const { std::vector program = this->program(); - program = eliminate_dead_code(std::move(program), visualizer); - return finalize (std::move(program), visualizer); + program = eliminate_dead_code(std::move(program)); + return finalize (std::move(program)); } - Program Builder::done(const char* debug_name, - bool allow_jit) const { - return this->done(debug_name, allow_jit, /*visualizer=*/nullptr); - } - - Program Builder::done(const char* debug_name, - bool allow_jit, - std::unique_ptr visualizer) const { + Program Builder::done(const char* debug_name, bool allow_jit) const { char buf[64] = "skvm-jit-"; if (!debug_name) { *SkStrAppendU32(buf+9, this->hash()) = '\0'; debug_name = buf; } - auto optimized = this->optimize(visualizer ? visualizer.get() : nullptr); - return {optimized, - std::move(visualizer), - fStrides, - fTraceHooks, debug_name, allow_jit}; + return {this->optimize(), fStrides, fTraceHooks, debug_name, allow_jit}; } uint64_t Builder::hash() const { @@ -3109,11 +3080,9 @@ namespace skvm { } Program::Program(const std::vector& instructions, - std::unique_ptr visualizer, const std::vector& strides, const std::vector& traceHooks, const char* debug_name, bool allow_jit) : Program() { - fImpl->visualizer = std::move(visualizer); fImpl->strides = strides; fImpl->traceHooks = traceHooks; if (gSkVMAllowJIT && allow_jit) { @@ -4303,17 +4272,9 @@ namespace skvm { enter(); for (Val id = 0; id < (Val)instructions.size(); id++) { - if (fImpl->visualizer && is_trace(instructions[id].op)) { - // Make sure trace commands stay on JIT for visualizer - continue; - } - auto start = a->size(); if (instructions[id].can_hoist && !emit(id, /*scalar=*/false)) { return false; } - if (fImpl->visualizer && instructions[id].can_hoist) { - fImpl->visualizer->addMachineCommands(id, start, a->size()); - } } // This point marks a kind of canonical fixed point for register contents: if loop @@ -4339,17 +4300,9 @@ namespace skvm { a->cmp(N, K); jump_if_less(&tail); for (Val id = 0; id < (Val)instructions.size(); id++) { - if (fImpl->visualizer != nullptr && is_trace(instructions[id].op)) { - // Make sure trace commands stay on JIT for visualizer - continue; - } - auto start = a->size(); if (!instructions[id].can_hoist && !emit(id, /*scalar=*/false)) { return false; } - if (fImpl->visualizer && !instructions[id].can_hoist) { - fImpl->visualizer->addMachineCommands(id, start, a->size()); - } } restore_incoming_regs(); for (int i = 0; i < (int)fImpl->strides.size(); i++) { @@ -4366,10 +4319,6 @@ namespace skvm { a->cmp(N, 1); jump_if_less(&done); for (Val id = 0; id < (Val)instructions.size(); id++) { - if (fImpl->visualizer && is_trace(instructions[id].op)) { - // Make sure trace commands stay on JIT for visualizer - continue; - } if (!instructions[id].can_hoist && !emit(id, /*scalar=*/true)) { return false; } diff --git a/src/core/SkVM.h b/src/core/SkVM.h index a6cd884de9..847b04651f 100644 --- a/src/core/SkVM.h +++ b/src/core/SkVM.h @@ -42,10 +42,6 @@ class SkWStream; namespace skvm { - namespace viz { - class Visualizer; - } - class Assembler { public: explicit Assembler(void* buf); @@ -622,18 +618,15 @@ namespace skvm { class Builder { public: + Builder(bool createDuplicates = false); Builder(Features, bool createDuplicates = false); - Program done(const char* debug_name, - bool allow_jit, - std::unique_ptr visualizer) const; - Program done(const char* debug_name = nullptr, - bool allow_jit=true) const; + Program done(const char* debug_name = nullptr, bool allow_jit=true) const; // Mostly for debugging, tests, etc. std::vector program() const { return fProgram; } - std::vector optimize(viz::Visualizer* visualizer = nullptr) const; + std::vector optimize() const; // Returns a trace-hook ID which must be passed to the trace opcodes. int attachTraceHook(TraceHook*); @@ -1024,10 +1017,8 @@ namespace skvm { // Optimization passes and data structures normally used by Builder::optimize(), // extracted here so they can be unit tested. - std::vector eliminate_dead_code(std::vector, - viz::Visualizer* visualizer = nullptr); - std::vector finalize(std::vector, - viz::Visualizer* visualizer = nullptr); + std::vector eliminate_dead_code(std::vector); + std::vector finalize (std::vector); using Reg = int; @@ -1041,7 +1032,6 @@ namespace skvm { class Program { public: Program(const std::vector& instructions, - std::unique_ptr visualizer, const std::vector& strides, const std::vector& traceHooks, const char* debug_name, bool allow_jit); @@ -1074,10 +1064,8 @@ namespace skvm { bool hasJIT() const; // Has this Program been JITted? bool hasTraceHooks() const; // Is this program instrumented for debugging? - void visualize(SkWStream* output, const char* code) const; void dump(SkWStream* = nullptr) const; void disassemble(SkWStream* = nullptr) const; - viz::Visualizer* visualizer(); private: void setupInterpreter(const std::vector&); diff --git a/src/sksl/SkSLMain.cpp b/src/sksl/SkSLMain.cpp index e2615e3962..89ee231b4d 100644 --- a/src/sksl/SkSLMain.cpp +++ b/src/sksl/SkSLMain.cpp @@ -6,12 +6,11 @@ */ #define SK_OPTS_NS skslc_standalone -#include "include/core/SkGraphics.h" -#include "include/core/SkStream.h" -#include "src/core/SkCpu.h" #include "src/core/SkOpts.h" #include "src/opts/SkChecksum_opts.h" #include "src/opts/SkVM_opts.h" + +#include "include/core/SkStream.h" #include "src/sksl/SkSLCompiler.h" #include "src/sksl/SkSLDehydrator.h" #include "src/sksl/SkSLFileOutputStream.h" @@ -23,7 +22,6 @@ #include "src/sksl/ir/SkSLVarDeclarations.h" #include "src/sksl/tracing/SkVMDebugTrace.h" #include "src/utils/SkShaderUtils.h" -#include "src/utils/SkVMVisualizer.h" #include "spirv-tools/libspirv.hpp" @@ -32,8 +30,6 @@ #include #include -extern bool gSkVMAllowJIT; - void SkDebugf(const char format[], ...) { va_list args; va_start(args, format); @@ -292,7 +288,7 @@ ResultCode processCommand(std::vector& args) { kind = SkSL::ProgramKind::kRuntimeShader; } else { printf("input filename must end in '.vert', '.frag', '.rtb', '.rtcf', " - "'.rts' or '.sksl'\n"); + "'.rts', or '.sksl'\n"); return ResultCode::kInputError; } @@ -322,7 +318,6 @@ ResultCode processCommand(std::vector& args) { settings.fRTFlipBinding = 0; const SkSL::String& outputPath = args[2]; - auto emitCompileError = [&](SkSL::FileOutputStream& out, const char* errorText) { // Overwrite the compiler output, if any, with an error message. out.close(); @@ -515,41 +510,9 @@ ResultCode processCommand(std::vector& args) { printf("error writing '%s'\n", outputPath.c_str()); return ResultCode::kOutputError; } - } else if (outputPath.ends_with(".html")) { - settings.fAllowTraceVarInSkVMDebugTrace = false; - - SkCpu::CacheRuntimeFeatures(); - gSkVMAllowJIT = true; - return compileProgramForSkVM( - [&](SkSL::Compiler&, SkSL::Program& program, SkSL::OutputStream& out) { - if (!debugTrace) { - debugTrace = std::make_unique(); - debugTrace->setSource(text.c_str()); - } - auto visualizer = std::make_unique(debugTrace.get()); - skvm::Builder builder(skvm::Features{}, /*createDuplicates=*/true); - if (!SkSL::testingOnly_ProgramToSkVMShader(program, &builder, debugTrace.get())) { - return false; - } - - std::unique_ptr redirect = as_SkWStream(out); - skvm::Program p = builder.done( - /*debug_name=*/nullptr, /*allow_jit=*/true, std::move(visualizer)); -#if defined(SKVM_JIT) - SkDynamicMemoryWStream asmFile; - p.disassemble(&asmFile); - auto dumpData = asmFile.detachAsData(); - std::string dumpString(static_cast(dumpData->data()),dumpData->size()); - p.visualize(redirect.get(), dumpString.c_str()); -#else - p.visualize(redirect.get(), nullptr); -#endif - return true; - }); } else { - printf("expected output path to end with one of: .glsl, .html, .metal, .hlsl, .spirv, " - ".asm.frag, .skvm, .stage, .asm.vert, .dehydrated.sksl (got '%s')\n", - outputPath.c_str()); + printf("expected output path to end with one of: .glsl, .metal, .hlsl, .spirv, .asm.frag, " + ".skvm, .stage, .asm.vert, .dehydrated.sksl (got '%s')\n", outputPath.c_str()); return ResultCode::kConfigurationError; } return ResultCode::kSuccess; diff --git a/src/utils/SkVMVisualizer.cpp b/src/utils/SkVMVisualizer.cpp deleted file mode 100644 index 4152eb7abb..0000000000 --- a/src/utils/SkVMVisualizer.cpp +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#include "src/utils/SkVMVisualizer.h" -#include -#include "src/core/SkStreamPriv.h" - -namespace { - -size_t get_addr(const char* str) { - size_t addr; - std::istringstream ss(str); - ss >> std::hex >> addr; - SkASSERT(!ss.fail()); - return addr; -} - -} - -namespace skvm::viz { - -bool Instruction::operator == (const Instruction& o) const { - return this->kind == o.kind && - this->startCode == o.startCode && - this->endCode == o.endCode && - this->instructionIndex == o.instructionIndex && - this->instruction == o.instruction && - this->duplicates == o.duplicates; -} - -SkString Instruction::classes() const { - SkString result((kind & InstructionFlags::kDead) ? "dead" : "normal"); - if (duplicates > 0) result += " origin"; - if (duplicates < 0) result += " deduped"; - return result; -} - -uint32_t InstructionHash::operator()(const Instruction& i) const { - uint32_t hash = 0; - hash = SkOpts::hash_fn(&i.kind, sizeof(i.kind), hash); - hash = SkOpts::hash_fn(&i.instructionIndex, sizeof(i.instructionIndex), hash); - hash = SkOpts::hash_fn(&i.instruction, sizeof(i.instruction), hash); - return hash; -} - -void Visualizer::parseDisassembler(SkWStream* output, const char* code) { - if (code == nullptr) { - fAsmLine = 0; - return; - } - // Read the disassembled code from <_skvm_jit> until - // the last command that is attached to the byte code - // We skip all the prelude (main loop organizing and such) - // generate the main loop running on vector values (keeping hoisted commands in place) - // and skip the tail loop (which is the same as the main, only on scalar values) - // We stop after the last byte code. - SkTArray commands; - SkStrSplit(code, "\n", kStrict_SkStrSplitMode, &commands); - for (const SkString& line : commands) { - ++fAsmLine; - if (line.find("<_skvm_jit>") >= 0) { - break; - } - } - - if (fAsmLine < commands.size()) { - const SkString& line = commands[fAsmLine]; - SkTArray tokens; - SkStrSplit(line.c_str(), "\t", kStrict_SkStrSplitMode, &tokens); - if (tokens.size() >= 2 && tokens[0].size() > 1) { - fAsmStart = get_addr(tokens[0].c_str()); - } - } - - fAsmEnd += fAsmStart; - for (size_t i = fAsmLine; i < commands.size(); ++i) { - const SkString& line = commands[i]; - SkTArray tokens; - SkStrSplit(line.c_str(), "\t", kStrict_SkStrSplitMode, &tokens); - size_t addr = 0; - if (tokens.size() >= 2 && tokens[0].size() > 1) { - addr = get_addr(tokens[0].c_str()); - } - if (addr > fAsmEnd) { - break; - } - addr -= fAsmStart; - if (!fAsm.empty()) { - MachineCommand& prev = fAsm.back(); - if (prev.command.isEmpty()) { - int len = addr - prev.address; - prev.command.printf("{ align %d bytes }", len); - } - } - SkString command; - for (size_t t = 2; t < tokens.size(); ++t) { - command += tokens[t]; - } - fAsm.push_back({addr, tokens[0], command, tokens[1]}); - } - if (!fAsm.empty()) { - MachineCommand& prev = fAsm.back(); - if (prev.command.isEmpty()) { - int len = fInstructions.back().endCode - prev.address; - prev.command.printf("{ align %d bytes }", len); - } - } - fAsmLine = 0; -} - -void Visualizer::dump(SkWStream* output, const char* code) { - SkDebugfStream stream; - fOutput = output ? output : &stream; - this->parseDisassembler(output, code); - this->dumpHead(); - for (size_t id = 0ul; id < fInstructions.size(); ++id) { - this->dumpInstruction(id); - } - this->dumpTail(); -} - -void Visualizer::markAsDeadCode(std::vector& live, const std::vector& newIds) { - for (size_t id = 0ul; id < fInstructions.size(); ++id) { - Instruction& instruction = fInstructions[id]; - if (instruction.instructionIndex < 0) { - // We skip commands that are duplicates of some other commands - // They either will be dead or alive together with the origin - continue; - } - SkASSERT(instruction.instructionIndex < (int)live.size()); - if (live[instruction.instructionIndex]) { - instruction.instructionIndex = newIds[instruction.instructionIndex]; - fToDisassembler[instruction.instructionIndex] = id; - } else { - instruction.kind - = static_cast(instruction.kind | InstructionFlags::kDead); - fToDisassembler[instruction.instructionIndex] = -1; - // Anything negative meaning the command is duplicate/dead - instruction.instructionIndex = -2; - } - } -} - -void Visualizer::addInstructions(std::vector& program) { - for (Val id = 0; id < (Val)program.size(); id++) { - skvm::Instruction& instr = program[id]; - auto isDuplicate = instr.op == Op::duplicate; - if (isDuplicate) { - this->markAsDuplicate(instr.immA, id); - instr = program[instr.immA]; - } - this->addInstruction({ - viz::InstructionFlags::kNormal, - /*startCode=*/0, /*endCode=0*/0, - id, - isDuplicate ? -1 : 0, - instr - }); - } -} - -void Visualizer::addInstruction(Instruction skvm) { - if (!touches_varying_memory(skvm.instruction.op)) { - if (auto found = fIndex.find(skvm)) { - auto& instruction = fInstructions[*found]; - ++(instruction.duplicates); - return; - } - } - fIndex.set(skvm, fInstructions.size()); - fToDisassembler.set(skvm.instructionIndex, fInstructions.size()); - fInstructions.emplace_back(std::move(skvm)); -} - -void Visualizer::finalize(const std::vector& all, - const std::vector& optimized) { - for (Val id = 0; id < (Val)all.size(); id++) { - if (optimized[id].can_hoist) { - size_t found = fToDisassembler[id]; - Instruction& instruction = fInstructions[found]; - instruction.kind = - static_cast(instruction.kind | InstructionFlags::kHoisted); - } - } -} - -void Visualizer::addMachineCommands(int id, size_t start, size_t end) { - size_t found = fToDisassembler[id]; - Instruction& instruction = fInstructions[found]; - instruction.startCode = start; - instruction.endCode = end; - fAsmEnd = std::max(fAsmEnd, end); -} - -SkString Visualizer::V(int reg) const { - if (reg == -1) { - return SkString("{optimized}"); - } else if (reg == -2) { - return SkString("{dead code}"); - } else { - return SkStringPrintf("v%d", reg); - } -} - -void Visualizer::formatVV(const char* op, int v1, int v2) const { - this->writeText("%s %s, %s", op, V(v1).c_str(), V(v2).c_str()); -} -void Visualizer::formatPV(const char* op, int imm, int v1) const { - this->writeText("%s Ptr%d, %s", op, imm, V(v1).c_str()); -} -void Visualizer::formatPVV(const char* op, int imm, int v1, int v2) const { - this->writeText("%s Ptr%d, %s, %s", op, imm, V(v1).c_str(), V(v2).c_str()); -} -void Visualizer::formatPVVVV(const char* op, int imm, int v1, int v2, int v3, int v4) const { - this->writeText("%s Ptr%d, %s, %s, %s, %s", - op, imm, V(v1).c_str(), V(v2).c_str(), V(v3).c_str(), V(v4).c_str()); -} -void Visualizer::formatA_(int id, const char* op) const { - writeText("%s = %s", V(id).c_str(), op); -} -void Visualizer::formatA_P(int id, const char* op, int imm) const { - this->writeText("%s = %s Ptr%d", V(id).c_str(), op, imm); -} -void Visualizer::formatA_PH(int id, const char* op, int immA, int immB) const { - this->writeText("%s = %s Ptr%d, %x", V(id).c_str(), op, immA, immB); -} -void Visualizer::formatA_PHH(int id, const char* op, int immA, int immB, int immC) const { - this->writeText("%s = %s Ptr%d, %x, %x", V(id).c_str(), op, immA, immB, immC); -} -void Visualizer::formatA_PHV(int id, const char* op, int immA, int immB, int v) const { - this->writeText("%s = %s Ptr%d, %x, V%d", V(id).c_str(), op, immA, immB, V(v).c_str()); -} -void Visualizer::formatA_S(int id, const char* op, int imm) const { - float f; - memcpy(&f, &imm, 4); - char buffer[kSkStrAppendScalar_MaxSize]; - char* stop = SkStrAppendScalar(buffer, f); - this->writeText("%s = %s %x (", V(id).c_str(), op, imm); - fOutput->write(buffer, stop - buffer); - this->writeText(")"); -} -void Visualizer::formatA_V(int id, const char* op, int v) const { - this->writeText("%s = %s %s", V(id).c_str(), op, V(v).c_str()); -} -void Visualizer::formatA_VV(int id, const char* op, int v1, int v2) const { - this->writeText("%s = %s %s, %s", V(id).c_str(), op, V(v1).c_str(), V(v2).c_str()); -} -void Visualizer::formatA_VVV(int id, const char* op, int v1, int v2, int v3) const { - this->writeText( - "%s = %s %s, %s, %s", V(id).c_str(), op, V(v1).c_str(), V(v2).c_str(), V(v3).c_str()); -} -void Visualizer::formatA_VC(int id, const char* op, int v, int imm) const { - this->writeText("%s = %s %s, %d", V(id).c_str(), op, V(v).c_str(), imm); -} - -void Visualizer::writeText(const char* format, ...) const { - SkString message; - va_list argp; - va_start(argp, format); - message.appendVAList(format, argp); - va_end(argp); - fOutput->writeText(message.c_str()); -} - -void Visualizer::dumpInstruction(int id0) const { - const Instruction& instruction = fInstructions[id0]; - const int id = instruction.instructionIndex; - const int x = instruction.instruction.x, - y = instruction.instruction.y, - z = instruction.instruction.z, - w = instruction.instruction.w; - const int immA = instruction.instruction.immA, - immB = instruction.instruction.immB, - immC = instruction.instruction.immC; - if (instruction.instruction.op == skvm::Op::trace_line) { - SkASSERT(fDebugInfo != nullptr); - SkASSERT(immA >= 0 && immB <= (int)fDebugInfo->fSource.size()); - this->writeText("// %s\n", - fDebugInfo->fSource[immB].c_str()); - return; - } else if (instruction.instruction.op == skvm::Op::trace_var || - instruction.instruction.op == skvm::Op::trace_scope) { - // TODO: We can add some visualization here - return; - } else if (instruction.instruction.op == skvm::Op::trace_enter) { - SkASSERT(fDebugInfo != nullptr); - SkASSERT(immA >= 0 && immA <= (int)fDebugInfo->fFuncInfo.size()); - std::string& func = fDebugInfo->fFuncInfo[immA].name; - SkString mask; - mask.printf(immC == 1 ? "%s(-1)" : "%s", V(x).c_str()); - this->writeText( - "↪%s%s\n", - mask.c_str(), - func.c_str()); - return; - } else if (instruction.instruction.op == skvm::Op::trace_exit) { - SkASSERT(fDebugInfo != nullptr); - SkASSERT(immA >= 0 && immA <= (int)fDebugInfo->fFuncInfo.size()); - std::string& func = fDebugInfo->fFuncInfo[immA].name; - SkString mask; - mask.printf(immC == 1 ? "%s(-1)" : "%s", V(x).c_str()); - this->writeText( - "↩%s%s\n", - mask.c_str(), - func.c_str()); - return; - } - // No label, to the operation - SkString label; - if ((instruction.kind & InstructionFlags::kHoisted) != 0) { - label.set("↑↑↑ "); - } - if (instruction.duplicates > 0) { - label.appendf("*%d", instruction.duplicates + 1); - } - SkString classes = instruction.classes(); - this->writeText("%s", classes.c_str(), label.c_str()); - // Operation - switch (instruction.instruction.op) { - case skvm::Op::assert_true: formatVV("assert_true", x, y); break; - case skvm::Op::store8: formatPV("store8", immA, x); break; - case skvm::Op::store16: formatPV("store16", immA, x); break; - case skvm::Op::store32: formatPV("store32", immA, x); break; - case skvm::Op::store64: formatPVV("store64", immA, x, y); break; - case skvm::Op::store128: formatPVVVV("store128", immA, x, y, z, w); break; - case skvm::Op::index: formatA_(id, "index"); break; - case skvm::Op::load8: formatA_P(id, "load8", immA); break; - case skvm::Op::load16: formatA_P(id, "load16", immA); break; - case skvm::Op::load32: formatA_P(id, "load32", immA); break; - case skvm::Op::load64: formatA_PH(id, "load64", immA, immB); break; - case skvm::Op::load128: formatA_PH(id, "load128", immA, immB); break; - case skvm::Op::gather8: formatA_PHV(id, "gather8", immA, immB, x); break; - case skvm::Op::gather16: formatA_PHV(id, "gather16", immA, immB, x); break; - case skvm::Op::gather32: formatA_PHV(id, "gather32", immA, immB, x); break; - case skvm::Op::uniform32: formatA_PH(id, "uniform32", immA, immB); break; - case skvm::Op::array32: formatA_PHH(id, "array32", immA, immB, immC); break; - case skvm::Op::splat: formatA_S(id, "splat", immA); break; - case skvm::Op:: add_f32: formatA_VV(id, "add_f32", x, y); break; - case skvm::Op:: sub_f32: formatA_VV(id, "sub_f32", x, y); break; - case skvm::Op:: mul_f32: formatA_VV(id, "mul_f32", x, y); break; - case skvm::Op:: div_f32: formatA_VV(id, "div_f32", x, y); break; - case skvm::Op:: min_f32: formatA_VV(id, "min_f32", x, y); break; - case skvm::Op:: max_f32: formatA_VV(id, "max_f32", x, y); break; - case skvm::Op:: fma_f32: formatA_VVV(id, "fma_f32", x, y, z); break; - case skvm::Op:: fms_f32: formatA_VVV(id, "fms_f32", x, y, z); break; - case skvm::Op::fnma_f32: formatA_VVV(id, "fnma_f32", x, y, z); break; - case skvm::Op::sqrt_f32: formatA_V(id, "sqrt_f32", x); break; - case skvm::Op:: eq_f32: formatA_VV(id, "eq_f32", x, y); break; - case skvm::Op::neq_f32: formatA_VV(id, "neq_f32", x, y); break; - case skvm::Op:: gt_f32: formatA_VV(id, "gt_f32", x, y); break; - case skvm::Op::gte_f32: formatA_VV(id, "gte_f32", x, y); break; - case skvm::Op::add_i32: formatA_VV(id, "add_i32", x, y); break; - case skvm::Op::sub_i32: formatA_VV(id, "sub_i32", x, y); break; - case skvm::Op::mul_i32: formatA_VV(id, "mul_i32", x, y); break; - case skvm::Op::shl_i32: formatA_VC(id, "shl_i32", x, immA); break; - case skvm::Op::shr_i32: formatA_VC(id, "shr_i32", x, immA); break; - case skvm::Op::sra_i32: formatA_VC(id, "sra_i32", x, immA); break; - case skvm::Op::eq_i32: formatA_VV(id, "eq_i32", x, y); break; - case skvm::Op::gt_i32: formatA_VV(id, "gt_i32", x, y); break; - case skvm::Op::bit_and: formatA_VV(id, "bit_and", x, y); break; - case skvm::Op::bit_or: formatA_VV(id, "bit_or", x, y); break; - case skvm::Op::bit_xor: formatA_VV(id, "bit_xor", x, y); break; - case skvm::Op::bit_clear: formatA_VV(id, "bit_clear", x, y); break; - case skvm::Op::select: formatA_VVV(id, "select", x, y, z); break; - case skvm::Op::ceil: formatA_V(id, "ceil", x); break; - case skvm::Op::floor: formatA_V(id, "floor", x); break; - case skvm::Op::to_f32: formatA_V(id, "to_f32", x); break; - case skvm::Op::to_fp16: formatA_V(id, "to_fp16", x); break; - case skvm::Op::from_fp16: formatA_V(id, "from_fp16", x); break; - case skvm::Op::trunc: formatA_V(id, "trunc", x); break; - case skvm::Op::round: formatA_V(id, "round", x); break; - default: SkASSERT(false); - } - // Generation - if ((instruction.kind & InstructionFlags::kDead) == 0) { - struct Compare - { - bool operator() (const MachineCommand& c, std::pair p) const - { return c.address < p.first; } - bool operator() (std::pair p, const MachineCommand& c) const - { return p.second <= c.address; } - }; - - std::pair range(instruction.startCode, instruction.endCode); - auto commands = std::equal_range(fAsm.begin(), fAsm.end(), range, Compare{ }); - for (const MachineCommand* line = commands.first; line != commands.second; ++line) { - this->writeText("\n%s%s", - line->label.c_str(), - line->command.c_str()); - } - fAsmLine = commands.second - fAsm.begin(); - } - this->writeText("\n"); -} - -void Visualizer::dumpHead() const { - this->writeText( - "\n" - "\n" - " SkVM Disassembler Output\n" - " \n" - " \n" - "\n" - "\n" - " " - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " " - "" - "" - "" - "\n" - " " - "" - "" - "\n" - " " - "" - "\n" - " " - "" - "\n" - " " - "\n" - " " - "" - "\n" - " " - "" - "\n" - " " - "" - "\n" - "
Legend
KindExampleDescription
       v1 = load32 Ptr1A regular SkVM command
*{N}v9 = gt_f32 v0, v1A {N} times deduped SkVM command
↑↑↑    v22 = splat 3f800000 (1)A hoisted SkVM command
mask↪v{N}(-1)// C++ source lineEnter into the procedure with mask v{N} (which has a constant value -1)" - "
mask↩v{N}// C++ source line" - "Exit the procedure with mask v{N}
// C++ source lineLine trace back to C++ code
{dead code} = mul_f32 v1, v18An eliminated \"dead code\" SkVM command
{address}vmovups (%rsi),%ymm0A disassembled machine command generated by SkVM command
\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " "); -} -void Visualizer::dumpTail() const { - this->writeText( - "
SkVM Code
KindCommandComments
\n" - "\n" - "" - ); -} -} // namespace skvm::viz diff --git a/src/utils/SkVMVisualizer.h b/src/utils/SkVMVisualizer.h deleted file mode 100644 index fb783d36eb..0000000000 --- a/src/utils/SkVMVisualizer.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkVMVisualizer_DEFINED -#define SkVMVisualizer_DEFINED -#include -#include -#include "include/core/SkSpan.h" -#include "include/core/SkStream.h" -#include "include/core/SkString.h" -#include "include/private/SkBitmaskEnum.h" -#include "include/private/SkChecksum.h" -#include "include/private/SkTHash.h" -#include "src/core/SkOpts.h" -#include "src/sksl/SkSLOutputStream.h" -#include "src/sksl/tracing/SkVMDebugTrace.h" - -namespace skvm::viz { - enum InstructionFlags : uint8_t { - kNormal = 0x00, - kHoisted = 0x01, - kDead = 0x02, - }; - - struct MachineCommand { - size_t address; - SkString label; - SkString command; - SkString extra; - }; - - struct Instruction { - InstructionFlags kind = InstructionFlags::kNormal; - // Machine commands range (for disassembling): - size_t startCode = 0; - size_t endCode = 0; - int instructionIndex; // index in the actual instructions list - int duplicates = 0; // number of duplicates; - // -1 means it's a duplicate itself; 0 - it does not have dups - skvm::Instruction instruction; - bool operator == (const Instruction& o) const; - SkString classes() const; - }; - - struct InstructionHash { - uint32_t operator()(const Instruction& i) const; - }; - - class Visualizer { - public: - explicit Visualizer(SkSL::SkVMDebugTrace* debugInfo) - : fDebugInfo(debugInfo), fOutput(nullptr) {} - ~Visualizer() = default; - void dump(SkWStream* output, const char* code); - void markAsDeadCode(std::vector& live, const std::vector& newIds); - void finalize(const std::vector& all, - const std::vector& optimized); - void addInstructions(std::vector& program); - void markAsDuplicate(int origin, int id) { - ++fInstructions[origin].duplicates; - } - void addInstruction(Instruction skvm); - void addMachineCommands(int id, size_t start, size_t end); - SkString V(int reg) const; - private: - void parseDisassembler(SkWStream* output, const char* code); - void dumpInstruction(int id) const; - void dumpHead() const; - void dumpTail() const; - void formatVV(const char* op, int v1, int v2) const; - void formatPV(const char* op, int imm, int v1) const; - void formatPVV(const char* op, int imm, int v1, int v2) const; - void formatPVVVV(const char* op, int imm, int v1, int v2, int v3, int v4) const; - void formatA_(int id, const char* op) const; - void formatA_P(int id, const char* op, int imm) const; - void formatA_PH(int id, const char* op, int immA, int immB) const; - void formatA_PHH(int id, const char* op, int immA, int immB, int immC) const; - void formatA_PHV(int id, const char* op, int immA, int immB, int v) const; - void formatA_S(int id, const char* op, int imm) const; - void formatA_V(int id, const char* op, int v) const; - void formatA_VV(int id, const char* op, int v1, int v2) const; - void formatA_VVV(int id, const char* op, int v1, int v2, int v3) const; - void formatA_VC(int id, const char* op, int v, int imm) const; - - void writeText(const char* format, ...) const; - - SkSL::SkVMDebugTrace* fDebugInfo; - SkTHashMap fIndex; - SkTArray fInstructions; - SkWStream* fOutput; - SkTHashMap fToDisassembler; - SkTArray fAsm; - mutable size_t fAsmLine = 0; - size_t fAsmStart = 0; - size_t fAsmEnd = 0; - }; -} // namespace skvm::viz - -namespace sknonstd { -template <> struct is_bitmask_enum : std::true_type {}; -} // namespace sknonstd - -#endif // SkVMVisualizer_DEFINED diff --git a/tests/SkVMTest.cpp b/tests/SkVMTest.cpp index 5be061f4a8..547efa49a1 100644 --- a/tests/SkVMTest.cpp +++ b/tests/SkVMTest.cpp @@ -10,11 +10,6 @@ #include "src/core/SkCpu.h" #include "src/core/SkMSAN.h" #include "src/core/SkVM.h" -#include "src/gpu/GrShaderCaps.h" -#include "src/sksl/SkSLCompiler.h" -#include "src/sksl/codegen/SkSLVMCodeGenerator.h" -#include "src/sksl/tracing/SkVMDebugTrace.h" -#include "src/utils/SkVMVisualizer.h" #include "tests/Test.h" template @@ -2798,59 +2793,3 @@ DEF_TEST(SkVM_duplicates, reporter) { } } } - -DEF_TEST(SkVM_Visualizer, r) { - const char* src = - "int main(int x, int y) {\n" - " int a = 99;\n" - " if (x > 0) a += 100;\n" - " if (y > 0) a += 101;\n" - " a = 102;\n" - " return a;\n" - "}"; - GrShaderCaps caps; - SkSL::Compiler compiler(&caps); - SkSL::Program::Settings settings; - auto program = compiler.convertProgram(SkSL::ProgramKind::kGeneric, - SkSL::String(src), settings); - const SkSL::FunctionDefinition* main = SkSL::Program_GetFunction(*program, "main"); - SkSL::SkVMDebugTrace d; - d.setSource(src); - auto v = std::make_unique(&d); - skvm::Builder b(skvm::Features{}, /*createDuplicates=*/true); - SkSL::ProgramToSkVM(*program, *main, &b, &d, /*uniforms=*/{}); - - skvm::Program p = b.done(nullptr, true, std::move(v)); -#if defined(SKVM_JIT) - SkDynamicMemoryWStream asmFile; - p.disassemble(&asmFile); - auto dumpData = asmFile.detachAsData(); - std::string dumpString((const char*)dumpData->data(), dumpData->size()); -#else - std::string dumpString(nullptr, 0); -#endif - SkDynamicMemoryWStream vizFile; - p.visualizer()->dump(&vizFile, dumpString.c_str()); - auto vizData = vizFile.detachAsData(); - std::string html((const char*)vizData->data(), vizData->size()); - //b.dump(); - //std::printf(html.c_str()); - // Check that html contains all types of information: - if (!dumpString.empty()) { - REPORTER_ASSERT(r, std::strstr(html.c_str(), "")); // machine commands - } - REPORTER_ASSERT(r, std::strstr(html.c_str(), "")); // SkVM byte code - REPORTER_ASSERT(r, std::strstr(html.c_str(), "")); // C++ source - REPORTER_ASSERT(r, std::strstr(html.c_str(), "")); // dead code - REPORTER_ASSERT(r, std::strstr(html.c_str(), "")); // deduped removed - REPORTER_ASSERT(r, std::strstr(html.c_str(), // deduped origins - "" - "↑↑↑ *13" - "v2 = splat 0 (0)")); - REPORTER_ASSERT(r, std::strstr(html.c_str(), // trace enter - "↪v9" - "int main(int x, int y)")); - REPORTER_ASSERT(r, std::strstr(html.c_str(), // trace exit - "↩v9" - "int main(int x, int y)")); -}