• 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.87
/Cpp2IL.Core/Analysis/DeadCodeEliminator.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
/// Removes pure instructions whose result is never used. This eliminates, among other things, the
10
/// dead flag/temporary computations the x86 lifter emits eagerly for every comparison - a single
11
/// <c>cmp</c>/<c>test</c> produces all of CF/OF/SF/ZF/PF plus scratch temporaries, but the branch
12
/// that follows only consumes one of them.
13
///
14
/// Must run while the graph is still in SSA form (every local is assigned exactly once), so that a
15
/// global use count of zero is sufficient to prove a definition dead. Instructions are turned into
16
/// nops rather than spliced out; the structural cleanup happens later, out of SSA, where it is safe
17
/// for phi nodes.
18
/// </summary>
19
public static class DeadCodeEliminator
20
{
21
    public static void Run(MethodAnalysisContext method) => Run(method.ControlFlowGraph!);
×
22

23
    public static void Run(ISILControlFlowGraph cfg)
24
    {
25
        // Removing a dead definition can make its operands dead in turn, so iterate to a fixpoint.
26
        // This is monotonic (each pass only nops instructions) and therefore always terminates.
27
        var changed = true;
3✔
28
        while (changed)
9✔
29
        {
30
            changed = false;
6✔
31

32
            var useCounts = CountUses(cfg);
6✔
33

34
            foreach (var block in cfg.Blocks)
50✔
35
            {
36
                foreach (var instruction in block.Instructions)
78✔
37
                {
38
                    if (!IsRemovable(instruction.OpCode))
20✔
39
                        continue;
40

41
                    // Only definitions of a register local are candidates. Stores have a memory or
42
                    // field destination (Destination is not a local) and are never dead.
43
                    if (instruction.Destination is not LocalVariable destination)
9✔
44
                        continue;
45

46
                    if (useCounts.TryGetValue(destination, out var count) && count > 0)
9✔
47
                        continue;
48

49
                    instruction.OpCode = OpCode.Nop;
3✔
50
                    instruction.Operands = [];
3✔
51
                    changed = true;
3✔
52
                }
53
            }
54
        }
55
    }
3✔
56

57
    private static Dictionary<LocalVariable, int> CountUses(ISILControlFlowGraph cfg)
58
    {
59
        var counts = new Dictionary<LocalVariable, int>();
6✔
60

61
        foreach (var block in cfg.Blocks)
50✔
62
            foreach (var instruction in block.Instructions)
78✔
63
                foreach (var used in UsedLocals(instruction))
58✔
64
                    counts[used] = counts.TryGetValue(used, out var c) ? c + 1 : 1;
9✔
65

66
        return counts;
6✔
67
    }
68

69
    /// <summary>
70
    /// Every local read by the instruction. The single write position - a plain local destination -
71
    /// is excluded. Memory and field operands always contribute their address/object locals as
72
    /// reads, even when they are the destination of a store.
73
    /// </summary>
74
    private static IEnumerable<LocalVariable> UsedLocals(Instruction instruction)
75
    {
76
        var destination = instruction.Destination as LocalVariable;
20✔
77

78
        foreach (var operand in instruction.Operands)
96✔
79
        {
80
            switch (operand)
28!
81
            {
82
                case LocalVariable local when !ReferenceEquals(local, destination):
18✔
83
                    yield return local;
9✔
84
                    break;
9✔
85
                case MemoryOperand memory:
86
                    if (memory.Base is LocalVariable baseLocal)
×
87
                        yield return baseLocal;
×
88
                    if (memory.Index is LocalVariable indexLocal)
×
89
                        yield return indexLocal;
×
90
                    break;
×
91
                // A static field access doesn't read the storage pointer it was resolved from, so that
92
                // pointer (and the class load feeding it) is free to die.
93
                case FieldReference { Field.IsStatic: false, Local: { } fieldLocal }:
UNCOV
94
                    yield return fieldLocal;
×
95
                    break;
96
            }
97
        }
98
    }
20✔
99

100
    /// <summary>
101
    /// Opcodes with no side effects, so removing a never-read result is safe. Calls, stores,
102
    /// returns and branches are intentionally excluded.
103
    /// </summary>
104
    private static bool IsRemovable(OpCode opCode) =>
105
        opCode switch
20✔
106
        {
20✔
107
            OpCode.Move or OpCode.Phi
20✔
108
                or OpCode.Add or OpCode.Subtract or OpCode.Multiply or OpCode.Divide
20✔
109
                or OpCode.ShiftLeft or OpCode.ShiftRight
20✔
110
                or OpCode.And or OpCode.Or or OpCode.Xor
20✔
111
                or OpCode.Not or OpCode.Negate=> true,
8✔
112
            >= OpCode.CheckEqual and <= OpCode.CheckLessOrEqual => true,
1✔
113
            _ => false
11✔
114
        };
20✔
115
}
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