diff options
Diffstat (limited to 'src/lj_opt_mem.c')
-rw-r--r-- | src/lj_opt_mem.c | 192 |
1 files changed, 134 insertions, 58 deletions
diff --git a/src/lj_opt_mem.c b/src/lj_opt_mem.c index dc74a06d..351d958c 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 | ** Or a NEWREF may rehash the table and move unrelated number keys. | 189 | ** Or a NEWREF may rehash the table and move unrelated number keys. |
@@ -272,7 +304,7 @@ TRef LJ_FASTCALL lj_opt_fwd_hrefk(jit_State *J) | |||
272 | while (ref > tab) { | 304 | while (ref > tab) { |
273 | IRIns *newref = IR(ref); | 305 | IRIns *newref = IR(ref); |
274 | if (tab == newref->op1) { | 306 | if (tab == newref->op1) { |
275 | if (fright->op1 == newref->op2) | 307 | if (fright->op1 == newref->op2 && fwd_aa_tab_clear(J, ref, tab)) |
276 | return ref; /* Forward from NEWREF. */ | 308 | return ref; /* Forward from NEWREF. */ |
277 | else | 309 | else |
278 | goto docse; | 310 | goto docse; |
@@ -282,7 +314,7 @@ TRef LJ_FASTCALL lj_opt_fwd_hrefk(jit_State *J) | |||
282 | ref = newref->prev; | 314 | ref = newref->prev; |
283 | } | 315 | } |
284 | /* No conflicting NEWREF: key location unchanged for HREFK of TDUP. */ | 316 | /* No conflicting NEWREF: key location unchanged for HREFK of TDUP. */ |
285 | if (IR(tab)->o == IR_TDUP) | 317 | if (IR(tab)->o == IR_TDUP && fwd_aa_tab_clear(J, tab, tab)) |
286 | fins->t.irt &= ~IRT_GUARD; /* Drop HREFK guard. */ | 318 | fins->t.irt &= ~IRT_GUARD; /* Drop HREFK guard. */ |
287 | docse: | 319 | docse: |
288 | return CSEFOLD; | 320 | return CSEFOLD; |
@@ -316,20 +348,6 @@ int LJ_FASTCALL lj_opt_fwd_href_nokey(jit_State *J) | |||
316 | return 1; /* No conflict. Can fold to niltv. */ | 348 | return 1; /* No conflict. Can fold to niltv. */ |
317 | } | 349 | } |
318 | 350 | ||
319 | /* Check whether there's no aliasing NEWREF for the left operand. */ | ||
320 | int LJ_FASTCALL lj_opt_fwd_tptr(jit_State *J, IRRef lim) | ||
321 | { | ||
322 | IRRef ta = fins->op1; | ||
323 | IRRef ref = J->chain[IR_NEWREF]; | ||
324 | while (ref > lim) { | ||
325 | IRIns *newref = IR(ref); | ||
326 | if (ta == newref->op1 || aa_table(J, ta, newref->op1) != ALIAS_NO) | ||
327 | return 0; /* Conflict. */ | ||
328 | ref = newref->prev; | ||
329 | } | ||
330 | return 1; /* No conflict. Can safely FOLD/CSE. */ | ||
331 | } | ||
332 | |||
333 | /* ASTORE/HSTORE elimination. */ | 351 | /* ASTORE/HSTORE elimination. */ |
334 | TRef LJ_FASTCALL lj_opt_dse_ahstore(jit_State *J) | 352 | TRef LJ_FASTCALL lj_opt_dse_ahstore(jit_State *J) |
335 | { | 353 | { |
@@ -353,9 +371,12 @@ TRef LJ_FASTCALL lj_opt_dse_ahstore(jit_State *J) | |||
353 | /* Different value: try to eliminate the redundant store. */ | 371 | /* Different value: try to eliminate the redundant store. */ |
354 | if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */ | 372 | if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */ |
355 | IRIns *ir; | 373 | IRIns *ir; |
356 | /* Check for any intervening guards (includes conflicting loads). */ | 374 | /* Check for any intervening guards (includes conflicting loads). |
375 | ** Note that lj_tab_keyindex and lj_vm_next don't need guards, | ||
376 | ** since they are followed by at least one guarded VLOAD. | ||
377 | */ | ||
357 | for (ir = IR(J->cur.nins-1); ir > store; ir--) | 378 | for (ir = IR(J->cur.nins-1); ir > store; ir--) |
358 | if (irt_isguard(ir->t) || ir->o == IR_CALLL) | 379 | if (irt_isguard(ir->t) || ir->o == IR_ALEN) |
359 | goto doemit; /* No elimination possible. */ | 380 | goto doemit; /* No elimination possible. */ |
360 | /* Remove redundant store from chain and replace with NOP. */ | 381 | /* Remove redundant store from chain and replace with NOP. */ |
361 | *refp = store->prev; | 382 | *refp = store->prev; |
@@ -370,6 +391,67 @@ doemit: | |||
370 | return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ | 391 | return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ |
371 | } | 392 | } |
372 | 393 | ||
394 | /* ALEN forwarding. */ | ||
395 | TRef LJ_FASTCALL lj_opt_fwd_alen(jit_State *J) | ||
396 | { | ||
397 | IRRef tab = fins->op1; /* Table reference. */ | ||
398 | IRRef lim = tab; /* Search limit. */ | ||
399 | IRRef ref; | ||
400 | |||
401 | /* Search for conflicting HSTORE with numeric key. */ | ||
402 | ref = J->chain[IR_HSTORE]; | ||
403 | while (ref > lim) { | ||
404 | IRIns *store = IR(ref); | ||
405 | IRIns *href = IR(store->op1); | ||
406 | IRIns *key = IR(href->op2); | ||
407 | if (irt_isnum(key->o == IR_KSLOT ? IR(key->op1)->t : key->t)) { | ||
408 | lim = ref; /* Conflicting store found, limits search for ALEN. */ | ||
409 | break; | ||
410 | } | ||
411 | ref = store->prev; | ||
412 | } | ||
413 | |||
414 | /* Try to find a matching ALEN. */ | ||
415 | ref = J->chain[IR_ALEN]; | ||
416 | while (ref > lim) { | ||
417 | /* CSE for ALEN only depends on the table, not the hint. */ | ||
418 | if (IR(ref)->op1 == tab) { | ||
419 | IRRef sref; | ||
420 | |||
421 | /* Search for aliasing table.clear. */ | ||
422 | if (!fwd_aa_tab_clear(J, ref, tab)) | ||
423 | break; | ||
424 | |||
425 | /* Search for hint-forwarding or conflicting store. */ | ||
426 | sref = J->chain[IR_ASTORE]; | ||
427 | while (sref > ref) { | ||
428 | IRIns *store = IR(sref); | ||
429 | IRIns *aref = IR(store->op1); | ||
430 | IRIns *fref = IR(aref->op1); | ||
431 | if (tab == fref->op1) { /* ASTORE to the same table. */ | ||
432 | /* Detect t[#t+1] = x idiom for push. */ | ||
433 | IRIns *idx = IR(aref->op2); | ||
434 | if (!irt_isnil(store->t) && | ||
435 | idx->o == IR_ADD && idx->op1 == ref && | ||
436 | IR(idx->op2)->o == IR_KINT && IR(idx->op2)->i == 1) { | ||
437 | /* Note: this requires an extra PHI check in loop unroll. */ | ||
438 | fins->op2 = aref->op2; /* Set ALEN hint. */ | ||
439 | } | ||
440 | goto doemit; /* Conflicting store, possibly giving a hint. */ | ||
441 | } else if (aa_table(J, tab, fref->op1) != ALIAS_NO) { | ||
442 | goto doemit; /* Conflicting store. */ | ||
443 | } | ||
444 | sref = store->prev; | ||
445 | } | ||
446 | |||
447 | return ref; /* Plain ALEN forwarding. */ | ||
448 | } | ||
449 | ref = IR(ref)->prev; | ||
450 | } | ||
451 | doemit: | ||
452 | return EMITFOLD; | ||
453 | } | ||
454 | |||
373 | /* -- ULOAD forwarding ---------------------------------------------------- */ | 455 | /* -- ULOAD forwarding ---------------------------------------------------- */ |
374 | 456 | ||
375 | /* The current alias analysis for upvalues is very simplistic. It only | 457 | /* The current alias analysis for upvalues is very simplistic. It only |
@@ -419,7 +501,6 @@ TRef LJ_FASTCALL lj_opt_fwd_uload(jit_State *J) | |||
419 | 501 | ||
420 | cselim: | 502 | cselim: |
421 | /* Try to find a matching load. Below the conflicting store, if any. */ | 503 | /* Try to find a matching load. Below the conflicting store, if any. */ |
422 | |||
423 | ref = J->chain[IR_ULOAD]; | 504 | ref = J->chain[IR_ULOAD]; |
424 | while (ref > lim) { | 505 | while (ref > lim) { |
425 | IRIns *ir = IR(ref); | 506 | IRIns *ir = IR(ref); |
@@ -549,8 +630,9 @@ TRef LJ_FASTCALL lj_opt_dse_fstore(jit_State *J) | |||
549 | goto doemit; | 630 | goto doemit; |
550 | break; /* Otherwise continue searching. */ | 631 | break; /* Otherwise continue searching. */ |
551 | case ALIAS_MUST: | 632 | case ALIAS_MUST: |
552 | if (store->op2 == val) /* Same value: drop the new store. */ | 633 | if (store->op2 == val && |
553 | return DROPFOLD; | 634 | !(xr->op2 >= IRFL_SBUF_W && xr->op2 <= IRFL_SBUF_R)) |
635 | return DROPFOLD; /* Same value: drop the new store. */ | ||
554 | /* Different value: try to eliminate the redundant store. */ | 636 | /* Different value: try to eliminate the redundant store. */ |
555 | if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */ | 637 | if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */ |
556 | IRIns *ir; | 638 | IRIns *ir; |
@@ -571,6 +653,29 @@ doemit: | |||
571 | return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ | 653 | return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ |
572 | } | 654 | } |
573 | 655 | ||
656 | /* Check whether there's no aliasing buffer op between IRFL_SBUF_*. */ | ||
657 | int LJ_FASTCALL lj_opt_fwd_sbuf(jit_State *J, IRRef lim) | ||
658 | { | ||
659 | IRRef ref; | ||
660 | if (J->chain[IR_BUFPUT] > lim) | ||
661 | return 0; /* Conflict. */ | ||
662 | ref = J->chain[IR_CALLS]; | ||
663 | while (ref > lim) { | ||
664 | IRIns *ir = IR(ref); | ||
665 | if (ir->op2 >= IRCALL_lj_strfmt_putint && ir->op2 < IRCALL_lj_buf_tostr) | ||
666 | return 0; /* Conflict. */ | ||
667 | ref = ir->prev; | ||
668 | } | ||
669 | ref = J->chain[IR_CALLL]; | ||
670 | while (ref > lim) { | ||
671 | IRIns *ir = IR(ref); | ||
672 | if (ir->op2 >= IRCALL_lj_strfmt_putint && ir->op2 < IRCALL_lj_buf_tostr) | ||
673 | return 0; /* Conflict. */ | ||
674 | ref = ir->prev; | ||
675 | } | ||
676 | return 1; /* No conflict. Can safely FOLD/CSE. */ | ||
677 | } | ||
678 | |||
574 | /* -- XLOAD forwarding and XSTORE elimination ----------------------------- */ | 679 | /* -- XLOAD forwarding and XSTORE elimination ----------------------------- */ |
575 | 680 | ||
576 | /* Find cdata allocation for a reference (if any). */ | 681 | /* Find cdata allocation for a reference (if any). */ |
@@ -822,35 +927,6 @@ doemit: | |||
822 | return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ | 927 | return EMITFOLD; /* Otherwise we have a conflict or simply no match. */ |
823 | } | 928 | } |
824 | 929 | ||
825 | /* -- Forwarding of lj_tab_len -------------------------------------------- */ | ||
826 | |||
827 | /* This is rather simplistic right now, but better than nothing. */ | ||
828 | TRef LJ_FASTCALL lj_opt_fwd_tab_len(jit_State *J) | ||
829 | { | ||
830 | IRRef tab = fins->op1; /* Table reference. */ | ||
831 | IRRef lim = tab; /* Search limit. */ | ||
832 | IRRef ref; | ||
833 | |||
834 | /* Any ASTORE is a conflict and limits the search. */ | ||
835 | if (J->chain[IR_ASTORE] > lim) lim = J->chain[IR_ASTORE]; | ||
836 | |||
837 | /* Search for conflicting HSTORE with numeric key. */ | ||
838 | ref = J->chain[IR_HSTORE]; | ||
839 | while (ref > lim) { | ||
840 | IRIns *store = IR(ref); | ||
841 | IRIns *href = IR(store->op1); | ||
842 | IRIns *key = IR(href->op2); | ||
843 | if (irt_isnum(key->o == IR_KSLOT ? IR(key->op1)->t : key->t)) { | ||
844 | lim = ref; /* Conflicting store found, limits search for TLEN. */ | ||
845 | break; | ||
846 | } | ||
847 | ref = store->prev; | ||
848 | } | ||
849 | |||
850 | /* Try to find a matching load. Below the conflicting store, if any. */ | ||
851 | return lj_opt_cselim(J, lim); | ||
852 | } | ||
853 | |||
854 | /* -- ASTORE/HSTORE previous type analysis -------------------------------- */ | 930 | /* -- ASTORE/HSTORE previous type analysis -------------------------------- */ |
855 | 931 | ||
856 | /* Check whether the previous value for a table store is non-nil. | 932 | /* Check whether the previous value for a table store is non-nil. |