diff options
| author | 2025-12-16 19:23:33 +0100 | |
|---|---|---|
| committer | 2025-12-16 19:23:33 +0100 | |
| commit | 842f49f34464440abb2799ef007959cc5e8b7cf8 (patch) | |
| tree | 244c590779b6d9c90a7d13d946e63238fa909dd7 | |
| parent | b3762f81b1a276f06bace301d56c9e8f6538058d (diff) | |
x86-64/emit: implement single-exit-point ret with jump threading
| -rw-r--r-- | ir/builder.c | 2 | ||||
| -rw-r--r-- | ir/regalloc.c | 2 | ||||
| -rw-r--r-- | x86_64/emit.c | 62 |
3 files changed, 40 insertions, 26 deletions
diff --git a/ir/builder.c b/ir/builder.c index 3ddb5a6..36f955c 100644 --- a/ir/builder.c +++ b/ir/builder.c @@ -218,7 +218,7 @@ useblk(struct function *fn, struct block *blk) blk->lnext = fn->entry; blk->lprev = fn->entry->lprev; blk->lprev->lnext = blk; - blk->id = ++fn->nblk; + blk->id = fn->nblk++; fn->entry->lprev = blk; } fn->curblk = blk; diff --git a/ir/regalloc.c b/ir/regalloc.c index 691793e..794cdd6 100644 --- a/ir/regalloc.c +++ b/ir/regalloc.c @@ -1233,6 +1233,8 @@ fini(struct rega *ra) freeblk(fn, blk); --id; } + } else if (allnops) { + vfree(&blk->ins); } } while ((blk = blk->lnext) != fn->entry); } diff --git a/x86_64/emit.c b/x86_64/emit.c index 8a1673d..4a9d9d9 100644 --- a/x86_64/emit.c +++ b/x86_64/emit.c @@ -1288,15 +1288,8 @@ emitbin(struct function *fn) struct block *blk; uchar **pcode = &objout.code; int npush = 0; - uint epilogueaddr = 0; bool saverestore; - if (nblkaddr < fn->nblk) { - blkaddr = xrealloc(blkaddr, fn->nblk * sizeof *blkaddr); - nblkaddr = fn->nblk; - } - memset(blkaddr, 0, nblkaddr * sizeof *blkaddr); - nops(pcode, 16); fnstart = *pcode; curfnsym = fn->name; @@ -1335,6 +1328,37 @@ emitbin(struct function *fn) DS("\x48\x81\xEC"), I32(fn->stksiz); } + if (*pcode - fnstart > 6) { + /* largue prologue -> largue epilogue -> transform to use single exit point */ + struct block *exit = NULL; + blk = fn->entry->lprev; + do { + if (blk->jmp.t == Jret) { + if (!exit) { + if (blk->ins.n == 0) { + exit = blk; + continue; + } else { + useblk(fn, exit = newblk(fn)); + exit->jmp.t = Jret; + } + } + blk->jmp.t = Jb; + memset(blk->jmp.arg, 0, sizeof blk->jmp.arg); + blk->s1 = exit; + } else if (exit) { + /* thread jumps to the exit block */ + if (blk->s1 && !blk->s1->ins.n && blk->s1->s1 == exit && !blk->s1->s2) blk->s1 = exit; + if (blk->s2 && !blk->s2->ins.n && blk->s2->s1 == exit && !blk->s2->s2) blk->s2 = exit; + } + } while ((blk = blk->lprev) != fn->entry); + } + + if (nblkaddr < fn->nblk) { + blkaddr = xrealloc(blkaddr, (nblkaddr = fn->nblk) * sizeof *blkaddr); + } + memset(blkaddr, 0, nblkaddr * sizeof *blkaddr); + blk = fn->entry; do { struct blkaddr *bb = &blkaddr[blk->id]; @@ -1356,24 +1380,12 @@ emitbin(struct function *fn) } if (blk->jmp.t == Jret) { /* epilogue */ - uint here = *pcode - fnstart; - if (epilogueaddr) { - int disp = epilogueaddr - (here + 2); - if ((uint)(disp + 128) < 256) {/* can use 1-byte displacement? */ - B(0xEB), B(disp); /* JMP rel8 */ - } else { - B(0xE9), I32(disp - 3); /* JMP rel32 */ - } - } else { - if (fn->stksiz && (saverestore || !usebp)) - Xadd(pcode, KPTR, mkoper(OREG, .reg = RSP), mkoper(OIMM, .imm = fn->stksiz)); - if (saverestore) { - epilogueaddr = here; - calleerestore(pcode, fn); - } - if (usebp) B(0xC9); /* leave */ - B(0xC3); /* ret */ - } + if (fn->stksiz && (saverestore || !usebp)) + Xadd(pcode, KPTR, mkoper(OREG, .reg = RSP), mkoper(OIMM, .imm = fn->stksiz)); + if (saverestore) + calleerestore(pcode, fn); + if (usebp) B(0xC9); /* leave */ + B(0xC3); /* ret */ } else if (blk->jmp.t == Jtrap) { DS("\x0F\x0B"); /* UD2 */ } else emitbranch(pcode, blk); |