Skip to content

Commit

Permalink
optimize construtor when initializer exists (#1267)
Browse files Browse the repository at this point in the history
* optimize construtor when initializer exists

* update GAS
  • Loading branch information
Hecate2 authored Dec 19, 2024
1 parent c6fcf9d commit d59d0b8
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,34 @@ private void ConvertObjectCreationExpression(SemanticModel model, BaseObjectCrea
ConvertObjectCreationExpressionInitializer(model, expression.Initializer);
}

/// <summary>
/// Check whether necessary to include the constructor instructions in the compiled contract
/// </summary>
/// <param name="convert">var (convert, _) = GetMethodConvertAndCallingConvention(model, constructor);</param>
/// <returns></returns>
public static bool CanSkipConstructor(MethodConvert? convert)
{
if (convert == null)
return false; // special complex cases like virtual methods
if (convert.Instructions.Count >= 1)
{
Instruction ret = convert.Instructions[0];
if (ret.OpCode == OpCode.RET)
return true;
}
if (convert.Instructions.Count >= 2)
{
// INITSLOT 0 locals, 1 args
// RET
Instruction initslot = convert.Instructions[0];
Instruction ret = convert.Instructions[1];
if (initslot.OpCode == OpCode.INITSLOT && initslot.Operand?[0] == 0 && initslot.Operand[1] == 1
&& ret.OpCode == OpCode.RET)
return true;
}
return false;
}

/// <summary>
/// Handles new MyClass() { PropertyA = "A", Property2 = 2, } in a GAS-efficient way
/// Do not initialize MyClass() by PACKing default values and then SETITEM for { PropertyA = "A", Property2 = 2, }
Expand All @@ -65,12 +93,7 @@ private bool TryOptimizedObjectCreation(SemanticModel model, BaseObjectCreationE
if (expression.Initializer == null || expression.Initializer.IsKind(SyntaxKind.CollectionInitializerExpression))
return false;
var (convert, methodCallingConvention) = GetMethodConvertAndCallingConvention(model, constructor);
if (convert == null || convert.Instructions.Count < 2)
return false;
Instruction initslot = convert.Instructions[0];
Instruction ret = convert.Instructions[1];
if (initslot.OpCode != OpCode.INITSLOT || initslot.Operand?[0] != 0 || initslot.Operand[1] != 1
|| ret.OpCode != OpCode.RET)
if (!CanSkipConstructor(convert))
return false;
// no constructor needed
var members = type.GetAllMembers().Where(p => !p.IsStatic).ToArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public abstract class Contract_ClassInit(Neo.SmartContract.Testing.SmartContract
/// <summary>
/// Optimization: "All"
/// </summary>
public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable<Neo.SmartContract.NefFile>(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHUQEBK/QFcFAAsLCxAQCRbAShMMAXPQShQQEBK/0EoVCwsLEBAJFsDQcGgVzmgSwEpxynIQcyIbaWvOdGwQzgmXOWwRzhCXOWwSzhCXOWucc2tqMOVoE84MAXOXOWgVzhPO2DloFM4QzhCXOWgUzhHOEJc5aED14mC7"));
public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable<Neo.SmartContract.NefFile>(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGkQEBK/QFcFAAsLCxAQCRbAEBASvwwBcxAQCRbAcGgVzmgSwEpxynIQcyIbaWvOdGwQzgmXOWwRzhCXOWwSzhCXOWucc2tqMOVoE84MAXOXOWgVzhPO2DloFM4QzhCXOWgUzhHOEJc5aECYKH3y"));

#endregion

Expand All @@ -25,7 +25,7 @@ public abstract class Contract_ClassInit(Neo.SmartContract.Testing.SmartContract
/// Unsafe method
/// </summary>
/// <remarks>
/// Script: VwUACwsLEBAJFsBKEwwBc9BKFBAQEr/QShULCwsQEAkWwNBwaBXOaBLASnHKchBzIhtpa850bBDOCZc5bBHOEJc5bBLOEJc5a5xza2ow5WgTzgwBc5c5aBXOE87YOWgUzhDOEJc5aBTOEc4QlzloQA==
/// Script: VwUACwsLEBAJFsAQEBK/DAFzEBAJFsBwaBXOaBLASnHKchBzIhtpa850bBDOCZc5bBHOEJc5bBLOEJc5a5xza2ow5WgTzgwBc5c5aBXOE87YOWgUzhDOEJc5aBTOEc4QlzloQA==
/// INITSLOT 0500 [64 datoshi]
/// PUSHNULL [1 datoshi]
/// PUSHNULL [1 datoshi]
Expand All @@ -35,28 +35,16 @@ public abstract class Contract_ClassInit(Neo.SmartContract.Testing.SmartContract
/// PUSHF [1 datoshi]
/// PUSH6 [1 datoshi]
/// PACK [2048 datoshi]
/// DUP [2 datoshi]
/// PUSH3 [1 datoshi]
/// PUSHDATA1 73 's' [8 datoshi]
/// SETITEM [8192 datoshi]
/// DUP [2 datoshi]
/// PUSH4 [1 datoshi]
/// PUSH0 [1 datoshi]
/// PUSH0 [1 datoshi]
/// PUSH2 [1 datoshi]
/// PACKSTRUCT [2048 datoshi]
/// SETITEM [8192 datoshi]
/// DUP [2 datoshi]
/// PUSH5 [1 datoshi]
/// PUSHNULL [1 datoshi]
/// PUSHNULL [1 datoshi]
/// PUSHNULL [1 datoshi]
/// PUSHDATA1 73 's' [8 datoshi]
/// PUSH0 [1 datoshi]
/// PUSH0 [1 datoshi]
/// PUSHF [1 datoshi]
/// PUSH6 [1 datoshi]
/// PACK [2048 datoshi]
/// SETITEM [8192 datoshi]
/// STLOC0 [2 datoshi]
/// LDLOC0 [2 datoshi]
/// PUSH5 [1 datoshi]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ public abstract class Contract_Initializer(Neo.SmartContract.Testing.SmartContra
{
#region Compiled data

public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_Initializer"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""sum"",""parameters"":[],""returntype"":""Integer"",""offset"":0,""safe"":false},{""name"":""sum1"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":62,""safe"":false},{""name"":""sum2"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":132,""safe"":false},{""name"":""anonymousObjectCreation"",""parameters"":[],""returntype"":""Void"",""offset"":208,""safe"":false}],""events"":[]},""permissions"":[],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}");
public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_Initializer"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""sum"",""parameters"":[],""returntype"":""Integer"",""offset"":0,""safe"":false},{""name"":""sum1"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":62,""safe"":false},{""name"":""sum2"",""parameters"":[{""name"":""a"",""type"":""Integer""},{""name"":""b"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":124,""safe"":false},{""name"":""anonymousObjectCreation"",""parameters"":[],""returntype"":""Void"",""offset"":200,""safe"":false}],""events"":[]},""permissions"":[],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}");

/// <summary>
/// Optimization: "All"
/// </summary>
public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable<Neo.SmartContract.NefFile>(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0BAVcBABIREsBwaBDOaBHOnkoCAAAAgC4EIgpKAv///38yHgP/////AAAAAJFKAv///38yDAMAAAAAAQAAAJ9AVwECEhESwEoQeNBKEXnQcGgQzmgRzp5KAgAAAIAuBCIKSgL///9/Mh4D/////wAAAACRSgL///9/MgwDAAAAAAEAAACfQFcBAhIREsBweEpoEFHQRXlKaBFR0EVoEM5oEc6eSgIAAACALgQiCkoC////fzIeA/////8AAAAAkUoC////fzIMAwAAAAABAAAAn0BXAgAMBUhlbGxvAGwSwHBBz+dHlhEMBWdyYXBlEsAUDAVhcHBsZRLAEsBxQc/nR5ZA9Z8EmQ=="));
public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable<Neo.SmartContract.NefFile>(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPlXAQASERLAcGgQzmgRzp5KAgAAAIAuBCIKSgL///9/Mh4D/////wAAAACRSgL///9/MgwDAAAAAAEAAACfQFcBAnl4EsBwaBDOaBHOnkoCAAAAgC4EIgpKAv///38yHgP/////AAAAAJFKAv///38yDAMAAAAAAQAAAJ9AVwECEhESwHB4SmgQUdBFeUpoEVHQRWgQzmgRzp5KAgAAAIAuBCIKSgL///9/Mh4D/////wAAAACRSgL///9/MgwDAAAAAAEAAACfQFcCAAwFSGVsbG8AbBLAcEHP50eWEQwFZ3JhcGUSwBQMBWFwcGxlEsASwHFBz+dHlkDNyhnT"));

#endregion

Expand Down Expand Up @@ -91,20 +91,12 @@ public abstract class Contract_Initializer(Neo.SmartContract.Testing.SmartContra
/// Unsafe method
/// </summary>
/// <remarks>
/// Script: VwECEhESwEoQeNBKEXnQcGgQzmgRzp5KAgAAAIAuBCIKSgL///9/Mh4D/////wAAAACRSgL///9/MgwDAAAAAAEAAACfQA==
/// Script: VwECeXgSwHBoEM5oEc6eSgIAAACALgQiCkoC////fzIeA/////8AAAAAkUoC////fzIMAwAAAAABAAAAn0A=
/// INITSLOT 0102 [64 datoshi]
/// PUSH2 [1 datoshi]
/// PUSH1 [1 datoshi]
/// LDARG1 [2 datoshi]
/// LDARG0 [2 datoshi]
/// PUSH2 [1 datoshi]
/// PACK [2048 datoshi]
/// DUP [2 datoshi]
/// PUSH0 [1 datoshi]
/// LDARG0 [2 datoshi]
/// SETITEM [8192 datoshi]
/// DUP [2 datoshi]
/// PUSH1 [1 datoshi]
/// LDARG1 [2 datoshi]
/// SETITEM [8192 datoshi]
/// STLOC0 [2 datoshi]
/// LDLOC0 [2 datoshi]
/// PUSH0 [1 datoshi]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ public abstract class Contract_Recursion(Neo.SmartContract.Testing.SmartContract
{
#region Compiled data

public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_Recursion"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""factorial"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":0,""safe"":false},{""name"":""hanoiTower"",""parameters"":[{""name"":""n"",""type"":""Integer""},{""name"":""src"",""type"":""Integer""},{""name"":""aux"",""type"":""Integer""},{""name"":""dst"",""type"":""Integer""}],""returntype"":""Array"",""offset"":52,""safe"":false},{""name"":""even"",""parameters"":[{""name"":""n"",""type"":""Integer""}],""returntype"":""Boolean"",""offset"":170,""safe"":false},{""name"":""odd"",""parameters"":[{""name"":""n"",""type"":""Integer""}],""returntype"":""Boolean"",""offset"":196,""safe"":false}],""events"":[]},""permissions"":[],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}");
public static Neo.SmartContract.Manifest.ContractManifest Manifest => Neo.SmartContract.Manifest.ContractManifest.Parse(@"{""name"":""Contract_Recursion"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""factorial"",""parameters"":[{""name"":""a"",""type"":""Integer""}],""returntype"":""Integer"",""offset"":0,""safe"":false},{""name"":""hanoiTower"",""parameters"":[{""name"":""n"",""type"":""Integer""},{""name"":""src"",""type"":""Integer""},{""name"":""aux"",""type"":""Integer""},{""name"":""dst"",""type"":""Integer""}],""returntype"":""Array"",""offset"":52,""safe"":false},{""name"":""even"",""parameters"":[{""name"":""n"",""type"":""Integer""}],""returntype"":""Boolean"",""offset"":146,""safe"":false},{""name"":""odd"",""parameters"":[{""name"":""n"",""type"":""Integer""}],""returntype"":""Boolean"",""offset"":172,""safe"":false}],""events"":[]},""permissions"":[],""trusts"":[],""extra"":{""nef"":{""optimization"":""All""}}}");

/// <summary>
/// Optimization: "All"
/// </summary>
public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable<Neo.SmartContract.NefFile>(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN5XAAF4ELgkHwwaTWludXMgbnVtYmVyIG5vdCBzdXBwb3J0ZWTgeBK4Jgp4eBGfNNKgQBFAVwUEeBC3JBgME0NvdW50IG9mIGRpc2tzIDw9IDDgeBGXJhnCcGgQEBATv0oQEdBKEXnQShJ70M9oQHp7eXgRnzTAcGgQEBATv0oQeNBKEXnQShJ70M97eXp4EZ80pEpxynIQcyIMaWvOdGhsz2ucc2tqMPRoQFcAAXgQlyYECEB4ELUmB3gRniIFeBGfNANAVwABeBCXJgQJQHgQtSYHeBGeIgV4EZ80z0DHiVku"));
public static Neo.SmartContract.NefFile Nef => Neo.IO.Helper.AsSerializable<Neo.SmartContract.NefFile>(Convert.FromBase64String(@"TkVGM1Rlc3RpbmdFbmdpbmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMZXAAF4ELgkHwwaTWludXMgbnVtYmVyIG5vdCBzdXBwb3J0ZWTgeBK4Jgp4eBGfNNKgQBFAVwUEeBC3JBgME0NvdW50IG9mIGRpc2tzIDw9IDDgeBGXJg3CcGh7eRETv89oQHp7eXgRnzTMcGh7eXgTv897eXp4EZ80vEpxynIQcyIMaWvOdGhsz2ucc2tqMPRoQFcAAXgQlyYECEB4ELUmB3gRniIFeBGfNANAVwABeBCXJgQJQHgQtSYHeBGeIgV4EZ80z0B+ZwPL"));

#endregion

Expand Down Expand Up @@ -83,7 +83,7 @@ public abstract class Contract_Recursion(Neo.SmartContract.Testing.SmartContract
/// Unsafe method
/// </summary>
/// <remarks>
/// Script: VwUEeBC3JBgME0NvdW50IG9mIGRpc2tzIDw9IDDgeBGXJhnCcGgQEBATv0oQEdBKEXnQShJ70M9oQHp7eXgRnzTAcGgQEBATv0oQeNBKEXnQShJ70M97eXp4EZ80pEpxynIQcyIMaWvOdGhsz2ucc2tqMPRoQA==
/// Script: VwUEeBC3JBgME0NvdW50IG9mIGRpc2tzIDw9IDDgeBGXJg3CcGh7eRETv89oQHp7eXgRnzTMcGh7eXgTv897eXp4EZ80vEpxynIQcyIMaWvOdGhsz2ucc2tqMPRoQA==
/// INITSLOT 0504 [64 datoshi]
/// LDARG0 [2 datoshi]
/// PUSH0 [1 datoshi]
Expand All @@ -94,27 +94,15 @@ public abstract class Contract_Recursion(Neo.SmartContract.Testing.SmartContract
/// LDARG0 [2 datoshi]
/// PUSH1 [1 datoshi]
/// EQUAL [32 datoshi]
/// JMPIFNOT 19 [2 datoshi]
/// JMPIFNOT 0D [2 datoshi]
/// NEWARRAY0 [16 datoshi]
/// STLOC0 [2 datoshi]
/// LDLOC0 [2 datoshi]
/// PUSH0 [1 datoshi]
/// PUSH0 [1 datoshi]
/// PUSH0 [1 datoshi]
/// LDARG3 [2 datoshi]
/// LDARG1 [2 datoshi]
/// PUSH1 [1 datoshi]
/// PUSH3 [1 datoshi]
/// PACKSTRUCT [2048 datoshi]
/// DUP [2 datoshi]
/// PUSH0 [1 datoshi]
/// PUSH1 [1 datoshi]
/// SETITEM [8192 datoshi]
/// DUP [2 datoshi]
/// PUSH1 [1 datoshi]
/// LDARG1 [2 datoshi]
/// SETITEM [8192 datoshi]
/// DUP [2 datoshi]
/// PUSH2 [1 datoshi]
/// LDARG3 [2 datoshi]
/// SETITEM [8192 datoshi]
/// APPEND [8192 datoshi]
/// LDLOC0 [2 datoshi]
/// RET [0 datoshi]
Expand All @@ -124,34 +112,22 @@ public abstract class Contract_Recursion(Neo.SmartContract.Testing.SmartContract
/// LDARG0 [2 datoshi]
/// PUSH1 [1 datoshi]
/// SUB [8 datoshi]
/// CALL C0 [512 datoshi]
/// CALL CC [512 datoshi]
/// STLOC0 [2 datoshi]
/// LDLOC0 [2 datoshi]
/// PUSH0 [1 datoshi]
/// PUSH0 [1 datoshi]
/// PUSH0 [1 datoshi]
/// LDARG3 [2 datoshi]
/// LDARG1 [2 datoshi]
/// LDARG0 [2 datoshi]
/// PUSH3 [1 datoshi]
/// PACKSTRUCT [2048 datoshi]
/// DUP [2 datoshi]
/// PUSH0 [1 datoshi]
/// LDARG0 [2 datoshi]
/// SETITEM [8192 datoshi]
/// DUP [2 datoshi]
/// PUSH1 [1 datoshi]
/// LDARG1 [2 datoshi]
/// SETITEM [8192 datoshi]
/// DUP [2 datoshi]
/// PUSH2 [1 datoshi]
/// LDARG3 [2 datoshi]
/// SETITEM [8192 datoshi]
/// APPEND [8192 datoshi]
/// LDARG3 [2 datoshi]
/// LDARG1 [2 datoshi]
/// LDARG2 [2 datoshi]
/// LDARG0 [2 datoshi]
/// PUSH1 [1 datoshi]
/// SUB [8 datoshi]
/// CALL A4 [512 datoshi]
/// CALL BC [512 datoshi]
/// DUP [2 datoshi]
/// STLOC1 [2 datoshi]
/// SIZE [4 datoshi]
Expand Down
2 changes: 1 addition & 1 deletion tests/Neo.Compiler.CSharp.UnitTests/UnitTest_ClassInit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void Test_InitInt()
public void Test_InitializationExpression()
{
Contract.TestInitializationExpression();
AssertGasConsumed(2013330);
AssertGasConsumed(1275690);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public void Initializer_Test()
Assert.AreEqual(3, Contract.Sum());
AssertGasConsumed(1052100);
Assert.AreEqual(12, Contract.Sum1(5, 7));
AssertGasConsumed(1604970);
AssertGasConsumed(1113210);
Assert.AreEqual(12, Contract.Sum2(5, 7));
AssertGasConsumed(1605330);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Neo.Compiler.CSharp.UnitTests/UnitTest_Recursion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public void Test_HanoiTower()
{
int src = 100, aux = 200, dst = 300;
var result = Contract.HanoiTower(1, src, aux, dst)!;
AssertGasConsumed(2094240);
AssertGasConsumed(1356600);
Assert.AreEqual(result.Count, 1);
List<(BigInteger rodId, BigInteger src, BigInteger dst)> expectedResult = [(1, src, dst)];
for (int i = 0; i < expectedResult.Count; ++i)
Expand Down
Loading

0 comments on commit d59d0b8

Please sign in to comment.