@@ -54,6 +54,19 @@ bool check_live_out(basic_block_t *bb, var_t *var)
54
54
return false;
55
55
}
56
56
57
+ void track_var_use (var_t * var , int insn_idx )
58
+ {
59
+ if (!var )
60
+ return ;
61
+
62
+ var -> use_count ++ ;
63
+
64
+ if (var -> first_use < 0 )
65
+ var -> first_use = insn_idx ;
66
+
67
+ var -> last_use = insn_idx ;
68
+ }
69
+
57
70
void refresh (basic_block_t * bb , insn_t * insn )
58
71
{
59
72
for (int i = 0 ; i < REG_CNT ; i ++ ) {
@@ -93,18 +106,57 @@ ph2_ir_t *bb_add_ph2_ir(basic_block_t *bb, opcode_t op)
93
106
return n ;
94
107
}
95
108
109
+ /* Calculate the cost of spilling a variable from a register.
110
+ * Higher cost means the variable is more valuable to keep in a register.
111
+ * The cost is computed based on multiple factors that affect performance.
112
+ */
96
113
int calculate_spill_cost (var_t * var , basic_block_t * bb , int current_idx )
97
114
{
115
+ int cost = 0 ;
116
+
117
+ /* Variables that are live-out of the basic block must be spilled anyway,
118
+ * so give them a high cost to prefer spilling them over others
119
+ */
98
120
if (check_live_out (bb , var ))
99
- return 1000 ;
121
+ cost += 1000 ;
100
122
123
+ /* Variables that will be used soon should have higher cost.
124
+ * The closer the next use, the higher the penalty for spilling
125
+ */
101
126
if (var -> consumed > current_idx ) {
102
127
int distance = var -> consumed - current_idx ;
103
128
if (distance < 10 )
104
- return 100 - distance * 10 ;
129
+ cost += 100 - distance * 10 ; /* Max 100 points for immediate use */
130
+ }
131
+
132
+ /* Frequently used variables should stay in registers.
133
+ * Each use adds 5 points to the cost
134
+ */
135
+ if (var -> use_count > 0 )
136
+ cost += var -> use_count * 5 ;
137
+
138
+ /* Variables inside loops are accessed repeatedly, so they should have much
139
+ * higher priority to stay in registers (200 points per level)
140
+ */
141
+ if (var -> loop_depth > 0 )
142
+ cost += var -> loop_depth * 200 ;
143
+
144
+ /* Constants can be easily reloaded, so prefer spilling them by reducing
145
+ * their cost
146
+ */
147
+ if (var -> is_const )
148
+ cost -= 50 ;
149
+
150
+ /* Variables with long live ranges may benefit from spilling to free up
151
+ * registers for other variables
152
+ */
153
+ if (var -> first_use >= 0 && var -> last_use >= 0 ) {
154
+ int range_length = var -> last_use - var -> first_use ;
155
+ if (range_length > 100 )
156
+ cost += 20 ; /* Small penalty for very long live ranges */
105
157
}
106
158
107
- return 0 ;
159
+ return cost ;
108
160
}
109
161
110
162
int find_best_spill (basic_block_t * bb ,
@@ -269,9 +321,8 @@ int prepare_dest(basic_block_t *bb, var_t *var, int operand_0, int operand_1)
269
321
}
270
322
}
271
323
272
- if (REGS [spilled ].var ) {
324
+ if (REGS [spilled ].var )
273
325
vreg_clear_phys (REGS [spilled ].var );
274
- }
275
326
276
327
spill_var (bb , REGS [spilled ].var , spilled );
277
328
REGS [spilled ].var = var ;
@@ -512,6 +563,7 @@ void reg_alloc(void)
512
563
513
564
switch (insn -> opcode ) {
514
565
case OP_unwound_phi :
566
+ track_var_use (insn -> rs1 , insn -> idx );
515
567
src0 = prepare_operand (bb , insn -> rs1 , -1 );
516
568
517
569
if (!insn -> rd -> offset ) {
@@ -610,6 +662,7 @@ void reg_alloc(void)
610
662
if (insn -> rd -> consumed == -1 )
611
663
break ;
612
664
665
+ track_var_use (insn -> rs1 , insn -> idx );
613
666
src0 = find_in_regs (insn -> rs1 );
614
667
615
668
/* If operand is loaded from stack, clear the original slot
@@ -754,6 +807,8 @@ void reg_alloc(void)
754
807
case OP_bit_and :
755
808
case OP_bit_or :
756
809
case OP_bit_xor :
810
+ track_var_use (insn -> rs1 , insn -> idx );
811
+ track_var_use (insn -> rs2 , insn -> idx );
757
812
src0 = prepare_operand (bb , insn -> rs1 , -1 );
758
813
src1 = prepare_operand (bb , insn -> rs2 , src0 );
759
814
dest = prepare_dest (bb , insn -> rd , src0 , src1 );
0 commit comments