Browse Source

more attempts at integer conversion stuff

pull/728/head
Daniel Grunwald 9 years ago
parent
commit
95c8e4bd00
  1. 67
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 150
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 2
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

67
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -485,6 +485,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -485,6 +485,11 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
/// <summary>
/// Whether we need to generate helper methods for u4->i or u->i8 conversions.
/// </summary>
internal bool needs_conv_i_ovf_un, needs_conv_i8_ovf_un;
EntityDeclaration DoDecompile(ITypeDefinition typeDef, ITypeResolveContext decompilationContext)
{
Debug.Assert(decompilationContext.CurrentTypeDefinition == typeDef);
@ -542,9 +547,68 @@ namespace ICSharpCode.Decompiler.CSharp @@ -542,9 +547,68 @@ namespace ICSharpCode.Decompiler.CSharp
section.Remove();
}
}
if (needs_conv_i_ovf_un) {
typeDecl.Members.Add(GenerateConvHelper(
"conv_i_ovf_un", KnownTypeCode.UInt32, KnownTypeCode.IntPtr, typeSystemAstBuilder,
// on 32-bit, 'conv.ovf u4->i' is like 'conv.ovf u4->i4'
new CheckedExpression(new CastExpression(
new NRefactory.CSharp.PrimitiveType("int"),
new IdentifierExpression("input")
)),
// on 64-bit, 'conv.ovf u4->i' is like 'conv.ovf u4->i8'
new IdentifierExpression("input")
));
needs_conv_i_ovf_un = false;
}
if (needs_conv_i8_ovf_un) {
typeDecl.Members.Add(GenerateConvHelper(
"conv_i8_ovf_un", KnownTypeCode.UIntPtr, KnownTypeCode.Int64, typeSystemAstBuilder,
// on 32-bit, 'conv.ovf u->i8' is like 'conv.ovf u4->i8'
new IdentifierExpression("input"),
// on 64-bit, 'conv.ovf u->i8' is like 'conv.ovf u8->i8'
new IdentifierExpression("input")
));
needs_conv_i8_ovf_un = false;
}
return typeDecl;
}
MethodDeclaration GenerateConvHelper(string name, KnownTypeCode source, KnownTypeCode target, TypeSystemAstBuilder typeSystemAstBuilder,
Expression intermediate32, Expression intermediate64)
{
MethodDeclaration method = new MethodDeclaration();
method.Name = name;
method.Modifiers = Modifiers.Private | Modifiers.Static;
method.Parameters.Add(new ParameterDeclaration(typeSystemAstBuilder.ConvertType(typeSystem.Compilation.FindType(source)), "input"));
method.ReturnType = typeSystemAstBuilder.ConvertType(typeSystem.Compilation.FindType(target));
method.Body = new BlockStatement {
new IfElseStatement {
Condition = new BinaryOperatorExpression {
Left = typeSystemAstBuilder.ConvertType(typeSystem.Compilation.FindType(KnownTypeCode.IntPtr)).Member("Size"),
Operator = BinaryOperatorType.Equality,
Right = new PrimitiveExpression(4)
},
TrueStatement = new BlockStatement { // 32-bit
new ReturnStatement(
new CastExpression(
method.ReturnType.Clone(),
intermediate32
)
)
},
FalseStatement = new BlockStatement { // 64-bit
new ReturnStatement(
new CastExpression(
method.ReturnType.Clone(),
intermediate64
)
)
},
}
};
return method;
}
EntityDeclaration DoDecompile(MethodDefinition methodDefinition, IMethod method, ITypeResolveContext decompilationContext)
{
@ -610,6 +674,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -610,6 +674,9 @@ namespace ICSharpCode.Decompiler.CSharp
}
var statementBuilder = new StatementBuilder(decompilationContext, method);
var body = statementBuilder.ConvertAsBlock(function.Body);
needs_conv_i_ovf_un |= statementBuilder.exprBuilder.needs_conv_i_ovf_un;
needs_conv_i8_ovf_un |= statementBuilder.exprBuilder.needs_conv_i8_ovf_un;
entityDecl.AddChild(body, Roles.Body);
}

150
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -609,6 +609,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -609,6 +609,11 @@ namespace ICSharpCode.Decompiler.CSharp
return result;
}
/// <summary>
/// Whether we need to generate helper methods for u4->i or u->i8 conversions.
/// </summary>
internal bool needs_conv_i_ovf_un, needs_conv_i8_ovf_un;
protected internal override TranslatedExpression VisitConv(Conv inst)
{
var arg = Translate(inst.Argument);
@ -623,6 +628,61 @@ namespace ICSharpCode.Decompiler.CSharp @@ -623,6 +628,61 @@ namespace ICSharpCode.Decompiler.CSharp
// Also, we need to be very careful with regards to the conversions we emit:
// In C#, zero vs. sign-extension depends on the input type,
// but in the ILAst Conv instruction it depends on the output type.
if (inst.CheckForOverflow || inst.Kind == ConversionKind.IntToFloat) {
// We need to first convert the argument to the expected sign.
// We also need to perform any input narrowing conversion so that it doesn't get mixed up with the overflow check.
Debug.Assert(inst.InputSign != Sign.None);
if (arg.Type.GetSize() > inputStackType.GetSize() || arg.Type.GetSign() != inst.InputSign) {
arg = arg.ConvertTo(compilation.FindType(inputStackType.ToKnownTypeCode(inst.InputSign)), this);
}
if (inst.Kind == ConversionKind.ZeroExtend) {
// Zero extension with overflow check -> throws if the input value is negative.
// C# sign/zero extension depends on the source type, so we can't directly
// cast from the signed input type to the target type.
// Instead, perform a checked cast to the unsigned version of the input type
// (to throw an exception for negative values).
// Then the actual zero extension can be left to our parent instruction
// (due to the ExpressionBuilder post-condition being flexible with regards to the integer type width).
return arg.ConvertTo(compilation.FindType(inputStackType.ToKnownTypeCode(Sign.Unsigned)), this, true)
.WithILInstruction(inst);
} else if (inst.Kind == ConversionKind.SignExtend) {
// Sign extension with overflow check.
// Sign-extending conversions can fail when the "larger type" isn't actually larger, that is, in exactly two cases:
// * U4 -> I on 32-bit
// * U -> I8 on 64-bit
if (inst.InputSign == Sign.Unsigned && inputStackType == StackType.I4 && inst.TargetType == IL.PrimitiveType.I) {
// conv u4->i
// on 32-bit, this is a sign-changing conversion with overflow-check
// on 64-bit, this is a sign extension
needs_conv_i_ovf_un = true;
arg = arg.ConvertTo(compilation.FindType(KnownTypeCode.UInt32), this);
return new InvocationExpression(new IdentifierExpression("conv_i_ovf_un"), arg.Expression)
.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.IntPtr)))
.WithILInstruction(inst);
} else if (inst.InputSign == Sign.Unsigned && (inputStackType == StackType.I && inst.TargetType == IL.PrimitiveType.I8)) {
// conv u->i8
// on 32-bit, this is a sign extension
// on 64-bit, this is a sign-changing conversion with overflow-check
needs_conv_i8_ovf_un = true;
arg = arg.ConvertTo(compilation.FindType(KnownTypeCode.UIntPtr), this);
return new InvocationExpression(new IdentifierExpression("conv_i8_ovf_un"), arg.Expression)
.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.IntPtr)))
.WithILInstruction(inst);
} else {
// The overflow check cannot actually fail, so we can take the simple solution of performing
// an unchecked cast to signed int and let the parent instruction handle the actual sign extension.
return arg.ConvertTo(compilation.FindType(inputStackType.ToKnownTypeCode(Sign.Signed)), this)
.WithILInstruction(inst);
}
} else {
// Size-preserving sign-changing conversion, or int-to-float conversion:
// We can directly cast to the target type.
return arg.ConvertTo(compilation.FindType(inst.TargetType.ToKnownTypeCode()), this, true)
.WithILInstruction(inst);
}
}
switch (inst.Kind) {
case ConversionKind.StopGCTracking:
if (arg.Type.Kind == TypeKind.ByReference) {
@ -634,8 +694,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -634,8 +694,6 @@ namespace ICSharpCode.Decompiler.CSharp
goto default;
}
case ConversionKind.SignExtend:
// Sign extension is easy because it can never fail due to overflow checking.
// We just need to ensure the input type before the conversion is signed.
// Also, if the argument was translated into an oversized C# type,
// we need to perform the truncatation to the input stack type.
@ -650,43 +708,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -650,43 +708,14 @@ namespace ICSharpCode.Decompiler.CSharp
// (our caller may have more information to pick a better fitting target type)
return arg.WithILInstruction(inst);
case ConversionKind.ZeroExtend:
// Zero extension may involve an overflow check.
if (!inst.CheckForOverflow || inst.InputSign == Sign.Unsigned) {
// If overflow check cannot fail, handle this just like sign extension (except for swapped signs)
if (arg.Type.GetSign() != Sign.Unsigned || arg.Type.GetSize() > inputStackType.GetSize()) {
arg = arg.ConvertTo(compilation.FindType(inputStackType.ToKnownTypeCode(Sign.Unsigned)), this);
}
return arg.WithILInstruction(inst);
// If overflow check cannot fail, handle this just like sign extension (except for swapped signs)
if (arg.Type.GetSign() != Sign.Unsigned || arg.Type.GetSize() > inputStackType.GetSize()) {
arg = arg.ConvertTo(compilation.FindType(inputStackType.ToKnownTypeCode(Sign.Unsigned)), this);
}
// Zero extension that should fail if the input type is negative.
// Split this conversion into steps:
// 1) perform input truncation if necessary, and perform unchecked cast to signed input type
if (arg.Type.GetSize() > inputStackType.GetSize() || arg.Type.GetSign() != inst.InputSign) {
arg = arg.ConvertTo(compilation.FindType(inputStackType.ToKnownTypeCode(inst.InputSign)), this);
}
// 2) perform sign conversion to unsigned with overflow check (input zero/sign extension can be combined with this step),
arg = arg.ConvertTo(compilation.FindType(inputStackType.ToKnownTypeCode(Sign.Unsigned)), this, true);
// 3) leave the actual zero extension to our caller.
return arg.WithILInstruction(inst);
case ConversionKind.Nop:
// Conversion between two types of same size; possibly sign-changing; may involve overflow checking.
if (!inst.CheckForOverflow || inst.InputSign == inst.TargetType.GetSign()) {
// a true nop: no need to generate any C# code
return arg.WithILInstruction(inst);
}
// sign-changing, overflow-checking conversion
// If the input conversion is a truncation, we need to perform it (without overflow-checking).
// If the argument has a different type than that expected by the overflow check,
// we need to ensure we use the correct input type.
if (arg.Type.GetSize() > inputStackType.GetSize() || arg.Type.GetSign() != inst.InputSign) {
arg = arg.ConvertTo(compilation.FindType(inputStackType.ToKnownTypeCode(inst.InputSign)), this);
}
// Note that an input conversion that is a sign/zero-extension can be combined with the
// overflow-checking conversion
goto default; // Perform the actual overflow-checking conversion
// no need to generate any C# code for a nop conversion
return arg.WithILInstruction(inst);
case ConversionKind.Truncate:
// Note: there are three sizes involved here:
// A = arg.Type.GetSize()
@ -700,41 +729,28 @@ namespace ICSharpCode.Decompiler.CSharp @@ -700,41 +729,28 @@ namespace ICSharpCode.Decompiler.CSharp
// In cases 1-3, the overall conversion is a truncation or no-op.
// In case 4, the overall conversion is a zero/sign extension, but to a smaller
// size than the original conversion.
if (!inst.CheckForOverflow) {
// Truncation without overflow check.
if (inst.TargetType.IsSmallIntegerType()) {
// If the target type is a small integer type, IL will implicitly sign- or zero-extend
// the result after the truncation back to StackType.I4.
// (which means there's actually 3 conversions involved!)
if (inst.TargetType.IsSmallIntegerType()) {
// If the target type is a small integer type, IL will implicitly sign- or zero-extend
// the result after the truncation back to StackType.I4.
// (which means there's actually 3 conversions involved!)
if (arg.Type.GetSize() <= inst.TargetType.GetSize() && arg.Type.GetSign() == inst.TargetType.GetSign()) {
// There's no actual truncation involved, and the result of the Conv instruciton is extended
// the same way as the original instruction
// -> we can return arg directly
return arg.WithILInstruction(inst);
} else {
// We need to actually truncate; *or* we need to change the sign for the remaining extension to I4.
goto default; // Emit simple cast to inst.TargetType
}
} else {
Debug.Assert(inst.TargetType.GetSize() == inst.ResultType.GetSize());
// For non-small integer types, we can let the whole unchecked truncation
// get handled by our caller (using the ExpressionBuilder post-condition).
// Case 4 (left-over extension from implicit conversion) can also be handled by our caller.
if (arg.Type.GetSize() <= inst.TargetType.GetSize() && arg.Type.GetSign() == inst.TargetType.GetSign()) {
// There's no actual truncation involved, and the result of the Conv instruction is extended
// the same way as the original instruction
// -> we can return arg directly
return arg.WithILInstruction(inst);
} else {
// We need to actually truncate; *or* we need to change the sign for the remaining extension to I4.
goto default; // Emit simple cast to inst.TargetType
}
} else {
Debug.Assert(inst.TargetType.GetSize() == inst.ResultType.GetSize());
// For non-small integer types, we can let the whole unchecked truncation
// get handled by our caller (using the ExpressionBuilder post-condition).
// Case 4 (left-over extension from implicit conversion) can also be handled by our caller.
return arg.WithILInstruction(inst);
}
// Truncation with overflow check
// Similar to nop-case: perform input truncation without overflow checking + ensure correct input sign.
if (arg.Type.GetSize() > inputStackType.GetSize() || arg.Type.GetSign() != inst.InputSign) {
arg = arg.ConvertTo(compilation.FindType(inputStackType.ToKnownTypeCode(inst.InputSign)), this);
}
goto default; // perform cast with overflow-check
case ConversionKind.IntToFloat: // TODO: handle these conversions correctly
case ConversionKind.FloatToInt:
case ConversionKind.FloatPrecisionChange:
default:
return arg.ConvertTo(compilation.FindType(inst.TargetType.ToKnownTypeCode()), this, inst.CheckForOverflow)
.WithILInstruction(inst);

2
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -30,7 +30,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -30,7 +30,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
class StatementBuilder : ILVisitor<Statement>
{
readonly ExpressionBuilder exprBuilder;
internal readonly ExpressionBuilder exprBuilder;
readonly IMethod currentMethod;
public StatementBuilder(ITypeResolveContext decompilationContext, IMethod currentMethod)

Loading…
Cancel
Save