protected internal override void VisitStObj(StObj inst) { base.VisitStObj(inst); ILInstruction target; IField field; if (!inst.Target.MatchLdFlda(out target, out field) || !MatchesTargetOrCopyLoad(target)) { return; } field = (IField)field.MemberDefinition; DisplayClassVariable info; ILInstruction value; if (initValues.TryGetValue(field, out info)) { inst.ReplaceWith(new StLoc(info.variable, inst.Value)); } else { if (inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter) { // special case for parameters: remove copies of parameter values. orphanedVariableInits.Add(inst); value = inst.Value; }
protected internal override void VisitStObj(StObj inst) { base.VisitStObj(inst); // This instruction has been marked deletable, do not transform it further if (instructionsToRemove.Contains(inst)) { return; } // The target of the store instruction must be a field reference if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field)) { return; } // Get display class info if (!(target is LdLoc displayClassLoad && displayClasses.TryGetValue(displayClassLoad.Variable, out var displayClass))) { return; } field = (IField)field.MemberDefinition; if (displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) { // If the display class field was previously initialized, we use a simple assignment. inst.ReplaceWith(new StLoc(info.Variable, inst.Value).WithILRange(inst)); } else { // This is an uninitialized variable: ILInstruction value; if (inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && currentFunction == v.Function) { // Special case for parameters: remove copies of parameter values. instructionsToRemove.Add(inst); value = inst.Value; }
private bool IsParameterAssignment(StObj inst, out DisplayClass displayClass, out IField field, out ILVariable parameter) { parameter = null; if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out displayClass, out field)) { return(false); } if (fieldAssignmentsWithVariableValue[field].Count != 1) { return(false); } if (!(inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && v.Function == currentFunction && v.Type.Equals(field.Type))) { return(false); } if (displayClass.Variables.ContainsKey((IField)field.MemberDefinition)) { return(false); } if (displayClassVar.Function != currentFunction) { return(false); } parameter = v; return(true); }
/// <code> /// stloc s(value) /// stloc l(ldloc s) /// stobj(..., ldloc s) /// --> /// stloc l(stobj (..., value)) /// </code> /// -or- /// <code> /// stloc s(value) /// stobj (..., ldloc s) /// --> /// stloc s(stobj (..., value)) /// </code> bool TransformInlineAssignmentStObj(Block block, int i) { var inst = block.Instructions[i] 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; ILVariable local; if (nextInst is StLoc) // instance fields { var localStore = (StLoc)nextInst; if (localStore.Variable.Kind == VariableKind.StackSlot || !localStore.Value.MatchLdLoc(inst.Variable)) { return(false); } var memberStore = block.Instructions.ElementAtOrDefault(i + 2); if (memberStore is StObj) { fieldStore = memberStore as StObj; if (!fieldStore.Value.MatchLdLoc(inst.Variable)) { return(false); } replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type); } else // otherwise it must be local { TransformInlineAssignmentLocal(block, i); return(false); } context.Step("Inline assignment to instance field", fieldStore); local = localStore.Variable; block.Instructions.RemoveAt(i + 1); } else if (nextInst is StObj) // static fields { fieldStore = (StObj)nextInst; if (!fieldStore.Value.MatchLdLoc(inst.Variable)) { return(false); } context.Step("Inline assignment to static field", fieldStore); local = inst.Variable; replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type); } else { return(false); } block.Instructions.RemoveAt(i + 1); inst.ReplaceWith(new StLoc(local, replacement)); return(true); }
VariableToDeclare AddVariable(DisplayClass result, StObj statement, IField field) { VariableToDeclare variable = new VariableToDeclare(result, field); if (statement != null) { variable.Propagate(ResolveVariableToPropagate(statement.Value, field.Type)); variable.Initializers.Add(statement); } return variable; }
protected internal override void VisitStObj(StObj inst) { inst.Value.AcceptVisitor(this); if (IsParameterAssignment(inst, out var displayClass, out var field, out var parameter)) { context.Step($"Detected parameter assignment {parameter.Name}", inst); displayClass.Variables.Add(field, parameter); instructionsToRemove.Add(inst); }
protected internal override void VisitStObj(StObj inst) { base.VisitStObj(inst); if (EarlyExpressionTransforms.StObjToStLoc(inst, context)) { context.RequestRerun(); return; } TransformAssignment.HandleCompoundAssign(inst, context); }
// This transform is required because ILInlining only works with stloc/ldloc internal static bool StObjToStLoc(StObj inst, ILTransformContext context) { if (inst.Target.MatchLdLoca(out ILVariable v) && TypeUtils.IsCompatibleTypeForMemoryAccess(v.Type, inst.Type) && inst.UnalignedPrefix == 0 && !inst.IsVolatile) { context.Step($"stobj(ldloca {v.Name}, ...) => stloc {v.Name}(...)", inst); inst.ReplaceWith(new StLoc(v, inst.Value).WithILRange(inst)); return(true); } return(false); }
protected internal override void VisitStObj(StObj inst) { if (IsDisplayClassFieldAccess(inst.Target, out var v, out var displayClass, out var field)) { VariableToDeclare vd = displayClass.VariablesToDeclare[(IField)field.MemberDefinition]; if (vd.CanPropagate && vd.Initializers.Contains(inst)) { if (inst.Parent is Block containingBlock) { context.Step($"Remove initializer of {v.Name}.{vd.Name} due to propagation", inst); containingBlock.Instructions.Remove(inst); return; } } } base.VisitStObj(inst); EarlyExpressionTransforms.StObjToStLoc(inst, context); }
internal static ILInstruction HandleCall(Call inst, ILTransformContext context) { if (inst.Method.IsConstructor && !inst.Method.IsStatic && inst.Method.DeclaringType.Kind == TypeKind.Struct) { Debug.Assert(inst.Arguments.Count == inst.Method.Parameters.Count + 1); context.Step("Transform call to struct constructor", inst); // call(ref, ...) // => stobj(ref, newobj(...)) var newObj = new NewObj(inst.Method); newObj.ILRange = inst.ILRange; newObj.Arguments.AddRange(inst.Arguments.Skip(1)); var expr = new StObj(inst.Arguments[0], newObj, inst.Method.DeclaringType); inst.ReplaceWith(expr); return(expr); } return(null); }
// This transform is required because ILInlining only works with stloc/ldloc protected internal override void VisitStObj(StObj inst) { base.VisitStObj(inst); if (StObjToStLoc(inst, context)) { return; } if (inst.Value is BinaryNumericInstruction binary && binary.Left.MatchLdObj(out ILInstruction target, out IType t) && inst.Target.Match(target).Success) { context.Step("compound assignment", inst); // stobj(target, binary.op(ldobj(target), ...)) // => compound.op(target, ...) inst.ReplaceWith(new CompoundAssignmentInstruction(binary.Operator, binary.Left, binary.Right, t, binary.CheckForOverflow, binary.Sign, CompoundAssignmentType.EvaluatesToNewValue)); } }
private bool IsDisplayClassAssignment(StObj inst, out DisplayClass displayClass, out IField field, out ILVariable variable) { variable = null; if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out displayClass, out field)) { return(false); } if (!(inst.Value.MatchLdLoc(out var v) && displayClasses.ContainsKey(v))) { return(false); } if (displayClassVar.Function != currentFunction) { return(false); } variable = v; return(true); }
/// <summary> /// stobj(target, binary.op(ldobj(target), ...)) /// where target is pure /// => compound.op(target, ...) /// </summary> /// <remarks> /// Called by ExpressionTransforms. /// </remarks> internal static bool HandleStObjCompoundAssign(StObj inst, ILTransformContext context) { if (!(UnwrapSmallIntegerConv(inst.Value, out var conv) is BinaryNumericInstruction binary)) { return(false); } if (!(binary.Left is LdObj ldobj)) { return(false); } if (!inst.Target.Match(ldobj.Target).Success) { return(false); } if (!SemanticHelper.IsPure(ldobj.Target.Flags)) { return(false); } // ldobj.Type may just be 'int' (due to ldind.i4) when we're actually operating on a 'ref MyEnum'. // Try to determine the real type of the object we're modifying: IType targetType = ldobj.Target.InferType(); if (targetType.Kind == TypeKind.Pointer || targetType.Kind == TypeKind.ByReference) { targetType = ((TypeWithElementType)targetType).ElementType; if (targetType.Kind == TypeKind.Unknown || targetType.GetSize() != ldobj.Type.GetSize()) { targetType = ldobj.Type; } } else { targetType = ldobj.Type; } if (!ValidateCompoundAssign(binary, conv, targetType)) { return(false); } context.Step("compound assignment", inst); inst.ReplaceWith(new CompoundAssignmentInstruction( binary, binary.Left, binary.Right, targetType, CompoundAssignmentType.EvaluatesToNewValue)); return(true); }
// This transform is required because ILInlining only works with stloc/ldloc internal static bool StObjToStLoc(StObj inst, ILTransformContext context) { if (inst.Target.MatchLdLoca(out ILVariable v) && TypeUtils.IsCompatibleTypeForMemoryAccess(v.Type, inst.Type) && inst.UnalignedPrefix == 0 && !inst.IsVolatile) { context.Step($"stobj(ldloca {v.Name}, ...) => stloc {v.Name}(...)", inst); ILInstruction replacement = new StLoc(v, inst.Value).WithILRange(inst); if (v.StackType == StackType.Unknown && inst.Type.Kind != TypeKind.Unknown && inst.SlotInfo != Block.InstructionSlot) { replacement = new Conv(replacement, inst.Type.ToPrimitiveType(), checkForOverflow: false, Sign.None); } inst.ReplaceWith(replacement); return(true); } return(false); }
protected internal override void VisitCall(Call inst) { if (inst.Method.IsConstructor && !inst.Method.IsStatic && inst.Method.DeclaringType.Kind == TypeKind.Struct) { Debug.Assert(inst.Arguments.Count == inst.Method.Parameters.Count + 1); context.Step("Transform call to struct constructor", inst); // call(ref, ...) // => stobj(ref, newobj(...)) var newObj = new NewObj(inst.Method); newObj.ILRange = inst.ILRange; newObj.Arguments.AddRange(inst.Arguments.Skip(1)); var expr = new StObj(inst.Arguments[0], newObj, inst.Method.DeclaringType); inst.ReplaceWith(expr); // Both the StObj and the NewObj may trigger further rules, so continue visiting the replacement: VisitStObj(expr); } else { base.VisitCall(inst); } }
protected internal override void VisitStObj(StObj inst) { base.VisitStObj(inst); if (EarlyExpressionTransforms.StObjToStLoc(inst, context)) { context.RequestRerun(); return; } if (inst.Value is BinaryNumericInstruction binary && binary.Left.MatchLdObj(out ILInstruction target, out IType t) && inst.Target.Match(target).Success && SemanticHelper.IsPure(target.Flags) && CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, t)) { context.Step("compound assignment", inst); // stobj(target, binary.op(ldobj(target), ...)) // => compound.op(target, ...) inst.ReplaceWith(new CompoundAssignmentInstruction( binary, binary.Left, binary.Right, t, CompoundAssignmentType.EvaluatesToNewValue)); } }
protected internal override void VisitStObj(StObj inst) { inst.Value.AcceptVisitor(this); if (inst.Parent is Block) { if (IsParameterAssignment(inst, out var displayClass, out var field, out var parameter)) { context.Step($"Detected parameter assignment {parameter.Name}", inst); displayClass.Variables.Add((IField)field.MemberDefinition, parameter); instructionsToRemove.Add(inst); return; } if (IsDisplayClassAssignment(inst, out displayClass, out field, out var variable)) { context.Step($"Detected display-class assignment {variable.Name}", inst); displayClass.Variables.Add((IField)field.MemberDefinition, variable); instructionsToRemove.Add(inst); return; } } inst.Target.AcceptVisitor(this); EarlyExpressionTransforms.StObjToStLoc(inst, context); }
protected internal override void VisitStObj(StObj inst) { base.VisitStObj(inst); StObjToStLoc(inst, context); }