aboutsummaryrefslogtreecommitdiff
path: root/src/boot/llvm/llasm.ml
blob: 70cf24706713d41883225c4bcee2324d07afafc2 (plain) (blame)
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
(*
 * machine-specific assembler routines.
 *)

open Common;;

type asm_glue =
    {
      asm_activate_glue : Llvm.llvalue;
      asm_yield_glue : Llvm.llvalue;
      asm_upcall_glues : Llvm.llvalue array;
    }
;;

let n_upcall_glues = 7
;;

(* x86-specific asm. *)

let x86_glue
  (llctx:Llvm.llcontext)
  (llmod:Llvm.llmodule)
  (abi:Llabi.abi)
  (sess:Session.sess)
  : asm_glue =
  let (prefix,align) =
    match sess.Session.sess_targ with
        Linux_x86_elf -> ("", 4)
      | Win32_x86_pe -> ("_",4)
      | MacOS_x86_macho -> ("_", 16)
  in
  let save_callee_saves =
    ["pushl %ebp";
     "pushl %edi";
     "pushl %esi";
     "pushl %ebx";]
  in
  let restore_callee_saves =
    ["popl  %ebx";
     "popl  %esi";
     "popl  %edi";
     "popl  %ebp";]
  in
  let load_esp_from_rust_sp =
    [ Printf.sprintf "movl  %d(%%edx), %%esp"
        (Abi.task_field_rust_sp * 4)]
  in
  let load_esp_from_runtime_sp =
    [ Printf.sprintf "movl  %d(%%edx), %%esp"
        (Abi.task_field_runtime_sp * 4) ]
  in
  let store_esp_to_rust_sp     =
    [ Printf.sprintf "movl  %%esp, %d(%%edx)"
        (Abi.task_field_rust_sp * 4) ]
  in
  let store_esp_to_runtime_sp  =
    [ Printf.sprintf "movl  %%esp, %d(%%edx)"
        (Abi.task_field_runtime_sp * 4) ]
  in

  let list_init i f = (Array.to_list (Array.init i f)) in
  let list_init_concat i f = List.concat (list_init i f) in

  let glue =
    [
      ("rust_activate_glue",
       String.concat "\n\t"
         (["movl  4(%esp), %edx    # edx = rust_task"]
          @ save_callee_saves
          @ store_esp_to_runtime_sp
          @ load_esp_from_rust_sp
            (* 
             * This 'add' instruction is a bit surprising.
             * See lengthy comment in boot/be/x86.ml activate_glue.
             *)
          @ [ Printf.sprintf
                "addl  $20, %d(%%edx)"
                (Abi.task_field_rust_sp * 4) ]

          @ restore_callee_saves
          @ ["ret"]));

      ("rust_yield_glue",
       String.concat "\n\t"

         (["movl  0(%esp), %edx    # edx = rust_task"]
          @ load_esp_from_rust_sp
          @ save_callee_saves
          @ store_esp_to_rust_sp
          @ load_esp_from_runtime_sp
          @ restore_callee_saves
          @ ["ret"]))
    ]
    @ list_init n_upcall_glues
      begin
        fun i ->
          (* 
           * 0, 4, 8, 12 are callee-saves
           * 16 is retpc
           * 20 is taskptr
           * 24 is callee
           * 28 .. (7+i) * 4 are args
           *)

          ((Printf.sprintf "rust_upcall_%d" i),
           String.concat "\n\t"
             (save_callee_saves
              @ ["movl  %esp, %ebp     # ebp = rust_sp";
                 "movl  20(%esp), %edx # edx = rust_task"]
              @ store_esp_to_rust_sp
              @ load_esp_from_runtime_sp
              @ [Printf.sprintf
                   "subl  $%d, %%esp   # esp -= args" ((i+1)*4);
                 "andl  $~0xf, %esp    # align esp down";
                 "movl  %edx, (%esp)   # arg[0] = rust_task "]

              @ (list_init_concat i
                   begin
                     fun j ->
                       [ Printf.sprintf "movl  %d(%%ebp),%%edx" ((j+7)*4);
                         Printf.sprintf "movl  %%edx,%d(%%esp)" ((j+1)*4) ]
                   end)

              @ ["movl  24(%ebp), %edx # edx = callee";
                 "call  *%edx          # call *%edx";
                 "movl  20(%ebp), %edx # edx = rust_task"]
              @ load_esp_from_rust_sp
              @ restore_callee_saves
              @ ["ret"]))
      end
  in

  let _ =
    Llvm.set_module_inline_asm llmod
      begin
        String.concat "\n"
          begin
            List.map
              begin
                fun (sym,asm) ->
                  Printf.sprintf
                    "\t.globl %s%s\n\t.balign %d\n%s%s:\n\t%s"
                    prefix sym align prefix sym asm
              end
              glue
          end
      end
  in

  let decl_cdecl_fn name out_ty arg_tys =
    let ty = Llvm.function_type out_ty arg_tys in
    let fn = Llvm.declare_function name ty llmod in
      Llvm.set_function_call_conv Llvm.CallConv.c fn;
      fn
  in

  let decl_glue s =
    let task_ptr_ty = Llvm.pointer_type abi.Llabi.task_ty in
    let void_ty = Llvm.void_type llctx in
      decl_cdecl_fn s void_ty [| task_ptr_ty |]
  in

  let decl_upcall n =
    let task_ptr_ty = Llvm.pointer_type abi.Llabi.task_ty in
    let word_ty = abi.Llabi.word_ty in
    let callee_ty = word_ty in
    let args_ty =
      Array.append
        [| task_ptr_ty; callee_ty |]
        (Array.init n (fun _ -> word_ty))
    in
    let name = Printf.sprintf "rust_upcall_%d" n in
      decl_cdecl_fn name word_ty args_ty
  in
    {
      asm_activate_glue = decl_glue "rust_activate_glue";
      asm_yield_glue = decl_glue "rust_yield_glue";
      asm_upcall_glues = Array.init n_upcall_glues decl_upcall;
    }
;;

(* x64-specific asm. *)
(* arm-specific asm. *)
(* ... *)


let get_glue
  (llctx:Llvm.llcontext)
  (llmod:Llvm.llmodule)
  (abi:Llabi.abi)
  (sess:Session.sess)
  : asm_glue =
  match sess.Session.sess_targ with
      Linux_x86_elf
    | Win32_x86_pe
    | MacOS_x86_macho ->
        x86_glue llctx llmod abi sess
;;


(*
 * Local Variables:
 * fill-column: 78;
 * indent-tabs-mode: nil
 * buffer-file-coding-system: utf-8-unix
 * compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
 * End:
 *)