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

Implement IL codes for #91, #93, fix #105 #104

Open
wants to merge 46 commits into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
7ac3be8
Merge branch 'kekyo:devel' into devel
cyborgyn Oct 9, 2021
a2c5f30
Merge branch 'devel' of git://github.com/kekyo/IL2C into kekyo-devel
cyborgyn Oct 10, 2021
69cf5f0
Merge branch 'kekyo-devel' into devel
cyborgyn Oct 10, 2021
e29d09e
Merge pull request #3 from kekyo/devel
cyborgyn Oct 12, 2021
e7fef2b
Merge pull request #4 from cyborgyn/devel
cyborgyn Oct 12, 2021
69e814f
Fix double addition of the same DLL reference, in case the transpiled…
cyborgyn Oct 12, 2021
663563c
Fix discovery of implemented method in case of explicit interface imp…
cyborgyn Oct 12, 2021
4234755
Add bitwise NOT arithmetic operator
cyborgyn Oct 12, 2021
19c92d9
Implement shr, shl IL codes
cyborgyn Oct 12, 2021
f6bb406
Add starg and star.s IL codes
cyborgyn Oct 12, 2021
9091c5b
Fix for #105, stop endless loop, fix static constructor handling #97,…
cyborgyn Oct 13, 2021
bab32a0
Implement Shr.un and Neg IL codes
cyborgyn Oct 13, 2021
80e54b5
Implement Bgt.un and Bgt.un.s IL codes
cyborgyn Oct 13, 2021
c5ac124
Extend ConditionalConverters to handle float type parameters
cyborgyn Oct 13, 2021
e01dc9a
Fix typo
cyborgyn Oct 13, 2021
4833ba2
Implement Stind.i1-i8, Stind.r4-r8 IL codes
cyborgyn Oct 13, 2021
90bd0a3
Fix StargConverter
cyborgyn Oct 13, 2021
05ececc
Add Enum to int conversion handling to GetRightExpression()
cyborgyn Oct 13, 2021
f98e142
Fix NullReferenceException occurring in MethodSignatureTypeComparerIm…
cyborgyn Oct 13, 2021
df35a87
Fix InternalWriteVTableTypePreDefinitions() to correctly handle empty…
cyborgyn Oct 13, 2021
1a9cfe8
Enable IL2C_RUNTIME_TYPE_BEGIN writer, to handle compilation of Syste…
cyborgyn Oct 13, 2021
2a73440
Enable handling without error such cases, where BaseType == null
cyborgyn Oct 13, 2021
e8029c1
Implement Switch IL code, for #93 and #91 v0.1
cyborgyn Oct 13, 2021
e1b6cc4
Implement Ldobj and Stobj IL codes
cyborgyn Oct 14, 2021
30c0889
Updated supported-opcodes.md by Unit tests, no regression so far
cyborgyn Oct 14, 2021
08ea06a
Add switch IL code tests
cyborgyn Oct 14, 2021
ff75c55
Fix switch test + generator
cyborgyn Oct 14, 2021
5453d39
Ldobj IL code test + related fixes in converters
cyborgyn Oct 14, 2021
8e3bac8
Fix ldobj unit test
cyborgyn Oct 14, 2021
e0d75e0
More ldobj tests
cyborgyn Oct 15, 2021
1153f48
More Ldobj tests
cyborgyn Oct 15, 2021
b549284
Merge branch 'devel' into feature/implement-ilcodes
cyborgyn Oct 16, 2021
e43b30c
Merge pull request #5 from kekyo/devel
cyborgyn Oct 17, 2021
3c3a734
Merge pull request #6 from cyborgyn/devel
cyborgyn Oct 17, 2021
796f113
Add Stobj IL Code unit tests
cyborgyn Oct 17, 2021
dd83236
Add more TypeInitializer unit tests
cyborgyn Oct 17, 2021
73afbfb
Add Shr and Shr.un IL Code unit tests + fix for it's converter to mak…
cyborgyn Oct 18, 2021
a28587a
Updated supported-opcodes.md & supported-runtime-system-features.md
cyborgyn Oct 18, 2021
92ed390
Merge branch 'devel' into feature/implement-ilcodes
cyborgyn Oct 18, 2021
76eeee7
Add NOT ILCode unit tests
cyborgyn Oct 19, 2021
2b7b595
Remove breaking unit tests
cyborgyn Oct 19, 2021
3cf87a5
Merge branch 'feature/implement-ilcodes' of github.com:cyborgyn/IL2C …
cyborgyn Oct 19, 2021
bc1476f
Refactor ArithmeticalConverters/ NotConverter according to guidelines
cyborgyn Oct 19, 2021
9753e44
Tried to add more Shr unit tests, but discovery fails then for all ot…
cyborgyn Oct 19, 2021
4790411
Remove method filter comment also for AssemblyPreparer
cyborgyn Oct 19, 2021
ca5706c
StargConverter simplification
cyborgyn Oct 19, 2021
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
35 changes: 32 additions & 3 deletions IL2C.Core/ILConveters/ArithmeticalConverters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using Mono.Cecil.Cil;

using IL2C.Translators;
using IL2C.Metadata;

namespace IL2C.ILConverters
{
Expand Down Expand Up @@ -275,7 +276,7 @@ internal abstract class ShiftConverter : InlineNoneConverter
{
public abstract ShiftDirection Direction { get; }

public override ExpressionEmitter Prepare(DecodeContext decodeContext)
public ExpressionEmitter Prepare(DecodeContext decodeContext, Func<ILocalVariableInformation, string> cast)
{
var si1 = decodeContext.PopStack();
var si0 = decodeContext.PopStack();
Expand All @@ -287,13 +288,26 @@ public override ExpressionEmitter Prepare(DecodeContext decodeContext)
si0.TargetType.FriendlyName);

var result = decodeContext.PushStack(si0.TargetType);

return (extractContext, _) => new[] { string.Format(

if (cast == null)
{
return (extractContext, _) => new[] { string.Format(
"{0} = {1} {2} {3}",
extractContext.GetSymbolName(result),
extractContext.GetSymbolName(si0),
Direction == ShiftDirection.Left ? "<<" : ">>",
extractContext.GetSymbolName(si1)) };
}
else
{
return (extractContext, _) => new[] { string.Format(
"{0} = ({4}){1} {2} {3}",
extractContext.GetSymbolName(result),
extractContext.GetSymbolName(si0),
Direction == ShiftDirection.Left ? "<<" : ">>",
extractContext.GetSymbolName(si1),
cast(si0)) };
}
}
}

Expand All @@ -302,20 +316,35 @@ internal sealed class ShiftRightConverter : ShiftConverter
public override OpCode OpCode => OpCodes.Shr;

public override ShiftDirection Direction => ShiftDirection.Right;

public override ExpressionEmitter Prepare(DecodeContext decodeContext)
{
return Prepare(decodeContext, si => si.TargetType.IsInt32StackFriendlyType ? "int32_t" : "int64_t");
}
}

internal sealed class ShiftLeftConverter : ShiftConverter
{
public override OpCode OpCode => OpCodes.Shl;

public override ShiftDirection Direction => ShiftDirection.Left;

public override ExpressionEmitter Prepare(DecodeContext decodeContext)
{
return Prepare(decodeContext, null);
}
}

internal sealed class ShiftRightUnConverter : ShiftConverter
Copy link
Owner

Choose a reason for hiding this comment

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

@cyborgyn Ouch, I forgot signed integer flag :)

shr opcode will understand signed bit on MSB:

shr 10000010
result: 11000001

It case, we can use C language shift operator with signed integer type (int32_t, int64_t and intptr_t).
But shr.un opcode:

shr.un 10000010
result: 01000001

Strictly ignores MSB bit and insert zero. ECMA-335 III.3.60 shr.un - shift integer right, unsigned section:

shr.un inserts a zero bit on each shift.

On IL2C side, The C language expression is having to write temporary casting unsigned integers explicitly, like:

stack0 = (int32_t)(((uint32_t)stack0) >> shiftAmount);

We need regression test for shr.un (and shr) converter, require these topics:

  • Positive and negative value makes valid shift result.

Copy link
Author

Choose a reason for hiding this comment

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

I did the temporary casting, if you look closely, and the tests are also walking through all of those tricky cases. One thing I forget, is negative shift (I never tried it, never even thought about it on any platform/language before). :) For the shift left, no magic happens, this is why it is omitted there.

Copy link
Owner

Choose a reason for hiding this comment

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

Sorry, I found it on branch latest commit. (I took and read commit from bottom of branch...)

I approved it!

Bit off topic: The CLR (.NET Framework CLR and Core CLR) behavior is funny. Because shr and shr.un opcode behavior isn't defined with any int32, int64 and IntPtr on ECMA-335.

The shr.un instruction shifts value (int32, int 64 or native int) right by the number of bits
specified by shiftAmount.

But real regression test on CLR is passed...

{
public override OpCode OpCode => OpCodes.Shr_Un;

public override ShiftDirection Direction => ShiftDirection.Right;

public override ExpressionEmitter Prepare(DecodeContext decodeContext)
{
return Prepare(decodeContext, si => si.TargetType.IsInt32StackFriendlyType ? "uint32_t" : "uint64_t");
}
}

internal sealed class NegConverter : InlineNoneConverter
Copy link
Owner

Choose a reason for hiding this comment

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

@cyborgyn The 'neg' opcode calculus is serious with floating point type...

ECMA-335 III.3.50 neg - negate :

Negation of integral values is standard twos-complement negation. In particular, negating the
most negative number (which does not have a positive counterpart) yields the most negative
number. To detect this overflow use the sub.ovf instruction instead (i.e., subtract from 0).
Negating a floating-point number cannot overflow; negating NaN returns NaN.

If we write the same expression in C language, I feel that the result may not be the same between C and IL (C#). Also, the current IL2C doesn't consider NaN value at all, so it seems that it's time to think about it....

At this point, I think it's okay to include TODO comments and write with comment out regression test case.

Copy link

Choose a reason for hiding this comment

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

I wrote ckfinite(float/double) opcode implementation in C to conform with these tests https://github.com/dotnet/runtime/blob/f9a2449/src/tests/JIT/IL_Conformance/Old/Base/ckfinite.il#L52.

Here is the C version: https://godbolt.org/z/vxMYrvb8d passing all conformance tests. coreclr uses the same approach by checking if all exponent bits are 1 to find out if float/double number is infinite or NaN, and emits ArithmeticException.

Was looking at the place to implement it in IL2C. Note: in this case, we need the raw single/double number in binary storage variable (e.g. uint32_t and uint64_t respectively) as it is represented in IL (IEEE 754 floating-point); without converting it to C float where it may or may not conform to IEEE 754 fp (ISO C99 is relaxed on the format...).

Feel free to implement ckfinite too, as this one is pretty simple. Otherwise, I'll take a stab at it; once this PR is merged. 🙂

Copy link
Owner

Choose a reason for hiding this comment

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

Good news! I will include NaN handler and ckfinite implementation when after merged this PR. (Memoized: #112)

Expand Down
88 changes: 88 additions & 0 deletions tests/IL2C.Core.Test.ILConverters/Shr/Shr.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace IL2C.ILConverters
{
// Arithmetic Shift right (with sign)
Copy link
Owner

Choose a reason for hiding this comment

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

Thank you inserted details!

// Warning!!! MSIL always operates for bit storage on Int32, or Int64
// and as it seems, smaller unsigned values (uint8 & uint16) will never
// be able to set the int32 highest bit => they will operate as logical shift right.
// On the other hand: uint32 and uint64 are still operated as arithmetic shift.
[TestCase((byte)123, "ShrByte", (byte)123, 0)]
[TestCase((byte)0, "ShrByte", (byte)1, 1)]
[TestCase((byte)(Byte.MaxValue - 1) / 2, "ShrByte", (byte)Byte.MaxValue, 1)]
[TestCase((byte)(Byte.MaxValue - 1) / 4, "ShrByte", (byte)Byte.MaxValue, 2)]

[TestCase((sbyte)123, "ShrSByte", (sbyte)123, 0)]
[TestCase((sbyte)0, "ShrSByte", (sbyte)1, 1)]
[TestCase((sbyte)-1, "ShrSByte", (sbyte)-1, 1)]
[TestCase((sbyte)-1, "ShrSByte", (sbyte)-5, 10)]
[TestCase((sbyte)(SByte.MaxValue - 1) / 2, "ShrSByte", (sbyte)SByte.MaxValue, 1)]
[TestCase((sbyte)(SByte.MaxValue - 1) / 4, "ShrSByte", (sbyte)SByte.MaxValue, 2)]

[TestCase((short)123, "ShrShort", (short)123, 0)]
[TestCase((short)0, "ShrShort", (short)1, 1)]
[TestCase((short)-1, "ShrShort", (short)-1, 1)]
[TestCase((short)-1, "ShrShort", (short)-5, 10)]
[TestCase((short)(Int16.MaxValue - 1) / 2, "ShrShort", (short)Int16.MaxValue, 1)]
[TestCase((short)(Int16.MaxValue - 1) / 4, "ShrShort", (short)Int16.MaxValue, 2)]

[TestCase((ushort)123, "ShrUshort", (ushort)123, 0)]
[TestCase((ushort)0, "ShrUshort", (ushort)1, 1)]
[TestCase((ushort)(UInt16.MaxValue - 1) / 2, "ShrUshort", (ushort)UInt16.MaxValue, 1)]
[TestCase((ushort)(UInt16.MaxValue - 1) / 4, "ShrUshort", (ushort)UInt16.MaxValue, 2)]

[TestCase((int)123, "ShrInt", (int)123, 0)]
[TestCase((int)0, "ShrInt", (int)1, 1)]
[TestCase((int)-1, "ShrInt", (int)-1, 1)]
[TestCase((int)-1, "ShrInt", (int)-5, 10)]
[TestCase((int)(Int32.MaxValue - 1) / 2, "ShrInt", (int)Int32.MaxValue, 1)]
[TestCase((int)(Int32.MaxValue - 1) / 4, "ShrInt", (int)Int32.MaxValue, 2)]

[TestCase((uint)123, "ShrUInt", (uint)123, 0)]
[TestCase((uint)0, "ShrUInt", (uint)1, 1)]
[TestCase((uint)UInt32.MaxValue, "ShrUInt", (uint)UInt32.MaxValue, 1)]
[TestCase((uint)UInt32.MaxValue, "ShrUInt", (uint)UInt32.MaxValue, 2)]

[TestCase((long)123, "ShrLong", (long)123, 0)]
[TestCase((long)0, "ShrLong", (long)1, 1)]
[TestCase((long)-1, "ShrLong", (long)-1, 1)]
[TestCase((long)-1, "ShrLong", (long)-5, 10)]
[TestCase((long)(Int64.MaxValue - 1) / 2, "ShrLong", (long)Int64.MaxValue, 1)]
[TestCase((long)(Int64.MaxValue - 1) / 4, "ShrLong", (long)Int64.MaxValue, 2)]

[TestCase((ulong)123, "ShrUlong", (ulong)123, 0)]
[TestCase((ulong)0, "ShrUlong", (ulong)1, 1)]
[TestCase((ulong)UInt64.MaxValue, "ShrUlong", (ulong)UInt64.MaxValue, 1)]
[TestCase((ulong)UInt64.MaxValue, "ShrUlong", (ulong)UInt64.MaxValue, 2)]
public sealed class Shr
{
[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern byte ShrByte(byte v, int num);

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern sbyte ShrSByte(sbyte v, int num);

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern short ShrShort(short v, int num);

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern ushort ShrUshort(ushort v, int num);

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern int ShrInt(int v, int num);

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern uint ShrUInt(uint v, int num);

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern long ShrLong(long v, int num);

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern ulong ShrUlong(ulong v, int num);
}
}
74 changes: 74 additions & 0 deletions tests/IL2C.Core.Test.ILConverters/Shr/Shr.il
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
.class public IL2C.ILConverters.Shr
{
.method public static uint8 ShrByte(uint8 v, int32 num) cil managed
{
.maxstack 2
ldarg.0
ldarg.1
shr
ret
}

.method public static int8 ShrSByte(int8 v, int32 num) cil managed
{
.maxstack 2
ldarg.0
ldarg.1
shr
ret
}

.method public static int16 ShrShort(int16 v, int32 num) cil managed
{
.maxstack 2
ldarg.0
ldarg.1
shr
ret
}

.method public static uint16 ShrUshort(uint16 v, int32 num) cil managed
{
.maxstack 2
ldarg.0
ldarg.1
shr
ret
}

.method public static int32 ShrInt(int32 v, int32 num) cil managed
{
.maxstack 2
ldarg.0
ldarg.1
shr
ret
}

.method public static uint32 ShrUInt(uint32 v, int32 num) cil managed
{
.maxstack 2
ldarg.0
ldarg.1
shr
ret
}

.method public static int64 ShrLong(int64 v, int32 num) cil managed
{
.maxstack 2
ldarg.0
ldarg.1
shr
ret
}

.method public static uint64 ShrUlong(uint64 v, int32 num) cil managed
{
.maxstack 2
ldarg.0
ldarg.1
shr
ret
}
}
84 changes: 84 additions & 0 deletions tests/IL2C.Core.Test.ILConverters/Shr_un/Shr_un.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace IL2C.ILConverters
{
// Logical Shift right (without sign)
[TestCase((byte)123, "ShrByte", (byte)123, 0)]
[TestCase((byte)0, "ShrByte", (byte)1, 1)]
[TestCase((byte)(Byte.MaxValue - 1) / 2, "ShrByte", (byte)Byte.MaxValue, 1)]
[TestCase((byte)(Byte.MaxValue - 1) / 4, "ShrByte", (byte)Byte.MaxValue, 2)]

[TestCase((sbyte)123, "ShrSByte", (sbyte)123, 0)]
[TestCase((sbyte)0, "ShrSByte", (sbyte)1, 1)]
[TestCase((sbyte)-1, "ShrSByte", (sbyte)-1, 1)]
[TestCase((sbyte)-1, "ShrSByte", (sbyte)-5, 10)]
[TestCase((sbyte)(SByte.MaxValue - 1) / 2, "ShrSByte", (sbyte)SByte.MaxValue, 1)]
[TestCase((sbyte)(SByte.MaxValue - 1) / 4, "ShrSByte", (sbyte)SByte.MaxValue, 2)]

[TestCase((short)123, "ShrShort", (short)123, 0)]
[TestCase((short)0, "ShrShort", (short)1, 1)]
[TestCase((short)-1, "ShrShort", (short)-1, 1)]
[TestCase((short)-1, "ShrShort", (short)-5, 10)]
[TestCase((short)(Int16.MaxValue - 1) / 2, "ShrShort", (short)Int16.MaxValue, 1)]
[TestCase((short)(Int16.MaxValue - 1) / 4, "ShrShort", (short)Int16.MaxValue, 2)]

[TestCase((ushort)123, "ShrUshort", (ushort)123, 0)]
[TestCase((ushort)0, "ShrUshort", (ushort)1, 1)]
[TestCase((ushort)(UInt16.MaxValue - 1) / 2, "ShrUshort", (ushort)UInt16.MaxValue, 1)]
[TestCase((ushort)(UInt16.MaxValue - 1) / 4, "ShrUshort", (ushort)UInt16.MaxValue, 2)]

[TestCase((int)123, "ShrInt", (int)123, 0)]
[TestCase((int)0, "ShrInt", (int)1, 1)]
[TestCase((int)0x7FFFFFFF, "ShrInt", (int)-1, 1)]
[TestCase((int)0x003FFFFF, "ShrInt", (int)-5, 10)]
[TestCase((int)(Int32.MaxValue - 1) / 2, "ShrInt", (int)Int32.MaxValue, 1)]
[TestCase((int)(Int32.MaxValue - 1) / 4, "ShrInt", (int)Int32.MaxValue, 2)]

[TestCase((uint)123, "ShrUInt", (uint)123, 0)]
[TestCase((uint)0, "ShrUInt", (uint)1, 1)]
[TestCase((uint)0x7FFFFFFF, "ShrUInt", (uint)UInt32.MaxValue, 1)]
[TestCase((uint)0x3FFFFFFF, "ShrUInt", (uint)UInt32.MaxValue, 2)]

[TestCase((long)123, "ShrLong", (long)123, 0)]
[TestCase((long)0, "ShrLong", (long)1, 1)]
[TestCase((long)0x7FFFFFFFFFFFFFFF, "ShrLong", (long)-1, 1)]
[TestCase((long)0x003FFFFFFFFFFFFF, "ShrLong", (long)-5, 10)]
[TestCase((long)(Int64.MaxValue - 1) / 2, "ShrLong", (long)Int64.MaxValue, 1)]
[TestCase((long)(Int64.MaxValue - 1) / 4, "ShrLong", (long)Int64.MaxValue, 2)]

[TestCase((ulong)123, "ShrUlong", (ulong)123, 0)]
[TestCase((ulong)0, "ShrUlong", (ulong)1, 1)]
[TestCase((ulong)0x7FFFFFFFFFFFFFFF, "ShrUlong", (ulong)UInt64.MaxValue, 1)]
[TestCase((ulong)0x3FFFFFFFFFFFFFFF, "ShrUlong", (ulong)UInt64.MaxValue, 2)]
public sealed class Shr_un
{
[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern byte ShrByte(byte v, int num);

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern sbyte ShrSByte(sbyte v, int num);

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern short ShrShort(short v, int num);

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern ushort ShrUshort(ushort v, int num);

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern int ShrInt(int v, int num);

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern uint ShrUInt(uint v, int num);

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern long ShrLong(long v, int num);

[MethodImpl(MethodImplOptions.ForwardRef)]
public static extern ulong ShrUlong(ulong v, int num);
}
}
Loading