Flee
This commit is contained in:
136
ExpressionElements/Shift.cs
Normal file
136
ExpressionElements/Shift.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements
|
||||
{
|
||||
internal class ShiftElement : BinaryExpressionElement
|
||||
{
|
||||
private ShiftOperation _myOperation;
|
||||
|
||||
public ShiftElement()
|
||||
{
|
||||
}
|
||||
|
||||
protected override System.Type GetResultType(System.Type leftType, System.Type rightType)
|
||||
{
|
||||
// Right argument (shift count) must be convertible to int32
|
||||
if (ImplicitConverter.EmitImplicitNumericConvert(rightType, typeof(Int32), null) == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Left argument must be an integer type
|
||||
if (Utility.IsIntegralType(leftType) == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
TypeCode tc = Type.GetTypeCode(leftType);
|
||||
|
||||
switch (tc)
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.Int32:
|
||||
return typeof(Int32);
|
||||
case TypeCode.UInt32:
|
||||
return typeof(UInt32);
|
||||
case TypeCode.Int64:
|
||||
return typeof(Int64);
|
||||
case TypeCode.UInt64:
|
||||
return typeof(UInt64);
|
||||
default:
|
||||
Debug.Assert(false, "unknown left shift operand");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void GetOperation(object operation)
|
||||
{
|
||||
_myOperation = (ShiftOperation)operation;
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
MyLeftChild.Emit(ilg, services);
|
||||
this.EmitShiftCount(ilg, services);
|
||||
this.EmitShift(ilg);
|
||||
}
|
||||
|
||||
// If the shift count is greater than the number of bits in the number, the result is undefined.
|
||||
// So we play it safe and force the shift count to 32/64 bits by ANDing it with the appropriate mask.
|
||||
private void EmitShiftCount(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
MyRightChild.Emit(ilg, services);
|
||||
TypeCode tc = Type.GetTypeCode(MyLeftChild.ResultType);
|
||||
switch (tc)
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.UInt32:
|
||||
ilg.Emit(OpCodes.Ldc_I4_S, Convert.ToSByte(0x1f));
|
||||
break;
|
||||
case TypeCode.Int64:
|
||||
case TypeCode.UInt64:
|
||||
ilg.Emit(OpCodes.Ldc_I4_S, Convert.ToSByte(0x3f));
|
||||
break;
|
||||
default:
|
||||
Debug.Assert(false, "unknown left shift operand");
|
||||
break;
|
||||
}
|
||||
|
||||
ilg.Emit(OpCodes.And);
|
||||
}
|
||||
|
||||
private void EmitShift(FleeILGenerator ilg)
|
||||
{
|
||||
TypeCode tc = Type.GetTypeCode(MyLeftChild.ResultType);
|
||||
OpCode op = default(OpCode);
|
||||
|
||||
switch (tc)
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.Int64:
|
||||
// Signed operand, emit a left shift or arithmetic right shift
|
||||
if (_myOperation == ShiftOperation.LeftShift)
|
||||
{
|
||||
op = OpCodes.Shl;
|
||||
}
|
||||
else
|
||||
{
|
||||
op = OpCodes.Shr;
|
||||
}
|
||||
break;
|
||||
case TypeCode.UInt32:
|
||||
case TypeCode.UInt64:
|
||||
// Unsigned operand, emit left shift or logical right shift
|
||||
if (_myOperation == ShiftOperation.LeftShift)
|
||||
{
|
||||
op = OpCodes.Shl;
|
||||
}
|
||||
else
|
||||
{
|
||||
op = OpCodes.Shr_Un;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Debug.Assert(false, "unknown left shift operand");
|
||||
break;
|
||||
}
|
||||
|
||||
ilg.Emit(op);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user