Browse Source

More strictly test the inline assignment pattern.

pull/960/head
Daniel Grunwald 8 years ago
parent
commit
385005b2e5
  1. 17
      ICSharpCode.Decompiler/IL/ILVariable.cs
  2. 12
      ICSharpCode.Decompiler/IL/Instructions.cs
  3. 8
      ICSharpCode.Decompiler/IL/Instructions.tt
  4. 31
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

17
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -335,8 +335,23 @@ namespace ICSharpCode.Decompiler.IL
{ {
output.WriteReference(this.Name, this, isLocal: true); output.WriteReference(this.Name, this, isLocal: true);
} }
/// <summary>
/// Gets whether this variable occurs within the specified instruction.
/// </summary>
internal bool IsUsedWithin(ILInstruction inst)
{
if (inst is IInstructionWithVariableOperand iwvo && iwvo.Variable == this) {
return true;
}
foreach (var child in inst.Children) {
if (IsUsedWithin(child))
return true;
}
return false;
}
} }
public interface IInstructionWithVariableOperand public interface IInstructionWithVariableOperand
{ {
ILVariable Variable { get; set; } ILVariable Variable { get; set; }

12
ICSharpCode.Decompiler/IL/Instructions.cs

@ -93,7 +93,8 @@ namespace ICSharpCode.Decompiler.IL
LdLoc, LdLoc,
/// <summary>Loads the address of a local variable. (ldarga/ldloca)</summary> /// <summary>Loads the address of a local variable. (ldarga/ldloca)</summary>
LdLoca, LdLoca,
/// <summary>Stores a value into a local variable. (starg/stloc)</summary> /// <summary>Stores a value into a local variable. (IL: starg/stloc)
/// Evaluates to the value that was stored (for byte/short variables: evaluates to the truncated value)</summary>
StLoc, StLoc,
/// <summary>Stores the value into an anonymous temporary variable, and returns the address of that variable.</summary> /// <summary>Stores the value into an anonymous temporary variable, and returns the address of that variable.</summary>
AddressOf, AddressOf,
@ -137,7 +138,8 @@ namespace ICSharpCode.Decompiler.IL
IsInst, IsInst,
/// <summary>Indirect load (ref/pointer dereference).</summary> /// <summary>Indirect load (ref/pointer dereference).</summary>
LdObj, LdObj,
/// <summary>Indirect store (store to ref/pointer).</summary> /// <summary>Indirect store (store to ref/pointer).
/// Evaluates to the value that was stored (when using type byte/short: evaluates to the truncated value)</summary>
StObj, StObj,
/// <summary>Boxes a value.</summary> /// <summary>Boxes a value.</summary>
Box, Box,
@ -2217,7 +2219,8 @@ namespace ICSharpCode.Decompiler.IL
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{ {
/// <summary>Stores a value into a local variable. (starg/stloc)</summary> /// <summary>Stores a value into a local variable. (IL: starg/stloc)
/// Evaluates to the value that was stored (for byte/short variables: evaluates to the truncated value)</summary>
public sealed partial class StLoc : ILInstruction, IStoreInstruction public sealed partial class StLoc : ILInstruction, IStoreInstruction
{ {
public StLoc(ILVariable variable, ILInstruction value) : base(OpCode.StLoc) public StLoc(ILVariable variable, ILInstruction value) : base(OpCode.StLoc)
@ -3540,7 +3543,8 @@ namespace ICSharpCode.Decompiler.IL
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{ {
/// <summary>Indirect store (store to ref/pointer).</summary> /// <summary>Indirect store (store to ref/pointer).
/// Evaluates to the value that was stored (when using type byte/short: evaluates to the truncated value)</summary>
public sealed partial class StObj : ILInstruction, ISupportsVolatilePrefix, ISupportsUnalignedPrefix public sealed partial class StObj : ILInstruction, ISupportsVolatilePrefix, ISupportsUnalignedPrefix
{ {
public StObj(ILInstruction target, ILInstruction value, IType type) : base(OpCode.StObj) public StObj(ILInstruction target, ILInstruction value, IType type) : base(OpCode.StObj)

8
ICSharpCode.Decompiler/IL/Instructions.tt

@ -150,7 +150,8 @@
CustomClassName("LdLoc"), NoArguments, HasVariableOperand("Load"), ResultType("variable.StackType")), CustomClassName("LdLoc"), NoArguments, HasVariableOperand("Load"), ResultType("variable.StackType")),
new OpCode("ldloca", "Loads the address of a local variable. (ldarga/ldloca)", new OpCode("ldloca", "Loads the address of a local variable. (ldarga/ldloca)",
CustomClassName("LdLoca"), NoArguments, ResultType("Ref"), HasVariableOperand("Address")), CustomClassName("LdLoca"), NoArguments, ResultType("Ref"), HasVariableOperand("Address")),
new OpCode("stloc", "Stores a value into a local variable. (starg/stloc)", new OpCode("stloc", "Stores a value into a local variable. (IL: starg/stloc)" + Environment.NewLine
+ "Evaluates to the value that was stored (for byte/short variables: evaluates to the truncated value)",
CustomClassName("StLoc"), HasVariableOperand("Store"), CustomArguments("value"), CustomClassName("StLoc"), HasVariableOperand("Store"), CustomArguments("value"),
ResultType("variable.StackType")), ResultType("variable.StackType")),
new OpCode("addressof", "Stores the value into an anonymous temporary variable, and returns the address of that variable.", new OpCode("addressof", "Stores the value into an anonymous temporary variable, and returns the address of that variable.",
@ -204,10 +205,11 @@
new OpCode("ldobj", "Indirect load (ref/pointer dereference).", new OpCode("ldobj", "Indirect load (ref/pointer dereference).",
CustomClassName("LdObj"), CustomArguments("target"), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal, CustomClassName("LdObj"), CustomArguments("target"), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal,
SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")), SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")),
new OpCode("stobj", "Indirect store (store to ref/pointer).", new OpCode("stobj", "Indirect store (store to ref/pointer)." + Environment.NewLine
+ "Evaluates to the value that was stored (when using type byte/short: evaluates to the truncated value)",
CustomClassName("StObj"), CustomArguments("target", "value"), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal, CustomClassName("StObj"), CustomArguments("target", "value"), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal,
SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")), SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")),
new OpCode("box", "Boxes a value.", new OpCode("box", "Boxes a value.",
Unary, HasTypeOperand, MemoryAccess, MayThrow, ResultType("O")), Unary, HasTypeOperand, MemoryAccess, MayThrow, ResultType("O")),
new OpCode("unbox", "Compute address inside box.", new OpCode("unbox", "Compute address inside box.",

31
ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
block.Instructions.RemoveAt(i); block.Instructions.RemoveAt(i);
continue; continue;
} }
if (TransformInlineAssignmentStObj(block, i)) if (TransformInlineAssignmentStObj(block, i) || TransformInlineAssignmentLocal(block, i))
continue; continue;
if (TransformInlineCompoundAssignmentCall(block, i)) if (TransformInlineCompoundAssignmentCall(block, i))
continue; continue;
@ -59,13 +59,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// --> /// -->
/// stloc l(stobj (..., value)) /// stloc l(stobj (..., value))
/// </code> /// </code>
/// e.g. used for inline assignment to instance field
///
/// -or- /// -or-
///
/// <code> /// <code>
/// stloc s(value) /// stloc s(value)
/// stobj (..., ldloc s) /// stobj (..., ldloc s)
/// --> /// -->
/// stloc s(stobj (..., value)) /// stloc s(stobj (..., value))
/// </code> /// </code>
/// e.g. used for inline assignment to static field
bool TransformInlineAssignmentStObj(Block block, int i) bool TransformInlineAssignmentStObj(Block block, int i)
{ {
var inst = block.Instructions[i] as StLoc; var inst = block.Instructions[i] as StLoc;
@ -76,27 +80,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILInstruction replacement; ILInstruction replacement;
StObj fieldStore; StObj fieldStore;
ILVariable local; ILVariable local;
if (nextInst is StLoc) { // instance fields if (nextInst is StLoc localStore) { // with extra local
var localStore = (StLoc)nextInst; if (localStore.Variable.Kind != VariableKind.Local || !localStore.Value.MatchLdLoc(inst.Variable))
if (localStore.Variable.Kind == VariableKind.StackSlot || !localStore.Value.MatchLdLoc(inst.Variable)) return false;
if (!(inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 2))
return false; return false;
var memberStore = block.Instructions.ElementAtOrDefault(i + 2); var memberStore = block.Instructions.ElementAtOrDefault(i + 2);
if (memberStore is StObj) { if (memberStore is StObj) {
fieldStore = memberStore as StObj; fieldStore = memberStore as StObj;
if (!fieldStore.Value.MatchLdLoc(inst.Variable)) if (!fieldStore.Value.MatchLdLoc(inst.Variable) || localStore.Variable.IsUsedWithin(fieldStore.Target))
return false; return false;
replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type); replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type);
} else { // otherwise it must be local } else {
return TransformInlineAssignmentLocal(block, i); return false;
} }
context.Step("Inline assignment to instance field", fieldStore); context.Step("Inline assignment stobj (with extra local)", fieldStore);
local = localStore.Variable; local = localStore.Variable;
block.Instructions.RemoveAt(i + 1); block.Instructions.RemoveAt(i + 1);
} else if (nextInst is StObj) { // static fields } else if (nextInst is StObj) { // without extra local
fieldStore = (StObj)nextInst; fieldStore = (StObj)nextInst;
if (!fieldStore.Value.MatchLdLoc(inst.Variable) || (fieldStore.Target.MatchLdFlda(out var target, out _) && target.MatchLdLoc(inst.Variable))) if (!fieldStore.Value.MatchLdLoc(inst.Variable) || inst.Variable.IsUsedWithin(fieldStore.Target))
return false; return false;
context.Step("Inline assignment to static field", fieldStore); context.Step("Inline assignment stobj", fieldStore);
local = inst.Variable; local = inst.Variable;
replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type); replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type);
} else { } else {
@ -246,7 +251,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var nextInst = block.Instructions.ElementAtOrDefault(i + 1) as StLoc; var nextInst = block.Instructions.ElementAtOrDefault(i + 1) as StLoc;
if (inst == null || nextInst == null) if (inst == null || nextInst == null)
return false; return false;
if (nextInst.Variable.Kind == VariableKind.StackSlot || !nextInst.Value.MatchLdLoc(inst.Variable)) if (inst.Variable.Kind != VariableKind.StackSlot)
return false;
if (nextInst.Variable.Kind != VariableKind.Local || !nextInst.Value.MatchLdLoc(inst.Variable))
return false; return false;
context.Step("Inline assignment to local variable", inst); context.Step("Inline assignment to local variable", inst);
var value = inst.Value; var value = inst.Value;

Loading…
Cancel
Save