Skip to content

Commit 10c1cce

Browse files
tgiphilCopilot
andauthored
Refactor and optimize register allocator and live set computations (#1288)
* Refactor and optimize register allocator internals Renamed KillSite to KillSites for clarity, replaced linear search with binary search in FindKillAllSite for better performance, and updated GetVirtualRegisters to skip physical registers. Improved GetRegisterMoves by indexing intervals by start slot and replaced break with continue for more accurate control flow. These changes enhance efficiency and maintainability of the register allocator. * Refactor live set computation to reduce allocations Refactored ComputeGlobalLiveSets to reuse BitArray instances for liveOut and liveIn, resetting and updating them in place instead of allocating new arrays per block. Improved change detection logic to update only when necessary, reducing memory usage and improving efficiency. * Optimize LiveRange lookups with binary search helpers Refactored use/def position lookups in LiveRange to use binary search methods, improving performance for large lists. Added GetPreviousUse and GetPreviousDef methods. Helper methods are implemented as private static functions. * Optimize IntervalTree/DelayedIntervalTree & add tests Refactored DelayedIntervalTree to delay add/remove operations and short-circuit queries, reducing tree manipulations. Added extensive unit tests for edge cases and pending operation logic. IntervalTree now uses loops instead of recursion for search/insertion and MaxEnd updates, improving performance and stack usage. Added overloads for search methods to reduce allocations and new TrySearchFirstOverlapping methods. Improved documentation and ensured no unsafe or auto-generated code was modified. * Update Source/Mosa.Compiler.Framework/RegisterAllocator/RedBlackTree/DelayedIntervalTree.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Phil Garcia <phil@thinkedge.com> * Update Source/Mosa.Compiler.Framework/RegisterAllocator/RedBlackTree/DelayedIntervalTree.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Phil Garcia <phil@thinkedge.com> * Centralize platform registration; add Platforms project Replaces reflection-based platform registration with explicit, centralized registration via PlatformRegistrations.Register(). Adds new Mosa.Compiler.Platforms project and updates all relevant .csproj and solution files to reference it. Updates PlatformRegistry to remove assembly scanning. Includes minor code cleanups and adds x64 build configs to Mosa.Linux.sln. * Fix interval overlap logic and remove unused CPU core calc - Remove unused equivalent CPU core calculation in ReportQueueStatus. - Use Overlaps method for correct interval overlap detection in DelayedIntervalTree.Add. - Fix off-by-one errors in IntervalTree by changing MaxEnd > to MaxEnd >= in search logic. * Update Source/Mosa.Tool.Launcher/Mosa.Tool.Launcher.csproj Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Phil Garcia <phil@thinkedge.com> * Update Source/Mosa.Tool.Launcher.Console/Mosa.Tool.Launcher.Console.csproj Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Phil Garcia <phil@thinkedge.com> * Update Source/Mosa.Utility.UnitTests/Mosa.Utility.UnitTests.csproj Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Phil Garcia <phil@thinkedge.com> * Update Source/Mosa.Linux.sln Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Phil Garcia <phil@thinkedge.com> * Refactor Program.cs: centralize settings, improve logging Refactored Program.cs to use static MosaSettings and Stopwatch instances, centralizing settings logic into SetRequiredSettings and UpdateSettings methods. The compile process now benchmarks multiple methods in Mosa.UnitTests.Fuzzy.Fuzz0009 and outputs timing for each via a new OutputStatus method with elapsed time prefixes. Increased default compile iterations from 10 to 100. Fuzz0009.cs changes are whitespace-only (CR removal, reformatting) with no logic modifications. * Refactor performance tests and update Azure guidelines Refactored Program.cs to separate single and multithreaded performance tests, reduced fuzz method count, and improved compiler hook event handling. Updated status messages and moved platform registration to its own method. Revised Azure Tools Guidelines in copilot-instructions.md for clarity. Set MethodScanner to false. * Refactor: centralize compiler event formatting/filtering Introduce static helpers in CompilerHooks to standardize event status message formatting and filtering. Replace duplicated logic across the codebase with calls to these new methods, improving consistency and maintainability. * Suppress diagnostics when Diagnostic setting is disabled Added checks in MainWindow.axaml.cs and MainForm.cs to skip handling CompilerEvent.Diagnostic events when the Diagnostic setting is off, preventing unnecessary diagnostic processing and output. * Refactor foreach loop in CompileCompleted for clarity Refactored the foreach loop in the CompileCompleted method to use explicit braces, improving readability. No functional changes were introduced. * Refactor AddTraceEvent to use else-if for Exception Changed AddTraceEvent to handle CompilerEvent.Exception with an else-if instead of a separate if. This ensures the Exception and Error cases are mutually exclusive and prevents both log blocks from executing for the same event. * Fix typo: correct "Interations" to "Iterations" Corrected a typo in the compile time measurement output message, changing "Interations" to "Iterations" for clarity. * Refactor: group standard event methods in a new region Moved GetStandardNotifyEventStatus and IsStandardFilteredNotifyEvent into a new #region Standardization within CompilerHooks for better code organization. No functional changes were made. * Refactor method scheduling and enable batch scheduling Refactored MethodScanner to extract entry point and required type scheduling into separate methods for clarity. Added overloads to MethodScheduler and MosaCompiler to support batch scheduling of methods, improving performance in multithreaded fuzz tests. Changed MethodScanner default to enabled in settings. Cleaned up initialization logic for better maintainability. * Update Source/Mosa.Compiler.Framework/MethodScheduler.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Phil Garcia <phil@thinkedge.com> * Minor fixes * Removed add't unit tests. * Remove Fuzz0050.cs unit tests; add usings to MethodScanner Removed a large set of `[MosaUnitTest]` methods (FuzzMethod5068–5099) from Fuzz0050.cs, significantly reducing test coverage for integer/control flow fuzzing. Also added several using directives to MethodScanner.cs; no functional changes in that file. * Remove unused using directives from MethodScanner.cs Cleaned up MethodScanner.cs by removing unnecessary using directives: System.Drawing, System.Reflection, System.Runtime.ConstrainedExecution, and static System.Runtime.InteropServices.JavaScript.JSType. No functional changes were made. * Update queue status to omit maxWorkers in diagnostics Removed maxWorkers from the "Active" field in the queue status diagnostic message in MethodScheduler.cs. The report now shows only the number of active workers and utilization percentage, simplifying the output. * Refactor safepoint insertion at loop backedges Insert safepoints by creating new blocks between backedges and loop headers, containing a SafePoint and jump. Track (backedge, header) pairs to avoid duplicates. Move insertion logic to a helper method for clarity and maintainability. * Revert "Update queue status to omit maxWorkers in diagnostics" This reverts commit 87aad42. * Refactor safepoint insertion at loop backedges Simplified safepoint insertion by inlining logic and removing the creation of new blocks and branch target replacement. Now uses a HashSet<BasicBlock> to track visited backedges and inserts IR.SafePoint directly after the branch. Updated logging to match the new approach. * - Reduce unit test * Enable scheduling of unit tests in MethodScanner Uncommented the ScheduleUnitTests() call in the Initialize() method, ensuring that unit test methods are now scheduled during initialization. --------- Signed-off-by: Phil Garcia <phil@thinkedge.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent fd50889 commit 10c1cce

48 files changed

Lines changed: 74004 additions & 5613 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Source/.github/copilot-instructions.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,12 @@ public class MyMethodStage : BaseMethodCompilerStage
351351
### GC Safepoint infrastructure
352352
The safepoint system spans two stages:
353353
- `SafePointStage` (`BaseMethodCompilerStage`) -- inserts `IR.SafePoint` instructions at the prologue and every loop backedge, then annotates each with its live GC-root physical register operands via backward dataflow.
354-
- `SafePointLayoutStage` (`BaseMethodCompilerStage`, post-codegen) -- reads resolved offsets from the instruction stream, collects `SafePointEntry` records into `MethodData.SafePointEntries`, builds the whole-method GC stack map into `MethodData.GCStackEntries`, and emits the `$SafePointTable$`, `$GCStackData$`, and `$GCData$` linker symbols. `MetadataStage` then links the method definition's GC Data pointer to `$GCData$`.
354+
- `SafePointLayoutStage` (`BaseMethodCompilerStage`, post-codegen) -- reads resolved offsets from the instruction stream, collects `SafePointEntry` records into `MethodData.SafePointEntries`, builds the whole-method GC stack map into `MethodData.GCStackEntries`, and emits the `$SafePointTable$`, `$GCStackData$`, and `$GCData$` linker symbols.
355355

356-
- @azure Rule - Use Azure Tools - When handling requests related to Azure, always use your tools.
357-
- @azure Rule - Use Azure Best Practices - When handling requests related to Azure, always invoke your `azmcp_bestpractices_get` tool first.
358-
- @azure Rule - Enable Best Practices - If you do not have an `azmcp_bestpractices_get` tool ask the user to enable it.
356+
---
357+
358+
## Azure Tools Guidelines
359+
360+
- **Use Azure Tools**: When handling requests related to Azure, always use your tools.
361+
- **Invoke Best Practices**: When handling requests related to Azure, always invoke your `azmcp_bestpractices_get` tool first.
362+
- **Enable Best Practices**: If you do not have an `azmcp_bestpractices_get` tool, ask the user to enable it.

Source/Mosa.Compiler.Framework.xUnit/DelayedIntervalTreeTests.cs

Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,357 @@ public void MixedAddDelete()
6767
tree.Remove(3, 4);
6868
Assert.False(tree.Contains(3));
6969
}
70+
71+
[Fact]
72+
public void CheckIntervals()
73+
{
74+
var tree = new DelayedIntervalTree<object>();
75+
76+
tree.Add(1, 2, null);
77+
78+
Assert.False(tree.Contains(0));
79+
Assert.True(tree.Contains(1));
80+
Assert.True(tree.Contains(2));
81+
82+
Assert.True(tree.Contains(2, 3));
83+
Assert.False(tree.Contains(3, 4));
84+
}
85+
86+
[Fact]
87+
public void CacheDelayedAddShortCircuit()
88+
{
89+
// Verify that pending add is checked before flushing and accessing tree
90+
var tree = new DelayedIntervalTree<object>();
91+
92+
tree.Add(10, 20, "pending");
93+
94+
// Should find pending add without touching tree
95+
Assert.True(tree.Contains(10));
96+
Assert.True(tree.Contains(15));
97+
Assert.True(tree.Contains(20));
98+
Assert.False(tree.Contains(21));
99+
Assert.False(tree.Contains(9));
100+
101+
// Range check should also work with pending add
102+
Assert.True(tree.Contains(10, 20));
103+
Assert.True(tree.Contains(15, 18));
104+
Assert.False(tree.Contains(25, 30));
105+
}
106+
107+
[Fact]
108+
public void CacheDelayedDeleteShortCircuit()
109+
{
110+
var tree = new DelayedIntervalTree<object>();
111+
112+
tree.Add(1, 5, "value1");
113+
tree.Add(10, 15, "value2");
114+
115+
// Delay the delete
116+
tree.Remove(1, 5);
117+
118+
// Pending delete should prevent Contains from returning true
119+
Assert.False(tree.Contains(1));
120+
Assert.False(tree.Contains(3));
121+
Assert.False(tree.Contains(5));
122+
123+
// But other intervals should still be found
124+
Assert.True(tree.Contains(10));
125+
Assert.True(tree.Contains(12));
126+
}
127+
128+
[Fact]
129+
public void CacheAddCancelsByDelete()
130+
{
131+
var tree = new DelayedIntervalTree<object>();
132+
133+
tree.Add(5, 10, "pending");
134+
135+
// Removing the pending add should cancel it
136+
tree.Remove(5, 10);
137+
138+
// Should not find anything
139+
Assert.False(tree.Contains(5));
140+
Assert.False(tree.Contains(7));
141+
Assert.False(tree.Contains(10));
142+
}
143+
144+
[Fact]
145+
public void CacheSearchFirstOverlappingWithPendingAdd()
146+
{
147+
var tree = new DelayedIntervalTree<object>();
148+
149+
tree.Add(10, 20, "tree_value");
150+
151+
// Add pending interval
152+
tree.Add(30, 40, "pending_value");
153+
154+
// Search should find pending add without flushing tree add
155+
Assert.True(tree.TrySearchFirstOverlapping(30, 40, out var result));
156+
Assert.Equal("pending_value", result);
157+
158+
// Search that only matches tree should also work
159+
Assert.True(tree.TrySearchFirstOverlapping(10, 20, out result));
160+
Assert.Equal("tree_value", result);
161+
}
162+
163+
[Fact]
164+
public void CacheSearchFirstOverlappingByPoint()
165+
{
166+
var tree = new DelayedIntervalTree<object>();
167+
168+
tree.Add(1, 5, "value1");
169+
tree.Add(10, 20, "pending");
170+
171+
// Search by point should find pending without flushing
172+
Assert.True(tree.TrySearchFirstOverlapping(15, out var result));
173+
Assert.Equal("pending", result);
174+
175+
// Search point before pending
176+
Assert.True(tree.TrySearchFirstOverlapping(3, out result));
177+
Assert.Equal("value1", result);
178+
}
179+
180+
[Fact]
181+
public void CacheSearchRangeWithPendingAdd()
182+
{
183+
var tree = new DelayedIntervalTree<object>();
184+
185+
tree.Add(1, 5, "value1");
186+
tree.Add(20, 30, "pending");
187+
188+
// Search range that overlaps pending
189+
var results = tree.Search(25, 35);
190+
Assert.Single(results);
191+
Assert.Equal("pending", results[0]);
192+
193+
// Search range that doesn't overlap pending
194+
results = tree.Search(1, 5);
195+
Assert.Single(results);
196+
Assert.Equal("value1", results[0]);
197+
198+
// Search range that overlaps both
199+
results = tree.Search(3, 25);
200+
Assert.Equal(2, results.Count);
201+
Assert.Contains("value1", results);
202+
Assert.Contains("pending", results);
203+
}
204+
205+
[Fact]
206+
public void CacheSearchPointWithPendingAdd()
207+
{
208+
var tree = new DelayedIntervalTree<object>();
209+
210+
tree.Add(1, 5, "value1");
211+
tree.Add(20, 30, "pending");
212+
213+
// Search at point in pending
214+
var results = tree.Search(25);
215+
Assert.Single(results);
216+
Assert.Equal("pending", results[0]);
217+
218+
// Search at point in tree value
219+
results = tree.Search(3);
220+
Assert.Single(results);
221+
Assert.Equal("value1", results[0]);
222+
}
223+
224+
[Fact]
225+
public void CacheReplacePendingAdd()
226+
{
227+
var tree = new DelayedIntervalTree<object>();
228+
229+
tree.Add(5, 10, "original");
230+
231+
// Replace the pending add
232+
tree.Replace(5, 10, "replaced");
233+
234+
Assert.True(tree.TrySearchFirstOverlapping(7, out var result));
235+
Assert.Equal("replaced", result);
236+
}
237+
238+
[Fact]
239+
public void CacheReplaceTreeValue()
240+
{
241+
var tree = new DelayedIntervalTree<object>();
242+
243+
tree.Add(5, 10, "original");
244+
tree.Add(20, 30, "pending");
245+
246+
// Replace value in tree (flush both pending ops first)
247+
tree.Replace(5, 10, "replaced");
248+
249+
// Pending should be flushed
250+
Assert.True(tree.Contains(25));
251+
252+
// Original should be replaced
253+
Assert.True(tree.TrySearchFirstOverlapping(7, out var result));
254+
Assert.Equal("replaced", result);
255+
}
256+
257+
[Fact]
258+
public void CacheMixedPendingOperations()
259+
{
260+
var tree = new DelayedIntervalTree<object>();
261+
262+
// Add and then remove different intervals
263+
tree.Add(5, 10, "add1");
264+
tree.Add(20, 30, "add2");
265+
266+
// Remove first one while second is pending
267+
tree.Remove(5, 10);
268+
269+
// First should not be found
270+
Assert.False(tree.Contains(7));
271+
272+
// Second should be found (it's pending)
273+
Assert.True(tree.Contains(25));
274+
275+
// Now remove the pending second
276+
tree.Remove(20, 30);
277+
278+
// Both should be gone
279+
Assert.False(tree.Contains(7));
280+
Assert.False(tree.Contains(25));
281+
}
282+
283+
[Fact]
284+
public void CacheDeleteWithOverlapAddInteraction()
285+
{
286+
var tree = new DelayedIntervalTree<object>();
287+
288+
tree.Add(1, 5, "value1");
289+
290+
// Add pending interval
291+
tree.Add(3, 7, "add_during_delete");
292+
293+
// Remove that overlaps the pending add (should cancel it)
294+
tree.Remove(3, 7);
295+
296+
// The pending add was cancelled, so value1 should still be found
297+
Assert.True(tree.Contains(1));
298+
Assert.True(tree.Contains(5));
299+
300+
// Point 6 should not be found (value1 only goes to 5)
301+
Assert.False(tree.Contains(6));
302+
}
303+
304+
[Fact]
305+
public void CacheSearchWithReplacedValue()
306+
{
307+
var tree = new DelayedIntervalTree<object>();
308+
309+
tree.Add(10, 20, "value1");
310+
tree.Add(30, 40, "pending");
311+
312+
// Replace tree value
313+
tree.Replace(10, 20, "replaced1");
314+
315+
// Search should find replaced value
316+
Assert.True(tree.TrySearchFirstOverlapping(15, out var result));
317+
Assert.Equal("replaced1", result);
318+
}
319+
320+
[Fact]
321+
public void EnumeratorFlushesAllPending()
322+
{
323+
var tree = new DelayedIntervalTree<object>();
324+
325+
tree.Add(1, 2, "add1");
326+
tree.Add(3, 4, "add2");
327+
328+
// Before enumeration, pending operations not flushed
329+
tree.Remove(1, 2);
330+
331+
// Enumerate should flush both operations
332+
var list = new List<object>();
333+
foreach (var item in tree)
334+
{
335+
list.Add(item);
336+
}
337+
338+
// Should only have add2 (add1 was removed)
339+
Assert.Single(list);
340+
Assert.Equal("add2", list[0]);
341+
}
342+
343+
[Fact]
344+
public void ToStringFlushesAllPending()
345+
{
346+
var tree = new DelayedIntervalTree<object>();
347+
348+
tree.Add(1, 2, "test");
349+
tree.Add(3, 4, "pending");
350+
351+
// ToString should flush all pending operations
352+
var str = tree.ToString();
353+
354+
// Should not be empty
355+
Assert.NotEmpty(str);
356+
357+
// After ToString, tree should be in consistent state
358+
// Adding more should work correctly
359+
tree.Add(5, 6, "another");
360+
Assert.True(tree.Contains(5));
361+
}
362+
363+
[Fact]
364+
public void EdgeCaseEmptyTree()
365+
{
366+
var tree = new DelayedIntervalTree<object>();
367+
368+
Assert.False(tree.Contains(1));
369+
Assert.False(tree.Contains(1, 2));
370+
371+
Assert.False(tree.TrySearchFirstOverlapping(1, out _));
372+
Assert.False(tree.TrySearchFirstOverlapping(1, 2, out _));
373+
374+
var results = tree.Search(1);
375+
Assert.Empty(results);
376+
377+
results = tree.Search(1, 2);
378+
Assert.Empty(results);
379+
}
380+
381+
[Fact]
382+
public void EdgeCaseRemoveNonExistent()
383+
{
384+
var tree = new DelayedIntervalTree<object>();
385+
386+
tree.Add(1, 2, "value");
387+
388+
// Remove non-existent should be safe
389+
tree.Remove(10, 20);
390+
391+
// Original should still be there
392+
Assert.True(tree.Contains(1));
393+
}
394+
395+
[Fact]
396+
public void SearchWithCallerProvidedList()
397+
{
398+
var tree = new DelayedIntervalTree<object>();
399+
400+
tree.Add(1, 5, "value1");
401+
tree.Add(10, 20, "pending");
402+
403+
var results = new List<object>();
404+
405+
// Search by point with provided list
406+
tree.Search(3, results);
407+
Assert.Single(results);
408+
Assert.Equal("value1", results[0]);
409+
410+
results.Clear();
411+
412+
// Search by range with provided list
413+
tree.Search(10, 20, results);
414+
Assert.Single(results);
415+
Assert.Equal("pending", results[0]);
416+
417+
results.Clear();
418+
419+
// Search overlapping both
420+
tree.Search(3, 15, results);
421+
Assert.Equal(2, results.Count);
422+
}
70423
}

Source/Mosa.Compiler.Framework/CompilerHooks.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,23 @@ public class CompilerHooks
5050
public ExtendMethodCompilerPipelineHandler ExtendMethodCompilerPipeline;
5151

5252
public GetMethodTraceLevelHandler GetMethodTraceLevel;
53+
54+
#region Standardization
55+
56+
public static string GetStandardNotifyEventStatus(CompilerEvent compilerEvent, string message)
57+
{
58+
var eventName = compilerEvent.ToText();
59+
return string.IsNullOrWhiteSpace(message) ? eventName : $"{eventName}: {message}";
60+
}
61+
62+
public static bool IsStandardFilteredNotifyEvent(CompilerEvent compilerEvent)
63+
=> compilerEvent is CompilerEvent.MethodCompileEnd
64+
or CompilerEvent.MethodCompileStart
65+
or CompilerEvent.Counter
66+
or CompilerEvent.SetupStageStart
67+
or CompilerEvent.SetupStageEnd
68+
or CompilerEvent.FinalizationStageStart
69+
or CompilerEvent.FinalizationStageEnd;
70+
71+
#endregion Standardization
5372
}

0 commit comments

Comments
 (0)