Skip to content

Commit cb48a58

Browse files
authored
Merge pull request #150 from wondercrash/fix/rem-div-unsigned
Handle signed vs unsigned in div & rem opcodes. Also fix tests
2 parents 709afc9 + b135fc5 commit cb48a58

File tree

9 files changed

+152
-47
lines changed

9 files changed

+152
-47
lines changed

src/Core/Echo/Memory/BitVectorSpan.Integer.cs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -609,8 +609,9 @@ private Trilean IntegerMultiplyLle(BitVectorSpan other)
609609
/// Interprets the bit vector as an integer and divides it by a second integer.
610610
/// </summary>
611611
/// <param name="other">The integer to divide the current integer by.</param>
612+
/// <param name="signed">Indicates whether the integers should be interpreted as signed or unsigned integers.</param>
612613
/// <exception cref="ArgumentException">Occurs when the sizes of the integers do not match in bit length.</exception>
613-
public void IntegerDivide(BitVectorSpan other)
614+
public void IntegerDivide(BitVectorSpan other, bool signed)
614615
{
615616
AssertSameBitSize(other);
616617

@@ -624,19 +625,31 @@ public void IntegerDivide(BitVectorSpan other)
624625
switch (Count)
625626
{
626627
case 8:
627-
U8 /= other.U8;
628+
if (signed)
629+
I8 /= other.I8;
630+
else
631+
U8 /= other.U8;
628632
return;
629633

630634
case 16:
631-
U16 /= other.U16;
635+
if (signed)
636+
I16 /= other.I16;
637+
else
638+
U16 /= other.U16;
632639
return;
633640

634641
case 32:
635-
U32 /= other.U32;
642+
if (signed)
643+
I32 /= other.I32;
644+
else
645+
U32 /= other.U32;
636646
return;
637647

638648
case 64:
639-
U64 /= other.U64;
649+
if (signed)
650+
I64 /= other.I64;
651+
else
652+
U64 /= other.U64;
640653
return;
641654

642655
default:
@@ -650,8 +663,9 @@ public void IntegerDivide(BitVectorSpan other)
650663
/// Interprets the bit vector as an integer, divides it by a second integer and produces the remainder.
651664
/// </summary>
652665
/// <param name="other">The integer to divide the current integer by.</param>
666+
/// <param name="signed">Indicates whether the integers should be interpreted as signed or unsigned integers.</param>
653667
/// <exception cref="ArgumentException">Occurs when the sizes of the integers do not match in bit length.</exception>
654-
public void IntegerRemainder(BitVectorSpan other)
668+
public void IntegerRemainder(BitVectorSpan other, bool signed)
655669
{
656670
AssertSameBitSize(other);
657671

@@ -665,19 +679,31 @@ public void IntegerRemainder(BitVectorSpan other)
665679
switch (Count)
666680
{
667681
case 8:
668-
U8 %= other.U8;
682+
if (signed)
683+
I8 %= other.I8;
684+
else
685+
U8 %= other.U8;
669686
return;
670687

671688
case 16:
672-
U16 %= other.U16;
689+
if (signed)
690+
I16 %= other.I16;
691+
else
692+
U16 %= other.U16;
673693
return;
674694

675695
case 32:
676-
U32 %= other.U32;
696+
if (signed)
697+
I32 %= other.I32;
698+
else
699+
U32 %= other.U32;
677700
return;
678701

679702
case 64:
680-
U64 %= other.U64;
703+
if (signed)
704+
I64 %= other.I64;
705+
else
706+
U64 %= other.U64;
681707
return;
682708

683709
default:

src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/Arithmetic/DivHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ protected override CilDispatchResult Evaluate(CilExecutionContext context, CilIn
2323
var argument2Value = argument2.Contents.AsSpan();
2424

2525
if (argument1.TypeHint == StackSlotTypeHint.Integer)
26-
argument1Value.IntegerDivide(argument2Value);
26+
argument1Value.IntegerDivide(argument2Value, IsSignedOperation(instruction));
2727
else
2828
argument1Value.FloatDivide(argument2Value);
2929

src/Platforms/Echo.Platforms.AsmResolver/Emulation/Dispatch/Arithmetic/RemHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ protected override CilDispatchResult Evaluate(
2626
var argument2Value = argument2.Contents.AsSpan();
2727

2828
if (argument1.TypeHint == StackSlotTypeHint.Integer)
29-
argument1Value.IntegerRemainder(argument2Value);
29+
argument1Value.IntegerRemainder(argument2Value, IsSignedOperation(instruction));
3030
else
3131
argument1Value.FloatRemainder(argument2Value);
3232

test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Arithmetic/BinaryOperatorTestBase.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,51 @@ protected void AssertCorrect(CilOpCode code, ulong a, ulong b, ulong expected)
146146
Assert.Equal(64, slot.Contents.Count);
147147
Assert.Equal(expected, slot.Contents.AsSpan().U64);
148148
}
149+
150+
protected void AssertCorrect(CilOpCode code, long a, long b, long expected)
151+
{
152+
var stack = Context.CurrentFrame.EvaluationStack;
153+
154+
stack.Push(new StackSlot(a, StackSlotTypeHint.Integer));
155+
stack.Push(new StackSlot(b, StackSlotTypeHint.Integer));
156+
157+
var result = Dispatcher.Dispatch(Context, new CilInstruction(code));
158+
159+
Assert.True(result.IsSuccess);
160+
var slot = Context.CurrentFrame.EvaluationStack.Peek();
161+
Assert.Equal(64, slot.Contents.Count);
162+
Assert.Equal(expected, slot.Contents.AsSpan().I64);
163+
}
164+
165+
protected void AssertCorrect(CilOpCode code, int a, long b, long expected)
166+
{
167+
var stack = Context.CurrentFrame.EvaluationStack;
168+
169+
stack.Push(new StackSlot(a, StackSlotTypeHint.Integer));
170+
stack.Push(new StackSlot(b, StackSlotTypeHint.Integer));
171+
172+
var result = Dispatcher.Dispatch(Context, new CilInstruction(code));
173+
174+
Assert.True(result.IsSuccess);
175+
var slot = Context.CurrentFrame.EvaluationStack.Peek();
176+
Assert.Equal(64, slot.Contents.Count);
177+
Assert.Equal(expected, slot.Contents.AsSpan().I64);
178+
}
179+
180+
protected void AssertCorrect(CilOpCode code, long a, int b, long expected)
181+
{
182+
var stack = Context.CurrentFrame.EvaluationStack;
183+
184+
stack.Push(new StackSlot(a, StackSlotTypeHint.Integer));
185+
stack.Push(new StackSlot(b, StackSlotTypeHint.Integer));
186+
187+
var result = Dispatcher.Dispatch(Context, new CilInstruction(code));
188+
189+
Assert.True(result.IsSuccess);
190+
var slot = Context.CurrentFrame.EvaluationStack.Peek();
191+
Assert.Equal(64, slot.Contents.Count);
192+
Assert.Equal(expected, slot.Contents.AsSpan().I64);
193+
}
149194

150195
protected void AssertCorrect(CilOpCode code, double a, double b, double expected)
151196
{

test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Arithmetic/DivHandlerTest.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using AsmResolver.PE.DotNet.Cil;
3-
using Echo.Platforms.AsmResolver.Emulation;
43
using Echo.Platforms.AsmResolver.Emulation.Stack;
54
using Echo.Platforms.AsmResolver.Tests.Mock;
65
using Xunit;
@@ -15,18 +14,32 @@ public DivHandlerTest(MockModuleFixture fixture)
1514
}
1615

1716
[Fact]
18-
public void DivI4ToI4() => AssertCorrect(CilOpCodes.Div, 1000u, 20u, 50u);
17+
public void DivI4ToI4() => AssertCorrect(CilOpCodes.Div, -1000, 20, -50);
18+
19+
[Fact]
20+
public void DivI4ToI8() => AssertCorrect(CilOpCodes.Div, -1000, 20L, -50L);
21+
22+
[Fact]
23+
public void DivI8ToI4() => AssertCorrect(CilOpCodes.Div, -1000L, 20, -50L);
24+
25+
[Fact]
26+
public void DivI8ToI8() => AssertCorrect(CilOpCodes.Div, -1000L, 20L, -50L);
27+
28+
[Theory]
29+
[InlineData(1000u, 20u, 50u)]
30+
public void DivU4ToU4(uint a, uint b, uint expected) => AssertCorrect(CilOpCodes.Div_Un, a, b, expected);
1931

2032
[Theory]
2133
[InlineData(1000u, 20ul, 50ul)]
22-
public void DivI4ToI8(uint a, ulong b, ulong expected) => AssertCorrect(CilOpCodes.Div, a, b, expected);
34+
public void DivU4ToU8(uint a, ulong b, ulong expected) => AssertCorrect(CilOpCodes.Div_Un, a, b, expected);
2335

2436
[Theory]
2537
[InlineData(1000ul, 20u, 50ul)]
26-
public void DivI8ToI4(ulong a, uint b, ulong expected) => AssertCorrect(CilOpCodes.Div, a, b, expected);
38+
public void DivU8ToU4(ulong a, uint b, ulong expected) => AssertCorrect(CilOpCodes.Div_Un, a, b, expected);
2739

28-
[Fact]
29-
public void DivI8ToI8() => AssertCorrect(CilOpCodes.Div, 1000ul, 20ul, 50ul);
40+
[Theory]
41+
[InlineData(1000ul, 20ul, 50ul)]
42+
public void DivU8ToU8(ulong a, ulong b, ulong expected) => AssertCorrect(CilOpCodes.Div_Un, a, b, expected);
3043

3144
[Fact]
3245
public void DivR8ToR8() => AssertCorrect(CilOpCodes.Div, 1000.0, 20.0, 50.0);
@@ -47,5 +60,4 @@ public void DivR4ToI4ShouldThrow()
4760
Assert.Equal(typeof(InvalidProgramException).FullName, type.FullName);
4861
}
4962
}
50-
51-
}
63+
}

test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Arithmetic/OrHandlerTest.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ public OrHandlerTest(MockModuleFixture fixture)
1212
}
1313

1414
[Fact]
15-
public void OrI4ToI4() => AssertCorrect(CilOpCodes.Or, 0x22220000u, 0x00001111, 0x22221111);
15+
public void OrI4ToI4() => AssertCorrect(CilOpCodes.Or, 0x22220000, 0x00001111, 0x22221111);
1616

1717
[Fact]
18-
public void OrI4ToI8() => AssertCorrect(CilOpCodes.Or, 0x22222222u, 0x11111111_00000000ul, 0x11111111_22222222ul);
18+
public void OrI4ToI8() => AssertCorrect(CilOpCodes.Or, 0x22222222, 0x11111111_00000000L, 0x11111111_22222222L);
1919

2020
[Fact]
21-
public void OrI8ToI4() => AssertCorrect(CilOpCodes.Or, 0x11111111_00000000ul, 0x22222222u, 0x11111111_22222222ul);
21+
public void OrI8ToI4() => AssertCorrect(CilOpCodes.Or, 0x11111111_00000000L, 0x22222222, 0x11111111_22222222L);
2222

2323
[Fact]
24-
public void OrI8ToI8() => AssertCorrect(CilOpCodes.Or, 0x11111111_00000000ul, 0x00000000_22222222ul, 0x11111111_22222222ul);
24+
public void OrI8ToI8() => AssertCorrect(CilOpCodes.Or, 0x11111111_00000000L, 0x00000000_22222222L, 0x11111111_22222222L);
2525
}
2626
}

test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Arithmetic/RemHandlerTest.cs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,50 @@ public RemHandlerTest(MockModuleFixture fixture)
1414
{
1515
}
1616

17+
[Theory]
18+
[InlineData(-1000, 20, 0)]
19+
[InlineData(-1000, 999, -1)]
20+
public void RemI4ToI4(int a, int b, int expected) => AssertCorrect(CilOpCodes.Rem, a, b, expected);
21+
22+
[Theory]
23+
[InlineData(-1000, 20L, 0L)]
24+
[InlineData(-1000, 999L, -1L)]
25+
public void RemI4ToI8(int a, long b, long expected) => AssertCorrect(CilOpCodes.Rem, a, b, expected);
26+
27+
[Theory]
28+
[InlineData(-1000L, 20, 0L)]
29+
[InlineData(-1000L, 999, -1L)]
30+
public void RemI8ToI4(long a, int b, long expected) => AssertCorrect(CilOpCodes.Rem, a, b, expected);
31+
32+
[Theory]
33+
[InlineData(-1000L, 20L, 0L)]
34+
[InlineData(-1000L, 999L, -1L)]
35+
public void RemI8ToI8(long a, long b, long expected) => AssertCorrect(CilOpCodes.Rem, a, b, expected);
36+
1737
[Theory]
1838
[InlineData(1000u, 20u, 0u)]
1939
[InlineData(1000u, 999u, 1u)]
20-
public void I4ToI4(uint a, uint b, uint expected) => AssertCorrect(CilOpCodes.Rem, a, b, expected);
40+
public void RemU4ToU4(uint a, uint b, uint expected) => AssertCorrect(CilOpCodes.Rem_Un, a, b, expected);
2141

2242
[Theory]
2343
[InlineData(1000u, 20ul, 0ul)]
2444
[InlineData(1000u, 999ul, 1ul)]
25-
public void I4ToI8(uint a, ulong b, ulong expected) => AssertCorrect(CilOpCodes.Rem, a, b, expected);
45+
public void RemU4ToU8(uint a, ulong b, ulong expected) => AssertCorrect(CilOpCodes.Rem_Un, a, b, expected);
2646

2747
[Theory]
2848
[InlineData(1000ul, 20u, 0ul)]
2949
[InlineData(1000ul, 999u, 1ul)]
30-
public void I8ToI4(ulong a, uint b, ulong expected) => AssertCorrect(CilOpCodes.Rem, a, b, expected);
50+
public void RemU8ToU4(ulong a, uint b, ulong expected) => AssertCorrect(CilOpCodes.Rem_Un, a, b, expected);
3151

3252
[Theory]
3353
[InlineData(1000ul, 20ul, 0ul)]
3454
[InlineData(1000ul, 999ul, 1ul)]
35-
public void I8ToI8(ulong a, ulong b, ulong expected) => AssertCorrect(CilOpCodes.Rem, a, b, expected);
55+
public void RemU8ToU8(ulong a, ulong b, ulong expected) => AssertCorrect(CilOpCodes.Rem_Un, a, b, expected);
3656

3757
[Theory]
38-
[InlineData(1000.0, 20.0, 1000.0 % 20.0)]
39-
[InlineData(1000.0, 999.0, 1000.0 % 999.0)]
40-
public void R8ToR8(double a, double b, double expected) => AssertCorrect(CilOpCodes.Rem, a, b, expected);
58+
[InlineData(1000.0, 20.0, 0.0)]
59+
[InlineData(1000.0, 999.0, 1.0)]
60+
public void RemR8ToR8(double a, double b, double expected) => AssertCorrect(CilOpCodes.Rem, a, b, expected);
4161

4262
[Fact]
4363
public void R4ToI4ShouldThrow()
@@ -55,4 +75,4 @@ public void R4ToI4ShouldThrow()
5575
Assert.Equal(typeof(InvalidProgramException).FullName, type.FullName);
5676
}
5777
}
58-
}
78+
}

test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Arithmetic/SubHandlerTest.cs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,22 @@ public SubHandlerTest(MockModuleFixture fixture)
1515
}
1616

1717
[Fact]
18-
public void SubI4ToI4() => AssertCorrect(CilOpCodes.Sub, 0x1234, 0x234u, 0x1000u);
18+
public void SubI4ToI4() => AssertCorrect(CilOpCodes.Sub, 0x1234, 0x234, 0x1000);
1919

20-
[Theory]
21-
[InlineData(1234, 234, 1000)]
22-
[InlineData(0x0000_0000, 1, 0xffff_ffff_ffff_ffff)]
23-
public void SubI4ToI8(uint a, ulong b, ulong expected) => AssertCorrect(CilOpCodes.Sub, a, b, expected);
24-
25-
[Theory]
26-
[InlineData(1234, 234, 1000)]
27-
[InlineData(0x0000_0000, 1, 0xffff_ffff_ffff_ffff)]
28-
public void SubI8ToI4(ulong a, uint b, ulong expected) => AssertCorrect(CilOpCodes.Sub, a, b, expected);
20+
[Fact]
21+
public void SubI4ToI8() => AssertCorrect(CilOpCodes.Sub, 1234, 234L, 1000L);
2922

3023
[Fact]
31-
public void SubI8ToI8() => AssertCorrect(CilOpCodes.Sub, 0x1234ul, 0x1000ul, 0x234ul);
24+
public void SubU4ToU8() => AssertCorrect(CilOpCodes.Sub, 0x0000_0000, 1L, 0xffff_ffff_ffff_ffffL);
25+
26+
[Fact]
27+
public void SubI8ToI4() => AssertCorrect(CilOpCodes.Sub, 1234L, 234, 1000L);
28+
29+
[Fact]
30+
public void SubU8ToU4() => AssertCorrect(CilOpCodes.Sub, 0x0000_0000L, 1, 0xffff_ffff_ffff_ffffL);
31+
32+
[Fact]
33+
public void SubI8ToI8() => AssertCorrect(CilOpCodes.Sub, 0x1234L, 0x1000L, 0x234L);
3234

3335
[Fact]
3436
public void SubR8ToR8() => AssertCorrect(CilOpCodes.Sub, 6.912D, 1.234D, 5.678D);

test/Platforms/Echo.Platforms.AsmResolver.Tests/Emulation/Dispatch/Arithmetic/XorHandlerTest.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ public XorHandlerTest(MockModuleFixture fixture)
1212
}
1313

1414
[Fact]
15-
public void XorI4ToI4() => AssertCorrect(CilOpCodes.Xor, 0x22220000u, 0x00001111, 0x22221111);
15+
public void XorI4ToI4() => AssertCorrect(CilOpCodes.Xor, 0x22220000, 0x00001111, 0x22221111);
1616

1717
[Fact]
18-
public void XorI4ToI8() => AssertCorrect(CilOpCodes.Xor, 0x22222222u, 0x11111111_00000000ul, 0x11111111_22222222ul);
18+
public void XorI4ToI8() => AssertCorrect(CilOpCodes.Xor, 0x22222222, 0x11111111_00000000L, 0x11111111_22222222);
1919

2020
[Fact]
21-
public void XorI8ToI4() => AssertCorrect(CilOpCodes.Xor, 0x11111111_00000000ul, 0x22222222u, 0x11111111_22222222ul);
21+
public void XorI8ToI4() => AssertCorrect(CilOpCodes.Xor, 0x11111111_00000000L, 0x22222222, 0x11111111_22222222);
2222

2323
[Fact]
24-
public void XorI8ToI8() => AssertCorrect(CilOpCodes.Xor, 0x11111111_00000000ul, 0x00000000_22222222ul, 0x11111111_22222222ul);
24+
public void XorI8ToI8() => AssertCorrect(CilOpCodes.Xor, 0x11111111_00000000L, 0x00000000_22222222L, 0x11111111_22222222L);
2525
}
2626
}

0 commit comments

Comments
 (0)