From df13f0ce01630c44040a61b96bebee1bca252c32 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 29 Jul 2019 23:59:19 +0200 Subject: [PATCH] Fix #1605: Inline ref locals more aggressively; this is necessary for VB compound assignments. --- .../Helpers/Tester.VB.cs | 4 ++-- .../ICSharpCode.Decompiler.Tests.csproj | 2 ++ .../TestCases/Pretty/RefLocalsAndReturns.cs | 4 +++- .../TestCases/VBPretty/.gitignore | 1 + .../TestCases/VBPretty/VBCompoundAssign.cs | 20 +++++++++++++++++++ .../TestCases/VBPretty/VBCompoundAssign.vb | 14 +++++++++++++ .../VBPrettyTestRunner.cs | 14 +++++++++++-- .../IL/Transforms/ILInlining.cs | 8 +++++++- 8 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/VBPretty/.gitignore create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.cs create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.vb diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs index e09cecda9..55f0f5a3e 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs @@ -56,10 +56,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers var provider = new VBCodeProvider(new Dictionary { { "CompilerVersion", "v4.0" } }); CompilerParameters options = new CompilerParameters(); options.GenerateExecutable = !flags.HasFlag(CompilerOptions.Library); - options.CompilerOptions = "/o" + (flags.HasFlag(CompilerOptions.Optimize) ? "+" : "-"); + options.CompilerOptions = "/optimize" + (flags.HasFlag(CompilerOptions.Optimize) ? "+" : "-"); options.CompilerOptions += (flags.HasFlag(CompilerOptions.UseDebug) ? " /debug" : ""); options.CompilerOptions += (flags.HasFlag(CompilerOptions.Force32Bit) ? " /platform:anycpu32bitpreferred" : ""); - options.CompilerOptions += "/optioninfer+ /optionexplicit+"; + options.CompilerOptions += " /optioninfer+ /optionexplicit+"; if (preprocessorSymbols.Count > 0) { options.CompilerOptions += " /d:" + string.Join(",", preprocessorSymbols.Select(p => $"{p.Key}={p.Value}")); } diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index ad5e8bb47..852443f6c 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -81,6 +81,7 @@ + @@ -243,6 +244,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs index 7f7053b18..385e0932c 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs @@ -193,7 +193,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { ref NormalStruct @ref = ref GetRef(); RefReassignment(ref @ref); - @ref = ref GetRef(); + if (s.GetHashCode() == 0) { + @ref = ref GetRef(); + } RefReassignment(ref @ref.GetHashCode() == 4 ? ref @ref : ref s); } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/.gitignore b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/.gitignore new file mode 100644 index 000000000..6a7461313 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/.gitignore @@ -0,0 +1 @@ +*.dll diff --git a/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.cs b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.cs new file mode 100644 index 000000000..8b8807ec4 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.cs @@ -0,0 +1,20 @@ +using Microsoft.VisualBasic; +using Microsoft.VisualBasic.CompilerServices; + +[StandardModule] +internal sealed class VBCompoundAssign +{ + public static double[] Sum3(int[] v) + { + double[] array = new double[4]; + int num = Information.UBound(v); + checked { + for (int i = 0; i <= num; i += 3) { + array[0] += v[i]; + array[1] += v[i + 1]; + array[2] += v[i + 2]; + } + return array; + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.vb b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.vb new file mode 100644 index 000000000..2dc0c7d96 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.vb @@ -0,0 +1,14 @@ +Imports System +Imports Microsoft.VisualBasic + +Module VBCompoundAssign + Function Sum3(v As Int32()) As Double() + Dim arr(3) As Double + For i = 0 To UBound(v) Step 3 + arr(0) += v(i) + arr(1) += v(i + 1) + arr(2) += v(i + 2) + Next + Return arr + End Function +End Module diff --git a/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs index e8d1d8071..8cf1c28c0 100644 --- a/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs @@ -61,17 +61,27 @@ namespace ICSharpCode.Decompiler.Tests }; [Test, Ignore("Implement VB async/await")] - public void Async([ValueSource("defaultOptions")] CompilerOptions options) + public void Async([ValueSource(nameof(defaultOptions))] CompilerOptions options) { Run(options: options); } + [Test] // TODO: legacy VB compound assign + public void VBCompoundAssign([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions options) + { + Run(options: options | CompilerOptions.Library); + } + void Run([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug, DecompilerSettings settings = null) { var vbFile = Path.Combine(TestCasePath, testName + ".vb"); var csFile = Path.Combine(TestCasePath, testName + ".cs"); + var exeFile = Path.Combine(TestCasePath, testName) + Tester.GetSuffix(options) + ".exe"; + if (options.HasFlag(CompilerOptions.Library)) { + exeFile = Path.ChangeExtension(exeFile, ".dll"); + } - var executable = Tester.CompileVB(vbFile, options); + var executable = Tester.CompileVB(vbFile, options | CompilerOptions.ReferenceVisualBasic, exeFile); var decompiled = Tester.DecompileCSharp(executable.PathToAssembly, settings); CodeAssert.FilesAreEqual(csFile, decompiled); diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 4f7b59d73..f3f642562 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -404,7 +404,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms } break; } - + if (inlinedExpression.ResultType == StackType.Ref) { + // VB likes to use ref locals for compound assignment + // (the C# compiler uses ref stack slots instead). + // We want to avoid unnecessary ref locals, so we'll always inline them if possible. + return true; + } + var parent = loadInst.Parent; if (NullableLiftingTransform.MatchNullableCtor(parent, out _, out _)) { // inline into nullable ctor call in lifted operator