-
Notifications
You must be signed in to change notification settings - Fork 36
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
base: devel
Are you sure you want to change the base?
Changes from all commits
7ac3be8
a2c5f30
69cf5f0
e29d09e
e7fef2b
69e814f
663563c
4234755
19c92d9
f6bb406
9091c5b
bab32a0
80e54b5
c5ac124
e01dc9a
4833ba2
90bd0a3
05ececc
f98e142
df35a87
1a9cfe8
2a73440
e8029c1
e1b6cc4
30c0889
08ea06a
ff75c55
5453d39
8e3bac8
e0d75e0
1153f48
b549284
e43b30c
3c3a734
796f113
dd83236
73afbfb
a28587a
92ed390
76eeee7
2b7b595
3cf87a5
bc1476f
9753e44
4790411
ca5706c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ | |
using Mono.Cecil.Cil; | ||
|
||
using IL2C.Translators; | ||
using IL2C.Metadata; | ||
|
||
namespace IL2C.ILConverters | ||
{ | ||
|
@@ -78,7 +79,7 @@ public static ExpressionEmitter Prepare(BinaryOperators binaryOperator, DecodeCo | |
extractContext.GetSymbolName(si1)) }; | ||
} | ||
|
||
// Double = (Float) % (Float) | ||
// Double = (Float) op (Float) | ||
if (si0.TargetType.IsFloatStackFriendlyType && si1.TargetType.IsFloatStackFriendlyType && | ||
(binaryOperator == BinaryOperators.Rem)) | ||
{ | ||
|
@@ -105,7 +106,7 @@ public static ExpressionEmitter Prepare(BinaryOperators binaryOperator, DecodeCo | |
} | ||
|
||
// ByRef = (Int32) + (ByRef) | ||
if (si0.TargetType.IsInt32StackFriendlyType && si1.TargetType.IsByReference && | ||
if (si0.TargetType.IsInt32StackFriendlyType && (si1.TargetType.IsByReference) && | ||
(binaryOperator == BinaryOperators.Add)) | ||
{ | ||
var result = decodeContext.PushStack(si1.TargetType); | ||
|
@@ -118,7 +119,7 @@ public static ExpressionEmitter Prepare(BinaryOperators binaryOperator, DecodeCo | |
} | ||
|
||
// ByRef = (IntPtr) + (ByRef) | ||
if (si0.TargetType.IsIntPtrStackFriendlyType && si1.TargetType.IsByReference && | ||
if (si0.TargetType.IsIntPtrStackFriendlyType && (si1.TargetType.IsByReference) && | ||
(binaryOperator == BinaryOperators.Add)) | ||
{ | ||
var result = decodeContext.PushStack(si1.TargetType); | ||
|
@@ -131,7 +132,7 @@ public static ExpressionEmitter Prepare(BinaryOperators binaryOperator, DecodeCo | |
} | ||
|
||
// ByRef = (ByRef) +/- (Int32|IntPtr) | ||
if (si0.TargetType.IsByReference && | ||
if ((si0.TargetType.IsByReference || si0.TargetType.IsArray) && | ||
(si1.TargetType.IsInt32StackFriendlyType || si1.TargetType.IsIntPtrStackFriendlyType) && | ||
((binaryOperator == BinaryOperators.Add) || (binaryOperator == BinaryOperators.Sub))) | ||
{ | ||
|
@@ -234,4 +235,142 @@ public override ExpressionEmitter Prepare(DecodeContext decodeContext) | |
ArithmeticalConverterUtilities.BinaryOperators.Rem, decodeContext); | ||
} | ||
} | ||
|
||
internal sealed class NotConverter : InlineNoneConverter | ||
{ | ||
public override OpCode OpCode => OpCodes.Not; | ||
|
||
public override ExpressionEmitter Prepare(DecodeContext decodeContext) | ||
{ | ||
var si0 = decodeContext.PopStack(); | ||
|
||
if (!(si0.TargetType.IsIntegerPrimitive && !si0.TargetType.IsCharType)) | ||
throw new InvalidProgramSequenceException( | ||
"Invalid arithmetical NOT operation: Location={0}, Type0={1}", | ||
decodeContext.CurrentCode.RawLocation, | ||
si0.TargetType.FriendlyName); | ||
|
||
var result = decodeContext.PushStack(si0.TargetType); | ||
|
||
return (extractContext, _) => new[] { string.Format( | ||
"{0} = ~{1}", | ||
extractContext.GetSymbolName(result), | ||
extractContext.GetSymbolName(si0)) }; | ||
} | ||
} | ||
|
||
internal enum ShiftDirection | ||
{ | ||
Left, Right | ||
} | ||
|
||
internal abstract class ShiftConverter : InlineNoneConverter | ||
{ | ||
public abstract ShiftDirection Direction { get; } | ||
|
||
public ExpressionEmitter Prepare(DecodeContext decodeContext, Func<ILocalVariableInformation, string> cast) | ||
{ | ||
var si1 = decodeContext.PopStack(); | ||
var si0 = decodeContext.PopStack(); | ||
|
||
if (si0.TargetType.IsFloatStackFriendlyType || si0.TargetType.IsByReference || !si1.TargetType.IsInt32Type) | ||
throw new InvalidProgramSequenceException( | ||
"Invalid arithmetical NOT operation: Location={0}, Type0={1}", | ||
decodeContext.CurrentCode.RawLocation, | ||
si0.TargetType.FriendlyName); | ||
|
||
var result = decodeContext.PushStack(si0.TargetType); | ||
|
||
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)) }; | ||
} | ||
} | ||
} | ||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cyborgyn Ouch, I forgot signed integer flag :)
It case, we can use C language shift operator with signed integer type (int32_t, int64_t and intptr_t).
Strictly ignores MSB bit and insert zero.
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:
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cyborgyn The 'neg' opcode calculus is serious with floating point type...
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 At this point, I think it's okay to include TODO comments and write with comment out regression test case. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. 🙂 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) |
||
{ | ||
public override OpCode OpCode => OpCodes.Neg; | ||
|
||
public override ExpressionEmitter Prepare(DecodeContext decodeContext) | ||
{ | ||
var si0 = decodeContext.PopStack(); | ||
Metadata.ILocalVariableInformation result; | ||
|
||
if (si0.TargetType.IsByReference) | ||
throw new InvalidProgramSequenceException( | ||
"Invalid arithmetical NEG operation: Location={0}, Type0={1}", | ||
decodeContext.CurrentCode.RawLocation, | ||
si0.TargetType.FriendlyName); | ||
|
||
if (si0.TargetType.IsInt32StackFriendlyType) | ||
{ | ||
result = decodeContext.PushStack(decodeContext.PrepareContext.MetadataContext.Int32Type); | ||
} | ||
else if (si0.TargetType.IsInt64StackFriendlyType) | ||
{ // Int64 = -(Int64) | ||
result = decodeContext.PushStack(decodeContext.PrepareContext.MetadataContext.Int64Type); | ||
} | ||
else | ||
{ // double = -(double) | ||
result = decodeContext.PushStack(decodeContext.PrepareContext.MetadataContext.DoubleType); | ||
} | ||
|
||
return (extractContext, _) => new[] { string.Format( | ||
"{0} = -{1}", | ||
extractContext.GetSymbolName(result), | ||
extractContext.GetSymbolName(si0)) }; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
///////////////////////////////////////////////////////////////////////////////////////////////// | ||
// | ||
// IL2C - A translator for ECMA-335 CIL/MSIL to C language. | ||
// Copyright (c) 2016-2019 Kouji Matsui (@kozy_kekyo, @kekyo2) | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
///////////////////////////////////////////////////////////////////////////////////////////////// | ||
|
||
using System; | ||
|
||
using Mono.Cecil.Cil; | ||
|
||
using IL2C.Translators; | ||
using IL2C.Metadata; | ||
|
||
namespace IL2C.ILConverters | ||
{ | ||
internal static class StargConverterUtilities | ||
{ | ||
public static ExpressionEmitter Prepare( | ||
int parameterIndex, DecodeContext decodeContext) | ||
{ | ||
var si = decodeContext.PopStack(); | ||
var parameter = decodeContext.Method.Parameters[parameterIndex]; | ||
|
||
// TODO: check target and source type | ||
|
||
return (extractContext, _) => new[] { string.Format( | ||
"{0} = {1}", | ||
parameter.ParameterName, | ||
extractContext.GetSymbolName(si)) }; | ||
} | ||
} | ||
|
||
internal sealed class StargSConverter : ShortInlineParamConverter | ||
{ | ||
public override OpCode OpCode => OpCodes.Starg_S; | ||
|
||
public override ExpressionEmitter Prepare(VariableInformation operand, DecodeContext decodeContext) | ||
{ | ||
return StargConverterUtilities.Prepare(operand.Index, decodeContext); | ||
} | ||
} | ||
|
||
internal sealed class StargConverter : InlineParamConverter | ||
{ | ||
public override OpCode OpCode => OpCodes.Starg; | ||
|
||
public override ExpressionEmitter Prepare(VariableInformation operand, DecodeContext decodeContext) | ||
{ | ||
return StargConverterUtilities.Prepare(operand.Index, decodeContext); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cyborgyn Likely
not
opcode's integer type limitation.ECMA-335: III.3.58 shl - shift integer left
section is written:shiftAmount (
si1
) tricky specification, it's exceptedint64
.We need regression test for
shl
andshr
converter, require these topics:shl
/shr
opcode each types int32 int64 and IntPtr combination with shiftAmount by int32 and IntPtr.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have already added quite an amount of tests especially for shr & shr.un, as they were not that intuitive when they sign extend, and when not. I found this:
// Arithmetic Shift right (with sign)
// 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.
Will try to add the native int shift parameter as test, good point, thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately I needed to comment them out again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I will take a look on my environment, could you tell me explicit commit id reproducible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it valid on newest commit on branch
feature/implement-ilcodes
(ca5706c) ?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Commented: #104 (comment)