<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Atariboy</title>
    <description>The latest articles on DEV Community by Atariboy (@sjkim1127).</description>
    <link>https://dev.to/sjkim1127</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4040129%2Fa612bd3a-f49e-4ea0-b453-f72707db48f0.jpg</url>
      <title>DEV Community: Atariboy</title>
      <link>https://dev.to/sjkim1127</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sjkim1127"/>
    <language>en</language>
    <item>
      <title>Building a Decompiler Pipeline in Rust: Why Fission Separates NIR and HIR</title>
      <dc:creator>Atariboy</dc:creator>
      <pubDate>Tue, 21 Jul 2026 12:45:58 +0000</pubDate>
      <link>https://dev.to/sjkim1127/building-a-decompiler-pipeline-in-rust-why-fission-separates-nir-and-hir-3b4a</link>
      <guid>https://dev.to/sjkim1127/building-a-decompiler-pipeline-in-rust-why-fission-separates-nir-and-hir-3b4a</guid>
      <description>&lt;h1&gt;
  
  
  Building a Decompiler Pipeline in Rust: Why Fission Separates NIR and HIR
&lt;/h1&gt;

&lt;p&gt;Decompiler output often looks simple from the outside.&lt;/p&gt;

&lt;p&gt;A binary goes in. Pseudocode comes out.&lt;/p&gt;

&lt;p&gt;But between those two points, a decompiler must recover several different kinds of information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;instruction semantics&lt;/li&gt;
&lt;li&gt;register and memory effects&lt;/li&gt;
&lt;li&gt;control flow&lt;/li&gt;
&lt;li&gt;stack variables&lt;/li&gt;
&lt;li&gt;calling conventions&lt;/li&gt;
&lt;li&gt;data types&lt;/li&gt;
&lt;li&gt;expressions&lt;/li&gt;
&lt;li&gt;loops and conditionals&lt;/li&gt;
&lt;li&gt;readable source-like structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trying to represent all of this in one intermediate representation quickly becomes difficult.&lt;/p&gt;

&lt;p&gt;While building &lt;a href="https://github.com/sjkim1127/Fission" rel="noopener noreferrer"&gt;Fission&lt;/a&gt;, a reverse-engineering and binary decompilation workspace written primarily in Rust, I decided to separate the decompiler pipeline into two main intermediate representations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NIR&lt;/strong&gt;, a lower-level representation intended to preserve machine semantics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HIR&lt;/strong&gt;, a higher-level representation intended to express recovered, human-readable program structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article explains why that separation exists, what each representation owns, and why it makes decompiler development easier to reason about.&lt;/p&gt;




&lt;h2&gt;
  
  
  Correctness and readability want different things
&lt;/h2&gt;

&lt;p&gt;A decompiler has at least two responsibilities.&lt;/p&gt;

&lt;p&gt;First, it must preserve the behavior of the original machine code.&lt;/p&gt;

&lt;p&gt;Second, it must produce output that a human can understand.&lt;/p&gt;

&lt;p&gt;Those goals overlap, but they are not identical.&lt;/p&gt;

&lt;p&gt;Consider a simplified fragment of machine-level behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tmp0 = RAX
tmp1 = tmp0 + 1
RAX = tmp1
flags = update_flags(tmp0, 1, tmp1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A human reader may prefer to see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;rax&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The concise form is easier to read, but it omits details that may still matter elsewhere in the pipeline.&lt;/p&gt;

&lt;p&gt;The flags update could affect a later conditional branch. The operation width may matter. The source and destination could alias. The operation may have originated from an instruction with additional side effects.&lt;/p&gt;

&lt;p&gt;If the decompiler converts everything into source-like syntax too early, it becomes easy to discard evidence.&lt;/p&gt;

&lt;p&gt;If it keeps everything at machine level until the final rendering stage, the output remains explicit but difficult to understand.&lt;/p&gt;

&lt;p&gt;That tension is the main reason Fission separates NIR and HIR.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fission pipeline
&lt;/h2&gt;

&lt;p&gt;At a high level, the pipeline looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Binary bytes
    ↓
Binary loader and static facts
    ↓
Sleigh decoding
    ↓
Raw P-Code
    ↓
Fission NIR
    ↓
Normalization and semantic recovery
    ↓
Fission HIR
    ↓
Control-flow structuring
    ↓
Pseudocode rendering
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fission uses Sleigh language definitions for instruction decoding and P-Code semantics.&lt;/p&gt;

&lt;p&gt;The later semantic pipeline, however, is owned by Fission itself.&lt;/p&gt;

&lt;p&gt;That includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NIR and HIR&lt;/li&gt;
&lt;li&gt;normalization passes&lt;/li&gt;
&lt;li&gt;type and calling-convention recovery&lt;/li&gt;
&lt;li&gt;control-flow analysis&lt;/li&gt;
&lt;li&gt;structural recovery&lt;/li&gt;
&lt;li&gt;pseudocode rendering&lt;/li&gt;
&lt;li&gt;telemetry&lt;/li&gt;
&lt;li&gt;regression testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ownership boundary is important.&lt;/p&gt;

&lt;p&gt;Sleigh describes what an instruction does.&lt;/p&gt;

&lt;p&gt;Fission decides how that behavior is represented, analyzed, transformed, structured, and presented.&lt;/p&gt;




&lt;h2&gt;
  
  
  What NIR is for
&lt;/h2&gt;

&lt;p&gt;NIR is the representation closest to lifted machine semantics.&lt;/p&gt;

&lt;p&gt;Its primary responsibility is not readability. Its responsibility is preserving enough information to support reliable analysis and transformation.&lt;/p&gt;

&lt;p&gt;NIR should make it possible to reason about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;explicit data flow&lt;/li&gt;
&lt;li&gt;register and memory operations&lt;/li&gt;
&lt;li&gt;address spaces&lt;/li&gt;
&lt;li&gt;operation widths&lt;/li&gt;
&lt;li&gt;branch targets&lt;/li&gt;
&lt;li&gt;side effects&lt;/li&gt;
&lt;li&gt;stack behavior&lt;/li&gt;
&lt;li&gt;control-flow edges&lt;/li&gt;
&lt;li&gt;lifted instruction provenance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simplified NIR sequence might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v0 = load64(stack_pointer + 8)
v1 = int_add(v0, 1)
store64(stack_pointer + 8, v1)
branch_if(v1 &amp;lt; 10, block_4)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is already easier to analyze than raw P-Code, but it remains explicit.&lt;/p&gt;

&lt;p&gt;The representation should not claim more than the analysis has proven.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;store64(stack_pointer + 8, v1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;should not immediately become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;unless the system has enough evidence that the stack location represents a stable local variable and that &lt;code&gt;counter&lt;/code&gt; is an appropriate recovered identity.&lt;/p&gt;

&lt;p&gt;NIR is therefore an &lt;strong&gt;evidence-preserving layer&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What HIR is for
&lt;/h2&gt;

&lt;p&gt;HIR represents higher-level concepts that are useful for transformation and human-readable output.&lt;/p&gt;

&lt;p&gt;It may contain constructs such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Assignment
Variable
Call
Return
If
While
Loop
Break
Continue
BinaryExpression
UnaryExpression
Cast
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An HIR fragment could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HIR is not merely prettier NIR.&lt;/p&gt;

&lt;p&gt;It has a different responsibility.&lt;/p&gt;

&lt;p&gt;NIR models behavior in a form suitable for analysis.&lt;/p&gt;

&lt;p&gt;HIR models recovered intent and program structure in a form suitable for normalization, structuring, and rendering.&lt;/p&gt;

&lt;p&gt;This distinction helps prevent the renderer from becoming responsible for decompiler semantics.&lt;/p&gt;

&lt;p&gt;The renderer should consume an already meaningful structure. It should not be the component that discovers loops, invents variables, repairs malformed conditions, or guesses expression relationships.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why a single IR becomes problematic
&lt;/h2&gt;

&lt;p&gt;A single representation initially appears simpler.&lt;/p&gt;

&lt;p&gt;There are fewer types, fewer conversion steps, and less code.&lt;/p&gt;

&lt;p&gt;Over time, however, one IR often accumulates incompatible responsibilities.&lt;/p&gt;

&lt;p&gt;A single node may be expected to represent both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INT_ADD(register_space[0x20], constant(1))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are related, but they do not have the same invariants.&lt;/p&gt;

&lt;p&gt;The lower-level operation cares about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;bit width&lt;/li&gt;
&lt;li&gt;signedness evidence&lt;/li&gt;
&lt;li&gt;address spaces&lt;/li&gt;
&lt;li&gt;overflow behavior&lt;/li&gt;
&lt;li&gt;exact operands&lt;/li&gt;
&lt;li&gt;provenance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The higher-level expression cares about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;variable identity&lt;/li&gt;
&lt;li&gt;expression simplification&lt;/li&gt;
&lt;li&gt;source-like syntax&lt;/li&gt;
&lt;li&gt;type presentation&lt;/li&gt;
&lt;li&gt;readability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Combining both levels usually leads to one of two outcomes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The IR remains too low-level
&lt;/h3&gt;

&lt;p&gt;The renderer becomes full of special cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;If this store follows this addition,
and both access the same stack location,
and the increment is one,
render it as x++.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is dangerous because the renderer begins to perform semantic analysis.&lt;/p&gt;

&lt;h3&gt;
  
  
  The IR becomes too high-level too early
&lt;/h3&gt;

&lt;p&gt;Low-level facts disappear before the analysis is complete.&lt;/p&gt;

&lt;p&gt;When output is incorrect, it becomes difficult to determine whether the problem came from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;instruction lifting&lt;/li&gt;
&lt;li&gt;NIR normalization&lt;/li&gt;
&lt;li&gt;variable recovery&lt;/li&gt;
&lt;li&gt;type inference&lt;/li&gt;
&lt;li&gt;control-flow structuring&lt;/li&gt;
&lt;li&gt;rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Separating the layers gives each stage a clearer contract.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ownership boundaries matter
&lt;/h2&gt;

&lt;p&gt;One of the main design principles in Fission is that a bug should be fixed in the layer that owns the behavior.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Incorrect binary metadata belongs in the loader or static-analysis layer.&lt;/li&gt;
&lt;li&gt;Incorrect instruction semantics belong in the lifting layer.&lt;/li&gt;
&lt;li&gt;Broken data flow belongs in NIR analysis or normalization.&lt;/li&gt;
&lt;li&gt;Incorrect variable recovery belongs in the semantic-recovery layer.&lt;/li&gt;
&lt;li&gt;Broken loop reconstruction belongs in the structuring layer.&lt;/li&gt;
&lt;li&gt;Formatting problems belong in the renderer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This sounds straightforward, but decompiler development makes it very tempting to patch the final output.&lt;/p&gt;

&lt;p&gt;Suppose the decompiler prints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="n"&gt;label_1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;when the expected structure is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It may be possible to add a renderer heuristic that recognizes the pattern.&lt;/p&gt;

&lt;p&gt;But the actual problem may be that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the CFG was classified incorrectly&lt;/li&gt;
&lt;li&gt;a dominance relation became stale&lt;/li&gt;
&lt;li&gt;a back edge was not recognized&lt;/li&gt;
&lt;li&gt;a region was collapsed before its proof was complete&lt;/li&gt;
&lt;li&gt;a branch polarity was reversed&lt;/li&gt;
&lt;li&gt;normalization removed required evidence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A renderer-level patch may improve one binary while hiding a structural defect that affects many others.&lt;/p&gt;

&lt;p&gt;The NIR/HIR separation makes these ownership mistakes easier to detect.&lt;/p&gt;




&lt;h2&gt;
  
  
  Structuring is not formatting
&lt;/h2&gt;

&lt;p&gt;Control-flow structuring deserves its own stage.&lt;/p&gt;

&lt;p&gt;A CFG containing basic blocks and edges does not automatically become an &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt;, or &lt;code&gt;switch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The structurer must reason about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dominators&lt;/li&gt;
&lt;li&gt;post-dominators&lt;/li&gt;
&lt;li&gt;strongly connected components&lt;/li&gt;
&lt;li&gt;loop headers&lt;/li&gt;
&lt;li&gt;back edges&lt;/li&gt;
&lt;li&gt;exit regions&lt;/li&gt;
&lt;li&gt;irreducible control flow&lt;/li&gt;
&lt;li&gt;region legality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Fission, the structuring path is intended to be deterministic and evidence-driven.&lt;/p&gt;

&lt;p&gt;A region should only be collapsed when the system has enough evidence that the resulting high-level construct preserves the original behavior.&lt;/p&gt;

&lt;p&gt;When the proof is incomplete, explicit fallback output is preferable to deceptively clean pseudocode.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="nl"&gt;label_1:&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="n"&gt;label_1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may be less readable than a reconstructed loop.&lt;/p&gt;

&lt;p&gt;But it is still better than emitting a clean-looking &lt;code&gt;while&lt;/code&gt; statement whose semantics are wrong.&lt;/p&gt;

&lt;p&gt;Readability matters.&lt;/p&gt;

&lt;p&gt;Incorrect readability is worse than honest low-level output.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Rust helps
&lt;/h2&gt;

&lt;p&gt;Rust is not automatically the best language for every decompiler.&lt;/p&gt;

&lt;p&gt;It introduces real costs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;longer compile times&lt;/li&gt;
&lt;li&gt;complex generic types&lt;/li&gt;
&lt;li&gt;ownership friction in graph-heavy transformations&lt;/li&gt;
&lt;li&gt;difficult mutation patterns&lt;/li&gt;
&lt;li&gt;a learning curve around lifetimes and traits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still, Rust has been useful for Fission because the system contains many ownership boundaries.&lt;/p&gt;

&lt;p&gt;Different parts of the workspace own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;binary metadata&lt;/li&gt;
&lt;li&gt;static facts&lt;/li&gt;
&lt;li&gt;lifted semantics&lt;/li&gt;
&lt;li&gt;NIR transformations&lt;/li&gt;
&lt;li&gt;HIR normalization&lt;/li&gt;
&lt;li&gt;CFG structures&lt;/li&gt;
&lt;li&gt;analysis caches&lt;/li&gt;
&lt;li&gt;emulator state&lt;/li&gt;
&lt;li&gt;telemetry&lt;/li&gt;
&lt;li&gt;user-facing output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rust makes it harder to blur those boundaries accidentally.&lt;/p&gt;

&lt;p&gt;Immutable inputs and explicit transformation outputs make questions like these easier to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which pass changed this function?&lt;/li&gt;
&lt;li&gt;Is this graph analysis still valid after mutation?&lt;/li&gt;
&lt;li&gt;Does the renderer own any semantic decisions?&lt;/li&gt;
&lt;li&gt;Can two analysis components mutate the same state?&lt;/li&gt;
&lt;li&gt;Is this result derived from canonical telemetry or from a parallel counter?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Rust type system does not prove the decompiler correct.&lt;/p&gt;

&lt;p&gt;It can, however, make architectural mistakes more visible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Determinism is a correctness property
&lt;/h2&gt;

&lt;p&gt;A decompiler should produce the same output for the same binary across separate runs.&lt;/p&gt;

&lt;p&gt;This is more subtle than it appears.&lt;/p&gt;

&lt;p&gt;An algorithm may accidentally rely on the iteration order of a hash map or hash set.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;
    &lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.find&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;is_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;nodes&lt;/code&gt; is backed by a randomized hash collection, the selected candidate may differ between processes.&lt;/p&gt;

&lt;p&gt;That can alter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;region-collapse order&lt;/li&gt;
&lt;li&gt;variable naming&lt;/li&gt;
&lt;li&gt;expression ordering&lt;/li&gt;
&lt;li&gt;block layout&lt;/li&gt;
&lt;li&gt;final pseudocode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a compiler or decompiler, this is not merely cosmetic.&lt;/p&gt;

&lt;p&gt;Nondeterminism makes regressions difficult to reproduce and quality measurements unreliable.&lt;/p&gt;

&lt;p&gt;Fission therefore treats deterministic output as an explicit correctness requirement.&lt;/p&gt;

&lt;p&gt;Transformations that choose among multiple candidates must use stable ordering or an explicitly defined ranking strategy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Measuring decompiler quality
&lt;/h2&gt;

&lt;p&gt;A major unresolved question is how to measure whether decompiler output is actually improving.&lt;/p&gt;

&lt;p&gt;Readable output is subjective.&lt;/p&gt;

&lt;p&gt;A change may make one function prettier while making another less accurate.&lt;/p&gt;

&lt;p&gt;Potential quality signals include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;semantic equivalence on controlled fixtures&lt;/li&gt;
&lt;li&gt;structural similarity against known source&lt;/li&gt;
&lt;li&gt;reduction in unnecessary &lt;code&gt;goto&lt;/code&gt; statements&lt;/li&gt;
&lt;li&gt;successful loop and conditional recovery&lt;/li&gt;
&lt;li&gt;stable variable identity&lt;/li&gt;
&lt;li&gt;type-recovery confidence&lt;/li&gt;
&lt;li&gt;deterministic output across runs&lt;/li&gt;
&lt;li&gt;regression rates across real binaries&lt;/li&gt;
&lt;li&gt;preservation of side effects and branch behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No single metric is enough.&lt;/p&gt;

&lt;p&gt;A practical decompiler quality system probably needs to combine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;mechanical checks&lt;/li&gt;
&lt;li&gt;real-binary regression corpora&lt;/li&gt;
&lt;li&gt;telemetry&lt;/li&gt;
&lt;li&gt;differential testing&lt;/li&gt;
&lt;li&gt;human review&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The important distinction is between output that merely looks better and output that is structurally and semantically better.&lt;/p&gt;




&lt;h2&gt;
  
  
  Current status
&lt;/h2&gt;

&lt;p&gt;Fission is still a research-heavy, early-stage project.&lt;/p&gt;

&lt;p&gt;Most current work is focused on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;x86 and x86-64 correctness&lt;/li&gt;
&lt;li&gt;NIR and HIR normalization&lt;/li&gt;
&lt;li&gt;control-flow structuring&lt;/li&gt;
&lt;li&gt;deterministic output&lt;/li&gt;
&lt;li&gt;readable pseudocode&lt;/li&gt;
&lt;li&gt;regression testing against real binaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository also contains experimental work on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;P-Code emulation&lt;/li&gt;
&lt;li&gt;deterministic execution recording and replay&lt;/li&gt;
&lt;li&gt;taint tracking&lt;/li&gt;
&lt;li&gt;concolic path exploration&lt;/li&gt;
&lt;li&gt;symbolic reasoning in Rust&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of these areas remain incomplete.&lt;/p&gt;

&lt;p&gt;I prefer to state those limitations clearly rather than present them as production-ready features.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The separation between NIR and HIR is not only an implementation detail.&lt;/p&gt;

&lt;p&gt;It is a way to preserve low-level evidence while still allowing the decompiler to recover readable, source-like structure.&lt;/p&gt;

&lt;p&gt;NIR asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What does the machine appear to do?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;HIR asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What higher-level structure is supported by the available evidence?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Keeping those questions separate helps make transformations easier to review, bugs easier to locate, and final output less dependent on renderer-specific heuristics.&lt;/p&gt;

&lt;p&gt;Fission is open source, and feedback is welcome from people working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;compiler IR design&lt;/li&gt;
&lt;li&gt;reverse engineering&lt;/li&gt;
&lt;li&gt;decompilation&lt;/li&gt;
&lt;li&gt;control-flow structuring&lt;/li&gt;
&lt;li&gt;symbolic execution&lt;/li&gt;
&lt;li&gt;Rust architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sjkim1127/Fission" rel="noopener noreferrer"&gt;https://github.com/sjkim1127/Fission&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>computerscience</category>
      <category>rust</category>
      <category>security</category>
    </item>
  </channel>
</rss>
