2 ** SINK: Allocation Sinking and Store Sinking.
3 ** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
16 #include "lj_target.h"
18 /* Some local macros to save typing. Undef'd at the end. */
19 #define IR(ref) (&J->cur.ir[(ref)])
21 /* Check whether the store ref points to an eligible allocation. */
22 static IRIns
*sink_checkalloc(jit_State
*J
, IRIns
*irs
)
24 IRIns
*ir
= IR(irs
->op1
);
25 if (!irref_isk(ir
->op2
))
26 return NULL
; /* Non-constant key. */
27 if (ir
->o
== IR_HREFK
|| ir
->o
== IR_AREF
)
29 else if (!(ir
->o
== IR_HREF
|| ir
->o
== IR_NEWREF
||
30 ir
->o
== IR_FREF
|| ir
->o
== IR_ADD
))
31 return NULL
; /* Unhandled reference type (for XSTORE). */
33 if (!(ir
->o
== IR_TNEW
|| ir
->o
== IR_TDUP
|| ir
->o
== IR_CNEW
))
34 return NULL
; /* Not an allocation. */
35 return ir
; /* Return allocation. */
38 /* Recursively check whether a value depends on a PHI. */
39 static int sink_phidep(jit_State
*J
, IRRef ref
, int *workp
)
42 if (!*workp
) return 1; /* Give up and pretend it does. */
44 if (irt_isphi(ir
->t
)) return 1;
45 if (ir
->op1
>= REF_FIRST
&& sink_phidep(J
, ir
->op1
, workp
)) return 1;
46 if (ir
->op2
>= REF_FIRST
&& sink_phidep(J
, ir
->op2
, workp
)) return 1;
50 /* Check whether a value is a sinkable PHI or loop-invariant. */
51 static int sink_checkphi(jit_State
*J
, IRIns
*ira
, IRRef ref
)
53 if (ref
>= REF_FIRST
) {
55 if (irt_isphi(ir
->t
) || (ir
->o
== IR_CONV
&& ir
->op2
== IRCONV_NUM_INT
&&
56 irt_isphi(IR(ir
->op1
)->t
))) {
58 return 1; /* Sinkable PHI. */
60 /* Otherwise the value must be loop-invariant. */
61 if (ref
< J
->loopref
) {
62 /* Check for PHI dependencies, but give up after reasonable effort. */
64 return !sink_phidep(J
, ref
, &work
);
66 return 0; /* Loop-variant. */
69 return 1; /* Constant (non-PHI). */
72 /* Mark non-sinkable allocations using single-pass backward propagation.
74 ** Roots for the marking process are:
75 ** - Some PHIs or snapshots (see below).
76 ** - Non-PHI, non-constant values stored to PHI allocations.
78 ** - Any remaining loads not eliminated by store-to-load forwarding.
79 ** - Stores with non-constant keys.
80 ** - All stored values.
82 static void sink_mark_ins(jit_State
*J
)
84 IRIns
*ir
, *irlast
= IR(J
->cur
.nins
-1);
85 for (ir
= irlast
; ; ir
--) {
88 return; /* Finished. */
89 case IR_ALOAD
: case IR_HLOAD
: case IR_XLOAD
: case IR_TBAR
: case IR_ALEN
:
90 irt_setmark(IR(ir
->op1
)->t
); /* Mark ref for remaining loads. */
93 if (irt_ismarked(ir
->t
) || ir
->op2
== IRFL_TAB_META
)
94 irt_setmark(IR(ir
->op1
)->t
); /* Mark table for remaining loads. */
96 case IR_ASTORE
: case IR_HSTORE
: case IR_FSTORE
: case IR_XSTORE
: {
97 IRIns
*ira
= sink_checkalloc(J
, ir
);
98 if (!ira
|| (irt_isphi(ira
->t
) && !sink_checkphi(J
, ira
, ir
->op2
)))
99 irt_setmark(IR(ir
->op1
)->t
); /* Mark ineligible ref. */
100 irt_setmark(IR(ir
->op2
)->t
); /* Mark stored value. */
105 if (irt_isphi(ir
->t
) &&
106 (!sink_checkphi(J
, ir
, ir
->op2
) ||
107 (LJ_32
&& ir
+1 < irlast
&& (ir
+1)->o
== IR_HIOP
&&
108 !sink_checkphi(J
, ir
, (ir
+1)->op2
))))
109 irt_setmark(ir
->t
); /* Mark ineligible allocation. */
113 irt_setmark(IR(ir
->op2
)->t
); /* Mark stored value. */
119 irt_setmark(IR(ir
->op1
)->t
); /* Mark (potentially) stored values. */
122 IRIns
*irl
= IR(ir
->op1
), *irr
= IR(ir
->op2
);
123 irl
->prev
= irr
->prev
= 0; /* Clear PHI value counts. */
124 if (irl
->o
== irr
->o
&&
125 (irl
->o
== IR_TNEW
|| irl
->o
== IR_TDUP
||
126 (LJ_HASFFI
&& (irl
->o
== IR_CNEW
|| irl
->o
== IR_CNEWI
))))
133 if (irt_ismarked(ir
->t
) || irt_isguard(ir
->t
)) { /* Propagate mark. */
134 if (ir
->op1
>= REF_FIRST
) irt_setmark(IR(ir
->op1
)->t
);
135 if (ir
->op2
>= REF_FIRST
) irt_setmark(IR(ir
->op2
)->t
);
142 /* Mark all instructions referenced by a snapshot. */
143 static void sink_mark_snap(jit_State
*J
, SnapShot
*snap
)
145 SnapEntry
*map
= &J
->cur
.snapmap
[snap
->mapofs
];
146 MSize n
, nent
= snap
->nent
;
147 for (n
= 0; n
< nent
; n
++) {
148 IRRef ref
= snap_ref(map
[n
]);
150 irt_setmark(IR(ref
)->t
);
154 /* Iteratively remark PHI refs with differing marks or PHI value counts. */
155 static void sink_remark_phi(jit_State
*J
)
161 for (ir
= IR(J
->cur
.nins
-1); ir
->o
== IR_PHI
; ir
--) {
162 IRIns
*irl
= IR(ir
->op1
), *irr
= IR(ir
->op2
);
163 if (!((irl
->t
.irt
^ irr
->t
.irt
) & IRT_MARK
) && irl
->prev
== irr
->prev
)
165 remark
|= (~(irl
->t
.irt
& irr
->t
.irt
) & IRT_MARK
);
166 irt_setmark(IR(ir
->op1
)->t
);
167 irt_setmark(IR(ir
->op2
)->t
);
172 /* Sweep instructions and tag sunken allocations and stores. */
173 static void sink_sweep_ins(jit_State
*J
)
175 IRIns
*ir
, *irbase
= IR(REF_BASE
);
176 for (ir
= IR(J
->cur
.nins
-1) ; ir
>= irbase
; ir
--) {
178 case IR_ASTORE
: case IR_HSTORE
: case IR_FSTORE
: case IR_XSTORE
: {
179 IRIns
*ira
= sink_checkalloc(J
, ir
);
180 if (ira
&& !irt_ismarked(ira
->t
)) {
181 int delta
= (int)(ir
- ira
);
182 ir
->prev
= REGSP(RID_SINK
, delta
> 255 ? 255 : delta
);
184 ir
->prev
= REGSP_INIT
;
189 if (!irt_ismarked(IR(ir
->op1
)->t
)) {
190 ir
->prev
= REGSP(RID_SINK
, 0);
192 irt_clearmark(ir
->t
);
193 ir
->prev
= REGSP_INIT
;
197 case IR_CNEW
: case IR_CNEWI
:
199 case IR_TNEW
: case IR_TDUP
:
200 if (!irt_ismarked(ir
->t
)) {
201 ir
->t
.irt
&= ~IRT_GUARD
;
202 ir
->prev
= REGSP(RID_SINK
, 0);
203 J
->cur
.sinktags
= 1; /* Signal present SINK tags to assembler. */
205 irt_clearmark(ir
->t
);
206 ir
->prev
= REGSP_INIT
;
210 IRIns
*ira
= IR(ir
->op2
);
211 if (!irt_ismarked(ira
->t
) &&
212 (ira
->o
== IR_TNEW
|| ira
->o
== IR_TDUP
||
213 (LJ_HASFFI
&& (ira
->o
== IR_CNEW
|| ira
->o
== IR_CNEWI
)))) {
214 ir
->prev
= REGSP(RID_SINK
, 0);
216 ir
->prev
= REGSP_INIT
;
221 irt_clearmark(ir
->t
);
222 ir
->prev
= REGSP_INIT
;
226 for (ir
= IR(J
->cur
.nk
); ir
< irbase
; ir
++) {
227 irt_clearmark(ir
->t
);
228 ir
->prev
= REGSP_INIT
;
229 /* The false-positive of irt_is64() for ASMREF_L (REF_NIL) is OK here. */
230 if (irt_is64(ir
->t
) && ir
->o
!= IR_KNULL
)
235 /* Allocation sinking and store sinking.
237 ** 1. Mark all non-sinkable allocations.
238 ** 2. Then sink all remaining allocations and the related stores.
240 void lj_opt_sink(jit_State
*J
)
242 const uint32_t need
= (JIT_F_OPT_SINK
|JIT_F_OPT_FWD
|
243 JIT_F_OPT_DCE
|JIT_F_OPT_CSE
|JIT_F_OPT_FOLD
);
244 if ((J
->flags
& need
) == need
&&
245 (J
->chain
[IR_TNEW
] || J
->chain
[IR_TDUP
] ||
246 (LJ_HASFFI
&& (J
->chain
[IR_CNEW
] || J
->chain
[IR_CNEWI
])))) {
248 sink_mark_snap(J
, &J
->cur
.snap
[J
->cur
.nsnap
-1]);