1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-29 08:20:20 +02:00

update manual for jit_allocai

git-archimport-id: bonzini@gnu.org--2004b/lightning--stable--1.2--patch-38
This commit is contained in:
Paolo Bonzini 2006-11-20 12:59:13 +00:00
parent 82d90f4ddc
commit f748b3c5e7
3 changed files with 92 additions and 47 deletions

4
NEWS
View file

@ -5,6 +5,10 @@ o Initial support for x86-64 back-end.
o Many bug fixes. o Many bug fixes.
o jit_pushr/jit_popr are deprecated, you need to #define
JIT_NEED_PUSH_POP prior to including lightning.h if you
want to use them.
o Support for stack-allocated variables. Because of this, o Support for stack-allocated variables. Because of this,
backends defining JIT_FP should now rename it to JIT_AP. backends defining JIT_FP should now rename it to JIT_AP.
JIT_FP is now a user-visible register used in ldxi/ldxr JIT_FP is now a user-visible register used in ldxi/ldxr

View file

@ -858,7 +858,7 @@ operations:
@section Macros composing the platform-independent layer @section Macros composing the platform-independent layer
@table @b @table @b
@item Register names (all mandatory but the last two) @item Register names (all mandatory but the last three)
@example @example
#define JIT_R #define JIT_R
#define JIT_R_NUM #define JIT_R_NUM
@ -866,6 +866,7 @@ operations:
#define JIT_V_NUM #define JIT_V_NUM
#define JIT_FPR #define JIT_FPR
#define JIT_FPR_NUM #define JIT_FPR_NUM
#define JIT_FP
#define JIT_SP #define JIT_SP
#define JIT_AP #define JIT_AP
#define JIT_RZERO #define JIT_RZERO
@ -878,6 +879,7 @@ operations:
@item Mandatory: @item Mandatory:
@example @example
#define jit_allocai()
#define jit_arg_c() #define jit_arg_c()
#define jit_arg_i() #define jit_arg_i()
#define jit_arg_l() #define jit_arg_l()
@ -1022,11 +1024,9 @@ operations:
#define jit_orr_i(d, s1, s2) #define jit_orr_i(d, s1, s2)
#define jit_patch_at(jump_pc, value) #define jit_patch_at(jump_pc, value)
#define jit_patch_movi(jump_pc, value) #define jit_patch_movi(jump_pc, value)
#define jit_pop_i(rs)
#define jit_prepare_d(numargs) #define jit_prepare_d(numargs)
#define jit_prepare_f(numargs) #define jit_prepare_f(numargs)
#define jit_prepare_i(numargs) #define jit_prepare_i(numargs)
#define jit_push_i(rs)
#define jit_pusharg_i(rs) #define jit_pusharg_i(rs)
#define jit_ret() #define jit_ret()
#define jit_retval_i(rd) #define jit_retval_i(rd)
@ -1242,10 +1242,6 @@ operations:
#define jit_ori_ul(d, rs, is) #define jit_ori_ul(d, rs, is)
#define jit_orr_ui(d, s1, s2) #define jit_orr_ui(d, s1, s2)
#define jit_orr_ul(d, s1, s2) #define jit_orr_ul(d, s1, s2)
#define jit_pop_ui(rs)
#define jit_pop_ul(rs)
#define jit_push_ui(rs)
#define jit_push_ul(rs)
#define jit_pusharg_c(rs) #define jit_pusharg_c(rs)
#define jit_pusharg_p(rs) #define jit_pusharg_p(rs)
#define jit_pusharg_s(rs) #define jit_pusharg_s(rs)
@ -1478,8 +1474,6 @@ operations:
#define jit_ner_l(d, s1, s2) #define jit_ner_l(d, s1, s2)
#define jit_ori_l(d, rs, is) #define jit_ori_l(d, rs, is)
#define jit_orr_l(d, s1, s2) #define jit_orr_l(d, s1, s2)
#define jit_pop_l(rs)
#define jit_push_l(rs)
#define jit_pusharg_l(rs) #define jit_pusharg_l(rs)
#define jit_retval_l(rd) #define jit_retval_l(rd)
#define jit_rshi_l(d, rs, is) #define jit_rshi_l(d, rs, is)

View file

@ -90,10 +90,9 @@ is equivalent to @code{i} on 32-bit machines, and @code{p} is
substantially equivalent to @code{ul}). substantially equivalent to @code{ul}).
There are at least seven integer registers, of which six are There are at least seven integer registers, of which six are
general-purpose, while the last is used to contain the stack pointer general-purpose, while the last is used to contain the frame pointer
(@code{SP}). The stack pointer can be used to allocate and access local (@code{FP}). The frame pointer can be used to allocate and access local
variables on the stack (which is supposed to grow downwards in memory variables on the stack, using the @code{allocai} instruction.
on all architectures).
Of the general-purpose registers, at least three are guaranteed to be Of the general-purpose registers, at least three are guaranteed to be
preserved across function calls (@code{V0}, @code{V1} and preserved across function calls (@code{V0}, @code{V1} and
@ -271,15 +270,6 @@ stxr c uc s us i ui l ul p f d *(O1+O2) = O3
stxi c uc s us i ui l ul p f d *(O1+O2) = O3 stxi c uc s us i ui l ul p f d *(O1+O2) = O3
@end example @end example
@item Stack management
These accept a single register parameter. These operations are not
guaranteed to be efficient on all architectures.
@example
pushr i ui l ul p @r{push }O1@r{ on the stack}
popr i ui l ul p @r{pop }O1@r{ off the stack}
@end example
@item Argument management @item Argument management
These are: These are:
@example @example
@ -388,16 +378,13 @@ These accept one argument except @code{ret} which has none; the
difference between @code{finish} and @code{calli} is that the difference between @code{finish} and @code{calli} is that the
latter does not clean the stack from pushed parameters (if any) latter does not clean the stack from pushed parameters (if any)
and the former must @strong{always} follow a @code{prepare} and the former must @strong{always} follow a @code{prepare}
instruction. Results are undefined when using function calls instruction.
in a leaf function.
@example @example
calli (not specified) @r{function call to O1} calli (not specified) @r{function call to O1}
callr (not specified) @r{function call to a register} callr (not specified) @r{function call to a register}
finish (not specified) @r{function call to O1} finish (not specified) @r{function call to O1}
finishr (not specified) @r{function call to a register} finishr (not specified) @r{function call to a register}
jmpi/jmpr (not specified) @r{unconditional jump to O1} jmpi/jmpr (not specified) @r{unconditional jump to O1}
prolog (not specified) @r{function prolog for O1 args}
leaf (not specified) @r{the same for leaf functions}
ret (not specified) @r{return from subroutine} ret (not specified) @r{return from subroutine}
retval c uc s us i ui l ul p f d @r{move return value} retval c uc s us i ui l ul p f d @r{move return value}
@r{to register} @r{to register}
@ -407,6 +394,26 @@ Like branch instruction, @code{jmpi} also returns a value which is to
be used to compile forward branches. @xref{Fibonacci, , Fibonacci be used to compile forward branches. @xref{Fibonacci, , Fibonacci
numbers}. numbers}.
@item Function prolog
These macros are used to set up the function prolog, in particular to
declare the number of arguments accepted by a function, and to reserve
space on the stack to be used for variables. They accept a single
numeric argument.
@example
prolog (not specified) @r{function prolog for O1 args}
leaf (not specified) @r{the same for leaf functions}
allocai (not specified) @r{reserve space on the stack}
@end example
Results are undefined when using function calls in a leaf function.
@code{allocai} receives the number of bytes to allocate and returns
the offset from the frame pointer register @code{FP} to the base of
the area. The area is aligned to an @code{int}; future versions of
@lightning{} may provide more fine-grained control on the alignment of
stack-allocated variables.
@end table @end table
As a small appetizer, here is a small function that adds 1 to the input As a small appetizer, here is a small function that adds 1 to the input
@ -740,39 +747,72 @@ and is able to compile different formulas to different functions.
Here is the code for the expression compiler; a sample usage will Here is the code for the expression compiler; a sample usage will
follow. follow.
Since @lightning{} does not provide push/pop instruction, this
example uses a stack-allocated area to store the data. Such an
area can be allocated using the macro @code{jit_allocai}, which
receives the number of bytes to allocate and returns the offset
from the frame pointer register @code{JIT_FP} to the base of the
area. The area is aligned to an @code{int}; future versions
of @lightning{} may provide more fine-grained control on the
alignment of stack-allocated variables.
Usually, you will use the @code{ldxi} and @code{stxi} instruction
to access stack-allocated variables. However, it is possible to
use operations such as @code{add} to compute the address of the
variables, and pass the address around.
@example @example
#include <stdio.h> #include <stdio.h>
#include "lightning.h" #include "lightning.h"
typedef int (*pifi)(int); @rem{/* Pointer to Int Function of Int */} typedef int (*pifi)(int); @rem{/* Pointer to Int Function of Int */}
void stack_push(int reg, int *sp)
{
jit_stxi_i (*sp, JIT_FP, reg);
*sp += sizeof (int);
}
void stack_pop(int reg, int *sp)
{
*sp -= sizeof (int);
jit_ldxi_i (reg, JIT_FP, *sp);
}
pifi compile_rpn(char *expr) pifi compile_rpn(char *expr)
@{ @{
pifi fn; pifi fn;
int stack_base, stack_ptr;
int in; int in;
fn = (pifi) (jit_get_ip().iptr); fn = (pifi) (jit_get_ip().iptr);
jit_leaf(1); jit_leaf(1);
in = jit_arg_i(); in = jit_arg_i();
jit_getarg_i(JIT_R0, in); stack_ptr = stack_base = jit_allocai (32 * sizeof (int));
jit_getarg_i(JIT_R2, in);
while (*expr) @{ while (*expr) @{
char buf[32]; char buf[32];
int n; int n;
if (sscanf(expr, "%[0-9]%n", buf, &n)) @{ if (sscanf(expr, "%[0-9]%n", buf, &n)) @{
expr += n - 1; expr += n - 1;
jit_push_i(JIT_R0); stack_push(JIT_R0, &stack_ptr);
jit_movi_i(JIT_R0, atoi(buf)); jit_movi_i(JIT_R0, atoi(buf));
@} else if (*expr == 'x') @{
stack_push(JIT_R0, &stack_ptr);
jit_movi_i(JIT_R0, JIT_R2);
@} else if (*expr == '+') @{ @} else if (*expr == '+') @{
jit_pop_i(JIT_R1); stack_pop(JIT_R1, &stack_ptr);
jit_addr_i(JIT_R0, JIT_R1, JIT_R0); jit_addr_i(JIT_R0, JIT_R1, JIT_R0);
@} else if (*expr == '-') @{ @} else if (*expr == '-') @{
jit_pop_i(JIT_R1); stack_pop(JIT_R1, &stack_ptr);
jit_subr_i(JIT_R0, JIT_R1, JIT_R0); jit_subr_i(JIT_R0, JIT_R1, JIT_R0);
@} else if (*expr == '*') @{ @} else if (*expr == '*') @{
jit_pop_i(JIT_R1); stack_pop(JIT_R1, &stack_ptr);
jit_mulr_i(JIT_R0, JIT_R1, JIT_R0); jit_mulr_i(JIT_R0, JIT_R1, JIT_R0);
@} else if (*expr == '/') @{ @} else if (*expr == '/') @{
jit_pop_i(JIT_R1); stack_pop(JIT_R1, &stack_ptr);
jit_divr_i(JIT_R0, JIT_R1, JIT_R0); jit_divr_i(JIT_R0, JIT_R1, JIT_R0);
@} else @{ @} else @{
fprintf(stderr, "cannot compile: %s\n", expr); fprintf(stderr, "cannot compile: %s\n", expr);
@ -786,13 +826,20 @@ pifi compile_rpn(char *expr)
@} @}
@end example @end example
The principle on which the calculator is based is easy: the stack The principle on which the calculator is based is easy: the stack top
top is held in R0, while the remaining items of the stack are held is held in R0, while the remaining items of the stack are held in the
on the hardware stack. Compiling an operand pushes the old stack memory area that we allocate with @code{allocai}. Compiling a numeric
top onto the stack and moves the operand into R0; compiling an operand or the argument @code{x} pushes the old stack top onto the
operator pops the second operand off the stack into R1, and stack and moves the operand into R0; compiling an operator pops the
compiles the operation so that the result goes into R0, thus second operand off the stack into R1, and compiles the operation so
becoming the new stack top. that the result goes into R0, thus becoming the new stack top.
This example allocates a fixed area for 32 @code{int}s. This is not
a problem when the function is a leaf like in this case; in a full-blown
compiler you will want to analyze the input and determine the number
of needed stack slots---a very simple example of register allocation.
The area is then managed like a stack using @code{stack_push} and
@code{stack_pop}.
Try to locate a call to @code{jit_set_ip} in the source code. You Try to locate a call to @code{jit_set_ip} in the source code. You
will not find one; this means that the client has to manually set will not find one; this means that the client has to manually set
@ -814,8 +861,8 @@ int main()
int i; int i;
jit_set_ip(codeBuffer); jit_set_ip(codeBuffer);
c2f = compile_rpn("9*5/32+"); c2f = compile_rpn("32x9*5/+");
f2c = compile_rpn("32-5*9/"); f2c = compile_rpn("x32-5*9/");
jit_flush_code(codeBuffer, jit_get_ip().ptr); jit_flush_code(codeBuffer, jit_get_ip().ptr);
printf("\nC:"); printf("\nC:");
@ -845,9 +892,9 @@ generation so powerful.
The @file{rpn.c} file in the @lightning{} distribution includes a more The @file{rpn.c} file in the @lightning{} distribution includes a more
complete (and more complex) implementation of @code{compile_rpn}, complete (and more complex) implementation of @code{compile_rpn},
which does constant folding, allows the argument to the functions which does constant folding and is able to assemble instructions with
to be used more than once, and is able to assemble instructions with an immediate parameter. Still, it is based on the same principle and
an immediate parameter. also uses @code{allocai} to allocate space for the stack.
@node Fibonacci @node Fibonacci
@section Fibonacci numbers @section Fibonacci numbers
@ -1160,9 +1207,9 @@ extern void _opt_muli_i(struct jit_state *, int, int, int);
@section Registers @section Registers
@chapter Accessing the whole register file @chapter Accessing the whole register file
As mentioned earlier in this chapter, all @lightning{} back-ends As mentioned earlier in this chapter, all @lightning{} back-ends are
are guaranteed to have at least six integer registers and six guaranteed to have at least six general-purpose integer registers and
floating-point registers, but many back-ends will have more. six floating-point registers, but many back-ends will have more.
To access the entire register files, you can use the To access the entire register files, you can use the
@code{JIT_R}, @code{JIT_V} and @code{JIT_FPR} macros. They @code{JIT_R}, @code{JIT_V} and @code{JIT_FPR} macros. They