Browse Source

#1852: Rename array.to.pointer opcode to get.pinnable.reference.

pull/1857/head
Daniel Grunwald 6 years ago
parent
commit
eb2a9e6b94
  1. 17
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs
  2. 9
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  3. 14
      ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs
  4. 82
      ICSharpCode.Decompiler/IL/Instructions.cs
  5. 8
      ICSharpCode.Decompiler/IL/Instructions.tt
  6. 2
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

17
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs

@ -65,6 +65,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
#if CS73
public class CustomPinnable
{
public ref int GetPinnableReference()
{
throw new NotImplementedException();
}
}
#endif
public unsafe delegate void UnsafeDelegate(byte* ptr); public unsafe delegate void UnsafeDelegate(byte* ptr);
private UnsafeDelegate unsafeDelegate; private UnsafeDelegate unsafeDelegate;
@ -361,6 +371,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
UsePointer(ptr); UsePointer(ptr);
} }
} }
public unsafe void CustomPinReferenceType(CustomPinnable mem)
{
fixed (int* ptr = mem) {
UsePointer(ptr);
}
}
#endif #endif
public unsafe string StackAlloc(int count) public unsafe string StackAlloc(int count)

9
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -819,8 +819,13 @@ namespace ICSharpCode.Decompiler.CSharp
var fixedStmt = new FixedStatement(); var fixedStmt = new FixedStatement();
fixedStmt.Type = exprBuilder.ConvertType(inst.Variable.Type); fixedStmt.Type = exprBuilder.ConvertType(inst.Variable.Type);
Expression initExpr; Expression initExpr;
if (inst.Init.OpCode == OpCode.ArrayToPointer) { if (inst.Init is GetPinnableReference gpr) {
initExpr = exprBuilder.Translate(((ArrayToPointer)inst.Init).Array); if (gpr.Method != null) {
IType expectedType = gpr.Method.IsStatic ? gpr.Method.Parameters[0].Type : gpr.Method.DeclaringType;
initExpr = exprBuilder.Translate(gpr.Argument, typeHint: expectedType).ConvertTo(expectedType, exprBuilder);
} else {
initExpr = exprBuilder.Translate(gpr.Argument);
}
} else { } else {
initExpr = exprBuilder.Translate(inst.Init, typeHint: inst.Variable.Type).ConvertTo(inst.Variable.Type, exprBuilder); initExpr = exprBuilder.Translate(inst.Init, typeHint: inst.Variable.Type).ConvertTo(inst.Variable.Type, exprBuilder);
} }

14
ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs

@ -139,11 +139,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
bool modified = false; bool modified = false;
for (int i = 0; i < container.Blocks.Count; i++) { for (int i = 0; i < container.Blocks.Count; i++) {
var block = container.Blocks[i]; var block = container.Blocks[i];
ILVariable v, p; if (IsNullSafeArrayToPointerPattern(block, out ILVariable v, out ILVariable p, out Block targetBlock)) {
Block targetBlock;
if (IsNullSafeArrayToPointerPattern(block, out v, out p, out targetBlock)) {
context.Step("NullSafeArrayToPointerPattern", block); context.Step("NullSafeArrayToPointerPattern", block);
ILInstruction arrayToPointer = new ArrayToPointer(new LdLoc(v)); ILInstruction arrayToPointer = new GetPinnableReference(new LdLoc(v), null);
if (p.StackType != StackType.Ref) { if (p.StackType != StackType.Ref) {
arrayToPointer = new Conv(arrayToPointer, p.StackType.ToPrimitiveType(), false, Sign.None); arrayToPointer = new Conv(arrayToPointer, p.StackType.ToPrimitiveType(), false, Sign.None);
} }
@ -432,7 +430,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return; // variable access that is not LdLoc return; // variable access that is not LdLoc
} }
} }
if (!(ldloc.Parent is ArrayToPointer arrayToPointer)) if (!(ldloc.Parent is GetPinnableReference arrayToPointer))
return; return;
if (!(arrayToPointer.Parent is Conv conv && conv.Kind == ConversionKind.StopGCTracking)) if (!(arrayToPointer.Parent is Conv conv && conv.Kind == ConversionKind.StopGCTracking))
return; return;
@ -446,7 +444,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
newVar.HasGeneratedName = oldVar.HasGeneratedName; newVar.HasGeneratedName = oldVar.HasGeneratedName;
oldVar.Function.Variables.Add(newVar); oldVar.Function.Variables.Add(newVar);
pinnedRegion.Variable = newVar; pinnedRegion.Variable = newVar;
pinnedRegion.Init = new ArrayToPointer(pinnedRegion.Init).WithILRange(arrayToPointer); pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, arrayToPointer.Method).WithILRange(arrayToPointer);
conv.ReplaceWith(new LdLoc(newVar).WithILRange(conv)); conv.ReplaceWith(new LdLoc(newVar).WithILRange(conv));
} }
@ -514,7 +512,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
newVar.HasGeneratedName = pinnedRegion.Variable.HasGeneratedName; newVar.HasGeneratedName = pinnedRegion.Variable.HasGeneratedName;
pinnedRegion.Variable.Function.Variables.Add(newVar); pinnedRegion.Variable.Function.Variables.Add(newVar);
pinnedRegion.Variable = newVar; pinnedRegion.Variable = newVar;
pinnedRegion.Init = new ArrayToPointer(pinnedRegion.Init); pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, null);
} }
return; return;
} }
@ -542,7 +540,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// make targetBlock the new entry point // make targetBlock the new entry point
body.Blocks.RemoveAt(targetBlock.ChildIndex); body.Blocks.RemoveAt(targetBlock.ChildIndex);
body.Blocks.Insert(0, targetBlock); body.Blocks.Insert(0, targetBlock);
pinnedRegion.Init = new ArrayToPointer(pinnedRegion.Init); pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, null);
ILVariable otherVar; ILVariable otherVar;
ILInstruction otherVarInit; ILInstruction otherVarInit;

82
ICSharpCode.Decompiler/IL/Instructions.cs

@ -184,9 +184,12 @@ namespace ICSharpCode.Decompiler.IL
LdLen, LdLen,
/// <summary>Load address of array element.</summary> /// <summary>Load address of array element.</summary>
LdElema, LdElema,
/// <summary>Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty. /// <summary>Retrieves a pinnable reference for the input object.
/// Also used to convert a string to a reference to the first character.</summary> /// The input must be an object reference (O).
ArrayToPointer, /// If the input is an array/string, evaluates to a reference to the first element/character, or to a null reference if the array is null or empty.
/// Otherwise, uses the GetPinnableReference method to get the reference, or evaluates to a null reference if the input is null.
/// </summary>
GetPinnableReference,
/// <summary>Maps a string value to an integer. This is used in switch(string).</summary> /// <summary>Maps a string value to an integer. This is used in switch(string).</summary>
StringToInt, StringToInt,
/// <summary>ILAst representation of Expression.Convert.</summary> /// <summary>ILAst representation of Expression.Convert.</summary>
@ -4756,21 +4759,25 @@ namespace ICSharpCode.Decompiler.IL
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{ {
/// <summary>Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty. /// <summary>Retrieves a pinnable reference for the input object.
/// Also used to convert a string to a reference to the first character.</summary> /// The input must be an object reference (O).
public sealed partial class ArrayToPointer : ILInstruction /// If the input is an array/string, evaluates to a reference to the first element/character, or to a null reference if the array is null or empty.
/// Otherwise, uses the GetPinnableReference method to get the reference, or evaluates to a null reference if the input is null.
/// </summary>
public sealed partial class GetPinnableReference : ILInstruction, IInstructionWithMethodOperand
{ {
public ArrayToPointer(ILInstruction array) : base(OpCode.ArrayToPointer) public GetPinnableReference(ILInstruction argument, IMethod method) : base(OpCode.GetPinnableReference)
{ {
this.Array = array; this.Argument = argument;
this.method = method;
} }
public static readonly SlotInfo ArraySlot = new SlotInfo("Array", canInlineInto: true); public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true);
ILInstruction array; ILInstruction argument;
public ILInstruction Array { public ILInstruction Argument {
get { return this.array; } get { return this.argument; }
set { set {
ValidateChild(value); ValidateChild(value);
SetChildInstruction(ref this.array, value, 0); SetChildInstruction(ref this.argument, value, 0);
} }
} }
protected sealed override int GetChildCount() protected sealed override int GetChildCount()
@ -4781,7 +4788,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index) { switch (index) {
case 0: case 0:
return this.array; return this.argument;
default: default:
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
@ -4790,7 +4797,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index) { switch (index) {
case 0: case 0:
this.Array = value; this.Argument = value;
break; break;
default: default:
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
@ -4800,21 +4807,24 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index) { switch (index) {
case 0: case 0:
return ArraySlot; return ArgumentSlot;
default: default:
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
} }
} }
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (ArrayToPointer)ShallowClone(); var clone = (GetPinnableReference)ShallowClone();
clone.Array = this.array.Clone(); clone.Argument = this.argument.Clone();
return clone; return clone;
} }
public override StackType ResultType { get { return StackType.Ref; } } public override StackType ResultType { get { return StackType.Ref; } }
readonly IMethod method;
/// <summary>Returns the method operand.</summary>
public IMethod Method { get { return method; } }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
return array.Flags; return argument.Flags;
} }
public override InstructionFlags DirectFlags { public override InstructionFlags DirectFlags {
get { get {
@ -4825,31 +4835,33 @@ namespace ICSharpCode.Decompiler.IL
{ {
WriteILRange(output, options); WriteILRange(output, options);
output.Write(OpCode); output.Write(OpCode);
output.Write(' ');
method.WriteTo(output);
output.Write('('); output.Write('(');
this.array.WriteTo(output, options); this.argument.WriteTo(output, options);
output.Write(')'); output.Write(')');
} }
public override void AcceptVisitor(ILVisitor visitor) public override void AcceptVisitor(ILVisitor visitor)
{ {
visitor.VisitArrayToPointer(this); visitor.VisitGetPinnableReference(this);
} }
public override T AcceptVisitor<T>(ILVisitor<T> visitor) public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{ {
return visitor.VisitArrayToPointer(this); return visitor.VisitGetPinnableReference(this);
} }
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context) public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{ {
return visitor.VisitArrayToPointer(this, context); return visitor.VisitGetPinnableReference(this, context);
} }
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{ {
var o = other as ArrayToPointer; var o = other as GetPinnableReference;
return o != null && this.array.PerformMatch(o.array, ref match); return o != null && this.argument.PerformMatch(o.argument, ref match) && method.Equals(o.method);
} }
internal override void CheckInvariant(ILPhase phase) internal override void CheckInvariant(ILPhase phase)
{ {
base.CheckInvariant(phase); base.CheckInvariant(phase);
Debug.Assert(array.ResultType == StackType.O); Debug.Assert(argument.ResultType == StackType.O);
} }
} }
} }
@ -6717,7 +6729,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
Default(inst); Default(inst);
} }
protected internal virtual void VisitArrayToPointer(ArrayToPointer inst) protected internal virtual void VisitGetPinnableReference(GetPinnableReference inst)
{ {
Default(inst); Default(inst);
} }
@ -7103,7 +7115,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(inst); return Default(inst);
} }
protected internal virtual T VisitArrayToPointer(ArrayToPointer inst) protected internal virtual T VisitGetPinnableReference(GetPinnableReference inst)
{ {
return Default(inst); return Default(inst);
} }
@ -7489,7 +7501,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(inst, context); return Default(inst, context);
} }
protected internal virtual T VisitArrayToPointer(ArrayToPointer inst, C context) protected internal virtual T VisitGetPinnableReference(GetPinnableReference inst, C context)
{ {
return Default(inst, context); return Default(inst, context);
} }
@ -7651,7 +7663,7 @@ namespace ICSharpCode.Decompiler.IL
"sizeof", "sizeof",
"ldlen", "ldlen",
"ldelema", "ldelema",
"array.to.pointer", "get.pinnable.reference",
"string.to.int", "string.to.int",
"expression.tree.cast", "expression.tree.cast",
"user.logic.operator", "user.logic.operator",
@ -8202,14 +8214,16 @@ namespace ICSharpCode.Decompiler.IL
array = default(ILInstruction); array = default(ILInstruction);
return false; return false;
} }
public bool MatchArrayToPointer(out ILInstruction array) public bool MatchGetPinnableReference(out ILInstruction argument, out IMethod method)
{ {
var inst = this as ArrayToPointer; var inst = this as GetPinnableReference;
if (inst != null) { if (inst != null) {
array = inst.Array; argument = inst.Argument;
method = inst.Method;
return true; return true;
} }
array = default(ILInstruction); argument = default(ILInstruction);
method = default(IMethod);
return false; return false;
} }
public bool MatchUserDefinedLogicOperator(out IMethod method, out ILInstruction left, out ILInstruction right) public bool MatchUserDefinedLogicOperator(out IMethod method, out ILInstruction left, out ILInstruction right)

8
ICSharpCode.Decompiler/IL/Instructions.tt

@ -279,9 +279,11 @@
new OpCode("ldelema", "Load address of array element.", new OpCode("ldelema", "Load address of array element.",
CustomClassName("LdElema"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true), CustomClassName("LdElema"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true),
MayThrowIfNotDelayed, ResultType("Ref"), SupportsReadonlyPrefix), MayThrowIfNotDelayed, ResultType("Ref"), SupportsReadonlyPrefix),
new OpCode("array.to.pointer", "Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty." + Environment.NewLine new OpCode("get.pinnable.reference", "Retrieves a pinnable reference for the input object." + Environment.NewLine
+ "Also used to convert a string to a reference to the first character.", + "The input must be an object reference (O)." + Environment.NewLine
CustomArguments(("array", new[] { "O" })), ResultType("Ref")), + "If the input is an array/string, evaluates to a reference to the first element/character, or to a null reference if the array is null or empty." + Environment.NewLine
+ "Otherwise, uses the GetPinnableReference method to get the reference, or evaluates to a null reference if the input is null." + Environment.NewLine,
CustomArguments(("argument", new[] { "O" })), ResultType("Ref"), HasMethodOperand),
new OpCode("string.to.int", "Maps a string value to an integer. This is used in switch(string).", new OpCode("string.to.int", "Maps a string value to an integer. This is used in switch(string).",
CustomArguments(("argument", new[] { "O" })), CustomConstructor, CustomWriteTo, ResultType("I4")), CustomArguments(("argument", new[] { "O" })), CustomConstructor, CustomWriteTo, ResultType("I4")),

2
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -459,7 +459,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break; break;
case OpCode.DynamicCompoundAssign: case OpCode.DynamicCompoundAssign:
return true; return true;
case OpCode.ArrayToPointer: case OpCode.GetPinnableReference:
case OpCode.LocAllocSpan: case OpCode.LocAllocSpan:
return true; // inline size-expressions into localloc.span return true; // inline size-expressions into localloc.span
case OpCode.Call: case OpCode.Call:

Loading…
Cancel
Save