• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Interaction of compiled code and the runtime
2
3## Introduction
4
5Compiled code and Panda runtime interact with each other during execution. This document describes the following interactions:
6* Runtime structures
7* Calling convention
8* Structure of compiled code stack frames and stack traversing
9* Transition from the interpreter to compiled code and vice versa
10* Calling the runtime
11* Deoptimization
12* Stack unwinding during exception handling
13
14## Panda runtime (the runtime)
15Panda runtime is a set of functions used to execute managed code. The runtime consists of several modules.
16The document describes the interpreter and the compiler modules.
17
18The interpreter, as a part of runtime, executes bytecodes of managed functions and manages hotness
19counter (see [Structure of `panda::Method`](#structure-of-pandamethod)) of managed functions.
20
21The compiler translates bytecodes of managed functions to native code. The compiler provides
22`panda::CompilerInterface::CompileMethodSync` for compilation. When a function is compiled, the compiler changes
23its entrypoint to the native code generated. When the function is called next time, the native code will be executed.
24
25## Calling convention
26Panda runtime and managed code must call functions according to the target calling convention.
27Compiled code of a managed function must accept one extra argument: the pointer to `panda::Method` which describes this function.
28This argument must be the first argument.
29
30Example:
31Consider a function int max(int a, int b).
32When the compiler generates native code for this function for ARM target it must consider that
33the function accepts 3 arguments:
34- a pointer to `panda::Method` in the register R0.
35- `a` in the register R1
36- `b` in the register R2
37
38The function must return the result in the register R0.
39
40## Structure of `panda::ManagedThread`
41`panda::ManagedThread` has the following fields that compiled code may use:
42
43| Field                | Type                  | Description |
44| ---                  | ----                  | ----------- |
45| sp_flag_             | bool*                 | Safepoint flag. See *Safepoints* in memory_management.md. |
46| pending_exception_   | panda::ObjectHeader*  | A pointer to a thrown exception or 0 if there is no exception thrown. |
47| runtime_entrypoints_ | void*[]               | A table of runtime entrypoints (See [Runtime entrypoints](#runtime_entrypoints)). |
48| stack_frame_kind_    | StackFrameKind        | A kind of the current stack frame (compiled code or interpreter stack frame). |
49
50## Access to `panda::ManagedThread` from compiled code
51There is an allocated register for each target architecture to store a pointer to `panda::ManagedThread`. This register is called `thread register` and
52must contain a valid pointer to `panda::ManagedThread` on entry to each compiled function.
53
54## Runtime entrypoints
55Runtime serves compiled code via runtime entrypoints. A runtime entrypoint is a function which conforms to the target calling convention.
56A table of the entrypoints is located in `panda::ManagedThread::runtime_entrypoints_` which could be accessed via `thread register`.
57
58## Structure of `panda::Method`
59`panda::Method` describes a managed function in the runtime.
60This document describes the following fields of `panda::Method`:
61
62| Field | Description |
63| ----- | ----------- |
64| hotness_counter_ | A hotness counter of the managed function. |
65| compiled_entry_point_ | Function entrypoint. |
66
67### Hotness counter
68The field `hotness_counter_` reflects hotness of a managed function. The interpreter increments it each time the function is called,
69backward branch is taken and call instruction is handled.
70When the hotness counter gets saturated (reaches the threshold), the interpreter triggers compilation of the function.
71Panda runtime provides a command line option to tune the hotness counter threshold: `--compiler-hotness-threshold`.
72
73### Entrypoint
74Entrypoint is a pointer to native code which can execute the function. This code must conform to the target calling convention and must accept
75one extra argument: a pointer to `panda::Method` ( See [Calling convention](#calling_convention)).
76The managed function may have compiled code or be executed by the interpreter.
77If the function has compiled code the `compiled_entry_point_` must point to compiled code.
78If the function is executed by the interpreter, the `compiled_entry_point_` must point to a runtime function `CompiledCodeToInterpreterBridge` which calls the interpreter.
79
80## Stack frame
81A stack frame contains data necessary to execute the function the frame belongs to.
82The runtime can create several kinds of stack frames. But all the frames of managed code must have the structure described in [Compiled code stack frame](#compiled-code-stack-frame).
83
84### Interpreter stack frame
85Interpreter stack frame is described by `panda::Frame` class. The class has fields to store virtual registers and a pointer to the previous stack frame.
86All the consecutive interpreter stack frames are organized into a linked list. The field `panda::Frame::prev_` contains a pointer to the previous interpreter (or compiled bridge) frame.
87
88### Compiled code stack frame
89Each compiled function is responsible for reserving stack frame for its purpose and then release it when the function doesn't need it.
90Generally compiled function builds the stack frame in prolog and releases it in epilog. If a compiled function doesn't require
91the stack frame it can omit its creation.
92When compiled code is being executed the `stack pointer` register must point to a valid stack frame (new stack frame created for the caller) and the
93`frame pointer` register must point to correct place in the frame before the following operations:
94* Managed objects access
95* Safepoint flag access
96* Call of managed functions or runtime entrypoints
97
98Release of the stack frame could be done by restoring values of `stack pointer` and `frame pointer` registers to the value they have at the moment of function entry.
99
100Compiled code stack frames of caller and callee must be continuous in the stack i.e. the callee's stack frame must immediately follow the caller's stack frame.
101
102A compiled code stack frame must have the following structure:
103
104```
105(Stack grows in increasing order: higher slot has lower address)
106-----+----------------------+ <- Stack pointer
107     | Callee parameters    |
108     +----------------------+
109     | Spills               |
110     +----------------------+
111     | Caller saved fp regs |
112  D  +----------------------+
113  A  | Caller saved regs    |
114  T  +----------------------+
115  A  | Callee saved fp regs |
116     +----------------------+
117     | Callee saved regs    |
118     +----------------------+
119     | Locals               |
120-----+----------------------+
121  H  | Properties           |
122  E  +----------------------+
123  A  | panda::Method*       |
124  D  +----------------------+ <- Frame pointer
125  E  | Frame pointer        |
126  R  +----------------------+
127     | Return address       |
128-----+----------------------+
129```
130Stack frame elements:
131- data - arbitrary data necessary for function execution. It is optional.
132- properties - define properties of the frame, i.e. whether it is OSR frame or not.
133- `panda::Method*` - pointer to `panda::Method`, which describes the called function.
134- frame pointer - pointer to the previous frame. The value of `frame pointer` is registered at the moment of function entry.
135- return address - address to which control will be transferred after the function gets returned.
136
137There are two special registers: `stack pointer` and `frame pointer`.
138`stack pointer` register contains a pointer to the last stack element.
139`frame pointer` register contains a pointer to the place in the stack where the return address is stored.
140
141Panda has a special class for getting cframe layout: `class CFrameLayout`. Everything related to the cframe layout
142should be processed via this class.
143
144## Calling a function from compiled code
145To call a managed function, the compiled code must resolve it (i.e. retrieve a pointer to callee's `panda::Method`),
146prepare arguments in the registers and the stack (if necessary) and jump to callee's entrypoint.
147Resolving of a function could be done by calling the corresponding runtime entrypoint.
148
149Example:
150Calling `int max(int a, int b)` function from compiled code on ARM architecture with arguments `2` and `3`
151could be described by the following pseudocode:
152```
153// tr - thread register
154// r0 contains a pointer to the current `panda::Method`
155// Step 1: Resolve `int max(int, int)`
156mov r1, MAX_INT_INT_ID // MAX_INT_INT_ID - identifier of int max(int, int) function
157ldr lr, [tr, #RESOLVE_RUNTIME_ENTRYPOINT_OFFSET]
158blx lr // call resolve(currentMethod, MAX_INT_INT_ID)
159// r0 contains a pointer to `panda::Method` which describes `int max(int, int)` function.
160// Step 2: Prepare arguments and entrypoint to call `int max(int, int)`
161mov r1, #2
162mov r2, #3
163lr = ldr [r0, #entrypoint_offset]
164// Step 3: Call the function
165blx lr // call max('max_method', 2, 3)
166// r0 contains the function result
167```
168
169## Calling a function from compiled code: Bridge function
170The compiler has an entrypoints table. Each entrypoint contains a link to the bridge function.
171The bridge function is auto-generated for each runtime function to be called using the macro assembly.
172The bridge function sets up the Boundary Frame and performs the call to the actual runtime function.
173
174To do a runtime call from compiled code the compiler generates:
175* Puts callee saved (if need) and parameter holding (if any) register values to the stack.
176* (callee saved registers goes to bridge function stack frame, and caller saved registers goes to the current stack frame).
177* Setups parameter holding register values.
178* Loads bridge function address and branch instruction.
179* Restores register values.
180
181The bridge function does the following:
182* Setups the bridge function stack frame.
183* Pushes the caller saved registers (except of registers holding function parameters) to the caller's stack frame.
184* Adjusts the Stack Pointer, and pass execution to the runtime function.
185* Restores the Stack Pointer and caller saved registers.
186
187Bridge function stack frame:
188```
189--------+------------------------------------------+
190        | Return address                           |
191        +------------------------------------------+
192 HEADER | Frame pointer                            |
193        +------------------------------------------+
194        | COMPILED_CODE_TO_INTERPRETER_BRIDGE flag |
195        +------------------------------------------+
196        | - unused -                               |
197--------+------------------------------------------+
198        |                                          |
199        | Callee saved regs                        |
200        |                                          |
201 DATA   +------------------------------------------+
202        |                                          |
203        | Callee saved fp regs                     |
204        |                                          |
205--------+------------------------------------------+
206        +  16-byte alignment pad to the next frame +
207```
208
209## Transition from the interpreter to compiled code
210When the interpreter handles a call instruction, it first resolves the callee method.
211The procedure varies depending on the callee's entrypoint:
212If the entrypoint points to `CompiledCodeToInterpreterBridge`, the interpreter executes the callee. In this case the interpreter calls itself directly.
213In other cases, the interpreter calls the function `InterpreterToCompiledCodeBridge` to pass the resolved callee function,
214the call instruction, the interpreter's frame and the pointer to `panda::ManagedThread`.
215
216`InterpreterToCompiledCodeBridge` function does the following:
217* Builds a boundary stack frame.
218* Sets the pointer to `panda::ManagedThread` to the thread register.
219* Changes stack frame kind in `panda::ManagedThread::stack_frame_kind_` to compiled code stack frame.
220* Prepares the arguments according to the target calling convention. The function uses the bytecode instruction (which must be a variant of `call` instruction)
221    and interpreter's frame to retrieve the function's arguments.
222* Jumps to the callee's entrypoint.
223* Saves the returned result to the interpreter stack frame.
224* Changes stack frame kind in `panda::ManagedThread::stack_frame_kind_` back to interpreter stack frame.
225* Drops the boundary stack frame.
226
227`InterpreterToCompiledCodeBridge`'s boundary stack frame is necessary to link the interpreter's frame with the compiled code's frame.
228Its structure is depicted below:
229```
230---- +----------------+ <- stack pointer
231b s  | INTERPRETER_   |
232o t  | TO_COMPILED_   |
233u a  | CODE_BRIDGE    |
234n c  +----------------+ <- frame pointer
235d k  | pointer to the |
236a    | interpreter    |
237r f  | frame          |
238y r  |                |
239  a  +----------------+
240  m  | return address |
241  e  |                |
242---- +----------------+
243```
244
245The structure of boundary frame is the same as a stack frame of compiled code.
246Instead of pointer to `panda::Method` the frame contains constant `INTERPRETER_TO_COMPILED_CODE_BRIDGE`.
247Frame pointer points to the previous interpreter frame.
248
249## Transition from compiled code to the interpreter
250If a function needs be executed by the interpreter, it must have `CompiledCodeToInterpreterBridge` as an entrypoint.
251`CompiledCodeToInterpreterBridge` does the following:
252* Changes stack frame kind in `panda::ManagedThread::stack_frame_kind_` to interpreter stack frame.
253* Creates a boundary stack frame which contains room for interpreter frame.
254* Fills in the interpreter frame by the arguments passed to `CompiledCodeToInterpreterBridge` in the registers or via the stack.
255* Calls the interpreter.
256* Stores the result in registers or in the stack according to the target calling convention.
257* Drops the boundary stack frame.
258* Changes stack frame kind in `panda::ManagedThread::stack_frame_kind_` back to compiled code stack frame.
259
260`CompiledCodeToInterpreterBridge`'s boundary stack frame is necessary to link the compiled code's frame with the interpreter's frame.
261Its structure is depicted below:
262```
263---- +----------------+ <-+ stack pointer
264  s  | interpreter's  | -+ `panda::Frame::prev_`
265b t  | frame          |  |
266o a  +----------------+ <+ frame pointer
267u c  | frame pointer  |
268n k  +----------------+
269d    | COMPILED_CODE_ |
270a f  | TO_            |
271r r  | INTERPRETER_   |
272y a  | BRIDGE         |
273  m  +----------------+
274  e  | return address |
275---- +----------------+
276     |     ...        |
277```
278
279The structure of boundary frame is the same as a stack frame of compiled code.
280Instead of a pointer to `panda::Method` the frame contains constant `COMPILED_CODE_TO_INTERPRETER_BRIDGE`.
281Frame pointer points to the previous frame in compiled code stack frame.
282The field `panda::Frame::prev_` must point to the boundary frame pointer.
283
284## Stack traversing
285Stack traversing is performed by the runtime. When the runtime examines a managed thread's stack, the thread cannot execute any managed code.
286Stack unwinding always starts from the top frame, the kind of which can be determined from `panda::ManagedThread::stak_frame_kind_` field. The pointer
287to the top frame can be determined depending on the kind of the top stack frame:
288* If the top stack frame is an interpreter stack frame, the address of the interpreter's frame can be retrieved from `panda::ManagedThread::GetCurrentFrame()`.
289* If the top stack frame is a compiled code stack frame, `frame pointer` register contains the address of the top stack frame.
290
291Having a pointer to the top stack frame, its kind and structure the runtime can move to the next frame.
292Moving to the next frame is done according to the table below:
293
294| Kind of the current stack frame | How to get a pointer to the next stack frame | Kind of the previous stack frame |
295| ------------------------------- | -------------------------------------------- | -------------------------------- |
296| Interpreter stack frame         | Read `panda::Frame::prev_` field             | Interpreter stack frame or COMPILED_CODE_TO_INTERPRETER boundary frame |
297| INTERPRETER_TO_COMPILED_CODE_BRIDGE boundary stack frame | Read `pointer to the interpreter frame` from the stack | Interpreter stack frame |
298| COMPILED_CODE_TO_INTERPRETER_BRIDGE boundary stack frame | Read `frame pointer` from the stack | Compiled code stack frame |
299| Compiled code stack frame | Read `frame pointer` | Compiled code stack frame or INTERPRETER_TO_COMPILED_CODE_BRIDGE boundary frame|
300
301Thus the runtime can traverse all the managed stack frames moving from one frame to the previous frame and changing frame type
302crossing the boundary frames.
303
304Unwinding of stack frames has specifics.
305* Compiled code could be combined from several managed functions (inlined functions). If the runtime needs to get information about inlined functions
306during handling a compiled code stack frame, it uses meta information generated by the compiler.
307* Compiled code may save any callee-saved registers on the stack. Before moving to the next stack frame the runtime must restore values of these registers.
308To do that the runtime uses information about callee-saved registers stored on the stack. This information is generated by the compiler.
309* Values of virtual registers could be changed during stack unwinding. For example, when GC moves an object, it must update all the references to the object.
310The runtime should provide an internal API for changing values of virtual registers.
311
312Example:
313Consider the following call sequence:
314```
315         calls        calls
316    foo --------> bar ------> baz
317(interpreted)  (compiled)  (interpreted)
318```
319Functions `foo` and `baz` are executed by the interpreter and the function `bar` has compiled code.
320In this situation the stack might look as follow:
321```
322---- +----------------+ <- stack pointer
323E    | native frame   |
324x u  | of             |
325e t  | interpreter    |
326c e  |                |
327---- +----------------+ <--- `panda::ManagedThread::GetCurrentFrame()`
328b    | baz's          | -+
329o s  | interperer     |  |
330u t  | stack frame    |  |
331n a  +----------------+<-+
332d c  | frame pointer  | -+
333a k  +----------------+  |
334r    | COMPILED_CODE_ |  |
335y f  | TO_            |  |
336  r  | INTERPRETER_   |  |
337  a  | BRIDGE         |  |
338  m  +----------------+  |
339  e  | return address |  |
340---- +----------------+  |
341     |      data      |  |
342     +----------------+  |
343 b   | panda::Method* |  |
344 a   +----------------+ <+
345 r   | frame pointer  | -+
346     +----------------+  |
347     | return address |  |
348---- +----------------+  |
349b s  | INTERPRETER_   |  |
350o t  | TO_COMPILED_   |  |
351u a  | CODE_BRIDGE    |  |
352n c  +----------------+ <+
353d k  | pointer to the | -+
354a    | interpreter    |  |
355r f  | frame          |  |
356y r  |                |  |
357  a  +----------------+  |
358  m  | return address |  |
359  e  |                |  |
360---- +----------------+  |
361E    | native frame   |  |
362x u  | of             |  |
363e t  | interpreter    |  |
364c e  |                |  |
365---- +----------------+  |
366     |      ...       |  |
367     +----------------+ <+
368     | foo's          |
369     | interpreter    |
370     | frame          |
371     +----------------+
372     |       ...      |
373```
374
375The runtime determines kind of the top stack frame by reading `panda::ManagedThread::stack_frame_kind_` (the top stack frame kind must be interpreter stack frame).
376`panda::ManagedThread::GetCurrentFrame()` method must return the pointer to `baz`'s interpreter stack frame.
377To go to the previous frame the runtime reads the field `panda::Frame::prev_` which must point to `COMPILED_CODE_TO_INTERPRETER_BRIDGE` boundary stack frame.
378It means that to get `bar`'s stack frame the runtime must read `frame pointer` and the kind of the next frame will be compiled code's frame.
379At this step the runtime has a pointer to `bar`'s compiled code stack frame. To go to the next frame runtime reads `frame pointer` again and gets
380`INTERPRETER_TO_COMPILED_CODE_BRIDGE` boundary stack frame. To reach `foo`'s interpreter stack frame the runtime reads `pointer to the interpreter's frame` field.
381
382## Deoptimization
383There may be a situation when compiled code cannot continue execution for some reason.
384For such cases compiled code must call `void Deoptimize()` runtime entrypoint to continue execution of the method in the interpreter from the point where compiled code gets stopped.
385The function reconstructs the interpreter stack frame and calls the interpreter.
386When compiled code is combined from several managed functions (inlined functions) `Deoptimize` reconstructs interpreter stack frame and calls the interpreter for each inlined function too.
387
388Details in [deoptimization documentation](deoptimization.md)
389## Throwing an exception
390Throwing an exception from compiled code is performed by calling the runtime entrypoint `void ThrowException(panda::ObjectHeader* exception)`.
391The function `ThrowException` does the following:
392* Saves all the callee-saved registers to the stack
393* Stores the pointer to the exception object to `panda::ManagedThread::pending_exception_`
394* Unwinds compiled code stack frames to find the corresponding exception handler by going from one stack frame to the previous and making checks.
395
396If the corresponding catch handler is found in the current stack frame, the runtime jumps to the handler.
397
398If an INTERPRETER_TO_COMPILED_CODE_BRIDGE boundary stack frame is reached, the runtime returns to the interpreter, letting it handle the exception.
399Returning to the interpreter is performed as follow:
4001. Determine the return address to the boundary frame. The return address is stored in the following compiled code stack frame.
4012. Set the pointer to the boundary frame into stack pointer, and assign the return address determined at the previous step to program counter.
402
403If there is no catch handler in the current frame then the runtime restores values of callee-saved registers and moves to the previous stack frame.
404
405Details of stack traversing are described in [Stack traversing](#stack_traversing)
406
407Finding a catch handler in a compiled code stack frame is performed according meta information generated by the compiler.
408
409The interpreter must ignore the returned value if `panda::ManagedThread::pending_exception_` is not 0.
410