Browse Source

Allow splitting variables that have their address taken.

This works if all addresses are immediately used in calls (as common with method calls on value-type,
which take 'this' by-reference); as long as the call doesn't return the reference again.

Closes #1136.
pull/1213/head
Daniel Grunwald 7 years ago
parent
commit
d166101387
  1. 6
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpUsing_Debug.cs
  2. 6
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpUsing_Release.cs
  3. 1
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  4. 22
      ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs

6
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpUsing_Debug.cs

@ -31,7 +31,7 @@ public static class FSharpUsingPatterns @@ -31,7 +31,7 @@ public static class FSharpUsingPatterns
public static void sample4()
{
Console.WriteLine("some text");
int num = default(int);
int num;
using (FileStream fileStream = File.OpenRead("x.txt")) {
num = fileStream.ReadByte();
}
@ -42,12 +42,12 @@ public static class FSharpUsingPatterns @@ -42,12 +42,12 @@ public static class FSharpUsingPatterns
public static void sample5()
{
Console.WriteLine("some text");
int num = default(int);
int num;
using (FileStream fileStream = File.OpenRead("x.txt")) {
num = fileStream.ReadByte();
}
int num2 = num;
int num3 = default(int);
int num3;
using (FileStream fileStream = File.OpenRead("x.txt")) {
fileStream.ReadByte();
num3 = fileStream.ReadByte();

6
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpUsing_Release.cs

@ -31,7 +31,7 @@ public static class FSharpUsingPatterns @@ -31,7 +31,7 @@ public static class FSharpUsingPatterns
public static void sample4()
{
Console.WriteLine("some text");
int num = default(int);
int num;
using (FileStream fileStream = File.OpenRead("x.txt")) {
num = fileStream.ReadByte();
}
@ -42,12 +42,12 @@ public static class FSharpUsingPatterns @@ -42,12 +42,12 @@ public static class FSharpUsingPatterns
public static void sample5()
{
Console.WriteLine("some text");
int num = default(int);
int num;
using (FileStream fileStream = File.OpenRead("x.txt")) {
num = fileStream.ReadByte();
}
int num2 = num;
int num3 = default(int);
int num3;
using (FileStream fileStream = File.OpenRead("x.txt")) {
fileStream.ReadByte();
num3 = fileStream.ReadByte();

1
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -799,6 +799,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -799,6 +799,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
name = fieldDef.Name.Substring(1, pos - 1);
}
v = function.RegisterVariable(VariableKind.Local, ldflda.Field.ReturnType, name);
v.HasInitialValue = true; // the field was default-initialized, so keep those semantics for the variable
v.StateMachineField = ldflda.Field;
fieldToVariableMap.Add(fieldDef, v);
}

22
ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs

@ -99,6 +99,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -99,6 +99,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (p.Type.SkipModifiers() is ByReferenceType brt && brt.ElementType.IsByRefLike)
return AddressUse.Unknown;
}
/* Currently there's not really any need to distinguish between readonly and readwrite method calls:
var addrParam = call.GetParameter(addressLoadingInstruction.ChildIndex);
bool isReadOnly;
if (addrParam == null) {
@ -106,8 +107,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -106,8 +107,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|| (call.Method.DeclaringType?.IsKnownType(KnownTypeCode.NullableOfT) ?? false);
} else {
isReadOnly = false;
}
return isReadOnly ? AddressUse.LocalRead : AddressUse.Unknown; // TODO AddressUse.LocalReadWrite;
}*/
return AddressUse.LocalReadWrite;
default:
return AddressUse.Unknown;
}
@ -122,7 +123,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -122,7 +123,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
class GroupStores : ReachingDefinitionsVisitor
{
readonly UnionFind<IInstructionWithVariableOperand> unionFind = new UnionFind<IInstructionWithVariableOperand>();
readonly HashSet<IInstructionWithVariableOperand> uninitVariableUsage = new HashSet<IInstructionWithVariableOperand>();
/// <summary>
/// For each uninitialized variable, one representative instruction that
/// potentially observes the unintialized value of the variable.
/// Used to merge together all such loads of the same uninitialized value.
/// </summary>
readonly Dictionary<ILVariable, IInstructionWithVariableOperand> uninitVariableUsage = new Dictionary<ILVariable, IInstructionWithVariableOperand>();
public GroupStores(ILFunction scope, CancellationToken cancellationToken) : base(scope, IsCandidateVariable, cancellationToken)
{
@ -144,7 +151,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -144,7 +151,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
if (IsAnalyzedVariable(inst.Variable)) {
if (IsPotentiallyUninitialized(state, inst.Variable)) {
uninitVariableUsage.Add(inst);
// merge all uninit loads together:
if (uninitVariableUsage.TryGetValue(inst.Variable, out var uninitLoad)) {
unionFind.Merge(inst, uninitLoad);
} else {
uninitVariableUsage.Add(inst.Variable, inst);
}
}
foreach (var store in GetStores(state, inst.Variable)) {
unionFind.Merge(inst, (IInstructionWithVariableOperand)store);
@ -170,7 +182,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -170,7 +182,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
newVariables.Add(representative, v);
inst.Variable.Function.Variables.Add(v);
}
if (uninitVariableUsage.Contains(inst)) {
if (inst.Variable.HasInitialValue && uninitVariableUsage.TryGetValue(inst.Variable, out var uninitLoad) && uninitLoad == inst) {
v.HasInitialValue = true;
}
return v;

Loading…
Cancel
Save