Skip to content

Commit

Permalink
add feature as a standalone optimizer
Browse files Browse the repository at this point in the history
  • Loading branch information
Hecate2 committed Sep 6, 2024
1 parent 6039fc5 commit 31fadad
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,7 @@ internal void Compile()
try
{
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
(nef, manifest, debugInfo) = Reachability.RemoveUncoveredInstructions(nef, manifest, debugInfo.Clone() as JObject);
(nef, manifest, debugInfo) = Reachability.RemoveUnnecessaryJumps(nef, manifest, debugInfo!.Clone() as JObject);
(nef, manifest, debugInfo) = Neo.Optimizer.Optimizer.Optimize(nef, manifest, debugInfo: debugInfo!, optimizationType: Options.Optimize);
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
}
catch (Exception ex)
Expand Down
37 changes: 37 additions & 0 deletions src/Neo.Compiler.CSharp/Optimizer/DumpNef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Text.RegularExpressions;

namespace Neo.Optimizer
Expand All @@ -32,6 +34,41 @@ static class DumpNef
static readonly Lazy<IReadOnlyDictionary<uint, string>> sysCallNames = new(
() => ApplicationEngine.Services.ToImmutableDictionary(kvp => kvp.Value.Hash, kvp => kvp.Value.Name));

public static byte[] ZipDebugInfo(byte[] content, string innerFilename)
{
using (var compressedFileStream = new MemoryStream())
{
using (var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Update, false))
{
var zipEntry = zipArchive.CreateEntry(innerFilename);
using (var originalFileStream = new MemoryStream(content))
{
using (var zipEntryStream = zipEntry.Open())
{
originalFileStream.CopyTo(zipEntryStream);
}
}
}
return compressedFileStream.ToArray();
}
}

public static string UnzipDebugInfo(byte[] zippedBuffer)
{
using var zippedStream = new MemoryStream(zippedBuffer);
using var archive = new ZipArchive(zippedStream, ZipArchiveMode.Read, false, Encoding.UTF8);
var entry = archive.Entries.FirstOrDefault();
if (entry != null)
{
using var unzippedEntryStream = entry.Open();
using var ms = new MemoryStream();
unzippedEntryStream.CopyTo(ms);
var unzippedArray = ms.ToArray();
return Encoding.UTF8.GetString(unzippedArray);
}
throw new ArgumentException("No file found in zip archive");
}

public static string GetInstructionAddressPadding(this Script script)
{
var digitCount = EnumerateInstructions(script).Last().address switch
Expand Down
20 changes: 19 additions & 1 deletion src/Neo.Compiler.CSharp/Optimizer/Strategies/Optimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.Compiler;
using Neo.Json;
using Neo.SmartContract;
using Neo.SmartContract.Manifest;
Expand All @@ -19,7 +20,7 @@

namespace Neo.Optimizer
{
class Optimizer
public static class Optimizer
{
public static readonly int[] OperandSizePrefixTable = new int[256];
public static readonly int[] OperandSizeTable = new int[256];
Expand Down Expand Up @@ -50,5 +51,22 @@ public static void RegisterStrategies(Type type)
strategies[name] = method.CreateDelegate<Func<NefFile, ContractManifest, JObject, (NefFile nef, ContractManifest manifest, JObject debugInfo)>>();
}
}

public static (NefFile, ContractManifest, JObject?) Optimize(NefFile nef, ContractManifest manifest, JObject? debugInfo = null, CompilationOptions.OptimizationType optimizationType = CompilationOptions.OptimizationType.All)
{
// Define the optimization type inside the manifest
if (optimizationType != CompilationOptions.OptimizationType.None)
{
manifest.Extra ??= new JObject();
manifest.Extra["nef"] = new JObject();
manifest.Extra["nef"]!["optimization"] = optimizationType.ToString();
}
if (!optimizationType.HasFlag(CompilationOptions.OptimizationType.Experimental))
return (nef, manifest, debugInfo); // do nothing
// TODO in the future: optimize by StrategyAttribute in a loop
(nef, manifest, debugInfo) = Reachability.RemoveUncoveredInstructions(nef, manifest, debugInfo?.Clone() as JObject);
(nef, manifest, debugInfo) = Reachability.RemoveUnnecessaryJumps(nef, manifest, debugInfo?.Clone() as JObject);
return (nef, manifest, debugInfo);
}
}
}
36 changes: 35 additions & 1 deletion src/Neo.Compiler.CSharp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,41 @@ private static void Handle(RootCommand command, Options options, string[]? paths
}
foreach (string path in paths)
{
if (Path.GetExtension(path).ToLowerInvariant() != ".cs")
string extension = Path.GetExtension(path).ToLowerInvariant();
if (extension == ".nef")
{
string directory = Path.GetDirectoryName(path)!;
string filename = Path.GetFileNameWithoutExtension(path)!;
Console.WriteLine($"Optimizing {filename}.nef to {filename}.optimized.nef...");
NefFile nef = NefFile.Parse(File.ReadAllBytes(path));
string manifestPath = Path.Join(directory, filename + ".manifest.json");
if (!File.Exists(manifestPath))
throw new FileNotFoundException($"{filename}.manifest.json required for optimization");
ContractManifest manifest = ContractManifest.Parse(File.ReadAllText(manifestPath));
string debugInfoPath = Path.Join(directory, filename + ".nefdbgnfo");
JObject? debugInfo;
if (File.Exists(debugInfoPath))
debugInfo = (JObject?)JObject.Parse(DumpNef.UnzipDebugInfo(File.ReadAllBytes(debugInfoPath)));
else
debugInfo = null;
(nef, manifest, debugInfo) = Neo.Optimizer.Optimizer.Optimize(nef, manifest, debugInfo, optimizationType: CompilationOptions.OptimizationType.All);
// Define the optimization type inside the manifest
manifest.Extra ??= new JObject();
manifest.Extra["nef"] = new JObject();
manifest.Extra["nef"]!["optimization"] = CompilationOptions.OptimizationType.All.ToString();
File.WriteAllBytes(Path.Combine(directory, filename + ".optimized.nef"), nef.ToArray());
File.WriteAllBytes(Path.Combine(directory, filename + ".optimized.manifest.json"), manifest.ToJson().ToByteArray(true));
if (options.Assembly)
{
string dumpnef = DumpNef.GenerateDumpNef(nef, debugInfo);
File.WriteAllText(Path.Combine(directory, filename + ".optimized.nef.txt"), dumpnef);
}
if (debugInfo != null)
File.WriteAllBytes(Path.Combine(directory, filename + ".optimized.nefdbgnfo"), DumpNef.ZipDebugInfo(debugInfo.ToByteArray(true), filename + ".optimized.debug.json"));
Console.WriteLine($"Optimization finished.");
return;
}
if (extension != ".cs")
{
Console.Error.WriteLine("The files must have a .cs extension.");
context.ExitCode = 1;
Expand Down

0 comments on commit 31fadad

Please sign in to comment.