Skip to content

Commit 317b4e8

Browse files
committed
Merge branch 'fix/3465'
2 parents 89083ea + ec58d57 commit 317b4e8

File tree

7 files changed

+135
-1
lines changed

7 files changed

+135
-1
lines changed

ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
<None Include="TestCases\ILPretty\Issue2260SwitchString.il" />
102102
<None Include="TestCases\ILPretty\Issue3442.il" />
103103
<None Include="TestCases\ILPretty\Issue3344CkFinite.il" />
104+
<None Include="testcases\ilpretty\Issue3465.il" />
104105
<None Include="TestCases\ILPretty\Issue3466.il" />
105106
<None Include="testcases\ilpretty\Issue3504.il" />
106107
<None Include="testcases\ilpretty\Issue3524.il" />
@@ -145,6 +146,7 @@
145146
<Compile Include="TestAssemblyResolver.cs" />
146147
<Compile Include="TestCases\ILPretty\Issue3344CkFinite.cs" />
147148
<Compile Include="TestCases\ILPretty\Issue3421.cs" />
149+
<Compile Include="TestCases\ILPretty\Issue3465.cs" />
148150
<Compile Include="TestCases\ILPretty\Issue3442.cs" />
149151
<Compile Include="TestCases\ILPretty\Issue3466.cs" />
150152
<Compile Include="TestCases\ILPretty\Issue3524.cs" />

ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,12 @@ public async Task Issue3442()
225225
await Run();
226226
}
227227

228+
[Test]
229+
public async Task Issue3465()
230+
{
231+
await Run();
232+
}
233+
228234
[Test]
229235
public async Task Issue3466()
230236
{
@@ -325,7 +331,7 @@ async Task Run([CallerMemberName] string testName = null, DecompilerSettings set
325331
var executable = await Tester.AssembleIL(ilFile, assemblerOptions).ConfigureAwait(false);
326332
var decompiled = await Tester.DecompileCSharp(executable, settings).ConfigureAwait(false);
327333

328-
CodeAssert.FilesAreEqual(csFile, decompiled);
334+
CodeAssert.FilesAreEqual(csFile, decompiled, ["EXPECTED_OUTPUT"]);
329335
Tester.RepeatOnIOError(() => File.Delete(decompiled));
330336
}
331337

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
#if EXPECTED_OUTPUT
3+
using System.Runtime.CompilerServices;
4+
#endif
5+
namespace Issue3465
6+
{
7+
internal class Program
8+
{
9+
private static Program programNull;
10+
11+
private static Program GetProgram()
12+
{
13+
return null;
14+
}
15+
16+
private static bool Test3465()
17+
{
18+
Program program = GetProgram();
19+
return System.Runtime.CompilerServices.Unsafe.As<Program, UIntPtr>(ref program) > System.Runtime.CompilerServices.Unsafe.As<Program, UIntPtr>(ref programNull);
20+
}
21+
}
22+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
.class private auto ansi '<Module>'
2+
{
3+
} // end of class <Module>
4+
5+
.class private auto ansi beforefieldinit Issue3465.Program
6+
extends [System.Runtime]System.Object
7+
{
8+
// Fields
9+
.field private static class Issue3465.Program programNull
10+
11+
// Methods
12+
.method private hidebysig static
13+
class Issue3465.Program GetProgram () cil managed
14+
{
15+
// Method begins at RVA 0x2050
16+
// Header size: 1
17+
// Code size: 2 (0x2)
18+
.maxstack 8
19+
20+
IL_0000: ldnull
21+
IL_0001: ret
22+
} // end of method Issue3465.Program::GetProgram
23+
24+
.method private hidebysig static
25+
bool Test3465 () cil managed
26+
{
27+
// Method begins at RVA 0x2054
28+
// Header size: 12
29+
// Code size: 7 (0x7)
30+
.maxstack 1
31+
.locals init (
32+
[0] bool
33+
)
34+
35+
IL_0000: call class Issue3465.Program Issue3465.Program::GetProgram()
36+
IL_0001: ldsfld class Issue3465.Program Issue3465.Program::programNull
37+
cgt.un
38+
IL_0002: stloc.0
39+
IL_0003: br.s IL_0005
40+
41+
IL_0005: ldloc.0
42+
IL_0006: ret
43+
} // end of method Program::Test3465
44+
45+
.method public hidebysig specialname rtspecialname
46+
instance void .ctor () cil managed
47+
{
48+
// Method begins at RVA 0x2067
49+
// Header size: 1
50+
// Code size: 8 (0x8)
51+
.maxstack 8
52+
53+
IL_0000: ldarg.0
54+
IL_0001: call instance void [System.Runtime]System.Object::.ctor()
55+
IL_0006: nop
56+
IL_0007: ret
57+
} // end of method Issue3465.Program::.ctor
58+
59+
} // end of class Issue3465.Program
60+

ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,21 @@ TranslatedExpression TranslateComp(Comp inst)
11101110
left = left.ConvertTo(inputType, this);
11111111
right = right.ConvertTo(inputType, this);
11121112
}
1113+
else if (inst.InputType == StackType.O)
1114+
{
1115+
// Unsafe.As<object, UIntPtr>(ref left) op Unsafe.As<object, UIntPtr>(ref right)
1116+
// TTo Unsafe.As<TFrom, TTo>(ref TFrom source)
1117+
var integerType = compilation.FindType(inst.Sign == Sign.Signed ? KnownTypeCode.IntPtr : KnownTypeCode.UIntPtr);
1118+
left = WrapInUnsafeAs(left, inst.Left);
1119+
right = WrapInUnsafeAs(right, inst.Right);
1120+
1121+
TranslatedExpression WrapInUnsafeAs(TranslatedExpression expr, ILInstruction inst)
1122+
{
1123+
var type = expr.Type;
1124+
expr = WrapInRef(expr, new ByReferenceType(type));
1125+
return CallUnsafeIntrinsic("As", [expr], integerType, typeArguments: [type, integerType]);
1126+
}
1127+
}
11131128
return new BinaryOperatorExpression(left.Expression, op, right.Expression)
11141129
.WithILInstruction(inst)
11151130
.WithRR(new OperatorResolveResult(compilation.FindType(TypeCode.Boolean),

ICSharpCode.Decompiler/IL/ILReader.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1852,6 +1852,11 @@ DecodedInstruction DecodeCallIndirect()
18521852

18531853
ILInstruction Comparison(ComparisonKind kind, bool un = false)
18541854
{
1855+
if (!kind.IsEqualityOrInequality() && PeekStackType() == StackType.O)
1856+
{
1857+
FlushExpressionStack();
1858+
}
1859+
18551860
var right = Pop();
18561861
var left = Pop();
18571862
// left will run before right, thus preserving the evaluation order

ICSharpCode.Decompiler/IL/Instructions/Comp.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,5 +229,29 @@ public static Comp LogicNot(ILInstruction arg, bool isLifted)
229229
var liftingKind = isLifted ? ComparisonLiftingKind.ThreeValuedLogic : ComparisonLiftingKind.None;
230230
return new Comp(ComparisonKind.Equality, liftingKind, StackType.I4, Sign.None, arg, new LdcI4(0));
231231
}
232+
233+
internal override bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved)
234+
{
235+
// ExpressionBuilder translates comp.o(a op b) for op not in (==, !=) into
236+
// Unsafe.As(ref a) op Unsafe.As(ref b), which requires that a and b are variables
237+
// and not expressions. Returning false in those cases prevents inlining.
238+
// However if one of the arguments is LdNull, then we don't need the Unsafe.As trickery, and can always inline.
239+
if (kind.IsEqualityOrInequality() || this.InputType != StackType.O)
240+
{
241+
// OK, won't need Unsafe.As.
242+
return true;
243+
}
244+
if (expressionBeingMoved is LdLoc || expressionBeingMoved.MatchLdsFld(out _))
245+
{
246+
// OK, can use variable/field name with Unsafe.As(ref x)
247+
return true;
248+
}
249+
if (Sign != Sign.Signed && (expressionBeingMoved is LdNull || Left is LdNull || Right is LdNull))
250+
{
251+
// OK, this is the "compare with null" special case that doesn't need Unsafe.As()
252+
return true;
253+
}
254+
return false;
255+
}
232256
}
233257
}

0 commit comments

Comments
 (0)