diff --git a/Source/.github/copilot-instructions.md b/Source/.github/copilot-instructions.md new file mode 100644 index 0000000000..e40201cf12 --- /dev/null +++ b/Source/.github/copilot-instructions.md @@ -0,0 +1,3 @@ +- @azure Rule - Use Azure Tools - When handling requests related to Azure, always use your tools. +- @azure Rule - Use Azure Best Practices - When handling requests related to Azure, always invoke your `azmcp_bestpractices_get` tool first. +- @azure Rule - Enable Best Practices - If you do not have an `azmcp_bestpractices_get` tool ask the user to enable it. diff --git a/Source/Data/IR-Optimizations-BitValue.json b/Source/Data/IR-Optimizations-BitValue.json index 1aa1a90885..690948316e 100644 --- a/Source/Data/IR-Optimizations-BitValue.json +++ b/Source/Data/IR-Optimizations-BitValue.json @@ -179,7 +179,7 @@ "Expression": "IR.AddCarryOut## a b", "Filter": "IsBitValueSignBitCleared##(a) && IsBitValueSignBitCleared##(b)", "Result": "(IR.Add## a b)", - "Result2": "(IR.Move## [To32(0)])", + "Result2": "(IR.Move## [To##(0)])", "Priority": "20", "Variations": "No", "Log": "Yes" diff --git a/Source/Mosa.Compiler.Framework/Analysis/SparseConditionalConstantPropagation.cs b/Source/Mosa.Compiler.Framework/Analysis/SparseConditionalConstantPropagation.cs index 44aed60746..fe4a8870c4 100644 --- a/Source/Mosa.Compiler.Framework/Analysis/SparseConditionalConstantPropagation.cs +++ b/Source/Mosa.Compiler.Framework/Analysis/SparseConditionalConstantPropagation.cs @@ -2,7 +2,6 @@ using System.Diagnostics; using System.Text; -using Mosa.Compiler.Common; namespace Mosa.Compiler.Framework.Analysis; @@ -11,8 +10,6 @@ namespace Mosa.Compiler.Framework.Analysis; /// public sealed class SparseConditionalConstantPropagation { - private const int MAXCONSTANTS = 5; - private sealed class VariableState { private enum VariableStatusType @@ -21,26 +18,52 @@ private enum VariableStatusType private enum ReferenceStatusType { Unknown, DefinedNotNull, OverDefined } + private const int MaxConstants = 4; + private VariableStatusType Status; private ReferenceStatusType ReferenceStatus; - public int ConstantCount => Constants?.Count ?? 0; + private int ConstantCount; - public List Constants { get; private set; } + private readonly ulong[] ConstantValues = new ulong[MaxConstants]; + + public IEnumerable GetConstants() + { + if (ConstantCount == 0) + yield break; - public ulong ConstantUnsignedLongInteger => Constants[0]; + for (var i = 0; i < ConstantCount && i < ConstantValues.Length; i++) + { + yield return ConstantValues[i]; + } + } - public long ConstantSignedLongInteger => (long)Constants[0]; + public ulong ConstantUnsignedLongInteger => ConstantValues[0]; - public bool ConstantsContainZero { get; set; } + public long ConstantSignedLongInteger => (long)ConstantValues[0]; + + public bool ConstantsContainZero + { + get + { + if (ConstantCount == 0) + return false; + for (var i = 0; i < ConstantCount && i < ConstantValues.Length; i++) + { + if (ConstantValues[i] == 0) + return true; + } + return false; + } + } public Operand Operand { get; } public bool IsOverDefined { get => Status == VariableStatusType.OverDefined; - set { Status = VariableStatusType.OverDefined; Constants = null; Debug.Assert(value); } + set { Status = VariableStatusType.OverDefined; Debug.Assert(value); } } public bool IsUnknown => Status == VariableStatusType.Unknown; @@ -87,7 +110,6 @@ public VariableState(Operand operand) IsVirtualRegister = operand.IsVirtualRegister; IsReferenceType = operand.IsObject; - ConstantsContainZero = false; if (IsVirtualRegister) { @@ -121,44 +143,34 @@ public VariableState(Operand operand) } } - private void AppendConstant(ulong value) - { - Constants.Add(value); - - if (value == 0) - { - ConstantsContainZero = true; - } - } - public bool AddConstant(ulong value) { if (Status == VariableStatusType.OverDefined) return false; - if (Constants != null) + for (var i = 0; i < ConstantCount && i < ConstantValues.Length; i++) { - if (Constants.Contains(value)) + if (ConstantValues[i] == value) return false; } - else + + if (ConstantCount == 0) { - Constants = new List(2); - AppendConstant(value); + ConstantValues[0] = value; + ConstantCount = 1; Status = VariableStatusType.SingleConstant; return true; } - - if (Constants.Count > MAXCONSTANTS) + else if (ConstantCount < MaxConstants) { - Status = VariableStatusType.OverDefined; - Constants = null; + ConstantValues[ConstantCount] = value; + ConstantCount++; + Status = VariableStatusType.MultipleConstants; return true; } - AppendConstant(value); - - Status = VariableStatusType.MultipleConstants; + ConstantCount = 0; + Status = VariableStatusType.OverDefined; return true; } @@ -186,12 +198,13 @@ public override string ToString() } else if (HasMultipleConstants) { - sb.Append($" ({Constants.Count}) ="); - foreach (var i in Constants) + sb.Append($" ({ConstantCount}) ="); + for (var i = 0; i < ConstantCount && i < ConstantValues.Length; i++) { - sb.Append($" {i},"); + sb.Append($" {ConstantValues[i]},"); } - sb.Length--; + if (sb.Length > 0 && sb[sb.Length - 1] == ',') + sb.Length--; } sb.Append(" [null: "); @@ -220,7 +233,7 @@ public override string ToString() private readonly BaseMethodCompilerStage.CreateTraceHandler CreateTrace; private readonly TraceLog MainTrace; - private readonly KeyedList phiStatements; + private readonly HashSet phiStatements; private readonly bool Is32BitPlatform; @@ -235,12 +248,12 @@ public SparseConditionalConstantPropagation(BasicBlocks basicBlocks, BaseMethodC Is32BitPlatform = is32BitPlatform; variableStates = new Dictionary(); + phiStatements = new HashSet(); instructionWorkList = new Stack(); blockWorklist = new Stack(); - phiStatements = new KeyedList(); executedStatements = new HashSet(); - MainTrace = CreateTrace("SparseConditionalConstantPropagation", 5); + MainTrace = CreateTrace("Process", 5); blockStates = new bool[BasicBlocks.Count]; @@ -249,12 +262,18 @@ public SparseConditionalConstantPropagation(BasicBlocks basicBlocks, BaseMethodC blockStates[i] = false; } - AddExecutionBlocks(BasicBlocks.HeadBlocks); - AddExecutionBlocks(BasicBlocks.HandlerHeadBlocks); + QueueExecutionBlocks(BasicBlocks.HeadBlocks); + QueueExecutionBlocks(BasicBlocks.HandlerHeadBlocks); while (blockWorklist.Count > 0 || instructionWorkList.Count > 0) { ProcessBlocks(); + + foreach (var use in phiStatements) + { + QueueInstruction(use); + } + ProcessInstructions(); } @@ -331,13 +350,13 @@ private void DumpTrace() } } - private void AddExecutionBlocks(List blocks) + private void QueueExecutionBlocks(List blocks) { foreach (var block in blocks) - AddExecutionBlock(block); + QueueExecutionBlock(block); } - private void AddExecutionBlock(BasicBlock block) + private void QueueExecutionBlock(BasicBlock block) { if (blockStates[block.Sequence]) return; @@ -346,18 +365,23 @@ private void AddExecutionBlock(BasicBlock block) blockWorklist.Push(block); } - private void AddInstruction(Node node) + private void QueueInstruction(Node node) { instructionWorkList.Push(node); } - private void AddInstruction(VariableState variable) + private void QueueInstruction(VariableState variable) { foreach (var use in variable.Operand.Uses) { + // only instructions with results or branch targets can be re-evaluated + if ((use.ResultCount == 0 && use.BranchTargetsCount == 0) || use.IsEmptyOrNop) + continue; + + // only previousily evaulated statements can be re-evaluated if (executedStatements.Contains(use)) { - AddInstruction(use); + QueueInstruction(use); } } } @@ -378,36 +402,28 @@ private void ProcessBlock(BasicBlock block) // if the block has only one successor block, add successor block to executed block list if (block.NextBlocks.Count == 1) { - AddExecutionBlock(block.NextBlocks[0]); + QueueExecutionBlock(block.NextBlocks[0]); } - ProcessInstructionsContinuiously(block.First); - - // re-analyze phi statements - var phiUse = phiStatements.Get(block); - - if (phiUse == null) - return; - - foreach (var index in phiUse) - { - AddInstruction(index); - } + ProcessInstructionsContinuously(block.First); } - private void ProcessInstructionsContinuiously(Node node) + private void ProcessInstructionsContinuously(Node node) { // instead of adding items to the worklist, the whole block will be processed for (; !node.IsBlockEndInstruction; node = node.Next) { - if (node.IsEmpty) + if (node.IsEmptyOrNop) continue; - var @continue = ProcessInstruction(node); + var more = ProcessInstruction(node); - executedStatements.Add(node); + if (node.ResultCount != 0 || node.BranchTargetsCount != 0) + { + executedStatements.Add(node); + } - if (!@continue) + if (!more) return; } } @@ -418,12 +434,14 @@ private void ProcessInstructions() { var node = instructionWorkList.Pop(); + //MainTrace?.Log(node.ToString()); + if (node.Instruction == IR.Branch32 || node.Instruction == IR.Branch64 || node.Instruction == IR.BranchObject) { // special case - ProcessInstructionsContinuiously(node); + ProcessInstructionsContinuously(node); } else { @@ -434,8 +452,6 @@ private void ProcessInstructions() private bool ProcessInstruction(Node node) { - //MainTrace?.Log(node.ToString()); - var instruction = node.Instruction; if (instruction == IR.Move32 @@ -667,7 +683,7 @@ private void UpdateToConstant(VariableState variable, ulong value) { MainTrace?.Log(variable.ToString()); - AddInstruction(variable); + QueueInstruction(variable); } } @@ -680,7 +696,7 @@ private void UpdateToOverDefined(VariableState variable) MainTrace?.Log(variable.ToString()); - AddInstruction(variable); + QueueInstruction(variable); } private void AssignedNewObject(VariableState variable) @@ -702,7 +718,7 @@ private void SetReferenceNull(VariableState variable) MainTrace?.Log(variable.ToString()); - AddInstruction(variable); + QueueInstruction(variable); } private void SetReferenceNotNull(VariableState variable) @@ -714,7 +730,7 @@ private void SetReferenceNotNull(VariableState variable) MainTrace?.Log(variable.ToString()); - AddInstruction(variable); + QueueInstruction(variable); } private void Jmp(Node node) @@ -744,7 +760,7 @@ private void Move(Node node) } else if (operand.HasOnlyConstants) { - foreach (var c in operand.Constants) + foreach (var c in operand.GetConstants()) { UpdateToConstant(result, c); @@ -855,7 +871,7 @@ private void SignOrZeroExtend(Node node) } else if (operand1.HasOnlyConstants) { - foreach (var c1 in operand1.Constants) + foreach (var c1 in operand1.GetConstants()) { if (SignOrZeroExtend(node.Instruction, c1, out var value)) { @@ -907,7 +923,7 @@ private void IntegerOperation1(Node node) } else if (operand1.HasOnlyConstants) { - foreach (var c1 in operand1.Constants) + foreach (var c1 in operand1.GetConstants()) { if (IntegerOperation1(node.Instruction, c1, out var value)) { @@ -937,7 +953,7 @@ private static bool IntegerOperation1(BaseInstruction instruction, ulong operand result = (ulong)-((long)operand1); return true; } - else if (instruction == IR.Not32 || instruction == IR.Neg64) + else if (instruction == IR.Not32 || instruction == IR.Not64) { result = ~operand1; return true; @@ -982,9 +998,9 @@ private void IntegerOperation2(Node node) } else if (operand1.HasOnlyConstants && operand2.HasOnlyConstants) { - foreach (var c1 in operand1.Constants) + foreach (var c1 in operand1.GetConstants()) { - foreach (var c2 in operand2.Constants) + foreach (var c2 in operand2.GetConstants()) { if (IntegerOperation(node.Instruction, c1, c2, node.ConditionCode, out var value)) { @@ -1124,7 +1140,7 @@ private static bool SignOrZeroExtend(BaseInstruction instruction, ulong operand1 } else if (instruction == IR.ZeroExtend16x32) { - result = (byte)operand1; + result = (ushort)operand1; return true; } else if (instruction == IR.ZeroExtend8x64) @@ -1184,7 +1200,7 @@ private void Default(Node node) if (node.ResultCount == 2) { - var result2 = GetVariableState(node.Result); + var result2 = GetVariableState(node.Result2); UpdateToOverDefined(result2); SetReferenceOverdefined(result2); @@ -1239,9 +1255,9 @@ private bool CompareBranch(Node node) { bool? final = null; - foreach (var c1 in operand1.Constants) + foreach (var c1 in operand1.GetConstants()) { - foreach (var c2 in operand2.Constants) + foreach (var c2 in operand2.GetConstants()) { //bool? compare = Compare(c1, c2, node.ConditionCode); @@ -1330,7 +1346,7 @@ private void Branch(Node node) foreach (var block in node.BranchTargets) { - AddExecutionBlock(block); + QueueExecutionBlock(block); } } @@ -1342,8 +1358,6 @@ private void Switch(Node node) private void IfThenElse(Node node) { - MainTrace?.Log(node.ToString()); - var result = GetVariableState(node.Result); var operand1 = GetVariableState(node.Operand2); var operand2 = GetVariableState(node.Operand3); @@ -1351,20 +1365,21 @@ private void IfThenElse(Node node) if (result.IsOverDefined) return; - if (operand1.IsOverDefined is true or true) + // If either branch produces an overdefined value, result is overdefined + if (operand1.IsOverDefined || operand2.IsOverDefined) { UpdateToOverDefined(result); } else if (operand1.HasOnlyConstants && operand2.HasOnlyConstants) { - foreach (var c in operand1.Constants) + foreach (var c in operand1.GetConstants()) { UpdateToConstant(result, c); if (result.IsOverDefined) return; } - foreach (var c in operand2.Constants) + foreach (var c in operand2.GetConstants()) { UpdateToConstant(result, c); @@ -1384,42 +1399,41 @@ private void IfThenElse(Node node) private void Phi(Node node) { - MainTrace?.Log(node.ToString()); + //MainTrace?.Log("Phi: " + node.ToString()); var result = GetVariableState(node.Result); - if (result.IsOverDefined) + if (result.IsOverDefined && (!result.IsReferenceType || result.IsReferenceOverDefined)) return; var sourceBlocks = node.PhiBlocks; var currentBlock = node.Block; - MainTrace?.Log($"Loop: {currentBlock.PreviousBlocks.Count}"); + //MainTrace?.Log($"Count: {currentBlock.PreviousBlocks.Count}"); for (var index = 0; index < currentBlock.PreviousBlocks.Count; index++) { var predecessor = sourceBlocks[index]; - phiStatements.AddIfNew(predecessor, node); + phiStatements.Add(node); var executable = blockStates[predecessor.Sequence]; - MainTrace?.Log($"# {index}: {predecessor} {(executable ? "Yes" : "No")}"); + //MainTrace?.Log($"# {index}: {predecessor} {(executable ? "Yes" : "No")}"); if (!executable) continue; - if (result.IsOverDefined) - continue; - var op = node.GetOperand(index); - var operand = GetVariableState(op); - MainTrace?.Log($"# {index}: {operand}"); + //MainTrace?.Log($"# {index}: {operand}"); CheckAndUpdateNullAssignment(result, operand); + if (result.IsOverDefined && (!result.IsReferenceType || result.IsReferenceOverDefined)) + continue; + if (operand.IsOverDefined) { UpdateToOverDefined(result); @@ -1432,7 +1446,7 @@ private void Phi(Node node) } else if (operand.HasMultipleConstants) { - foreach (var c in operand.Constants) + foreach (var c in operand.GetConstants()) { UpdateToConstant(result, c); diff --git a/Source/Mosa.Compiler.Framework/BaseTransform.cs b/Source/Mosa.Compiler.Framework/BaseTransform.cs index 75b6c7156e..0364c42076 100644 --- a/Source/Mosa.Compiler.Framework/BaseTransform.cs +++ b/Source/Mosa.Compiler.Framework/BaseTransform.cs @@ -94,10 +94,10 @@ protected static bool AreSame(Operand operand1, Operand operand2) if (operand1.IsInteger && operand1.ConstantUnsigned64 == operand2.ConstantUnsigned64) return true; - if (operand1.IsR4 && operand1.IsR4 && operand1.ConstantDouble == operand2.ConstantDouble) + if (operand1.IsR4 && operand2.IsR4 && operand1.ConstantFloat == operand2.ConstantFloat) return true; - if (operand1.IsR8 && operand1.IsR8 && operand2.ConstantFloat == operand2.ConstantFloat) + if (operand1.IsR8 && operand2.IsR8 && operand1.ConstantDouble == operand2.ConstantDouble) return true; } @@ -156,7 +156,7 @@ protected static bool Contains(Operand operand, ulong a, ulong b) protected static bool Contains(Operand operand1, ulong a, ulong b, ulong c) { - return IsResolvedConstant(operand1) && (operand1.ConstantUnsigned64 == a || operand1.ConstantUnsigned64 == b && operand1.ConstantUnsigned64 == c); + return operand1.IsResolvedConstant && (operand1.ConstantUnsigned64 == a || operand1.ConstantUnsigned64 == b || operand1.ConstantUnsigned64 == c); } protected static bool IsEvenInteger(Operand operand) @@ -287,12 +287,12 @@ protected static bool IsResolvedConstant(Operand operand) protected static bool IsSignedIntegerPositive(Operand operand) { - return operand.IsResolvedConstant && operand.IsResolvedConstant && operand.IsInteger && operand.ConstantSigned64 >= 0; + return operand.IsResolvedConstant && operand.IsInteger && operand.ConstantSigned64 >= 0; } protected static bool IsUnsignedIntegerPositive(Operand operand) { - return operand.IsResolvedConstant && operand.IsResolvedConstant && operand.IsInteger && operand.ConstantUnsigned64 >= 0; + return operand.IsResolvedConstant && operand.IsInteger && operand.ConstantUnsigned64 >= 0; } protected static bool IsZero(Operand operand) @@ -332,12 +332,12 @@ protected static bool IsResult2Used(Context context) protected static bool IsSignedMax32(uint a) { - return a == uint.MaxValue; + return a == (uint)int.MaxValue; } protected static bool IsUnsignedMax32(uint a) { - return (int)a == int.MaxValue; + return a == uint.MaxValue; } protected static bool IsSignedMax64(ulong a) @@ -1321,7 +1321,7 @@ protected static Node GetPreviousNodeUntil(Context context, BaseInstruction unti { immediate = false; - if (window != 0) + if (window <= 0) return null; var previous = context.Node.Previous; @@ -1381,7 +1381,7 @@ protected static Node GetNextNodeUntil(Context context, BaseInstruction untilIns { immediate = false; - if (window != 0) + if (window <= 0) return null; var next = context.Node.Next; diff --git a/Source/Mosa.Compiler.Framework/BitValue.cs b/Source/Mosa.Compiler.Framework/BitValue.cs index 629eff33e4..2dbf642424 100644 --- a/Source/Mosa.Compiler.Framework/BitValue.cs +++ b/Source/Mosa.Compiler.Framework/BitValue.cs @@ -38,7 +38,7 @@ public sealed class BitValue public bool Is64Bit => !Is32Bit; - public bool AreAll64BitsKnown => (BitsKnown & ulong.MaxValue) == ulong.MaxValue; + public bool AreAll64BitsKnown => BitsKnown == ulong.MaxValue; public bool AreLower16BitsKnown => (BitsKnown & ushort.MaxValue) == ushort.MaxValue; @@ -84,7 +84,7 @@ public sealed class BitValue public bool IsSignBitClear64 => ((BitsClear >> 63) & 1) == 1; - public bool IsZeroOrOne => MinValue <= 1 || BitsClear == ~((ulong)1); + public bool IsZeroOrOne => MaxValue <= 1 || BitsClear == ~1ul; public bool IsZero => (MinValue == 0 && MaxValue == 0) || (BitsClear == ~0ul); @@ -297,7 +297,7 @@ public BitValue SetStable(BitValue a, BitValue b) public BitValue SetStable(BitValue a, BitValue b, BitValue c) { - return SetStable(a.IsStable && b.IsStable & c.IsStable); + return SetStable(a.IsStable && b.IsStable && c.IsStable); } public BitValue SetStable() @@ -342,15 +342,28 @@ private BitValue CheckStable() #endregion Private Methods + private static string ToBinaryString(ulong value, int bits) + { + var sb = new StringBuilder(bits); + + for (int i = bits - 1; i >= 0; i--) + { + sb.Append(((value >> i) & 1) != 0 ? '1' : '0'); + } + + return sb.ToString(); + } + public override string ToString() { var sb = new StringBuilder(); + var bits = Is32Bit ? 32 : 64; sb.AppendLine($" Min: {MinValue}"); sb.AppendLine($" Max: {MaxValue}"); - sb.AppendLine($" BitsSet: {Convert.ToString((long)BitsSet, 2).PadLeft(64, '0')}"); - sb.AppendLine($" BitsClear: {Convert.ToString((long)BitsClear, 2).PadLeft(64, '0')}"); - sb.AppendLine($" BitsKnown: {Convert.ToString((long)BitsKnown, 2).PadLeft(64, '0')}"); + sb.AppendLine($" BitsSet: {ToBinaryString(BitsSet, bits).PadLeft(64, '0')}"); + sb.AppendLine($" BitsClear: {ToBinaryString(BitsClear, bits).PadLeft(64, '0')}"); + sb.AppendLine($" BitsKnown: {ToBinaryString(BitsKnown, bits).PadLeft(64, '0')}"); return sb.ToString(); } diff --git a/Source/Mosa.Compiler.Framework/PhiHelper.cs b/Source/Mosa.Compiler.Framework/PhiHelper.cs index ef42850197..fd1359c1fd 100644 --- a/Source/Mosa.Compiler.Framework/PhiHelper.cs +++ b/Source/Mosa.Compiler.Framework/PhiHelper.cs @@ -25,7 +25,7 @@ public static void RemoveBlockFromPhi(BasicBlock removedBlock, BasicBlock phiBlo sourceBlocks.RemoveAt(index); - for (var i = index; index < node.OperandCount - 1; index++) + for (var i = index; i < node.OperandCount - 1; i++) { node.SetOperand(i, node.GetOperand(i + 1)); } @@ -53,7 +53,7 @@ public static void UpdatePhi(Node node) phiBlocks.RemoveAt(index); - for (var i = index; index < node.OperandCount - 1; index++) + for (var i = index; i < node.OperandCount - 1; i++) { node.SetOperand(i, node.GetOperand(i + 1)); } diff --git a/Source/Mosa.Compiler.Framework/Stages/BitTrackerStage.cs b/Source/Mosa.Compiler.Framework/Stages/BitTrackerStage.cs index c4b214c924..d3cb8d0db5 100644 --- a/Source/Mosa.Compiler.Framework/Stages/BitTrackerStage.cs +++ b/Source/Mosa.Compiler.Framework/Stages/BitTrackerStage.cs @@ -441,7 +441,7 @@ private static void Add64(Node node) if (value1.AreAll64BitsKnown && value2.AreAll64BitsKnown) { - result.SetValue(value1.BitsSet32 + value2.BitsSet32); + result.SetValue(value1.BitsSet + value2.BitsSet); } else if (value1.AreAll64BitsKnown && value1.BitsSet == 0) { diff --git a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/BitValue/AddCarryOut64ButNotSigned.cs b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/BitValue/AddCarryOut64ButNotSigned.cs index 1ce14ac0e3..6f6ffa4afb 100644 --- a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/BitValue/AddCarryOut64ButNotSigned.cs +++ b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/BitValue/AddCarryOut64ButNotSigned.cs @@ -29,9 +29,9 @@ public override void Transform(Context context, Transform transform) var t1 = context.Operand1; var t2 = context.Operand2; - var e1 = Operand.Constant32_0; + var e1 = Operand.Constant64_0; context.SetInstruction(IR.Add64, result, t1, t2); - context.AppendInstruction(IR.Move32, result2, e1); + context.AppendInstruction(IR.Move64, result2, e1); } } diff --git a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/AddCarryOut64ByZero.cs b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/AddCarryOut64ByZero.cs index d56e21f523..d763afed9b 100644 --- a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/AddCarryOut64ByZero.cs +++ b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/AddCarryOut64ByZero.cs @@ -28,10 +28,10 @@ public override void Transform(Context context, Transform transform) var t1 = context.Operand1; - var e1 = Operand.Constant32_0; + var e1 = Operand.Constant64_0; context.SetInstruction(IR.Move64, result, t1); - context.AppendInstruction(IR.Move32, result2, e1); + context.AppendInstruction(IR.Move64, result2, e1); } } @@ -59,9 +59,9 @@ public override void Transform(Context context, Transform transform) var t1 = context.Operand2; - var e1 = Operand.Constant32_0; + var e1 = Operand.Constant64_0; context.SetInstruction(IR.Move64, result, t1); - context.AppendInstruction(IR.Move32, result2, e1); + context.AppendInstruction(IR.Move64, result2, e1); } } diff --git a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/AddOverflowOut64ByZero.cs b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/AddOverflowOut64ByZero.cs index a4732e295d..2ddfc6f19d 100644 --- a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/AddOverflowOut64ByZero.cs +++ b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/AddOverflowOut64ByZero.cs @@ -28,9 +28,9 @@ public override void Transform(Context context, Transform transform) var t1 = context.Operand1; - var e1 = Operand.Constant32_0; + var e1 = Operand.Constant64_0; context.SetInstruction(IR.Move64, result, t1); - context.AppendInstruction(IR.Move32, result2, e1); + context.AppendInstruction(IR.Move64, result2, e1); } } diff --git a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulCarryOut64ByOne.cs b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulCarryOut64ByOne.cs index 39c4c14fb1..0b3c156780 100644 --- a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulCarryOut64ByOne.cs +++ b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulCarryOut64ByOne.cs @@ -28,9 +28,9 @@ public override void Transform(Context context, Transform transform) var t1 = context.Operand1; - var e1 = Operand.Constant32_0; + var e1 = Operand.Constant64_0; context.SetInstruction(IR.Move64, result, t1); - context.AppendInstruction(IR.Move32, result2, e1); + context.AppendInstruction(IR.Move64, result2, e1); } } diff --git a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulCarryOut64ByZero.cs b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulCarryOut64ByZero.cs index 74e848eb52..d69618eb3d 100644 --- a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulCarryOut64ByZero.cs +++ b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulCarryOut64ByZero.cs @@ -27,9 +27,8 @@ public override void Transform(Context context, Transform transform) var result2 = context.Result2; var e1 = Operand.Constant64_0; - var e2 = Operand.Constant32_0; context.SetInstruction(IR.Move64, result, e1); - context.AppendInstruction(IR.Move32, result2, e2); + context.AppendInstruction(IR.Move64, result2, e1); } } diff --git a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulOverflowOut64ByOne.cs b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulOverflowOut64ByOne.cs index 4c22e247ac..b969ce73c5 100644 --- a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulOverflowOut64ByOne.cs +++ b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulOverflowOut64ByOne.cs @@ -28,9 +28,9 @@ public override void Transform(Context context, Transform transform) var t1 = context.Operand1; - var e1 = Operand.Constant32_0; + var e1 = Operand.Constant64_0; context.SetInstruction(IR.Move64, result, t1); - context.AppendInstruction(IR.Move32, result2, e1); + context.AppendInstruction(IR.Move64, result2, e1); } } diff --git a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulOverflowOut64ByZero.cs b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulOverflowOut64ByZero.cs index 060488ec83..28bce5d48e 100644 --- a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulOverflowOut64ByZero.cs +++ b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/MulOverflowOut64ByZero.cs @@ -27,9 +27,8 @@ public override void Transform(Context context, Transform transform) var result2 = context.Result2; var e1 = Operand.Constant64_0; - var e2 = Operand.Constant32_0; context.SetInstruction(IR.Move64, result, e1); - context.AppendInstruction(IR.Move32, result2, e2); + context.AppendInstruction(IR.Move64, result2, e1); } } diff --git a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/SubCarryOut64ByZero.cs b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/SubCarryOut64ByZero.cs index b06812a2cb..94a62364df 100644 --- a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/SubCarryOut64ByZero.cs +++ b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/SubCarryOut64ByZero.cs @@ -28,9 +28,9 @@ public override void Transform(Context context, Transform transform) var t1 = context.Operand1; - var e1 = Operand.Constant32_0; + var e1 = Operand.Constant64_0; context.SetInstruction(IR.Move64, result, t1); - context.AppendInstruction(IR.Move32, result2, e1); + context.AppendInstruction(IR.Move64, result2, e1); } } diff --git a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/SubOverflowOut64ByZero.cs b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/SubOverflowOut64ByZero.cs index 08f0c56b9b..bae1136803 100644 --- a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/SubOverflowOut64ByZero.cs +++ b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Auto/StrengthReduction/SubOverflowOut64ByZero.cs @@ -28,9 +28,9 @@ public override void Transform(Context context, Transform transform) var t1 = context.Operand1; - var e1 = Operand.Constant32_0; + var e1 = Operand.Constant64_0; context.SetInstruction(IR.Move64, result, t1); - context.AppendInstruction(IR.Move32, result2, e1); + context.AppendInstruction(IR.Move64, result2, e1); } } diff --git a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Manual/ConstantFolding/Switch.cs b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Manual/ConstantFolding/Switch.cs index 9488ae429b..9ca2686540 100644 --- a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Manual/ConstantFolding/Switch.cs +++ b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Manual/ConstantFolding/Switch.cs @@ -19,16 +19,21 @@ public override bool Match(Context context, Transform transform) public override void Transform(Context context, Transform transform) { var index = context.Operand1.ConstantSigned32; - var max = context.BranchTargets.Count - 1; + var count = context.BranchTargetsCount; - var targets = new List(context.BranchTargets.Count); - - foreach (var target in context.BranchTargets) + // Guard: no branch targets -> nothing to fold + if (count == 0) { - targets.Add(target); + context.SetNop(); + return; } - if (index < max) + // Copy targets for phi update after modifications + var targets = new List(context.BranchTargets); + + // If the constant index is inside the valid target range, jump to that target. + // Otherwise, fall through. + if (index >= 0 && index < count) { var newtarget = context.BranchTargets[index]; diff --git a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Manual/PHI/PhiR4Update.cs b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Manual/PHI/PhiR4Update.cs index 245685f5ad..29cbb0f54d 100644 --- a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Manual/PHI/PhiR4Update.cs +++ b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Manual/PHI/PhiR4Update.cs @@ -4,7 +4,7 @@ namespace Mosa.Compiler.Framework.Transforms.Optimizations.Manual.Phi; public sealed class PhiR4Update : BasePhiTransform { - public PhiR4Update() : base(IR.Phi32, TransformType.Manual | TransformType.Optimization) + public PhiR4Update() : base(IR.PhiR4, TransformType.Manual | TransformType.Optimization) { } diff --git a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Manual/PHI/PhiR8Update.cs b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Manual/PHI/PhiR8Update.cs index fe9631bc2b..2679a6ae82 100644 --- a/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Manual/PHI/PhiR8Update.cs +++ b/Source/Mosa.Compiler.Framework/Transforms/Optimizations/Manual/PHI/PhiR8Update.cs @@ -4,7 +4,7 @@ namespace Mosa.Compiler.Framework.Transforms.Optimizations.Manual.Phi; public sealed class PhiR8Update : BasePhiTransform { - public PhiR8Update() : base(IR.Phi32, TransformType.Manual | TransformType.Optimization) + public PhiR8Update() : base(IR.PhiR8, TransformType.Manual | TransformType.Optimization) { } diff --git a/Source/Mosa.UnitTests/FlowControl/WhileTests.cs b/Source/Mosa.UnitTests/FlowControl/WhileTests.cs index 723d20cf45..62443db58b 100644 --- a/Source/Mosa.UnitTests/FlowControl/WhileTests.cs +++ b/Source/Mosa.UnitTests/FlowControl/WhileTests.cs @@ -183,4 +183,169 @@ public static int WhileNestedEqualsI4(int a, int b, int start, int limit) return count; } + + // === SCCP Bug Regression Tests === + // These tests specifically target the bug where SCCP incorrectly eliminated null checks + // in while loops traversing linked objects, causing infinite loops + + private class LinkedNode + { + public int Value; + public LinkedNode Next; + + public LinkedNode(int value) + { + Value = value; + } + } + + [MosaUnitTest] + public static int WhileObjectTraversalNullCheck() + { + // Create a simple linked list: 1 -> 2 -> 3 -> null + var head = new LinkedNode(1); + head.Next = new LinkedNode(2); + head.Next.Next = new LinkedNode(3); + + var sum = 0; + var current = head; + + // This while loop with null check was being incorrectly optimized by SCCP + while (current != null) + { + sum += current.Value; + current = current.Next; + } + + return sum; // Should be 6 + } + + [MosaUnitTest] + public static int WhileObjectTraversalCount() + { + // Create a longer chain to ensure the loop iterates multiple times + var head = new LinkedNode(10); + var current = head; + + for (int i = 1; i < 5; i++) + { + current.Next = new LinkedNode(10 + i); + current = current.Next; + } + + // Traverse and count + current = head; + int count = 0; + + while (current != null) + { + count++; + current = current.Next; + } + + return count; // Should be 5 + } + + [MosaUnitTest] + public static bool WhileObjectTraversalLongChain() + { + // Create a chain of 10 nodes + var head = new LinkedNode(0); + var current = head; + + for (int i = 1; i < 10; i++) + { + current.Next = new LinkedNode(i); + current = current.Next; + } + + // Traverse to end + current = head; + int lastValue = -1; + + while (current != null) + { + lastValue = current.Value; + current = current.Next; + } + + return lastValue == 9; // Last node should have value 9 + } + + [MosaUnitTest] + public static int WhileObjectTraversalSum() + { + // Create list: 10 -> 20 -> 30 -> 40 + var head = new LinkedNode(10); + head.Next = new LinkedNode(20); + head.Next.Next = new LinkedNode(30); + head.Next.Next.Next = new LinkedNode(40); + + int sum = 0; + var current = head; + + // This is the exact pattern from GCTests::SumLinkedList that was failing + while (current != null) + { + sum += current.Value; + current = current.Next; + } + + return sum; // Should be 100 + } + + [MosaUnitTest] + public static bool WhileObjectTraversalWithModification() + { + // Create a chain and modify values during traversal + var head = new LinkedNode(1); + head.Next = new LinkedNode(2); + head.Next.Next = new LinkedNode(3); + + var current = head; + int product = 1; + + while (current != null) + { + product *= current.Value; + current.Value *= 10; // Modify during traversal + current = current.Next; + } + + // Verify both product (1*2*3 = 6) and modifications occurred + return product == 6 && head.Value == 10 && head.Next.Value == 20; + } + + [MosaUnitTest] + public static int WhileObjectNullCheckAtStart() + { + // Test when list is initially null + LinkedNode head = null; + int count = 0; + + while (head != null) + { + count++; + head = head.Next; + } + + return count; // Should be 0 + } + + [MosaUnitTest] + public static int WhileObjectSingleNode() + { + // Test with single-node list + var head = new LinkedNode(42); + var current = head; + int sum = 0; + + while (current != null) + { + sum += current.Value; + current = current.Next; + } + + return sum; // Should be 42 + } } diff --git a/Source/Mosa.Utility.SourceCodeGenerator/BuildTransformations.cs b/Source/Mosa.Utility.SourceCodeGenerator/BuildTransformations.cs index 48d1c41786..bf3b87441e 100644 --- a/Source/Mosa.Utility.SourceCodeGenerator/BuildTransformations.cs +++ b/Source/Mosa.Utility.SourceCodeGenerator/BuildTransformations.cs @@ -95,12 +95,12 @@ private void GenerateTranformations(string name, string type, string subName, st if (expression.Contains("R#")) { GenerateTransformation(R4(name), R4(type), R4(subName), new Transformation(R4(expression), R4(filter), R4(result), R4(result2), prefilter), optimization, priority, variations, log, commutativeInstructions); - GenerateTransformation(R8(name), R8(type), R8(subName), new Transformation(R8(expression), R8(filter), R8(result), R4(result2), prefilter), optimization, priority, variations, log, commutativeInstructions); + GenerateTransformation(R8(name), R8(type), R8(subName), new Transformation(R8(expression), R8(filter), R8(result), R8(result2), prefilter), optimization, priority, variations, log, commutativeInstructions); } else if (expression.Contains("##")) { GenerateTransformation(To32(name), To32(type), To32(subName), new Transformation(To32(expression), To32(filter), To32(result), To32(result2), prefilter), optimization, priority, variations, log, commutativeInstructions); - GenerateTransformation(To64(name), To64(type), To64(subName), new Transformation(To64(expression), To64(filter), To64(result), To32(result2), prefilter), optimization, priority, variations, log, commutativeInstructions); + GenerateTransformation(To64(name), To64(type), To64(subName), new Transformation(To64(expression), To64(filter), To64(result), To64(result2), prefilter), optimization, priority, variations, log, commutativeInstructions); } else { @@ -713,7 +713,6 @@ private string ProcessFilters(Method filter, Transformation transform) if (!register) { var first = transform.LabelSet.GetFirstPosition(parameter.Value); - var parent = NodeNbrToNode[first.NodeNbr]; var operandName = GetOperandName(first.OperandIndex); @@ -890,13 +889,11 @@ protected void ProcessConditions(Operand operand, Node parent) { EmitCondition($"context.{operandName}.ConstantUnsigned64 != {CreateConstantName(operand)}"); } - - if (operand.IsDouble) + else if (operand.IsDouble) { EmitCondition($"context.{operandName}.ConstantDouble != {CreateConstantName(operand)}"); } - - if (operand.IsFloat) + else if (operand.IsFloat) { EmitCondition($"context.{operandName}.ConstantFloat != {CreateConstantName(operand)}"); }