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
|
- optype_t exists on all architectures, but is arch specific. There
must be fewer than 32 values in it. (This may be bumped to 64
on all arches if 32 is not enough).
- op_t exists on all architectures. It is always a 64 bit integer,
where the low 8 bits contain an op_type. The rest of the integer
is machine specific
- inst_t is a struct that has three mandatory fields:
- name
- number of ops
- for each op, a bit field describing which optypes are acceptable
(remember there are only 32 different optypes).
It will typically have additional fields describing the encoding of
the instruction.
Files:
- pixman-assembler.h:
- includes pixman-arch-assembler.h
- declaration of machine_spec_t, which is a struct that describes the
machine in question.
- declaration of general assembler methods:
pixman_assembler_t *_pixman_assembler_create (const machine_spec_t *machine);
uint8_t *_pixman_assembler_link (pixman_assembler_t *assembler,
pixman_fragment_t *fragment,
...);
pixman_fragment_t *_pixman_fragment_new (const machine_spec_t *machine);
void pixman_fragment_assemble (fragment_t *fragment,
...);
- declaration of stack manager and register allocator
void pixman_reg_alloc_init (reg_alloc_t *alloc,
stack_man_t *stack_man,
const machine_spec *spec,
const reg_pool_t *pool);
- pixman-assembler.c:
- includes pixman-assembler.h
- has code to:
- based on instruction name, it will find the entry in inst_t
that matches the given ops.
- locate labels, based on their names
- fix up jumps and other relocations
it will call out to methods defined in the machine spec.
- implementation of register allcoator and stack manager.
- pixman-<arch>-assembler.h contains:
- definition of op_type_t, inst_t for the arch in question
- definition of op_t for the architecture in question
- macros to construct op_t for the architecture in question
- declaration of pixman_assembler_create_<arch>
- pixman-arch-assembler.c contains
- definition of inst_t table
- definition of pixman_assembler_<arch>_create()
- definition of machine_spec_t for the architecture, with methods for
- how to spill to the stack
- how to patch up jumps
- definition of register pools
- user code will
- include pixman-assembler.h
- define pixman_reg_pools as it sees fit
- call pixman_assembler_create_<arch>
- call pixman_reg_alloc_init_<arch>
Should all the machine_spec stuff go in the user code? Is there any real difference?
Where should the current code-manager.c go? This is platform specific,
not machine specific. pixman-assembler.c is tempting.
Also, pixman-assembler.[ch] should probably be pixman-jit.[ch]
Notes about x86:
How to call a statically known function (e.g, memcpy() or malloc())
from JIT compiled code:
- In 64 bit mode:
The JIT compiled code can end up more than 4GB away from the
function, which means a standard call with 32-bit offset is not good
enough. Instead we have to do "call RIP_REL(label)", where label
refers to a place where the address of the function in question is
stored.
It might be possible to detect at compile time whether the final
generated code would be less than 4GB away, which would allow a call
rel32 to be used. It gets a little bit tricky though because the
rel32 has a different length, and in general we don't know the final
address of the jit compiled code until later.
- In 32 bit mode:
This is easy: Just do <call rel32> and have the linker fix it up
before generating the final code.
|