From 77b7ce0ffbc6ace87d49b9e18758c3c875b2d697 Mon Sep 17 00:00:00 2001 From: Xavier Leroy Date: Wed, 5 Mar 2025 11:17:08 +0100 Subject: [PATCH] Register allocation: try aggressive coalescing first Run a first round of coloring with aggressive coalescing of moves. If this causes spilling, retry with iterated coalescing, like before. This should result in fewer moves for functions that can run entirely in registers. --- backend/IRC.ml | 19 ++++++++++--------- backend/IRC.mli | 8 ++++++-- backend/Regalloc.ml | 17 +++++++++++++---- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/backend/IRC.ml b/backend/IRC.ml index f9d7d770f3..87cb722808 100644 --- a/backend/IRC.ml +++ b/backend/IRC.ml @@ -669,7 +669,7 @@ let combine g u v = (* Attempt coalescing *) -let coalesce g = +let coalesce forced_coalescing g = let m = DLinkMove.pick g.worklistMoves in let x = getAlias m.src and y = getAlias m.dst in let (u, v) = if y.nstate = Colored then (y, x) else (x, y) in @@ -681,7 +681,7 @@ let coalesce g = DLinkMove.insert m g.constrainedMoves; addWorkList g u; addWorkList g v - end else if canCoalesce g u v then begin + end else if forced_coalescing || canCoalesce g u v then begin DLinkMove.insert m g.coalescedMoves; combine g u v; addWorkList g u @@ -758,16 +758,16 @@ let selectSpill g = (* Produce the order of nodes that we'll use for coloring *) -let rec nodeOrder g stack = +let rec nodeOrder forced g stack = (*i checkInvariants g; *) if DLinkNode.notempty g.simplifyWorklist then - (let n = simplify g in nodeOrder g (n :: stack)) + (let n = simplify g in nodeOrder forced g (n :: stack)) else if DLinkMove.notempty g.worklistMoves then - (coalesce g; nodeOrder g stack) + (coalesce forced g; nodeOrder forced g stack) else if DLinkNode.notempty g.freezeWorklist then - (freeze g; nodeOrder g stack) + (freeze g; nodeOrder forced g stack) else if DLinkNode.notempty g.spillWorklist then - (let n = selectSpill g in nodeOrder g (n :: stack)) + (let n = selectSpill g in nodeOrder forced g (n :: stack)) else stack @@ -921,7 +921,8 @@ let add_interf g v1 v2 = let add_pref g v1 v2 = let n1 = nodeOfVar g v1 in let n2 = nodeOfVar g v2 in addMovePref g n1 n2 -let coloring g = +let coloring ~coalescing g = + let forced = match coalescing with `Aggressive -> true | `Prudent -> false in initialNodePartition g; - List.iter (assign_color g) (nodeOrder g []); + List.iter (assign_color g) (nodeOrder forced g []); location_of_var g (* total function var -> location *) diff --git a/backend/IRC.mli b/backend/IRC.mli index 59471329be..21ce3d5f50 100644 --- a/backend/IRC.mli +++ b/backend/IRC.mli @@ -35,8 +35,12 @@ val add_interf: graph -> var -> var -> unit (* Add a preference between two variables. *) val add_pref: graph -> var -> var -> unit -(* Color the graph. Return an assignment of locations to variables. *) -val coloring: graph -> (var -> loc) +(* Color the graph. Return an assignment of locations to variables. + [~coalescing] sets the coalescing mode: + - [`Prudent] to perform only coalescings that cannot cause spilling; + - [`Aggressive] to force coalescing whenever semantically sound. *) + +val coloring: coalescing:[`Prudent|`Aggressive] -> graph -> (var -> loc) (* Auxiliaries to deal with register classes *) val class_of_type: AST.typ -> int diff --git a/backend/Regalloc.ml b/backend/Regalloc.ml index 86c311b275..9e00e95d4c 100644 --- a/backend/Regalloc.ml +++ b/backend/Regalloc.ml @@ -733,7 +733,7 @@ let rec add_interfs_block g blk live = add_interfs_instr g instr live'; live_before instr live' -let find_coloring f liveness = +let find_coloring ~coalescing f liveness = (*type_function f; (* for debugging *)*) let g = IRC.init (spill_costs f) in PTree.fold @@ -742,7 +742,7 @@ let find_coloring f liveness = add_interfs_destroyed g (transfer_live f f.fn_entrypoint (PMap.get f.fn_entrypoint liveness)) destroyed_at_function_entry; - IRC.coloring g + IRC.coloring ~coalescing g (*********** Determination of variables that need spill code insertion *****) @@ -1153,19 +1153,28 @@ let transl_function fn alloc = exception Timeout let rec first_round f liveness = - let alloc = find_coloring f liveness in + let alloc = find_coloring ~coalescing:`Aggressive f liveness in if !option_dalloctrace then begin fprintf !pp "-------------- After initial register allocation\n\n"; PrintXTL.print_function !pp ~alloc: alloc ~live: liveness f end; let ts = tospill_function f alloc in + if VSet.is_empty ts then success f alloc else second_round f liveness + +and second_round f liveness = + let alloc = find_coloring ~coalescing:`Prudent f liveness in + if !option_dalloctrace then begin + fprintf !pp "-------------- After register allocation without aggressive coalescing\n\n"; + PrintXTL.print_function !pp ~alloc: alloc ~live: liveness f + end; + let ts = tospill_function f alloc in if VSet.is_empty ts then success f alloc else more_rounds f ts 1 and more_rounds f ts count = if count >= 40 then raise Timeout; let f' = spill_function f ts count in let liveness = liveness_analysis f' in - let alloc = find_coloring f' liveness in + let alloc = find_coloring ~coalescing:`Prudent f' liveness in if !option_dalloctrace then begin fprintf !pp "-------------- After register allocation (round %d)\n\n" count; PrintXTL.print_function !pp ~alloc: alloc ~live: liveness f'