From a966338cf292d127f80756012775ecfa456e9770 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 3 Apr 2026 02:01:06 +0300 Subject: [PATCH 1/3] [snippy] Support dump-initial-registers-yaml without model plugin --- .../dump-initial-registers-no-model.yaml | 20 ++++++++++++++++ .../include/snippy/Generator/SnippyModule.h | 4 ++++ llvm/tools/llvm-snippy/lib/FlowGenerator.cpp | 23 +++++++++++++++++++ .../lib/Generator/SnippyModule.cpp | 8 +++++++ 4 files changed, 55 insertions(+) create mode 100644 llvm/test/tools/llvm-snippy/dump-initial-registers-no-model.yaml diff --git a/llvm/test/tools/llvm-snippy/dump-initial-registers-no-model.yaml b/llvm/test/tools/llvm-snippy/dump-initial-registers-no-model.yaml new file mode 100644 index 000000000000..97db6d240c6b --- /dev/null +++ b/llvm/test/tools/llvm-snippy/dump-initial-registers-no-model.yaml @@ -0,0 +1,20 @@ +# RUN: rm -f %t.initial-regs.yaml +# RUN: llvm-snippy %s -mtriple=riscv64-unknown-elf -num-instrs=10 \ +# RUN: --init-regs-in-elf \ +# RUN: --dump-initial-registers-yaml=%t.initial-regs.yaml +# RUN: test -s %t.initial-regs.yaml + +sections: + - name: text + VMA: 0x1000 + LMA: 0x1000 + SIZE: 0x1000 + ACCESS: rx + - name: data + VMA: 0x3000 + LMA: 0x3000 + SIZE: 0x1000 + ACCESS: rw + +histogram: + - [ADD, 1] \ No newline at end of file diff --git a/llvm/tools/llvm-snippy/include/snippy/Generator/SnippyModule.h b/llvm/tools/llvm-snippy/include/snippy/Generator/SnippyModule.h index 99847a3b17c5..18affbdb565d 100644 --- a/llvm/tools/llvm-snippy/include/snippy/Generator/SnippyModule.h +++ b/llvm/tools/llvm-snippy/include/snippy/Generator/SnippyModule.h @@ -318,6 +318,8 @@ class SnippyProgramContext final { const IRegisterState & getInitialRegisterState(const TargetSubtargetInfo &ST) const; + const IRegisterState &getInitialRegisterState() const; + bool hasProgramStateSaveSpace() const { return PGSK.get(); } const auto &getProgramStateSaveSpace() const { return *PGSK; } auto &getProgramStateSaveSpace() { return *PGSK; } @@ -372,6 +374,8 @@ class SnippyProgramContext final { std::unique_ptr MemManager; std::unique_ptr SMCManager; + const TargetSubtargetInfo *InitialStateSubtarget = nullptr; + constexpr static auto SmallStringDefaultSize = 16; std::optional ROMSection; diff --git a/llvm/tools/llvm-snippy/lib/FlowGenerator.cpp b/llvm/tools/llvm-snippy/lib/FlowGenerator.cpp index eea39dfb89a5..475b128cd9b8 100644 --- a/llvm/tools/llvm-snippy/lib/FlowGenerator.cpp +++ b/llvm/tools/llvm-snippy/lib/FlowGenerator.cpp @@ -192,6 +192,27 @@ static auto createSMCInitRoutine(GeneratorContext &Ctx) { return ExtModule; } +void dumpInitialRegistersIfRequested(SnippyProgramContext &ProgContext, + LLVMContext &Ctx, + const PassConfig &PassCfg) { + auto &YamlPath = PassCfg.RegistersConfig.InitialStateOutputYaml; + if (YamlPath.empty()) + return; + + if (PassCfg.ModelPluginConfig.runOnModel()) + return; + + auto &Regs = ProgContext.getInitialRegisterState(); + + std::error_code EC; + raw_fd_ostream File(YamlPath, EC); + if (EC) + snippy::fatal(Ctx, "Failed to dump initial registers", + "cannot open file \"" + YamlPath + "\": " + EC.message()); + + Regs.saveAsYAMLFile(File); +} + } // namespace [[maybe_unused]] static void dumpSelfcheck(const std::vector &Data, @@ -446,6 +467,8 @@ GeneratorResult FlowGenerator::generate(LLVMState &State, if (ProgramCfg.MemoryCfg.SkipRuntimeMemInit) Modules = {&MainModule}; + dumpInitialRegistersIfRequested(ProgContext, State.getCtx(), PassCfg); + dumpVerificationIntervalsIfNeeeded(MainModule, GenCtx, BaseFileName); if (PassCfg.ModelPluginConfig.runOnModel()) { diff --git a/llvm/tools/llvm-snippy/lib/Generator/SnippyModule.cpp b/llvm/tools/llvm-snippy/lib/Generator/SnippyModule.cpp index 875ac7a20398..90c0c388645c 100644 --- a/llvm/tools/llvm-snippy/lib/Generator/SnippyModule.cpp +++ b/llvm/tools/llvm-snippy/lib/Generator/SnippyModule.cpp @@ -320,6 +320,13 @@ const IRegisterState &SnippyProgramContext::getInitialRegisterState( return *InitialMachineState; } +const IRegisterState &SnippyProgramContext::getInitialRegisterState() const { + assert(InitialStateSubtarget && + "Initial register state requested before target context " + "initialization"); + return getInitialRegisterState(*InitialStateSubtarget); +} + SnippyProgramContext::SnippyProgramContext(LLVMState &State, RegisterGenerator &RegGen, std::vector Pools, @@ -353,6 +360,7 @@ void SnippyProgramContext::createTargetContext(const Config &Cfg, const TargetSubtargetInfo &STI, const RegPoolWrapper &RP) { assert(!TargetContext && "Double context insertion"); + InitialStateSubtarget = &STI; TargetContext = State->getSnippyTarget().createTargetContext(*State, Cfg, &STI, RP); } From c84e09ac15f22a2f83b8b2924349e773daf2d021 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 20 Jun 2026 21:04:57 +0300 Subject: [PATCH 2/3] [snippy] Dump initial registers without model plugin --- .../dump-initial-registers-no-model.yaml | 18 +++---------- .../include/snippy/Generator/SnippyModule.h | 4 +++ llvm/tools/llvm-snippy/lib/FlowGenerator.cpp | 26 +++---------------- .../Generator/SimulatorContextWrapperPass.cpp | 2 +- .../lib/Generator/SnippyModule.cpp | 25 ++++++++++++++++++ 5 files changed, 37 insertions(+), 38 deletions(-) diff --git a/llvm/test/tools/llvm-snippy/dump-initial-registers-no-model.yaml b/llvm/test/tools/llvm-snippy/dump-initial-registers-no-model.yaml index 97db6d240c6b..5e33e31ceabc 100644 --- a/llvm/test/tools/llvm-snippy/dump-initial-registers-no-model.yaml +++ b/llvm/test/tools/llvm-snippy/dump-initial-registers-no-model.yaml @@ -1,20 +1,10 @@ -# RUN: rm -f %t.initial-regs.yaml -# RUN: llvm-snippy %s -mtriple=riscv64-unknown-elf -num-instrs=10 \ +# RUN: llvm-snippy %s -mtriple=riscv64 --model-plugin=None -num-instrs=10 \ # RUN: --init-regs-in-elf \ # RUN: --dump-initial-registers-yaml=%t.initial-regs.yaml # RUN: test -s %t.initial-regs.yaml -sections: - - name: text - VMA: 0x1000 - LMA: 0x1000 - SIZE: 0x1000 - ACCESS: rx - - name: data - VMA: 0x3000 - LMA: 0x3000 - SIZE: 0x1000 - ACCESS: rw +include: + - "Inputs/sections.yaml" histogram: - - [ADD, 1] \ No newline at end of file + - [ADD, 1] diff --git a/llvm/tools/llvm-snippy/include/snippy/Generator/SnippyModule.h b/llvm/tools/llvm-snippy/include/snippy/Generator/SnippyModule.h index 18affbdb565d..5b8f7d14fa59 100644 --- a/llvm/tools/llvm-snippy/include/snippy/Generator/SnippyModule.h +++ b/llvm/tools/llvm-snippy/include/snippy/Generator/SnippyModule.h @@ -318,6 +318,10 @@ class SnippyProgramContext final { const IRegisterState & getInitialRegisterState(const TargetSubtargetInfo &ST) const; + void dumpInitialRegisterState(StringRef YamlPath) const; + void dumpInitialRegisterState(StringRef YamlPath, + const TargetSubtargetInfo &ST) const; + const IRegisterState &getInitialRegisterState() const; bool hasProgramStateSaveSpace() const { return PGSK.get(); } diff --git a/llvm/tools/llvm-snippy/lib/FlowGenerator.cpp b/llvm/tools/llvm-snippy/lib/FlowGenerator.cpp index 475b128cd9b8..534fc8980848 100644 --- a/llvm/tools/llvm-snippy/lib/FlowGenerator.cpp +++ b/llvm/tools/llvm-snippy/lib/FlowGenerator.cpp @@ -192,27 +192,6 @@ static auto createSMCInitRoutine(GeneratorContext &Ctx) { return ExtModule; } -void dumpInitialRegistersIfRequested(SnippyProgramContext &ProgContext, - LLVMContext &Ctx, - const PassConfig &PassCfg) { - auto &YamlPath = PassCfg.RegistersConfig.InitialStateOutputYaml; - if (YamlPath.empty()) - return; - - if (PassCfg.ModelPluginConfig.runOnModel()) - return; - - auto &Regs = ProgContext.getInitialRegisterState(); - - std::error_code EC; - raw_fd_ostream File(YamlPath, EC); - if (EC) - snippy::fatal(Ctx, "Failed to dump initial registers", - "cannot open file \"" + YamlPath + "\": " + EC.message()); - - Regs.saveAsYAMLFile(File); -} - } // namespace [[maybe_unused]] static void dumpSelfcheck(const std::vector &Data, @@ -467,8 +446,6 @@ GeneratorResult FlowGenerator::generate(LLVMState &State, if (ProgramCfg.MemoryCfg.SkipRuntimeMemInit) Modules = {&MainModule}; - dumpInitialRegistersIfRequested(ProgContext, State.getCtx(), PassCfg); - dumpVerificationIntervalsIfNeeeded(MainModule, GenCtx, BaseFileName); if (PassCfg.ModelPluginConfig.runOnModel()) { @@ -548,6 +525,9 @@ GeneratorResult FlowGenerator::generate(LLVMState &State, } else { + ProgContext.dumpInitialRegisterState( + PassCfg.RegistersConfig.InitialStateOutputYaml); + snippy::warn(WarningName::NoModelExec, State.getCtx(), "Skipping snippet execution on the model", "model was set to 'None'."); diff --git a/llvm/tools/llvm-snippy/lib/Generator/SimulatorContextWrapperPass.cpp b/llvm/tools/llvm-snippy/lib/Generator/SimulatorContextWrapperPass.cpp index 6ed95aa8fc76..57456771725a 100644 --- a/llvm/tools/llvm-snippy/lib/Generator/SimulatorContextWrapperPass.cpp +++ b/llvm/tools/llvm-snippy/lib/Generator/SimulatorContextWrapperPass.cpp @@ -229,7 +229,7 @@ Error SimulatorContext::runSimulator(const RunInfo &RI) { auto Err = I.setPC(ElfData->ProgStart); if (Err) return Err; - I.dumpCurrentRegState(InitialStateOutputYaml); + ProgCtx.dumpInitialRegisterState(InitialStateOutputYaml, I.getSubTarget()); if (ElfData->ProgEnd == std::nullopt) return createStringError( diff --git a/llvm/tools/llvm-snippy/lib/Generator/SnippyModule.cpp b/llvm/tools/llvm-snippy/lib/Generator/SnippyModule.cpp index 90c0c388645c..f7a0ad008e6b 100644 --- a/llvm/tools/llvm-snippy/lib/Generator/SnippyModule.cpp +++ b/llvm/tools/llvm-snippy/lib/Generator/SnippyModule.cpp @@ -320,6 +320,31 @@ const IRegisterState &SnippyProgramContext::getInitialRegisterState( return *InitialMachineState; } +void SnippyProgramContext::dumpInitialRegisterState( + StringRef YamlPath, const TargetSubtargetInfo &ST) const { + if (YamlPath.empty()) + return; + + std::error_code EC; + raw_fd_ostream File(YamlPath, EC); + if (EC) + snippy::fatal(State->getCtx(), "Failed to dump initial registers", + "cannot open file \"" + Twine(YamlPath) + + "\": " + EC.message()); + + getInitialRegisterState(ST).saveAsYAMLFile(File); +} + +void SnippyProgramContext::dumpInitialRegisterState(StringRef YamlPath) const { + if (YamlPath.empty()) + return; + + assert( + InitialStateSubtarget && + "Initial register state requested before target context initialization"); + dumpInitialRegisterState(YamlPath, *InitialStateSubtarget); +} + const IRegisterState &SnippyProgramContext::getInitialRegisterState() const { assert(InitialStateSubtarget && "Initial register state requested before target context " From e05275625dd14af998b4f0e4a630ce6edfa5d971 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 24 Oct 2026 20:03:46 +0300 Subject: [PATCH 3/3] [snippy] Unify register state YAML dumping Move YAML file handling to IRegisterState and reuse it in both model and no-model paths. Get the entry function subtarget at the call site and remove the redundant SnippyProgramContext helpers and cached pointer. Extend regression coverage for no-model initial register dumps. --- .../dump-initial-registers-no-model.yaml | 10 ------ .../dump-initial-registers-no-model.yaml | 25 ++++++++++++++ .../include/snippy/Generator/SnippyModule.h | 8 ----- .../include/snippy/Simulator/Simulator.h | 1 + llvm/tools/llvm-snippy/lib/FlowGenerator.cpp | 11 +++++-- .../llvm-snippy/lib/Generator/Interpreter.cpp | 10 +----- .../Generator/SimulatorContextWrapperPass.cpp | 2 +- .../lib/Generator/SnippyModule.cpp | 33 ------------------- .../llvm-snippy/lib/Simulator/Simulator.cpp | 15 +++++++++ 9 files changed, 52 insertions(+), 63 deletions(-) delete mode 100644 llvm/test/tools/llvm-snippy/dump-initial-registers-no-model.yaml create mode 100644 llvm/test/tools/llvm-snippy/dumps/dump-registers/dump-initial-registers-no-model.yaml diff --git a/llvm/test/tools/llvm-snippy/dump-initial-registers-no-model.yaml b/llvm/test/tools/llvm-snippy/dump-initial-registers-no-model.yaml deleted file mode 100644 index 5e33e31ceabc..000000000000 --- a/llvm/test/tools/llvm-snippy/dump-initial-registers-no-model.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# RUN: llvm-snippy %s -mtriple=riscv64 --model-plugin=None -num-instrs=10 \ -# RUN: --init-regs-in-elf \ -# RUN: --dump-initial-registers-yaml=%t.initial-regs.yaml -# RUN: test -s %t.initial-regs.yaml - -include: - - "Inputs/sections.yaml" - -histogram: - - [ADD, 1] diff --git a/llvm/test/tools/llvm-snippy/dumps/dump-registers/dump-initial-registers-no-model.yaml b/llvm/test/tools/llvm-snippy/dumps/dump-registers/dump-initial-registers-no-model.yaml new file mode 100644 index 000000000000..9055d1e6bd82 --- /dev/null +++ b/llvm/test/tools/llvm-snippy/dumps/dump-registers/dump-initial-registers-no-model.yaml @@ -0,0 +1,25 @@ +# REQUIRES: skip-snippy-model-mixin +# RUN: llvm-snippy %s -mtriple=riscv64 --model-plugin=None -num-instrs=10 \ +# RUN: --init-regs-in-elf \ +# RUN: --dump-initial-registers-yaml=%t.initial-regs.yaml +# RUN: FileCheck %s --input-file=%t.initial-regs.yaml + +# RUN: llvm-snippy %s -mtriple=riscv64 --model-plugin=None -num-instrs=10 \ +# RUN: --initial-regs-yaml=%t.initial-regs.yaml \ +# RUN: --dump-initial-registers-yaml=%t.reloaded-regs.yaml +# RUN: diff %t.initial-regs.yaml %t.reloaded-regs.yaml + +# RUN: not llvm-snippy %s -mtriple=riscv64 --model-plugin=None -num-instrs=10 \ +# RUN: --dump-initial-registers-yaml=%t.initial-regs.yaml/invalid 2>&1 \ +# RUN: | FileCheck %s --check-prefix=ERROR + +# CHECK: registers: +# CHECK-NEXT: - [ X0, 0x0000000000000000 ] +# CHECK: - [ X31, 0x{{([[:xdigit:]]{16})}} ] +# ERROR: error: Register dump error: + +include: + - "../../Inputs/sections.yaml" + +histogram: + - [ADD, 1] diff --git a/llvm/tools/llvm-snippy/include/snippy/Generator/SnippyModule.h b/llvm/tools/llvm-snippy/include/snippy/Generator/SnippyModule.h index 5b8f7d14fa59..99847a3b17c5 100644 --- a/llvm/tools/llvm-snippy/include/snippy/Generator/SnippyModule.h +++ b/llvm/tools/llvm-snippy/include/snippy/Generator/SnippyModule.h @@ -318,12 +318,6 @@ class SnippyProgramContext final { const IRegisterState & getInitialRegisterState(const TargetSubtargetInfo &ST) const; - void dumpInitialRegisterState(StringRef YamlPath) const; - void dumpInitialRegisterState(StringRef YamlPath, - const TargetSubtargetInfo &ST) const; - - const IRegisterState &getInitialRegisterState() const; - bool hasProgramStateSaveSpace() const { return PGSK.get(); } const auto &getProgramStateSaveSpace() const { return *PGSK; } auto &getProgramStateSaveSpace() { return *PGSK; } @@ -378,8 +372,6 @@ class SnippyProgramContext final { std::unique_ptr MemManager; std::unique_ptr SMCManager; - const TargetSubtargetInfo *InitialStateSubtarget = nullptr; - constexpr static auto SmallStringDefaultSize = 16; std::optional ROMSection; diff --git a/llvm/tools/llvm-snippy/include/snippy/Simulator/Simulator.h b/llvm/tools/llvm-snippy/include/snippy/Simulator/Simulator.h index e2e5d873e1b7..2746c6f42dee 100644 --- a/llvm/tools/llvm-snippy/include/snippy/Simulator/Simulator.h +++ b/llvm/tools/llvm-snippy/include/snippy/Simulator/Simulator.h @@ -38,6 +38,7 @@ struct IRegisterState { virtual void loadFromYamlFile(StringRef, WarningsT &, const SnippyTarget *Tgt = nullptr) = 0; virtual void saveAsYAMLFile(raw_ostream &) const = 0; + void saveAsYAMLFile(StringRef YamlPath) const; virtual bool operator==(const IRegisterState &) const = 0; bool operator!=(const IRegisterState &Another) const { diff --git a/llvm/tools/llvm-snippy/lib/FlowGenerator.cpp b/llvm/tools/llvm-snippy/lib/FlowGenerator.cpp index 534fc8980848..c38152e3f1a1 100644 --- a/llvm/tools/llvm-snippy/lib/FlowGenerator.cpp +++ b/llvm/tools/llvm-snippy/lib/FlowGenerator.cpp @@ -525,8 +525,15 @@ GeneratorResult FlowGenerator::generate(LLVMState &State, } else { - ProgContext.dumpInitialRegisterState( - PassCfg.RegistersConfig.InitialStateOutputYaml); + if (auto &YamlPath = PassCfg.RegistersConfig.InitialStateOutputYaml; + !YamlPath.empty()) { + auto *EntryFunc = + MainModule.getModule().getFunction(ProgramCfg.EntryPointName); + assert(EntryFunc); + auto &ST = State.getSubtargetImpl(*EntryFunc); + auto &RegState = ProgContext.getInitialRegisterState(ST); + RegState.saveAsYAMLFile(YamlPath); + } snippy::warn(WarningName::NoModelExec, State.getCtx(), "Skipping snippet execution on the model", diff --git a/llvm/tools/llvm-snippy/lib/Generator/Interpreter.cpp b/llvm/tools/llvm-snippy/lib/Generator/Interpreter.cpp index a48a3c820243..2c39d8c82d8c 100644 --- a/llvm/tools/llvm-snippy/lib/Generator/Interpreter.cpp +++ b/llvm/tools/llvm-snippy/lib/Generator/Interpreter.cpp @@ -331,15 +331,7 @@ void Interpreter::dumpRegsAsYAML(const IRegisterState &Regs, raw_ostream &OS) { } void Interpreter::dumpRegs(const IRegisterState &Regs, StringRef YamlPath) { - if (YamlPath.empty()) - return; - - std::error_code EC; - raw_fd_ostream File(YamlPath, EC); - if (EC) - snippy::fatal(formatv("Register dump error: {0}", EC.message())); - - dumpRegsAsYAML(Regs, File); + Regs.saveAsYAMLFile(YamlPath); } void Interpreter::dumpOneRange(NamedMemoryRange Range, diff --git a/llvm/tools/llvm-snippy/lib/Generator/SimulatorContextWrapperPass.cpp b/llvm/tools/llvm-snippy/lib/Generator/SimulatorContextWrapperPass.cpp index 57456771725a..6ed95aa8fc76 100644 --- a/llvm/tools/llvm-snippy/lib/Generator/SimulatorContextWrapperPass.cpp +++ b/llvm/tools/llvm-snippy/lib/Generator/SimulatorContextWrapperPass.cpp @@ -229,7 +229,7 @@ Error SimulatorContext::runSimulator(const RunInfo &RI) { auto Err = I.setPC(ElfData->ProgStart); if (Err) return Err; - ProgCtx.dumpInitialRegisterState(InitialStateOutputYaml, I.getSubTarget()); + I.dumpCurrentRegState(InitialStateOutputYaml); if (ElfData->ProgEnd == std::nullopt) return createStringError( diff --git a/llvm/tools/llvm-snippy/lib/Generator/SnippyModule.cpp b/llvm/tools/llvm-snippy/lib/Generator/SnippyModule.cpp index f7a0ad008e6b..875ac7a20398 100644 --- a/llvm/tools/llvm-snippy/lib/Generator/SnippyModule.cpp +++ b/llvm/tools/llvm-snippy/lib/Generator/SnippyModule.cpp @@ -320,38 +320,6 @@ const IRegisterState &SnippyProgramContext::getInitialRegisterState( return *InitialMachineState; } -void SnippyProgramContext::dumpInitialRegisterState( - StringRef YamlPath, const TargetSubtargetInfo &ST) const { - if (YamlPath.empty()) - return; - - std::error_code EC; - raw_fd_ostream File(YamlPath, EC); - if (EC) - snippy::fatal(State->getCtx(), "Failed to dump initial registers", - "cannot open file \"" + Twine(YamlPath) + - "\": " + EC.message()); - - getInitialRegisterState(ST).saveAsYAMLFile(File); -} - -void SnippyProgramContext::dumpInitialRegisterState(StringRef YamlPath) const { - if (YamlPath.empty()) - return; - - assert( - InitialStateSubtarget && - "Initial register state requested before target context initialization"); - dumpInitialRegisterState(YamlPath, *InitialStateSubtarget); -} - -const IRegisterState &SnippyProgramContext::getInitialRegisterState() const { - assert(InitialStateSubtarget && - "Initial register state requested before target context " - "initialization"); - return getInitialRegisterState(*InitialStateSubtarget); -} - SnippyProgramContext::SnippyProgramContext(LLVMState &State, RegisterGenerator &RegGen, std::vector Pools, @@ -385,7 +353,6 @@ void SnippyProgramContext::createTargetContext(const Config &Cfg, const TargetSubtargetInfo &STI, const RegPoolWrapper &RP) { assert(!TargetContext && "Double context insertion"); - InitialStateSubtarget = &STI; TargetContext = State->getSnippyTarget().createTargetContext(*State, Cfg, &STI, RP); } diff --git a/llvm/tools/llvm-snippy/lib/Simulator/Simulator.cpp b/llvm/tools/llvm-snippy/lib/Simulator/Simulator.cpp index 1436d5f8920e..a001e0a0b262 100644 --- a/llvm/tools/llvm-snippy/lib/Simulator/Simulator.cpp +++ b/llvm/tools/llvm-snippy/lib/Simulator/Simulator.cpp @@ -8,10 +8,25 @@ #include "snippy/Simulator/Simulator.h" +#include "snippy/Support/Error.h" + +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/YAMLTraits.h" namespace llvm { +void snippy::IRegisterState::saveAsYAMLFile(StringRef YamlPath) const { + if (YamlPath.empty()) + return; + + std::error_code EC; + raw_fd_ostream File(YamlPath, EC); + if (EC) + snippy::fatal(formatv("Register dump error: {0}", EC.message())); + + saveAsYAMLFile(File); +} + namespace { struct RegionKey {