Skip to content

Commit b902414

Browse files
Hecate2Jim8yshargon
authored
More fixes on optimizer (#911)
* fix try if * safer dumpnef * no ABORT in try with catch & catch with finally * safer debuginfo changes; nef & manifest UNHANDLED * format * cover catch before any return of branch type --------- Co-authored-by: Jimmy <jimmy@r3e.network> Co-authored-by: Shargon <shargon@gmail.com>
1 parent 0d466bb commit b902414

File tree

3 files changed

+100
-47
lines changed

3 files changed

+100
-47
lines changed

src/Neo.Compiler.CSharp/Optimizer/DumpNef.cs

+27-22
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ public static string GenerateDumpNef(NefFile nef, JToken debugInfo)
183183
addressToInstruction.Add(a, i);
184184
Dictionary<int, string> methodStartAddrToName = new();
185185
Dictionary<int, string> methodEndAddrToName = new();
186-
Dictionary<int, (int docId, int startLine, int startCol, int endLine, int endCol)> newAddrToSequencePoint = new();
186+
Dictionary<int, List<(int docId, int startLine, int startCol, int endLine, int endCol)>> newAddrToSequencePoint = new();
187187

188188
foreach (JToken? method in (JArray)debugInfo["methods"]!)
189189
{
@@ -196,7 +196,10 @@ public static string GenerateDumpNef(NefFile nef, JToken debugInfo)
196196
{
197197
GroupCollection sequencePointGroups = SequencePointRegex.Match(sequencePoint!.AsString()).Groups;
198198
GroupCollection documentGroups = DocumentRegex.Match(sequencePointGroups[2].ToString()).Groups;
199-
newAddrToSequencePoint.Add(int.Parse(sequencePointGroups[1].Value), (
199+
int addr = int.Parse(sequencePointGroups[1].Value);
200+
if (!newAddrToSequencePoint.ContainsKey(addr))
201+
newAddrToSequencePoint.Add(addr, new());
202+
newAddrToSequencePoint[addr].Add((
200203
int.Parse(documentGroups[1].ToString()),
201204
int.Parse(documentGroups[2].ToString()),
202205
int.Parse(documentGroups[3].ToString()),
@@ -216,26 +219,28 @@ public static string GenerateDumpNef(NefFile nef, JToken debugInfo)
216219
dumpnef += $"# Method End {methodEndAddrToName[a]}\n";
217220
if (newAddrToSequencePoint.ContainsKey(a))
218221
{
219-
var (docId, startLine, startCol, endLine, endCol) = newAddrToSequencePoint[a];
220-
string docPath = debugInfo["documents"]![docId]!.AsString();
221-
if (debugInfo["document-root"] != null)
222-
docPath = Path.Combine(debugInfo["document-root"]!.AsString(), docPath);
223-
if (!docPathToContent.ContainsKey(docPath))
224-
docPathToContent.Add(docPath, File.ReadAllLines(docPath).ToArray());
225-
if (startLine == endLine)
226-
dumpnef += $"# Code {Path.GetFileName(docPath)} line {startLine}: \"{docPathToContent[docPath][startLine - 1][(startCol - 1)..(endCol - 1)]}\"\n";
227-
else
228-
for (int lineIndex = startLine; lineIndex <= endLine; lineIndex++)
229-
{
230-
string src;
231-
if (lineIndex == startLine)
232-
src = docPathToContent[docPath][lineIndex - 1][(startCol - 1)..].Trim();
233-
else if (lineIndex == endLine)
234-
src = docPathToContent[docPath][lineIndex - 1][..(endCol - 1)].Trim();
235-
else
236-
src = docPathToContent[docPath][lineIndex - 1].Trim();
237-
dumpnef += $"# Code {Path.GetFileName(docPath)} line {startLine}: \"{src}\"\n";
238-
}
222+
foreach ((int docId, int startLine, int startCol, int endLine, int endCol) in newAddrToSequencePoint[a])
223+
{
224+
string docPath = debugInfo["documents"]![docId]!.AsString();
225+
if (debugInfo["document-root"] != null)
226+
docPath = Path.Combine(debugInfo["document-root"]!.AsString(), docPath);
227+
if (!docPathToContent.ContainsKey(docPath))
228+
docPathToContent.Add(docPath, File.ReadAllLines(docPath).ToArray());
229+
if (startLine == endLine)
230+
dumpnef += $"# Code {Path.GetFileName(docPath)} line {startLine}: \"{docPathToContent[docPath][startLine - 1][(startCol - 1)..(endCol - 1)]}\"\n";
231+
else
232+
for (int lineIndex = startLine; lineIndex <= endLine; lineIndex++)
233+
{
234+
string src;
235+
if (lineIndex == startLine)
236+
src = docPathToContent[docPath][lineIndex - 1][(startCol - 1)..].Trim();
237+
else if (lineIndex == endLine)
238+
src = docPathToContent[docPath][lineIndex - 1][..(endCol - 1)].Trim();
239+
else
240+
src = docPathToContent[docPath][lineIndex - 1].Trim();
241+
dumpnef += $"# Code {Path.GetFileName(docPath)} line {startLine}: \"{src}\"\n";
242+
}
243+
}
239244
}
240245
if (a < script.Length)
241246
dumpnef += $"{WriteInstruction(a, script.GetInstruction(a), script.GetInstructionAddressPadding(), nef.Tokens)}\n";

src/Neo.Compiler.CSharp/Optimizer/Strategies/Reachability.cs

+62-21
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,7 @@ public static (NefFile, ContractManifest, JToken) RemoveUncoveredInstructions(Ne
106106
nef.Compiler = AppDomain.CurrentDomain.FriendlyName;
107107
nef.CheckSum = NefFile.ComputeChecksum(nef);
108108

109-
Dictionary<int, (int docId, int startLine, int startCol, int endLine, int endCol)> newAddrToSequencePoint = new();
110-
Dictionary<int, string> newMethodStart = new();
111-
Dictionary<int, string> newMethodEnd = new();
109+
//Dictionary<int, (int docId, int startLine, int startCol, int endLine, int endCol)> newAddrToSequencePoint = new();
112110
HashSet<JToken> methodsToRemove = new();
113111
foreach (JToken? method in (JArray)debugInfo["methods"]!)
114112
{
@@ -121,8 +119,6 @@ public static (NefFile, ContractManifest, JToken) RemoveUncoveredInstructions(Ne
121119
}
122120
int methodStart = (int)simplifiedInstructionsToAddress[oldAddressToInstruction[oldMethodStart]]!;
123121
int methodEnd = (int)simplifiedInstructionsToAddress[oldAddressToInstruction[oldMethodEnd]]!;
124-
newMethodStart.Add(methodStart, method["id"]!.AsString()); // TODO: same format of method name as dumpnef
125-
newMethodEnd.Add(methodEnd, method["id"]!.AsString());
126122
method["range"] = $"{methodStart}-{methodEnd}";
127123

128124
int previousSequencePoint = methodStart;
@@ -140,14 +136,6 @@ public static (NefFile, ContractManifest, JToken) RemoveUncoveredInstructions(Ne
140136
}
141137
else
142138
newSequencePoints.Add(new JString($"{previousSequencePoint}{sequencePointGroups[2]}"));
143-
GroupCollection documentGroups = DocumentRegex.Match(sequencePointGroups[2].ToString()).Groups;
144-
newAddrToSequencePoint.Add(previousSequencePoint, (
145-
int.Parse(documentGroups[1].ToString()),
146-
int.Parse(documentGroups[2].ToString()),
147-
int.Parse(documentGroups[3].ToString()),
148-
int.Parse(documentGroups[4].ToString()),
149-
int.Parse(documentGroups[5].ToString())
150-
));
151139
}
152140
method["sequence-points"] = newSequencePoints;
153141
}
@@ -173,6 +161,8 @@ public static Dictionary<int, bool>
173161
Parallel.ForEach(manifest.Abi.Methods, method =>
174162
CoverInstruction(method.Offset, script, coveredMap)
175163
);
164+
//foreach (ContractMethodDescriptor method in manifest.Abi.Methods)
165+
// CoverInstruction(method.Offset, script, coveredMap);
176166
// start from _deploy method
177167
foreach (JToken? method in (JArray)debugInfo["methods"]!)
178168
{
@@ -249,7 +239,17 @@ public static BranchType CoverInstruction(int addr, Script script, Dictionary<in
249239

250240
// TODO: ABORTMSG may THROW instead of ABORT. Just throw new NotImplementedException for ABORTMSG?
251241
if (instruction.OpCode == OpCode.ABORT || instruction.OpCode == OpCode.ABORTMSG)
242+
{
243+
// See if we are in a try. There may still be runtime exceptions
244+
((catchAddr, finallyAddr), stackType) = stack.Peek();
245+
if (stackType == TryStack.TRY && catchAddr != -1)
246+
// Visit catchAddr because there may still be exceptions at runtime
247+
return CoverInstruction(catchAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
248+
if (stackType == TryStack.CATCH && finallyAddr != -1)
249+
// Visit finallyAddr because there may still be exceptions at runtime
250+
return CoverInstruction(finallyAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
252251
return BranchType.ABORT;
252+
}
253253
if (callWithJump.Contains(instruction.OpCode))
254254
{
255255
int callTarget = ComputeJumpTarget(addr, instruction);
@@ -260,12 +260,32 @@ public static BranchType CoverInstruction(int addr, Script script, Dictionary<in
260260
continue;
261261
}
262262
if (returnedType == BranchType.ABORT)
263+
{
264+
// See if we are in a try. There may still be runtime exceptions
265+
((catchAddr, finallyAddr), stackType) = stack.Peek();
266+
if (stackType == TryStack.TRY && catchAddr != -1)
267+
// Visit catchAddr because there may still be exceptions at runtime
268+
return CoverInstruction(catchAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
269+
if (stackType == TryStack.CATCH && finallyAddr != -1)
270+
// Visit finallyAddr because there may still be exceptions at runtime
271+
return CoverInstruction(finallyAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
263272
return BranchType.ABORT;
273+
}
264274
if (returnedType == BranchType.THROW)
265275
goto HANDLE_THROW;
266276
}
267277
if (instruction.OpCode == OpCode.RET)
278+
{
279+
// See if we are in a try. There may still be runtime exceptions
280+
((catchAddr, finallyAddr), stackType) = stack.Peek();
281+
if (stackType == TryStack.TRY && catchAddr != -1)
282+
// Visit catchAddr because there may still be exceptions at runtime
283+
CoverInstruction(catchAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
284+
if (stackType == TryStack.CATCH && finallyAddr != -1)
285+
// Visit finallyAddr because there may still be exceptions at runtime
286+
CoverInstruction(finallyAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
268287
return BranchType.OK;
288+
}
269289
if (tryThrowFinally.Contains(instruction.OpCode))
270290
{
271291
if (instruction.OpCode == OpCode.TRY || instruction.OpCode == OpCode.TRY_L)
@@ -275,14 +295,15 @@ public static BranchType CoverInstruction(int addr, Script script, Dictionary<in
275295
if (instruction.OpCode == OpCode.ENDTRY)
276296
{
277297
((catchAddr, finallyAddr), stackType) = stack.Peek();
278-
if (stackType != TryStack.TRY && stackType != TryStack.CATCH) throw new BadScriptException("No try stack on ENDTRY");
298+
if (stackType != TryStack.TRY && stackType != TryStack.CATCH)
299+
throw new BadScriptException("No try stack on ENDTRY");
279300

280-
if (stackType == TryStack.TRY)
281-
{
301+
if (stackType == TryStack.TRY && catchAddr != -1)
282302
// Visit catchAddr because there may still be exceptions at runtime
283-
Stack<((int returnAddr, int finallyAddr), TryStack stackType)> newStack = new(stack);
284-
CoverInstruction(catchAddr, script, coveredMap, stack: newStack, throwed: true);
285-
}
303+
CoverInstruction(catchAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
304+
if (stackType == TryStack.CATCH && finallyAddr != -1)
305+
// Visit finallyAddr because there may still be exceptions at runtime
306+
CoverInstruction(finallyAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
286307

287308
stack.Pop();
288309
int endPointer = addr + instruction.TokenI8;
@@ -325,12 +346,32 @@ public static BranchType CoverInstruction(int addr, Script script, Dictionary<in
325346
if (conditionalJump.Contains(instruction.OpCode) || conditionalJump_L.Contains(instruction.OpCode))
326347
{
327348
int targetAddress = ComputeJumpTarget(addr, instruction);
328-
BranchType noJump = CoverInstruction(addr + instruction.Size, script, coveredMap);
329-
BranchType jump = CoverInstruction(targetAddress, script, coveredMap);
349+
BranchType noJump = CoverInstruction(addr + instruction.Size, script, coveredMap, stack: new(stack.Reverse()));
350+
BranchType jump = CoverInstruction(targetAddress, script, coveredMap, stack: new(stack.Reverse()));
330351
if (noJump == BranchType.OK || jump == BranchType.OK)
352+
{
353+
// See if we are in a try. There may still be runtime exceptions
354+
((catchAddr, finallyAddr), stackType) = stack.Peek();
355+
if (stackType == TryStack.TRY && catchAddr != -1)
356+
// Visit catchAddr because there may still be exceptions at runtime
357+
CoverInstruction(catchAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
358+
if (stackType == TryStack.CATCH && finallyAddr != -1)
359+
// Visit finallyAddr because there may still be exceptions at runtime
360+
CoverInstruction(finallyAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
331361
return BranchType.OK;
362+
}
332363
if (noJump == BranchType.ABORT && jump == BranchType.ABORT)
364+
{
365+
// See if we are in a try. There may still be runtime exceptions
366+
((catchAddr, finallyAddr), stackType) = stack.Peek();
367+
if (stackType == TryStack.TRY && catchAddr != -1)
368+
// Visit catchAddr because there may still be exceptions at runtime
369+
return CoverInstruction(catchAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
370+
if (stackType == TryStack.CATCH && finallyAddr != -1)
371+
// Visit finallyAddr because there may still be exceptions at runtime
372+
return CoverInstruction(finallyAddr, script, coveredMap, stack: new(stack.Reverse()), throwed: true);
333373
return BranchType.ABORT;
374+
}
334375
if (noJump == BranchType.THROW || jump == BranchType.THROW) // THROW, ABORT => THROW
335376
goto HANDLE_THROW;
336377
throw new Exception($"Unknown {nameof(BranchType)} {noJump} {jump}");

src/Neo.Compiler.CSharp/Program.cs

+11-4
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ private static int ProcessOutputs(Options options, string folder, CompilationCon
157157
{
158158
try
159159
{
160-
(nef, manifest, debugInfo) = Reachability.RemoveUncoveredInstructions(nef, manifest, debugInfo);
160+
(nef, manifest, debugInfo) = Reachability.RemoveUncoveredInstructions(nef, manifest, debugInfo.Clone());
161161
}
162162
catch (Exception ex)
163163
{
@@ -272,9 +272,16 @@ private static int ProcessOutputs(Options options, string folder, CompilationCon
272272
path = Path.Combine(outputFolder, $"{baseName}.asm");
273273
File.WriteAllText(path, context.CreateAssembly());
274274
Console.WriteLine($"Created {path}");
275-
path = Path.Combine(outputFolder, $"{baseName}.nef.txt");
276-
File.WriteAllText(path, DumpNef.GenerateDumpNef(nef, debugInfo));
277-
Console.WriteLine($"Created {path}");
275+
try
276+
{
277+
path = Path.Combine(outputFolder, $"{baseName}.nef.txt");
278+
File.WriteAllText(path, DumpNef.GenerateDumpNef(nef, debugInfo));
279+
Console.WriteLine($"Created {path}");
280+
}
281+
catch (Exception ex)
282+
{
283+
Console.Error.WriteLine($"Failed to dumpnef: {ex}");
284+
}
278285
}
279286
Console.WriteLine("Compilation completed successfully.");
280287
return 0;

0 commit comments

Comments
 (0)