Initial snarf.
[shack.git] / arch / x86 / util / x86_mir_atom.kupo
blob20f9cc31ca852007aabd84b5e78071634a8318c0
1 (*
2    Generate x86 code from an MIR atom expression tree.
3    Copyright (C) 2002,2001 Justin David Smith, Caltech
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License
7    as published by the Free Software Foundation; either version 2
8    of the License, or (at your option) any later version.
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *)
21 (* Useful modules *)
22 open Format
24 open Mir
25 open Mir_pos
26 open Mir_exn
27 open Mir_print
29 open Symbol
30 open Sizeof_const
32 open Frame_type
33 open X86_runtime
34 open X86_build
35 open X86_frame
36 open X86_mir_env
37 open X86_inst_type
40 (* Declare the types of some common variable names used in the
41    OCaml code below.  In theory, these declarations can be over-
42    ridden by dcl statements when necessary. *)
43 declarations:
44    (* Labels *)
45    lbl label;
47    (* Operands *)
48    op;
49    op1;
50    op2;
51    ohi;
52    olo;
53    ohi1;
54    olo1;
55    ohi2;
56    olo2;
59 (* Begin MIR transform code *)
60 inline_kupo:
61 begin_ocaml_code`
63 module Pos = MakePos (struct let name = "X86_mir_atom" end)
64 open Pos
67 (* Constants *)
68 let one32 = Int32.one
69 let three32 = Int32.of_int 3
70 let minus_one32 = Int32.of_int (-1)
73 (* Imports *)
74 let reg64_lookup { cg_reg64 = reg64 } = reg64_lookup reg64
75 let fvals_lookup { cg_fvals = fvals } = fvals_lookup fvals
78 let atom_warning msg =
79    eprintf "x86_mir_atom warning:  %s\n" msg
82 (***  Utilities  ***)
85 (* int32_ok
86    Checks to see if this integer atomclass is int32-manipulation
87    friendly.  Bytes and words are generally represented by 32-bit
88    ints while in registers, therefore most operations that work
89    on int32 can be used unmodified for the int8, int16 cases.  *)
90 let int32_ok = function
91    Rawint.Int8 | Rawint.Int16 | Rawint.Int32 -> true
92  | Rawint.Int64 -> false
95 (* ptr_ok
96    Checks to see if the atomclass given is a valid pointer class.
97    This function will not consider ACPointerInfix to be a valid
98    pointer; such pointers must be converted to real base or offset
99    pointers before use.  *)
100 let ptr_ok = function
101    ACFunction _
102  | ACPointer _ ->
103       true
104  | ACInt
105  | ACRawInt _
106  | ACFloat _
107  | ACPoly
108  | ACPointerInfix _ ->
109       false
112 (* reorder_atoms
113    If the first atom is a constant, then this reorders the atoms
114    so that appears as the second atom.  Usually instructions can
115    be more easily optimized if constant terms appear as the second
116    operand, instead of the first.  *)
117 let reorder_atoms a1 a2 =
118    match a1 with
119       AtomInt _
120     | AtomRawInt _
121     | AtomFloat _  -> a2, a1
122     | _            -> a1, a2
125 (* build_atom_block
126    Constructs a block with instructions given, that jumps to the
127    next block as determined by running cont on operand v.  This
128    is mostly just a shorthand notation for something that will
129    be /frequently/ computed...  *)
130 let build_atom_block debug name insts v cont =
131    let blocks, label = cont v in
132    let block, label = code_build_dst_block debug name insts label in
133       block :: blocks, label
136 (* add_binop_inst
137    Add the instruction to the listbuf. *)
138 let add_binop_inst cons insts op1 op2 =
139    `+insts:
140       add_inst `{ cons op1 op2 `};`
143 (***  Coercions  ***)
146 (* build_int_of_int32
147    Build the int31 representation.  *)
148 let build_int_of_int32 insts op =
149    `+insts:
150       sall  op,   $1;
151       orl   op,   $1;`
154 (* build_int32_of_int
155    Build an int32 from the int31 representation.  *)
156 let build_int32_of_int insts op =
157    `+insts:
158       sarl  op,   $1;`
161 (* build_nop
162    No transformation applied to the operand indicated.  *)
163 let build_nop insts _ = insts
166 (* code_int32_of_float_op
167    Coerces a floating-point value to an int32 value.  This accomplishes
168    the task by loading the value onto the stack, then popping it off as
169    a signed integer.  Warning: we only played the signed int game here.
170    Op has already been decoded; this constructs a new block.  *)
171 let code_int32_of_float_op debug coerce op cont =
172    let insts, v =
173       `new insts -> v:
174          new   %v;
175          comm_string "Coercing a float to an int32";
176          fldt  op;
177          fistp float_reg_1;
178          modify_listbuf `{ coerce insts (Register float_reg_1) `};
179          movl  v,    float_reg_1;`
180    in
181       build_atom_block debug "coerce_int32_of_float" insts v cont
184 (* code_float_of_int32_op
185    Coerces a signed int32 to a floating-point value.  This accomplishes
186    the task by loading the value onto the stack as a signed int, then
187    popping it off as a float.  *)
188 let code_float_of_int32_op debug coerce op cont =
189    let insts, v =
190       `new insts -> v:
191          new   @v;
192          comm_string "Coercing an int32 to a float";
193          movl  float_reg_1,   op;
194          modify_listbuf `{ coerce insts (Register float_reg_1) `};
195          fild  float_reg_1;
196          fstpt v;`
197    in
198       build_atom_block debug "coerce_float_of_int32" insts v cont
201 (* code_int32_of_rawint_op
202    Coerces a rawint of any 32-bit precision into a rawint which has
203    at most 32-bits of precision.  64-bit values are not permitted
204    here.  The operand is already converted when this is called. *)
205 let code_int32_of_rawint_op pos debug pre signed op cont =
206    let pos = string_pos "code_int32_of_rawint_op" pos in
207    let mov_extend pre op =
208       if signed then
209          MOVS (pre, op, op)
210       else
211          MOVZ (pre, op, op)
212    in
213    let v = Register (new_symbol_string "coerce") in
214    let r =
215       match pre with
216          Rawint.Int8 ->
217             (* Note that for 8-bit source, we must somehow ensure that
218                the register used supports 8-bit referencing; the only
219                way to do that with the current allocator is pick a
220                specific register that is safe for this purpose; I will
221                use ECX since that is always getting clobbered... *)
222             ecx
223        | Rawint.Int16
224        | Rawint.Int32
225        | Rawint.Int64 ->
226             (* For other cases, we can use any hardware register. *)
227             new_symbol_string "coerce_tmp"
228    in
229    let source_string =
230       match pre, signed with
231          Rawint.Int8,  true  -> "int8"
232        | Rawint.Int16, true  -> "int16"
233        | Rawint.Int32, true  -> "int32"
234        | Rawint.Int64, true  -> "int64"
235        | Rawint.Int8,  false -> "uint8"
236        | Rawint.Int16, false -> "uint16"
237        | Rawint.Int32, false -> "uint32"
238        | Rawint.Int64, false -> "uint64"
239    in
240    let insts = Listbuf.of_list
241       [CommentString ("Rawint " ^ source_string ^ " to Int32 coercion");
242        code_load_int32 r op]
243    in
244    let insts =
245       match pre with
246          Rawint.Int8 ->
247             Listbuf.add insts (mov_extend IB (Register r))
248        | Rawint.Int16 ->
249             Listbuf.add insts (mov_extend IW (Register r))
250        | Rawint.Int32 ->
251             insts
252        | Rawint.Int64 ->
253             raise (MirException (pos, InternalError "Int64 cannot be used as a destination here"))
254    in
255    let insts = Listbuf.add insts (code_store_int32 v r) in
256       build_atom_block debug "coerce_int32_of_rawint" insts v cont
259 (* code_int64_of_float
260    Coerces a floating-point value to an int64 value.  This accomplishes
261    the task by loading the value onto the stack, then popping it off as
262    a signed integer.  Warning: we only played the signed int game here. *)
263 let rec code_int64_of_float cg pos debug a cont =
264    let pos = string_pos "code_int64_of_float" pos in
265       code_atom_float cg pos debug a (fun op ->
266          let insts, vhi, vlo =
267             `new -> vhi, vlo:
268                new      %vhi, %vlo;
269                comm_string "Coercing a float to an int64";
270                fldt     op;
271                fistp64  float_reg_1;
272                movl     vlo,  float_reg_1;
273                movl     vhi,  float_reg_2;`
274          in
275          let blocks, label = cont vhi vlo in
276          let block, label = code_build_dst_block debug "coerce_int64_of_float" insts label in
277             block :: blocks, label)
280 (* code_float_of_int64
281    Coerces a signed int64 to a floating-point value.  This accomplishes
282    the task by loading the value onto the stack as a signed int, then
283    popping it off as a float.  *)
284 and code_float_of_int64 cg pos debug a cont =
285    let pos = string_pos "code_float_of_int64" pos in
286       code_atom_int64 cg pos debug a (fun ohi olo ->
287          let insts, v =
288             `new -> v:
289                new      @v;
290                comm_string "Coercing an int64 to a float";
291                movl     float_reg_1, olo;
292                movl     float_reg_2, ohi;
293                fild64   float_reg_1;
294                fstpt    v;`
295          in
296             build_atom_block debug "coerce_float_of_int64" insts v cont)
299 (* code_int32_of_float
300    code_float_of_int32
301    code_int_of_float
302    code_float_of_int
303    Toplevel functions for coercing integers to floats and vice versa.  *)
304 and code_int32_of_float cg pos debug a cont =
305    let pos = string_pos "code_int32_of_float" pos in
306       code_atom_float cg pos debug a (fun op ->
307          code_int32_of_float_op debug build_nop op cont)
309 and code_float_of_int32 cg pos debug a cont =
310    let pos = string_pos "code_float_of_int32" pos in
311       code_atom_int32 cg pos debug a (fun op ->
312          code_float_of_int32_op debug build_nop op cont)
314 and code_int_of_float cg pos debug a cont =
315    let pos = string_pos "code_int_of_float" pos in
316       code_atom_float cg pos debug a (fun op ->
317          code_int32_of_float_op debug build_int_of_int32 op cont)
319 and code_float_of_int cg pos debug a cont =
320    let pos = string_pos "code_float_of_int" pos in
321       code_atom_int cg pos debug a (fun op ->
322          code_float_of_int32_op debug build_int32_of_int op cont)
325 (* code_int32_of_int
326    Coerces a signed int to a rawint value.  *)
327 and code_int32_of_int cg pos debug a cont =
328    let pos = string_pos "code_int32_of_int" pos in
329       code_atom_int cg pos debug a (fun op ->
330          let insts, v =
331             `new -> v:
332                new   %v;
333                comm_string "Coercing an int to an int32";
334                movl  v, op;`
335          in
336          let insts = build_int32_of_int insts v in
337             build_atom_block debug "coerce_int32_of_int" insts v cont)
340 (* code_int_of_int32
341    Coerces a rawint value into a signed ML int.  *)
342 and code_int_of_int32 cg pos debug a cont =
343    let pos = string_pos "code_int_of_int32" pos in
344       code_atom_int32 cg pos debug a (fun op ->
345          let insts, v =
346             `new -> v:
347                new   %v;
348                comm_string "Coercing an int32 to an int";
349                movl  v, op;`
350          in
351          let insts = build_int_of_int32 insts v in
352             build_atom_block debug "coerce_int_of_int32" insts v cont)
355 (* code_int32_of_rawint
356    Coerces a rawint of any precision into a rawint which has
357    at most 32-bits of precision.  64-bit result values are
358    not permitted here.  NOTE: the resulting type is always
359    a 32-bit value which is sign-extended or zero-extended
360    appropriately.  *)
361 and code_int32_of_rawint cg pos debug pre signed a cont =
362    let pos = string_pos "code_int32_of_rawint" pos in
364    (* coerce_small
365       Assuming spre was already a 32-bit value, coerce to whatever...
366       Operand has already been converted to an int32-compatible value
367       by this point (if coercion from an int64 was necessary to begin
368       with).  *)
369    let coerce_small op =
370       code_int32_of_rawint_op pos debug pre signed op cont
371    in
373    (* coerce_large
374       Assuming spre was a 64-bit value, coerce to a general
375       32-bit value before applying the specific coerction to
376       dpre. *)
377    let coerce_large ohi olo =
378       let insts, v =
379          `new -> v:
380             new   %r,   %v;
381             comm_string "Int64 to Int32 coercion";
382             movl  r,    olo;
383             movl  v,    r;`
384       in
385          build_atom_block debug "coerce_int32_of_int64" insts v cont
386    in
388       (* Determine whether we need to chop down a 64-bit value into
389          a 32-bit value...  If the latter, we need to cut down the
390          64-bit value before continuing... *)
391       if int32_ok pre then
392          code_atom_int32 cg pos debug a coerce_small
393       else
394          code_atom_int64 cg pos debug a coerce_large
397 (* code_int64_of_rawint
398    Coerce a rawint value of any precision into a 64-bit value.  *)
399 and code_int64_of_rawint cg pos debug spre signed a cont =
400    if int32_ok spre then
401       (* A 32-bit value must be coerced to 64-bits. *)
402       code_atom_int32 cg pos debug a (fun op ->
403          let insts, vhi, vlo =
404             if signed then
405                `new -> vhi, vlo:
406                   new   %vhi, %vlo;
407                   comm_string "Rawint signed coercion to int64 (sign-extend)";
408                   movl  eax,  op;
409                   cltd;
410                   movl  vlo,  eax;
411                   movl  vhi,  edx;`
412             else
413                `new -> vhi, vlo:
414                   new   %vhi, %vlo, %r;
415                   comm_string "Rawint unsigned coercion to int64 (zero-extend)";
416                   movl  r,    op;
417                   movl  vlo,  r;
418                   movl  vhi,  $0;`
419          in
420          let blocks, label = cont vhi vlo in
421          let block, label = code_build_dst_block debug "coerce_int64_of_int32" insts label in
422             block :: blocks, label)
423    else
424       (* No change in internal representation; ignore *)
425       code_atom_int64 cg pos debug a cont
428 (***  Relative Operations  ***)
431 (* code_cond_int_op
432    See below; this is a more generic form that allows you to
433    specify arbitrary operands for the TRUE and FALSE values. *)
434 and code_cond_int_op debug op cont v op1 op2 tval fval =
435    (* Construct success and failure blocks *)
436    let blocks, label = cont v in
437    let isucc = Listbuf.of_elt (MOV (IL, v, tval)) in
438    let ifail = Listbuf.of_elt (MOV (IL, v, fval)) in
439    let bsucc, lsucc = code_build_dst_block debug "relop_succ" isucc label in
440    let bfail, lfail = code_build_dst_block debug "relop_fail" ifail label in
442    (* Construct the comparison operation itself *)
443    let build_cmp op1 op2 = CMP (IL, op1, op2) in
444    let insts = build_relop_int32 build_cmp Listbuf.empty op op1 op2 lsucc in
446    (* Assemble all the blocks together *)
447    let block, label = code_build_dst_block debug "relop" insts lfail in
448       block :: bfail :: bsucc :: blocks, label
451 (* code_relop_int_op
452    See code_relop_int32; this auxiliary function handles the
453    actual conditional code for comparison, but does not do the
454    coercions that the higher-level functions require.  *)
455 and code_relop_int_op debug op cont v op1 op2 =
456    let one = ImmediateNumber (OffNumber Int32.one) in
457    let zero = ImmediateNumber (OffNumber Int32.zero) in
458       code_cond_int_op debug op cont v op1 op2 one zero
461 (* code_relop_int
462    Relations work transparently on ML ints, however we should use
463    the special encoder for ML ints to code the atoms themselves.  *)
464 and code_relop_int cg pos debug op a1 a2 cont =
465    let pos = string_pos "code_relop_int" pos in
466       code_basic_binop_int cg pos debug a1 a2 (code_relop_int_op debug op cont)
469 (* code_relop_poly
470    Relations work transparently on poly values, however we should use
471    the special encoder for poly values to code the atoms themselves.  *)
472 and code_relop_poly cg pos debug op a1 a2 cont =
473    let pos = string_pos "code_relop_poly" pos in
474       code_basic_binop_poly cg pos debug a1 a2 (code_relop_int_op debug op cont)
477 (* code_relop_int32
478    Wrapper for the various integer relative operations that you can use.  *)
479 and code_relop_int32 cg pos debug pre signed op a1 a2 cont =
480    let pos = string_pos "code_relop_int32" pos in
481       code_basic_binop_int32 cg pos debug a1 a2 (fun v op1 op2 ->
482          code_int32_of_rawint_op pos debug pre signed op1 (fun op1 ->
483             code_int32_of_rawint_op pos debug pre signed op2 (fun op2 ->
484                code_relop_int_op debug op cont v op1 op2)))
487 (* code_relop_ptr
488    Wrapper for operations that compare pointers...  *)
489 and code_relop_ptr cg pos debug pre signed op a1 a2 cont =
490    let pos = string_pos "code_relop_ptr" pos in
491       code_atom_ptr_off cg pos debug a1 (fun op1 ->
492          code_atom_ptr_off cg pos debug a2 (fun op2 ->
493             let v = Register (new_symbol_string "relop_res") in
494                code_relop_int_op debug op cont v op1 op2))
497 (* code_relop_int64
498    See code_relop_int32; this does pretty much the same damn thing
499    except for 64-bit integers.  A distinguishing feature here is
500    the need for a slightly different function structure due to the
501    way 64-bit ints are handled.  *)
502 and code_relop_int64 cg pos debug op a1 a2 cont =
503    let pos = string_pos "code_relop_int64" pos in
504       code_atom_int64 cg pos debug a1 (fun ohi1 olo1 ->
505          code_atom_int64 cg pos debug a2 (fun ohi2 olo2 ->
506             (* Construct success and failure blocks *)
507             let v = Register (new_symbol_string "relop_res") in
508             let blocks, label = cont v in
509             let isucc = Listbuf.of_elt (MOV (IL, v, ImmediateNumber (OffNumber Int32.one))) in
510             let ifail = Listbuf.of_elt (MOV (IL, v, ImmediateNumber (OffNumber Int32.zero))) in
511             let bsucc, lsucc = code_build_dst_block debug "relop_succ" isucc label in
512             let bfail, lfail = code_build_dst_block debug "relop_fail" ifail label in
514             (* Construct the comparison operation itself *)
515             let build_cmp op1 op2 = CMP (IL, op1, op2) in
516             let insts = build_relop_int64 build_cmp Listbuf.empty op ohi1 olo1 ohi2 olo2 lsucc lfail in
518             (* Assemble all the blocks together *)
519             let block, label = code_build_dst_block debug "relop" insts lfail in
520                block :: bfail :: bsucc :: blocks, label))
523 (* code_relop_rawint
524    Handles any type of rawint expression. *)
525 and code_relop_rawint cg pos debug pre signed op a1 a2 cont =
526    if int32_ok pre then
527       code_relop_int32 cg pos debug pre signed op a1 a2 cont
528    else
529       code_relop_int64 cg pos debug op a1 a2 cont
532 (* code_cond_float_op
533    See below; this is a more generic form that allows you to
534    specify arbitrary operands for the TRUE and FALSE values. *)
535 and code_cond_float_op debug op cont v op1 op2 tval fval =
536    (* Construct success and failure blocks *)
537    let blocks, label = cont v in
538    let isucc = Listbuf.of_elt (MOV (IL, v, tval)) in
539    let ifail = Listbuf.of_elt (MOV (IL, v, fval)) in
540    let bsucc, lsucc = code_build_dst_block debug "relop_float_succ" isucc label in
541    let bfail, lfail = code_build_dst_block debug "relop_float_fail" ifail label in
543    (* Construct the comparison code *)
544    let insts = build_relop_float Listbuf.empty op op1 op2 lsucc in
545    let block, label = code_build_dst_block debug "relop_float" insts lfail in
546       block :: bfail :: bsucc :: blocks, label
549 (* code_relop_float_op
550    Similar to above, but provides default values for T and F.  *)
551 and code_relop_float_op debug op cont v op1 op2 =
552    code_cond_float_op debug op cont v op1 op2 (ImmediateNumber (OffNumber Int32.one)) (ImmediateNumber (OffNumber Int32.zero))
555 (* code_relop_float
556    Similar to the above relops, but this one works for floats.  We cannot
557    directly use JCC on floating-point comparisons because they set flags
558    in the FP status word, not the main flags.  Furthermore, we're forced
559    to load the status word into AX (it's either that or memory) to check
560    the flags.  This sucks. *)
561 and code_relop_float cg pos debug op a1 a2 cont =
562    let pos = string_pos "code_relop_float" pos in
563       code_basic_binop_float cg pos debug a1 a2 (fun _ op1 op2 ->
564          (* The variable generated is a float var; we need an int *)
565          let v = Register (new_symbol_string "float_relop_res") in
566             code_relop_float_op debug op cont v op1 op2)
569 (***  Int/Int32 Operations  ***)
572 (* code_unop_int
573    Writes a general-purpose unary operation for 32-bit values.
574    cons contains code to build a list of instructions which are
575    added to the inst buffer given to it.  Note that cont is
576    expected to convert the result into int31 representation!  *)
577 and code_unop_int cg pos debug a cont cons =
578    let pos = string_pos "code_unop_int" pos in
579    code_atom_int cg pos debug a (fun op ->
580       let insts, v =
581          `new insts -> v:
582             new   %r,   %v;
583             movl  r,    op;
584             modify_listbuf `{ cons insts (Register r) `};
585             movl  v,    r;`
586       in
587          build_atom_block debug "unop" insts v cont)
590 (* code_unop_int32
591    Writes a general-purpose unary operation for 32-bit values.
592    cons contains code to build the actual inst (single-form).  *)
593 and code_unop_int32 cg pos debug a cont cons =
594    let pos = string_pos "code_unop_int32" pos in
595    code_atom_int32 cg pos debug a (fun op ->
596       let insts, v =
597          `new -> v:
598             new   %r,   %v;
599             movl  r,    op;
600             add_inst `{ cons (Register r) `};
601             movl  v,    r;`
602       in
603          build_atom_block debug "unop" insts v cont)
606 (* code_mem_header
607    Writes code to access a dword in a header (the
608    word that contains the index and the tag).  cons contains
609    code to build an inst that will return either the index or
610    the tag (depending on which you want), it takes the value
611    of that header dword as an input.  *)
612 and code_mem_header_int word cg pos debug a cont cons =
613    let pos = string_pos "code_mem_header1" pos in
614    code_atom_ptr_base cg pos debug a (fun op ->
615       let insts, v =
616          `new insts -> v:
617             dcl   $word;
618             new   %r1,  %r2,  %v;
619             movl  r1,   op;
620             movl  r2,   *(r1, word);
621             modify_listbuf `{ cons insts (Register r2) `};
622             movl  v,    r2;`
623       in
624          build_atom_block debug "mem_header1" insts v cont)
627 (* code_mem_funindex
628    Writes code to access the closure index of a function pointed
629    to by the given atom.  The atom MUST point to the beginning of
630    an escape version of a function in the program; otherwise, well
631    you'll get interesting results for sure.  The index will be
632    packaged up into the operand passed to cont.  *)
633 and code_mem_funindex_int cg pos debug a cont cons =
634    let pos = string_pos "code_mem_funindex" pos in
635       code_mem_header_int index_word32 cg pos debug a cont cons
637 and code_mem_funindex_int32 cg pos debug a cont =
638    code_mem_funindex_int cg pos debug a cont (fun insts _ -> insts)
641 (* code_fun_pointer
642    Writes code to load the address of the _escape_ version
643    of the function indicated by the label v.  An operand to
644    the resulting pointer will be passed to cont.  *)
645 and code_fun_pointer_int32 cg pos debug v cont =
646    let pos = string_pos "code_fun_pointer_int32" pos in
647    let { cg_esc_funs = esc_funs } = cg in
648    let v', _, _ = try SymbolTable.find esc_funs v with
649       Not_found -> raise (MirException (pos, UnboundVar v))
650    in
651    let insts, v'' =
652       `new -> v'':
653          new   %r,   %v'';
654          dcl   lbl v';
656          comm_string `{ Printf.sprintf "Function %s escapes to %s" (string_of_symbol v) (string_of_symbol v') `};
657          leal  r,    v';
658          movl  v'',  r;`
659    in
660       build_atom_block debug "fun_pointer" insts v'' cont
663 (* code_basic_binop_int32
664    Writes the infrastructure used by most binary operations.  *)
665 and code_basic_binop_int32 cg pos debug a1 a2 cont =
666    let pos = string_pos "code_basic_binop_int32" pos in
667    let v = Register (new_symbol_string "binop_res") in
668    code_atom_int32 cg pos debug a1 (fun op1 ->
669       code_atom_int32 cg pos debug a2 (fun op2 ->
670          cont v op1 op2))
673 (* code_basic_binop_int
674    Writes the infrastructure used by most binary operations.  *)
675 and code_basic_binop_int cg pos debug a1 a2 cont =
676    let pos = string_pos "code_basic_binop_int" pos in
677    let v = Register (new_symbol_string "binop_res") in
678    code_atom_int cg pos debug a1 (fun op1 ->
679       code_atom_int cg pos debug a2 (fun op2 ->
680          cont v op1 op2))
683 (* code_basic_binop_poly
684    Writes the infrastructure used by most binary operations.  *)
685 and code_basic_binop_poly cg pos debug a1 a2 cont =
686    let pos = string_pos "code_basic_binop_poly" pos in
687    let v = Register (new_symbol_string "binop_res") in
688    code_atom_poly cg pos debug a1 (fun op1 ->
689       code_atom_poly cg pos debug a2 (fun op2 ->
690          cont v op1 op2))
693 (* code_binop_int32
694    Writes a general-purpose binary operation for 32-bit values.
695    cons contains code to build the actual inst, it takes two
696    arguments which will be operands.  Note: the first form is
697    some common code which we'll see in a lot of binops, might
698    as well list it here.  *)
699 and code_binop_int_aux cg pos debug a1 a2 cont basic cons =
700    let pos = string_pos "code_binop_int_aux" pos in
701    basic cg pos debug a1 a2 (fun v op1 op2 ->
702       let insts =
703          `new insts:
704             new   %r;
705             dcl   v;
706             movl  r,    op1;
707             modify_listbuf `{ cons insts (Register r) op2 `};
708             movl  v,    r;`
709       in
710          build_atom_block debug "binop" insts v cont)
712 and code_binop_int32 cg pos debug a1 a2 cont cons =
713    code_binop_int_aux cg pos debug a1 a2 cont code_basic_binop_int32 (add_binop_inst cons)
715 and code_binop_int_nowrap cg pos debug a1 a2 cont cons =
716    code_binop_int_aux cg pos debug a1 a2 cont code_basic_binop_int (add_binop_inst cons)
719 (* code_binop_wrap_int
720    Convert the int31 to int32, perform the op, then convert back. *)
721 and code_binop_int_wrap cg pos debug a1 a2 cont cons =
722    code_binop_int_aux cg pos debug a1 a2 cont code_basic_binop_int (fun insts op1 op2 ->
723       `+insts:
724          new   %v1,  %v2;
725          movl  v1,   op1;
726          movl  v2,   op2;
727          sarl  v1,   $1;
728          sarl  v2,   $1;
729          add_inst `{ cons (Register v1) (Register v2) `};
730          shll  v1,   $1;
731          orl   v1,   $1;
732          movl  op1,  v1;`)
735 (* code_binop_int32_cl
736    This is a special form of the above usually used for shl,
737    shr instructions.  It indicates that one of the arguments
738    must either be ecx (which is the case for shifting), or an
739    immediate value.  *)
740 and code_binop_int_cl_aux cg pos debug a1 a2 cont shift cons =
741    let pos = string_pos "code_binop_int32_cl" pos in
742    let basic =
743       if shift then
744          code_basic_binop_int
745       else
746          code_basic_binop_int32
747    in
748    basic cg pos debug a1 a2 (fun v op1 op2 ->
749       let r = new_symbol_string "binop_tmp" in
750       let insts = Listbuf.of_elt (code_load_int32 r op1) in
751       let insts =
752          match op2 with
753             ImmediateNumber (OffNumber i) ->
754                if shift then
755                   cons insts (Register r) (ImmediateNumber (OffNumber (Int32.shift_right i 1)))
756                else
757                   cons insts (Register r) op2
758           | _ ->
759                let insts = Listbuf.add insts (code_load_int32 ecx op2) in
760                let insts =
761                   if shift then
762                      Listbuf.add insts (SAR (IL, Register ecx, ImmediateNumber (OffNumber Int32.one)))
763                   else
764                      insts
765                in
766                   cons insts (Register r) (Register ecx)
767       in
768       let insts = Listbuf.add insts (code_store_int32 v r) in
769          build_atom_block debug "binop" insts v cont)
771 and code_binop_int32_cl cg pos debug a1 a2 cont cons =
772    code_binop_int_cl_aux cg pos debug a1 a2 cont false (fun insts op1 op2 ->
773       `+insts:
774          add_inst `{ cons op1 op2 `};`)
776 and code_binop_int_cl cg pos debug a1 a2 cont cons =
777    code_binop_int_cl_aux cg pos debug a1 a2 cont true (fun insts op1 op2 ->
778       `+insts:
779          (* op2 has already been shifted appropriately here *)
780          sarl  op1,  $1;
781          add_inst `{ cons op1 op2 `};
782          shll  op1,  $1;
783          orl   op1,  $1;`)
786 (* code_mulop_int32
787    MUL has the same funny semantics as the div instructions have.
788    This operation only applies to unsigned MUL; signed multiplication
789    uses IMUL which accepts two operands (for no apparent reason). *)
790 and code_mulop_int32 cg pos debug a1 a2 cont =
791    let pos = string_pos "code_mulop_int32" pos in
792    code_basic_binop_int32 cg pos debug a1 a2 (fun v op1 op2 ->
793       (* MUL cannot have an immediate arg *)
794       let insts =
795          `new:
796             new   %r;
797             dcl   v;
798             movl  r,    op2;
799             movl  eax,  op1;
800             mull  r;
801             movl  v,    eax;`
802       in
803          build_atom_block debug "mulop" insts v cont)
806 (* code_divop_int32
807    Just like code_mulop_int32, except this form handles both signed
808    and unsigned division (based on the inst the cons function builds),
809    and it also handles remainders (pass result == eax for division,
810    or result == edx for remainder).  I hate the funky div semantics --justins *)
811 and code_divop_int_aux cg pos debug a1 a2 cont result clop basic cons =
812    (* TEMP: The DIVOP code could be seriously improved with Kupo now
813             The current structure of the code is very messy though... *)
814    let pos = string_pos "code_divop_int_aux" pos in
815    basic cg pos debug a1 a2 (fun v op1 op2 ->
816       let insts =
817          `new insts:
818             new   %r;
819             dcl   v,  %result;
821             (* DIV, IDIV cannot have immediate args *)
822             movl  r,    op2;
823             movl  eax,  op1;
825             (* We have to clear the high-order bits of the numerator *)
826             add_inst `{ clop `};
827             modify_listbuf `{ cons insts (Register eax) (Register r) `};
828             movl  v,    result;`
829       in
830          build_atom_block debug "divop" insts v cont)
832 and code_divop_int32 cg pos debug pre signed a1 a2 cont result =
833    let clop, cons =
834       if signed then
835          CLTD, (fun op -> IDIV (IL, op))
836       else
837          MOV (IL, Register edx, ImmediateNumber (OffNumber Int32.zero)), (fun op -> DIV (IL, op))
838    in
839    let code_basic_binop_int32 cg pos debug a1 a2 cont =
840       code_basic_binop_int32 cg pos debug a1 a2 (fun v op1 op2 ->
841          code_int32_of_rawint_op pos debug pre signed op1 (fun op1 ->
842             code_int32_of_rawint_op pos debug pre signed op2 (fun op2 ->
843                cont v op1 op2)))
844    in
845       code_divop_int_aux cg pos debug a1 a2 cont result clop code_basic_binop_int32 (fun insts _ op ->
846          Listbuf.add insts (cons op))
848 and code_divop_int cg pos debug a1 a2 cont result clop cons =
849    code_divop_int_aux cg pos debug a1 a2 cont result clop code_basic_binop_int (fun insts op1 op2 ->
850       `+insts:
851          new   %v2;
852          dcl   %result;
854          movl  v2,      op2;
855          sarl  op1,     $1;
856          sarl  v2,      $1;
857          add_inst `{ cons (Register v2) `};
858          shll  result,  $1;
859          orl   result,  $1;`)
862 (* code_let_mem_aux
863    References some random garbage in memory.  We need to know the
864    exact precision here, so we know how many bytes we are reading
865    out of memory.  Note: the first function given is an internal
866    form which may be of use to other atom classes as well.  *)
867 and code_let_mem_aux cg pos debug a1 a2 cons =
868    let pos = string_pos "code_let_mem_aux" pos in
869    let insts = Listbuf.empty in
870    code_atom_ptr_off cg pos debug a1 (fun opptr ->
871       let rptr = new_symbol_string "let_mem_aux" in
872       let load_insts insts operand =
873          let insts = Listbuf.add insts (code_load_int32 rptr opptr) in
874             cons operand insts
875       in
876       match a2 with
877          AtomRawInt i ->
878             (* Offset is constant *)
879             let operand = MemRegOff(rptr, OffNumber (Rawint.to_int32 i)) in
880                load_insts insts operand
881        | _ ->
882             (* Offset in a register; it must be a byte-offset *)
883             let rofs = new_symbol_string "let_mem_off" in
884             code_atom_int32 cg pos debug a2 (fun oofs ->
885                let insts = Listbuf.add insts (code_load_int32 rofs oofs) in
886                let operand = MemRegRegOffMul (rptr, rofs, OffNumber Int32.zero, Int32.one) in
887                   load_insts insts operand))
889 (* JDS: Killed JYH's revert, let's try this again.
890    JDS: This code can be removed once the next function down is verified. -2002.08.17
891    TEMP
892 and code_let_mem_int cg pos debug a1 a2 cont =
893    let pos = string_pos "code_let_mem_int" pos in
894    let v = Register (new_symbol_string "let_mem") in
895    let load_insts_int operand insts =
896       let rval = new_symbol_string "let_mem_tmp" in
897       let insts = Listbuf.add_list insts
898          [CommentString "Loading an int from memory";
899           code_load_int32 rval operand;
900           code_store_int32 v rval]
901       in
902       let blocks, label = cont v in
903       let block, label = code_build_dst_block debug "code_let_mem_int" insts label in
904          block :: blocks, label
905    in
906       code_let_mem_aux cg pos debug a1 a2 load_insts_int
907  *)
909 and code_let_mem_int cg pos debug a1 a2 cont =
910    let pos = string_pos "code_let_mem_int" pos in
911    let v = Register (new_symbol_string "let_mem") in
912    let load_insts_int op insts =
913       let insts, v =
914          `+insts -> v:
915             new   %v,   %rval;
916             comm_string "Loading an int from memory";
917             movl  rval, op;
918             movl  v,    rval;`
919       in
920       let blocks, label = cont v in
921       let block, label = code_build_dst_block debug "code_let_mem_int" insts label in
922          block :: blocks, label
923    in
924       code_let_mem_aux cg pos debug a1 a2 load_insts_int
926 and code_let_mem_int32 cg pos debug int_class a1 a2 cont =
927    let pos = string_pos "code_let_mem_int32" pos in
928    let load_insts_int32 operand insts =
929       let v = Register (new_symbol_string "let_mem") in
930       let rval = new_symbol_string "let_mem_tmp" in
931       let name, code_load_mem =
932          match int_class with
933             Rawint.Int8,  false -> "uint8",  code_load_uint8
934           | Rawint.Int8,  true  -> "int8",   code_load_int8
935           | Rawint.Int16, false -> "uint16", code_load_uint16
936           | Rawint.Int16, true  -> "int16",  code_load_int16
937           | Rawint.Int32, false -> "uint32", code_load_uint32
938           | Rawint.Int32, true  -> "int32",  code_load_int32
939           | _ -> raise (MirException (pos, InternalError "code_let_mem_int32"))
940       in
941       let insts = Listbuf.add_list insts
942          [CommentString ("Loading a " ^ name ^ " from memory");
943           code_load_mem rval operand;
944           code_store_int32 v rval]
945       in
946       let blocks, label = cont v in
947       let block, label = code_build_dst_block debug "code_let_mem_int32" insts label in
948          block :: blocks, label
949    in
950       code_let_mem_aux cg pos debug a1 a2 load_insts_int32
953 (* code_let_mem_poly
954    When a polymorphic value is loaded, we don't know if
955    it is an int or a pointer.  check the LSB to test whether it
956    is a pointer. *)
957 and code_let_mem_poly cg pos debug a1 a2 cont =
958    let pos = string_pos "code_let_mem_poly" pos in
959    let load_insts_poly operand insts =
960       let v = Register (new_symbol_string "let_mem_poly") in
961       let rval = new_symbol_string "let_mem_poly_tmp" in
963       (* Construct the continuation *)
964       let blocks, label1 = cont v in
966       (* What we do just before the continuation *)
967       let insts2 = Listbuf.of_elt (code_store_int32 v rval) in
968       let block2, label2 = code_build_dst_block debug "code_let_mem_poly" insts2 label1 in
970       (* IF LSB is zero, lookup from the pointer table *)
971       let insts3 = Listbuf.of_elt (MOV (IL, Register rval, MemRegRegOffMul (pointer_base, rval, OffNumber Int32.zero, Int32.one))) in
972       let block3, label3 = code_build_dst_block debug "code_let_mem_poly" insts3 label2 in
974       (* Preprocessing: check the LSB *)
975       let insts = Listbuf.add_list insts
976          [CommentString "Loading a <poly> from memory";
977           code_load_uint32 rval operand;
978           TEST (IL, Register rval, ImmediateNumber (OffNumber Int32.one));
979           JCC (NEQ, ImmediateLabel label2)]
980       in
981       let block4, label4 = code_build_dst_block debug "code_let_mem_poly" insts label3 in
982          block4 :: block3 :: block2 :: blocks, label4
983    in
984       code_let_mem_aux cg pos debug a1 a2 load_insts_poly
987 (* code_binop_int_max
988    code_binop_int_min
989    code_binop_int32_max
990    code_binop_int32_min
991    Computes max- and min-code for integer values.  This is more
992    optimized than doing the computation in FIR, since we can do
993    the branch optimization here.  *)
994 and code_binop_int_max cg pos debug a1 a2 cont =
995    let pos = string_pos "code_binop_int_max" pos in
996       code_basic_binop_int cg pos debug a1 a2 (fun v op1 op2 ->
997          code_cond_int_op debug GT cont v op1 op2 op1 op2)
999 and code_binop_int_min cg pos debug a1 a2 cont =
1000    let pos = string_pos "code_binop_int_min" pos in
1001       code_basic_binop_int cg pos debug a1 a2 (fun v op1 op2 ->
1002          code_cond_int_op debug LT cont v op1 op2 op1 op2)
1004 and code_binop_int32_max cg pos debug a1 a2 cont =
1005    let pos = string_pos "code_binop_int32_max" pos in
1006       code_basic_binop_int32 cg pos debug a1 a2 (fun v op1 op2 ->
1007          code_cond_int_op debug GT cont v op1 op2 op1 op2)
1009 and code_binop_int32_min cg pos debug a1 a2 cont =
1010    let pos = string_pos "code_binop_int32_min" pos in
1011       code_basic_binop_int32 cg pos debug a1 a2 (fun v op1 op2 ->
1012          code_cond_int_op debug LT cont v op1 op2 op1 op2)
1015 (* code_atom_int
1016    Builds blocks to compute the atom expression given.  *)
1017 and code_atom_int cg pos debug a cont =
1018    let pos = string_pos "code_atom_int" pos in
1019    match a with
1020       (* Constants and variables *)
1021       AtomInt i ->
1022          cont (code_operand_of_int i)
1023     | AtomVar (ACInt, v) ->
1024          cont (Register v)
1026       (* Illegal operands *)
1027     | AtomFloat _
1028     | AtomRawInt _
1029     | AtomVar _ ->
1030          raise (MirException (pos, ImplicitCoercion a))
1032       (* Escaping functions *)
1033     | AtomFunVar (ac, _) ->
1034          raise (MirException (pos, ImplicitCoercion2 (ACInt, ac)))
1036       (* Unary arithmetic & bitwise operations *)
1037     | AtomUnop (IntOfRawIntOp (pre, _), a) when int32_ok pre ->
1038          code_int_of_int32 cg pos debug a cont
1039     | AtomUnop (IntOfFloatOp _, a) ->
1040          code_int_of_float cg pos debug a cont
1041     | AtomUnop (IntOfPolyOp, a) ->
1042          (* Poly values are currently transformed directly to int *)
1043          (* TEMP: should probably set LSB explicitly here, or have
1044             SOME sort of safety check?? *)
1045          code_atom_poly cg pos debug a cont
1046     | AtomUnop (UMinusOp ACInt, a) ->
1047          code_unop_int cg pos debug a cont (fun insts op ->
1048             `+insts:
1049                negl  op;
1050                incl  op;`)
1051     | AtomUnop (NotOp ACInt, a) ->
1052          code_unop_int cg pos debug a cont (fun insts op ->
1053             `+insts:
1054                notl  op;
1055                incl  op;`)
1056     | AtomUnop (AbsOp ACInt, a) ->
1057          (* BUG: this, of course, is wrong, but I don't know how to do a conditional here --jyh *)
1058          code_unop_int cg pos debug a cont (fun insts op ->
1059             `+insts:
1060                notl  op;
1061                incl  op;`)
1063       (* Unary memory operations *)
1064     | AtomUnop (MemIndexOp (_, ACInt), a)
1065     | AtomUnop (MemFunIndexOp (_, ACInt), a) ->
1066          code_mem_header_int index_word32 cg pos debug a cont (fun insts op ->
1067             `+insts:
1068                add_inst `{ build_shrop_int op (pred index_shift) `};
1069                andl     op,   index_pred_mask32;
1070                orl      op,   $1;`)
1071     | AtomUnop (MemFunArityTagOp (_, ACInt), a) ->
1072          code_mem_header_int arity_tag_word32 cg pos debug a cont (fun insts op ->
1073             `+insts:
1074                add_inst `{ build_shrop_int op (pred arity_tag_shift) `};
1075                andl     op,   arity_tag_pred_mask32;
1076                orl      op,   $1;`)
1077     | AtomUnop (MemTagOp (PtrBlock, ACInt), a) ->
1078          code_mem_header_int tag_word32 cg pos debug a cont (fun insts op ->
1079             `+insts:
1080                add_inst `{ build_shrop_int op (pred tag_shift) `};
1081                andl     op,   tag_pred_mask32;
1082                orl      op,   $1;`)
1083     | AtomUnop (MemSizeOp (PtrAggr, ACInt), a) ->
1084          code_mem_header_int size_word32 cg pos debug a cont (fun insts op ->
1085             `+insts:
1086                add_inst `{ build_shrop_int op (pred size_shift) `};
1087                andl     op,   size_pred_aggr_mask32;
1088                orl      op,   $1;`)
1089     | AtomUnop (MemSizeOp (PtrBlock, ACInt), a) ->
1090          code_mem_header_int size_word32 cg pos debug a cont (fun insts op ->
1091             `+insts:
1092                add_inst `{ build_shrop_int op (pred size_shift) `};
1093                andl     op,   size_pred_block_mask32;
1094                orl      op,   $1;`)
1096       (* Unknown unary *)
1097     | AtomUnop (_, _) ->
1098          raise (MirException (pos, NotImplemented "code_atom_int: AtomUnop"))
1100       (* Binary arithmetic operations *)
1101     | AtomBinop (PlusOp ACInt, a1, a2) ->
1102          let a1, a2 = reorder_atoms a1 a2 in
1103             code_binop_int_aux cg pos debug a1 a2 cont code_basic_binop_int (fun insts op1 op2 ->
1104                `+insts:
1105                   addl  op1,  op2;
1106                   decl  op1;`)
1107     | AtomBinop (MinusOp ACInt, a1, a2) ->
1108          code_binop_int_aux cg pos debug a1 a2 cont code_basic_binop_int (fun insts op1 op2 ->
1109             `+insts:
1110                subl  op1,  op2;
1111                incl  op1;`)
1113       (* Multiply and divide *)
1114     | AtomBinop (MulOp ACInt, a1, a2) ->
1115          (* Signed case *)
1116          let a1, a2 = reorder_atoms a1 a2 in
1117             code_binop_int_wrap cg pos debug a1 a2 cont (fun op1 op2 -> IMUL (IL, op1, op2))
1119     | AtomBinop (DivOp ACInt, a1, a2) ->
1120          (* Signed case *)
1121          code_divop_int cg pos debug a1 a2 cont eax CLTD (fun op -> IDIV (IL, op))
1122     | AtomBinop (RemOp ACInt, a1, a2) ->
1123          (* Signed case *)
1124          code_divop_int cg pos debug a1 a2 cont edx CLTD (fun op -> IDIV (IL, op))
1126       (* Binary bitwise operations *)
1127     | AtomBinop (SlOp ACInt, a1, a2) ->
1128          (* Signed case *)
1129          code_binop_int_cl cg pos debug a1 a2 cont (fun op1 op2 -> SAL (IL, op1, op2))
1130     | AtomBinop (ASrOp ACInt, a1, a2) ->
1131          (* Signed case *)
1132          code_binop_int_cl cg pos debug a1 a2 cont (fun op1 op2 -> SAR (IL, op1, op2))
1133     | AtomBinop (LSrOp ACInt, a1, a2) ->
1134          (* Unsigned case *)
1135          code_binop_int_cl cg pos debug a1 a2 cont (fun op1 op2 -> SHR (IL, op1, op2))
1136     | AtomBinop (AndOp ACInt, a1, a2) ->
1137          let a1, a2 = reorder_atoms a1 a2 in
1138             code_binop_int_nowrap cg pos debug a1 a2 cont (fun op1 op2 -> AND (IL, op1, op2))
1139     | AtomBinop (OrOp ACInt, a1, a2) ->
1140          let a1, a2 = reorder_atoms a1 a2 in
1141             code_binop_int_nowrap cg pos debug a1 a2 cont (fun op1 op2 -> OR (IL, op1, op2))
1142     | AtomBinop (XorOp ACInt, a1, a2) ->
1143          let a1, a2 = reorder_atoms a1 a2 in
1144             code_binop_int_aux cg pos debug a1 a2 cont code_basic_binop_int (fun insts op1 op2 ->
1145                `+insts:
1146                   xorl  op1,  op2;
1147                   orl   op1,  $1;`)
1149       (* Max & Min *)
1150     | AtomBinop (MaxOp ACInt, a1, a2) ->
1151          let a1, a2 = reorder_atoms a1 a2 in
1152             code_binop_int_max cg pos debug a1 a2 cont
1153     | AtomBinop (MinOp ACInt, a1, a2) ->
1154          let a1, a2 = reorder_atoms a1 a2 in
1155             code_binop_int_min cg pos debug a1 a2 cont
1157       (* Binary memory operations *)
1158     | AtomBinop (MemOp (ACInt, _), a1, a2) ->
1159          code_let_mem_int cg pos debug a1 a2 cont
1161       (* Comparative operators *)
1162     | AtomBinop (CmpOp ACInt, a1, a2) ->
1163          let gt = AtomBinop (GtOp ACInt, a1, a2) in
1164          let lt = AtomBinop (LtOp ACInt, a1, a2) in
1165          let a = AtomBinop (MinusOp (ACRawInt (Rawint.Int32, true)), gt, lt) in
1166          let a = AtomUnop (IntOfRawIntOp (Rawint.Int32, true), a) in
1167          let pos = string_pos "code_atom_int_CmpOp" pos in
1168             code_atom_int cg pos debug a cont
1170       (* Unknown binary *)
1171     | AtomBinop (_, _, _) ->
1172          raise (MirException (pos, NotImplemented "code_atom_int: AtomBinop"))
1175 (* code_atom_int8
1176    Builds blocks to compute the atom expression given.  *)
1177 and code_atom_int8 cg pos debug signed a cont =
1178    let pos = string_pos "code_atom_int8" pos in
1179       code_atom_int32 cg pos debug a (fun op ->
1180          code_int32_of_rawint_op pos debug Rawint.Int8 signed op cont)
1183 (* code_atom_int16
1184    Builds blocks to compute the atom expression given.  *)
1185 and code_atom_int16 cg pos debug signed a cont =
1186    let pos = string_pos "code_atom_int8" pos in
1187       code_atom_int32 cg pos debug a (fun op ->
1188          code_int32_of_rawint_op pos debug Rawint.Int16 signed op cont)
1191 (* code_atom_int32
1192    Builds blocks to compute the atom expression given.  *)
1193 and code_atom_int32 cg pos debug a cont =
1194    let pos = string_pos "code_atom_int32" pos in
1195    match a with
1196       (* Constants and variables *)
1197       AtomRawInt i when int32_ok (Rawint.precision i) ->
1198          cont (ImmediateNumber (OffNumber (Rawint.to_int32 i)))
1199     | AtomVar (ACRawInt (pre, _), v) when int32_ok pre ->
1200          cont (Register v)
1202       (* Illegal operands *)
1203     | AtomInt _
1204     | AtomFloat _
1205     | AtomRawInt _
1206     | AtomVar _
1207     | AtomFunVar _ ->
1208          raise (MirException (pos, ImplicitCoercion a))
1210       (* Unary coercion operators *)
1211     | AtomUnop (RawIntOfIntOp (pre, _), a) when int32_ok pre ->
1212          code_int32_of_int cg pos debug a cont
1213     | AtomUnop (RawIntOfFloatOp (pre, true, _), a) when int32_ok pre ->
1214          code_int32_of_float cg pos debug a cont
1215     | AtomUnop (RawIntOfFloatOp (pre, false, _), a) when int32_ok pre ->
1216          atom_warning "coercion from float to unsigned will be treated as coercion from float to signed, expect range errors";
1217          code_int32_of_float cg pos debug a cont
1218     | AtomUnop (RawIntOfRawIntOp (dpre, dsigned, spre, ssigned), a) when int32_ok dpre && dsigned = ssigned ->
1219          code_int32_of_rawint cg pos debug spre ssigned a cont
1220     | AtomUnop (RawIntOfRawIntOp (dpre, dsigned, spre, ssigned), a) when int32_ok dpre && dpre = spre ->
1221          (* sign change with same precision *)
1222          code_int32_of_rawint cg pos debug spre ssigned a cont
1223     | AtomUnop (RawIntOfRawIntOp (dpre, dsigned, spre, ssigned), a) when int32_ok dpre && dsigned <> ssigned ->
1224          atom_warning "in coercions that change signedness, source sign wins in any bit extensions";
1225          code_int32_of_rawint cg pos debug spre ssigned a cont
1226     | AtomUnop (Int32OfPointerOp _, a) ->
1227          (* Reading the raw numerical value of a pointer; this is identity *)
1228          code_atom_ptr_off cg pos debug a cont
1230       (* Unary arithmetic & bitwise operations *)
1231     | AtomUnop (UMinusOp (ACRawInt (pre, _)), a) when int32_ok pre ->
1232          code_unop_int32 cg pos debug a cont (fun op -> NEG (IL, op))
1233     | AtomUnop (NotOp (ACRawInt (pre, _)), a) when int32_ok pre ->
1234          code_unop_int32 cg pos debug a cont (fun op -> NOT (IL, op))
1236       (* Read bitfields *)
1237     | AtomUnop (BitFieldOp (pre, true, ofs, len), a) when int32_ok pre ->
1238          code_atom_int32 cg pos debug a (fun op ->
1239             let insts, v =
1240                `new -> v:
1241                   new   %r, %v;
1242                   dcl   $len, $ofs;
1244                   movl  r,    op;
1245                   sall  r,    $32 - (ofs + len);
1246                   sarl  r,    $32 - len;
1247                   movl  v,    r;`
1248             in
1249                build_atom_block debug "bitfield" insts v cont)
1250     | AtomUnop (BitFieldOp (pre, false, ofs, len), a) when int32_ok pre ->
1251          code_atom_int32 cg pos debug a (fun op ->
1252             let insts, v =
1253                `new -> v:
1254                   new   %r, %v;
1255                   dcl   $len, $ofs;
1257                   movl  r,    op;
1258                   shrl  r,    $ofs;
1259                   andl  r,    $(pow2 len) - 1;
1260                   movl  v,    r;`
1261             in
1262                build_atom_block debug "bitfield" insts v cont)
1264       (* Unary memory operations *)
1265     | AtomUnop (MemIndexOp (_, ACRawInt (pre, _)), a)
1266     | AtomUnop (MemFunIndexOp (_, ACRawInt (pre, _)), a)
1267       when int32_ok pre ->
1268          code_mem_header_int index_word32 cg pos debug a cont (fun insts op ->
1269             `+insts:
1270                shrl  op,   $index_shift32;
1271                andl  op,   $index_mask32;`)
1272     | AtomUnop (MemFunArityTagOp (_, ACRawInt (pre, _)), a)
1273       when int32_ok pre ->
1274          code_mem_header_int arity_tag_word32 cg pos debug a cont (fun insts op ->
1275             `+insts:
1276                shrl  op,   $arity_tag_shift32;
1277                andl  op,   $arity_tag_mask32;`)
1278     | AtomUnop (MemTagOp (PtrBlock, ACRawInt (pre, _)), a)
1279       when int32_ok pre ->
1280          code_mem_header_int tag_word32 cg pos debug a cont (fun insts op ->
1281             `+insts:
1282                shrl  op,   $tag_shift32;
1283                andl  op,   $tag_mask32;`)
1284     | AtomUnop (MemSizeOp (PtrAggr, ACRawInt (pre, _)), a)
1285       when int32_ok pre ->
1286          code_mem_header_int size_word32 cg pos debug a cont (fun insts op ->
1287             `+insts:
1288                shrl  op,   $size_shift32;
1289                andl  op,   $size_aggr_mask32;`)
1290     | AtomUnop (MemSizeOp (PtrBlock, ACRawInt (pre, _)), a)
1291       when int32_ok pre ->
1292          code_mem_header_int size_word32 cg pos debug a cont (fun insts op ->
1293             `+insts:
1294                shrl  op,   $size_shift32;
1295                andl  op,   $size_block_mask32;`)
1297       (* Unary operations on infix pointers *)
1298     | AtomUnop (OffsetOfInfixPointerOp _, a) ->
1299          code_atom_ptr_infix cg pos debug a (fun base offset ->
1300             let insts, v =
1301                `new -> v:
1302                   new   %v;
1303                   dcl   base, offset;
1305                   movl  v,    offset;
1306                   subl  v,    base;`
1307             in
1308                build_atom_block debug "base" insts v cont)
1310       (* Unknown unary *)
1311     | AtomUnop _ ->
1312          raise (MirException (pos, NotImplemented "code_atom_int32: AtomUnop"))
1314       (* Binary arithmetic operations *)
1315     | AtomBinop (PlusOp (ACRawInt (pre,  _)), a1, a2) when int32_ok pre ->
1316          let a1, a2 = reorder_atoms a1 a2 in
1317             code_binop_int32 cg pos debug a1 a2 cont (fun op1 op2 -> ADD (IL, op1, op2))
1318     | AtomBinop (MinusOp (ACRawInt (pre,  _)), a1, a2) when int32_ok pre ->
1319          code_binop_int32 cg pos debug a1 a2 cont (fun op1 op2 -> SUB (IL, op1, op2))
1321       (* Multiply and divide *)
1322     | AtomBinop (MulOp (ACRawInt (pre, true)), a1, a2) when int32_ok pre ->
1323          (* Signed case *)
1324          let a1, a2 = reorder_atoms a1 a2 in
1325             code_binop_int32 cg pos debug a1 a2 cont (fun op1 op2 -> IMUL (IL, op1, op2))
1326     | AtomBinop (MulOp (ACRawInt (pre, false)), a1, a2) when int32_ok pre ->
1327          (* Unsigned case *)
1328          let a1, a2 = reorder_atoms a1 a2 in
1329             code_mulop_int32 cg pos debug a1 a2 cont
1330     | AtomBinop (DivOp (ACRawInt (pre, signed)), a1, a2) when int32_ok pre ->
1331          code_divop_int32 cg pos debug pre signed a1 a2 cont eax
1332     | AtomBinop (RemOp (ACRawInt (pre, signed)), a1, a2) when int32_ok pre ->
1333          code_divop_int32 cg pos debug pre signed a1 a2 cont edx
1335       (* Binary bitwise operations *)
1336     | AtomBinop (SlOp (ACRawInt (pre, true)), a1, a2) when int32_ok pre ->
1337          (* Signed case *)
1338          code_binop_int32_cl cg pos debug a1 a2 cont (fun op1 op2 -> SAL (IL, op1, op2))
1339     | AtomBinop (SlOp (ACRawInt (pre, false)), a1, a2) when int32_ok pre ->
1340          (* Unsigned case *)
1341          code_binop_int32_cl cg pos debug a1 a2 cont (fun op1 op2 -> SHL (IL, op1, op2))
1342     | AtomBinop (ASrOp (ACRawInt (pre, _)), a1, a2) when int32_ok pre ->
1343          (* Signed case *)
1344          code_binop_int32_cl cg pos debug a1 a2 cont (fun op1 op2 -> SAR (IL, op1, op2))
1345     | AtomBinop (LSrOp (ACRawInt (pre, _)), a1, a2) when int32_ok pre ->
1346          (* Unsigned case *)
1347          code_binop_int32_cl cg pos debug a1 a2 cont (fun op1 op2 -> SHR (IL, op1, op2))
1348     | AtomBinop (AndOp (ACRawInt (pre, _)), a1, a2) when int32_ok pre ->
1349          let a1, a2 = reorder_atoms a1 a2 in
1350             code_binop_int32 cg pos debug a1 a2 cont (fun op1 op2 -> AND (IL, op1, op2))
1351     | AtomBinop (OrOp (ACRawInt (pre, _)), a1, a2) when int32_ok pre ->
1352          let a1, a2 = reorder_atoms a1 a2 in
1353             code_binop_int32 cg pos debug a1 a2 cont (fun op1 op2 -> OR (IL, op1, op2))
1354     | AtomBinop (XorOp (ACRawInt (pre, _)), a1, a2) when int32_ok pre ->
1355          let a1, a2 = reorder_atoms a1 a2 in
1356             code_binop_int32 cg pos debug a1 a2 cont (fun op1 op2 -> XOR (IL, op1, op2))
1358       (* Max & Min *)
1359     | AtomBinop (MaxOp (ACRawInt (pre, _)), a1, a2) when int32_ok pre ->
1360          let a1, a2 = reorder_atoms a1 a2 in
1361             code_binop_int32_max cg pos debug a1 a2 cont
1362     | AtomBinop (MinOp (ACRawInt (pre, _)), a1, a2) when int32_ok pre ->
1363          let a1, a2 = reorder_atoms a1 a2 in
1364             code_binop_int32_min cg pos debug a1 a2 cont
1366       (* Set bitfields *)
1367     | AtomBinop (SetBitFieldOp (pre, _, ofs, len), a1, a2) when int32_ok pre ->
1368          code_basic_binop_int32 cg pos debug a1 a2 (fun v op1 op2 ->
1369             let insts =
1370                `new:
1371                   new   %r1, %r2;
1372                   dcl   v, $len, $ofs;
1373                   def   mask = (pow2 len) - 1;
1375                   movl  r2,   op2;
1376                   andl  r2,   $mask;
1377                   shll  r2,   $ofs;
1378                   movl  r1,   op1;
1379                   andl  r1,   $~(mask << ofs);
1380                   orl   r1,   r2;
1381                   movl  v,    r1;`
1382             in
1383                build_atom_block debug "bitfield" insts v cont)
1385       (* Binary relational operations on poly data.
1386          Currently you can only check for equivalence. *)
1387     | AtomBinop (EqOp ACPoly, a1, a2) ->
1388          code_relop_poly cg pos debug EQ a1 a2 cont
1389     | AtomBinop (NeqOp ACPoly, a1, a2) ->
1390          code_relop_poly cg pos debug NEQ a1 a2 cont
1392       (* Binary relational operations on int *)
1393     | AtomBinop (EqOp ACInt, a1, a2) ->
1394          code_relop_int cg pos debug EQ a1 a2 cont
1395     | AtomBinop (NeqOp ACInt, a1, a2) ->
1396          code_relop_int cg pos debug NEQ a1 a2 cont
1397     | AtomBinop (LtOp ACInt, a1, a2) ->
1398          code_relop_int cg pos debug LT a1 a2 cont
1399     | AtomBinop (LeOp ACInt, a1, a2) ->
1400          code_relop_int cg pos debug LE a1 a2 cont
1401     | AtomBinop (GtOp ACInt, a1, a2) ->
1402          code_relop_int cg pos debug GT a1 a2 cont
1403     | AtomBinop (GeOp ACInt, a1, a2) ->
1404          code_relop_int cg pos debug GE a1 a2 cont
1406       (* Binary relational operations on floats *)
1407     | AtomBinop (EqOp (ACFloat _), a1, a2) ->
1408          code_relop_float cg pos debug EQ a1 a2 cont
1409     | AtomBinop (NeqOp (ACFloat _), a1, a2) ->
1410          code_relop_float cg pos debug NEQ a1 a2 cont
1411     | AtomBinop (LtOp (ACFloat _), a1, a2) ->
1412          code_relop_float cg pos debug LT a1 a2 cont
1413     | AtomBinop (LeOp (ACFloat _), a1, a2) ->
1414          code_relop_float cg pos debug LE a1 a2 cont
1415     | AtomBinop (GtOp (ACFloat _), a1, a2) ->
1416          code_relop_float cg pos debug GT a1 a2 cont
1417     | AtomBinop (GeOp (ACFloat _), a1, a2) ->
1418          code_relop_float cg pos debug GE a1 a2 cont
1420       (* Binary relational operations on rawint *)
1421     | AtomBinop (EqOp (ACRawInt (pre, signed)), a1, a2) ->
1422          code_relop_rawint cg pos debug pre signed EQ a1 a2 cont
1423     | AtomBinop (NeqOp (ACRawInt (pre, signed)), a1, a2) ->
1424          code_relop_rawint cg pos debug pre signed NEQ a1 a2 cont
1425     | AtomBinop (LtOp (ACRawInt (pre, true)), a1, a2) ->
1426          code_relop_rawint cg pos debug pre true LT a1 a2 cont
1427     | AtomBinop (LtOp (ACRawInt (pre, false)), a1, a2) ->
1428          code_relop_rawint cg pos debug pre false ULT a1 a2 cont
1429     | AtomBinop (LeOp (ACRawInt (pre, true)), a1, a2) ->
1430          code_relop_rawint cg pos debug pre true LE a1 a2 cont
1431     | AtomBinop (LeOp (ACRawInt (pre, false)), a1, a2) ->
1432          code_relop_rawint cg pos debug pre false ULE a1 a2 cont
1433     | AtomBinop (GtOp (ACRawInt (pre, true)), a1, a2) ->
1434          code_relop_rawint cg pos debug pre true GT a1 a2 cont
1435     | AtomBinop (GtOp (ACRawInt (pre, false)), a1, a2) ->
1436          code_relop_rawint cg pos debug pre false UGT a1 a2 cont
1437     | AtomBinop (GeOp (ACRawInt (pre, true)), a1, a2) ->
1438          code_relop_rawint cg pos debug pre true GE a1 a2 cont
1439     | AtomBinop (GeOp (ACRawInt (pre, false)), a1, a2) ->
1440          code_relop_rawint cg pos debug pre false UGE a1 a2 cont
1442       (* Binary relational operations on pointers *)
1443     | AtomBinop (EqOp (ACPointer _), a1, a2) ->
1444          code_relop_ptr cg pos debug Rawint.Int32 false EQ a1 a2 cont
1445     | AtomBinop (NeqOp (ACPointer _), a1, a2) ->
1446          code_relop_ptr cg pos debug Rawint.Int32 false NEQ a1 a2 cont
1448       (* Binary memory operations *)
1449     | AtomBinop (MemOp (ACRawInt (pre, signed), _), a1, a2) when int32_ok pre ->
1450          code_let_mem_int32 cg pos debug (pre, signed) a1 a2 cont
1452       (* Unknown binary *)
1453     | AtomBinop _ ->
1454          raise (MirException (pos, NotImplemented "code_atom_int32: AtomBinop"))
1457 (* code_atom_poly
1458    Builds blocks to compute the atom expression given.  Polymorphic
1459    values are HEAVILY restricted in what operations they can participate
1460    in; this is a drastic change from earlier policies which let Poly and
1461    Int32 be used interchangably. *)
1462 and code_atom_poly cg pos debug a cont =
1463    let pos = string_pos "code_atom_poly" pos in
1464    match a with
1465       (* Constants and variables *)
1466     | AtomVar (ACPoly, v) ->
1467          cont (Register v)
1469       (* Illegal operands *)
1470     | AtomInt _
1471     | AtomFloat _
1472     | AtomRawInt _
1473     | AtomVar _
1474     | AtomFunVar _ ->
1475          raise (MirException (pos, ImplicitCoercion a))
1477       (* Unary coercion operators *)
1478     | AtomUnop (PolyOfIntOp, a) ->
1479          (* Coerce ML Int to Int32, but do NOT do the bit-shift *)
1480          code_atom_int cg pos debug a cont
1481     | AtomUnop (PolyOfPointerOp _, a)
1482     | AtomUnop (PolyOfFunctionOp _, a) ->
1483          (* Coerce a real pointer to Int32; identity operation. *)
1484          code_atom_ptr_off cg pos debug a cont
1486       (* Unknown unary *)
1487     | AtomUnop _ ->
1488          raise (MirException (pos, NotImplemented "code_atom_int32: AtomUnop"))
1490       (* Binary memory operations *)
1491     | AtomBinop (MemOp (ACPoly, _), a1, a2) ->
1492          code_let_mem_poly cg pos debug a1 a2 cont
1494       (* Unknown binary *)
1495     | AtomBinop _ ->
1496          raise (MirException (pos, NotImplemented "code_atom_int32: AtomBinop"))
1499 (***  Pointer Operations  ***)
1502 (* code_atom_ptr_base
1503    Builds blocks to compute the atom expression given.  Here the result of
1504    the atom expression must be a pointer type; furthermore it must point to
1505    the base of a block in memory. *)
1506 and code_atom_ptr_base cg pos debug a cont =
1507    let pos = string_pos "code_atom_ptr_base" pos in
1508       match a with
1509          AtomVar (ACPointerInfix _, v) ->
1510             let v, _ = reg64_lookup cg pos v in
1511                cont (Register v)
1512        | AtomVar (ac, v) when ptr_ok ac ->
1513             cont (Register v)
1515          (* Escaping functions *)
1516        | AtomFunVar (_, v) ->
1517             code_fun_pointer_int32 cg pos debug v cont
1519          (* Coercions to pointer base *)
1520        | AtomUnop (PointerOfPolyOp _, a)
1521        | AtomUnop (FunctionOfPolyOp _, a) ->
1522             code_atom_poly cg pos debug a cont
1524          (* Infix pointer addition *)
1525        | AtomUnop (BaseOfInfixPointerOp _, a)
1526        | AtomBinop (InfixOfBaseOffsetOp _, a, _)
1527        | AtomBinop (PlusPointerOp _, a, _) ->
1528             code_atom_ptr_base cg pos debug a cont
1530          (* Binary memory operations *)
1531        | AtomBinop (MemOp (ac, _), a1, a2) when ptr_ok ac ->
1532             code_let_mem_int32 cg pos debug (Rawint.Int32, false) a1 a2 cont
1534          (* Unknown expression *)
1535        | AtomInt _
1536        | AtomFloat _
1537        | AtomRawInt _
1538        | AtomVar _
1539        | AtomUnop _
1540        | AtomBinop _ ->
1541             raise (MirException (pos, StringFormatError ("code_atom_ptr_base: expression not implemented",
1542                (fun buf -> pp_print_atom buf a))))
1545 (* code_atom_ptr_off
1546    Builds blocks to compute the atom expression given.  Here the result of
1547    the atom expression must be a pointer type, and it may be an infix int32
1548    pointer.  WARNING: do NOT use this when getting header fields. This will
1549    return a real pointer. *)
1550 and code_atom_ptr_off cg pos debug a cont =
1551    let pos = string_pos "code_atom_ptr_off" pos in
1552       match a with
1553          AtomVar (ACPointerInfix _, v) ->
1554             let _, v = reg64_lookup cg pos v in
1555                cont (Register v)
1556        | AtomVar (ac, v) when ptr_ok ac ->
1557             cont (Register v)
1559          (* Escaping functions *)
1560        | AtomFunVar (_, v) ->
1561             code_fun_pointer_int32 cg pos debug v cont
1563          (* Coercions to pointer offset.  Note: if the value is polymorphic
1564             then it is assumed to be a base pointer, not an infix pointer. *)
1565        | AtomUnop (PointerOfPolyOp _, a)
1566        | AtomUnop (FunctionOfPolyOp _, a) ->
1567             code_atom_poly cg pos debug a cont
1569          (* Infix pointers; note that pointer arithmetic and the infix
1570             constructor really use the same mechanism since in this case,
1571             we are only interested in the final offset pointer... *)
1572        | AtomBinop (InfixOfBaseOffsetOp _, a1, a2)
1573        | AtomBinop (PlusPointerOp _, a1, a2) ->
1574             code_atom_ptr_off cg pos debug a1 (fun op1 ->
1575                code_atom_int32 cg pos debug a2 (fun op2 ->
1576                   let insts, vlo =
1577                      `new -> vlo:
1578                         new   %vlo, %rlo;
1579                         movl  rlo,  op1;
1580                         movl  vlo,  rlo;
1581                         addl  vlo,  op2;`
1582                   in
1583                   let blocks, label = cont vlo in
1584                   let block, label = code_build_dst_block debug "ptr" insts label in
1585                      block :: blocks, label))
1587          (* Binary memory operations *)
1588        | AtomBinop (MemOp (ac, _), a1, a2) when ptr_ok ac ->
1589             code_let_mem_int32 cg pos debug (Rawint.Int32, false) a1 a2 cont
1591          (* Unknown expression *)
1592        | AtomInt _
1593        | AtomFloat _
1594        | AtomRawInt _
1595        | AtomVar _
1596        | AtomUnop _
1597        | AtomBinop _ ->
1598             raise (MirException (pos, StringFormatError ("code_atom_ptr_off: expression not implemented", (fun buf -> pp_print_atom buf a))))
1601 (* code_atom_ptr_infix
1602    Builds blocks to compute the atom expression given.  Here the result of
1603    the atom expression must be a pointer offset type, and the continuation
1604    is given both parts.  *)
1605 and code_atom_ptr_infix cg pos debug a cont =
1606    let pos = string_pos "code_atom_ptr_infix" pos in
1607       match a with
1608          (* Load from a variable *)
1609          AtomVar (ACPointerInfix _, v) ->
1610             let vhi, vlo = reg64_lookup cg pos v in
1611                cont (Register vhi) (Register vlo)
1613          (* Infix pointer construction.  Note here that we build the
1614             real pointer offset by using the code_atom_ptr_off mechanism,
1615             which will take the base/offset we already have and perform
1616             the pointer arithmetic to make the offset pointer. *)
1617        | AtomBinop (InfixOfBaseOffsetOp _, a1, a2) ->
1618             code_atom_ptr_base cg pos debug a1 (fun a1 ->
1619                code_atom_ptr_off cg pos debug a (fun a2 ->
1620                   cont a1 a2))
1622          (* Pointer addition.  In this case, we do the same thing as
1623             in code_atom_ptr_off, but we have to keep around an already-
1624             existing base pointer. *)
1625        | AtomBinop (PlusPointerOp _, a1, a2) ->
1626             code_atom_ptr_infix cg pos debug a1 (fun ohi olo ->
1627                code_atom_int32 cg pos debug a2 (fun op ->
1628                   let insts, vlo =
1629                      `new -> vlo:
1630                         new   %vlo, %rlo;
1632                         movl  rlo,  olo;
1633                         movl  vlo,  rlo;
1634                         addl  vlo,  op;`
1635                   in
1636                   let blocks, label = cont ohi vlo in
1637                   let block, label = code_build_dst_block debug "ptr_off" insts label in
1638                      block :: blocks, label))
1640          (* Other operations are unknown *)
1641        | AtomInt _
1642        | AtomFloat _
1643        | AtomRawInt _
1644        | AtomVar _
1645        | AtomFunVar _
1646        | AtomUnop _
1647        | AtomBinop _ ->
1648             raise (MirException (pos, StringFormatError ("code_atom_ptr_infix: expression not implemented", (fun buf -> pp_print_atom buf a))))
1651 (***  Int64 Operations  ***)
1654 (* code_basic_unop_int64
1655    Writes wrapper code for a unary 64-bit operation.  *)
1656 and code_basic_unop_int64 cg pos debug a cont cons =
1657    let pos = string_pos "code_basic_unop_int64" pos in
1658    code_atom_int64 cg pos debug a (fun ohi olo ->
1659       (* TEMP: Vars MUST be interned first, unfortunately! *)
1660       let vhi = Register (new_symbol_string "unop_int64_hi") in
1661       let vlo = Register (new_symbol_string "unop_int64_lo") in
1662       let blocks, label = cont vhi vlo in
1663       let insts =
1664          `new insts:
1665             new   %rhi, %rlo;
1666             dcl   vhi,  vlo;
1668             movl  rhi,  ohi;
1669             movl  rlo,  olo;
1670             modify_listbuf `{ cons insts label (Register rhi) (Register rlo) `};
1671             movl  vhi,  rhi;
1672             movl  vlo,  rlo;`
1673       in
1674       let block, label = code_build_dst_block debug "unop_int64" insts label in
1675          block :: blocks, label)
1678 (* code_unop_int64
1679    Writes a unary 64-bit operation.  *)
1680 and code_unop_int64 cg pos debug a cont conshi conslo =
1681    let pos = string_pos "code_unop_int64" pos in
1682    code_basic_unop_int64 cg pos debug a cont (fun insts label ohi olo ->
1683       `+insts:
1684          add_inst `{ conslo olo `};
1685          add_inst `{ conshi ohi `};`)
1688 (* code_basic_binop_int64
1689    Writes the infrastructure used by several int64 binary operations.  *)
1690 and code_basic_binop_int64 cg pos debug a1 a2 cont cons =
1691    let pos = string_pos "code_basic_binop_int64" pos in
1692    code_atom_int64 cg pos debug a1 (fun ohi1 olo1 ->
1693       code_atom_int64 cg pos debug a2 (fun ohi2 olo2 ->
1694          (* TEMP: Cannot eliminate explicit var declarations *)
1695          let vhi = Register (new_symbol_string "binop_int64_hi") in
1696          let vlo = Register (new_symbol_string "binop_int64_lo") in
1697          let blocks, label = cont vhi vlo in
1698          let insts =
1699             `new insts:
1700                new   %rhi, %rlo;
1701                dcl   vhi,  vlo;
1703                movl  rhi,  ohi1;
1704                movl  rlo,  olo1;
1705                modify_listbuf `{ cons insts label (Register rhi) (Register rlo) ohi2 olo2 `};
1706                movl  vhi,  rhi;
1707                movl  vlo,  rlo;`
1708          in
1709          let block, label = code_build_dst_block debug "binop_int64" insts label in
1710             block :: blocks, label))
1713 (* code_shift_binop_int64
1714    Writes the infrastructure required for a 64-bit shift.  *)
1715 and code_shift_binop_int64 cg pos debug a1 a2 cont cons =
1716    let pos = string_pos "code_basic_binop_int64" pos in
1717    code_atom_int64 cg pos debug a1 (fun ohi1 olo1 ->
1718       code_atom_int64 cg pos debug a2 (fun ohi2 olo2 ->
1719          (* TEMP: Cannot remove explicit var decls here *)
1720          let vhi = Register (new_symbol_string "binop_int64_hi") in
1721          let vlo = Register (new_symbol_string "binop_int64_lo") in
1722          let blocks, label = cont vhi vlo in
1724          (* Build the code that will patch in/store result *)
1725          let insts, rhi, rlo =
1726             `new -> rhi, rlo:
1727                new   %rhi, %rlo;
1728                dcl   vhi,  vlo;
1729                movl  vhi,  rhi;
1730                movl  vlo,  rlo;`
1731          in
1732          let block, label = code_build_dst_block debug "binop_int64" insts label in
1733          let blocks = block :: blocks in
1735          (* Build the code to load and shift *)
1736          let insts =
1737             `new insts:
1738                dcl   rhi,  rlo;
1739                movl  rhi,  ohi1;
1740                movl  rlo,  olo1;
1741                modify_listbuf `{ cons insts label rhi rlo ohi2 olo2 `};`
1742          in
1743          let block, label = code_build_dst_block debug "binop_int64" insts label in
1744             block :: blocks, label))
1747 (* code_binop_int64
1748    Codes a typical binary operator, where conshi and conslo build the
1749    opcodes for building the high and low 32-bits, respectively.  The
1750    low-order bits will be built first so the high-order constructor may
1751    take advantage of such things as the flags set by the low constructor
1752    (carry, borrow, etc).  *)
1753 and code_binop_int64 cg pos debug a1 a2 cont conshi conslo =
1754    let pos = string_pos "code_binop_int64" pos in
1755    code_basic_binop_int64 cg pos debug a1 a2 cont (fun insts label ohi1 olo1 ohi2 olo2 ->
1756       `+insts:
1757          new   %rhi2,   %rlo2;
1758          movl  rlo2,    olo2;
1759          add_inst `{ conslo olo1 (Register rlo2) `};
1760          movl  rhi2,    ohi2;
1761          add_inst `{ conshi ohi1 (Register rhi2) `};`)
1764 (* code_let_mem_int64
1765    Reads an int64 value from memory.  This simply looks like two int32
1766    memory read operations, so lets build it up to look that way.  *)
1767 and code_let_mem_int64 cg pos debug aptr aofs cont =
1768    let pos = string_pos "code_let_mem_int64" pos in
1769    let int_class = Rawint.Int32, true in
1770    let offset = Rawint.of_int Rawint.Int32 true sizeof_int32 in
1771    let alo, ahi = aofs, AtomBinop (PlusOp (ACRawInt (Rawint.Int32, true)), aofs, AtomRawInt offset) in
1772    code_let_mem_int32 cg pos debug int_class aptr alo (fun vlo ->
1773       code_let_mem_int32 cg pos debug int_class aptr ahi (fun vhi ->
1774          cont vhi vlo))
1777 (* code_atom_int64
1778    Builds blocks to compute the atom expression given.  *)
1779 and code_atom_int64 cg pos debug a cont =
1780    let pos = string_pos "code_atom_int64" pos in
1781    match a with
1782       (* Constants and variables *)
1783       AtomInt _
1784     | AtomFunVar _
1785     | AtomFloat _ ->
1786          raise (MirException (pos, ImplicitCoercion a))
1787     | AtomRawInt i when int32_ok (Rawint.precision i) ->
1788          raise (MirException (pos, ImplicitCoercion a))
1789     | AtomRawInt i ->
1790          let i = Rawint.to_int64 i in
1791          let mask = Int64.pred (Int64.shift_left Int64.one 32) in
1792          let ihi = Int64.to_int32 (Int64.shift_right_logical i 32) in
1793          let ilo = Int64.to_int32 (Int64.logand i mask) in
1794             cont (ImmediateNumber (OffNumber ihi)) (ImmediateNumber (OffNumber ilo))
1795     | AtomVar (ACRawInt (Rawint.Int64, _), v) ->
1796          let vhi, vlo = reg64_lookup cg pos v in
1797             cont (Register vhi) (Register vlo)
1798     | AtomVar _ ->
1799          raise (MirException (pos, ImplicitCoercion a))
1801       (* Unary arithmetic *)
1802     | AtomUnop (UMinusOp (ACRawInt (Rawint.Int64, signed)), a) ->
1803          code_basic_unop_int64 cg pos debug a cont (fun insts label ohi olo ->
1804             `+insts:
1805                notl  ohi;
1806                negl  olo;
1807                sbbl  ohi,  $-1;`)
1808     | AtomUnop (NotOp (ACRawInt (Rawint.Int64, signed)), a) ->
1809          let cons op = NOT (IL, op) in
1810             code_unop_int64 cg pos debug a cont cons cons
1812       (* Coercions *)
1813     | AtomUnop (RawIntOfFloatOp (Rawint.Int64, true, _), a) ->
1814          code_int64_of_float cg pos debug a cont
1815     | AtomUnop (RawIntOfFloatOp (Rawint.Int64, false, _), a) ->
1816          atom_warning "coercion from float to unsigned will be treated as coercion from float to signed, expect range errors";
1817          code_int64_of_float cg pos debug a cont
1818     | AtomUnop (RawIntOfRawIntOp (Rawint.Int64, dsigned, spre, ssigned), a) when dsigned = ssigned ->
1819          code_int64_of_rawint cg pos debug spre ssigned a cont
1820     | AtomUnop (RawIntOfRawIntOp (Rawint.Int64, dsigned, Rawint.Int64, ssigned), a) ->
1821          (* sign change on same precision *)
1822          code_int64_of_rawint cg pos debug Rawint.Int64 ssigned a cont
1823     | AtomUnop (RawIntOfRawIntOp (Rawint.Int64, dsigned, spre, ssigned), a) when dsigned <> ssigned ->
1824          atom_warning "in coercions that change signedness, source sign wins in any bit extensions";
1825          code_int64_of_rawint cg pos debug spre ssigned a cont
1827       (* Unknown unary *)
1828     | AtomUnop (_, _) ->
1829          raise (MirException (pos, NotImplemented "code_atom_int64: AtomUnop"))
1831       (* Binary arithmetic operations *)
1832     | AtomBinop (PlusOp (ACRawInt (Rawint.Int64, _)), a1, a2) ->
1833          code_binop_int64 cg pos debug a1 a2 cont (fun op1 op2 -> ADC (IL, op1, op2)) (fun op1 op2 -> ADD (IL, op1, op2))
1834     | AtomBinop (MinusOp (ACRawInt (Rawint.Int64, _)), a1, a2) ->
1835          code_binop_int64 cg pos debug a1 a2 cont (fun op1 op2 -> SBB (IL, op1, op2)) (fun op1 op2 -> SUB (IL, op1, op2))
1836     | AtomBinop (MulOp (ACRawInt (Rawint.Int64, false)), a1, a2) ->
1837          (* Unsigned multiplication is easier *)
1838          code_basic_binop_int64 cg pos debug a1 a2 cont (fun insts label ohi1 olo1 ohi2 olo2 ->
1839             `+insts:
1840                new   %rhi, %rlo;
1842                (* Multiply the high-order bits -- here it gets murky *)
1843                movl  eax,  olo1;
1844                mull  ohi2;
1845                movl  rhi,  eax;
1846                movl  eax,  ohi1;
1847                mull  olo2;
1848                addl  rhi,  eax;
1850                (* Multiply the low-order bits, add carry to rhi *)
1851                movl  eax,  olo1;
1852                mull  olo2;
1853                movl  olo1, eax;
1854                addl  rhi,  edx;
1855                movl  ohi1, rhi;`)
1856     | AtomBinop (MulOp (ACRawInt (Rawint.Int64, true)), a1, a2) ->
1857          (* Signed multiplication will use the FP stack -- for now *)
1858          let a1 = AtomUnop (FloatOfRawIntOp (Rawfloat.LongDouble, Rawint.Int64, true), a1) in
1859          let a2 = AtomUnop (FloatOfRawIntOp (Rawfloat.LongDouble, Rawint.Int64, true), a2) in
1860          let a  = AtomBinop (MulOp (ACFloat Rawfloat.LongDouble), a1, a2) in
1861          let a  = AtomUnop  (RawIntOfFloatOp (Rawint.Int64, true, Rawfloat.LongDouble), a) in
1862             code_atom_int64 cg pos debug a cont
1863     | AtomBinop (DivOp (ACRawInt (Rawint.Int64, true)), a1, a2) ->
1864          (* Signed division will use the FP stack -- for now *)
1865          let a1 = AtomUnop (FloatOfRawIntOp (Rawfloat.LongDouble, Rawint.Int64, true), a1) in
1866          let a2 = AtomUnop (FloatOfRawIntOp (Rawfloat.LongDouble, Rawint.Int64, true), a2) in
1867          let a  = AtomBinop (DivOp (ACFloat Rawfloat.LongDouble), a1, a2) in
1868          let a  = AtomUnop  (RawIntOfFloatOp (Rawint.Int64, true, Rawfloat.LongDouble), a) in
1869             code_atom_int64 cg pos debug a cont
1870     | AtomBinop (AndOp (ACRawInt (Rawint.Int64, _)), a1, a2) ->
1871          let logop op1 op2 = AND (IL, op1, op2) in
1872             code_binop_int64 cg pos debug a1 a2 cont logop logop
1873     | AtomBinop (OrOp (ACRawInt (Rawint.Int64, _)), a1, a2) ->
1874          let logop op1 op2 = OR (IL, op1, op2) in
1875             code_binop_int64 cg pos debug a1 a2 cont logop logop
1876     | AtomBinop (XorOp (ACRawInt (Rawint.Int64, _)), a1, a2) ->
1877          let logop op1 op2 = XOR (IL, op1, op2) in
1878             code_binop_int64 cg pos debug a1 a2 cont logop logop
1879     | AtomBinop (SlOp (ACRawInt (Rawint.Int64, signed)), a1, a2) ->
1880          code_shift_binop_int64 cg pos debug a1 a2 cont (fun insts label ohi1 olo1 ohi2 olo2 ->
1881             `+insts:
1882                movl  ecx,  olo2;
1883                shldl ohi1, olo1, ecx;
1884                shll  olo1, ecx;
1885                testl ecx,  $32;
1886                je    label;
1887                movl  ohi1, olo1;
1888                movl  olo1, $0;`)
1889     | AtomBinop (ASrOp (ACRawInt (Rawint.Int64, signed)), a1, a2) ->
1890          code_shift_binop_int64 cg pos debug a1 a2 cont (fun insts label ohi1 olo1 ohi2 olo2 ->
1891             `+insts:
1892                movl  ecx,  olo2;
1893                shrdl olo1, ohi1, ecx;
1894                sarl  ohi1, ecx;
1895                testl ecx,  $32;
1896                je    label;
1897                movl  olo1, ohi1;
1898                sarl  ohi1, $31;`)
1899     | AtomBinop (LSrOp (ACRawInt (Rawint.Int64, signed)), a1, a2) ->
1900          code_shift_binop_int64 cg pos debug a1 a2 cont (fun insts label ohi1 olo1 ohi2 olo2 ->
1901             `+insts:
1902                movl  ecx,  olo2;
1903                shrdl olo1, ohi1, ecx;
1904                shrl  ohi1, ecx;
1905                testl ecx,  $32;
1906                je    label;
1907                movl  olo1, ohi1;
1908                movl  ohi1, $0;`)
1910       (* Binary memory operations *)
1911     | AtomBinop (MemOp (ACRawInt (Rawint.Int64, _), _), a1, a2) ->
1912          code_let_mem_int64 cg pos debug a1 a2 cont
1914       (* Unknown binary *)
1915     | AtomBinop (_, _, _) ->
1916          raise (MirException (pos, NotImplemented "code_atom_int64: AtomBinop"))
1919 (***  Floating-point Operations  ***)
1922 (* code_float_const
1923    Writes the code necessary to load a constant into a floating-point
1924    register.  This just looks up the label that the constant is stored
1925    at and substitutes it in the continuation.  *)
1926 and code_float_const cg pos debug f cont =
1927    let pos = string_pos "code_float_const" pos in
1928    let f = Rawfloat.to_float80 f in
1929    let v = fvals_lookup cg pos f in
1930       cont v
1933 (* code_unop_float
1934    Writes code for a unary floating-point operation that reads its
1935    sole input from %st(0) and writes the result to same register.  *)
1936 and code_unop_float cg pos debug a cont cons =
1937    let pos = string_pos "code_unop_float" pos in
1938    code_atom_float cg pos debug a (fun op ->
1939       let insts, v =
1940          `new -> v:
1941             new   @v;
1942             fldt  op;
1943             add_inst `{ cons `};
1944             fstpt v;`
1945       in
1946          build_atom_block debug "unop" insts v cont)
1949 (* code_basic_binop_float
1950    Writes the infrastructure used by most binary operations.  *)
1951 and code_basic_binop_float cg pos debug a1 a2 cont =
1952    let pos = string_pos "code_basic_binop_float" pos in
1953    let v = FloatRegister (new_symbol_string "binop_res") in
1954    code_atom_float cg pos debug a1 (fun op1 ->
1955       code_atom_float cg pos debug a2 (fun op2 ->
1956          cont v op1 op2))
1959 (* code_binop_float
1960    Writes a binary operator of two floats, where the binary operation
1961    in question is a no-operand that reads both values from the stack,
1962    pops the stack and stores the result in the new top-of-stack.  There
1963    may be other forms of this function for instructions which must take
1964    an argument, or require the arguments to be put on stack in reverse
1965    order.  *)
1966 and code_binop_float cg pos debug a1 a2 cont cons =
1967    let pos = string_pos "code_binop_float" pos in
1968    code_basic_binop_float cg pos debug a1 a2 (fun v op1 op2 ->
1969       let insts, v =
1970          `new -> v:
1971             new   @v;
1972             fldt  op2;        (* This will be %st(1) *)
1973             fldt  op1;        (* This will be %st(0) *)
1974             add_inst `{ cons `};
1975             fstpt v;`
1976       in
1977          build_atom_block debug "binop" insts v cont)
1980 (* code_binop_pop_float
1981    Same as above, but cons doesn't pop the stack, so we do it ourselves.  *)
1982 and code_binop_pop_float cg pos debug a1 a2 cont cons =
1983    let pos = string_pos "code_binop_pop_float" pos in
1984    code_basic_binop_float cg pos debug a1 a2 (fun v op1 op2 ->
1985       let insts, v =
1986          `new -> v:
1987             new   @v;
1988             fldt  op2;        (* This will be %st(1) *)
1989             fldt  op1;        (* This will be %st(0) *)
1990             add_inst `{ cons `};
1991             fstpt v;
1992             fstpt @st(0);`
1993       in
1994          build_atom_block debug "binop" insts v cont)
1997 (* code_let_mem_float
1998    Reads a floating-point value from memory.  We can mostly reuse
1999    code that is constructed earlier, only varying the final load
2000    (which we use the FP stack as an intermediary for, in convenience). *)
2001 and code_let_mem_float cg pos debug float_class a1 a2 cont =
2002    let pos = string_pos "code_let_mem_float" pos in
2003    let v = FloatRegister (new_symbol_string "let_mem") in
2004    let float_class = float_prec float_class in
2005    let load_insts_float operand insts =
2006       let insts = Listbuf.add_list insts
2007          [CommentString "Loading a float from memory";
2008           FMOVP (FT, v, float_class, operand)]
2009       in
2010       let blocks, label = cont v in
2011       let block, label = code_build_dst_block debug "code_let_mem_float" insts label in
2012          block :: blocks, label
2013    in
2014       code_let_mem_aux cg pos debug a1 a2 load_insts_float
2017 (* code_binop_float_max
2018    code_binop_float_min
2019    Computes max- and min-code for floating-point values.  This is
2020    more optimized than doing the computation in FIR, since we can
2021    do the branch optimization here.  *)
2022 and code_binop_float_max cg pos debug a1 a2 cont =
2023    let pos = string_pos "code_binop_float_max" pos in
2024       code_basic_binop_float cg pos debug a1 a2 (fun v op1 op2 ->
2025          code_cond_float_op debug GT cont v op1 op2 op1 op2)
2027 and code_binop_float_min cg pos debug a1 a2 cont =
2028    let pos = string_pos "code_binop_float_min" pos in
2029       code_basic_binop_float cg pos debug a1 a2 (fun v op1 op2 ->
2030          code_cond_float_op debug LT cont v op1 op2 op1 op2)
2033 (* code_atom_float
2034    Builds blocks to compute the atom expression given.  *)
2035 and code_atom_float cg pos debug a cont =
2036    let pos = string_pos "code_atom_float" pos in
2037    match a with
2038       (* Constants and variables *)
2039       AtomInt _
2040     | AtomRawInt _
2041     | AtomFunVar _ ->
2042          raise (MirException (pos, ImplicitCoercion a))
2043     | AtomVar (ACFloat _, v) ->
2044          cont (FloatRegister v)
2045     | AtomVar _ ->
2046          raise (MirException (pos, ImplicitCoercion a))
2047     | AtomFloat x ->
2048          code_float_const cg pos debug x cont
2050       (* Unary arithmetic & bitwise operations *)
2051     | AtomUnop (UMinusOp (ACFloat _), a) ->
2052          code_unop_float cg pos debug a cont FCHS
2053     | AtomUnop (FloatOfRawIntOp (_, pre, true), a) when int32_ok pre ->
2054          code_float_of_int32 cg pos debug a cont
2055     | AtomUnop (FloatOfRawIntOp (_, Rawint.Int64, true), a) ->
2056          code_float_of_int64 cg pos debug a cont
2058       (* Unary FloatOfFloat is an identity *)
2059     | AtomUnop (FloatOfFloatOp _, a) ->
2060          code_atom_float cg pos debug a cont
2062       (* Unary triginometric and roots *)
2063     | AtomUnop (SinOp _, a) ->
2064          code_unop_float cg pos debug a cont FSIN
2065     | AtomUnop (CosOp _, a) ->
2066          code_unop_float cg pos debug a cont FCOS
2067     | AtomUnop (SqrtOp _, a) ->
2068          code_unop_float cg pos debug a cont FSQRT
2070       (* Unknown unary *)
2071     | AtomUnop (_, _) ->
2072          raise (MirException (pos, NotImplemented "code_atom_float: AtomUnop"))
2074       (* Addition and subtraction *)
2075     | AtomBinop (PlusOp (ACFloat _), a1, a2) ->
2076          code_binop_float cg pos debug a2 a1 cont FADDP
2077     | AtomBinop (MinusOp (ACFloat _), a1, a2) ->
2078          code_binop_float cg pos debug a1 a2 cont FSUBP
2079     | AtomBinop (MulOp (ACFloat _), a1, a2) ->
2080          code_binop_float cg pos debug a2 a1 cont FMULP
2081     | AtomBinop (DivOp (ACFloat _), a1, a2) ->
2082          code_binop_float cg pos debug a1 a2 cont FDIVP
2083     | AtomBinop (RemOp (ACFloat _), a1, a2) ->
2084          code_binop_pop_float cg pos debug a1 a2 cont FPREM
2086       (* Bounds operations *)
2087     | AtomBinop (MaxOp (ACFloat _), a1, a2) ->
2088          code_binop_float_max cg pos debug a1 a2 cont
2089     | AtomBinop (MinOp (ACFloat _), a1, a2) ->
2090          code_binop_float_min cg pos debug a1 a2 cont
2092       (* Triginometric operations *)
2093     | AtomBinop (ATan2Op _, a1, a2) ->
2094          code_binop_pop_float cg pos debug a1 a2 cont FPATAN
2096       (* Binary memory operations *)
2097     | AtomBinop (MemOp (ACFloat fprec, _), a1, a2) ->
2098          code_let_mem_float cg pos debug fprec a1 a2 cont
2100       (* Unknown binary *)
2101     | AtomBinop (_, _, _) ->
2102          raise (MirException (pos, NotImplemented "code_atom_float: AtomBinop"))
2105 `end_ocaml_code