Browse Source

First version of variable inlining.

Added ILAst SlotInfo.
pull/728/head
Daniel Grunwald 11 years ago
parent
commit
1720fe5bba
  1. 3
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  3. 65
      ICSharpCode.Decompiler/IL/ILReader.cs
  4. 14
      ICSharpCode.Decompiler/IL/ILVariable.cs
  5. 20
      ICSharpCode.Decompiler/IL/InstructionFlags.cs
  6. 416
      ICSharpCode.Decompiler/IL/Instructions.cs
  7. 66
      ICSharpCode.Decompiler/IL/Instructions.tt
  8. 47
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  9. 12
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  10. 20
      ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs
  11. 6
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  12. 45
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  13. 7
      ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
  14. 20
      ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
  15. 10
      ICSharpCode.Decompiler/IL/Instructions/Return.cs
  16. 32
      ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs
  17. 24
      ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs
  18. 53
      ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs
  19. 24
      ICSharpCode.Decompiler/IL/SemanticHelper.cs
  20. 44
      ICSharpCode.Decompiler/IL/SlotInfo.cs
  21. 310
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  22. 4
      ICSharpCode.Decompiler/IL/Transforms/OptimizingTransform.cs
  23. 3
      ICSharpCode.Decompiler/IL/Transforms/TransformingVisitor.cs
  24. 1
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  25. 62
      ICSharpCode.Decompiler/Tests/TestCases/ValueTypeCall.cs
  26. 6
      ICSharpCode.Decompiler/Tests/TestRunner.cs

3
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -44,6 +44,7 @@ namespace ICSharpCode.Decompiler.CSharp
new OptimizingTransform(), new OptimizingTransform(),
new LoopDetection(), new LoopDetection(),
new ControlFlowSimplification(), new ControlFlowSimplification(),
new ILInlining(),
new TransformingVisitor() new TransformingVisitor()
}; };
@ -269,6 +270,8 @@ namespace ICSharpCode.Decompiler.CSharp
// insert variables at start of body // insert variables at start of body
Statement prevVarDecl = null; Statement prevVarDecl = null;
foreach (var v in function.Variables) { foreach (var v in function.Variables) {
if (v.LoadCount == 0 && v.StoreCount == 0 && v.AddressCount == 0)
continue;
if (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot) { if (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot) {
var type = typeSystemAstBuilder.ConvertType(v.Type); var type = typeSystemAstBuilder.ConvertType(v.Type);
var varDecl = new VariableDeclarationStatement(type, v.Name); var varDecl = new VariableDeclarationStatement(type, v.Name);

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -105,8 +105,10 @@
<Compile Include="IL\Instructions\UnaryInstruction.cs" /> <Compile Include="IL\Instructions\UnaryInstruction.cs" />
<Compile Include="IL\IInlineContext.cs" /> <Compile Include="IL\IInlineContext.cs" />
<Compile Include="IL\NRTypeExtensions.cs" /> <Compile Include="IL\NRTypeExtensions.cs" />
<Compile Include="IL\SlotInfo.cs" />
<Compile Include="IL\Transforms\ControlFlowSimplification.cs" /> <Compile Include="IL\Transforms\ControlFlowSimplification.cs" />
<Compile Include="IL\Transforms\IILTransform.cs" /> <Compile Include="IL\Transforms\IILTransform.cs" />
<Compile Include="IL\Transforms\ILInlining.cs" />
<Compile Include="IL\Transforms\LoopDetection.cs" /> <Compile Include="IL\Transforms\LoopDetection.cs" />
<Compile Include="IL\Transforms\OptimizingTransform.cs" /> <Compile Include="IL\Transforms\OptimizingTransform.cs" />
<Compile Include="IL\Transforms\TransformingVisitor.cs" /> <Compile Include="IL\Transforms\TransformingVisitor.cs" />

65
ICSharpCode.Decompiler/IL/ILReader.cs

@ -150,7 +150,8 @@ namespace ICSharpCode.Decompiler.IL
/// <summary> /// <summary>
/// Warn when invalid IL is detected. /// Warn when invalid IL is detected.
/// ILSpy should be able to handle invalid IL; but this method can be helpful for debugging the ILReader, as this method should not get called when processing valid IL. /// ILSpy should be able to handle invalid IL; but this method can be helpful for debugging the ILReader,
/// as this method should not get called when processing valid IL.
/// </summary> /// </summary>
void Warn(string message) void Warn(string message)
{ {
@ -207,6 +208,7 @@ namespace ICSharpCode.Decompiler.IL
Warn("Unknown result type (might be due to invalid IL)"); Warn("Unknown result type (might be due to invalid IL)");
decodedInstruction.CheckInvariant(); decodedInstruction.CheckInvariant();
decodedInstruction.ILRange = new Interval(start, reader.Position); decodedInstruction.ILRange = new Interval(start, reader.Position);
UnpackPush(decodedInstruction).ILRange = decodedInstruction.ILRange;
instructionBuilder.Add(decodedInstruction); instructionBuilder.Add(decodedInstruction);
if (decodedInstruction.HasFlag(InstructionFlags.EndPointUnreachable)) { if (decodedInstruction.HasFlag(InstructionFlags.EndPointUnreachable)) {
if (!stackByOffset.TryGetValue(reader.Position, out currentStack)) { if (!stackByOffset.TryGetValue(reader.Position, out currentStack)) {
@ -269,6 +271,16 @@ namespace ICSharpCode.Decompiler.IL
return function; return function;
} }
static ILInstruction UnpackPush(ILInstruction inst)
{
ILVariable v;
ILInstruction inner;
if (inst.MatchStLoc(out v, out inner) && v.Kind == VariableKind.StackSlot)
return inner;
else
return inst;
}
ILInstruction Neg() ILInstruction Neg()
{ {
switch (PeekStackType()) { switch (PeekStackType()) {
@ -280,7 +292,7 @@ namespace ICSharpCode.Decompiler.IL
case StackType.F: case StackType.F:
return Push(new Sub(new LdcF(0), Pop(), checkForOverflow: false, sign: Sign.None)); return Push(new Sub(new LdcF(0), Pop(), checkForOverflow: false, sign: Sign.None));
default: default:
Warn("Unsupported input type for neg: "); Warn("Unsupported input type for neg.");
goto case StackType.I4; goto case StackType.I4;
} }
} }
@ -669,14 +681,7 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Stsfld: case ILOpCode.Stsfld:
return new StsFld(Pop(), ReadAndDecodeFieldReference()); return new StsFld(Pop(), ReadAndDecodeFieldReference());
case ILOpCode.Ldtoken: case ILOpCode.Ldtoken:
var memberReference = ReadAndDecodeMetadataToken() as MemberReference; return Push(LdToken(ReadAndDecodeMetadataToken()));
if (memberReference is TypeReference)
return new LdTypeToken(typeSystem.Resolve((TypeReference)memberReference));
if (memberReference is FieldReference)
return new LdMemberToken(typeSystem.Resolve((FieldReference)memberReference));
if (memberReference is MethodReference)
return new LdMemberToken(typeSystem.Resolve((MethodReference)memberReference));
throw new NotImplementedException();
case ILOpCode.Ldvirtftn: case ILOpCode.Ldvirtftn:
return Push(new LdVirtFtn(Pop(), ReadAndDecodeMethodReference())); return Push(new LdVirtFtn(Pop(), ReadAndDecodeMethodReference()));
case ILOpCode.Mkrefany: case ILOpCode.Mkrefany:
@ -690,7 +695,7 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Rethrow: case ILOpCode.Rethrow:
return new Rethrow(); return new Rethrow();
case ILOpCode.Sizeof: case ILOpCode.Sizeof:
return new SizeOf(ReadAndDecodeTypeReference()); return Push(new SizeOf(ReadAndDecodeTypeReference()));
case ILOpCode.Stelem: case ILOpCode.Stelem:
return StElem(ReadAndDecodeTypeReference()); return StElem(ReadAndDecodeTypeReference());
case ILOpCode.Stelem_I1: case ILOpCode.Stelem_I1:
@ -780,19 +785,20 @@ namespace ICSharpCode.Decompiler.IL
ILInstruction Push(ILInstruction inst) ILInstruction Push(ILInstruction inst)
{ {
Debug.Assert(inst.ResultType != StackType.Void); Debug.Assert(inst.ResultType != StackType.Void);
var v = new ILVariable(VariableKind.StackSlot, inst.ResultType, inst.ILRange.Start); IType type = compilation.FindType(inst.ResultType.ToKnownTypeCode());
var v = new ILVariable(VariableKind.StackSlot, type, inst.ResultType, inst.ILRange.Start);
v.Name = "S_" + inst.ILRange.Start.ToString("x4"); v.Name = "S_" + inst.ILRange.Start.ToString("x4");
currentStack = currentStack.Push(v); currentStack = currentStack.Push(v);
return new StLoc(v, inst); return new StLoc(v, inst);
} }
IL.LdLoc Peek() LdLoc Peek()
{ {
// TODO: handle stack underflow? // TODO: handle stack underflow?
return new LdLoc(currentStack.Peek()); return new LdLoc(currentStack.Peek());
} }
IL.LdLoc Pop() LdLoc Pop()
{ {
// TODO: handle stack underflow? // TODO: handle stack underflow?
ILVariable v; ILVariable v;
@ -803,9 +809,9 @@ namespace ICSharpCode.Decompiler.IL
private ILInstruction Return() private ILInstruction Return()
{ {
if (body.Method.ReturnType.GetStackType() == StackType.Void) if (body.Method.ReturnType.GetStackType() == StackType.Void)
return new ICSharpCode.Decompiler.IL.Return(); return new IL.Return();
else else
return new ICSharpCode.Decompiler.IL.Return(Pop()); return new IL.Return(Pop());
} }
private ILInstruction DecodeLdstr() private ILInstruction DecodeLdstr()
@ -859,25 +865,29 @@ namespace ICSharpCode.Decompiler.IL
ILInstruction InitObj(ILInstruction target, IType type) ILInstruction InitObj(ILInstruction target, IType type)
{ {
return new Void(new StObj(target, new DefaultValue(type), type)); return new StObj(target, new DefaultValue(type), type);
} }
private ILInstruction DecodeConstrainedCall() private ILInstruction DecodeConstrainedCall()
{ {
var typeRef = ReadAndDecodeTypeReference(); var typeRef = ReadAndDecodeTypeReference();
var inst = DecodeInstruction(); var inst = DecodeInstruction();
var call = inst as CallInstruction; var call = UnpackPush(inst) as CallInstruction;
if (call != null) if (call != null)
call.ConstrainedTo = typeRef; call.ConstrainedTo = typeRef;
else
Warn("Ignored invalid 'constrained' prefix");
return inst; return inst;
} }
private ILInstruction DecodeTailCall() private ILInstruction DecodeTailCall()
{ {
var inst = DecodeInstruction(); var inst = DecodeInstruction();
var call = inst as CallInstruction; var call = UnpackPush(inst) as CallInstruction;
if (call != null) if (call != null)
call.IsTail = true; call.IsTail = true;
else
Warn("Ignored invalid 'tail' prefix");
return inst; return inst;
} }
@ -885,7 +895,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
byte alignment = reader.ReadByte(); byte alignment = reader.ReadByte();
var inst = DecodeInstruction(); var inst = DecodeInstruction();
var sup = inst as ISupportsUnalignedPrefix; var sup = UnpackPush(inst) as ISupportsUnalignedPrefix;
if (sup != null) if (sup != null)
sup.UnalignedPrefix = alignment; sup.UnalignedPrefix = alignment;
else else
@ -896,7 +906,7 @@ namespace ICSharpCode.Decompiler.IL
private ILInstruction DecodeVolatile() private ILInstruction DecodeVolatile()
{ {
var inst = DecodeInstruction(); var inst = DecodeInstruction();
var svp = inst as ISupportsVolatilePrefix; var svp = UnpackPush(inst) as ISupportsVolatilePrefix;
if (svp != null) if (svp != null)
svp.IsVolatile = true; svp.IsVolatile = true;
else else
@ -907,7 +917,7 @@ namespace ICSharpCode.Decompiler.IL
private ILInstruction DecodeReadonly() private ILInstruction DecodeReadonly()
{ {
var inst = DecodeInstruction(); var inst = DecodeInstruction();
var ldelema = inst as LdElema; var ldelema = UnpackPush(inst) as LdElema;
if (ldelema != null) if (ldelema != null)
ldelema.IsReadOnly = true; ldelema.IsReadOnly = true;
else else
@ -1043,5 +1053,16 @@ namespace ICSharpCode.Decompiler.IL
var left = Pop(); var left = Pop();
return Push(BinaryNumericInstruction.Create(opCode, left, right, checkForOverflow, sign)); return Push(BinaryNumericInstruction.Create(opCode, left, right, checkForOverflow, sign));
} }
ILInstruction LdToken(IMetadataTokenProvider token)
{
if (token is TypeReference)
return new LdTypeToken(typeSystem.Resolve((TypeReference)token));
if (token is FieldReference)
return new LdMemberToken(typeSystem.Resolve((FieldReference)token));
if (token is MethodReference)
return new LdMemberToken(typeSystem.Resolve((MethodReference)token));
throw new NotImplementedException();
}
} }
} }

14
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -29,6 +29,10 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
Local, Local,
/// <summary> /// <summary>
/// A pinned local variable
/// </summary>
PinnedLocal,
/// <summary>
/// A parameter. /// A parameter.
/// </summary> /// </summary>
Parameter, Parameter,
@ -96,13 +100,11 @@ namespace ICSharpCode.Decompiler.IL
this.Index = index; this.Index = index;
} }
public ILVariable(VariableKind kind, StackType type, int index) public ILVariable(VariableKind kind, IType type, StackType stackType, int index)
{ {
if (type == null)
throw new ArgumentNullException("type");
this.Kind = kind; this.Kind = kind;
this.Type = SpecialType.UnknownType; this.Type = type;
this.StackType = type; this.StackType = stackType;
this.Index = index; this.Index = index;
} }
@ -115,6 +117,8 @@ namespace ICSharpCode.Decompiler.IL
{ {
output.WriteDefinition(this.Name, this, isLocal: true); output.WriteDefinition(this.Name, this, isLocal: true);
output.Write(" : "); output.Write(" : ");
if (Kind == VariableKind.PinnedLocal)
output.Write("pinned ");
Type.WriteTo(output); Type.WriteTo(output);
output.Write("({0} ldloc, {1} ldloca, {2} stloc)", LoadCount, AddressCount, StoreCount); output.Write("({0} ldloc, {1} ldloca, {2} stloc)", LoadCount, AddressCount, StoreCount);
} }

20
ICSharpCode.Decompiler/IL/InstructionFlags.cs

@ -28,26 +28,6 @@ namespace ICSharpCode.Decompiler.IL
public enum InstructionFlags public enum InstructionFlags
{ {
None = 0, None = 0,
/// <summary>
/// Phase-1 execution of this instruction may pop from the evaluation stack.
/// </summary>
MayPop = 0x01,
/// <summary>
/// Phase-1 execution of this instruction may peek at the top-most element of the evaluation stack.
/// If MayPop is also set, this flag refers to the top-most element after an arbitrary number of pops.
/// </summary>
MayPeek = 0x02,
/// <summary>
/// Phase-2 execution of this instruction may read the evaluation stack.
/// This is not set for instructions that access the stack only in phase-1.
/// </summary>
MayReadEvaluationStack = 0x04,
/// <summary>
/// Phase-2 execution of this instruction may modify the evaluation stack.
/// This is not set for instructions that access the stack only in phase-1.
/// </summary>
MayWriteEvaluationStack = 0x08,
/// <summary> /// <summary>
/// The instruction may read from local variables. /// The instruction may read from local variables.
/// </summary> /// </summary>

416
ICSharpCode.Decompiler/IL/Instructions.cs

@ -102,6 +102,8 @@ namespace ICSharpCode.Decompiler.IL
LdLoca, LdLoca,
/// <summary>Stores a value into a local variable. (starg/stloc)</summary> /// <summary>Stores a value into a local variable. (starg/stloc)</summary>
StLoc, StLoc,
/// <summary>Stores the value into an anonymous temporary variable, and returns the address of that variable.</summary>
AddressOf,
/// <summary>Loads a constant string.</summary> /// <summary>Loads a constant string.</summary>
LdStr, LdStr,
/// <summary>Loads a constant 32-bit integer.</summary> /// <summary>Loads a constant 32-bit integer.</summary>
@ -172,6 +174,44 @@ namespace ICSharpCode.Decompiler.IL
LdElema, LdElema,
} }
/// <summary>Instruction without any arguments</summary>
public abstract partial class SimpleInstruction : ILInstruction
{
protected SimpleInstruction(OpCode opCode) : base(opCode)
{
}
protected sealed override int GetChildCount()
{
return 0;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone()
{
var clone = (SimpleInstruction)ShallowClone();
return clone;
}
}
/// <summary>Instruction with a single argument</summary> /// <summary>Instruction with a single argument</summary>
public abstract partial class UnaryInstruction : ILInstruction public abstract partial class UnaryInstruction : ILInstruction
{ {
@ -179,11 +219,12 @@ namespace ICSharpCode.Decompiler.IL
{ {
this.Argument = argument; this.Argument = argument;
} }
public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true);
ILInstruction argument; ILInstruction argument;
public ILInstruction Argument { public ILInstruction Argument {
get { return this.argument; } get { return this.argument; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.argument, value, 0); SetChildInstruction(ref this.argument, value, 0);
} }
} }
@ -210,17 +251,21 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return ArgumentSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (UnaryInstruction)ShallowClone(); var clone = (UnaryInstruction)ShallowClone();
clone.Argument = this.argument.Clone(); clone.Argument = this.argument.Clone();
return clone; return clone;
} }
internal sealed override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
this.Argument = this.argument.Inline(flagsBefore, context);
return this;
}
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
return argument.Flags; return argument.Flags;
@ -242,19 +287,21 @@ namespace ICSharpCode.Decompiler.IL
this.Left = left; this.Left = left;
this.Right = right; this.Right = right;
} }
public static readonly SlotInfo LeftSlot = new SlotInfo("Left", canInlineInto: true);
ILInstruction left; ILInstruction left;
public ILInstruction Left { public ILInstruction Left {
get { return this.left; } get { return this.left; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.left, value, 0); SetChildInstruction(ref this.left, value, 0);
} }
} }
public static readonly SlotInfo RightSlot = new SlotInfo("Right", canInlineInto: true);
ILInstruction right; ILInstruction right;
public ILInstruction Right { public ILInstruction Right {
get { return this.right; } get { return this.right; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.right, value, 1); SetChildInstruction(ref this.right, value, 1);
} }
} }
@ -286,6 +333,17 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return LeftSlot;
case 1:
return RightSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (BinaryInstruction)ShallowClone(); var clone = (BinaryInstruction)ShallowClone();
@ -293,12 +351,6 @@ namespace ICSharpCode.Decompiler.IL
clone.Right = this.right.Clone(); clone.Right = this.right.Clone();
return clone; return clone;
} }
internal sealed override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
this.Right = this.right.Inline(flagsBefore | ((this.left.Flags) & ~(InstructionFlags.MayPeek | InstructionFlags.MayPop)), context);
this.Left = this.left.Inline(flagsBefore, context);
return this;
}
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
return left.Flags | right.Flags; return left.Flags | right.Flags;
@ -334,6 +386,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>A container of IL blocks.</summary> /// <summary>A container of IL blocks.</summary>
public sealed partial class ILFunction : ILInstruction public sealed partial class ILFunction : ILInstruction
{ {
public static readonly SlotInfo BodySlot = new SlotInfo("Body");
ILInstruction body; ILInstruction body;
public ILInstruction Body { public ILInstruction Body {
get { return this.body; } get { return this.body; }
@ -365,6 +418,15 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return BodySlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (ILFunction)ShallowClone(); var clone = (ILFunction)ShallowClone();
@ -636,14 +698,16 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c></summary> /// <summary>If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c></summary>
public sealed partial class IfInstruction : ILInstruction public sealed partial class IfInstruction : ILInstruction
{ {
public static readonly SlotInfo ConditionSlot = new SlotInfo("Condition", canInlineInto: true);
ILInstruction condition; ILInstruction condition;
public ILInstruction Condition { public ILInstruction Condition {
get { return this.condition; } get { return this.condition; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.condition, value, 0); SetChildInstruction(ref this.condition, value, 0);
} }
} }
public static readonly SlotInfo TrueInstSlot = new SlotInfo("TrueInst");
ILInstruction trueInst; ILInstruction trueInst;
public ILInstruction TrueInst { public ILInstruction TrueInst {
get { return this.trueInst; } get { return this.trueInst; }
@ -652,6 +716,7 @@ namespace ICSharpCode.Decompiler.IL
SetChildInstruction(ref this.trueInst, value, 1); SetChildInstruction(ref this.trueInst, value, 1);
} }
} }
public static readonly SlotInfo FalseInstSlot = new SlotInfo("FalseInst");
ILInstruction falseInst; ILInstruction falseInst;
public ILInstruction FalseInst { public ILInstruction FalseInst {
get { return this.falseInst; } get { return this.falseInst; }
@ -693,6 +758,19 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return ConditionSlot;
case 1:
return TrueInstSlot;
case 2:
return FalseInstSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (IfInstruction)ShallowClone(); var clone = (IfInstruction)ShallowClone();
@ -728,6 +806,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Switch section within a switch statement</summary> /// <summary>Switch section within a switch statement</summary>
public sealed partial class SwitchSection : ILInstruction public sealed partial class SwitchSection : ILInstruction
{ {
public static readonly SlotInfo BodySlot = new SlotInfo("Body");
ILInstruction body; ILInstruction body;
public ILInstruction Body { public ILInstruction Body {
get { return this.body; } get { return this.body; }
@ -759,6 +838,15 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return BodySlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (SwitchSection)ShallowClone(); var clone = (SwitchSection)ShallowClone();
@ -800,14 +888,16 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(variable != null); Debug.Assert(variable != null);
this.variable = variable; this.variable = variable;
} }
public static readonly SlotInfo FilterSlot = new SlotInfo("Filter");
ILInstruction filter; ILInstruction filter;
public ILInstruction Filter { public ILInstruction Filter {
get { return this.filter; } get { return this.filter; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.filter, value, 0); SetChildInstruction(ref this.filter, value, 0);
} }
} }
public static readonly SlotInfo BodySlot = new SlotInfo("Body");
ILInstruction body; ILInstruction body;
public ILInstruction Body { public ILInstruction Body {
get { return this.body; } get { return this.body; }
@ -844,6 +934,17 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return FilterSlot;
case 1:
return BodySlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (TryCatchHandler)ShallowClone(); var clone = (TryCatchHandler)ShallowClone();
@ -1135,11 +1236,12 @@ namespace ICSharpCode.Decompiler.IL
readonly ILVariable variable; readonly ILVariable variable;
/// <summary>Returns the variable operand.</summary> /// <summary>Returns the variable operand.</summary>
public ILVariable Variable { get { return variable; } } public ILVariable Variable { get { return variable; } }
public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true);
ILInstruction value; ILInstruction value;
public ILInstruction Value { public ILInstruction Value {
get { return this.value; } get { return this.value; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.value, value, 0); SetChildInstruction(ref this.value, value, 0);
} }
} }
@ -1166,17 +1268,21 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return ValueSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (StLoc)ShallowClone(); var clone = (StLoc)ShallowClone();
clone.Value = this.value.Clone(); clone.Value = this.value.Clone();
return clone; return clone;
} }
internal sealed override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
this.Value = this.value.Inline(flagsBefore, context);
return this;
}
public override StackType ResultType { get { return variable.StackType; } } public override StackType ResultType { get { return variable.StackType; } }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
@ -1201,6 +1307,82 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
/// <summary>Stores the value into an anonymous temporary variable, and returns the address of that variable.</summary>
public sealed partial class AddressOf : ILInstruction
{
public AddressOf(ILInstruction value) : base(OpCode.AddressOf)
{
this.Value = value;
}
public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true);
ILInstruction value;
public ILInstruction Value {
get { return this.value; }
set {
ValidateChild(value);
SetChildInstruction(ref this.value, value, 0);
}
}
protected sealed override int GetChildCount()
{
return 1;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
case 0:
return this.value;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
case 0:
this.Value = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return ValueSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone()
{
var clone = (AddressOf)ShallowClone();
clone.Value = this.value.Clone();
return clone;
}
public override StackType ResultType { get { return StackType.Ref; } }
protected override InstructionFlags ComputeFlags()
{
return value.Flags;
}
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
output.Write('(');
this.value.WriteTo(output);
output.Write(')');
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitAddressOf(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitAddressOf(this);
}
}
/// <summary>Loads a constant string.</summary> /// <summary>Loads a constant string.</summary>
public sealed partial class LdStr : SimpleInstruction public sealed partial class LdStr : SimpleInstruction
{ {
@ -1510,11 +1692,12 @@ namespace ICSharpCode.Decompiler.IL
this.Target = target; this.Target = target;
this.field = field; this.field = field;
} }
public static readonly SlotInfo TargetSlot = new SlotInfo("Target", canInlineInto: true);
ILInstruction target; ILInstruction target;
public ILInstruction Target { public ILInstruction Target {
get { return this.target; } get { return this.target; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.target, value, 0); SetChildInstruction(ref this.target, value, 0);
} }
} }
@ -1541,17 +1724,21 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return TargetSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (LdFld)ShallowClone(); var clone = (LdFld)ShallowClone();
clone.Target = this.target.Clone(); clone.Target = this.target.Clone();
return clone; return clone;
} }
internal sealed override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
this.Target = this.target.Inline(flagsBefore, context);
return this;
}
/// <summary>Gets/Sets whether the memory access is volatile.</summary> /// <summary>Gets/Sets whether the memory access is volatile.</summary>
public bool IsVolatile { get; set; } public bool IsVolatile { get; set; }
/// <summary>Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix.</summary> /// <summary>Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix.</summary>
@ -1595,11 +1782,12 @@ namespace ICSharpCode.Decompiler.IL
this.Target = target; this.Target = target;
this.field = field; this.field = field;
} }
public static readonly SlotInfo TargetSlot = new SlotInfo("Target", canInlineInto: true);
ILInstruction target; ILInstruction target;
public ILInstruction Target { public ILInstruction Target {
get { return this.target; } get { return this.target; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.target, value, 0); SetChildInstruction(ref this.target, value, 0);
} }
} }
@ -1626,17 +1814,21 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return TargetSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (LdFlda)ShallowClone(); var clone = (LdFlda)ShallowClone();
clone.Target = this.target.Clone(); clone.Target = this.target.Clone();
return clone; return clone;
} }
internal sealed override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
this.Target = this.target.Inline(flagsBefore, context);
return this;
}
readonly IField field; readonly IField field;
/// <summary>Returns the field operand.</summary> /// <summary>Returns the field operand.</summary>
public IField Field { get { return field; } } public IField Field { get { return field; } }
@ -1673,19 +1865,21 @@ namespace ICSharpCode.Decompiler.IL
this.Value = value; this.Value = value;
this.field = field; this.field = field;
} }
public static readonly SlotInfo TargetSlot = new SlotInfo("Target", canInlineInto: true);
ILInstruction target; ILInstruction target;
public ILInstruction Target { public ILInstruction Target {
get { return this.target; } get { return this.target; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.target, value, 0); SetChildInstruction(ref this.target, value, 0);
} }
} }
public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true);
ILInstruction value; ILInstruction value;
public ILInstruction Value { public ILInstruction Value {
get { return this.value; } get { return this.value; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.value, value, 1); SetChildInstruction(ref this.value, value, 1);
} }
} }
@ -1717,6 +1911,17 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return TargetSlot;
case 1:
return ValueSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (StFld)ShallowClone(); var clone = (StFld)ShallowClone();
@ -1724,12 +1929,6 @@ namespace ICSharpCode.Decompiler.IL
clone.Value = this.value.Clone(); clone.Value = this.value.Clone();
return clone; return clone;
} }
internal sealed override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
this.Value = this.value.Inline(flagsBefore | ((this.target.Flags) & ~(InstructionFlags.MayPeek | InstructionFlags.MayPop)), context);
this.Target = this.target.Inline(flagsBefore, context);
return this;
}
/// <summary>Gets/Sets whether the memory access is volatile.</summary> /// <summary>Gets/Sets whether the memory access is volatile.</summary>
public bool IsVolatile { get; set; } public bool IsVolatile { get; set; }
/// <summary>Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix.</summary> /// <summary>Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix.</summary>
@ -1841,11 +2040,12 @@ namespace ICSharpCode.Decompiler.IL
this.Value = value; this.Value = value;
this.field = field; this.field = field;
} }
public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true);
ILInstruction value; ILInstruction value;
public ILInstruction Value { public ILInstruction Value {
get { return this.value; } get { return this.value; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.value, value, 0); SetChildInstruction(ref this.value, value, 0);
} }
} }
@ -1872,17 +2072,21 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return ValueSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (StsFld)ShallowClone(); var clone = (StsFld)ShallowClone();
clone.Value = this.value.Clone(); clone.Value = this.value.Clone();
return clone; return clone;
} }
internal sealed override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
this.Value = this.value.Inline(flagsBefore, context);
return this;
}
/// <summary>Gets/Sets whether the memory access is volatile.</summary> /// <summary>Gets/Sets whether the memory access is volatile.</summary>
public bool IsVolatile { get; set; } public bool IsVolatile { get; set; }
/// <summary>Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix.</summary> /// <summary>Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix.</summary>
@ -1990,11 +2194,12 @@ namespace ICSharpCode.Decompiler.IL
this.Target = target; this.Target = target;
this.type = type; this.type = type;
} }
public static readonly SlotInfo TargetSlot = new SlotInfo("Target", canInlineInto: true);
ILInstruction target; ILInstruction target;
public ILInstruction Target { public ILInstruction Target {
get { return this.target; } get { return this.target; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.target, value, 0); SetChildInstruction(ref this.target, value, 0);
} }
} }
@ -2021,17 +2226,21 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return TargetSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (LdObj)ShallowClone(); var clone = (LdObj)ShallowClone();
clone.Target = this.target.Clone(); clone.Target = this.target.Clone();
return clone; return clone;
} }
internal sealed override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
this.Target = this.target.Inline(flagsBefore, context);
return this;
}
readonly IType type; readonly IType type;
/// <summary>Returns the type operand.</summary> /// <summary>Returns the type operand.</summary>
public IType Type { get { return type; } } public IType Type { get { return type; } }
@ -2076,19 +2285,21 @@ namespace ICSharpCode.Decompiler.IL
this.Value = value; this.Value = value;
this.type = type; this.type = type;
} }
public static readonly SlotInfo TargetSlot = new SlotInfo("Target", canInlineInto: true);
ILInstruction target; ILInstruction target;
public ILInstruction Target { public ILInstruction Target {
get { return this.target; } get { return this.target; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.target, value, 0); SetChildInstruction(ref this.target, value, 0);
} }
} }
public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true);
ILInstruction value; ILInstruction value;
public ILInstruction Value { public ILInstruction Value {
get { return this.value; } get { return this.value; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.value, value, 1); SetChildInstruction(ref this.value, value, 1);
} }
} }
@ -2120,6 +2331,17 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return TargetSlot;
case 1:
return ValueSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (StObj)ShallowClone(); var clone = (StObj)ShallowClone();
@ -2127,12 +2349,6 @@ namespace ICSharpCode.Decompiler.IL
clone.Value = this.value.Clone(); clone.Value = this.value.Clone();
return clone; return clone;
} }
internal sealed override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
this.Value = this.value.Inline(flagsBefore | ((this.target.Flags) & ~(InstructionFlags.MayPeek | InstructionFlags.MayPop)), context);
this.Target = this.target.Inline(flagsBefore, context);
return this;
}
readonly IType type; readonly IType type;
/// <summary>Returns the type operand.</summary> /// <summary>Returns the type operand.</summary>
public IType Type { get { return type; } } public IType Type { get { return type; } }
@ -2300,11 +2516,12 @@ namespace ICSharpCode.Decompiler.IL
readonly IType type; readonly IType type;
/// <summary>Returns the type operand.</summary> /// <summary>Returns the type operand.</summary>
public IType Type { get { return type; } } public IType Type { get { return type; } }
public static readonly SlotInfo SizeSlot = new SlotInfo("Size", canInlineInto: true);
ILInstruction size; ILInstruction size;
public ILInstruction Size { public ILInstruction Size {
get { return this.size; } get { return this.size; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.size, value, 0); SetChildInstruction(ref this.size, value, 0);
} }
} }
@ -2331,17 +2548,21 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return SizeSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (NewArr)ShallowClone(); var clone = (NewArr)ShallowClone();
clone.Size = this.size.Clone(); clone.Size = this.size.Clone();
return clone; return clone;
} }
internal sealed override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
this.Size = this.size.Inline(flagsBefore, context);
return this;
}
public override StackType ResultType { get { return StackType.O; } } public override StackType ResultType { get { return StackType.O; } }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
@ -2469,11 +2690,12 @@ namespace ICSharpCode.Decompiler.IL
{ {
this.Array = array; this.Array = array;
} }
public static readonly SlotInfo ArraySlot = new SlotInfo("Array", canInlineInto: true);
ILInstruction array; ILInstruction array;
public ILInstruction Array { public ILInstruction Array {
get { return this.array; } get { return this.array; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.array, value, 0); SetChildInstruction(ref this.array, value, 0);
} }
} }
@ -2500,17 +2722,21 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return ArraySlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (LdLen)ShallowClone(); var clone = (LdLen)ShallowClone();
clone.Array = this.array.Clone(); clone.Array = this.array.Clone();
return clone; return clone;
} }
internal sealed override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
this.Array = this.array.Inline(flagsBefore, context);
return this;
}
public override StackType ResultType { get { return StackType.I; } } public override StackType ResultType { get { return StackType.I; } }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
@ -2542,19 +2768,21 @@ namespace ICSharpCode.Decompiler.IL
this.Index = index; this.Index = index;
this.type = type; this.type = type;
} }
public static readonly SlotInfo ArraySlot = new SlotInfo("Array", canInlineInto: true);
ILInstruction array; ILInstruction array;
public ILInstruction Array { public ILInstruction Array {
get { return this.array; } get { return this.array; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.array, value, 0); SetChildInstruction(ref this.array, value, 0);
} }
} }
public static readonly SlotInfo IndexSlot = new SlotInfo("Index", canInlineInto: true);
ILInstruction index; ILInstruction index;
public ILInstruction Index { public ILInstruction Index {
get { return this.index; } get { return this.index; }
set { set {
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref this.index, value, 1); SetChildInstruction(ref this.index, value, 1);
} }
} }
@ -2586,6 +2814,17 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return ArraySlot;
case 1:
return IndexSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (LdElema)ShallowClone(); var clone = (LdElema)ShallowClone();
@ -2593,12 +2832,6 @@ namespace ICSharpCode.Decompiler.IL
clone.Index = this.index.Clone(); clone.Index = this.index.Clone();
return clone; return clone;
} }
internal sealed override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
this.Index = this.index.Inline(flagsBefore | ((this.array.Flags) & ~(InstructionFlags.MayPeek | InstructionFlags.MayPop)), context);
this.Array = this.array.Inline(flagsBefore, context);
return this;
}
readonly IType type; readonly IType type;
/// <summary>Returns the type operand.</summary> /// <summary>Returns the type operand.</summary>
public IType Type { get { return type; } } public IType Type { get { return type; } }
@ -2789,6 +3022,10 @@ namespace ICSharpCode.Decompiler.IL
{ {
Default(inst); Default(inst);
} }
protected internal virtual void VisitAddressOf(AddressOf inst)
{
Default(inst);
}
protected internal virtual void VisitLdStr(LdStr inst) protected internal virtual void VisitLdStr(LdStr inst)
{ {
Default(inst); Default(inst);
@ -3083,6 +3320,10 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(inst); return Default(inst);
} }
protected internal virtual T VisitAddressOf(AddressOf inst)
{
return Default(inst);
}
protected internal virtual T VisitLdStr(LdStr inst) protected internal virtual T VisitLdStr(LdStr inst)
{ {
return Default(inst); return Default(inst);
@ -3313,6 +3554,7 @@ namespace ICSharpCode.Decompiler.IL
"ldloc", "ldloc",
"ldloca", "ldloca",
"stloc", "stloc",
"addressof",
"ldstr", "ldstr",
"ldc.i4", "ldc.i4",
"ldc.i8", "ldc.i8",
@ -3608,6 +3850,16 @@ namespace ICSharpCode.Decompiler.IL
value = default(ILInstruction); value = default(ILInstruction);
return false; return false;
} }
public bool MatchAddressOf(out ILInstruction value)
{
var inst = this as AddressOf;
if (inst != null) {
value = inst.Value;
return true;
}
value = default(ILInstruction);
return false;
}
public bool MatchLdStr(out string value) public bool MatchLdStr(out string value)
{ {
var inst = this as LdStr; var inst = this as LdStr;

66
ICSharpCode.Decompiler/IL/Instructions.tt

@ -24,6 +24,8 @@
<#@ output extension=".cs" #> <#@ output extension=".cs" #>
<# <#
OpCode[] baseClasses = { OpCode[] baseClasses = {
new OpCode("SimpleInstruction", "Instruction without any arguments",
AbstractBaseClass, CustomArguments(), CustomWriteTo),
new OpCode("UnaryInstruction", "Instruction with a single argument", new OpCode("UnaryInstruction", "Instruction with a single argument",
AbstractBaseClass, CustomArguments("argument")), AbstractBaseClass, CustomArguments("argument")),
new OpCode("BinaryInstruction", "Instruction with two arguments: Left and Right", new OpCode("BinaryInstruction", "Instruction with two arguments: Left and Right",
@ -60,7 +62,7 @@
new OpCode("if", "If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c>", new OpCode("if", "If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c>",
CustomClassName("IfInstruction"), CustomClassName("IfInstruction"),
CustomChildren(new []{ CustomChildren(new []{
new ChildInfo("condition") { IsArgument = true }, new ArgumentInfo("condition"),
new ChildInfo("trueInst"), new ChildInfo("trueInst"),
new ChildInfo("falseInst"), new ChildInfo("falseInst"),
}), CustomConstructor, CustomComputeFlags, CustomWriteTo), }), CustomConstructor, CustomComputeFlags, CustomWriteTo),
@ -73,7 +75,7 @@
BaseClass("TryInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo), BaseClass("TryInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo),
new OpCode("try.catch.handler", "Catch handler within a try-catch statement.", new OpCode("try.catch.handler", "Catch handler within a try-catch statement.",
CustomChildren(new [] { CustomChildren(new [] {
new ChildInfo("filter") { IsArgument = true }, new ChildInfo("filter"),
new ChildInfo("body"), new ChildInfo("body"),
}), HasVariableOperand, CustomWriteTo, CustomComputeFlags), }), HasVariableOperand, CustomWriteTo, CustomComputeFlags),
new OpCode("try.finally", "Try-finally statement", new OpCode("try.finally", "Try-finally statement",
@ -106,6 +108,9 @@
new OpCode("stloc", "Stores a value into a local variable. (starg/stloc)", new OpCode("stloc", "Stores a value into a local variable. (starg/stloc)",
CustomClassName("StLoc"), HasVariableOperand, CustomArguments("value"), CustomClassName("StLoc"), HasVariableOperand, 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.",
CustomClassName("AddressOf"), CustomArguments("value"), ResultType("Ref")),
new OpCode("ldstr", "Loads a constant string.", new OpCode("ldstr", "Loads a constant string.",
CustomClassName("LdStr"), LoadConstant("string"), ResultType("O")), CustomClassName("LdStr"), LoadConstant("string"), ResultType("O")),
new OpCode("ldc.i4", "Loads a constant 32-bit integer.", new OpCode("ldc.i4", "Loads a constant 32-bit integer.",
@ -557,19 +562,40 @@ namespace ICSharpCode.Decompiler.IL
static Action<OpCode> CustomArguments(params string[] arguments) static Action<OpCode> CustomArguments(params string[] arguments)
{ {
return CustomChildren(arguments.Select(arg => new ChildInfo(arg) { IsArgument = true}).ToArray(), generateInline: true); return CustomChildren(arguments.Select(arg => new ArgumentInfo(arg)).ToArray(), generateInline: true);
} }
class ChildInfo class ChildInfo
{ {
public readonly string PropertyName; public readonly string PropertyName;
public readonly string Name; public readonly string Name;
public bool IsArgument; public readonly string SlotName;
public bool CanInlineInto;
public ChildInfo(string name) public ChildInfo(string name)
{ {
this.Name = name; this.Name = name;
this.PropertyName = MakeName(name); this.PropertyName = MakeName(name);
this.SlotName = this.PropertyName + "Slot";
}
public string GetSlotInit()
{
StringBuilder b = new StringBuilder();
b.Append("new SlotInfo(\"" + PropertyName + "\"");
if (CanInlineInto)
b.Append(", canInlineInto: true");
b.Append(")");
return b.ToString();
}
}
class ArgumentInfo : ChildInfo
{
public ArgumentInfo(string name) : base(name)
{
this.CanInlineInto = true;
} }
} }
@ -589,11 +615,12 @@ namespace ICSharpCode.Decompiler.IL
if (i > 0) if (i > 0)
opCode.WriteArguments.Add("output.Write(\", \");"); opCode.WriteArguments.Add("output.Write(\", \");");
opCode.WriteArguments.Add("this." + arg + ".WriteTo(output);"); opCode.WriteArguments.Add("this." + arg + ".WriteTo(output);");
opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";");
opCode.Members.Add("ILInstruction " + arg + ";"); opCode.Members.Add("ILInstruction " + arg + ";");
opCode.Members.Add("public ILInstruction " + argProp + " {" + Environment.NewLine opCode.Members.Add("public ILInstruction " + argProp + " {" + Environment.NewLine
+ "\tget { return this." + arg + "; }" + Environment.NewLine + "\tget { return this." + arg + "; }" + Environment.NewLine
+ "\tset {" + Environment.NewLine + "\tset {" + Environment.NewLine
+ "\t\t" + (children[i].IsArgument ? "ValidateArgument" : "ValidateChild") + "(value);" + Environment.NewLine + "\t\tValidateChild(value);" + Environment.NewLine
+ "\t\tSetChildInstruction(ref this." + arg + ", value, " + i + ");" + Environment.NewLine + "\t\tSetChildInstruction(ref this." + arg + ", value, " + i + ");" + Environment.NewLine
+ "\t}" + Environment.NewLine + "\t}" + Environment.NewLine
+ "}"); + "}");
@ -637,34 +664,29 @@ namespace ICSharpCode.Decompiler.IL
opCode.Members.Add(b.ToString()); opCode.Members.Add(b.ToString());
b = new StringBuilder(); b = new StringBuilder();
b.AppendLine("public sealed override ILInstruction Clone()"); b.AppendLine("protected sealed override SlotInfo GetChildSlot(int index)");
b.AppendLine("{"); b.AppendLine("{");
b.AppendLine("\tvar clone = (" + opCode.Name + ")ShallowClone();"); b.AppendLine("\tswitch (index) {");
for (int i = 0; i < children.Length; i++) { for (int i = 0; i < children.Length; i++) {
b.AppendLine("\tclone." + children[i].PropertyName + " = this." + children[i].Name + ".Clone();"); b.AppendLine("\t\tcase " + i + ":");
b.AppendLine("\t\t\treturn " + children[i].SlotName + ";");
} }
b.AppendLine("\treturn clone;"); b.AppendLine("\t\tdefault:");
b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();");
b.AppendLine("\t}");
b.Append("}"); b.Append("}");
opCode.Members.Add(b.ToString()); opCode.Members.Add(b.ToString());
if (generateInline) {
b = new StringBuilder(); b = new StringBuilder();
b.AppendLine("internal sealed override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)"); b.AppendLine("public sealed override ILInstruction Clone()");
b.AppendLine("{"); b.AppendLine("{");
for (int i = children.Length - 1; i >= 0; i--) { b.AppendLine("\tvar clone = (" + opCode.Name + ")ShallowClone();");
string arg = children[i].Name; for (int i = 0; i < children.Length; i++) {
b.Append("\tthis." + MakeName(arg) + " = this." + arg + ".Inline(flagsBefore"); b.AppendLine("\tclone." + children[i].PropertyName + " = this." + children[i].Name + ".Clone();");
if (i > 0) {
b.Append(" | ((");
b.Append(string.Join(" | ", children.Take(i).Select(child2 => "this." + child2.Name + ".Flags")));
b.Append(") & ~(InstructionFlags.MayPeek | InstructionFlags.MayPop))");
}
b.AppendLine(", context);");
} }
b.AppendLine("\treturn this;"); b.AppendLine("\treturn clone;");
b.Append("}"); b.Append("}");
opCode.Members.Add(b.ToString()); opCode.Members.Add(b.ToString());
}
}; };
} }

47
ICSharpCode.Decompiler/IL/Instructions/Block.cs

@ -30,35 +30,22 @@ namespace ICSharpCode.Decompiler.IL
{ {
/// <summary> /// <summary>
/// A block consists of a list of IL instructions. /// A block consists of a list of IL instructions.
/// <para> ///
/// Phase-1 execution of a block is a no-op: any peek/pop instructions within the block are ignored at this stage.
/// </para>
/// <para>
/// Phase-2 execution will execute the instructions in order, pseudo-code:
/// </para>
/// <code>
/// foreach (var inst in Instructions) {
/// var result = inst.Phase1().Phase2();
/// if (result != void) evalStack.Push(result);
/// }
/// return FinalInstruction.Phase1().Phase2();
/// </code>
/// <para> /// <para>
/// Note: if execution reaches the end of the instruction list, /// Note: if execution reaches the end of the instruction list,
/// the FinalInstruction (which is not part of the list) will be executed. /// the FinalInstruction (which is not part of the list) will be executed.
/// The block returns returns the result value of the FinalInstruction. /// The block returns returns the result value of the FinalInstruction.
/// For blocks returning void, the FinalInstruction will usually be 'nop'. /// For blocks returning void, the FinalInstruction will usually be 'nop'.
///
/// TODO: now that 'implicit push' is gone, reconsider whether we need
/// a separate slot for FinalInstruction
/// </para> /// </para>
/// </summary> /// </summary>
/// <remarks>
/// Fun fact: wrapping a pop instruction in a block
/// (<c>new Block { FinalInstruction = popInst }</c>) turns it
/// from a phase-1 pop instruction to a phase-2 pop instruction.
/// However, this is just of theoretical interest; we currently don't plan to use inline blocks that
/// pop elements that they didn't push themselves.
/// </remarks>
partial class Block : ILInstruction partial class Block : ILInstruction
{ {
public static readonly SlotInfo InstructionSlot = new SlotInfo("Instruction", isCollection: true);
public static readonly SlotInfo FinalInstructionSlot = new SlotInfo("FinalInstruction");
public readonly InstructionCollection<ILInstruction> Instructions; public readonly InstructionCollection<ILInstruction> Instructions;
ILInstruction finalInstruction; ILInstruction finalInstruction;
@ -168,15 +155,19 @@ namespace ICSharpCode.Decompiler.IL
Instructions[index] = value; Instructions[index] = value;
} }
protected override SlotInfo GetChildSlot(int index)
{
if (index == Instructions.Count)
return InstructionSlot;
else
return FinalInstructionSlot;
}
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
var flags = InstructionFlags.None; var flags = InstructionFlags.None;
foreach (var inst in Instructions) { foreach (var inst in Instructions) {
flags |= Phase1Boundary(inst.Flags); flags |= Phase1Boundary(inst.Flags);
if (inst.ResultType != StackType.Void) {
// implicit push
flags |= InstructionFlags.MayWriteEvaluationStack;
}
} }
flags |= Phase1Boundary(FinalInstruction.Flags); flags |= Phase1Boundary(FinalInstruction.Flags);
return flags; return flags;
@ -187,16 +178,10 @@ namespace ICSharpCode.Decompiler.IL
/// The MayPop and MayPeek flags are removed and converted into /// The MayPop and MayPeek flags are removed and converted into
/// MayReadEvaluationStack and/or MayWriteEvaluationStack flags. /// MayReadEvaluationStack and/or MayWriteEvaluationStack flags.
/// </summary> /// </summary>
[Obsolete("there's not phase-1 evaluation anymore")] [Obsolete("there's no phase-1 evaluation anymore")]
internal static InstructionFlags Phase1Boundary(InstructionFlags flags) internal static InstructionFlags Phase1Boundary(InstructionFlags flags)
{ {
return flags; return flags;
} }
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
// an inline block has no phase-1 effects, so we're immediately done with inlining
return this;
}
} }
} }

12
ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs

@ -38,6 +38,7 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
partial class BlockContainer : ILInstruction partial class BlockContainer : ILInstruction
{ {
public readonly SlotInfo BlockSlot = new SlotInfo("Block", isCollection: true);
public readonly InstructionCollection<Block> Blocks; public readonly InstructionCollection<Block> Blocks;
/// <summary> /// <summary>
@ -133,6 +134,11 @@ namespace ICSharpCode.Decompiler.IL
throw new InvalidOperationException("Cannot replace blocks in BlockContainer"); throw new InvalidOperationException("Cannot replace blocks in BlockContainer");
} }
protected override SlotInfo GetChildSlot(int index)
{
return BlockSlot;
}
internal override void CheckInvariant() internal override void CheckInvariant()
{ {
base.CheckInvariant(); base.CheckInvariant();
@ -154,12 +160,6 @@ namespace ICSharpCode.Decompiler.IL
flags &= ~InstructionFlags.EndPointUnreachable; flags &= ~InstructionFlags.EndPointUnreachable;
return flags; return flags;
} }
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
// Blocks are phase-1 boundaries
return this;
}
} }
} }

20
ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs

@ -29,6 +29,8 @@ namespace ICSharpCode.Decompiler.IL
{ {
public abstract class CallInstruction : ILInstruction public abstract class CallInstruction : ILInstruction
{ {
public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true, isCollection: true);
public static CallInstruction Create(OpCode opCode, IMethod method) public static CallInstruction Create(OpCode opCode, IMethod method)
{ {
switch (opCode) { switch (opCode) {
@ -98,6 +100,11 @@ namespace ICSharpCode.Decompiler.IL
Arguments[index] = value; Arguments[index] = value;
} }
protected override SlotInfo GetChildSlot(int index)
{
return ArgumentSlot;
}
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
var flags = InstructionFlags.MayThrow | InstructionFlags.SideEffect; var flags = InstructionFlags.MayThrow | InstructionFlags.SideEffect;
@ -125,18 +132,5 @@ namespace ICSharpCode.Decompiler.IL
} }
output.Write(')'); output.Write(')');
} }
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
InstructionFlags operandFlags = InstructionFlags.None;
for (int i = 0; i < Arguments.Count - 1; i++) {
operandFlags |= Arguments[i].Flags;
}
flagsBefore |= operandFlags & ~(InstructionFlags.MayPeek | InstructionFlags.MayPop);
for (int i = Arguments.Count - 1; i >= 0; i--) {
Arguments[i] = Arguments[i].Inline(flagsBefore, context);
}
return this;
}
} }
} }

6
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -62,11 +62,5 @@ namespace ICSharpCode.Decompiler.IL
// We intentionally don't propagate any flags from the lambda body! // We intentionally don't propagate any flags from the lambda body!
return InstructionFlags.MayThrow; return InstructionFlags.MayThrow;
} }
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
// To the outside, lambda creation looks like a constant
return this;
}
} }
} }

45
ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs

@ -39,15 +39,6 @@ namespace ICSharpCode.Decompiler.IL
this.OpCode = opCode; this.OpCode = opCode;
} }
protected void ValidateArgument(ILInstruction inst)
{
if (inst == null)
throw new ArgumentNullException("inst");
if (inst.ResultType == StackType.Void)
throw new ArgumentException("Argument must not be of type void", "inst");
Debug.Assert(!this.IsDescendantOf(inst), "ILAst must form a tree");
}
protected void ValidateChild(ILInstruction inst) protected void ValidateChild(ILInstruction inst)
{ {
if (inst == null) if (inst == null)
@ -128,6 +119,12 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
public Interval ILRange; public Interval ILRange;
public void AddILRange(Interval ilRange)
{
// TODO: try to combine the two ranges
this.ILRange = ilRange;
}
/// <summary> /// <summary>
/// Writes the ILAst to the text output. /// Writes the ILAst to the text output.
/// </summary> /// </summary>
@ -162,6 +159,7 @@ namespace ICSharpCode.Decompiler.IL
protected abstract int GetChildCount(); protected abstract int GetChildCount();
protected abstract ILInstruction GetChild(int index); protected abstract ILInstruction GetChild(int index);
protected abstract void SetChild(int index, ILInstruction value); protected abstract void SetChild(int index, ILInstruction value);
protected abstract SlotInfo GetChildSlot(int index);
#region ChildrenCollection + ChildrenEnumerator #region ChildrenCollection + ChildrenEnumerator
public struct ChildrenCollection : IReadOnlyList<ILInstruction> public struct ChildrenCollection : IReadOnlyList<ILInstruction>
@ -290,6 +288,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary> /// <summary>
/// Returns all descendants of the ILInstruction in post-order. /// Returns all descendants of the ILInstruction in post-order.
/// (including the ILInstruction itself)
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Within a loop 'foreach (var node in inst.Descendants)', it is illegal to /// Within a loop 'foreach (var node in inst.Descendants)', it is illegal to
@ -324,26 +323,10 @@ namespace ICSharpCode.Decompiler.IL
stack.Pop().Dispose(); stack.Pop().Dispose();
} }
} }
yield return this;
} }
} }
/// <summary>
/// Attempts inlining from the inline context into this instruction.
/// </summary>
/// <param name="flagsBefore">Combined instruction flags of the instructions
/// that the instructions getting inlined would get moved over.</param>
/// <param name="context">The inline context providing the values on the evaluation stack.</param>
/// <returns>
/// Returns the modified ILInstruction after inlining is complete.
/// Note that inlining modifies the AST in-place, so this method usually returns <c>this</c>
/// (unless <c>this</c> should be replaced by another node)
/// </returns>
/// <remarks>
/// Inlining from an inline context representing the actual evaluation stack
/// is equivalent to phase-1 execution of the instruction.
/// </remarks>
internal abstract ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context);
/// <summary> /// <summary>
/// Number of parents that refer to this instruction and are connected to the root. /// Number of parents that refer to this instruction and are connected to the root.
/// Usually is 0 for unconnected nodes and 1 for connected nodes, but may temporarily increase to 2 /// Usually is 0 for unconnected nodes and 1 for connected nodes, but may temporarily increase to 2
@ -405,6 +388,16 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
public int ChildIndex { get; internal set; } public int ChildIndex { get; internal set; }
/// <summary>
/// Gets information about the slot in which this instruction is stored.
/// (i.e., the relation of this instruction to its parent instruction)
/// </summary>
public SlotInfo SlotInfo {
get {
return parent.GetChildSlot(this.ChildIndex);
}
}
/// <summary> /// <summary>
/// Replaces a child of this ILInstruction. /// Replaces a child of this ILInstruction.
/// </summary> /// </summary>

7
ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs

@ -52,13 +52,6 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
this.Condition = condition.Inline(flagsBefore, context);
// note: we skip TrueInst and FalseInst because there's a phase-1-boundary around them
return this;
}
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
return condition.Flags | Block.Phase1Boundary(CombineFlags(trueInst.Flags, falseInst.Flags)); return condition.Flags | Block.Phase1Boundary(CombineFlags(trueInst.Flags, falseInst.Flags));

20
ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs

@ -33,32 +33,36 @@ namespace ICSharpCode.Decompiler.IL
return inst != null && inst.Variable == variable; return inst != null && inst.Variable == variable;
} }
public bool MatchLdLoca(ILVariable variable)
{
var inst = this as LdLoca;
return inst != null && inst.Variable == variable;
}
public bool MatchLdThis() public bool MatchLdThis()
{ {
var inst = this as LdLoc; var inst = this as LdLoc;
return inst != null && inst.Variable.Kind == VariableKind.This; return inst != null && inst.Variable.Kind == VariableKind.This;
} }
public bool MatchStLoc(ILVariable variable, out ILInstruction value) public bool MatchStLoc(out ILVariable variable)
{ {
var inst = this as StLoc; var inst = this as StLoc;
if (inst != null && inst.Variable == variable) { if (inst != null) {
value = inst.Value; variable = inst.Variable;
return true; return true;
} }
value = null; variable = null;
return false; return false;
} }
public bool MatchStLoc(out ILVariable variable, out ILInstruction value) public bool MatchStLoc(ILVariable variable, out ILInstruction value)
{ {
var inst = this as StLoc; var inst = this as StLoc;
if (inst != null) { if (inst != null && inst.Variable == variable) {
variable = inst.Variable;
value = inst.Value; value = inst.Value;
return true; return true;
} }
variable = null;
value = null; value = null;
return false; return false;
} }

10
ICSharpCode.Decompiler/IL/Instructions/Return.cs

@ -24,6 +24,8 @@ namespace ICSharpCode.Decompiler.IL
{ {
partial class Return partial class Return
{ {
public static readonly SlotInfo ReturnValueSlot = new SlotInfo("ReturnValue", canInlineInto: true);
ILInstruction returnValue; ILInstruction returnValue;
/// <summary> /// <summary>
@ -33,7 +35,7 @@ namespace ICSharpCode.Decompiler.IL
get { return returnValue; } get { return returnValue; }
set { set {
if (value != null) if (value != null)
ValidateArgument(value); ValidateChild(value);
SetChildInstruction(ref returnValue, value, 0); SetChildInstruction(ref returnValue, value, 0);
} }
} }
@ -95,11 +97,9 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context) protected override SlotInfo GetChildSlot(int index)
{ {
if (returnValue != null) return ReturnValueSlot;
this.ReturnValue = returnValue.Inline(flagsBefore, context);
return this;
} }
} }
} }

32
ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs

@ -28,46 +28,16 @@ namespace ICSharpCode.Decompiler.IL
/// <summary> /// <summary>
/// A simple instruction that does not have any arguments. /// A simple instruction that does not have any arguments.
/// </summary> /// </summary>
public abstract class SimpleInstruction : ILInstruction public abstract partial class SimpleInstruction : ILInstruction
{ {
protected SimpleInstruction(OpCode opCode) : base(opCode)
{
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);
} }
protected sealed override int GetChildCount()
{
return 0;
}
protected sealed override ILInstruction GetChild(int index)
{
throw new IndexOutOfRangeException();
}
protected sealed override void SetChild(int index, ILInstruction value)
{
throw new IndexOutOfRangeException();
}
public sealed override ILInstruction Clone()
{
return ShallowClone();
}
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
return InstructionFlags.None; return InstructionFlags.None;
} }
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
// Nothing to do, since we don't have arguments.
return this;
}
} }
} }

24
ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs

@ -26,6 +26,9 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
partial class SwitchInstruction partial class SwitchInstruction
{ {
public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true);
public static readonly SlotInfo SectionSlot = new SlotInfo("Section", isCollection: true);
public SwitchInstruction(ILInstruction value) public SwitchInstruction(ILInstruction value)
: base(OpCode.SwitchInstruction) : base(OpCode.SwitchInstruction)
{ {
@ -44,12 +47,6 @@ namespace ICSharpCode.Decompiler.IL
public readonly InstructionCollection<SwitchSection> Sections; public readonly InstructionCollection<SwitchSection> Sections;
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
Value = value.Inline(flagsBefore, context);
return this;
}
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
var sectionFlags = InstructionFlags.None; var sectionFlags = InstructionFlags.None;
@ -93,6 +90,13 @@ namespace ICSharpCode.Decompiler.IL
Sections[index - 1] = (SwitchSection)value; Sections[index - 1] = (SwitchSection)value;
} }
protected override SlotInfo GetChildSlot(int index)
{
if (index == 0)
return ValueSlot;
return SectionSlot;
}
public override ILInstruction Clone() public override ILInstruction Clone()
{ {
var clone = new SwitchInstruction(value.Clone()); var clone = new SwitchInstruction(value.Clone());
@ -113,14 +117,6 @@ namespace ICSharpCode.Decompiler.IL
public LongSet Labels { get; set; } public LongSet Labels { get; set; }
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
// Inlining into switch sections would be madness.
// To keep phase-1 execution semantics consistent with inlining, there's a
// phase-1-boundary around every switch section.
return this;
}
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
return Block.Phase1Boundary(body.Flags); return Block.Phase1Boundary(body.Flags);

53
ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs

@ -25,6 +25,8 @@ namespace ICSharpCode.Decompiler.IL
{ {
public abstract class TryInstruction : ILInstruction public abstract class TryInstruction : ILInstruction
{ {
public static readonly SlotInfo TryBlockSlot = new SlotInfo("TryBlock");
protected TryInstruction(OpCode opCode, ILInstruction tryBlock) : base(opCode) protected TryInstruction(OpCode opCode, ILInstruction tryBlock) : base(opCode)
{ {
this.TryBlock = tryBlock; this.TryBlock = tryBlock;
@ -38,14 +40,6 @@ namespace ICSharpCode.Decompiler.IL
SetChildInstruction(ref this.tryBlock, value, 0); SetChildInstruction(ref this.tryBlock, value, 0);
} }
} }
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
// Inlining into exception-handling constructs would be madness.
// To keep phase-1 execution semantics consistent with inlining, there's a
// phase-1-boundary around every try/catch/finally/fault block.
return this;
}
} }
/// <summary> /// <summary>
@ -58,6 +52,7 @@ namespace ICSharpCode.Decompiler.IL
/// </remarks> /// </remarks>
partial class TryCatch : TryInstruction partial class TryCatch : TryInstruction
{ {
public static readonly SlotInfo HandlerSlot = new SlotInfo("Handler", isCollection: true);
public readonly InstructionCollection<TryCatchHandler> Handlers; public readonly InstructionCollection<TryCatchHandler> Handlers;
public TryCatch(ILInstruction tryBlock) : base(OpCode.TryCatch, tryBlock) public TryCatch(ILInstruction tryBlock) : base(OpCode.TryCatch, tryBlock)
@ -115,6 +110,14 @@ namespace ICSharpCode.Decompiler.IL
else else
Handlers[index - 1] = (TryCatchHandler)value; Handlers[index - 1] = (TryCatchHandler)value;
} }
protected override SlotInfo GetChildSlot(int index)
{
if (index == 0)
return TryBlockSlot;
else
return HandlerSlot;
}
} }
/// <summary> /// <summary>
@ -131,12 +134,6 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
partial class TryCatchHandler partial class TryCatchHandler
{ {
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
// should never happen as TryCatchHandler only appears within TryCatch instructions
throw new InvalidOperationException();
}
internal override void CheckInvariant() internal override void CheckInvariant()
{ {
base.CheckInvariant(); base.CheckInvariant();
@ -183,6 +180,8 @@ namespace ICSharpCode.Decompiler.IL
partial class TryFinally partial class TryFinally
{ {
public static readonly SlotInfo FinallyBlockSlot = new SlotInfo("FinallyBlock");
public TryFinally(ILInstruction tryBlock, ILInstruction finallyBlock) : base(OpCode.TryFinally, tryBlock) public TryFinally(ILInstruction tryBlock, ILInstruction finallyBlock) : base(OpCode.TryFinally, tryBlock)
{ {
this.FinallyBlock = finallyBlock; this.FinallyBlock = finallyBlock;
@ -254,10 +253,24 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return TryBlockSlot;
case 1:
return FinallyBlockSlot;
default:
throw new IndexOutOfRangeException();
}
}
} }
partial class TryFault partial class TryFault
{ {
public static readonly SlotInfo FaultBlockSlot = new SlotInfo("FaultBlock");
public TryFault(ILInstruction tryBlock, ILInstruction faultBlock) : base(OpCode.TryFinally, tryBlock) public TryFault(ILInstruction tryBlock, ILInstruction faultBlock) : base(OpCode.TryFinally, tryBlock)
{ {
this.FaultBlock = faultBlock; this.FaultBlock = faultBlock;
@ -328,5 +341,17 @@ namespace ICSharpCode.Decompiler.IL
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
protected override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return TryBlockSlot;
case 1:
return FaultBlockSlot;
default:
throw new IndexOutOfRangeException();
}
}
} }
} }

24
ICSharpCode.Decompiler/IL/SemanticHelper.cs

@ -24,23 +24,27 @@ namespace ICSharpCode.Decompiler.IL
{ {
// TODO: consider moving IfInstruction.CombineFlags and Block.Phase1Boundary here // TODO: consider moving IfInstruction.CombineFlags and Block.Phase1Boundary here
/// <summary>
/// Gets whether instruction is pure:
/// * must not have side effects
/// * must not throw exceptions
/// * must not branch
/// </summary>
internal static bool IsPure(InstructionFlags inst)
{
const InstructionFlags pureFlags = InstructionFlags.MayReadLocals;
return (inst & ~pureFlags) == 0;
}
/// <summary> /// <summary>
/// Gets whether the instruction sequence 'inst1; inst2;' may be ordered to 'inst2; inst1;' /// Gets whether the instruction sequence 'inst1; inst2;' may be ordered to 'inst2; inst1;'
/// </summary> /// </summary>
internal static bool MayReorder(InstructionFlags inst1, InstructionFlags inst2) internal static bool MayReorder(InstructionFlags inst1, InstructionFlags inst2)
{ {
// If both instructions perform an impure action, we cannot reorder them // If both instructions perform an impure action, we cannot reorder them
const InstructionFlags pureFlags = if (!IsPure(inst1) && !IsPure(inst2))
InstructionFlags.MayPeek
| InstructionFlags.MayReadEvaluationStack
| InstructionFlags.MayReadLocals;
if ((inst1 & inst2 & ~pureFlags) != 0)
return false;
// We cannot reorder if inst2 might pop what inst1 peeks at
if (ConflictingPair(inst1, inst2, InstructionFlags.MayPeek, InstructionFlags.MayPop))
return false;
if (ConflictingPair(inst1, inst2, InstructionFlags.MayReadEvaluationStack, InstructionFlags.MayWriteEvaluationStack))
return false; return false;
// We cannot reorder if inst2 might write what inst1 looks at
if (ConflictingPair(inst1, inst2, InstructionFlags.MayReadLocals, InstructionFlags.MayWriteLocals | InstructionFlags.SideEffect)) if (ConflictingPair(inst1, inst2, InstructionFlags.MayReadLocals, InstructionFlags.MayWriteLocals | InstructionFlags.SideEffect))
return false; return false;
return true; return true;

44
ICSharpCode.Decompiler/IL/SlotInfo.cs

@ -0,0 +1,44 @@
/*
* Created by SharpDevelop.
* User: Daniel
* Date: 2015-05-31
* Time: 14:26
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>
/// Holds information about the role of an instruction within its parent instruction.
/// </summary>
public class SlotInfo
{
public static SlotInfo None = new SlotInfo("<no slot>");
/// <summary>
/// Gets the name of the slot.
/// </summary>
public readonly string Name;
/// <summary>
/// Gets whether it is possible to inline into this slot.
/// </summary>
public readonly bool CanInlineInto;
public readonly bool IsCollection;
public SlotInfo(string name, bool canInlineInto = false, bool isCollection = false)
{
this.IsCollection = isCollection;
this.Name = name;
this.CanInlineInto = canInlineInto;
}
public override string ToString()
{
return Name;
}
}
}

310
ICSharpCode.Decompiler/ILAst/ILInlining.cs → ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -1,4 +1,4 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team // Copyright (c) 2011-2015 Daniel Grunwald
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this // Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software // software and associated documentation files (the "Software"), to deal in the Software
@ -21,121 +21,31 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using Mono.Cecil; using Mono.Cecil;
using ICSharpCode.Decompiler.IL;
namespace ICSharpCode.Decompiler.ILAst namespace ICSharpCode.Decompiler.IL
{ {
/// <summary> /// <summary>
/// Performs inlining transformations. /// Performs inlining transformations.
/// </summary> /// </summary>
public class ILInlining public class ILInlining : IILTransform
{ {
readonly ILBlock method; public void Run(ILFunction function, ILTransformContext context)
internal Dictionary<ILVariable, int> numStloc = new Dictionary<ILVariable, int>();
internal Dictionary<ILVariable, int> numLdloc = new Dictionary<ILVariable, int>();
internal Dictionary<ILVariable, int> numLdloca = new Dictionary<ILVariable, int>();
public ILInlining(ILBlock method)
{
this.method = method;
AnalyzeMethod();
}
void AnalyzeMethod()
{
numStloc.Clear();
numLdloc.Clear();
numLdloca.Clear();
// Analyse the whole method
AnalyzeNode(method);
}
/// <summary>
/// For each variable reference, adds <paramref name="direction"/> to the num* dicts.
/// Direction will be 1 for analysis, and -1 when removing a node from analysis.
/// </summary>
void AnalyzeNode(ILNode node, int direction = 1)
{
ILExpression expr = node as ILExpression;
if (expr != null) {
ILVariable locVar = expr.Operand as ILVariable;
if (locVar != null) {
if (expr.Code == ILCode.Stloc) {
numStloc[locVar] = numStloc.GetOrDefault(locVar) + direction;
} else if (expr.Code == ILCode.Ldloc) {
numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + direction;
} else if (expr.Code == ILCode.Ldloca) {
numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + direction;
} else {
throw new NotSupportedException(expr.Code.ToString());
}
}
foreach (ILExpression child in expr.Arguments)
AnalyzeNode(child, direction);
} else {
var catchBlock = node as ILTryCatchBlock.CatchBlock;
if (catchBlock != null && catchBlock.ExceptionVariable != null) {
numStloc[catchBlock.ExceptionVariable] = numStloc.GetOrDefault(catchBlock.ExceptionVariable) + direction;
}
foreach (ILNode child in node.GetChildren())
AnalyzeNode(child, direction);
}
}
public bool InlineAllVariables()
{
bool modified = false;
ILInlining i = new ILInlining(method);
foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>())
modified |= i.InlineAllInBlock(block);
return modified;
}
public bool InlineAllInBlock(ILBlock block)
{ {
bool modified = false; foreach (var block in function.Descendants.OfType<Block>()) {
List<ILNode> body = block.Body; InlineAllInBlock(block);
if (block is ILTryCatchBlock.CatchBlock && body.Count > 1) {
ILVariable v = ((ILTryCatchBlock.CatchBlock)block).ExceptionVariable;
if (v != null && v.IsGenerated) {
if (numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1) {
ILVariable v2;
ILExpression ldException;
if (body[0].Match(ILCode.Stloc, out v2, out ldException) && ldException.MatchLdloc(v)) {
body.RemoveAt(0);
((ILTryCatchBlock.CatchBlock)block).ExceptionVariable = v2;
modified = true;
}
}
}
}
for(int i = 0; i < body.Count - 1;) {
ILVariable locVar;
ILExpression expr;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(block.Body, i, aggressive: false)) {
modified = true;
i = Math.Max(0, i - 1); // Go back one step
} else {
i++;
}
} }
foreach(ILBasicBlock bb in body.OfType<ILBasicBlock>()) {
modified |= InlineAllInBasicBlock(bb);
}
return modified;
} }
public bool InlineAllInBasicBlock(ILBasicBlock bb) public bool InlineAllInBlock(Block block)
{ {
bool modified = false; bool modified = false;
List<ILNode> body = bb.Body; int i = 0;
for(int i = 0; i < body.Count;) { while (i < block.Instructions.Count) {
ILVariable locVar; if (InlineOneIfPossible(block, i, aggressive: false)) {
ILExpression expr;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(bb.Body, i, aggressive: false)) {
modified = true; modified = true;
i = Math.Max(0, i - 1); // Go back one step i = Math.Max(0, i - 1);
// Go back one step
} else { } else {
i++; i++;
} }
@ -144,19 +54,16 @@ namespace ICSharpCode.Decompiler.ILAst
} }
/// <summary> /// <summary>
/// Inlines instructions before pos into block.Body[pos]. /// Inlines instructions before pos into block.Instructions[pos].
/// </summary> /// </summary>
/// <returns>The number of instructions that were inlined.</returns> /// <returns>The number of instructions that were inlined.</returns>
public int InlineInto(List<ILNode> body, int pos, bool aggressive) public int InlineInto(Block block, int pos, bool aggressive)
{ {
if (pos >= body.Count) if (pos >= block.Instructions.Count)
return 0; return 0;
int count = 0; int count = 0;
while (--pos >= 0) { while (--pos >= 0) {
ILExpression expr = body[pos] as ILExpression; if (InlineOneIfPossible(block, pos, aggressive))
if (expr == null || expr.Code != ILCode.Stloc)
break;
if (InlineOneIfPossible(body, pos, aggressive))
count++; count++;
else else
break; break;
@ -171,10 +78,10 @@ namespace ICSharpCode.Decompiler.ILAst
/// <remarks> /// <remarks>
/// After the operation, pos will point to the new combined instruction. /// After the operation, pos will point to the new combined instruction.
/// </remarks> /// </remarks>
public bool InlineIfPossible(List<ILNode> body, ref int pos) public bool InlineIfPossible(Block block, ref int pos)
{ {
if (InlineOneIfPossible(body, pos, true)) { if (InlineOneIfPossible(block, pos, true)) {
pos -= InlineInto(body, pos, false); pos -= InlineInto(block, pos, false);
return true; return true;
} }
return false; return false;
@ -183,29 +90,30 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary> /// <summary>
/// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible. /// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// </summary> /// </summary>
public bool InlineOneIfPossible(List<ILNode> body, int pos, bool aggressive) public bool InlineOneIfPossible(Block block, int pos, bool aggressive)
{ {
ILVariable v; StLoc stloc = block.Instructions[pos] as StLoc;
ILExpression inlinedExpression; if (stloc != null && stloc.Variable.Kind != VariableKind.PinnedLocal) {
if (body[pos].Match(ILCode.Stloc, out v, out inlinedExpression) && !v.IsPinned) { ILVariable v = stloc.Variable;
if (InlineIfPossible(v, inlinedExpression, body.ElementAtOrDefault(pos+1), aggressive)) { if (InlineIfPossible(v, stloc.Value, block.Instructions.ElementAtOrDefault(pos+1), aggressive)) {
// Assign the ranges of the stloc instruction: // Assign the ranges of the stloc instruction:
inlinedExpression.ILRanges.AddRange(((ILExpression)body[pos]).ILRanges); stloc.Value.AddILRange(stloc.ILRange);
// Remove the stloc instruction: // Remove the stloc instruction:
body.RemoveAt(pos); Debug.Assert(block.Instructions[pos] == stloc);
block.Instructions.RemoveAt(pos);
return true; return true;
} else if (numLdloc.GetOrDefault(v) == 0 && numLdloca.GetOrDefault(v) == 0) { } else if (v.LoadCount == 0 && v.AddressCount == 0) {
// The variable is never loaded // The variable is never loaded
if (inlinedExpression.HasNoSideEffects()) { if (SemanticHelper.IsPure(stloc.Value.Flags)) {
// Remove completely // Remove completely if the instruction has no effects
AnalyzeNode(body[pos], -1); // (except for reading locals)
body.RemoveAt(pos); block.Instructions.RemoveAt(pos);
return true; return true;
} else if (inlinedExpression.CanBeExpressionStatement() && v.IsGenerated) { } else if (v.Kind == VariableKind.StackSlot) {
// Assign the ranges of the stloc instruction: // Assign the ranges of the stloc instruction:
inlinedExpression.ILRanges.AddRange(((ILExpression)body[pos]).ILRanges); stloc.Value.AddILRange(stloc.ILRange);
// Remove the stloc, but keep the inner expression // Remove the stloc, but keep the inner expression
body[pos] = inlinedExpression; stloc.ReplaceWith(stloc.Value);
return true; return true;
} }
} }
@ -216,46 +124,40 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary> /// <summary>
/// Inlines 'expr' into 'next', if possible. /// Inlines 'expr' into 'next', if possible.
/// </summary> /// </summary>
bool InlineIfPossible(ILVariable v, ILExpression inlinedExpression, ILNode next, bool aggressive) bool InlineIfPossible(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, bool aggressive)
{ {
// ensure the variable is accessed only a single time // ensure the variable is accessed only a single time
if (numStloc.GetOrDefault(v) != 1) if (v.StoreCount != 1)
return false; return false;
int ldloc = numLdloc.GetOrDefault(v); if (v.LoadCount > 1 || v.LoadCount + v.AddressCount != 1)
if (ldloc > 1 || ldloc + numLdloca.GetOrDefault(v) != 1)
return false; return false;
if (next is ILCondition) ILInstruction loadInst;
next = ((ILCondition)next).Condition; if (FindLoadInNext(next, v, inlinedExpression, out loadInst) == true) {
else if (next is ILWhileLoop) if (loadInst.OpCode == OpCode.LdLoca) {
next = ((ILWhileLoop)next).Condition; //if (!IsGeneratedValueTypeTemporary((ILInstruction)next, loadInst, v, inlinedExpression))
ILExpression parent;
int pos;
if (FindLoadInNext(next as ILExpression, v, inlinedExpression, out parent, out pos) == true) {
if (ldloc == 0) {
if (!IsGeneratedValueTypeTemporary((ILExpression)next, parent, pos, v, inlinedExpression))
return false; return false;
} else { } else {
if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent, inlinedExpression)) Debug.Assert(loadInst.OpCode == OpCode.LdLoc);
if (!aggressive && v.Kind != VariableKind.StackSlot && !NonAggressiveInlineInto(next, loadInst, inlinedExpression))
return false; return false;
} }
// Assign the ranges of the ldloc instruction: // Assign the ranges of the ldloc instruction:
inlinedExpression.ILRanges.AddRange(parent.Arguments[pos].ILRanges); inlinedExpression.AddILRange(loadInst.ILRange);
if (ldloc == 0) { if (loadInst.OpCode == OpCode.LdLoca) {
// it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' so that the types // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof'
// comes out correctly // to preserve the semantics of the compiler-generated temporary
parent.Arguments[pos] = new ILExpression(ILCode.AddressOf, null, inlinedExpression); loadInst.ReplaceWith(new AddressOf(inlinedExpression));
} else { } else {
parent.Arguments[pos] = inlinedExpression; loadInst.ReplaceWith(inlinedExpression);
} }
return true; return true;
} }
return false; return false;
} }
/*
/// <summary> /// <summary>
/// Is this a temporary variable generated by the C# compiler for instance method calls on value type values /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
/// </summary> /// </summary>
@ -264,7 +166,7 @@ namespace ICSharpCode.Decompiler.ILAst
/// <param name="pos">Index of the load within 'parent'</param> /// <param name="pos">Index of the load within 'parent'</param>
/// <param name="v">The variable being inlined.</param> /// <param name="v">The variable being inlined.</param>
/// <param name="inlinedExpression">The expression being inlined</param> /// <param name="inlinedExpression">The expression being inlined</param>
bool IsGeneratedValueTypeTemporary(ILExpression next, ILExpression parent, int pos, ILVariable v, ILExpression inlinedExpression) bool IsGeneratedValueTypeTemporary(ILInstruction next, ILInstruction parent, int pos, ILVariable v, ILInstruction inlinedExpression)
{ {
if (pos == 0 && v.Type != null && v.Type.IsValueType) { if (pos == 0 && v.Type != null && v.Type.IsValueType) {
// Inlining a value type variable is allowed only if the resulting code will maintain the semantics // Inlining a value type variable is allowed only if the resulting code will maintain the semantics
@ -317,7 +219,7 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Castclass: case ILCode.Castclass:
case ILCode.Unbox_Any: case ILCode.Unbox_Any:
// These are valid, but might occur as part of a foreach loop variable. // These are valid, but might occur as part of a foreach loop variable.
ILExpression arg = inlinedExpression.Arguments[0]; ILInstruction arg = inlinedExpression.Arguments[0];
if (arg.Code == ILCode.CallGetter || arg.Code == ILCode.CallvirtGetter || arg.Code == ILCode.Call || arg.Code == ILCode.Callvirt) { if (arg.Code == ILCode.CallGetter || arg.Code == ILCode.CallvirtGetter || arg.Code == ILCode.Call || arg.Code == ILCode.Callvirt) {
mr = (MethodReference)arg.Operand; mr = (MethodReference)arg.Operand;
if (mr.Name == "get_Current" && mr.HasThis) if (mr.Name == "get_Current" && mr.HasThis)
@ -345,24 +247,28 @@ namespace ICSharpCode.Decompiler.ILAst
} }
return false; return false;
} }
*/
/// <summary> /// <summary>
/// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable. /// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable.
/// </summary> /// </summary>
/// <param name="next">The next top-level expression</param> /// <param name="next">The next top-level expression</param>
/// <param name="parent">The direct parent of the load within 'next'</param> /// <param name="loadInst">The load within 'next'</param>
/// <param name="inlinedExpression">The expression being inlined</param> /// <param name="inlinedExpression">The expression being inlined</param>
bool NonAggressiveInlineInto(ILExpression next, ILExpression parent, ILExpression inlinedExpression) bool NonAggressiveInlineInto(ILInstruction next, ILInstruction loadInst, ILInstruction inlinedExpression)
{ {
if (inlinedExpression.Code == ILCode.DefaultValue) Debug.Assert(loadInst.IsDescendantOf(next));
if (inlinedExpression.OpCode == OpCode.DefaultValue)
return true; return true;
switch (next.Code) { var parent = loadInst.Parent;
case ILCode.Ret: switch (next.OpCode) {
case ILCode.Brtrue: case OpCode.Return:
case OpCode.IfInstruction:
return parent == next; return parent == next;
case ILCode.Switch: case OpCode.SwitchInstruction:
return parent == next || (parent.Code == ILCode.Sub && parent == next.Arguments[0]); return parent == next || (parent.OpCode == OpCode.Sub && parent.Parent == next);
default: default:
return false; return false;
} }
@ -371,37 +277,32 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary> /// <summary>
/// Gets whether 'expressionBeingMoved' can be inlined into 'expr'. /// Gets whether 'expressionBeingMoved' can be inlined into 'expr'.
/// </summary> /// </summary>
public bool CanInlineInto(ILExpression expr, ILVariable v, ILExpression expressionBeingMoved) public bool CanInlineInto(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved)
{ {
ILExpression parent; ILInstruction loadInst;
int pos; return FindLoadInNext(expr, v, expressionBeingMoved, out loadInst) == true;
return FindLoadInNext(expr, v, expressionBeingMoved, out parent, out pos) == true;
} }
/// <summary> /// <summary>
/// Finds the position to inline to. /// Finds the position to inline to.
/// </summary> /// </summary>
/// <returns>true = found; false = cannot continue search; null = not found</returns> /// <returns>true = found; false = cannot continue search; null = not found</returns>
bool? FindLoadInNext(ILExpression expr, ILVariable v, ILExpression expressionBeingMoved, out ILExpression parent, out int pos) bool? FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, out ILInstruction loadInst)
{ {
parent = null; loadInst = null;
pos = 0;
if (expr == null) if (expr == null)
return false; return false;
for (int i = 0; i < expr.Arguments.Count; i++) { if (expr.MatchLdLoc(v) || expr.MatchLdLoca(v)) {
// Stop when seeing an opcode that does not guarantee that its operands will be evaluated. // Match found, we can inline
// Inlining in that case might result in the inlined expresion not being evaluted. loadInst = expr;
if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp || expr.Code == ILCode.NullCoalescing))
return false;
ILExpression arg = expr.Arguments[i];
if ((arg.Code == ILCode.Ldloc || arg.Code == ILCode.Ldloca) && arg.Operand == v) {
parent = expr;
pos = i;
return true; return true;
} }
bool? r = FindLoadInNext(arg, v, expressionBeingMoved, out parent, out pos); foreach (var child in expr.Children) {
if (!child.SlotInfo.CanInlineInto)
return false;
// Recursively try to find the load instruction
bool? r = FindLoadInNext(child, v, expressionBeingMoved, out loadInst);
if (r != null) if (r != null)
return r; return r;
} }
@ -414,40 +315,25 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary> /// <summary>
/// Determines whether it is safe to move 'expressionBeingMoved' past 'expr' /// Determines whether it is safe to move 'expressionBeingMoved' past 'expr'
/// </summary> /// </summary>
bool IsSafeForInlineOver(ILExpression expr, ILExpression expressionBeingMoved) bool IsSafeForInlineOver(ILInstruction expr, ILInstruction expressionBeingMoved)
{ {
switch (expr.Code) { ILVariable v;
case ILCode.Ldloc: if (expr.MatchLdLoc(out v) && v.AddressCount == 0) {
ILVariable loadedVar = (ILVariable)expr.Operand; // MayReorder() only looks at flags, so it doesn't
if (numLdloca.GetOrDefault(loadedVar) != 0) { // allow reordering 'stloc x y; ldloc v' to 'ldloc v; stloc x y'
// abort, inlining is not possible
return false; // We'll allow the reordering unless x==v
} if (expressionBeingMoved.HasFlag(InstructionFlags.MayWriteLocals)) {
foreach (ILExpression potentialStore in expressionBeingMoved.GetSelfAndChildrenRecursive<ILExpression>()) { foreach (var stloc in expressionBeingMoved.Descendants.OfType<StLoc>()) {
if (potentialStore.Code == ILCode.Stloc && potentialStore.Operand == loadedVar) if (stloc.Variable == v)
return false; return false;
} }
// the expression is loading a non-forbidden variable
return true;
case ILCode.Ldloca:
case ILCode.Ldflda:
case ILCode.Ldsflda:
case ILCode.Ldelema:
case ILCode.AddressOf:
case ILCode.ValueOf:
case ILCode.NullableOf:
// address-loading instructions are safe if their arguments are safe
foreach (ILExpression arg in expr.Arguments) {
if (!IsSafeForInlineOver(arg, expressionBeingMoved))
return false;
} }
return true; return true;
default:
// instructions with no side-effects are safe (except for Ldloc and Ldloca which are handled separately)
return expr.HasNoSideEffects();
} }
return SemanticHelper.MayReorder(expressionBeingMoved.Flags, expr.Flags);
} }
/*
/// <summary> /// <summary>
/// Runs a very simple form of copy propagation. /// Runs a very simple form of copy propagation.
/// Copy propagation is used in two cases: /// Copy propagation is used in two cases:
@ -461,7 +347,7 @@ namespace ICSharpCode.Decompiler.ILAst
foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) { for (int i = 0; i < block.Body.Count; i++) {
ILVariable v; ILVariable v;
ILExpression copiedExpr; ILInstruction copiedExpr;
if (block.Body[i].Match(ILCode.Stloc, out v, out copiedExpr) if (block.Body[i].Match(ILCode.Stloc, out v, out copiedExpr)
&& !v.IsParameter && numStloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0 && !v.IsParameter && numStloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0
&& CanPerformCopyPropagation(copiedExpr, v)) && CanPerformCopyPropagation(copiedExpr, v))
@ -470,16 +356,16 @@ namespace ICSharpCode.Decompiler.ILAst
ILVariable[] uninlinedArgs = new ILVariable[copiedExpr.Arguments.Count]; ILVariable[] uninlinedArgs = new ILVariable[copiedExpr.Arguments.Count];
for (int j = 0; j < uninlinedArgs.Length; j++) { for (int j = 0; j < uninlinedArgs.Length; j++) {
uninlinedArgs[j] = new ILVariable { IsGenerated = true, Name = v.Name + "_cp_" + j }; uninlinedArgs[j] = new ILVariable { IsGenerated = true, Name = v.Name + "_cp_" + j };
block.Body.Insert(i++, new ILExpression(ILCode.Stloc, uninlinedArgs[j], copiedExpr.Arguments[j])); block.Body.Insert(i++, new ILInstruction(ILCode.Stloc, uninlinedArgs[j], copiedExpr.Arguments[j]));
} }
// perform copy propagation: // perform copy propagation:
foreach (var expr in method.GetSelfAndChildrenRecursive<ILExpression>()) { foreach (var expr in method.GetSelfAndChildrenRecursive<ILInstruction>()) {
if (expr.Code == ILCode.Ldloc && expr.Operand == v) { if (expr.Code == ILCode.Ldloc && expr.Operand == v) {
expr.Code = copiedExpr.Code; expr.Code = copiedExpr.Code;
expr.Operand = copiedExpr.Operand; expr.Operand = copiedExpr.Operand;
for (int j = 0; j < uninlinedArgs.Length; j++) { for (int j = 0; j < uninlinedArgs.Length; j++) {
expr.Arguments.Add(new ILExpression(ILCode.Ldloc, uninlinedArgs[j])); expr.Arguments.Add(new ILInstruction(ILCode.Ldloc, uninlinedArgs[j]));
} }
} }
} }
@ -496,7 +382,7 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
bool CanPerformCopyPropagation(ILExpression expr, ILVariable copyVariable) bool CanPerformCopyPropagation(ILInstruction expr, ILVariable copyVariable)
{ {
switch (expr.Code) { switch (expr.Code) {
case ILCode.Ldloca: case ILCode.Ldloca:
@ -519,6 +405,6 @@ namespace ICSharpCode.Decompiler.ILAst
default: default:
return false; return false;
} }
} }*/
} }
} }

4
ICSharpCode.Decompiler/IL/Transforms/OptimizingTransform.cs

@ -49,7 +49,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (ret.ReturnValue != null && ret.ReturnValue.MatchLdLoc(out v) if (ret.ReturnValue != null && ret.ReturnValue.MatchLdLoc(out v)
&& v.IsSingleUse && block.Instructions[0].MatchStLoc(v, out inst)) && v.IsSingleUse && block.Instructions[0].MatchStLoc(v, out inst))
{ {
ret.ReturnValue = block.Instructions[0]; inst.AddILRange(ret.ReturnValue.ILRange);
inst.AddILRange(block.Instructions[0].ILRange);
ret.ReturnValue = inst;
block.Instructions.RemoveAt(0); block.Instructions.RemoveAt(0);
} }
} }

3
ICSharpCode.Decompiler/IL/Transforms/TransformingVisitor.cs

@ -225,10 +225,9 @@ namespace ICSharpCode.Decompiler.IL
// If the block has only one instruction, we can remove the block too // If the block has only one instruction, we can remove the block too
// (but only if this doesn't change the pop-order in the phase 1 evaluation of the parent block) // (but only if this doesn't change the pop-order in the phase 1 evaluation of the parent block)
if (block.Instructions.Count == 0) { if (block.Instructions.Count == 0) {
if (!block.FinalInstruction.HasFlag(InstructionFlags.MayPeek | InstructionFlags.MayPop))
return block.FinalInstruction; return block.FinalInstruction;
} else if (block.Instructions.Count == 1 && block.FinalInstruction.OpCode == OpCode.Nop) { } else if (block.Instructions.Count == 1 && block.FinalInstruction.OpCode == OpCode.Nop) {
if (block.Instructions[0].ResultType == StackType.Void && !block.Instructions[0].HasFlag(InstructionFlags.MayPeek | InstructionFlags.MayPop)) if (block.Instructions[0].ResultType == StackType.Void)
return block.Instructions[0]; return block.Instructions[0];
} }
return block; return block;

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

@ -108,6 +108,7 @@
<Compile Include="TestCases\HelloWorld.cs" /> <Compile Include="TestCases\HelloWorld.cs" />
<Compile Include="TestCases\PropertiesAndEvents.cs" /> <Compile Include="TestCases\PropertiesAndEvents.cs" />
<Compile Include="TestCases\Switch.cs" /> <Compile Include="TestCases\Switch.cs" />
<Compile Include="TestCases\ValueTypeCall.cs" />
<Compile Include="TestRunner.cs" /> <Compile Include="TestRunner.cs" />
<Compile Include="Util\LongSetTests.cs" /> <Compile Include="Util\LongSetTests.cs" />
<Compile Include="CustomAttributes\CustomAttributeTests.cs" /> <Compile Include="CustomAttributes\CustomAttributeTests.cs" />

62
ICSharpCode.Decompiler/Tests/TestCases/ValueTypeCall.cs

@ -0,0 +1,62 @@
using System;
namespace ValueTypeCall
{
public struct MutValueType
{
public int val;
public void Increment()
{
Console.WriteLine("Inc() called on {0}", val);
val++;
}
}
public class Program
{
public static void Main()
{
MutValueType m = new MutValueType();
RefParameter(ref m);
ValueParameter(m);
Field();
Box();
}
static void RefParameter(ref MutValueType m)
{
m.Increment();
m.Increment();
}
static void ValueParameter(MutValueType m)
{
m.Increment();
m.Increment();
}
static readonly MutValueType ReadonlyField = new MutValueType { val = 100 };
static MutValueType MutableField = new MutValueType { val = 200 };
static void Field()
{
ReadonlyField.Increment();
ReadonlyField.Increment();
MutableField.Increment();
MutableField.Increment();
// Ensure that 'v' isn't incorrectly removed
// as a compiler-generated temporary
MutValueType v = MutableField;
v.Increment();
Console.WriteLine("Final value in MutableField: " + MutableField.val);
}
static void Box()
{
object o = new MutValueType { val = 300 };
((MutValueType)o).Increment();
((MutValueType)o).Increment();
}
}
}

6
ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -64,6 +64,12 @@ namespace ICSharpCode.Decompiler.Tests
TestCompileDecompileCompileOutputAll("Generics.cs"); TestCompileDecompileCompileOutputAll("Generics.cs");
} }
[Test]
public void ValueTypeCall()
{
TestCompileDecompileCompileOutputAll("ValueTypeCall.cs");
}
void TestCompileDecompileCompileOutputAll(string testFileName) void TestCompileDecompileCompileOutputAll(string testFileName)
{ {
TestCompileDecompileCompileOutput(testFileName, CompilerOptions.None); TestCompileDecompileCompileOutput(testFileName, CompilerOptions.None);

Loading…
Cancel
Save