Skip to content

Commit 66f2ebe

Browse files
committed
Register allocator refactoring
1 parent fcc6da3 commit 66f2ebe

File tree

4 files changed

+83
-41
lines changed

4 files changed

+83
-41
lines changed

ext/opcache/jit/zend_jit.c

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,19 @@ static zend_bool zend_ssa_is_last_use(const zend_op_array *op_array, const zend_
133133
return use < 0 || zend_ssa_is_no_val_use(op_array->opcodes + use, ssa->ops + use, var);
134134
}
135135

136+
static zend_bool zend_ival_is_last_use(const zend_lifetime_interval *ival, int use)
137+
{
138+
if (ival->flags & ZREG_LAST_USE) {
139+
const zend_life_range *range = &ival->range;
140+
141+
while (range->next) {
142+
range = range->next;
143+
}
144+
return range->end == use;
145+
}
146+
return 0;
147+
}
148+
136149
static zend_bool zend_is_commutative(zend_uchar opcode)
137150
{
138151
return
@@ -702,9 +715,7 @@ static int zend_jit_add_range(zend_lifetime_interval **intervals, int var, uint3
702715
}
703716
ival->ssa_var = var;
704717
ival->reg = ZREG_NONE;
705-
ival->split = 0;
706-
ival->store = 0;
707-
ival->load = 0;
718+
ival->flags = 0;
708719
ival->range.start = from;
709720
ival->range.end = to;
710721
ival->range.next = NULL;
@@ -831,13 +842,12 @@ static int zend_jit_split_interval(zend_lifetime_interval *current, uint32_t pos
831842
}
832843
}
833844

834-
current->store = 1;
845+
current->flags |= ZREG_STORE;
835846

836847
ival->ssa_var = current->ssa_var;
837848
ival->reg = ZREG_NONE;
838-
ival->split = 1;
839-
ival->store = 0;
840-
ival->load = 1;
849+
ival->flags |= ZREG_SPLIT | ZREG_LOAD;
850+
ival->flags &= ZREG_STORE;
841851
ival->hint = NULL;
842852

843853
do {
@@ -1431,7 +1441,7 @@ static int zend_jit_try_allocate_free_reg(const zend_op_array *op_array, zend_ss
14311441

14321442
/* See "Linear Scan Register Allocation on SSA Form", Christian Wimmer and
14331443
Michael Franz, CGO'10 (2010), Figure 6. */
1434-
if (current->split) {
1444+
if (current->flags & ZREG_SPLIT) {
14351445
/* for each interval it in inactive intersecting with current do */
14361446
/* freeUntilPos[it.reg] = next intersection of it with current */
14371447
it = inactive;
@@ -1451,9 +1461,13 @@ static int zend_jit_try_allocate_free_reg(const zend_op_array *op_array, zend_ss
14511461
range = &current->range;
14521462
do {
14531463
uint32_t line = range->start;
1464+
uint32_t last_use_line = (uint32_t)-1;
14541465
zend_regset regset;
14551466
zend_reg reg;
14561467

1468+
if ((current->flags & ZREG_LAST_USE) && !range->next) {
1469+
last_use_line = range->end;
1470+
}
14571471
if (ssa->ops[line].op1_def == current->ssa_var ||
14581472
ssa->ops[line].op2_def == current->ssa_var ||
14591473
ssa->ops[line].result_def == current->ssa_var) {
@@ -1463,7 +1477,7 @@ static int zend_jit_try_allocate_free_reg(const zend_op_array *op_array, zend_ss
14631477
regset = zend_jit_get_scratch_regset(
14641478
op_array->opcodes + line,
14651479
ssa->ops + line,
1466-
op_array, ssa, current->ssa_var);
1480+
op_array, ssa, current->ssa_var, line == last_use_line);
14671481
ZEND_REGSET_FOREACH(regset, reg) {
14681482
if (line < freeUntilPos[reg]) {
14691483
freeUntilPos[reg] = line;
@@ -1717,6 +1731,22 @@ static zend_lifetime_interval** zend_jit_allocate_registers(const zend_op_array
17171731
goto failure;
17181732
}
17191733

1734+
if (list) {
1735+
/* Set ZREG_LAST_USE flags */
1736+
ival = list;
1737+
while (ival) {
1738+
zend_life_range *range = &ival->range;
1739+
1740+
while (range->next) {
1741+
range = range->next;
1742+
}
1743+
if (zend_ssa_is_last_use(op_array, ssa, ival->ssa_var, range->end)) {
1744+
ival->flags |= ZREG_LAST_USE;
1745+
}
1746+
ival = ival->list_next;
1747+
}
1748+
}
1749+
17201750
if (list) {
17211751
if (ZCG(accel_directives).jit_debug & ZEND_JIT_DEBUG_REG_ALLOC) {
17221752
fprintf(stderr, "Live Ranges \"%s\"\n", op_array->function_name ? ZSTR_VAL(op_array->function_name) : "[main]");
@@ -1733,10 +1763,10 @@ static zend_lifetime_interval** zend_jit_allocate_registers(const zend_op_array
17331763
fprintf(stderr, ", %u-%u", range->start, range->end);
17341764
range = range->next;
17351765
}
1736-
if (ival->load) {
1766+
if (ival->flags & ZREG_LOAD) {
17371767
fprintf(stderr, " load");
17381768
}
1739-
if (ival->store) {
1769+
if (ival->flags & ZREG_STORE) {
17401770
fprintf(stderr, " store");
17411771
}
17421772
if (ival->hint) {
@@ -1781,13 +1811,13 @@ static zend_lifetime_interval** zend_jit_allocate_registers(const zend_op_array
17811811
src = phi->sources[0];
17821812
if (intervals[i]) {
17831813
if (!intervals[src]) {
1784-
intervals[i]->load = 1;
1814+
intervals[i]->flags |= ZREG_LOAD;
17851815
} else if (intervals[i]->reg != intervals[src]->reg) {
1786-
intervals[i]->load = 1;
1787-
intervals[src]->store = 1;
1816+
intervals[i]->flags |= ZREG_LOAD;
1817+
intervals[src]->flags |= ZREG_STORE;
17881818
}
17891819
} else if (intervals[src]) {
1790-
intervals[src]->store = 1;
1820+
intervals[src]->flags |= ZREG_STORE;
17911821
}
17921822
}
17931823
} else {
@@ -1815,7 +1845,7 @@ static zend_lifetime_interval** zend_jit_allocate_registers(const zend_op_array
18151845
}
18161846
if (need_move) {
18171847
if (intervals[i]) {
1818-
intervals[i]->load = 1;
1848+
intervals[i]->flags |= ZREG_LOAD;
18191849
}
18201850
for (k = 0; k < ssa->cfg.blocks[phi->block].predecessors_count; k++) {
18211851
src = phi->sources[k];
@@ -1827,7 +1857,7 @@ static zend_lifetime_interval** zend_jit_allocate_registers(const zend_op_array
18271857
src = ssa->vars[src].definition_phi->sources[0];
18281858
}
18291859
if (intervals[src]) {
1830-
intervals[src]->store = 1;
1860+
intervals[src]->flags |= ZREG_STORE;
18311861
}
18321862
}
18331863
}
@@ -1838,15 +1868,15 @@ static zend_lifetime_interval** zend_jit_allocate_registers(const zend_op_array
18381868
/* Remove useless register allocation */
18391869
for (i = 0; i < ssa->vars_count; i++) {
18401870
if (intervals[i] &&
1841-
(intervals[i]->load ||
1842-
(intervals[i]->store && ssa->vars[i].definition >= 0)) &&
1871+
((intervals[i]->flags & ZREG_LOAD) ||
1872+
((intervals[i]->flags & ZREG_STORE) && ssa->vars[i].definition >= 0)) &&
18431873
ssa->vars[i].use_chain < 0) {
18441874
zend_bool may_remove = 1;
18451875
zend_ssa_phi *phi = ssa->vars[i].phi_use_chain;
18461876

18471877
while (phi) {
18481878
if (intervals[phi->ssa_var] &&
1849-
!intervals[phi->ssa_var]->load) {
1879+
!(intervals[phi->ssa_var]->flags & ZREG_LOAD)) {
18501880
may_remove = 0;
18511881
break;
18521882
}
@@ -1860,16 +1890,16 @@ static zend_lifetime_interval** zend_jit_allocate_registers(const zend_op_array
18601890
/* Remove intervals used once */
18611891
for (i = 0; i < ssa->vars_count; i++) {
18621892
if (intervals[i] &&
1863-
intervals[i]->load &&
1864-
intervals[i]->store &&
1893+
(intervals[i]->flags & ZREG_LOAD) &&
1894+
(intervals[i]->flags & ZREG_STORE) &&
18651895
(ssa->vars[i].use_chain < 0 ||
18661896
zend_ssa_next_use(ssa->ops, i, ssa->vars[i].use_chain) < 0)) {
18671897
zend_bool may_remove = 1;
18681898
zend_ssa_phi *phi = ssa->vars[i].phi_use_chain;
18691899

18701900
while (phi) {
18711901
if (intervals[phi->ssa_var] &&
1872-
!intervals[phi->ssa_var]->load) {
1902+
!(intervals[phi->ssa_var]->flags & ZREG_LOAD)) {
18731903
may_remove = 0;
18741904
break;
18751905
}
@@ -1899,10 +1929,10 @@ static zend_lifetime_interval** zend_jit_allocate_registers(const zend_op_array
18991929
range = range->next;
19001930
}
19011931
fprintf(stderr, " (%s)", zend_reg_name[ival->reg]);
1902-
if (ival->load) {
1932+
if (ival->flags & ZREG_LOAD) {
19031933
fprintf(stderr, " load");
19041934
}
1905-
if (ival->store) {
1935+
if (ival->flags & ZREG_STORE) {
19061936
fprintf(stderr, " store");
19071937
}
19081938
if (ival->hint) {
@@ -2131,13 +2161,13 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
21312161
zend_lifetime_interval *ival = ra[phi->ssa_var];
21322162

21332163
if (ival) {
2134-
if (ival->load) {
2164+
if (ival->flags & ZREG_LOAD) {
21352165
ZEND_ASSERT(ival->reg != ZREG_NONE);
21362166

21372167
if (!zend_jit_load_var(&dasm_state, ssa->var_info[phi->ssa_var].type, ssa->vars[phi->ssa_var].var, ival->reg)) {
21382168
goto jit_failure;
21392169
}
2140-
} else if (ival->store) {
2170+
} else if (ival->flags & ZREG_STORE) {
21412171
ZEND_ASSERT(ival->reg != ZREG_NONE);
21422172

21432173
if (!zend_jit_store_var(&dasm_state, ssa->var_info[phi->ssa_var].type, ssa->vars[phi->ssa_var].var, ival->reg)) {

ext/opcache/jit/zend_jit.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,24 @@ struct _zend_life_range {
105105
zend_life_range *next;
106106
};
107107

108+
#define ZREG_FLAGS_SHIFT 8
109+
110+
#define ZREG_STORE (1<<0)
111+
#define ZREG_LOAD (1<<1)
112+
#define ZREG_LAST_USE (1<<2)
113+
#define ZREG_SPLIT (1<<3)
114+
108115
struct _zend_lifetime_interval {
109116
int ssa_var;
110-
int8_t reg;
111-
zend_bool split;
112-
zend_bool store;
113-
zend_bool load;
117+
union {
118+
struct {
119+
ZEND_ENDIAN_LOHI_3(
120+
int8_t reg,
121+
uint8_t flags,
122+
uint16_t reserved
123+
)};
124+
uint32_t reg_flags;
125+
};
114126
zend_life_range range;
115127
zend_lifetime_interval *hint;
116128
zend_lifetime_interval *used_as_hint;

ext/opcache/jit/zend_jit_x86.dasc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11114,7 +11114,7 @@ static zend_bool zend_needs_extra_reg_for_const(const zend_op_array *op_array, c
1111411114
return 0;
1111511115
}
1111611116

11117-
static zend_regset zend_jit_get_scratch_regset(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, int current_var)
11117+
static zend_regset zend_jit_get_scratch_regset(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, int current_var, zend_bool last_use)
1111811118
{
1111911119
uint32_t op1_info, op2_info, res_info;
1112011120
zend_regset regset = ZEND_REGSET_SCRATCH;
@@ -11222,7 +11222,7 @@ static zend_regset zend_jit_get_scratch_regset(const zend_op *opline, const zend
1122211222
regset = ZEND_REGSET_EMPTY;
1122311223
if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG)) {
1122411224
if (ssa_op->result_def != current_var &&
11225-
(ssa_op->op1_use != current_var || !zend_ssa_is_last_use(op_array, ssa, current_var, ssa_op - ssa->ops))) {
11225+
(ssa_op->op1_use != current_var || !last_use)) {
1122611226
ZEND_REGSET_INCL(regset, ZREG_R0);
1122711227
}
1122811228
res_info = OP1_INFO();
@@ -11244,14 +11244,14 @@ static zend_regset zend_jit_get_scratch_regset(const zend_op *opline, const zend
1124411244
} else {
1124511245
ZEND_REGSET_INCL(regset, ZREG_XMM0);
1124611246
if (ssa_op->result_def != current_var &&
11247-
(ssa_op->op1_use != current_var || !zend_ssa_is_last_use(op_array, ssa, current_var, ssa_op - ssa->ops))) {
11247+
(ssa_op->op1_use != current_var || !last_use)) {
1124811248
ZEND_REGSET_INCL(regset, ZREG_XMM1);
1124911249
}
1125011250
}
1125111251
}
1125211252
if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_DOUBLE)) {
1125311253
if (ssa_op->result_def != current_var &&
11254-
(ssa_op->op1_use != current_var || !zend_ssa_is_last_use(op_array, ssa, current_var, ssa_op - ssa->ops))) {
11254+
(ssa_op->op1_use != current_var || !last_use)) {
1125511255
ZEND_REGSET_INCL(regset, ZREG_XMM0);
1125611256
}
1125711257
}
@@ -11270,7 +11270,7 @@ static zend_regset zend_jit_get_scratch_regset(const zend_op *opline, const zend
1127011270
!(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) {
1127111271
regset = ZEND_REGSET_EMPTY;
1127211272
if (ssa_op->result_def != current_var &&
11273-
(ssa_op->op1_use != current_var || !zend_ssa_is_last_use(op_array, ssa, current_var, ssa_op - ssa->ops))) {
11273+
(ssa_op->op1_use != current_var || !last_use)) {
1127411274
ZEND_REGSET_INCL(regset, ZREG_R0);
1127511275
}
1127611276
}
@@ -11283,7 +11283,7 @@ static zend_regset zend_jit_get_scratch_regset(const zend_op *opline, const zend
1128311283
!(op2_info & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)-MAY_BE_LONG))) {
1128411284
regset = ZEND_REGSET_EMPTY;
1128511285
if (ssa_op->result_def != current_var &&
11286-
(ssa_op->op1_use != current_var || !zend_ssa_is_last_use(op_array, ssa, current_var, ssa_op - ssa->ops))) {
11286+
(ssa_op->op1_use != current_var || !last_use)) {
1128711287
ZEND_REGSET_INCL(regset, ZREG_R0);
1128811288
}
1128911289
if (opline->op2_type != IS_CONST && ssa_op->op2_use != current_var) {
@@ -11301,7 +11301,7 @@ static zend_regset zend_jit_get_scratch_regset(const zend_op *opline, const zend
1130111301
Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_LONG &&
1130211302
zend_long_is_power_of_two(Z_LVAL_P(RT_CONSTANT(opline, opline->op2)))) {
1130311303
if (ssa_op->result_def != current_var &&
11304-
(ssa_op->op1_use != current_var || !zend_ssa_is_last_use(op_array, ssa, current_var, ssa_op - ssa->ops))) {
11304+
(ssa_op->op1_use != current_var || !last_use)) {
1130511305
ZEND_REGSET_INCL(regset, ZREG_R0);
1130611306
}
1130711307
} else {

ext/opcache/jit/zend_jit_x86.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,9 @@ typedef uintptr_t zend_jit_addr;
249249
#define OP_REG(ssa_op, op) \
250250
(ra && ssa_op->op >= 0 && ra[ssa_op->op] ? \
251251
OP_REG_EX(ra[ssa_op->op]->reg, \
252-
ra[ssa_op->op]->store, \
253-
ra[ssa_op->op]->load, \
254-
zend_ssa_is_last_use(op_array, ssa, ssa_op->op, ssa_op - ssa->ops) \
252+
(ra[ssa_op->op]->flags & ZREG_STORE), \
253+
(ra[ssa_op->op]->flags & ZREG_LOAD), \
254+
zend_ival_is_last_use(ra[ssa_op->op], ssa_op - ssa->ops) \
255255
) : ZREG_NONE)
256256

257257
static zend_always_inline zend_jit_addr _zend_jit_decode_op(zend_uchar op_type, znode_op op, const zend_op *opline, zend_reg reg)

0 commit comments

Comments
 (0)