]> git.unchartedbackwaters.co.uk Git - francis/libjit.git/commitdiff
Implement tail calls from a function to itself.
authorRhys Weatherley <rweather@southern-storm.com.au>
Fri, 11 Jun 2004 01:39:00 +0000 (01:39 +0000)
committerRhys Weatherley <rweather@southern-storm.com.au>
Fri, 11 Jun 2004 01:39:00 +0000 (01:39 +0000)
15 files changed:
ChangeLog
doc/libjit.texi
jit/jit-insn.c
jit/jit-internal.h
jit/jit-reg-alloc.c
jit/jit-rules-arm.c
jit/jit-rules-arm.sel
jit/jit-rules-interp.c
jit/jit-rules-x86.c
jit/jit-rules-x86.sel
jit/jit-rules.h
jit/jit-value.c
tutorial/Makefile.am
tutorial/t2.c
tutorial/t5.c [new file with mode: 0644]

index 7ef2e89bc96dd3bb461fdc28fecf8bff23c07188..bdd5019faa5a02536df28064827cca07674ec9d8 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,13 @@
 
+2004-06-11  Rhys Weatherley  <rweather@southern-storm.com.au>
+
+       * doc/libjit.texi, jit/jit-insn.c, jit/jit-internal.h,
+       jit/jit-reg-alloc.c, jit/jit-rules-arm.c, jit/jit-rules-arm.sel,
+       jit/jit-rules-interp.c, jit/jit-rules-x86.c, jit/jit-rules-x86.sel,
+       jit/jit-rules.h, jit/jit-value.c, tutorial/Makefile.am,
+       tutorial/t2.c, tutorial/t5.c: implement tail calls from a
+       function to itself.
+
 2004-06-10  Rhys Weatherley  <rweather@southern-storm.com.au>
 
        * jit/jit-apply-arm.c, jit/jit-gen-arm.c, jit/jit-gen-arm.h,
index 34b91f39ba3079adc34573bf54c22b26a3cfbab0..537c0233a19df7b013caaa6a4c822a6869aca87b 100644 (file)
@@ -213,6 +213,7 @@ but a real program would be expected to handle such errors.
 * Tutorial 2::              Tutorial 2 - gcd
 * Tutorial 3::              Tutorial 3 - compiling on-demand
 * Tutorial 4::              Tutorial 4 - mul_add, C++ version
+* Tutorial 5::              Tutorial 5 - gcd, with tail calls
 * Dynamic Pascal::          Dynamic Pascal - A full JIT example
 @end menu
 
@@ -641,7 +642,7 @@ heuristics.
 
 @c -----------------------------------------------------------------------
 
-@node Tutorial 4, Dynamic Pascal, Tutorial 3, Tutorials
+@node Tutorial 4, Tutorial 5, Tutorial 3, Tutorials
 @section Tutorial 4 - mul_add, C++ version
 @cindex mul_add C++ tutorial
 
@@ -756,7 +757,49 @@ library.
 
 @c -----------------------------------------------------------------------
 
-@node Dynamic Pascal, Initialization, Tutorial 4, Tutorials
+@node Tutorial 5, Dynamic Pascal, Tutorial 4, Tutorials
+@section Tutorial 5 - gcd, with tail calls
+@cindex gcd with tail calls
+
+Astute readers would have noticed that Tutorial 2 included two instances
+of "tail calls".  That is, calls to the same function that are immediately
+followed by a @code{return} instruction.
+
+Libjit can optimize tail calls if you provide the @code{JIT_CALL_TAIL}
+flag to @code{jit_insn_call}.  Previously, we used the following code
+to call @code{gcd} recursively:
+
+@example
+temp3 = jit_insn_call
+    (function, "gcd", function, 0, temp_args, 2, 0);
+jit_insn_return(function, temp3);
+@end example
+
+@noindent
+In Tutorial 5, this is modified to the following:
+
+@example
+jit_insn_call(function, "gcd", function, 0, temp_args, 2, JIT_CALL_TAIL);
+@end example
+
+There is no need for the @code{jit_insn_return}, because the call
+will never return to that point in the code.  Behind the scenes,
+@code{libjit} will convert the call into a jump back to the head
+of the function.
+
+Tail calls can only be used in certain circumstances.  The source
+and destination of the call must have the same function signatures.
+None of the parameters should point to local variables in the current
+stack frame.  And tail calls cannot be used from any source function
+that uses @code{try} or @code{alloca} statements.
+
+Because it can be difficult for @code{libjit} to determine when these
+conditions have been met, it relies upon the caller to supply the
+@code{JIT_CALL_TAIL} flag when it is appropriate to use a tail call.
+
+@c -----------------------------------------------------------------------
+
+@node Dynamic Pascal, Initialization, Tutorial 5, Tutorials
 @section Dynamic Pascal - A full JIT example
 @cindex Dynamic Pascal
 
index 19f56baec88f192108e5b02ec1100fb1dce75151..11826e5c9f7d7a3ed9cc1e76dc30bd28ba3ae7b0 100644 (file)
@@ -4950,6 +4950,142 @@ static int restore_eh_frame_after_call(jit_function_t func, int flags)
 #endif
 }
 
+/*
+ * Determine if two signatures are identical for the purpose of tail calls.
+ */
+static int signature_identical(jit_type_t type1, jit_type_t type2)
+{
+       unsigned int param;
+
+       /* Handle the easy case first */
+       if(type1 == type2)
+       {
+               return 1;
+       }
+
+       /* Remove the tags and then bail out if either type is invalid */
+       type1 = jit_type_remove_tags(type1);
+       type2 = jit_type_remove_tags(type2);
+       if(!type1 || !type2)
+       {
+               return 0;
+       }
+
+       /* Normalize pointer types, but leave signature types as-is */
+       if(type1->kind == JIT_TYPE_PTR)
+       {
+               type1 = jit_type_normalize(type1);
+       }
+       if(type2->kind == JIT_TYPE_PTR)
+       {
+               type2 = jit_type_normalize(type2);
+       }
+
+#ifdef JIT_NFLOAT_IS_DOUBLE
+       /* "double" and "nfloat" are identical on this platform */
+       if((type1->kind == JIT_TYPE_FLOAT64 || type1->kind == JIT_TYPE_NFLOAT) &&
+          (type2->kind == JIT_TYPE_FLOAT64 || type2->kind == JIT_TYPE_NFLOAT))
+       {
+               return 1;
+       }
+#endif
+
+       /* If the kinds are not the same now, then we don't have a match */
+       if(type1->kind != type2->kind)
+       {
+               return 0;
+       }
+
+       /* Structure and union types must have the same size and alignment */
+       if(type1->kind == JIT_TYPE_STRUCT || type1->kind == JIT_TYPE_UNION)
+       {
+               return (jit_type_get_size(type1) == jit_type_get_size(type2) &&
+                               jit_type_get_alignment(type1) == jit_type_get_alignment(type2));
+       }
+
+       /* Signature types must be compared component-by-component */
+       if(type1->kind == JIT_TYPE_SIGNATURE)
+       {
+               if(type1->abi != type2->abi)
+               {
+                       return 0;
+               }
+               if(!signature_identical(type1->sub_type, type2->sub_type))
+               {
+                       return 0;
+               }
+               if(type1->num_components != type2->num_components)
+               {
+                       return 0;
+               }
+               for(param = 0; param < type1->num_components; ++param)
+               {
+                       if(!signature_identical(type1->components[param].type,
+                                                                       type2->components[param].type))
+                       {
+                               return 0;
+                       }
+               }
+       }
+
+       /* If we get here, then the types are compatible */
+       return 1;
+}
+
+/*
+ * Create call setup instructions, taking tail calls into effect.
+ */
+static int create_call_setup_insns
+       (jit_function_t func, jit_function_t callee, jit_type_t signature,
+        jit_value_t *args, unsigned int num_args,
+        int is_nested, int nesting_level, jit_value_t *struct_return, int flags)
+{
+       jit_value_t *new_args;
+       jit_value_t value;
+       unsigned int arg_num;
+
+       /* If we are performing a tail call, then duplicate the argument
+          values so that we don't accidentally destroy parameters in
+          situations like func(x, y) -> func(y, x) */
+       if((flags & JIT_CALL_TAIL) != 0 && num_args > 0)
+       {
+               new_args = (jit_value_t *)alloca(sizeof(jit_value_t) * num_args);
+               for(arg_num = 0; arg_num < num_args; ++arg_num)
+               {
+                       value = args[arg_num];
+                       if(value && value->is_parameter)
+                       {
+                               value = jit_insn_dup(func, value);
+                               if(!value)
+                               {
+                                       return 0;
+                               }
+                       }
+                       new_args[arg_num] = value;
+               }
+               args = new_args;
+       }
+
+       /* If we are calling ourselves, then store back to our own parameters */
+       if((flags & JIT_CALL_TAIL) != 0 && func == callee)
+       {
+               for(arg_num = 0; arg_num < num_args; ++arg_num)
+               {
+                       if(!jit_insn_store(func, jit_value_get_param(func, arg_num),
+                                                          args[arg_num]))
+                       {
+                               return 0;
+                       }
+               }
+               return 1;
+       }
+
+       /* Let the back end do the work */
+       return _jit_create_call_setup_insns
+               (func, signature, args, num_args,
+                is_nested, nesting_level, struct_return, flags);
+}
+
 /*@
  * @deftypefun jit_value_t jit_insn_call (jit_function_t func, {const char *} name, jit_function_t jit_func, jit_type_t signature, {jit_value_t *} args, {unsigned int} num_args, int flags)
  * Call the function @code{jit_func}, which may or may not be translated yet.
@@ -4998,6 +5134,8 @@ jit_value_t jit_insn_call
        jit_value_t *new_args;
        jit_value_t return_value;
        jit_insn_t insn;
+       jit_label_t entry_point;
+       jit_label_t label_end;
 
        /* Bail out if there is something wrong with the parameters */
        if(!_jit_function_ensure_builder(func) || !jit_func)
@@ -5011,6 +5149,21 @@ jit_value_t jit_insn_call
                signature = jit_func->signature;
        }
 
+       /* Verify that tail calls are possible to the destination */
+       if((flags & JIT_CALL_TAIL) != 0)
+       {
+               if(func->nested_parent || jit_func->nested_parent)
+               {
+                       /* Cannot use tail calls with nested function calls */
+                       flags &= ~JIT_CALL_TAIL;
+               }
+               else if(!signature_identical(signature, func->signature))
+               {
+                       /* The signatures are not the same, so tail calls not allowed */
+                       flags &= ~JIT_CALL_TAIL;
+               }
+       }
+
        /* Determine the nesting relationship with the current function */
        if(jit_func->nested_parent)
        {
@@ -5074,35 +5227,70 @@ jit_value_t jit_insn_call
        }
 
        /* Create the instructions to push the parameters onto the stack */
-       if(!_jit_create_call_setup_insns
-                       (func, signature, new_args, num_args,
-                        is_nested, nesting_level, &return_value))
+       if(!create_call_setup_insns
+                       (func, jit_func, signature, new_args, num_args,
+                        is_nested, nesting_level, &return_value, flags))
        {
                return 0;
        }
 
-       /* Functions that call out are not leaves */
-       func->builder->non_leaf = 1;
-
        /* Start a new block and output the "call" instruction */
-       if(!jit_insn_new_block(func))
+       if((flags & JIT_CALL_TAIL) != 0 && func == jit_func)
        {
-               return 0;
+               /* We are performing a tail call to ourselves, which we can
+                  turn into an unconditional branch back to our entry point */
+               entry_point = jit_label_undefined;
+               label_end = jit_label_undefined;
+               if(!jit_insn_branch(func, &entry_point))
+               {
+                       return 0;
+               }
+               if(!jit_insn_label(func, &entry_point))
+               {
+                       return 0;
+               }
+               if(!jit_insn_label(func, &label_end))
+               {
+                       return 0;
+               }
+               if(!jit_insn_move_blocks_to_start(func, entry_point, label_end))
+               {
+                       return 0;
+               }
        }
-       insn = _jit_block_add_insn(func->builder->current_block);
-       if(!insn)
+       else
        {
-               return 0;
+               /* Functions that call out are not leaves */
+               func->builder->non_leaf = 1;
+
+               /* Performing a regular call, or a tail call to someone else */
+               if(!jit_insn_new_block(func))
+               {
+                       return 0;
+               }
+               insn = _jit_block_add_insn(func->builder->current_block);
+               if(!insn)
+               {
+                       return 0;
+               }
+               if((flags & JIT_CALL_TAIL) != 0)
+               {
+                       func->builder->has_tail_call = 1;
+                       insn->opcode = JIT_OP_CALL_TAIL;
+               }
+               else
+               {
+                       insn->opcode = JIT_OP_CALL;
+               }
+               insn->flags = JIT_INSN_DEST_IS_FUNCTION | JIT_INSN_VALUE1_IS_NAME;
+               insn->dest = (jit_value_t)jit_func;
+               insn->value1 = (jit_value_t)name;
        }
-       insn->opcode = JIT_OP_CALL;
-       insn->flags = JIT_INSN_DEST_IS_FUNCTION | JIT_INSN_VALUE1_IS_NAME;
-       insn->dest = (jit_value_t)jit_func;
-       insn->value1 = (jit_value_t)name;
 
        /* If the function does not return, then end the current block.
           The next block does not have "entered_via_top" set so that
           it will be eliminated during later code generation */
-       if((flags & JIT_CALL_NORETURN) != 0)
+       if((flags & (JIT_CALL_NORETURN | JIT_CALL_TAIL)) != 0)
        {
                func->builder->current_block->ends_in_dead = 1;
                if(!jit_insn_new_block(func))
@@ -5122,10 +5310,13 @@ jit_value_t jit_insn_call
        }
 
        /* Create the instructions necessary to move the return value into place */
-       if(!_jit_create_call_return_insns
-                       (func, signature, new_args, num_args, return_value, is_nested))
+       if((flags & JIT_CALL_TAIL) == 0)
        {
-               return 0;
+               if(!_jit_create_call_return_insns
+                               (func, signature, new_args, num_args, return_value, is_nested))
+               {
+                       return 0;
+               }
        }
 
        /* Restore exception frame information after the call */
@@ -5157,6 +5348,19 @@ jit_value_t jit_insn_call_indirect
                return 0;
        }
 
+       /* Verify that tail calls are possible to the destination */
+       if((flags & JIT_CALL_TAIL) != 0)
+       {
+               if(func->nested_parent)
+               {
+                       flags &= ~JIT_CALL_TAIL;
+               }
+               else if(!signature_identical(signature, func->signature))
+               {
+                       flags &= ~JIT_CALL_TAIL;
+               }
+       }
+
        /* Convert the arguments to the actual parameter types */
        if(num_args > 0)
        {
@@ -5178,8 +5382,8 @@ jit_value_t jit_insn_call_indirect
        }
 
        /* Create the instructions to push the parameters onto the stack */
-       if(!_jit_create_call_setup_insns
-                       (func, signature, new_args, num_args, 0, 0, &return_value))
+       if(!create_call_setup_insns
+               (func, 0, signature, new_args, num_args, 0, 0, &return_value, flags))
        {
                return 0;
        }
@@ -5212,7 +5416,7 @@ jit_value_t jit_insn_call_indirect
        /* If the function does not return, then end the current block.
           The next block does not have "entered_via_top" set so that
           it will be eliminated during later code generation */
-       if((flags & JIT_CALL_NORETURN) != 0)
+       if((flags & (JIT_CALL_NORETURN | JIT_CALL_TAIL)) != 0)
        {
                func->builder->current_block->ends_in_dead = 1;
                if(!jit_insn_new_block(func))
@@ -5232,10 +5436,13 @@ jit_value_t jit_insn_call_indirect
        }
 
        /* Create the instructions necessary to move the return value into place */
-       if(!_jit_create_call_return_insns
-                       (func, signature, new_args, num_args, return_value, 0))
+       if((flags & JIT_CALL_TAIL) == 0)
        {
-               return 0;
+               if(!_jit_create_call_return_insns
+                               (func, signature, new_args, num_args, return_value, 0))
+               {
+                       return 0;
+               }
        }
 
        /* Restore exception frame information after the call */
@@ -5271,6 +5478,19 @@ jit_value_t jit_insn_call_indirect_vtable
                return 0;
        }
 
+       /* Verify that tail calls are possible to the destination */
+       if((flags & JIT_CALL_TAIL) != 0)
+       {
+               if(func->nested_parent)
+               {
+                       flags &= ~JIT_CALL_TAIL;
+               }
+               else if(!signature_identical(signature, func->signature))
+               {
+                       flags &= ~JIT_CALL_TAIL;
+               }
+       }
+
        /* Convert the arguments to the actual parameter types */
        if(num_args > 0)
        {
@@ -5292,8 +5512,8 @@ jit_value_t jit_insn_call_indirect_vtable
        }
 
        /* Create the instructions to push the parameters onto the stack */
-       if(!_jit_create_call_setup_insns
-                       (func, signature, new_args, num_args, 0, 0, &return_value))
+       if(!create_call_setup_insns
+               (func, 0, signature, new_args, num_args, 0, 0, &return_value, flags))
        {
                return 0;
        }
@@ -5324,7 +5544,7 @@ jit_value_t jit_insn_call_indirect_vtable
        /* If the function does not return, then end the current block.
           The next block does not have "entered_via_top" set so that
           it will be eliminated during later code generation */
-       if((flags & JIT_CALL_NORETURN) != 0)
+       if((flags & (JIT_CALL_NORETURN | JIT_CALL_TAIL)) != 0)
        {
                func->builder->current_block->ends_in_dead = 1;
                if(!jit_insn_new_block(func))
@@ -5344,10 +5564,13 @@ jit_value_t jit_insn_call_indirect_vtable
        }
 
        /* Create the instructions necessary to move the return value into place */
-       if(!_jit_create_call_return_insns
-                       (func, signature, new_args, num_args, return_value, 0))
+       if((flags & JIT_CALL_TAIL) == 0)
        {
-               return 0;
+               if(!_jit_create_call_return_insns
+                               (func, signature, new_args, num_args, return_value, 0))
+               {
+                       return 0;
+               }
        }
 
        /* Restore exception frame information after the call */
@@ -5380,6 +5603,19 @@ jit_value_t jit_insn_call_native
                return 0;
        }
 
+       /* Verify that tail calls are possible to the destination */
+       if((flags & JIT_CALL_TAIL) != 0)
+       {
+               if(func->nested_parent)
+               {
+                       flags &= ~JIT_CALL_TAIL;
+               }
+               else if(!signature_identical(signature, func->signature))
+               {
+                       flags &= ~JIT_CALL_TAIL;
+               }
+       }
+
        /* Convert the arguments to the actual parameter types */
        if(num_args > 0)
        {
@@ -5401,8 +5637,8 @@ jit_value_t jit_insn_call_native
        }
 
        /* Create the instructions to push the parameters onto the stack */
-       if(!_jit_create_call_setup_insns
-                       (func, signature, new_args, num_args, 0, 0, &return_value))
+       if(!create_call_setup_insns
+               (func, 0, signature, new_args, num_args, 0, 0, &return_value, flags))
        {
                return 0;
        }
@@ -5432,7 +5668,7 @@ jit_value_t jit_insn_call_native
        /* If the function does not return, then end the current block.
           The next block does not have "entered_via_top" set so that
           it will be eliminated during later code generation */
-       if((flags & JIT_CALL_NORETURN) != 0)
+       if((flags & (JIT_CALL_NORETURN | JIT_CALL_TAIL)) != 0)
        {
                func->builder->current_block->ends_in_dead = 1;
                if(!jit_insn_new_block(func))
@@ -5452,10 +5688,13 @@ jit_value_t jit_insn_call_native
        }
 
        /* Create the instructions necessary to move the return value into place */
-       if(!_jit_create_call_return_insns
-                       (func, signature, new_args, num_args, return_value, 0))
+       if((flags & JIT_CALL_TAIL) == 0)
        {
-               return 0;
+               if(!_jit_create_call_return_insns
+                               (func, signature, new_args, num_args, return_value, 0))
+               {
+                       return 0;
+               }
        }
 
        /* Restore exception frame information after the call */
@@ -7277,6 +7516,7 @@ int jit_insn_move_blocks_to_start
        {
                if(func->builder->init_insn <= init_block->last_insn)
                {
+                       _jit_value_ref_params(func);
                        block = _jit_block_create(func, 0);
                        if(!block)
                        {
index 8ec50ca33bc251f4879a6096385487539041d5dd..a249e04ca06d66df22dd4b4ec6293e32fbaf5676 100644 (file)
@@ -226,6 +226,13 @@ struct _jit_value
  */
 void _jit_value_free(void *value);
 
+/*
+ * Add references to all of the parameter values in a function.
+ * This is used when the initialization block is split during a
+ * "jit_insn_move_blocks_to_start" instruction.
+ */
+void _jit_value_ref_params(jit_function_t func);
+
 /*
  * Internal structure of an instruction.
  */
@@ -305,6 +312,9 @@ struct _jit_builder
        /* Flag that indicates if the function has an ordinary return */
        int                                     ordinary_return : 1;
 
+       /* Flag that indicates that the current function contains a tail call */
+       int                                     has_tail_call : 1;
+
        /* List of all instructions in this function */
        jit_insn_t                 *insns;
        int                                     num_insns;
index 1ae3d0647ed4eaf31ecc7daecc0eb050489ac123..39ea14be01b45ae4001e172eef65c3262e195de2 100644 (file)
@@ -1457,6 +1457,23 @@ void _jit_regs_alloc_global(jit_gencode_t gen, jit_function_t func)
                return;
        }
 
+       /* If the current function involves a tail call, then we don't do
+          global register allocation and we also prevent the code generator
+          from using any of the callee-saved registers.  This simplifies
+          tail calls, which don't have to worry about restoring such registers */
+       if(func->builder->has_tail_call)
+       {
+               for(reg = 0; reg < JIT_NUM_REGS; ++reg)
+               {
+                       if((_jit_reg_info[reg].flags &
+                                       (JIT_REG_FIXED | JIT_REG_CALL_USED)) == 0)
+                       {
+                               jit_reg_set_used(gen->permanent, reg);
+                       }
+               }
+               return;
+       }
+
        /* Scan all values within the function, looking for the most used.
           We will replace this with a better allocation strategy later */
        block = func->builder->value_pool.blocks;
index b937d58245b9c24f6741183e244655ae5e2c1399..61a08aa1c650806983c1671ef6be9e46c1ad9906 100644 (file)
@@ -551,7 +551,7 @@ int _jit_create_entry_insns(jit_function_t func)
 int _jit_create_call_setup_insns
        (jit_function_t func, jit_type_t signature,
         jit_value_t *args, unsigned int num_args,
-        int is_nested, int nesting_level, jit_value_t *struct_return)
+        int is_nested, int nesting_level, jit_value_t *struct_return, int flags)
 {
        jit_type_t type = jit_type_get_return(signature);
        jit_value_t value;
index db3a6b547c9153d03e5a9c84624b3169faa86a66..f25973e3f3f107b512ec75c2c91bfafb848abf0b 100644 (file)
@@ -563,6 +563,7 @@ JIT_OP_CALL:
 JIT_OP_CALL_TAIL:
        [] -> {
                jit_function_t func = (jit_function_t)(insn->dest);
+               arm_pop_frame_tail(inst, 0);
                arm_jump(inst, func->closure_entry);
        }
 
index 4d7b81f47a7a3f19bb06464386462ff3e703326e..75162c902d0b4a774e80eb33345e6762063d1adb 100644 (file)
@@ -355,7 +355,7 @@ int _jit_create_entry_insns(jit_function_t func)
 }
 
 /*@
- * @deftypefun int _jit_create_call_setup_insns (jit_function_t func, jit_type_t signature, {jit_value_t *} args, {unsigned int} num_args, int is_nested, int nested_level, jit_value_t *struct_return)
+ * @deftypefun int _jit_create_call_setup_insns (jit_function_t func, jit_type_t signature, {jit_value_t *} args, {unsigned int} num_args, int is_nested, int nested_level, jit_value_t *struct_return, int flags)
  * Create instructions within @code{func} necessary to set up for a
  * function call to a function with the specified @code{signature}.
  * Use @code{jit_insn_push} to push values onto the system stack,
@@ -376,7 +376,7 @@ int _jit_create_entry_insns(jit_function_t func)
 int _jit_create_call_setup_insns
        (jit_function_t func, jit_type_t signature,
         jit_value_t *args, unsigned int num_args,
-        int is_nested, int nested_level, jit_value_t *struct_return)
+        int is_nested, int nested_level, jit_value_t *struct_return, int flags)
 {
        jit_type_t type;
        jit_type_t vtype;
index 3a09e832edfd492cdd71e7af16eedbfee3c00a7a..bc683dbf58a4340501ee0e72dcaa7160a10daa40 100644 (file)
@@ -335,7 +335,7 @@ int _jit_create_entry_insns(jit_function_t func)
 int _jit_create_call_setup_insns
        (jit_function_t func, jit_type_t signature,
         jit_value_t *args, unsigned int num_args,
-        int is_nested, int nesting_level, jit_value_t *struct_return)
+        int is_nested, int nesting_level, jit_value_t *struct_return, int flags)
 {
        jit_type_t type = jit_type_get_return(signature);
        jit_value_t value;
index 253e913e9b074081b05e206fdd525c67cab6dc3f..3da10d6d2852da7839a610a0c6677cb7b541b22e 100644 (file)
@@ -1439,6 +1439,8 @@ JIT_OP_CALL:
 JIT_OP_CALL_TAIL:
        [] -> {
                jit_function_t func = (jit_function_t)(insn->dest);
+               x86_mov_reg_reg(inst, X86_ESP, X86_EBP, sizeof(void *));
+               x86_pop_reg(inst, X86_EBP);
                x86_jump_code(inst, func->closure_entry);
        }
 
index f73ab04b079f02f8dd7a55b768929789b428e587..43b604fe04fb45387368f95430221810e28fbb38 100644 (file)
@@ -170,7 +170,7 @@ int _jit_create_entry_insns(jit_function_t func);
 int _jit_create_call_setup_insns
        (jit_function_t func, jit_type_t signature,
         jit_value_t *args, unsigned int num_args,
-        int is_nested, int nesting_level, jit_value_t *struct_return);
+        int is_nested, int nesting_level, jit_value_t *struct_return, int flags);
 int _jit_setup_indirect_pointer(jit_function_t func, jit_value_t value);
 int _jit_create_call_return_insns
        (jit_function_t func, jit_type_t signature,
index f5c1d866fea02fe3a963393a59c39282ab396ddf..4141d1d7e49e7bf85acff444e267b9b00600aa4b 100644 (file)
@@ -655,6 +655,22 @@ void jit_value_ref(jit_function_t func, jit_value_t value)
        }
 }
 
+void _jit_value_ref_params(jit_function_t func)
+{
+       unsigned int num_params;
+       unsigned int param;
+       if(func->builder->param_values)
+       {
+               num_params = jit_type_num_params(func->signature);
+               for(param = 0; param < num_params; ++param)
+               {
+                       jit_value_ref(func, func->builder->param_values[param]);
+               }
+       }
+       jit_value_ref(func, func->builder->struct_return);
+       jit_value_ref(func, func->builder->parent_frame);
+}
+
 /*@
  * @deftypefun void jit_value_set_volatile (jit_value_t value)
  * Set a flag on a value to indicate that it is volatile.  The contents
index 33e244a27b688749ab1cb386ad9eb872d7ad1ad6..83b5148ef94fb9c140d3781e569bb92a2afea311 100644 (file)
@@ -1,5 +1,5 @@
 
-noinst_PROGRAMS = t1 t2 t3 t4
+noinst_PROGRAMS = t1 t2 t3 t4 t5
 
 t1_SOURCES = t1.c
 t1_LDADD = $(top_builddir)/jit/libjit.la
@@ -18,5 +18,9 @@ t4_LDADD = $(top_builddir)/jitplus/libjitplus.la $(top_builddir)/jit/libjit.la
 t4_DEPENDENCIES = $(top_builddir)/jitplus/libjitplus.la \
                                  $(top_builddir)/jit/libjit.la
 
+t5_SOURCES = t5.c
+t5_LDADD = $(top_builddir)/jit/libjit.la
+t5_DEPENDENCIES = $(top_builddir)/jit/libjit.la
+
 AM_CFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I. -I$(srcdir)
 AM_CXXFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I. -I$(srcdir)
index a4d67a2ed0c2103a03b34608dade9884707a21a8..1160e2b4873ea571fb8dd095164b03ae69893b2d 100644 (file)
@@ -90,9 +90,7 @@ int main(int argc, char **argv)
        jit_insn_return(function, temp4);
 
        /* Compile the function */
-       jit_dump_function(stdout, function, "gcd");
        jit_function_compile(function);
-       jit_dump_function(stdout, function, "gcd");
 
        /* Unlock the context */
        jit_context_build_end(context);
diff --git a/tutorial/t5.c b/tutorial/t5.c
new file mode 100644 (file)
index 0000000..7bac8a5
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+
+Tutorial 5 - gcd, with tail call optimization
+
+Builds and compiles the following function:
+
+unsigned int gcd(unsigned int x, unsigned int y)
+{
+    if(x == y)
+    {
+        return x;
+    }
+    else if(x < y)
+    {
+        return gcd(x, y - x);
+    }
+    else
+    {
+        return gcd(x - y, y);
+    }
+}
+
+*/
+
+#include <stdio.h>
+#include <jit/jit.h>
+
+int main(int argc, char **argv)
+{
+       jit_context_t context;
+       jit_type_t params[2];
+       jit_type_t signature;
+       jit_function_t function;
+       jit_value_t x, y;
+       jit_value_t temp1, temp2;
+       jit_value_t temp_args[2];
+       jit_label_t label1 = jit_label_undefined;
+       jit_label_t label2 = jit_label_undefined;
+       jit_uint arg1, arg2;
+       void *args[2];
+       jit_uint result;
+
+       /* Create a context to hold the JIT's primary state */
+       context = jit_context_create();
+
+       /* Lock the context while we build and compile the function */
+       jit_context_build_start(context);
+
+       /* Build the function signature */
+       params[0] = jit_type_uint;
+       params[1] = jit_type_uint;
+       signature = jit_type_create_signature
+               (jit_abi_cdecl, jit_type_uint, params, 2, 1);
+
+       /* Create the function object */
+       function = jit_function_create(context, signature);
+
+       /* Check the condition "if(x == y)" */
+       x = jit_value_get_param(function, 0);
+       y = jit_value_get_param(function, 1);
+       temp1 = jit_insn_eq(function, x, y);
+       jit_insn_branch_if_not(function, temp1, &label1);
+
+       /* Implement "return x" */
+       jit_insn_return(function, x);
+
+       /* Set "label1" at this position */
+       jit_insn_label(function, &label1);
+
+       /* Check the condition "if(x < y)" */
+       temp2 = jit_insn_lt(function, x, y);
+       jit_insn_branch_if_not(function, temp2, &label2);
+
+       /* Implement "return gcd(x, y - x)" */
+       temp_args[0] = x;
+       temp_args[1] = jit_insn_sub(function, y, x);
+       jit_insn_call(function, "gcd", function, 0, temp_args, 2, JIT_CALL_TAIL);
+
+       /* Set "label2" at this position */
+       jit_insn_label(function, &label2);
+
+       /* Implement "return gcd(x - y, y)" */
+       temp_args[0] = jit_insn_sub(function, x, y);
+       temp_args[1] = y;
+       jit_insn_call(function, "gcd", function, 0, temp_args, 2, JIT_CALL_TAIL);
+
+       /* Compile the function */
+       jit_function_compile(function);
+
+       /* Unlock the context */
+       jit_context_build_end(context);
+
+       /* Execute the function and print the result */
+       arg1 = 27;
+       arg2 = 14;
+       args[0] = &arg1;
+       args[1] = &arg2;
+       jit_function_apply(function, args, &result);
+       printf("gcd(27, 14) = %u\n", (unsigned int)result);
+
+       /* Clean up */
+       jit_context_destroy(context);
+
+       /* Finished */
+       return 0;
+}