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:
parent
82d90f4ddc
commit
f748b3c5e7
3 changed files with 92 additions and 47 deletions
4
NEWS
4
NEWS
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
123
doc/using.texi
123
doc/using.texi
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue