Skip to content

Commit ba8e549

Browse files
authored
Merge pull request #158 from wondercrash/fix/conv-r4-r8
Fix conv.r4/r8 conversion and type hint in ConvHandler
2 parents 96a94fe + 50c9f1d commit ba8e549

File tree

2 files changed

+231
-27
lines changed
  • src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/Misc
  • test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Misc

2 files changed

+231
-27
lines changed

src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/Misc/ConvHandler.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,14 +139,14 @@ private static (StackSlot Result, Trilean Overflow) HandleIntegerConversion(
139139
overflow = false;
140140
result = pool.Rent(32, false);
141141
if (span.IsFullyKnown)
142-
result.AsSpan().Write(span.I32);
142+
result.AsSpan().Write(((float)(isSigned ? span.I64 : span.U64))); // Read as I64/U64 because span was resized to 64 bits, I32/U32 could lose upper bits for large values
143143
break;
144144

145145
case ElementType.R8:
146146
overflow = false;
147147
result = pool.Rent(64, false);
148148
if (span.IsFullyKnown)
149-
result.AsSpan().Write(span.I64);
149+
result.AsSpan().Write((double)(isSigned ? span.I64 : span.U64));
150150
break;
151151

152152
default:
@@ -189,7 +189,6 @@ private static (StackSlot Result, Trilean Overflow) HandleUnknownFloatConversion
189189
case ElementType.U1:
190190
case ElementType.I2:
191191
case ElementType.U2:
192-
case ElementType.R4:
193192
case ElementType.I4:
194193
case ElementType.U4:
195194
case ElementType.I when context.Machine.Is32Bit:
@@ -199,12 +198,19 @@ private static (StackSlot Result, Trilean Overflow) HandleUnknownFloatConversion
199198

200199
case ElementType.I8:
201200
case ElementType.U8:
202-
case ElementType.R8:
203201
case ElementType.I when !context.Machine.Is32Bit:
204202
case ElementType.U when !context.Machine.Is32Bit:
205203
result = new StackSlot(pool.Rent(64, false), StackSlotTypeHint.Integer);
206204
break;
207205

206+
case ElementType.R4:
207+
result = new StackSlot(pool.Rent(32, false), StackSlotTypeHint.Float);
208+
break;
209+
210+
case ElementType.R8:
211+
result = new StackSlot(pool.Rent(64, false), StackSlotTypeHint.Float);
212+
break;
213+
208214
default:
209215
throw new ArgumentOutOfRangeException();
210216
}
Lines changed: 221 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using AsmResolver.PE.DotNet.Cil;
22
using Echo.Memory;
3+
using Echo.Platforms.AsmResolver.Emulation.Stack;
34
using Echo.Platforms.AsmResolver.Tests.Mock;
45
using Xunit;
56

@@ -13,44 +14,241 @@ public ConvHandlerTest(MockModuleFixture fixture)
1314
}
1415

1516
[Theory]
16-
[InlineData(0x7f, CilCode.Conv_I1, 0x7fL)]
17-
[InlineData(0x7f, CilCode.Conv_Ovf_I1, 0x7fL)]
18-
[InlineData(0x80, CilCode.Conv_I1, -0x80L)]
19-
[InlineData(0x80, CilCode.Conv_Ovf_I1, null)]
20-
[InlineData(0x80, CilCode.Conv_I2, 0x80L)]
21-
[InlineData(0x7fff, CilCode.Conv_I2, 0x7fffL)]
22-
[InlineData(0x8000, CilCode.Conv_I2, -0x8000L)]
23-
[InlineData(0x8000, CilCode.Conv_Ovf_I2, null)]
24-
[InlineData(0x7fffffff, CilCode.Conv_I4, 0x7fffffffL)]
25-
[InlineData(0x80000000, CilCode.Conv_I4, -0x80000000L)]
26-
[InlineData(0x80000000, CilCode.Conv_Ovf_I4, null)]
27-
public void ConvIToI(long value, CilCode code, long? expectedValue)
17+
[InlineData(0x7f, CilCode.Conv_I1, 0x7f)]
18+
[InlineData(0x7f, CilCode.Conv_Ovf_I1, 0x7f)]
19+
[InlineData(0x80, CilCode.Conv_I1, -0x80)]
20+
[InlineData(-1, CilCode.Conv_I1, -1)]
21+
[InlineData(0x1ff, CilCode.Conv_I1, -1)]
22+
[InlineData(0x80, CilCode.Conv_I2, 0x80)]
23+
[InlineData(0x7fff, CilCode.Conv_I2, 0x7fff)]
24+
[InlineData(0x8000, CilCode.Conv_I2, -0x8000)]
25+
[InlineData(0x1ffff, CilCode.Conv_I2, -1)]
26+
[InlineData(0x7fffffff, CilCode.Conv_I4, 0x7fffffff)]
27+
[InlineData(0x80000000L, CilCode.Conv_I4, -0x80000000)]
28+
[InlineData(0x1ffffffffL, CilCode.Conv_I4, -1)]
29+
[InlineData(-1, CilCode.Conv_I8, -1)]
30+
[InlineData(0, CilCode.Conv_I8, 0)]
31+
[InlineData(0x7fffffffL, CilCode.Conv_I8, 0x7fffffff)]
32+
public void ConvIToI(long value, CilCode code, long expectedValue)
2833
{
2934
var stack = Context.CurrentFrame.EvaluationStack;
3035

3136
stack.Push(new BitVector(value), Context.Machine.ContextModule.CorLibTypeFactory.Int64);
3237

3338
var result = Dispatcher.Dispatch(Context, new CilInstruction(code.ToOpCode()));
34-
35-
Assert.Equal(expectedValue.HasValue, result.IsSuccess);
36-
if (result.IsSuccess)
37-
Assert.Equal(expectedValue!.Value, stack.Peek().Contents.Resize(64, true).AsSpan().I64);
39+
40+
Assert.True(result.IsSuccess);
41+
var span = stack.Peek().Contents.AsSpan();
42+
if (code.ToOpCode().StackBehaviourPush == CilStackBehaviour.PushI8)
43+
Assert.Equal(expectedValue, span.I64);
44+
else
45+
Assert.Equal((int) expectedValue, span.I32);
46+
}
47+
48+
[Theory]
49+
[InlineData(0x42, CilCode.Conv_U1, 0x42)]
50+
[InlineData(0xff, CilCode.Conv_U1, 0xff)]
51+
[InlineData(0x1ff, CilCode.Conv_U1, 0xff)]
52+
[InlineData(-1, CilCode.Conv_U1, 0xff)]
53+
[InlineData(0x1234, CilCode.Conv_U2, 0x1234)]
54+
[InlineData(0xffff, CilCode.Conv_U2, 0xffff)]
55+
[InlineData(0x1ffff, CilCode.Conv_U2, 0xffff)]
56+
[InlineData(-1, CilCode.Conv_U2, 0xffff)]
57+
[InlineData(0x12345678, CilCode.Conv_U4, 0x12345678)]
58+
[InlineData(0xffffffffL, CilCode.Conv_U4, unchecked((int) 0xffffffff))]
59+
[InlineData(0x1ffffffffL, CilCode.Conv_U4, unchecked((int) 0xffffffff))]
60+
[InlineData(-1, CilCode.Conv_U4, unchecked((int) 0xffffffff))]
61+
[InlineData(42, CilCode.Conv_U8, 42)]
62+
[InlineData(0, CilCode.Conv_U8, 0)]
63+
[InlineData(-1, CilCode.Conv_U8, -1)]
64+
public void ConvIToU(long value, CilCode code, long expectedValue)
65+
{
66+
var stack = Context.CurrentFrame.EvaluationStack;
67+
68+
stack.Push(new BitVector(value), Context.Machine.ContextModule.CorLibTypeFactory.Int64);
69+
70+
var result = Dispatcher.Dispatch(Context, new CilInstruction(code.ToOpCode()));
71+
72+
Assert.True(result.IsSuccess);
73+
var span = stack.Peek().Contents.AsSpan();
74+
if (code.ToOpCode().StackBehaviourPush == CilStackBehaviour.PushI8)
75+
Assert.Equal(expectedValue, span.I64);
76+
else
77+
Assert.Equal((int) expectedValue, span.I32);
78+
}
79+
80+
[Theory]
81+
[InlineData(42, CilCode.Conv_R4, 42.0f)]
82+
[InlineData(-1, CilCode.Conv_R4, -1.0f)]
83+
[InlineData(0, CilCode.Conv_R4, 0.0f)]
84+
[InlineData(0x1_0000_0000L, CilCode.Conv_R4, 4294967296.0f)]
85+
[InlineData((long) int.MaxValue, CilCode.Conv_R4, (float) int.MaxValue)]
86+
[InlineData((long) int.MinValue, CilCode.Conv_R4, (float) int.MinValue)]
87+
public void ConvIToR4(long value, CilCode code, float expectedValue)
88+
{
89+
var stack = Context.CurrentFrame.EvaluationStack;
90+
91+
stack.Push(new BitVector(value), Context.Machine.ContextModule.CorLibTypeFactory.Int64);
92+
93+
var result = Dispatcher.Dispatch(Context, new CilInstruction(code.ToOpCode()));
94+
95+
Assert.True(result.IsSuccess);
96+
Assert.Equal(expectedValue, stack.Peek().Contents.AsSpan().F32);
97+
}
98+
99+
[Theory]
100+
[InlineData(42, CilCode.Conv_R8, 42.0d)]
101+
[InlineData(-1, CilCode.Conv_R8, -1.0d)]
102+
[InlineData(0, CilCode.Conv_R8, 0.0d)]
103+
[InlineData(0x1_0000_0000L, CilCode.Conv_R8, 4294967296.0d)]
104+
[InlineData((long) int.MaxValue, CilCode.Conv_R8, (double) int.MaxValue)]
105+
[InlineData((long) int.MinValue, CilCode.Conv_R8, (double) int.MinValue)]
106+
public void ConvIToR8(long value, CilCode code, double expectedValue)
107+
{
108+
var stack = Context.CurrentFrame.EvaluationStack;
109+
110+
stack.Push(new BitVector(value), Context.Machine.ContextModule.CorLibTypeFactory.Int64);
111+
112+
var result = Dispatcher.Dispatch(Context, new CilInstruction(code.ToOpCode()));
113+
114+
Assert.True(result.IsSuccess);
115+
Assert.Equal(expectedValue, stack.Peek().Contents.AsSpan().F64);
116+
}
117+
118+
[Fact]
119+
public void ConvUnknownFloatToR4ShouldHaveFloatTypeHint()
120+
{
121+
var stack = Context.CurrentFrame.EvaluationStack;
122+
123+
stack.Push(new StackSlot(new BitVector(64, false), StackSlotTypeHint.Float));
124+
125+
var result = Dispatcher.Dispatch(Context, new CilInstruction(CilOpCodes.Conv_R4));
126+
127+
Assert.True(result.IsSuccess);
128+
Assert.Equal(StackSlotTypeHint.Float, stack.Peek().TypeHint);
129+
}
130+
131+
[Fact]
132+
public void ConvUnknownFloatToI4ShouldHaveIntegerTypeHint()
133+
{
134+
var stack = Context.CurrentFrame.EvaluationStack;
135+
136+
stack.Push(new StackSlot(new BitVector(64, false), StackSlotTypeHint.Float));
137+
138+
var result = Dispatcher.Dispatch(Context, new CilInstruction(CilOpCodes.Conv_I4));
139+
140+
Assert.True(result.IsSuccess);
141+
Assert.Equal(StackSlotTypeHint.Integer, stack.Peek().TypeHint);
142+
}
143+
144+
[Theory]
145+
[InlineData(1.0d, CilCode.Conv_I1, 1)]
146+
[InlineData(-1.0d, CilCode.Conv_I1, -1)]
147+
[InlineData(100.9d, CilCode.Conv_I1, 100)]
148+
[InlineData(1.0d, CilCode.Conv_I2, 1)]
149+
[InlineData(-1.0d, CilCode.Conv_I2, -1)]
150+
[InlineData(1000.9d, CilCode.Conv_I2, 1000)]
151+
[InlineData(1.0d, CilCode.Conv_I4, 1)]
152+
[InlineData(-1.0d, CilCode.Conv_I4, -1)]
153+
[InlineData(3.7d, CilCode.Conv_I4, 3)]
154+
[InlineData(-3.7d, CilCode.Conv_I4, -3)]
155+
[InlineData(0.0d, CilCode.Conv_I4, 0)]
156+
[InlineData(0.0d, CilCode.Conv_I8, 0)]
157+
[InlineData(42.0d, CilCode.Conv_I8, 42)]
158+
[InlineData(-1.0d, CilCode.Conv_I8, -1)]
159+
[InlineData(99999.9d, CilCode.Conv_I8, 99999)]
160+
public void ConvRToI(double value, CilCode code, long expectedValue)
161+
{
162+
var stack = Context.CurrentFrame.EvaluationStack;
163+
164+
stack.Push(new BitVector(value), Context.Machine.ContextModule.CorLibTypeFactory.Double);
165+
166+
var result = Dispatcher.Dispatch(Context, new CilInstruction(code.ToOpCode()));
167+
168+
Assert.True(result.IsSuccess);
169+
var span = stack.Peek().Contents.AsSpan();
170+
if (code.ToOpCode().StackBehaviourPush == CilStackBehaviour.PushI8)
171+
Assert.Equal(expectedValue, span.I64);
172+
else
173+
Assert.Equal((int) expectedValue, span.I32);
174+
}
175+
176+
[Theory]
177+
[InlineData(42.0d, CilCode.Conv_U1, 42)]
178+
[InlineData(0.0d, CilCode.Conv_U1, 0)]
179+
[InlineData(200.9d, CilCode.Conv_U1, 200)]
180+
[InlineData(42.0d, CilCode.Conv_U2, 42)]
181+
[InlineData(0.0d, CilCode.Conv_U2, 0)]
182+
[InlineData(50000.9d, CilCode.Conv_U2, 50000)]
183+
[InlineData(42.0d, CilCode.Conv_U4, 42)]
184+
[InlineData(3.7d, CilCode.Conv_U4, 3)]
185+
[InlineData(0.0d, CilCode.Conv_U4, 0)]
186+
[InlineData(42.0d, CilCode.Conv_U8, 42)]
187+
[InlineData(0.0d, CilCode.Conv_U8, 0)]
188+
[InlineData(99999.9d, CilCode.Conv_U8, 99999)]
189+
public void ConvRToU(double value, CilCode code, long expectedValue)
190+
{
191+
var stack = Context.CurrentFrame.EvaluationStack;
192+
193+
stack.Push(new BitVector(value), Context.Machine.ContextModule.CorLibTypeFactory.Double);
194+
195+
var result = Dispatcher.Dispatch(Context, new CilInstruction(code.ToOpCode()));
196+
197+
Assert.True(result.IsSuccess);
198+
var span = stack.Peek().Contents.AsSpan();
199+
if (code.ToOpCode().StackBehaviourPush == CilStackBehaviour.PushI8)
200+
Assert.Equal(expectedValue, span.I64);
201+
else
202+
Assert.Equal((int) expectedValue, span.I32);
38203
}
39204

40205
[Theory]
41-
[InlineData(1.0, CilCode.Conv_I4, 1L)]
42-
[InlineData(-1.0, CilCode.Conv_I4, -1L)]
43-
public void ConvFToI(double value, CilCode code, long? expectedValue)
206+
[InlineData(0x80, CilCode.Conv_Ovf_I1)]
207+
[InlineData(-0x81, CilCode.Conv_Ovf_I1)]
208+
[InlineData(0x8000, CilCode.Conv_Ovf_I2)]
209+
[InlineData(-0x8001, CilCode.Conv_Ovf_I2)]
210+
[InlineData(0x80000000L, CilCode.Conv_Ovf_I4)]
211+
[InlineData(0x100, CilCode.Conv_Ovf_U1)]
212+
[InlineData(-1, CilCode.Conv_Ovf_U1)]
213+
[InlineData(0x10000, CilCode.Conv_Ovf_U2)]
214+
[InlineData(-1, CilCode.Conv_Ovf_U2)]
215+
[InlineData(0x100000000L, CilCode.Conv_Ovf_U4)]
216+
[InlineData(-1, CilCode.Conv_Ovf_U4)]
217+
public void ConvIOvfShouldOverflow(long value, CilCode code)
218+
{
219+
var stack = Context.CurrentFrame.EvaluationStack;
220+
221+
stack.Push(new BitVector(value), Context.Machine.ContextModule.CorLibTypeFactory.Int64);
222+
223+
var result = Dispatcher.Dispatch(Context, new CilInstruction(code.ToOpCode()));
224+
225+
Assert.False(result.IsSuccess);
226+
Assert.False(result.ExceptionObject.IsNull);
227+
Assert.Equal(result.ExceptionObject.GetObjectType(), Context.Machine.ValueFactory.OverflowExceptionType);
228+
}
229+
230+
[Theory]
231+
[InlineData(-1.0d, CilCode.Conv_Ovf_U1)]
232+
[InlineData(256.0d, CilCode.Conv_Ovf_U1)]
233+
[InlineData(-1.0d, CilCode.Conv_Ovf_U2)]
234+
[InlineData(65536.0d, CilCode.Conv_Ovf_U2)]
235+
[InlineData(-1.0d, CilCode.Conv_Ovf_U4)]
236+
[InlineData(-1.0d, CilCode.Conv_Ovf_U8)]
237+
[InlineData(128.0d, CilCode.Conv_Ovf_I1)]
238+
[InlineData(-129.0d, CilCode.Conv_Ovf_I1)]
239+
[InlineData(32768.0d, CilCode.Conv_Ovf_I2)]
240+
[InlineData(-32769.0d, CilCode.Conv_Ovf_I2)]
241+
public void ConvROvfShouldOverflow(double value, CilCode code)
44242
{
45243
var stack = Context.CurrentFrame.EvaluationStack;
46244

47245
stack.Push(new BitVector(value), Context.Machine.ContextModule.CorLibTypeFactory.Double);
48246

49247
var result = Dispatcher.Dispatch(Context, new CilInstruction(code.ToOpCode()));
50-
51-
Assert.Equal(expectedValue.HasValue, result.IsSuccess);
52-
if (result.IsSuccess)
53-
Assert.Equal(expectedValue!.Value, stack.Peek().Contents.Resize(64, true).AsSpan().I64);
248+
249+
Assert.False(result.IsSuccess);
250+
Assert.False(result.ExceptionObject.IsNull);
251+
Assert.Equal(result.ExceptionObject.GetObjectType(), Context.Machine.ValueFactory.OverflowExceptionType);
54252
}
55253
}
56254
}

0 commit comments

Comments
 (0)