Code generation templates for Pike. |
|
These paired files should all implement the following functions/macros: |
|
PIKE_OPCODE_T |
Type with opcode granularity. This is defined in ../program.h. |
|
PIKE_OPCODE_T *PROG_COUNTER; |
Return the current program counter. |
|
void ins_pointer(INT32 ptr); |
Store a 32bit pointer at the current offset. |
|
INT32 read_pointer(INT32 off); |
Read a 32bit pointer from the specified offset. |
|
void upd_pointer(INT32 off, INT32 ptr); |
Store a 32bit pointer at the specified offset. |
|
INT32 LOW_GET_JUMP(void); |
Extract a 32bit pointer from the position following this |
instruction. Note that if OPCODE_RETURN_JUMPADDR is set, the |
value in PROG_COUNTER typically needs to be offset to |
compensate for machine code that is after the opcode function |
call (see JUMP_SET_TO_PC_AT_NEXT). |
|
void LOW_SKIPJUMP(void); |
Advance PROG_COUNTER past a 32bit pointer. Note that if |
OPCODE_RETURN_JUMPADDR is set, the value in PROG_COUNTER |
typically needs to be offset to compensate for machine code |
that is after the opcode function call (see |
JUMP_SET_TO_PC_AT_NEXT). |
|
void ins_align(INT32 align); |
Align the current offset to the specified alignment. |
|
void ins_byte(INT32 val); |
Insert an 8bit unsigned value at the current offset. |
|
void ins_data(INT32 val); |
Insert a 32bit value at the current offset. |
|
INT32 read_program_data(PIKE_OPCODE_T *origin, int offset) |
Read a data item stored by ins_data. Note that the offset |
is in number of data units. |
|
void ins_f_byte(unsigned int op); |
Insert the opcode 'op' at the current offset. |
|
Code to update Pike_fp->pc to point to the current offset |
should be inserted first, but it's only necessary if the |
source line or file has changed since the previous opcode. |
|
Also, if PIKE_DEBUG is defined and the opcode is completely |
inlined, then a call to simple_debug_instr_prologue_0 should |
be inserted before the opcode itself but after the Pike_fp->pc |
update (if there is any). |
|
void ins_f_byte_with_arg(unsigned int op, unsigned INT32 arg); |
Insert the opcode 'op' with the primary argument 'arg' at |
the current offset. See ins_f_byte for further details. |
|
void ins_f_byte_with_2_args(unsigned int op, |
unsigned INT32 arg1, |
unsigned INT32 arg2); |
Insert the opcode 'op' with the primary argument 'arg1' and |
the secondary argument 'arg2' at the current offset. See |
ins_f_byte for further details. |
|
void UPDATE_PC(void) |
Insert code to update Pike_fp->pc to the current position. |
|
INT32 READ_INCR_BYTE(PIKE_OPCODE_T *pc); |
Return the byte stored at 'pc' by ins_byte(), and increment |
'pc' to the next legal position. |
|
Optional macros: |
|
void INIT_INTERPRETER_STATE(void) |
Called once during initialization of the interpreter. Typically |
used to detect and configure CPU specific options. Since it |
get called after the instrs table has been initialized (but before |
it has been used), it may alter it. |
|
void CALL_MACHINE_CODE(PIKE_OPCODE_T *pc) |
Start execution of the machine-code located at 'pc'. |
NOTE: This macro does not return, but instead contains |
code that returns from the calling context. The value |
returned in the macro should be one of -1 (inter return), |
or -2 (inter escape catch). |
|
void EXIT_MACHINE_CODE() |
Clean up from CALL_MACHINE_CODE. |
|
void START_NEW_FUNCTION(int store_lines) |
Called at the start of a function. store_lines is true for any |
non-constant evaluation function. This hook can be used to |
add common helper subroutines and/or reset code-generator state. |
|
void END_FUNCTION(int store_lines) |
Called after all f-codes for a function have been emitted. |
Typically used to clean up after START_NEW_FUNCTION(). |
store_lines will contain the same value as when |
START_NEW_FUNCTION() was called. |
|
void SET_PROG_COUNTER(PIKE_OPCODE_T *newpc) |
Set PROG_COUNTER to a new value. |
|
GLOBAL_DEF_PROG_COUNTER; |
Declare stuff that is needed for PROG_COUNTER at the global |
level in the interpreter. |
|
DEF_PROG_COUNTER; |
Declare stuff that is needed for PROG_COUNTER in each opcode |
function. |
|
int PIKE_OPCODE_ALIGN; |
Alignment restriction for PIKE_OPCODE_T (debug). |
|
void INS_ENTRY(void) |
Mark the entry point from eval_instruction(). |
Useful to add startup code. Note that this in turn |
will typically require use of OPCODE_INLINE_RETURN. |
|
int ENTRY_PROLOGUE_SIZE |
Size (in opcodes) of the prologue inserted by INS_ENTRY, which |
should be skipped e.g. when tail recursing. Required when |
INS_ENTRY is used. |
|
void RELOCATE_program(struct program *p, PIKE_OPCODE_T *new); |
Relocate the copy of 'p'->program at 'new' to be able |
to execute at the new position. |
|
void FLUSH_INSTRUCTION_CACHE(void *addr, size_t len); |
Flush the memory at 'addr' from the instruction cache. |
|
void ENCODE_PROGRAM(struct program *p, struct dynamic_buffer_s *buf); |
Encode 'p'->program in a way accepted by DECODE_PROGRAM(). |
NOTE: The encoded data MUST have the length p->num_program * |
sizeof(PIKE_OPCODE_T). |
|
void DECODE_PROGRAM(struct program *p) |
Decode 'p'->program as encoded by ENCODE_PROGRAM(). |
NOTE: 'p'->relocations is valid at this point. |
|
void FLUSH_CODE_GENERATOR_STATE(void) |
Called at labels and beginning of functions to notify |
the code generator that registers and other states |
must be updated at this point. |
|
void ADJUST_PIKE_PC(PIKE_OPCODE_T *pc) |
Called after opcodes that modify Pike_fp->pc. The passed |
argument is the pc they will put there. Useful when UPDATE_PC |
inserts code that update Pike_fp->pc relatively. (Note: Not |
used anymore.) |
|
int ALIGN_PIKE_JUMPS |
This can be defined to a number which will cause Pike to |
insert zeroes in the code after instructions which do not |
return to permit better alignment of code. Please note that |
this is not guaranteed and should only be used for optimization. |
|
int ALIGN_PIKE_FUNCTION_BEGINNINGS |
Similar to ALIGN_PIKE_JUMPS, but only for the beginning |
of a function. |
|
int INS_F_JUMP(unsigned int op, int backward_jump) |
Similar to ins_f_byte, but is only called for jump and branch |
instructions that take a constant target address. The return |
value should be the offset in the program to the empty address |
field of the jump instruction, which will be filled in by |
UPDATE_F_JUMP. You can also return -1 to make the code use the |
same behaviour as if INS_F_JUMP was not defined. |
|
backward_jump is only relevant for branch opcodes if |
OPCODE_INLINE_BRANCH is defined. If it's set, a call to |
branch_check_threads_etc should be compiled in whenever the |
branch is taken. |
|
int INS_F_JUMP_WITH_ARG(unsigned int op, unsigned INT32 arg, int backward_jump) |
Similar to INS_F_JUMP(), but called for instructions that take |
one parameter. |
|
int INS_F_JUMP_WITH_TWO_ARGS(unsigned int op, |
unsigned INT32 arg1, |
unsigned INT32 arg2, |
int backward_jump) |
Similar to INS_F_JUMP(), but called for instructions that take |
two parameters. |
|
void UPDATE_F_JUMP(INT32 offset, INT32 to_offset) |
If you define any of the INS_F_JUMP* functions you must also |
define this one. It's called when the compiler knows where to |
jump. (See ia32.c for an example of this and INS_F_JUMP.) |
|
INT32 READ_F_JUMP(INT32 offset) |
If you define any of the INS_F_JUMP* functions you must also |
define this one. It's called when the compiler needs to read |
back the to_offset that was passed to UPDATE_F_JUMP. |
|
OPCODE_INLINE_BRANCH |
If defined, test functions that return nonzero when the branch |
is to be taken will be generated for I_BRANCH instructions. |
The machine code generated by INS_F_JUMP* must test the return |
value for those opcodes and jump iff it's nonzero. This is to |
facilitate easier inlining of branches in the machine code. |
|
OPCODE_INLINE_RETURN |
If defined, opcode functions that perform INTER_RETURN will |
return (void *)(ptrdiff_t)-1 when they want to exit from |
the running interpreter. These opcodes also have the I_RETURN |
flag set. This is to facilitate easier use of and clean up |
of INS_ENTRY(). |
|
OPCODE_RETURN_JUMPADDR |
If defined, jump functions that return the address to jump to |
will be generated for I_JUMP instructions, so the ins_f_byte* |
must generate machine code that (unconditionally) jumps to the |
return value for those opcodes. If this isn't defined, they |
will instead use SET_PROG_COUNTER to change the address they |
return to. This macro allows faster code on cpus where setting |
the return address wreaks havoc in the instruction pipelines. |
|
JUMP_SET_TO_PC_AT_NEXT(PIKE_OPCODE_T *PC) |
Used in I_JUMP opcodes to store the pc to the next |
instruction, to compensate for any machine code that is |
inserted after the call. PC is the lvalue where it should be |
stored. Must be defined if OPCODE_RETURN_JUMPADDR is. |
|
void CHECK_RELOC(size_t reloc, size_t program_size) |
Check if a relocation is valid for the program. |
Should throw an error on bad relocations. |
|
void DISASSEMBLE_CODE(void *addr, size_t bytes) |
Debug function that dumps the generated code on stderr. |
|
Help structures and functions implemented in other places: |
|
struct instr instrs[]; |
Array of bytecode instruction definitions. Indexed by |
F-opcode minus F_OFFSET. See opcodes.h for details. |
|
PIKE_OPCODE_T *inter_return_opcode_F_CATCH(PIKE_OPCODE_T *addr) |
Function to simplify implementation of F_CATCH in |
OPCODE_INLINE_RETURN mode. See interpret.c for details. |
|