Browse Source

Avoid using inline assignments if they truncate the input value.

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

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

@ -20,11 +20,34 @@ extends [mscorlib]System.Object @@ -20,11 +20,34 @@ extends [mscorlib]System.Object
.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()
{

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)

59
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);
local = localStore.Variable;
nextPos = pos + 2;
} else {
return false;
local = inst.Variable;
localStore = null;
nextPos = pos + 1;
}
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))
if (!(block.Instructions[nextPos] is StObj stobj))
return false;
context.Step("Inline assignment stobj", fieldStore);
local = inst.Variable;
replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type);
} else {
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;
@ -264,6 +268,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -264,6 +268,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
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