From e1fe6cff54965075ba3b02b2356dbe73c4136905 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 30 Nov 2012 11:12:53 +0100 Subject: [PATCH] Importing tail-recursion optimization --- gmqcc.h | 1 + ir.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.c | 3 ++ 3 files changed, 98 insertions(+) diff --git a/gmqcc.h b/gmqcc.h index 459c2ff..84d3fd1 100644 --- a/gmqcc.h +++ b/gmqcc.h @@ -895,6 +895,7 @@ static const unsigned int opts_opt_oflag[] = { # include "opts.def" 0 }; +extern unsigned int optimization_count[COUNT_OPTIMIZATIONS]; /* other options: */ enum { diff --git a/ir.c b/ir.c index f0a8a84..72129db 100644 --- a/ir.c +++ b/ir.c @@ -524,11 +524,105 @@ ir_block* ir_function_create_block(lex_ctx ctx, ir_function *self, const char *l return bn; } +bool ir_function_pass_tailcall(ir_function *self) +{ + size_t b, p; + + for (b = 0; b < vec_size(self->blocks); ++b) { + ir_value *funcval; + ir_instr *ret, *call, *store = NULL; + ir_block *block = self->blocks[b]; + + if (!block->final || vec_size(block->instr) < 2) + continue; + + ret = block->instr[vec_size(block->instr)-1]; + if (ret->opcode != INSTR_DONE && ret->opcode != INSTR_RETURN) + continue; + + call = block->instr[vec_size(block->instr)-2]; + if (call->opcode >= INSTR_STORE_F && call->opcode <= INSTR_STORE_FNC) { + /* account for the unoptimized + * CALL + * STORE %return, %tmp + * RETURN %tmp + * version + */ + if (vec_size(block->instr) < 3) + continue; + + store = call; + call = block->instr[vec_size(block->instr)-3]; + } + + if (call->opcode < INSTR_CALL0 || call->opcode > INSTR_CALL8) + continue; + + if (store) { + /* optimize out the STORE */ + if (ret->_ops[0] && + ret->_ops[0] == store->_ops[0] && + store->_ops[1] == call->_ops[0]) + { + ++optimization_count[OPTIM_MINOR]; + call->_ops[0] = store->_ops[0]; + vec_remove(block, vec_size(block->instr) - 2, 1); + ir_instr_delete(store); + } + else + continue; + } + + if (!call->_ops[0]) + continue; + + funcval = call->_ops[1]; + if (!funcval) + continue; + if (funcval->vtype != TYPE_FUNCTION || funcval->constval.vfunc != self) + continue; + + /* now we have a CALL and a RET, check if it's a tailcall */ + if (ret->_ops[0] && call->_ops[0] != ret->_ops[0]) + continue; + + ++optimization_count[OPTIM_TAIL_RECURSION]; + vec_shrinkby(block->instr, 2); + + block->final = false; /* open it back up */ + + /* emite parameter-stores */ + for (p = 0; p < vec_size(call->params); ++p) { + /* assert(call->params_count <= self->locals_count); */ + if (!ir_block_create_store(block, self->locals[p], call->params[p])) { + irerror(call->context, "failed to create tailcall store instruction for parameter %i", (int)p); + return false; + } + } + if (!ir_block_create_jump(block, self->blocks[0])) { + irerror(call->context, "failed to create tailcall jump"); + return false; + } + + ir_instr_delete(call); + ir_instr_delete(ret); + } + + return true; +} + bool ir_function_finalize(ir_function *self) { if (self->builtin) return true; + if (OPTS_OPTIMIZATION(OPTIM_TAIL_RECURSION)) { + if (!ir_function_pass_tailcall(self)) { + irerror(self->context, "tailcall optimization pass broke something in `%s`", self->name); + return false; + } + } + if (!ir_function_naive_phi(self)) return false; diff --git a/main.c b/main.c index 205cf04..6aad127 100644 --- a/main.c +++ b/main.c @@ -27,6 +27,9 @@ uint32_t opts_flags[1 + (COUNT_FLAGS / 32)]; uint32_t opts_optimization[1 + (COUNT_OPTIMIZATIONS / 32)]; +/* counter increased in ir.c */ +unsigned int optimization_count[COUNT_OPTIMIZATIONS]; + uint32_t opts_O = 1; const char *opts_output = "progs.dat"; int opts_standard = COMPILER_GMQCC; -- 2.39.2