Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replace STSFLD with PUSH const #1203

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 58 additions & 3 deletions src/Neo.Compiler.CSharp/Optimizer/Analysers/BasicBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Neo.SmartContract;
using Neo.SmartContract.Manifest;
using Neo.VM;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
Expand Down Expand Up @@ -53,9 +54,16 @@ public BasicBlock(Dictionary<int, Instruction> instructions)
this.instructions = addrToInstructions.Select(kv => kv.i).ToList();
}

//public void SetNextBasicBlock(BasicBlock block) => this.nextBlock = block;
//public void SetJumpTargetBlock1(BasicBlock block) => this.jumpTargetBlock1 = block;
//public void SetJumpTargetBlock2(BasicBlock block) => this.jumpTargetBlock2 = block;
public int FindFirstOpCode(OpCode opCode, ReadOnlyMemory<byte>? operand = null)
{
int addr = this.startAddr;
foreach (Instruction i in this.instructions)
if (i.OpCode == opCode && (operand == null || operand.Equals(i.Operand)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no coverage for this method.

Also, maybe something like (!operand.HasValue || operand.Value.Span.SequenceEqual(i.Operand.Span))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I did plan to use this method but I found it meaningless later.

return addr;
else
addr += i.Size;
return -1;
}
}

/// <summary>
Expand Down Expand Up @@ -118,6 +126,53 @@ orderby kv.Key ascending
}
}

/// <summary>
/// Get the tree of basic blocks covered from the entryAddr.
/// </summary>
/// <param name="entryAddr">Entry address of a basic block</param>
/// <param name="includeCall">If true, calls to other basic blocks are included</param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public HashSet<BasicBlock> BlocksCoveredFromAddr(int entryAddr, bool includeCall = true)
{
if (!basicBlocksByStartAddr.TryGetValue(entryAddr, out BasicBlock? entryBlock))
throw new ArgumentException($"{nameof(entryAddr)} must be starting address of a basic block");
BasicBlock currentBlock = entryBlock;
HashSet<BasicBlock> returned = new() { currentBlock };
Queue<BasicBlock> queue = new Queue<BasicBlock>(returned);
while (queue.Count > 0)
{
currentBlock = queue.Dequeue();
if (currentBlock.nextBlock != null && returned.Add(currentBlock.nextBlock))
queue.Enqueue(currentBlock.nextBlock);
if (includeCall || !OpCodeTypes.callWithJump.Contains(currentBlock.instructions.Last().OpCode))
foreach (BasicBlock target in currentBlock.jumpTargetBlocks)
if (returned.Add(target))
queue.Enqueue(target);
}
return returned;
}

/// <summary>
/// Get the set of addresses covered by the input set of BasicBlocks
/// </summary>
/// <param name="blocks"></param>
/// <returns></returns>
public static HashSet<int> AddrCoveredByBlocks(IEnumerable<BasicBlock> blocks)
{
HashSet<int> result = new();
foreach (BasicBlock currentBlock in blocks)
{
int addr = currentBlock.startAddr;
foreach (Instruction i in currentBlock.instructions)
{
result.Add(addr);
addr += i.Size;
}
}
return result;
}

public IEnumerable<Instruction> GetScriptInstructions()
{
// WARNING: OpCode.NOP at the start of a basic block may not be included
Expand Down
162 changes: 86 additions & 76 deletions src/Neo.Compiler.CSharp/Optimizer/Analysers/InstructionCoverage.cs

Large diffs are not rendered by default.

64 changes: 48 additions & 16 deletions src/Neo.Compiler.CSharp/Optimizer/Analysers/OpCodeTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,54 @@ namespace Neo.Optimizer
static class OpCodeTypes
{
public static readonly HashSet<OpCode> push = new();
public static readonly HashSet<OpCode> pushConst = new();
public static readonly HashSet<OpCode> allowedBasicBlockEnds;

static OpCodeTypes()
{
foreach (OpCode op in pushInt)
push.Add(op);
foreach (OpCode op in pushBool)
push.Add(op);
push.Add(PUSHA);
push.Add(PUSHNULL);
foreach (OpCode op in pushData)
push.Add(op);
foreach (OpCode op in pushConstInt)
push.Add(op);
foreach (OpCode op in pushStackOps)
push.Add(op);
foreach (OpCode op in pushNewCompoundType)
push.Add(op);
pushConst = pushConst
.Union(pushInt)
.Union(pushBool)
.Union(pushData)
.Union(pushConstInt)
.Union(pushNewCompoundType)
.ToHashSet();
pushConst.Add(PUSHA);
pushConst.Add(PUSHNULL);

push = pushConst.Union(pushStackOps).ToHashSet();

allowedBasicBlockEnds = ((OpCode[])Enum.GetValues(typeof(OpCode)))
.Where(i => JumpTarget.SingleJumpInOperand(i) && i != PUSHA || JumpTarget.DoubleJumpInOperand(i)).ToHashSet()
.Union(new HashSet<OpCode>() { RET, ABORT, ABORTMSG, THROW, ENDFINALLY
}).ToHashSet();
.Union(new HashSet<OpCode>() { RET, ABORT, ABORTMSG, THROW, ENDFINALLY })
.ToHashSet();
}

public static byte SlotIndex(Instruction i)
{
OpCode o = i.OpCode;
if (slotNonConst.Contains(o))
return i.TokenU8;
return SlotIndex(o);
}

public static byte SlotIndex(OpCode o)
{
if (slotNonConst.Contains(o))
throw new ArgumentException($"Instruction is needed to get slot index for {o}");
if (loadArguments.Contains(o))
return o - LDARG0;
if (storeArguments.Contains(o))
return o - STARG0;
if (loadLocalVariables.Contains(o))
return o - LDLOC0;
if (storeLocalVariables.Contains(o))
return o - STLOC0;
if (loadStaticFields.Contains(o))
return o - LDSFLD0;
if (storeStaticFields.Contains(o))
return o - STSFLD0;
throw new ArgumentException($"OpCode {o} cannot access slot");
}

public static readonly HashSet<OpCode> pushInt = new()
Expand Down Expand Up @@ -124,6 +150,7 @@ static OpCodeTypes()
CALL,
CALL_L,
CALLA,
// CALLT is not included because it generally calls another contract
};

public static readonly HashSet<OpCode> conditionalJump = new()
Expand Down Expand Up @@ -216,5 +243,10 @@ static OpCodeTypes()
STARG5,
STARG6,
};
public static readonly HashSet<OpCode> slotNonConst = new()
{
LDARG, LDLOC, LDSFLD,
STARG, STLOC, STSFLD,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ public static (NefFile, ContractManifest, JObject?) BuildOptimizedAssets(
//nef.Compiler = AppDomain.CurrentDomain.FriendlyName;
nef.CheckSum = NefFile.ComputeChecksum(nef);
foreach (ContractMethodDescriptor method in manifest.Abi.Methods)
method.Offset = (int)simplifiedInstructionsToAddress[oldAddressToInstruction[method.Offset]]!;
if (oldAddressToInstruction.TryGetValue(method.Offset, out Instruction? i)
&& simplifiedInstructionsToAddress.Contains(i))
method.Offset = (int)simplifiedInstructionsToAddress[i]!;
else // old start of method was deleted
method.Offset = oldSequencePointAddressToNew![method.Offset];
debugInfo = DebugInfoBuilder.ModifyDebugInfo(
debugInfo, simplifiedInstructionsToAddress, oldAddressToInstruction,
oldSequencePointAddressToNew: oldSequencePointAddressToNew);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ static class DebugInfoBuilder
{
GroupCollection rangeGroups = RangeRegex.Match(method!["range"]!.AsString()).Groups;
(int oldMethodStart, int oldMethodEnd) = (int.Parse(rangeGroups[1].ToString()), int.Parse(rangeGroups[2].ToString()));
if (!simplifiedInstructionsToAddress.Contains(oldAddressToInstruction[oldMethodStart]))
int methodStart;
if (simplifiedInstructionsToAddress.Contains(oldAddressToInstruction[oldMethodStart]))
methodStart = (int)simplifiedInstructionsToAddress[oldAddressToInstruction[oldMethodStart]]!;
else if (oldSequencePointAddressToNew?.TryGetValue(oldMethodStart, out methodStart) != true)
{
methodsToRemove.Add(method);
continue;
}
int methodStart = (int)simplifiedInstructionsToAddress[oldAddressToInstruction[oldMethodStart]]!;
// The instruction at the end of the method may have been deleted.
// We need to find the last instruction that is not deleted.
//int methodEnd = (int)simplifiedInstructionsToAddress[oldAddressToInstruction[oldMethodEnd]]!;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ public static Script BuildScriptWithJumpTargets(
throw new BadScriptException($"Target instruction of {i.OpCode} at new address {a} is deleted");
}
if (i.OpCode == OpCode.JMP || conditionalJump.Contains(i.OpCode) || i.OpCode == OpCode.CALL || i.OpCode == OpCode.ENDTRY)
simplifiedScript.Add(BitConverter.GetBytes(delta)[0]);
if (sbyte.MinValue <= delta && delta <= sbyte.MaxValue)
simplifiedScript.Add(BitConverter.GetBytes(delta)[0]);
else
// TODO: build with _L version
shargon marked this conversation as resolved.
Show resolved Hide resolved
throw new NotImplementedException($"Need {i.OpCode}_L for delta={delta}");
if (i.OpCode == OpCode.PUSHA || i.OpCode == OpCode.JMP_L || conditionalJump_L.Contains(i.OpCode) || i.OpCode == OpCode.CALL_L || i.OpCode == OpCode.ENDTRY_L)
simplifiedScript = simplifiedScript.Concat(BitConverter.GetBytes(delta)).ToList();
continue;
Expand Down
2 changes: 2 additions & 0 deletions src/Neo.Compiler.CSharp/Optimizer/Strategies/Optimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public static (NefFile, ContractManifest, JObject?) Optimize(NefFile nef, Contra
(nef, manifest, debugInfo) = Reachability.ReplaceJumpWithRet(nef, manifest, debugInfo);
(nef, manifest, debugInfo) = Reachability.RemoveUncoveredInstructions(nef, manifest, debugInfo);
(nef, manifest, debugInfo) = Peephole.RemoveDupDrop(nef, manifest, debugInfo);
(nef, manifest, debugInfo) = Peephole.InitStaticToConst(nef, manifest, debugInfo);
(nef, manifest, debugInfo) = Peephole.RemoveInitialize(nef, manifest, debugInfo);
(nef, manifest, debugInfo) = Reachability.RemoveUnnecessaryJumps(nef, manifest, debugInfo);
(nef, manifest, debugInfo) = Reachability.ReplaceJumpWithRet(nef, manifest, debugInfo);
return (nef, manifest, debugInfo);
Expand Down
Loading
Loading