Browse Source

[nullables] Lifted compound assignments

pull/897/head
Daniel Grunwald 8 years ago
parent
commit
61900e33c3
  1. 32
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 2
      ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs
  3. 22
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs
  4. 7
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  5. 19
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

32
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -743,18 +743,9 @@ namespace ICSharpCode.Decompiler.CSharp
right = right.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this); right = right.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this);
} }
TranslatedExpression result = new BinaryOperatorExpression(left.Expression, op, right.Expression) return new BinaryOperatorExpression(left.Expression, op, right.Expression)
.WithILInstruction(inst) .WithILInstruction(inst)
.WithRR(resolver.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult)); .WithRR(resolver.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult));
if (inst.UnderlyingResultType == StackType.I) {
// C# doesn't have shift operators for IntPtr, so we first shifted a long/ulong,
// and now have to case back down to IntPtr/UIntPtr:
IType targetType = compilation.FindType(sign == Sign.Unsigned ? KnownTypeCode.UIntPtr : KnownTypeCode.IntPtr);
if (inst.IsLifted)
targetType = NullableType.Create(compilation, targetType);
result = result.ConvertTo(targetType, this);
}
return result;
} }
protected internal override TranslatedExpression VisitCompoundAssignmentInstruction(CompoundAssignmentInstruction inst, TranslationContext context) protected internal override TranslatedExpression VisitCompoundAssignmentInstruction(CompoundAssignmentInstruction inst, TranslationContext context)
@ -789,7 +780,7 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
var target = Translate(inst.Target); var target = Translate(inst.Target);
var value = Translate(inst.Value); var value = Translate(inst.Value);
value = PrepareArithmeticArgument(value, inst.Value.ResultType, inst.Sign, isLifted: false); value = PrepareArithmeticArgument(value, inst.RightInputType, inst.Sign, inst.IsLifted);
TranslatedExpression resultExpr; TranslatedExpression resultExpr;
if (inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToOldValue) { if (inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToOldValue) {
@ -836,25 +827,16 @@ namespace ICSharpCode.Decompiler.CSharp
var target = Translate(inst.Target); var target = Translate(inst.Target);
var value = Translate(inst.Value); var value = Translate(inst.Value);
IType targetType;
if (inst.ResultType == StackType.I4)
targetType = compilation.FindType(inst.Sign == Sign.Unsigned ? KnownTypeCode.UInt32 : KnownTypeCode.Int32);
else
targetType = compilation.FindType(inst.Sign == Sign.Unsigned ? KnownTypeCode.UInt64 : KnownTypeCode.Int64);
target = target.ConvertTo(targetType, this);
// Shift operators in C# always expect type 'int' on the right-hand-side // Shift operators in C# always expect type 'int' on the right-hand-side
if (NullableType.IsNullable(value.Type)) {
value = value.ConvertTo(NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Int32)), this);
} else {
value = value.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this); value = value.ConvertTo(compilation.FindType(KnownTypeCode.Int32), this);
}
TranslatedExpression result = new AssignmentExpression(target.Expression, op, value.Expression) return new AssignmentExpression(target.Expression, op, value.Expression)
.WithILInstruction(inst) .WithILInstruction(inst)
.WithRR(resolver.ResolveAssignment(op, target.ResolveResult, value.ResolveResult)); .WithRR(resolver.ResolveAssignment(op, target.ResolveResult, value.ResolveResult));
if (inst.ResultType == StackType.I) {
// C# doesn't have shift operators for IntPtr, so we first shifted a long/ulong,
// and now have to case back down to IntPtr/UIntPtr:
result = result.ConvertTo(compilation.FindType(inst.Sign == Sign.Unsigned ? KnownTypeCode.UIntPtr : KnownTypeCode.IntPtr), this);
}
return result;
} }
static bool AssignmentOperatorMightCheckForOverflow(AssignmentOperatorType op) static bool AssignmentOperatorMightCheckForOverflow(AssignmentOperatorType op)

2
ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs

@ -60,7 +60,7 @@ namespace ICSharpCode.Decompiler.IL
public readonly BinaryNumericOperator Operator; public readonly BinaryNumericOperator Operator;
/// <summary> /// <summary>
/// Gets whether this conversion is a lifted nullable operation. /// Gets whether this is a lifted nullable operation.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// A lifted binary operation allows its arguments to be a value of type Nullable{T}, where /// A lifted binary operation allows its arguments to be a value of type Nullable{T}, where

22
ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs

@ -42,6 +42,10 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
public readonly Sign Sign; public readonly Sign Sign;
public readonly StackType LeftInputType;
public readonly StackType RightInputType;
public StackType UnderlyingResultType { get; }
/// <summary> /// <summary>
/// The operator used by this assignment operator instruction. /// The operator used by this assignment operator instruction.
/// </summary> /// </summary>
@ -49,17 +53,23 @@ namespace ICSharpCode.Decompiler.IL
public readonly CompoundAssignmentType CompoundAssignmentType; public readonly CompoundAssignmentType CompoundAssignmentType;
public CompoundAssignmentInstruction(BinaryNumericOperator op, ILInstruction target, ILInstruction value, IType type, bool checkForOverflow, Sign sign, CompoundAssignmentType compoundAssigmentType) public bool IsLifted { get; }
public CompoundAssignmentInstruction(BinaryNumericInstruction binary, ILInstruction target, ILInstruction value, IType type, CompoundAssignmentType compoundAssignmentType)
: base(OpCode.CompoundAssignmentInstruction) : base(OpCode.CompoundAssignmentInstruction)
{ {
this.CheckForOverflow = checkForOverflow; this.CheckForOverflow = binary.CheckForOverflow;
this.Sign = sign; this.Sign = binary.Sign;
this.Operator = op; this.LeftInputType = binary.LeftInputType;
this.RightInputType = binary.RightInputType;
this.UnderlyingResultType = binary.UnderlyingResultType;
this.Operator = binary.Operator;
this.CompoundAssignmentType = compoundAssignmentType;
this.IsLifted = binary.IsLifted;
this.Target = target; this.Target = target;
this.type = type; this.type = type;
this.Value = value; this.Value = value;
this.CompoundAssignmentType = compoundAssigmentType; Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub));
Debug.Assert(compoundAssigmentType == CompoundAssignmentType.EvaluatesToNewValue || (op == BinaryNumericOperator.Add || op == BinaryNumericOperator.Sub));
Debug.Assert(IsValidCompoundAssignmentTarget(Target)); Debug.Assert(IsValidCompoundAssignmentTarget(Target));
} }

7
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -283,7 +283,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
} }
// This transform is required because ILInlining only works with stloc/ldloc
protected internal override void VisitStObj(StObj inst) protected internal override void VisitStObj(StObj inst)
{ {
base.VisitStObj(inst); base.VisitStObj(inst);
@ -292,17 +291,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
if (inst.Value is BinaryNumericInstruction binary if (inst.Value is BinaryNumericInstruction binary
&& !binary.IsLifted
&& binary.Left.MatchLdObj(out ILInstruction target, out IType t) && binary.Left.MatchLdObj(out ILInstruction target, out IType t)
&& inst.Target.Match(target).Success) && inst.Target.Match(target).Success)
{ {
context.Step("compound assignment", inst); context.Step("compound assignment", inst);
// stobj(target, binary.op(ldobj(target), ...)) // stobj(target, binary.op(ldobj(target), ...))
// => compound.op(target, ...) // => compound.op(target, ...)
inst.ReplaceWith(new CompoundAssignmentInstruction(binary.Operator, binary.Left, binary.Right, t, binary.CheckForOverflow, binary.Sign, CompoundAssignmentType.EvaluatesToNewValue)); inst.ReplaceWith(new CompoundAssignmentInstruction(
binary, binary.Left, binary.Right,
t, CompoundAssignmentType.EvaluatesToNewValue));
} }
} }
// This transform is required because ILInlining only works with stloc/ldloc
internal static bool StObjToStLoc(StObj inst, ILTransformContext context) internal static bool StObjToStLoc(StObj inst, ILTransformContext context)
{ {
if (inst.Target.MatchLdLoca(out ILVariable v) if (inst.Target.MatchLdLoca(out ILVariable v)

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

@ -147,13 +147,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var usages = next.Descendants.Where(d => d.MatchLdLoc(localVariable)).ToArray(); var usages = next.Descendants.Where(d => d.MatchLdLoc(localVariable)).ToArray();
if (usages.Length != 1) if (usages.Length != 1)
return false; return false;
if (binary.IsLifted)
return false;
context.Step($"Compound assignment to '{owner.Name}'", setterCall); context.Step($"Compound assignment to '{owner.Name}'", setterCall);
block.Instructions.RemoveAt(i + 1); block.Instructions.RemoveAt(i + 1);
block.Instructions.RemoveAt(i); block.Instructions.RemoveAt(i);
usages[0].ReplaceWith(new CompoundAssignmentInstruction(binary.Operator, getterCall, binary.Right, usages[0].ReplaceWith(new CompoundAssignmentInstruction(
getterCall.Method.ReturnType, binary.CheckForOverflow, binary.Sign, CompoundAssignmentType.EvaluatesToNewValue)); binary, getterCall, binary.Right,
getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue));
return true; return true;
} }
@ -239,10 +238,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (binary == null || !binary.Left.MatchLdLoc(nextInst.Variable) || !binary.Right.MatchLdcI4(1) if (binary == null || !binary.Left.MatchLdLoc(nextInst.Variable) || !binary.Right.MatchLdcI4(1)
|| (binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub)) || (binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub))
return false; return false;
if (binary.IsLifted)
return false;
context.Step($"TransformPostIncDecOperator", inst); context.Step($"TransformPostIncDecOperator", inst);
var assignment = new CompoundAssignmentInstruction(binary.Operator, new LdObj(inst.Value, targetType), binary.Right, targetType, binary.CheckForOverflow, binary.Sign, CompoundAssignmentType.EvaluatesToOldValue); var assignment = new CompoundAssignmentInstruction(binary, new LdObj(inst.Value, targetType), binary.Right, targetType, CompoundAssignmentType.EvaluatesToOldValue);
stobj.ReplaceWith(new StLoc(nextInst.Variable, assignment)); stobj.ReplaceWith(new StLoc(nextInst.Variable, assignment));
block.Instructions.RemoveAt(i + 1); block.Instructions.RemoveAt(i + 1);
return true; return true;
@ -291,10 +288,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (binary == null || !binary.Left.MatchLdLoc(fieldValue.Variable) || !binary.Right.MatchLdcI4(1) if (binary == null || !binary.Left.MatchLdLoc(fieldValue.Variable) || !binary.Right.MatchLdcI4(1)
|| (binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub)) || (binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub))
return false; return false;
if (binary.IsLifted)
return false;
context.Step($"TransformCSharp4PostIncDecOperatorOnAddress", baseFieldAddress); context.Step($"TransformCSharp4PostIncDecOperatorOnAddress", baseFieldAddress);
var assignment = new CompoundAssignmentInstruction(binary.Operator, new LdObj(baseAddress, t), binary.Right, t, binary.CheckForOverflow, binary.Sign, CompoundAssignmentType.EvaluatesToOldValue); var assignment = new CompoundAssignmentInstruction(binary, new LdObj(baseAddress, t), binary.Right, t, CompoundAssignmentType.EvaluatesToOldValue);
stobj.ReplaceWith(new StLoc(fieldValueCopyToLocal.Variable, assignment)); stobj.ReplaceWith(new StLoc(fieldValueCopyToLocal.Variable, assignment));
block.Instructions.RemoveAt(i + 2); block.Instructions.RemoveAt(i + 2);
block.Instructions.RemoveAt(i + 1); block.Instructions.RemoveAt(i + 1);
@ -323,10 +318,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var binary = stobj.Value as BinaryNumericInstruction; var binary = stobj.Value as BinaryNumericInstruction;
if (binary == null || !binary.Left.MatchLdLoc(inst.Variable) || !binary.Right.MatchLdcI4(1)) if (binary == null || !binary.Left.MatchLdLoc(inst.Variable) || !binary.Right.MatchLdcI4(1))
return false; return false;
if (binary.IsLifted)
return false;
context.Step($"TransformPostIncDecOnStaticField", inst); context.Step($"TransformPostIncDecOnStaticField", inst);
var assignment = new CompoundAssignmentInstruction(binary.Operator, inst.Value, binary.Right, type, binary.CheckForOverflow, binary.Sign, CompoundAssignmentType.EvaluatesToOldValue); var assignment = new CompoundAssignmentInstruction(binary, inst.Value, binary.Right, type, CompoundAssignmentType.EvaluatesToOldValue);
stobj.ReplaceWith(new StLoc(inst.Variable, assignment)); stobj.ReplaceWith(new StLoc(inst.Variable, assignment));
return true; return true;
} }

Loading…
Cancel
Save