Browse Source

Simplify operators on nullable values: all comparisons

pull/205/head
Pent Ploompuu 15 years ago
parent
commit
9c2a58d292
  1. 196
      ICSharpCode.Decompiler/ILAst/NullableOperators.cs

196
ICSharpCode.Decompiler/ILAst/NullableOperators.cs

@ -38,7 +38,7 @@ namespace ICSharpCode.Decompiler.ILAst
static bool SimplifyNullableOperators(ILExpression expr) static bool SimplifyNullableOperators(ILExpression expr)
{ {
if (IsNullableOperation(expr)) return true; if (PatternMatcher.Simplify(expr)) return true;
bool modified = false; bool modified = false;
foreach (var a in expr.Arguments) foreach (var a in expr.Arguments)
@ -46,27 +46,9 @@ namespace ICSharpCode.Decompiler.ILAst
return modified; return modified;
} }
static bool IsNullableOperation(ILExpression expr)
{
if (expr.Code != ILCode.LogicAnd && expr.Code != ILCode.LogicOr) return false;
var ps = PatternMatcher.Comparisons;
for (int i = 0; i < ps.Length; i += 2) {
var pm = new PatternMatcher();
if (!pm.Match(ps[i], expr)) continue;
var n = pm.BuildNew(ps[i + 1], expr);
expr.Code = n.Code;
expr.Operand = n.Operand;
expr.Arguments = n.Arguments;
expr.ILRanges = n.ILRanges;
return true;
}
return false;
}
struct PatternMatcher struct PatternMatcher
{ {
public abstract class Pattern abstract class Pattern
{ {
public readonly Pattern[] Arguments; public readonly Pattern[] Arguments;
@ -83,6 +65,21 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
public static Pattern operator &(Pattern a, Pattern b)
{
return new ILPattern(ILCode.LogicAnd, a, b);
}
public static Pattern operator |(Pattern a, Pattern b)
{
return new ILPattern(ILCode.LogicOr, a, b);
}
public static Pattern operator !(Pattern a)
{
return new ILPattern(ILCode.LogicNot, a);
}
} }
sealed class ILPattern : Pattern sealed class ILPattern : Pattern
@ -134,7 +131,7 @@ namespace ICSharpCode.Decompiler.ILAst
public OperatorPattern(params Pattern[] arguments) : base(arguments) { } public OperatorPattern(params Pattern[] arguments) : base(arguments) { }
public OperatorPattern(bool? equals, bool? custom, params Pattern[] arguments) public OperatorPattern(bool? equals, bool? custom, Pattern[] arguments)
: base(arguments) : base(arguments)
{ {
this.equals = equals; this.equals = equals;
@ -242,56 +239,95 @@ namespace ICSharpCode.Decompiler.ILAst
static readonly Tuple<string, string, string> GetValueOrDefault = new Tuple<string, string, string>("GetValueOrDefault", "Nullable`1", "System"); static readonly Tuple<string, string, string> GetValueOrDefault = new Tuple<string, string, string>("GetValueOrDefault", "Nullable`1", "System");
static readonly Tuple<string, string, string> get_HasValue = new Tuple<string, string, string>("get_HasValue", "Nullable`1", "System"); static readonly Tuple<string, string, string> get_HasValue = new Tuple<string, string, string>("get_HasValue", "Nullable`1", "System");
static readonly VariablePattern VariableRefA = new VariablePattern(ILCode.Ldloca, false), VariableRefB = new VariablePattern(ILCode.Ldloca, true); static readonly Pattern VariableRefA = new VariablePattern(ILCode.Ldloca, false), VariableRefB = new VariablePattern(ILCode.Ldloca, true);
static readonly OperatorPattern OperatorVariableAB = new OperatorPattern(new VariablePattern(ILCode.Ldloc, false), new VariablePattern(ILCode.Ldloc, true)); static readonly Pattern VariableA = new VariablePattern(ILCode.Ldloc, false), VariableB = new VariablePattern(ILCode.Ldloc, true);
static readonly MethodPattern[] Call2GetValueOrDefault = new[] { static readonly Pattern VariableAHasValue = new MethodPattern(ILCode.CallGetter, get_HasValue, VariableRefA);
new MethodPattern(ILCode.Call, GetValueOrDefault, VariableRefA), static readonly Pattern VariableAGetValueOrDefault = new MethodPattern(ILCode.Call, GetValueOrDefault, VariableRefA);
new MethodPattern(ILCode.Call, GetValueOrDefault, VariableRefB)}; static readonly Pattern NotVariableAHasValue = new ILPattern(ILCode.Ceq, VariableAHasValue, new PrimitivePattern(ILCode.Ldc_I4, 0));
static readonly MethodPattern CallVariableAget_HasValue = new MethodPattern(ILCode.CallGetter, get_HasValue, VariableRefA); static readonly Pattern VariableBHasValue = new MethodPattern(ILCode.CallGetter, get_HasValue, VariableRefB);
static readonly MethodPattern[] Call2get_HasValue = new[] { CallVariableAget_HasValue, new MethodPattern(ILCode.CallGetter, get_HasValue, VariableRefB) }; static readonly Pattern VariableBGetValueOrDefault = new MethodPattern(ILCode.Call, GetValueOrDefault, VariableRefB);
static readonly Pattern CeqHasValue = new ILPattern(ILCode.Ceq, Call2get_HasValue); static readonly Pattern NotVariableBHasValue = new ILPattern(ILCode.Ceq, VariableBHasValue, new PrimitivePattern(ILCode.Ldc_I4, 0));
static readonly Pattern AndHasValue = new ILPattern(ILCode.And, Call2get_HasValue); static readonly Pattern CeqHasValue = new ILPattern(ILCode.Ceq, VariableAHasValue, VariableBHasValue);
static readonly Pattern AndHasValue = new ILPattern(ILCode.And, VariableAHasValue, VariableBHasValue);
public static readonly Pattern[] Comparisons = new Pattern[] { static readonly Pattern NotCeqHasValue = new ILPattern(ILCode.Ceq, CeqHasValue, new PrimitivePattern(ILCode.Ldc_I4, 0));
static readonly Pattern[] LoadValuesNN = new[] { VariableAGetValueOrDefault, VariableBGetValueOrDefault };
static OperatorPattern OperatorNN(bool? equals = null, bool? custom = null)
{
return new OperatorPattern(equals, custom, LoadValuesNN);
}
static readonly Pattern[] LoadValuesNV = new[] { VariableAGetValueOrDefault, VariableB };
static OperatorPattern OperatorNV(bool? equals = null, bool? custom = null)
{
return new OperatorPattern(equals, custom, LoadValuesNV);
}
static readonly Pattern[] LoadValuesVN = new[] { VariableA, VariableBGetValueOrDefault };
static OperatorPattern OperatorVN(bool? equals = null, bool? custom = null)
{
return new OperatorPattern(equals, custom, LoadValuesVN);
}
static readonly Pattern[] Comparisons = new Pattern[] {
/* both operands nullable */
// == (Primitive, Decimal)
!!OperatorNN(equals: true) & CeqHasValue,
// == (Struct)
!!CeqHasValue & (!VariableAHasValue | OperatorNN(equals: true, custom: true)),
// != (Primitive)
!OperatorNN(equals: true, custom: false) | NotCeqHasValue,
// != (Decimal)
OperatorNN(equals: false, custom: true) | NotCeqHasValue,
// != (Struct)
!CeqHasValue | (!!VariableAHasValue & OperatorNN(equals: false, custom: true)),
// > , < (Primitive, Decimal), >= , <= (Decimal)
!!OperatorNN() & AndHasValue,
// >= , <= (Primitive)
!OperatorNN(custom: false) & AndHasValue,
// > , < , >= , <= (Struct)
AndHasValue & OperatorNN(custom: true),
/* only first operand nullable */
// == (Primitive, Decimal) // == (Primitive, Decimal)
new ILPattern(ILCode.LogicAnd, new ILPattern(ILCode.LogicNot, new ILPattern(ILCode.LogicNot, new OperatorPattern(true, null, Call2GetValueOrDefault))), CeqHasValue), !!OperatorNV(equals: true) & VariableAHasValue,
OperatorVariableAB,
// == (Struct) // == (Struct)
new ILPattern(ILCode.LogicAnd, !!VariableAHasValue & OperatorNV(equals: true, custom: true),
new ILPattern(ILCode.LogicNot, new ILPattern(ILCode.LogicNot, CeqHasValue)), // != (Primitive)
new ILPattern(ILCode.LogicOr, new ILPattern(ILCode.LogicNot, CallVariableAget_HasValue), new OperatorPattern(true, true, Call2GetValueOrDefault))), !OperatorNV(equals: true, custom: false) | NotVariableAHasValue,
OperatorVariableAB, // != (Decimal)
// != (P) OperatorNV(equals: false, custom: true) | NotVariableAHasValue,
new ILPattern(ILCode.LogicOr, // != (Struct)
new ILPattern(ILCode.LogicNot, new OperatorPattern(true, false, Call2GetValueOrDefault)), !VariableAHasValue | OperatorNV(equals: false, custom: true),
new ILPattern(ILCode.Ceq, CeqHasValue, new PrimitivePattern(ILCode.Ldc_I4, 0))), // > , < (Primitive, Decimal), >= , <= (Decimal)
new ILPattern(ILCode.LogicNot, OperatorVariableAB), !!OperatorNV() & VariableAHasValue,
// != (D) // >= , <= (Primitive)
new ILPattern(ILCode.LogicOr, !OperatorNV(custom: false) & VariableAHasValue,
new OperatorPattern(false, true, Call2GetValueOrDefault), // > , < , >= , <= (Struct)
new ILPattern(ILCode.Ceq, CeqHasValue, new PrimitivePattern(ILCode.Ldc_I4, 0))), VariableAHasValue & OperatorNV(custom: true),
OperatorVariableAB,
// != (S) /* only second operand nullable */
new ILPattern(ILCode.LogicOr, // == (Primitive, Decimal)
new ILPattern(ILCode.LogicNot, CeqHasValue), !!OperatorVN(equals: true) & VariableBHasValue,
new ILPattern(ILCode.LogicAnd, // == (Struct)
new ILPattern(ILCode.LogicNot, new ILPattern(ILCode.LogicNot, CallVariableAget_HasValue)), !!VariableBHasValue & OperatorVN(equals: true, custom: true),
new OperatorPattern(false, true, Call2GetValueOrDefault))), // != (Primitive)
OperatorVariableAB, !OperatorVN(equals: true, custom: false) | NotVariableBHasValue,
// > (P, D), < (P, D), >= (D), <= (D) // != (Decimal)
new ILPattern(ILCode.LogicAnd, new ILPattern(ILCode.LogicNot, new ILPattern(ILCode.LogicNot, new OperatorPattern(null, null, Call2GetValueOrDefault))), AndHasValue), OperatorVN(equals: false, custom: true) | NotVariableBHasValue,
OperatorVariableAB, // != (Struct)
// >= (P), <= (P) !VariableBHasValue | OperatorVN(equals: false, custom: true),
new ILPattern(ILCode.LogicAnd, new ILPattern(ILCode.LogicNot, new OperatorPattern(null, false, Call2GetValueOrDefault)), AndHasValue), // > , < (Primitive, Decimal), >= , <= (Decimal)
new ILPattern(ILCode.LogicNot, OperatorVariableAB), !!OperatorVN() & VariableBHasValue,
// > (S), < (S), >= (S), <= (S) // >= , <= (Primitive)
new ILPattern(ILCode.LogicAnd, AndHasValue, new OperatorPattern(null, true, Call2GetValueOrDefault)), !OperatorVN(custom: false) & VariableBHasValue,
OperatorVariableAB, // > , < , >= , <= (Struct)
VariableBHasValue & OperatorVN(custom: true),
}; };
public ILVariable A, B; ILVariable A, B;
public ILExpression Operator; ILExpression Operator;
public bool Match(Pattern p, ILExpression e) bool Match(Pattern p, ILExpression e)
{ {
if (!p.Match(ref this, e) || e.Arguments.Count != p.Arguments.Length || e.Prefixes != null) return false; if (!p.Match(ref this, e) || e.Arguments.Count != p.Arguments.Length || e.Prefixes != null) return false;
for (int i = 0; i < p.Arguments.Length; i++) for (int i = 0; i < p.Arguments.Length; i++)
@ -299,14 +335,36 @@ namespace ICSharpCode.Decompiler.ILAst
return true; return true;
} }
public ILExpression BuildNew(Pattern p, ILExpression old) ILExpression BuildNew(Pattern p, ILExpression old)
{ {
var args = new ILExpression[p.Arguments.Length]; var args = new ILExpression[p.Arguments.Length];
for (int i = 0; i < args.Length; i++) args[i] = BuildNew(p.Arguments[i], old); for (int i = 0; i < args.Length; i++) args[i] = BuildNew(p.Arguments[i], old);
var res = p.BuildNew(ref this, args); var res = p.BuildNew(ref this, args);
if (args.Length == 2) res.ILRanges = ILRange.OrderAndJoint(old.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(el => el.ILRanges)); if (p is OperatorPattern) res.ILRanges = ILRange.OrderAndJoint(old.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(el => el.ILRanges));
return res; return res;
} }
static readonly Pattern OperatorVariableAB = new OperatorPattern(VariableA, VariableB);
static readonly Pattern NotOperatorVariableAB = !OperatorVariableAB;
public static bool Simplify(ILExpression expr)
{
if (expr.Code != ILCode.LogicAnd && expr.Code != ILCode.LogicOr) return false;
var ps = Comparisons;
for (int i = 0; i < ps.Length; i++) {
var pm = new PatternMatcher();
if (!pm.Match(ps[i], expr)) continue;
var pi = i % 8;
var n = pm.BuildNew(pi != 2 && pi != 6 ? OperatorVariableAB : NotOperatorVariableAB, expr);
expr.Code = n.Code;
expr.Operand = n.Operand;
expr.Arguments = n.Arguments;
expr.ILRanges = n.ILRanges;
return true;
}
return false;
}
} }
} }
} }

Loading…
Cancel
Save