]> de.git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
Importing tail-recursion optimization
authorWolfgang (Blub) Bumiller <blub@speed.at>
Fri, 30 Nov 2012 10:12:53 +0000 (11:12 +0100)
committerWolfgang (Blub) Bumiller <blub@speed.at>
Fri, 30 Nov 2012 10:12:53 +0000 (11:12 +0100)
gmqcc.h
ir.c
main.c

diff --git a/gmqcc.h b/gmqcc.h
index 459c2ff0cf83ff37bff2d55e902bd1cf3a73da2e..84d3fd19e8a937fce3730b7d1702573730a4570e 100644 (file)
--- 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 f0a8a84fbd6d51e618b21d0e8aac80f7bb998755..72129db6a9b55c62145ac79983bde2d190addfca 100644 (file)
--- 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 205cf0486fab79e744707321d6e68bb2e673f2fd..6aad127b29fb0c7504277e45d2910c7e4c4698a8 100644 (file)
--- 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;