Skip to content

Commit

Permalink
add pop count
Browse files Browse the repository at this point in the history
  • Loading branch information
Jim8y committed Aug 24, 2024
1 parent 9c19cd7 commit bff30a8
Show file tree
Hide file tree
Showing 14 changed files with 587 additions and 2 deletions.
41 changes: 41 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/StackHelpers.OpCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,47 @@ namespace Neo.Compiler;

partial class MethodConvert
{

private Instruction Nop()
{
return AddInstruction(OpCode.NOP);
}

private Instruction PushM1()
{
return AddInstruction(OpCode.PUSHM1);
}

private Instruction Push0()
{
return AddInstruction(OpCode.PUSH0);
}

private Instruction Push1()
{
return AddInstruction(OpCode.PUSH1);
}

private Instruction Push2()
{
return AddInstruction(OpCode.PUSH2);
}

private Instruction Push3()
{
return AddInstruction(OpCode.PUSH3);
}

private Instruction Push4()
{
return AddInstruction(OpCode.PUSH4);
}

private Instruction Push5()
{
return AddInstruction(OpCode.PUSH5);
}

#region Constants

private Instruction Abort()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -570,4 +570,51 @@ private static void HandleBigIntegerEquals(MethodConvert methodConvert, Semantic
methodConvert.PrepareArgumentsForMethod(model, symbol, arguments);
methodConvert.AddInstruction(OpCode.NUMEQUAL);
}

private static void HandleBigIntegerPopCount(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, ExpressionSyntax? instanceExpression, IReadOnlyList<SyntaxNode>? arguments)
{
if (instanceExpression is not null)
methodConvert.ConvertExpression(model, instanceExpression);
if (arguments is not null)
methodConvert.PrepareArgumentsForMethod(model, symbol, arguments);

// Check if the value is within int range
methodConvert.AddInstruction(OpCode.DUP);
methodConvert.Within(int.MinValue, int.MaxValue);
var endIntCheck = new JumpTarget();
methodConvert.Jump(OpCode.JMPIFNOT, endIntCheck);

// If within int range, mask with 0xFFFFFFFF
methodConvert.Push(0xFFFFFFFF);
methodConvert.AddInstruction(OpCode.AND);
var endMask = new JumpTarget();
methodConvert.Jump(OpCode.JMP, endMask);

// If larger than int, throw exception, cause too many check will make the script too long.
endIntCheck.Instruction = methodConvert.AddInstruction(OpCode.NOP);
methodConvert.Push("Value out of range, must be between int.MinValue and int.MaxValue.");
methodConvert.Throw();
endMask.Instruction = methodConvert.AddInstruction(OpCode.NOP);

// Initialize count to 0
methodConvert.Push(0); // value count
methodConvert.Swap(); // count value
// Loop to count the number of 1 bit
JumpTarget loopStart = new();
JumpTarget endLoop = new();
loopStart.Instruction = methodConvert.Dup(); // count value value
methodConvert.Push0(); // count value value 0
methodConvert.Jump(OpCode.JMPEQ, endLoop); // count value
methodConvert.Dup(); // count value value
methodConvert.Push1(); // count value value 1
methodConvert.And(); // count value (value & 1)
methodConvert.Rot(); // value (value & 1) count
methodConvert.Add(); // value count += (value & 1)
methodConvert.Swap(); // count value
methodConvert.Push1(); // count value 1
methodConvert.ShR(); // count value >>= 1
methodConvert.Jump(OpCode.JMP, loopStart);

endLoop.Instruction = methodConvert.Drop(); // Drop the remaining value
}
}
34 changes: 34 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Byte.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,38 @@ private static void HandleByteRotateRight(MethodConvert methodConvert, SemanticM
methodConvert.Push((BigInteger.One << bitWidth) - 1); // Push (2^bitWidth - 1) as bitmask
methodConvert.AddInstruction(OpCode.AND); // Ensure final result is bitWidth-bit
}

private static void HandleBytePopCount(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, ExpressionSyntax? instanceExpression, IReadOnlyList<SyntaxNode>? arguments)
{
if (instanceExpression is not null)
methodConvert.ConvertExpression(model, instanceExpression);
if (arguments is not null)
methodConvert.PrepareArgumentsForMethod(model, symbol, arguments);
// Determine bit width of int
var bitWidth = sizeof(byte) * 8;

// Mask to ensure the value is treated as a 8-bit unsigned integer
methodConvert.Push((BigInteger.One << bitWidth) - 1); // 0xFF
methodConvert.And(); // value = value & 0xFF
// Initialize count to 0
methodConvert.Push(0); // value count
methodConvert.Swap(); // count value
// Loop to count the number of 1 bits
JumpTarget loopStart = new();
JumpTarget endLoop = new();
loopStart.Instruction = methodConvert.Dup(); // count value value
methodConvert.Push0(); // count value value 0
methodConvert.Jump(OpCode.JMPEQ, endLoop); // count value
methodConvert.Dup(); // count value value
methodConvert.Push1(); // count value value 1
methodConvert.And(); // count value (value & 1)
methodConvert.Rot(); // value (value & 1) count
methodConvert.Add(); // value count += (value & 1)
methodConvert.Swap(); // count value
methodConvert.Push1(); // count value 1
methodConvert.ShR(); // count value >>= 1
methodConvert.Jump(OpCode.JMP, loopStart);

endLoop.Instruction = methodConvert.Drop(); // Drop the remaining value
}
}
35 changes: 35 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Int.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,4 +264,39 @@ private static void HandleIntRotateRight(MethodConvert methodConvert, SemanticMo
methodConvert.AddInstruction(OpCode.SUB);
endTarget.Instruction = methodConvert.AddInstruction(OpCode.NOP);
}

// implement PopCount
private static void HandleIntPopCount(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, ExpressionSyntax? instanceExpression, IReadOnlyList<SyntaxNode>? arguments)
{
if (instanceExpression is not null)
methodConvert.ConvertExpression(model, instanceExpression);
if (arguments is not null)
methodConvert.PrepareArgumentsForMethod(model, symbol, arguments);
// Determine bit width of int
var bitWidth = sizeof(int) * 8;

// Mask to ensure the value is treated as a 32-bit unsigned integer
methodConvert.Push((BigInteger.One << bitWidth) - 1); // 0xFFFFFFFF
methodConvert.And(); // value = value & 0xFFFFFFFF
// Initialize count to 0
methodConvert.Push(0); // value count
methodConvert.Swap(); // count value
// Loop to count the number of 1 bits
JumpTarget loopStart = new();
JumpTarget endLoop = new();
loopStart.Instruction = methodConvert.Dup(); // count value value
methodConvert.Push0(); // count value value 0
methodConvert.Jump(OpCode.JMPEQ, endLoop); // count value
methodConvert.Dup(); // count value value
methodConvert.Push1(); // count value value 1
methodConvert.And(); // count value (value & 1)
methodConvert.Rot(); // value (value & 1) count
methodConvert.Add(); // value count += (value & 1)
methodConvert.Swap(); // count value
methodConvert.Push1(); // count value 1
methodConvert.ShR(); // count value >>= 1
methodConvert.Jump(OpCode.JMP, loopStart);

endLoop.Instruction = methodConvert.Drop(); // Drop the remaining value
}
}
34 changes: 34 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Long.cs
Original file line number Diff line number Diff line change
Expand Up @@ -268,4 +268,38 @@ private static void HandleLongRotateRight(MethodConvert methodConvert, SemanticM
methodConvert.AddInstruction(OpCode.SUB);
endTarget.Instruction = methodConvert.AddInstruction(OpCode.NOP);
}

private static void HandleLongPopCount(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, ExpressionSyntax? instanceExpression, IReadOnlyList<SyntaxNode>? arguments)
{
if (instanceExpression is not null)
methodConvert.ConvertExpression(model, instanceExpression);
if (arguments is not null)
methodConvert.PrepareArgumentsForMethod(model, symbol, arguments);
// Determine bit width of int
var bitWidth = sizeof(long) * 8;

// Mask to ensure the value is treated as a 64-bit unsigned integer
methodConvert.Push((BigInteger.One << bitWidth) - 1); // 0xFFFFFFFFFFFFFFFF
methodConvert.And(); // value = value & 0xFFFFFFFFFFFFFFFF
// Initialize count to 0
methodConvert.Push(0); // value count
methodConvert.Swap(); // count value
// Loop to count the number of 1 bits
JumpTarget loopStart = new();
JumpTarget endLoop = new();
loopStart.Instruction = methodConvert.Dup(); // count value value
methodConvert.Push0(); // count value value 0
methodConvert.Jump(OpCode.JMPEQ, endLoop); // count value
methodConvert.Dup(); // count value value
methodConvert.Push1(); // count value value 1
methodConvert.And(); // count value (value & 1)
methodConvert.Rot(); // value (value & 1) count
methodConvert.Add(); // value count += (value & 1)
methodConvert.Swap(); // count value
methodConvert.Push1(); // count value 1
methodConvert.ShR(); // count value >>= 1
methodConvert.Jump(OpCode.JMP, loopStart);

endLoop.Instruction = methodConvert.Drop(); // Drop the remaining value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ private static void RegisterSystemCallHandlers()

// Nullable type handlers
RegisterNullableTypeHandlers();

// BitOperations handlers
RegisterBitOperationsHandlers();
}

private static void RegisterBigIntegerHandlers()
Expand Down Expand Up @@ -478,6 +481,19 @@ private static void RegisterNumericTypeHandlers()
// RegisterHandler((string s) => bool.Parse(s), HandleBoolParse);
RegisterHandler((string s) => BigInteger.Parse(s), HandleBigIntegerParse);
#endregion

// Numeric PopCount
#region PopCount
RegisterHandler((byte value) => byte.PopCount(value), HandleBytePopCount);
RegisterHandler((sbyte value) => sbyte.PopCount(value), HandleSBytePopCount);
RegisterHandler((short value) => short.PopCount(value), HandleShortPopCount);
RegisterHandler((ushort value) => ushort.PopCount(value), HandleUShortPopCount);
RegisterHandler((int value) => int.PopCount(value), HandleIntPopCount);
RegisterHandler((uint value) => uint.PopCount(value), HandleUIntPopCount);
RegisterHandler((long value) => long.PopCount(value), HandleLongPopCount);
RegisterHandler((ulong value) => ulong.PopCount(value), HandleULongPopCount);
RegisterHandler((BigInteger value) => BigInteger.PopCount(value), HandleBigIntegerPopCount);
#endregion PopCount
}

private static void RegisterMathHandlers()
Expand Down Expand Up @@ -715,4 +731,20 @@ private static void RegisterNullableTypeHandlers()
RegisterHandler((string? value, bool result) => bool.TryParse(value, out result), HandleBoolTryParseWithOut);
#endregion
}

private static void RegisterBitOperationsHandlers()
{
// Static Methods
RegisterHandler((uint value) => BitOperations.LeadingZeroCount(value), HandleUIntLeadingZeroCount);
RegisterHandler((ulong value) => BitOperations.LeadingZeroCount(value), HandleULongLeadingZeroCount);
RegisterHandler((uint value) => BitOperations.Log2(value), HandleBigIntegerLog2);
RegisterHandler((ulong value) => BitOperations.Log2(value), HandleBigIntegerLog2);
RegisterHandler((uint value) => BitOperations.PopCount(value), HandleUIntPopCount);
RegisterHandler((ulong value) => BitOperations.PopCount(value), HandleULongPopCount);
RegisterHandler((uint value, int offset) => BitOperations.RotateLeft(value, offset), HandleUIntRotateLeft);
RegisterHandler((ulong value, int offset) => BitOperations.RotateLeft(value, offset), HandleULongRotateLeft);
RegisterHandler((uint value, int offset) => BitOperations.RotateRight(value, offset), HandleUIntRotateRight);
RegisterHandler((ulong value, int offset) => BitOperations.RotateRight(value, offset), HandleULongRotateRight);
}

}
34 changes: 34 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.SByte.cs
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,38 @@ private static void HandleSByteRotateRight(MethodConvert methodConvert, Semantic
methodConvert.AddInstruction(OpCode.SUB);
endTarget.Instruction = methodConvert.AddInstruction(OpCode.NOP);
}

private static void HandleSBytePopCount(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, ExpressionSyntax? instanceExpression, IReadOnlyList<SyntaxNode>? arguments)
{
if (instanceExpression is not null)
methodConvert.ConvertExpression(model, instanceExpression);
if (arguments is not null)
methodConvert.PrepareArgumentsForMethod(model, symbol, arguments);
// Determine bit width of int
var bitWidth = sizeof(sbyte) * 8;

// Mask to ensure the value is treated as a 8-bit unsigned integer
methodConvert.Push((BigInteger.One << bitWidth) - 1); // 0xFF
methodConvert.And(); // value = value & 0xFF
// Initialize count to 0
methodConvert.Push(0); // value count
methodConvert.Swap(); // count value
// Loop to count the number of 1 bits
JumpTarget loopStart = new();
JumpTarget endLoop = new();
loopStart.Instruction = methodConvert.Dup(); // count value value
methodConvert.Push0(); // count value value 0
methodConvert.Jump(OpCode.JMPEQ, endLoop); // count value
methodConvert.Dup(); // count value value
methodConvert.Push1(); // count value value 1
methodConvert.And(); // count value (value & 1)
methodConvert.Rot(); // value (value & 1) count
methodConvert.Add(); // value count += (value & 1)
methodConvert.Swap(); // count value
methodConvert.Push1(); // count value 1
methodConvert.ShR(); // count value >>= 1
methodConvert.Jump(OpCode.JMP, loopStart);

endLoop.Instruction = methodConvert.Drop(); // Drop the remaining value
}
}
34 changes: 34 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.Short.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,4 +269,38 @@ private static void HandleShortRotateRight(MethodConvert methodConvert, Semantic
methodConvert.AddInstruction(OpCode.SUB);
endTarget.Instruction = methodConvert.AddInstruction(OpCode.NOP);
}

private static void HandleShortPopCount(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, ExpressionSyntax? instanceExpression, IReadOnlyList<SyntaxNode>? arguments)
{
if (instanceExpression is not null)
methodConvert.ConvertExpression(model, instanceExpression);
if (arguments is not null)
methodConvert.PrepareArgumentsForMethod(model, symbol, arguments);
// Determine bit width of int
var bitWidth = sizeof(short) * 8;

// Mask to ensure the value is treated as a 16-bit unsigned integer
methodConvert.Push((BigInteger.One << bitWidth) - 1); // 0xFFFF
methodConvert.And(); // value = value & 0xFFFF
// Initialize count to 0
methodConvert.Push(0); // value count
methodConvert.Swap(); // count value
// Loop to count the number of 1 bits
JumpTarget loopStart = new();
JumpTarget endLoop = new();
loopStart.Instruction = methodConvert.Dup(); // count value value
methodConvert.Push0(); // count value value 0
methodConvert.Jump(OpCode.JMPEQ, endLoop); // count value
methodConvert.Dup(); // count value value
methodConvert.Push1(); // count value value 1
methodConvert.And(); // count value (value & 1)
methodConvert.Rot(); // value (value & 1) count
methodConvert.Add(); // value count += (value & 1)
methodConvert.Swap(); // count value
methodConvert.Push1(); // count value 1
methodConvert.ShR(); // count value >>= 1
methodConvert.Jump(OpCode.JMP, loopStart);

endLoop.Instruction = methodConvert.Drop(); // Drop the remaining value
}
}
34 changes: 34 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/System/SystemCall.UInt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,38 @@ private static void HandleUIntRotateRight(MethodConvert methodConvert, SemanticM
methodConvert.Push((BigInteger.One << bitWidth) - 1); // Push (2^bitWidth - 1) as bitmask
methodConvert.AddInstruction(OpCode.AND); // Ensure final result is bitWidth-bit
}

private static void HandleUIntPopCount(MethodConvert methodConvert, SemanticModel model, IMethodSymbol symbol, ExpressionSyntax? instanceExpression, IReadOnlyList<SyntaxNode>? arguments)
{
if (instanceExpression is not null)
methodConvert.ConvertExpression(model, instanceExpression);
if (arguments is not null)
methodConvert.PrepareArgumentsForMethod(model, symbol, arguments);
// Determine bit width of int
var bitWidth = sizeof(int) * 8;

// Mask to ensure the value is treated as a 32-bit unsigned integer
methodConvert.Push((BigInteger.One << bitWidth) - 1); // 0xFFFFFFFF
methodConvert.And(); // value = value & 0xFFFFFFFF
// Initialize count to 0
methodConvert.Push(0); // value count
methodConvert.Swap(); // count value
// Loop to count the number of 1 bits
JumpTarget loopStart = new();
JumpTarget endLoop = new();
loopStart.Instruction = methodConvert.Dup(); // count value value
methodConvert.Push0(); // count value value 0
methodConvert.Jump(OpCode.JMPEQ, endLoop); // count value
methodConvert.Dup(); // count value value
methodConvert.Push1(); // count value value 1
methodConvert.And(); // count value (value & 1)
methodConvert.Rot(); // value (value & 1) count
methodConvert.Add(); // value count += (value & 1)
methodConvert.Swap(); // count value
methodConvert.Push1(); // count value 1
methodConvert.ShR(); // count value >>= 1
methodConvert.Jump(OpCode.JMP, loopStart);

endLoop.Instruction = methodConvert.Drop(); // Drop the remaining value
}
}
Loading

0 comments on commit bff30a8

Please sign in to comment.