diff options
Diffstat (limited to 'src/lj_opt_mem.c')
-rw-r--r-- | src/lj_opt_mem.c | 195 |
1 files changed, 136 insertions, 59 deletions
diff --git a/src/lj_opt_mem.c b/src/lj_opt_mem.c index feec6bb7..cafa0523 100644 --- a/src/lj_opt_mem.c +++ b/src/lj_opt_mem.c | |||
@@ -17,12 +17,14 @@ | |||
17 | #include "lj_ir.h" | 17 | #include "lj_ir.h" |
18 | #include "lj_jit.h" | 18 | #include "lj_jit.h" |
19 | #include "lj_iropt.h" | 19 | #include "lj_iropt.h" |
20 | #include "lj_ircall.h" | ||
21 | #include "lj_dispatch.h" | ||
20 | 22 | ||
21 | /* Some local macros to save typing. Undef'd at the end. */ | 23 | /* Some local macros to save typing. Undef'd at the end. */ |
22 | #define IR(ref) (&J->cur.ir[(ref)]) | 24 | #define IR(ref) (&J->cur.ir[(ref)]) |
23 | #define fins (&J->fold.ins) | 25 | #define fins (&J->fold.ins) |
24 | #define fleft (&J->fold.left) | 26 | #define fleft (J->fold.left) |
25 | #define fright (&J->fold.right) | 27 | #define fright (J->fold.right) |
26 | 28 | ||
27 | /* | 29 | /* |
28 | ** Caveat #1: return value is not always a TRef -- only use with tref_ref(). | 30 | ** Caveat #1: return value is not always a TRef -- only use with tref_ref(). |
@@ -55,8 +57,8 @@ static AliasRet aa_table(jit_State *J, IRRef ta, IRRef tb) | |||
55 | { | 57 | { |
56 | IRIns *taba = IR(ta), *tabb = IR(tb); | 58 | IRIns *taba = IR(ta), *tabb = IR(tb); |
57 | int newa, newb; | 59 | int newa, newb; |
58 | lua_assert(ta != tb); | 60 | lj_assertJ(ta != tb, "bad usage"); |
59 | lua_assert(irt_istab(taba->t) && irt_istab(tabb->t)); | 61 | lj_assertJ(irt_istab(taba->t) && irt_istab(tabb->t), "bad usage"); |
60 | /* Disambiguate new allocations. */ | 62 | /* Disambiguate new allocations. */ |
61 | newa = (taba->o == IR_TNEW || taba->o == IR_TDUP); | 63 | newa = (taba->o == IR_TNEW || taba->o == IR_TDUP); |
62 | newb = (tabb->o == IR_TNEW || tabb->o == IR_TDUP); | 64 | newb = (tabb->o == IR_TNEW || tabb->o == IR_TDUP); |
@@ -70,6 +72,34 @@ static AliasRet aa_table(jit_State *J, IRRef ta, IRRef tb) | |||
70 | return aa_escape(J, taba, tabb); | 72 | return aa_escape(J, taba, tabb); |
71 | } | 73 | } |
72 | 74 | ||
75 | /* Check whether there's no aliasing table.clear. */ | ||
76 | static int fwd_aa_tab_clear(jit_State *J, IRRef lim, IRRef ta) | ||
77 | { | ||
78 | IRRef ref = J->chain[IR_CALLS]; | ||
79 | while (ref > lim) { | ||
80 | IRIns *calls = IR(ref); | ||
81 | if (calls->op2 == IRCALL_lj_tab_clear && | ||
82 | (ta == calls->op1 || aa_table(J, ta, calls->op1) != ALIAS_NO)) | ||
83 | return 0; /* Conflict. */ | ||
84 | ref = calls->prev; | ||
85 | } | ||
86 | return 1; /* No conflict. Can safely FOLD/CSE. */ | ||
87 | } | ||
88 | |||
89 | /* Check whether there's no aliasing NEWREF/table.clear for the left operand. */ | ||
90 | int LJ_FASTCALL lj_opt_fwd_tptr(jit_State *J, IRRef lim) | ||
91 | { | ||
92 | IRRef ta = fins->op1; | ||
93 | IRRef ref = J->chain[IR_NEWREF]; | ||
94 | while (ref > lim) { | ||
95 | IRIns *newref = IR(ref); | ||
96 | if (ta == newref->op1 || aa_table(J, ta, newref->op1) != ALIAS_NO) | ||
97 | return 0; /* Conflict. */ | ||
98 | ref = newref->prev; | ||
99 | } | ||
100 | return fwd_aa_tab_clear(J, lim, ta); | ||
101 | } | ||
102 | |||
73 | /* Alias analysis for array and hash access using key-based disambiguation. */ | 103 | /* Alias analysis for array and hash access using key-based disambiguation. */ |
74 | static AliasRet aa_ahref(jit_State *J, IRIns *refa, IRIns *refb) | 104 | static AliasRet aa_ahref(jit_State *J, IRIns *refa, IRIns *refb) |
75 | { | 105 | { |
@@ -98,7 +128,7 @@ static AliasRet aa_ahref(jit_State *J, IRIns *refa, IRIns *refb) | |||
98 | /* Disambiguate array references based on index arithmetic. */ | 128 | /* Disambiguate array references based on index arithmetic. */ |
99 | int32_t ofsa = 0, ofsb = 0; | 129 | int32_t ofsa = 0, ofsb = 0; |
100 | IRRef basea = ka, baseb = kb; | 130 | IRRef basea = ka, baseb = kb; |
101 | lua_assert(refb->o == IR_AREF); | 131 | lj_assertJ(refb->o == IR_AREF, "expected AREF"); |
102 | /* Gather base and offset from t[base] or t[base+-ofs]. */ | 132 | /* Gather base and offset from t[base] or t[base+-ofs]. */ |
103 | if (keya->o == IR_ADD && irref_isk(keya->op2)) { | 133 | if (keya->o == IR_ADD && irref_isk(keya->op2)) { |
104 | basea = keya->op1; | 134 | basea = keya->op1; |
@@ -116,8 +146,9 @@ static AliasRet aa_ahref(jit_State *J, IRIns *refa, IRIns *refb) | |||
116 | return ALIAS_NO; /* t[base+-o1] vs. t[base+-o2] and o1 != o2. */ | 146 | return ALIAS_NO; /* t[base+-o1] vs. t[base+-o2] and o1 != o2. */ |
117 | } else { | 147 | } else { |
118 | /* Disambiguate hash references based on the type of their keys. */ | 148 | /* Disambiguate hash references based on the type of their keys. */ |
119 | lua_assert((refa->o==IR_HREF || refa->o==IR_HREFK || refa->o==IR_NEWREF) && | 149 | lj_assertJ((refa->o==IR_HREF || refa->o==IR_HREFK || refa->o==IR_NEWREF) && |
120 | (refb->o==IR_HREF || refb->o==IR_HREFK || refb->o==IR_NEWREF)); | 150 | (refb->o==IR_HREF || refb->o==IR_HREFK || refb->o==IR_NEWREF), |
151 | "bad xREF IR op %d or %d", refa->o, refb->o); | ||
121 | if (!irt_sametype(keya->t, keyb->t)) | 152 | if (!irt_sametype(keya->t, keyb->t)) |
122 | return ALIAS_NO; /* Different key types. */ | 153 | return ALIAS_NO; /* Different key types. */ |
123 | } | 154 | } |
@@ -151,7 +182,8 @@ static TRef fwd_ahload(jit_State *J, IRRef xref) | |||
151 | IRIns *ir = (xr->o == IR_HREFK || xr->o == IR_AREF) ? IR(xr->op1) : xr; | 182 | IRIns *ir = (xr->o == IR_HREFK || xr->o == IR_AREF) ? IR(xr->op1) : xr; |
152 | IRRef tab = ir->op1; | 183 | IRRef tab = ir->op1; |
153 | ir = IR(tab); | 184 | ir = IR(tab); |
154 | if (ir->o == IR_TNEW || (ir->o == IR_TDUP && irref_isk(xr->op2))) { | 185 | if ((ir->o == IR_TNEW || (ir->o == IR_TDUP && irref_isk(xr->op2))) && |
186 | fwd_aa_tab_clear(J, tab, tab)) { | ||
155 | /* A NEWREF with a number key may end up pointing to the array part. | 187 | /* A NEWREF with a number key may end up pointing to the array part. |
156 | ** But it's referenced from HSTORE and not found in the ASTORE chain. | 188 | ** But it's referenced from HSTORE and not found in the ASTORE chain. |
157 | ** For now simply consider this a conflict without forwarding anything. | 189 | ** For now simply consider this a conflict without forwarding anything. |
@@ -191,7 +223,8 @@ static TRef fwd_ahload(jit_State *J, IRRef xref) | |||
191 | if (key->o == IR_KSLOT) key = IR(key->op1); | 223 | if (key->o == IR_KSLOT) key = IR(key->op1); |
192 | lj_ir_kvalue(J->L, &keyv, key); | 224 | lj_ir_kvalue(J->L, &keyv, key); |
193 | tv = lj_tab_get(J->L, ir_ktab(IR(ir->op1)), &keyv); | 225 | tv = lj_tab_get(J->L, ir_ktab(IR(ir->op1)), &keyv); |
194 | lua_assert(itype2irt(tv) == irt_type(fins->t)); | 226 | lj_assertJ(itype2irt(tv) == irt_type(fins->t), |
227 | "mismatched type in constant table"); | ||
195 | if (irt_isnum(fins->t)) | 228 | if (irt_isnum(fins->t)) |
196 | return lj_ir_knum_u64(J, tv->u64); | 229 | return lj_ir_knum_u64(J, tv->u64); |
197 | else if (LJ_DUALNUM && irt_isint(fins->t)) | 230 | else if (LJ_DUALNUM && irt_isint(fins->t)) |
@@ -265,7 +298,7 @@ TRef LJ_FASTCALL lj_opt_fwd_hrefk(jit_State *J) | |||
265 | while (ref > tab) { | 298 | while (ref > tab) { |
266 | IRIns *newref = IR(ref); | 299 | IRIns *newref = IR(ref); |
267 | if (tab == newref->op1) { | 300 | if (tab == newref->op1) { |
268 | if (fright->op1 == newref->op2) | 301 | if (fright->op1 == newref->op2 && fwd_aa_tab_clear(J, ref, tab)) |
269 | return ref; /* Forward from NEWREF. */ | 302 | return ref; /* Forward from NEWREF. */ |
270 | else | 303 | else |
271 | goto docse; | 304 | goto docse; |
@@ -275,7 +308,7 @@ TRef LJ_FASTCALL lj_opt_fwd_hrefk(jit_State *J) | |||
275 | ref = newref->prev; | 308 | ref = newref->prev; |
276 | } | 309 | } |
277 | /* No conflicting NEWREF: key location unchanged for HREFK of TDUP. */ | 310 | /* No conflicting NEWREF: key location unchanged for HREFK of TDUP. */ |
278 | if (IR(tab)->o == IR_TDUP) | 311 | if (IR(tab)->o == IR_TDUP && fwd_aa_tab_clear(J, tab, tab)) |
279 | fins->t.irt &= ~IRT_GUARD; /* Drop HREFK guard. */ | 312 | fins->t.irt &= ~IRT_GUARD; /* Drop HREFK guard. */ |
280 | docse: | 313 | docse: |
281 | return CSEFOLD; | 314 | return CSEFOLD; |
@@ -309,20 +342,6 @@ int LJ_FASTCALL lj_opt_fwd_href_nokey(jit_State *J) | |||
309 | return 1; /* No conflict. Can fold to niltv. */ | 342 | return 1; /* No conflict. Can fold to niltv. */ |
310 | } | 343 | } |
311 | 344 | ||
312 | /* Check whether there's no aliasing NEWREF for the left operand. */ | ||
313 | int LJ_FASTCALL lj_opt_fwd_tptr(jit_State *J, IRRef lim) | ||
314 | { | ||
315 | IRRef ta = fins->op1; | ||
316 | IRRef ref = J->chain[IR_NEWREF]; | ||
317 | while (ref > lim) { | ||
318 | IRIns *newref = IR(ref); | ||
319 | if (ta == newref->op1 || aa_table(J, ta, newref->op1) != ALIAS_NO) | ||
320 | return 0; /* Conflict. */ | ||
321 | ref = newref->prev; | ||
322 | } | ||
323 | return 1; /* No conflict. Can safely FOLD/CSE. */ | ||
324 | } | ||
325 | |||
326 | /* ASTORE/HSTORE elimination. */ | 345 | /* ASTORE/HSTORE elimination. */ |
327 | TRef LJ_FASTCALL lj_opt_dse_ahstore(jit_State *J) | 346 | TRef LJ_FASTCALL lj_opt_dse_ahstore(jit_State *J) |
328 | { | 347 | { |
@@ -346,9 +365,12 @@ TRef LJ_FASTCALL lj_opt_dse_ahstore(jit_State *J) | |||
346 | /* Different value: try to eliminate the redundant store. */ | 365 | /* Different value: try to eliminate the redundant store. */ |
347 | if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */ | 366 | if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */ |
348 | IRIns *ir; | 367 | IRIns *ir; |
349 | /* Check for any intervening guards (includes conflicting loads). */ | 368 | /* Check for any intervening guards (includes conflicting loads). |
369 | ** Note that lj_tab_keyindex and lj_vm_next don't need guards, | ||
370 | ** since they are followed by at least one guarded VLOAD. | ||
371 | */ | ||
350 | for (ir = IR(J->cur.nins-1); ir > store; ir--) | 372 | for (ir = IR(J->cur.nins-1); ir > store; ir--) |
351 | if (irt_isguard(ir->t) || ir->o == IR_CALLL) | 373 | if (irt_isguard(ir->t) || ir->o == IR_ALEN) |
352 | goto doemit; /* No elimination possible. */ | 374 | goto doemit; /* No elimination possible. */ |
353 | /* Remove redundant store from chain and replace with NOP. */ | 375 | /* Remove redundant store from chain and replace with NOP. */ |
354 | *refp = store->prev; | 376 | *refp = store->prev; |
@@ -363,6 +385,67 @@ doemit: | |||
363 | return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ | 385 | return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ |
364 | } | 386 | } |
365 | 387 | ||
388 | /* ALEN forwarding. */ | ||
389 | TRef LJ_FASTCALL lj_opt_fwd_alen(jit_State *J) | ||
390 | { | ||
391 | IRRef tab = fins->op1; /* Table reference. */ | ||
392 | IRRef lim = tab; /* Search limit. */ | ||
393 | IRRef ref; | ||
394 | |||
395 | /* Search for conflicting HSTORE with numeric key. */ | ||
396 | ref = J->chain[IR_HSTORE]; | ||
397 | while (ref > lim) { | ||
398 | IRIns *store = IR(ref); | ||
399 | IRIns *href = IR(store->op1); | ||
400 | IRIns *key = IR(href->op2); | ||
401 | if (irt_isnum(key->o == IR_KSLOT ? IR(key->op1)->t : key->t)) { | ||
402 | lim = ref; /* Conflicting store found, limits search for ALEN. */ | ||
403 | break; | ||
404 | } | ||
405 | ref = store->prev; | ||
406 | } | ||
407 | |||
408 | /* Try to find a matching ALEN. */ | ||
409 | ref = J->chain[IR_ALEN]; | ||
410 | while (ref > lim) { | ||
411 | /* CSE for ALEN only depends on the table, not the hint. */ | ||
412 | if (IR(ref)->op1 == tab) { | ||
413 | IRRef sref; | ||
414 | |||
415 | /* Search for aliasing table.clear. */ | ||
416 | if (!fwd_aa_tab_clear(J, ref, tab)) | ||
417 | break; | ||
418 | |||
419 | /* Search for hint-forwarding or conflicting store. */ | ||
420 | sref = J->chain[IR_ASTORE]; | ||
421 | while (sref > ref) { | ||
422 | IRIns *store = IR(sref); | ||
423 | IRIns *aref = IR(store->op1); | ||
424 | IRIns *fref = IR(aref->op1); | ||
425 | if (tab == fref->op1) { /* ASTORE to the same table. */ | ||
426 | /* Detect t[#t+1] = x idiom for push. */ | ||
427 | IRIns *idx = IR(aref->op2); | ||
428 | if (!irt_isnil(store->t) && | ||
429 | idx->o == IR_ADD && idx->op1 == ref && | ||
430 | IR(idx->op2)->o == IR_KINT && IR(idx->op2)->i == 1) { | ||
431 | /* Note: this requires an extra PHI check in loop unroll. */ | ||
432 | fins->op2 = aref->op2; /* Set ALEN hint. */ | ||
433 | } | ||
434 | goto doemit; /* Conflicting store, possibly giving a hint. */ | ||
435 | } else if (aa_table(J, tab, fref->op1) == ALIAS_NO) { | ||
436 | goto doemit; /* Conflicting store. */ | ||
437 | } | ||
438 | sref = store->prev; | ||
439 | } | ||
440 | |||
441 | return ref; /* Plain ALEN forwarding. */ | ||
442 | } | ||
443 | ref = IR(ref)->prev; | ||
444 | } | ||
445 | doemit: | ||
446 | return EMITFOLD; | ||
447 | } | ||
448 | |||
366 | /* -- ULOAD forwarding ---------------------------------------------------- */ | 449 | /* -- ULOAD forwarding ---------------------------------------------------- */ |
367 | 450 | ||
368 | /* The current alias analysis for upvalues is very simplistic. It only | 451 | /* The current alias analysis for upvalues is very simplistic. It only |
@@ -412,7 +495,6 @@ TRef LJ_FASTCALL lj_opt_fwd_uload(jit_State *J) | |||
412 | 495 | ||
413 | cselim: | 496 | cselim: |
414 | /* Try to find a matching load. Below the conflicting store, if any. */ | 497 | /* Try to find a matching load. Below the conflicting store, if any. */ |
415 | |||
416 | ref = J->chain[IR_ULOAD]; | 498 | ref = J->chain[IR_ULOAD]; |
417 | while (ref > lim) { | 499 | while (ref > lim) { |
418 | IRIns *ir = IR(ref); | 500 | IRIns *ir = IR(ref); |
@@ -542,8 +624,9 @@ TRef LJ_FASTCALL lj_opt_dse_fstore(jit_State *J) | |||
542 | goto doemit; | 624 | goto doemit; |
543 | break; /* Otherwise continue searching. */ | 625 | break; /* Otherwise continue searching. */ |
544 | case ALIAS_MUST: | 626 | case ALIAS_MUST: |
545 | if (store->op2 == val) /* Same value: drop the new store. */ | 627 | if (store->op2 == val && |
546 | return DROPFOLD; | 628 | !(xr->op2 >= IRFL_SBUF_W && xr->op2 <= IRFL_SBUF_R)) |
629 | return DROPFOLD; /* Same value: drop the new store. */ | ||
547 | /* Different value: try to eliminate the redundant store. */ | 630 | /* Different value: try to eliminate the redundant store. */ |
548 | if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */ | 631 | if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */ |
549 | IRIns *ir; | 632 | IRIns *ir; |
@@ -564,6 +647,29 @@ doemit: | |||
564 | return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ | 647 | return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ |
565 | } | 648 | } |
566 | 649 | ||
650 | /* Check whether there's no aliasing buffer op between IRFL_SBUF_*. */ | ||
651 | int LJ_FASTCALL lj_opt_fwd_sbuf(jit_State *J, IRRef lim) | ||
652 | { | ||
653 | IRRef ref; | ||
654 | if (J->chain[IR_BUFPUT] > lim) | ||
655 | return 0; /* Conflict. */ | ||
656 | ref = J->chain[IR_CALLS]; | ||
657 | while (ref > lim) { | ||
658 | IRIns *ir = IR(ref); | ||
659 | if (ir->op2 >= IRCALL_lj_strfmt_putint && ir->op2 < IRCALL_lj_buf_tostr) | ||
660 | return 0; /* Conflict. */ | ||
661 | ref = ir->prev; | ||
662 | } | ||
663 | ref = J->chain[IR_CALLL]; | ||
664 | while (ref > lim) { | ||
665 | IRIns *ir = IR(ref); | ||
666 | if (ir->op2 >= IRCALL_lj_strfmt_putint && ir->op2 < IRCALL_lj_buf_tostr) | ||
667 | return 0; /* Conflict. */ | ||
668 | ref = ir->prev; | ||
669 | } | ||
670 | return 1; /* No conflict. Can safely FOLD/CSE. */ | ||
671 | } | ||
672 | |||
567 | /* -- XLOAD forwarding and XSTORE elimination ----------------------------- */ | 673 | /* -- XLOAD forwarding and XSTORE elimination ----------------------------- */ |
568 | 674 | ||
569 | /* Find cdata allocation for a reference (if any). */ | 675 | /* Find cdata allocation for a reference (if any). */ |
@@ -815,35 +921,6 @@ doemit: | |||
815 | return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ | 921 | return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ |
816 | } | 922 | } |
817 | 923 | ||
818 | /* -- Forwarding of lj_tab_len -------------------------------------------- */ | ||
819 | |||
820 | /* This is rather simplistic right now, but better than nothing. */ | ||
821 | TRef LJ_FASTCALL lj_opt_fwd_tab_len(jit_State *J) | ||
822 | { | ||
823 | IRRef tab = fins->op1; /* Table reference. */ | ||
824 | IRRef lim = tab; /* Search limit. */ | ||
825 | IRRef ref; | ||
826 | |||
827 | /* Any ASTORE is a conflict and limits the search. */ | ||
828 | if (J->chain[IR_ASTORE] > lim) lim = J->chain[IR_ASTORE]; | ||
829 | |||
830 | /* Search for conflicting HSTORE with numeric key. */ | ||
831 | ref = J->chain[IR_HSTORE]; | ||
832 | while (ref > lim) { | ||
833 | IRIns *store = IR(ref); | ||
834 | IRIns *href = IR(store->op1); | ||
835 | IRIns *key = IR(href->op2); | ||
836 | if (irt_isnum(key->o == IR_KSLOT ? IR(key->op1)->t : key->t)) { | ||
837 | lim = ref; /* Conflicting store found, limits search for TLEN. */ | ||
838 | break; | ||
839 | } | ||
840 | ref = store->prev; | ||
841 | } | ||
842 | |||
843 | /* Try to find a matching load. Below the conflicting store, if any. */ | ||
844 | return lj_opt_cselim(J, lim); | ||
845 | } | ||
846 | |||
847 | /* -- ASTORE/HSTORE previous type analysis -------------------------------- */ | 924 | /* -- ASTORE/HSTORE previous type analysis -------------------------------- */ |
848 | 925 | ||
849 | /* Check whether the previous value for a table store is non-nil. | 926 | /* Check whether the previous value for a table store is non-nil. |