Browse Source

Avoid using inline assignments if they truncate the input value.

pull/960/head
Daniel Grunwald 8 years ago
parent
commit
c7e60a9b3c
  1. 427
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il
  2. 4
      ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs
  3. 63
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs
  4. 3
      ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs

427
ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il

@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
.assembly extern mscorlib
{
.publickeytoken = ( b7 7a 5c 56 19 34 e0 89 )
.ver 4:0:0:0
.publickeytoken = ( b7 7a 5c 56 19 34 e0 89 )
.ver 4:0:0:0
}
.assembly 'StackTypes'
{
.ver 0:0:0:0
.ver 0:0:0:0
}
.module StackTypes.exe
@ -15,204 +15,227 @@ @@ -15,204 +15,227 @@
.class private auto ansi abstract sealed beforefieldinit Program
extends [mscorlib]System.Object
{
.method public hidebysig static void Main (string[] args) cil managed
{
.maxstack 8
.entrypoint
//call void Program::Int32OrNativeTests()
ret
} // end of method Main
/*
.method public static void Int32OrNativeTests()
{
ldstr "Int32OrNative(0x7fffffff, false) = {0}"
ldc.i4 0x7fffffff
ldc.i4 0
call native int Program::Int32OrNative(int32, bool)
box native int
call void [mscorlib]System.Console::WriteLine(string, object)
ldstr "Int32OrNative(0x7fffffff, true) = {0}"
ldc.i4 0x7fffffff
ldc.i4 1
call native int Program::Int32OrNative(int32, bool)
box native int
call void [mscorlib]System.Console::WriteLine(string, object)
ldstr "Int32OrNative(-1, false) = {0}"
ldc.i4.m1
ldc.i4 0
call native int Program::Int32OrNative(int32, bool)
box native int
call void [mscorlib]System.Console::WriteLine(string, object)
ldstr "Int32OrNative(-1, true) = {0}"
ldc.i4.m1
ldc.i4 1
call native int Program::Int32OrNative(int32, bool)
box native int
call void [mscorlib]System.Console::WriteLine(string, object)
ldstr "Int32OrNativeLoopStyle(0x7fffffff):"
call void [mscorlib]System.Console::WriteLine(string)
ldc.i4 0x7fffffff
call void Program::Int32OrNativeLoopStyle(int32)
ldstr "Int32OrNativeLoopStyle(-1):"
call void [mscorlib]System.Console::WriteLine(string)
ldc.i4.m1
call void Program::Int32OrNativeLoopStyle(int32)
ldstr "Int32OrNativeDeadCode(0x7fffffff) = {0}"
ldc.i4 0x7fffffff
call native int Program::Int32OrNativeDeadCode(int32)
box native int
call void [mscorlib]System.Console::WriteLine(string, object)
ldstr "Int32OrNativeDeadCode(-1) = {0}"
ldc.i4.m1
call native int Program::Int32OrNativeDeadCode(int32)
box native int
call void [mscorlib]System.Console::WriteLine(string, object)
ldc.i4 0x7fffffff
call void Program::RunInt32OrNativeMultiUse(int32)
ldc.i4.m1
call void Program::RunInt32OrNativeMultiUse(int32)
ret
}
.method public static native int Int32OrNative(int32 val, bool use_native)
{
ldarg.1
brtrue use_native_int
use_i4:
ldarg.0
br after_if
after_if:
ldc.i4.1
add
ret
use_native_int:
ldarg.0
conv.u
br after_if
}
.method public static void Int32OrNativeLoopStyle(int32 val)
{
.locals init (
int32 i
)
ldarg.0
loop:
ldc.i4.1
add
call void Program::Print(native int)
ldloc.0
brtrue end
ldc.i4.1
stloc.0
ldarg.0
conv.u
br loop
end:
ret
}
.method public static native int Int32OrNativeDeadCode(int32 val)
{
use_i4:
ldarg.0
br after_if
after_if:
ldc.i4.1
add
ret
use_native_int: // dead code
ldarg.0
conv.u
br after_if
}
.method public static void RunInt32OrNativeMultiUse(int32 val)
{
ldstr "RunInt32OrNativeMultiUse({0}, push_i: false, use2: false) = {1}"
ldarg val
box int32
ldarg val
ldc.i4 0 // push_i
ldc.i4 0 // use2
call native int Program::Int32OrNativeMultiUse(int32 val, bool push_i, bool use2)
box native int
call void [mscorlib]System.Console::WriteLine(string, object, object)
ldstr "RunInt32OrNativeMultiUse({0}, push_i: false, use2: true) = {1}"
ldarg val
box int32
ldarg val
ldc.i4 0 // push_i
ldc.i4 1 // use2
call native int Program::Int32OrNativeMultiUse(int32 val, bool push_i, bool use2)
box native int
call void [mscorlib]System.Console::WriteLine(string, object, object)
ldstr "RunInt32OrNativeMultiUse({0}, push_i: true, use2: false) = {1}"
ldarg val
box int32
ldarg val
ldc.i4 1 // push_i
ldc.i4 0 // use2
call native int Program::Int32OrNativeMultiUse(int32 val, bool push_i, bool use2)
box native int
call void [mscorlib]System.Console::WriteLine(string, object, object)
ldstr "RunInt32OrNativeMultiUse({0}, push_i: true, use2: true) = {1}"
ldarg val
box int32
ldarg val
ldc.i4 1 // push_i
ldc.i4 1 // use2
call native int Program::Int32OrNativeMultiUse(int32 val, bool push_i, bool use2)
box native int
call void [mscorlib]System.Console::WriteLine(string, object, object)
ret
}
.method public static native int Int32OrNativeMultiUse(int32 val, bool push_i, bool use2)
{
ldarg.1
brtrue push_i
br push_i4
push_i4:
ldarg.0
ldarg.2
brtrue use2
br use1
push_i:
ldarg.0
conv.u
br use1
use1:
ldc.i4.1
add
ret
use2:
ldc.i4.2
add
ret
}
*/
.method public hidebysig static void Main (string[] args) cil managed
{
.maxstack 8
.entrypoint
call void Program::InlineAssignByte()
//call void Program::Int32OrNativeTests()
ret
} // end of method Main
.method public static void InlineAssignByte()
{
.locals init (
int8 local
)
ldstr "InlineAssignByte: WriteLine(local = {0})"
ldc.i4 300
dup
br pointless // this pointless branch is a workaround for https://github.com/dotnet/coreclr/issues/14784
// it doesn't have any effect on ILSpy as TransformAssignment runs after control-flow reconstruction
pointless:
// This assignment cannot be turned into a C# inline assignment, because doing so would truncate to 8 bits.
stloc.0
box int32
call void [mscorlib]System.Console::WriteLine(string, object)
ldstr "InlineAssignByte: local is {0}"
ldloc.0
box int32
call void [mscorlib]System.Console::WriteLine(string, object)
ret
}
/*
.method public static void Int32OrNativeTests()
{
ldstr "Int32OrNative(0x7fffffff, false) = {0}"
ldc.i4 0x7fffffff
ldc.i4 0
call native int Program::Int32OrNative(int32, bool)
box native int
call void [mscorlib]System.Console::WriteLine(string, object)
ldstr "Int32OrNative(0x7fffffff, true) = {0}"
ldc.i4 0x7fffffff
ldc.i4 1
call native int Program::Int32OrNative(int32, bool)
box native int
call void [mscorlib]System.Console::WriteLine(string, object)
ldstr "Int32OrNative(-1, false) = {0}"
ldc.i4.m1
ldc.i4 0
call native int Program::Int32OrNative(int32, bool)
box native int
call void [mscorlib]System.Console::WriteLine(string, object)
ldstr "Int32OrNative(-1, true) = {0}"
ldc.i4.m1
ldc.i4 1
call native int Program::Int32OrNative(int32, bool)
box native int
call void [mscorlib]System.Console::WriteLine(string, object)
ldstr "Int32OrNativeLoopStyle(0x7fffffff):"
call void [mscorlib]System.Console::WriteLine(string)
ldc.i4 0x7fffffff
call void Program::Int32OrNativeLoopStyle(int32)
ldstr "Int32OrNativeLoopStyle(-1):"
call void [mscorlib]System.Console::WriteLine(string)
ldc.i4.m1
call void Program::Int32OrNativeLoopStyle(int32)
ldstr "Int32OrNativeDeadCode(0x7fffffff) = {0}"
ldc.i4 0x7fffffff
call native int Program::Int32OrNativeDeadCode(int32)
box native int
call void [mscorlib]System.Console::WriteLine(string, object)
ldstr "Int32OrNativeDeadCode(-1) = {0}"
ldc.i4.m1
call native int Program::Int32OrNativeDeadCode(int32)
box native int
call void [mscorlib]System.Console::WriteLine(string, object)
ldc.i4 0x7fffffff
call void Program::RunInt32OrNativeMultiUse(int32)
ldc.i4.m1
call void Program::RunInt32OrNativeMultiUse(int32)
ret
}
.method public static native int Int32OrNative(int32 val, bool use_native)
{
ldarg.1
brtrue use_native_int
use_i4:
ldarg.0
br after_if
after_if:
ldc.i4.1
add
ret
use_native_int:
ldarg.0
conv.u
br after_if
}
.method public static void Int32OrNativeLoopStyle(int32 val)
{
.locals init (
int32 i
)
ldarg.0
loop:
ldc.i4.1
add
call void Program::Print(native int)
ldloc.0
brtrue end
ldc.i4.1
stloc.0
ldarg.0
conv.u
br loop
end:
ret
}
.method public static native int Int32OrNativeDeadCode(int32 val)
{
use_i4:
ldarg.0
br after_if
after_if:
ldc.i4.1
add
ret
use_native_int: // dead code
ldarg.0
conv.u
br after_if
}
.method public static void RunInt32OrNativeMultiUse(int32 val)
{
ldstr "RunInt32OrNativeMultiUse({0}, push_i: false, use2: false) = {1}"
ldarg val
box int32
ldarg val
ldc.i4 0 // push_i
ldc.i4 0 // use2
call native int Program::Int32OrNativeMultiUse(int32 val, bool push_i, bool use2)
box native int
call void [mscorlib]System.Console::WriteLine(string, object, object)
ldstr "RunInt32OrNativeMultiUse({0}, push_i: false, use2: true) = {1}"
ldarg val
box int32
ldarg val
ldc.i4 0 // push_i
ldc.i4 1 // use2
call native int Program::Int32OrNativeMultiUse(int32 val, bool push_i, bool use2)
box native int
call void [mscorlib]System.Console::WriteLine(string, object, object)
ldstr "RunInt32OrNativeMultiUse({0}, push_i: true, use2: false) = {1}"
ldarg val
box int32
ldarg val
ldc.i4 1 // push_i
ldc.i4 0 // use2
call native int Program::Int32OrNativeMultiUse(int32 val, bool push_i, bool use2)
box native int
call void [mscorlib]System.Console::WriteLine(string, object, object)
ldstr "RunInt32OrNativeMultiUse({0}, push_i: true, use2: true) = {1}"
ldarg val
box int32
ldarg val
ldc.i4 1 // push_i
ldc.i4 1 // use2
call native int Program::Int32OrNativeMultiUse(int32 val, bool push_i, bool use2)
box native int
call void [mscorlib]System.Console::WriteLine(string, object, object)
ret
}
.method public static native int Int32OrNativeMultiUse(int32 val, bool push_i, bool use2)
{
ldarg.1
brtrue push_i
br push_i4
push_i4:
ldarg.0
ldarg.2
brtrue use2
br use1
push_i:
ldarg.0
conv.u
br use1
use1:
ldc.i4.1
add
ret
use2:
ldc.i4.2
add
ret
}
*/
.method public static void Print(native int val)
{
ldarg.0
box native int
call void [mscorlib]System.Console::WriteLine(object)
ret
}
.method public static void Print(native int val)
{
ldarg.0
box native int
call void [mscorlib]System.Console::WriteLine(object)
ret
}
}

4
ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs

@ -140,6 +140,10 @@ namespace ICSharpCode.Decompiler.IL @@ -140,6 +140,10 @@ namespace ICSharpCode.Decompiler.IL
/// </remarks>
public int IndexOf(T item)
{
if (item == null) {
// InstructionCollection can't contain nulls
return -1;
}
// If this collection is the item's primary position, we can use ChildIndex:
int index = item.ChildIndex - firstChildIndex;
if (index >= 0 && index < list.Count && list[index] == item)

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

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
@ -70,45 +71,43 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -70,45 +71,43 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// stloc s(stobj (..., value))
/// </code>
/// e.g. used for inline assignment to static field
bool TransformInlineAssignmentStObj(Block block, int i)
bool TransformInlineAssignmentStObj(Block block, int pos)
{
var inst = block.Instructions[i] as StLoc;
var inst = block.Instructions[pos] as StLoc;
// in some cases it can be a compiler-generated local
if (inst == null || (inst.Variable.Kind != VariableKind.StackSlot && inst.Variable.Kind != VariableKind.Local))
return false;
var nextInst = block.Instructions.ElementAtOrDefault(i + 1);
ILInstruction replacement;
StObj fieldStore;
if (IsImplicitTruncation(inst.Value, inst.Variable.Type)) {
// 'stloc s' is implicitly truncating the value
return false;
}
ILVariable local;
if (nextInst is StLoc localStore) { // with extra local
int nextPos;
if (block.Instructions[pos + 1] is StLoc localStore) { // with extra local
if (localStore.Variable.Kind != VariableKind.Local || !localStore.Value.MatchLdLoc(inst.Variable))
return false;
if (!(inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 2))
return false;
var memberStore = block.Instructions.ElementAtOrDefault(i + 2);
if (memberStore is StObj) {
fieldStore = memberStore as StObj;
if (!fieldStore.Value.MatchLdLoc(inst.Variable) || localStore.Variable.IsUsedWithin(fieldStore.Target))
return false;
replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type);
} else {
return false;
}
context.Step("Inline assignment stobj (with extra local)", fieldStore);
local = localStore.Variable;
block.Instructions.RemoveAt(i + 1);
} else if (nextInst is StObj) { // without extra local
fieldStore = (StObj)nextInst;
if (!fieldStore.Value.MatchLdLoc(inst.Variable) || inst.Variable.IsUsedWithin(fieldStore.Target))
return false;
context.Step("Inline assignment stobj", fieldStore);
local = inst.Variable;
replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type);
nextPos = pos + 2;
} else {
local = inst.Variable;
localStore = null;
nextPos = pos + 1;
}
if (!(block.Instructions[nextPos] is StObj stobj))
return false;
if (!stobj.Value.MatchLdLoc(inst.Variable) || inst.Variable.IsUsedWithin(stobj.Target))
return false;
if (IsImplicitTruncation(inst.Value, stobj.Type)) {
// 'stloc s' is implicitly truncating the value
return false;
}
block.Instructions.RemoveAt(i + 1);
inst.ReplaceWith(new StLoc(local, replacement));
context.Step("Inline assignment stobj", stobj);
block.Instructions.Remove(localStore);
block.Instructions.Remove(stobj);
stobj.Value = inst.Value;
inst.ReplaceWith(new StLoc(local, stobj));
return true;
}
@ -253,8 +252,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -253,8 +252,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (inst.Variable.Kind != VariableKind.StackSlot)
return false;
Debug.Assert(!inst.Variable.Type.IsSmallIntegerType());
if (nextInst.Variable.Kind != VariableKind.Local || !nextInst.Value.MatchLdLoc(inst.Variable))
return false;
if (IsImplicitTruncation(inst.Value, nextInst.Variable.Type)) {
// 'stloc l' is implicitly truncating the stack value
return false;
}
context.Step("Inline assignment to local variable", inst);
var value = inst.Value;
var var = nextInst.Variable;
@ -263,7 +267,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -263,7 +267,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
nextInst.ReplaceWith(new StLoc(stackVar, new StLoc(var, value)));
return true;
}
bool IsImplicitTruncation(ILInstruction value, IType type)
{
return type.IsSmallIntegerType();
}
/// <code>
/// stloc s(ldloc l)
/// stloc l(binary.op(ldloc s, ldc.i4 1))

3
ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs

@ -106,7 +106,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -106,7 +106,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
public static bool IsSmallIntegerType(this IType type)
{
return GetSize(type) < 4;
int size = GetSize(type);
return size > 0 && size < 4;
}
/// <summary>

Loading…
Cancel
Save