summaryrefslogtreecommitdiff
path: root/portability
blob: ba5e8832ebe1a2191469230bb2df31068500b9f5 (plain)
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.