1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
  
14
  
15
  
16
  
17
  
18
  
19
  
20
  
21
  
22
  
23
  
24
  
25
  
26
  
27
  
28
  
29
  
30
  
31
  
32
  
33
  
34
  
35
  
36
  
37
  
38
  
39
  
40
  
41
  
42
  
43
  
44
  
45
  
46
  
47
  
48
  
49
  
50
  
51
  
52
  
53
  
54
  
55
  
56
  
57
  
58
  
59
  
60
  
61
  
62
  
63
  
64
  
65
  
66
  
67
  
68
  
69
  
70
  
71
  
72
  
73
  
74
  
75
  
76
  
77
  
78
  
79
  
80
  
81
  
82
  
83
  
84
  
85
  
86
  
87
  
88
  
89
  
90
  
91
  
92
  
93
  
94
  
95
  
96
  
97
  
98
  
99
  
100
  
101
  
102
  
103
  
104
  
105
  
106
  
107
  
108
  
109
  
110
  
111
  
112
  
113
  
114
  
115
  
116
  
117
  
118
  
119
  
120
  
121
  
122
  
123
  
124
  
125
  
126
  
127
  
128
  
129
  
130
  
131
  
132
  
133
  
134
  
135
  
136
  
137
  
138
  
139
  
140
  
141
  
142
  
143
  
144
  
145
  
146
  
147
  
148
  
149
  
150
  
151
  
152
  
153
  
154
  
155
  
156
  
157
  
158
  
159
  
160
  
161
  
162
  
163
  
164
  
165
  
166
  
167
  
168
  
169
  
170
  
171
  
172
  
173
  
174
  
175
  
176
  
177
  
178
  
179
  
180
  
181
  
182
  
183
  
184
  
185
  
186
  
187
  
188
  
189
  
190
  
191
  
192
  
193
  
194
  
195
  
196
  
197
  
198
  
199
  
200
  
201
  
202
  
203
  
204
  
205
  
206
  
207
  
208
  
209
  
210
  
211
  
212
  
213
  
214
  
215
  
216
  
217
  
218
  
219
  
220
  
221
  
222
  
223
  
224
  
225
  
226
  
227
  
228
  
229
  
230
  
231
  
232
  
233
  
234
  
235
  
236
  
237
  
238
  
239
  
240
  
241
  
242
  
243
  
244
  
245
  
246
  
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.