• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SamboyCoding / Cpp2IL / 30178263727

25 Jul 2026 10:48PM UTC coverage: 36.302% (-0.8%) from 37.144%
30178263727

push

github

SamboyCoding
Decompiler: Recover static fields, throw helpers, and rgctx, fix bool flags, fix this param in IL, fix float handling somewhat

2890 of 9069 branches covered (31.87%)

Branch coverage included in aggregate %.

28 of 319 new or added lines in 17 files covered. (8.78%)

10 existing lines in 6 files now uncovered.

5291 of 13467 relevant lines covered (39.29%)

164158.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.64
/Cpp2IL.Core/Analysis/SsaSimplifier.cs
1
using System.Collections.Generic;
2
using Cpp2IL.Core.Graphs;
3
using Cpp2IL.Core.ISIL;
4
using Cpp2IL.Core.Model.Contexts;
5

6
namespace Cpp2IL.Core.Analysis;
7

8
/// <summary>
9
/// Copy and constant propagation, performed while the graph is still in SSA form.
10
/// </summary>
11
public static class SsaSimplifier
12
{
13
    public static void Run(MethodAnalysisContext method) => Run(method.ControlFlowGraph!, method.ParameterLocals);
×
14

15
    public static void Run(ISILControlFlowGraph cfg, List<LocalVariable> parameterLocals)
16
    {
17
        // dest -> value for every forwardable copy/constant. SSA's single-assignment property means a
18
        // local is defined at most once, so there is never a conflicting entry for the same key.
19
        var forwarded = new Dictionary<LocalVariable, object>();
4✔
20

21
        foreach (var block in cfg.Blocks)
40✔
22
            foreach (var instruction in block.Instructions)
60✔
23
                if (instruction.OpCode == OpCode.Move
14✔
24
                    && instruction.Operands[0] is LocalVariable dest
14✔
25
                    && !parameterLocals.Contains(dest)
14✔
26
                    && IsForwardable(instruction.Operands[1]))
14✔
27
                    forwarded[dest] = instruction.Operands[1];
4✔
28

29
        if (forwarded.Count == 0)
4✔
30
            return;
1✔
31

32
        // Collapse copy chains (t1 := a; t2 := t1; ...) so each local maps straight to its final value.
33
        var resolved = new Dictionary<LocalVariable, object>();
3✔
34
        foreach (var dest in forwarded.Keys)
14✔
35
            resolved[dest] = Resolve(dest, forwarded);
4✔
36

37
        // One global substitution of every use.
38
        foreach (var block in cfg.Blocks)
30✔
39
            foreach (var instruction in block.Instructions)
46✔
40
                ReplaceUses(instruction, resolved);
11✔
41

42
        // A forwarded local is dead now - unless a use could not take its value (a constant cannot be
43
        // a memory base/index, so such a use keeps the original local). Drop the defining Move only
44
        // once the local truly has no reads left; the leftover nops are cleared by SsaForm.Remove.
45
        var reads = CollectReadLocals(cfg);
3✔
46
        foreach (var block in cfg.Blocks)
30✔
47
            foreach (var instruction in block.Instructions)
46✔
48
                if (instruction.OpCode == OpCode.Move
11✔
49
                    && instruction.Operands[0] is LocalVariable dest
11✔
50
                    && forwarded.ContainsKey(dest)
11✔
51
                    && !reads.Contains(dest))
11✔
52
                {
53
                    instruction.OpCode = OpCode.Nop;
4✔
54
                    instruction.Operands = [];
4✔
55
                }
56
    }
3✔
57

58
    // Follows local-to-local copies to the end of the chain. The visited set guards against a cycle a
59
    // malformed graph could present; a well-formed SSA graph (definitions dominate uses) has none.
60
    private static object Resolve(LocalVariable dest, Dictionary<LocalVariable, object> forwarded)
61
    {
62
        var value = forwarded[dest];
4✔
63
        var visited = new HashSet<LocalVariable> { dest };
4✔
64

65
        while (value is LocalVariable next && forwarded.TryGetValue(next, out var nextValue) && visited.Add(next))
5✔
66
            value = nextValue;
1✔
67

68
        return value;
4✔
69
    }
70

71
    private static void ReplaceUses(Instruction instruction, Dictionary<LocalVariable, object> resolved)
72
    {
73
        // The single definition position (a Move/Call destination local) must not be rewritten - only
74
        // reads are forwarded. In SSA the local being eliminated never appears as a use of itself, so
75
        // skipping just its own definition operand is sufficient.
76
        var destination = instruction.Destination;
11✔
77

78
        for (var i = 0; i < instruction.Operands.Count; i++)
60✔
79
        {
80
            switch (instruction.Operands[i])
19!
81
            {
82
                case LocalVariable local when !ReferenceEquals(local, destination) && resolved.TryGetValue(local, out var value):
11✔
83
                    instruction.Operands[i] = value;
3✔
84
                    break;
3✔
85

86
                // A memory base/index must stay an address-holding local, so only a local replacement
87
                // is substituted there; a constant is left in place (which keeps the source Move alive).
88
                case MemoryOperand memory:
89
                    if (memory.Base is LocalVariable baseLocal && resolved.TryGetValue(baseLocal, out var baseValue) && baseValue is LocalVariable baseReplacement)
1!
90
                        memory.Base = baseReplacement;
1✔
91
                    if (memory.Index is LocalVariable indexLocal && resolved.TryGetValue(indexLocal, out var indexValue) && indexValue is LocalVariable indexReplacement)
1!
92
                        memory.Index = indexReplacement;
×
93
                    instruction.Operands[i] = memory; // MemoryOperand is a struct, write the copy back
1✔
94
                    break;
1✔
95

96
                // Same as a memory base: the object a field is read from must stay a local.
NEW
97
                case FieldReference { Local: { } fieldLocal } field when resolved.TryGetValue(fieldLocal, out var fieldValue) && fieldValue is LocalVariable fieldReplacement:
×
NEW
98
                    field.Local = fieldReplacement;
×
99
                    break;
100
            }
101
        }
102
    }
11✔
103

104
    // Every local read by some instruction. The single write position (a plain local destination) is
105
    // excluded; memory and field operands always contribute their address/object locals as reads.
106
    private static HashSet<LocalVariable> CollectReadLocals(ISILControlFlowGraph cfg)
107
    {
108
        var reads = new HashSet<LocalVariable>();
3✔
109

110
        foreach (var block in cfg.Blocks)
30✔
111
            foreach (var instruction in block.Instructions)
46✔
112
            {
113
                var destination = instruction.Destination;
11✔
114

115
                foreach (var operand in instruction.Operands)
60✔
116
                {
117
                    switch (operand)
19!
118
                    {
119
                        case LocalVariable local when !ReferenceEquals(local, destination):
9✔
120
                            reads.Add(local);
4✔
121
                            break;
4✔
122
                        case MemoryOperand memory:
123
                            if (memory.Base is LocalVariable baseLocal)
1✔
124
                                reads.Add(baseLocal);
1✔
125
                            if (memory.Index is LocalVariable indexLocal)
1!
126
                                reads.Add(indexLocal);
×
127
                            break;
×
128
                        case FieldReference field when field.Local is { } fieldLocal:
×
129
                            reads.Add(fieldLocal);
×
130
                            break;
131
                    }
132
                }
133
            }
134

135
        return reads;
3✔
136
    }
137

138
    // Pure values that are safe to duplicate across uses: other locals (copies) and constants. Memory
139
    // and field loads are excluded so a load is never re-executed; they are handled post-SSA instead.
140
    private static bool IsForwardable(object value) =>
141
        value switch
6!
142
        {
6✔
143
            LocalVariable => true,
3✔
144
            MemoryOperand => false,
2✔
145
            FieldReference => false,
×
146
            _ => true
1✔
147
        };
6✔
148
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc