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

SamboyCoding / Cpp2IL / 17101091060

20 Aug 2025 02:16PM UTC coverage: 30.421% (-3.9%) from 34.352%
17101091060

Pull #481

github

web-flow
Merge 42542dde2 into d5260685f
Pull Request #481: Decompiler

1804 of 7551 branches covered (23.89%)

Branch coverage included in aggregate %.

102 of 1827 new or added lines in 35 files covered. (5.58%)

40 existing lines in 6 files now uncovered.

4095 of 11840 relevant lines covered (34.59%)

165714.86 hits per line

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

0.0
/Cpp2IL.Core/Actions/Inlining.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.Actions;
7

8
public class Inlining : IAction
9
{
10
    public void Apply(MethodAnalysisContext method)
11
    {
NEW
12
        var cfg = method.ControlFlowGraph!;
×
13

NEW
14
        InlineLocals(method);
×
15

16
        // Repeat until no change
NEW
17
        var changed = true;
×
NEW
18
        while (changed)
×
NEW
19
            changed = InlineConstantsSinglePass(cfg);
×
20

21
        // More locals can now be inlined
NEW
22
        InlineLocals(method);
×
23

NEW
24
        cfg.RemoveNops();
×
NEW
25
        cfg.RemoveEmptyBlocks();
×
NEW
26
    }
×
27

28
    private static bool InlineConstantsSinglePass(ISILControlFlowGraph graph)
29
    {
NEW
30
        var changed = false;
×
31

NEW
32
        var visited = new HashSet<Block>();
×
NEW
33
        var queue = new Queue<Block>();
×
34

NEW
35
        queue.Enqueue(graph.EntryBlock);
×
NEW
36
        visited.Add(graph.EntryBlock);
×
37

NEW
38
        while (queue.Count > 0)
×
39
        {
NEW
40
            var block = queue.Dequeue();
×
41

NEW
42
            for (var i = 0; i < block.Instructions.Count; i++)
×
43
            {
NEW
44
                var instruction = block.Instructions[i];
×
45

46
                // If it's move and it moves something to local, replace and remove it
NEW
47
                if (instruction.OpCode == OpCode.Move && instruction.Operands[0] is LocalVariable local)
×
48
                {
NEW
49
                    if (IsLocalUsedAfterInstruction(block, i + 1, local, out var usedByMemory))
×
50
                    {
51
                        // This can't be inlined into memory operand
NEW
52
                        if (usedByMemory) continue;
×
53

54
                        // Replace local
NEW
55
                        ReplaceLocalsUntilReassignment(block, i + 1, local, instruction.Operands[1]);
×
56

57
                        // Change that move to nop
NEW
58
                        instruction.OpCode = OpCode.Nop;
×
NEW
59
                        instruction.Operands = [];
×
60

NEW
61
                        changed = true;
×
62
                    }
63
                }
64
            }
65

NEW
66
            foreach (var successor in block.Successors)
×
67
            {
NEW
68
                if (visited.Add(successor))
×
NEW
69
                    queue.Enqueue(successor);
×
70
            }
71
        }
72

NEW
73
        return changed;
×
74
    }
75

76
    private static void InlineLocals(MethodAnalysisContext method)
77
    {
NEW
78
        var graph = method.ControlFlowGraph;
×
79

NEW
80
        var visited = new HashSet<Block>();
×
NEW
81
        var queue = new Queue<Block>();
×
82

NEW
83
        queue.Enqueue(graph!.EntryBlock);
×
NEW
84
        visited.Add(graph.EntryBlock);
×
85

NEW
86
        while (queue.Count > 0)
×
87
        {
NEW
88
            var block = queue.Dequeue();
×
89

NEW
90
            for (var i = 0; i < block.Instructions.Count; i++)
×
91
            {
NEW
92
                var instruction = block.Instructions[i];
×
93

94
                // If it's move and it moves local to local, replace and remove it
NEW
95
                if (instruction.OpCode == OpCode.Move && instruction.Operands[0] is LocalVariable local && instruction.Operands[1] is LocalVariable source)
×
96
                {
97
                    // Replace local with source
NEW
98
                    ReplaceLocalsUntilReassignment(block, i + 1, local, source);
×
99

NEW
100
                    if (!method.ParameterLocals.Contains(local))
×
NEW
101
                        method.Locals.Remove(local);
×
102

103
                    // Change that move to nop
NEW
104
                    instruction.OpCode = OpCode.Nop;
×
NEW
105
                    instruction.Operands = [];
×
106
                }
107
            }
108

NEW
109
            foreach (var successor in block.Successors)
×
110
            {
NEW
111
                if (visited.Add(successor))
×
NEW
112
                    queue.Enqueue(successor);
×
113
            }
114
        }
NEW
115
    }
×
116

117
    private static void ReplaceLocalsUntilReassignment(Block block, int startIndex, LocalVariable local, object replacement)
118
    {
NEW
119
        var visited = new HashSet<(Block, int)>();
×
120

121
        void ProcessBlock(Block currentBlock, int index)
122
        {
NEW
123
            var key = (currentBlock, index);
×
124

NEW
125
            if (!visited.Add(key))
×
NEW
126
                return;
×
127

128
            // Process instructions starting at the given index
NEW
129
            for (var i = index; i < currentBlock.Instructions.Count; i++)
×
130
            {
NEW
131
                var instruction = currentBlock.Instructions[i];
×
132

133
                // Stop on this branch when reassigned
NEW
134
                if (instruction.Destination is LocalVariable destLocal && destLocal == local)
×
NEW
135
                    return;
×
136

137
                // Replace operands
NEW
138
                for (var j = 0; j < instruction.Operands.Count; j++)
×
139
                {
NEW
140
                    var operand = instruction.Operands[j];
×
141

NEW
142
                    if (operand is LocalVariable usedLocal && usedLocal == local)
×
NEW
143
                        instruction.Operands[j] = replacement;
×
144

145
                    // [base]
NEW
146
                    if (operand is MemoryOperand { Index: null, Addend: 0, Scale: 0 } memoryLocal)
×
147
                    {
NEW
148
                        if (memoryLocal.Base is LocalVariable baseLocal && baseLocal == local)
×
NEW
149
                            instruction.Operands[j] = replacement;
×
150
                    }
151

NEW
152
                    if (operand is MemoryOperand memory)
×
153
                    {
154
                        // [addend]
NEW
155
                        if (memory.IsConstant && (replacement is MemoryOperand { IsConstant: true } replacementMemory))
×
NEW
156
                            memory.Addend = replacementMemory.Addend;
×
157

NEW
158
                        if (memory.Base is LocalVariable baseLocal && baseLocal == local)
×
NEW
159
                            memory.Base = replacement;
×
160

NEW
161
                        if (memory.Index is LocalVariable indexLocal && indexLocal == local)
×
NEW
162
                            memory.Index = replacement;
×
163
                    }
164
                }
165
            }
166

167
            // Process successors
NEW
168
            foreach (var successor in currentBlock.Successors)
×
169
            {
NEW
170
                ProcessBlock(successor, 0);
×
171
            }
NEW
172
        }
×
173

NEW
174
        ProcessBlock(block, startIndex);
×
NEW
175
    }
×
176

177
    private static bool IsLocalUsedAfterInstruction(Block block, int startIndex, LocalVariable local, out bool usedByMemory)
178
    {
NEW
179
        var visited = new HashSet<(Block, int)>();
×
180

181
        bool ProcessBlock(Block currentBlock, int index, out bool usedByMemory2)
182
        {
NEW
183
            usedByMemory2 = false;
×
184

NEW
185
            var key = (currentBlock, index);
×
186

NEW
187
            if (!visited.Add(key))
×
NEW
188
                return false;
×
189

190
            // Process instructions
NEW
191
            for (var i = index; i < currentBlock.Instructions.Count; i++)
×
192
            {
NEW
193
                var instruction = currentBlock.Instructions[i];
×
194

195
                // Direct usage check
NEW
196
                if (instruction.Sources.Contains(local))
×
NEW
197
                    return true;
×
198

199
                // Used in memory operand
NEW
200
                foreach (var source in instruction.Sources)
×
201
                {
NEW
202
                    if (source is MemoryOperand memory)
×
203
                    {
NEW
204
                        if (memory.Base is LocalVariable memLocal && memLocal == local)
×
205
                        {
NEW
206
                            usedByMemory2 = true;
×
NEW
207
                            return true;
×
208
                        }
209

NEW
210
                        if (memory.Index is LocalVariable memLocal2 && memLocal2 == local)
×
211
                        {
NEW
212
                            usedByMemory2 = true;
×
NEW
213
                            return true;
×
214
                        }
215
                    }
216
                }
217
            }
218

219
            // Process successors
NEW
220
            foreach (var successor in currentBlock.Successors)
×
221
            {
NEW
222
                if (ProcessBlock(successor, 0, out usedByMemory2))
×
NEW
223
                    return true;
×
224
            }
225

NEW
226
            return false;
×
NEW
227
        }
×
228

NEW
229
        return ProcessBlock(block, startIndex, out usedByMemory);
×
230
    }
231
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc