1 (***********************************************************************)
5 (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
7 (* Copyright 1996 Institut National de Recherche en Informatique et *)
8 (* en Automatique. All rights reserved. This file is distributed *)
9 (* under the terms of the Q Public License version 1.0. *)
11 (***********************************************************************)
13 (* $Id: emit.mlp 9475 2009-12-16 10:04:38Z xleroy $ *)
15 (* Emission of x86-64 (AMD 64) assembly code *)
27 match Config.system with
32 (* Tradeoff between code size and code speed *)
34 let fastcode_flag = ref true
36 let stack_offset = ref 0
38 (* Layout of the stack frame *)
40 let frame_required () =
41 !contains_calls || num_stack_slots.(0) > 0 || num_stack_slots.(1) > 0
43 let frame_size () = (* includes return address *)
44 if frame_required() then begin
46 (!stack_offset + 8 * (num_stack_slots.(0) + num_stack_slots.(1)) + 8)
51 let slot_offset loc cl =
53 Incoming n -> frame_size() + n
56 then !stack_offset + n * 8
57 else !stack_offset + (num_stack_slots.(0) + n) * 8
63 if macosx then emit_string "_";
64 Emitaux.emit_symbol '$' s
67 if !Clflags.dlcode && not macosx
68 then `call {emit_symbol s}@PLT`
69 else `call {emit_symbol s}`
72 if !Clflags.dlcode && not macosx
73 then `jmp {emit_symbol s}@PLT`
74 else `jmp {emit_symbol s}`
76 let load_symbol_addr s =
78 then `movq {emit_symbol s}@GOTPCREL(%rip)`
80 then `leaq {emit_symbol s}(%rip)`
81 else `movq ${emit_symbol s}`
87 emit_string ".L"; emit_int lbl
89 (* Output a .align directive. *)
92 let n = if macosx then Misc.log2 n else n in
93 ` .align {emit_int n}\n`
95 let emit_Llabel fallthrough lbl =
96 if not fallthrough && !fastcode_flag then emit_align 4;
99 (* Output a pseudo-register *)
101 let emit_reg = function
103 emit_string (register_name r)
104 | { loc = Stack s } as r ->
105 let ofs = slot_offset s (register_class r) in
106 `{emit_int ofs}(%rsp)`
107 | { loc = Unknown } ->
110 (* Output a reference to the lower 8, 16 or 32 bits of a register *)
113 [| "%al"; "%bl"; "%dil"; "%sil"; "%dl"; "%cl"; "%r8b"; "%r9b";
114 "%r10b"; "%r11b"; "%bpl"; "%r12b"; "%r13b" |]
115 let reg_low_16_name =
116 [| "%ax"; "%bx"; "%di"; "%si"; "%dx"; "%cx"; "%r8w"; "%r9w";
117 "%r10w"; "%r11w"; "%bp"; "%r12w"; "%r13w" |]
118 let reg_low_32_name =
119 [| "%eax"; "%ebx"; "%edi"; "%esi"; "%edx"; "%ecx"; "%r8d"; "%r9d";
120 "%r10d"; "%r11d"; "%ebp"; "%r12d"; "%r13d" |]
122 let emit_subreg tbl r =
127 let ofs = slot_offset s (register_class r) in
128 `{emit_int ofs}(%rsp)`
132 let emit_reg8 r = emit_subreg reg_low_8_name r
133 let emit_reg16 r = emit_subreg reg_low_16_name r
134 let emit_reg32 r = emit_subreg reg_low_32_name r
136 (* Output an addressing mode *)
138 let emit_addressing addr r n =
140 | Ibased _ when !Clflags.dlcode -> assert false
143 if d <> 0 then ` + {emit_int d}`;
146 if d <> 0 then emit_int d;
149 if d <> 0 then emit_int d;
150 `({emit_reg r.(n)}, {emit_reg r.(n+1)})`
152 if d <> 0 then emit_int d;
153 `({emit_reg r.(n)}, {emit_reg r.(n)})`
154 | Iscaled(scale, d) ->
155 if d <> 0 then emit_int d;
156 `(, {emit_reg r.(n)}, {emit_int scale})`
157 | Iindexed2scaled(scale, d) ->
158 if d <> 0 then emit_int d;
159 `({emit_reg r.(n)}, {emit_reg r.(n+1)}, {emit_int scale})`
161 (* Record live pointers at call points -- see Emitaux *)
163 let record_frame_label live dbg =
164 let lbl = new_label() in
165 let live_offset = ref [] in
168 {typ = Addr; loc = Reg r} ->
169 live_offset := ((r lsl 1) + 1) :: !live_offset
170 | {typ = Addr; loc = Stack s} as reg ->
171 live_offset := slot_offset s (register_class reg) :: !live_offset
176 fd_frame_size = frame_size();
177 fd_live_offset = !live_offset;
178 fd_debuginfo = dbg } :: !frame_descriptors;
181 let record_frame live dbg =
182 let lbl = record_frame_label live dbg in `{emit_label lbl}:\n`
184 (* Record calls to the GC -- we've moved them out of the way *)
187 { gc_lbl: label; (* Entry label *)
188 gc_return_lbl: label; (* Where to branch after GC *)
189 gc_frame: label } (* Label of frame descriptor *)
191 let call_gc_sites = ref ([] : gc_call list)
193 let emit_call_gc gc =
194 `{emit_label gc.gc_lbl}: {emit_call "caml_call_gc"}\n`;
195 `{emit_label gc.gc_frame}: jmp {emit_label gc.gc_return_lbl}\n`
197 (* Record calls to caml_ml_array_bound_error.
198 In -g mode, we maintain one call to caml_ml_array_bound_error
199 per bound check site. Without -g, we can share a single call. *)
201 type bound_error_call =
202 { bd_lbl: label; (* Entry label *)
203 bd_frame: label } (* Label of frame descriptor *)
205 let bound_error_sites = ref ([] : bound_error_call list)
206 let bound_error_call = ref 0
208 let bound_error_label dbg =
209 if !Clflags.debug then begin
210 let lbl_bound_error = new_label() in
211 let lbl_frame = record_frame_label Reg.Set.empty dbg in
213 { bd_lbl = lbl_bound_error; bd_frame = lbl_frame } :: !bound_error_sites;
216 if !bound_error_call = 0 then bound_error_call := new_label();
220 let emit_call_bound_error bd =
221 `{emit_label bd.bd_lbl}: {emit_call "caml_ml_array_bound_error"}\n`;
222 `{emit_label bd.bd_frame}:\n`
224 let emit_call_bound_errors () =
225 List.iter emit_call_bound_error !bound_error_sites;
226 if !bound_error_call > 0 then
227 `{emit_label !bound_error_call}: {emit_call "caml_ml_array_bound_error"}\n`
229 (* Names for instructions *)
231 let instr_for_intop = function
243 let instr_for_floatop = function
250 let instr_for_floatarithmem = function
252 | Ifloatsub -> "subsd"
253 | Ifloatmul -> "mulsd"
254 | Ifloatdiv -> "divsd"
256 let name_for_cond_branch = function
257 Isigned Ceq -> "e" | Isigned Cne -> "ne"
258 | Isigned Cle -> "le" | Isigned Cgt -> "g"
259 | Isigned Clt -> "l" | Isigned Cge -> "ge"
260 | Iunsigned Ceq -> "e" | Iunsigned Cne -> "ne"
261 | Iunsigned Cle -> "be" | Iunsigned Cgt -> "a"
262 | Iunsigned Clt -> "b" | Iunsigned Cge -> "ae"
264 (* Output an = 0 or <> 0 test. *)
266 let output_test_zero arg =
268 Reg r -> ` testq {emit_reg arg}, {emit_reg arg}\n`
269 | _ -> ` cmpq $0, {emit_reg arg}\n`
271 (* Output a floating-point compare and branch *)
273 let emit_float_test cmp neg arg lbl =
275 | Ceq | Cne -> ` ucomisd `
278 `{emit_reg arg.(1)}, {emit_reg arg.(0)}\n`;
279 let (branch_opcode, need_jp) =
280 match (cmp, neg) with
281 (Ceq, false) -> ("je", true)
282 | (Ceq, true) -> ("jne", true)
283 | (Cne, false) -> ("jne", true)
284 | (Cne, true) -> ("je", true)
285 | (Clt, false) -> ("jb", true)
286 | (Clt, true) -> ("jae", true)
287 | (Cle, false) -> ("jbe", true)
288 | (Cle, true) -> ("ja", true)
289 | (Cgt, false) -> ("ja", false)
290 | (Cgt, true) -> ("jbe", false)
291 | (Cge, false) -> ("jae", true)
292 | (Cge, true) -> ("jb", false) in
293 let branch_if_not_comparable =
294 if cmp = Cne then not neg else neg in
296 if branch_if_not_comparable then begin
297 ` jp {emit_label lbl}\n`;
298 ` {emit_string branch_opcode} {emit_label lbl}\n`
300 let next = new_label() in
301 ` jp {emit_label next}\n`;
302 ` {emit_string branch_opcode} {emit_label lbl}\n`;
303 `{emit_label next}:\n`
306 ` {emit_string branch_opcode} {emit_label lbl}\n`
309 (* Deallocate the stack frame before a return or tail call *)
311 let output_epilogue () =
312 if frame_required() then begin
313 let n = frame_size() - 8 in
314 ` addq ${emit_int n}, %rsp\n`
317 (* Output the assembly code for an instruction *)
319 (* Name of current function *)
320 let function_name = ref ""
321 (* Entry point for tail recursive calls *)
322 let tailrec_entry_point = ref 0
324 let float_constants = ref ([] : (int * string) list)
326 let emit_instr fallthrough i =
329 | Lop(Imove | Ispill | Ireload) ->
330 let src = i.arg.(0) and dst = i.res.(0) in
331 if src.loc <> dst.loc then begin
332 if src.typ = Float then
333 ` movsd {emit_reg src}, {emit_reg dst}\n`
335 ` movq {emit_reg src}, {emit_reg dst}\n`
337 | Lop(Iconst_int n) ->
339 match i.res.(0).loc with
340 Reg n -> ` xorq {emit_reg i.res.(0)}, {emit_reg i.res.(0)}\n`
341 | _ -> ` movq $0, {emit_reg i.res.(0)}\n`
342 end else if n <= 0x7FFFFFFFn && n >= -0x80000000n then
343 ` movq ${emit_nativeint n}, {emit_reg i.res.(0)}\n`
345 ` movabsq ${emit_nativeint n}, {emit_reg i.res.(0)}\n`
346 | Lop(Iconst_float s) ->
347 begin match Int64.bits_of_float (float_of_string s) with
348 | 0x0000_0000_0000_0000L -> (* +0.0 *)
349 ` xorpd {emit_reg i.res.(0)}, {emit_reg i.res.(0)}\n`
351 let lbl = new_label() in
352 float_constants := (lbl, s) :: !float_constants;
353 ` movlpd {emit_label lbl}(%rip), {emit_reg i.res.(0)}\n`
355 | Lop(Iconst_symbol s) ->
356 ` {load_symbol_addr s}, {emit_reg i.res.(0)}\n`
358 ` call *{emit_reg i.arg.(0)}\n`;
359 record_frame i.live i.dbg
360 | Lop(Icall_imm(s)) ->
362 record_frame i.live i.dbg
363 | Lop(Itailcall_ind) ->
365 ` jmp *{emit_reg i.arg.(0)}\n`
366 | Lop(Itailcall_imm s) ->
367 if s = !function_name then
368 ` jmp {emit_label !tailrec_entry_point}\n`
373 | Lop(Iextcall(s, alloc)) ->
375 ` {load_symbol_addr s}, %rax\n`;
376 ` {emit_call "caml_c_call"}\n`;
377 record_frame i.live i.dbg
381 | Lop(Istackoffset n) ->
383 then ` addq ${emit_int(-n)}, %rsp\n`
384 else ` subq ${emit_int(n)}, %rsp\n`;
385 stack_offset := !stack_offset + n
386 | Lop(Iload(chunk, addr)) ->
387 let dest = i.res.(0) in
388 begin match chunk with
390 ` movq {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
392 ` movzbq {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
394 ` movsbq {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
395 | Sixteen_unsigned ->
396 ` movzwq {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
398 ` movswq {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
399 | Thirtytwo_unsigned ->
400 ` movl {emit_addressing addr i.arg 0}, {emit_reg32 dest}\n`
401 | Thirtytwo_signed ->
402 ` movslq {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
404 ` cvtss2sd {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
405 | Double | Double_u ->
406 ` movlpd {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
408 | Lop(Istore(chunk, addr)) ->
409 begin match chunk with
411 ` movq {emit_reg i.arg.(0)}, {emit_addressing addr i.arg 1}\n`
412 | Byte_unsigned | Byte_signed ->
413 ` movb {emit_reg8 i.arg.(0)}, {emit_addressing addr i.arg 1}\n`
414 | Sixteen_unsigned | Sixteen_signed ->
415 ` movw {emit_reg16 i.arg.(0)}, {emit_addressing addr i.arg 1}\n`
416 | Thirtytwo_signed | Thirtytwo_unsigned ->
417 ` movl {emit_reg32 i.arg.(0)}, {emit_addressing addr i.arg 1}\n`
419 ` cvtsd2ss {emit_reg i.arg.(0)}, %xmm15\n`;
420 ` movss %xmm15, {emit_addressing addr i.arg 1}\n`
421 | Double | Double_u ->
422 ` movlpd {emit_reg i.arg.(0)}, {emit_addressing addr i.arg 1}\n`
425 if !fastcode_flag then begin
426 let lbl_redo = new_label() in
427 `{emit_label lbl_redo}: subq ${emit_int n}, %r15\n`;
428 if !Clflags.dlcode then begin
429 ` {load_symbol_addr "caml_young_limit"}, %rax\n`;
430 ` cmpq (%rax), %r15\n`;
432 ` cmpq {emit_symbol "caml_young_limit"}(%rip), %r15\n`;
433 let lbl_call_gc = new_label() in
434 let lbl_frame = record_frame_label i.live Debuginfo.none in
435 ` jb {emit_label lbl_call_gc}\n`;
436 ` leaq 8(%r15), {emit_reg i.res.(0)}\n`;
438 { gc_lbl = lbl_call_gc;
439 gc_return_lbl = lbl_redo;
440 gc_frame = lbl_frame } :: !call_gc_sites
443 16 -> ` {emit_call "caml_alloc1"}\n`
444 | 24 -> ` {emit_call "caml_alloc2"}\n`
445 | 32 -> ` {emit_call "caml_alloc3"}\n`
446 | _ -> ` movq ${emit_int n}, %rax\n`;
447 ` {emit_call "caml_allocN"}\n`
449 `{record_frame i.live Debuginfo.none} leaq 8(%r15), {emit_reg i.res.(0)}\n`
451 | Lop(Iintop(Icomp cmp)) ->
452 ` cmpq {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`;
453 let b = name_for_cond_branch cmp in
454 ` set{emit_string b} %al\n`;
455 ` movzbq %al, {emit_reg i.res.(0)}\n`
456 | Lop(Iintop_imm(Icomp cmp, n)) ->
457 ` cmpq ${emit_int n}, {emit_reg i.arg.(0)}\n`;
458 let b = name_for_cond_branch cmp in
459 ` set{emit_string b} %al\n`;
460 ` movzbq %al, {emit_reg i.res.(0)}\n`
461 | Lop(Iintop Icheckbound) ->
462 let lbl = bound_error_label i.dbg in
463 ` cmpq {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`;
464 ` jbe {emit_label lbl}\n`
465 | Lop(Iintop_imm(Icheckbound, n)) ->
466 let lbl = bound_error_label i.dbg in
467 ` cmpq ${emit_int n}, {emit_reg i.arg.(0)}\n`;
468 ` jbe {emit_label lbl}\n`
469 | Lop(Iintop(Idiv | Imod)) ->
471 ` idivq {emit_reg i.arg.(1)}\n`
472 | Lop(Iintop(Ilsl | Ilsr | Iasr as op)) ->
473 (* We have i.arg.(0) = i.res.(0) and i.arg.(1) = %rcx *)
474 ` {emit_string(instr_for_intop op)} %cl, {emit_reg i.res.(0)}\n`
476 (* We have i.arg.(0) = i.res.(0) *)
477 ` {emit_string(instr_for_intop op)} {emit_reg i.arg.(1)}, {emit_reg i.res.(0)}\n`
478 | Lop(Iintop_imm(Iadd, n)) when i.arg.(0).loc <> i.res.(0).loc ->
479 ` leaq {emit_int n}({emit_reg i.arg.(0)}), {emit_reg i.res.(0)}\n`
480 | Lop(Iintop_imm(Iadd, 1) | Iintop_imm(Isub, -1)) ->
481 ` incq {emit_reg i.res.(0)}\n`
482 | Lop(Iintop_imm(Iadd, -1) | Iintop_imm(Isub, 1)) ->
483 ` decq {emit_reg i.res.(0)}\n`
484 | Lop(Iintop_imm(Idiv, n)) ->
485 (* Note: i.arg.(0) = i.res.(0) = rdx (cf. selection.ml) *)
486 let l = Misc.log2 n in
487 ` movq {emit_reg i.arg.(0)}, %rax\n`;
488 ` addq ${emit_int(n-1)}, {emit_reg i.arg.(0)}\n`;
489 ` testq %rax, %rax\n`;
490 ` cmovns %rax, {emit_reg i.arg.(0)}\n`;
491 ` sarq ${emit_int l}, {emit_reg i.res.(0)}\n`
492 | Lop(Iintop_imm(Imod, n)) ->
493 (* Note: i.arg.(0) = i.res.(0) = rdx (cf. selection.ml) *)
494 ` movq {emit_reg i.arg.(0)}, %rax\n`;
495 ` testq %rax, %rax\n`;
496 ` leaq {emit_int(n-1)}(%rax), %rax\n`;
497 ` cmovns {emit_reg i.arg.(0)}, %rax\n`;
498 ` andq ${emit_int (-n)}, %rax\n`;
499 ` subq %rax, {emit_reg i.res.(0)}\n`
500 | Lop(Iintop_imm(op, n)) ->
501 (* We have i.arg.(0) = i.res.(0) *)
502 ` {emit_string(instr_for_intop op)} ${emit_int n}, {emit_reg i.res.(0)}\n`
504 ` xorpd {emit_symbol "caml_negf_mask"}(%rip), {emit_reg i.res.(0)}\n`
506 ` andpd {emit_symbol "caml_absf_mask"}(%rip), {emit_reg i.res.(0)}\n`
507 | Lop(Iaddf | Isubf | Imulf | Idivf as floatop) ->
508 ` {emit_string(instr_for_floatop floatop)} {emit_reg i.arg.(1)}, {emit_reg i.res.(0)}\n`
509 | Lop(Ifloatofint) ->
510 ` cvtsi2sdq {emit_reg i.arg.(0)}, {emit_reg i.res.(0)}\n`
511 | Lop(Iintoffloat) ->
512 ` cvttsd2siq {emit_reg i.arg.(0)}, {emit_reg i.res.(0)}\n`
513 | Lop(Ispecific(Ilea addr)) ->
514 ` leaq {emit_addressing addr i.arg 0}, {emit_reg i.res.(0)}\n`
515 | Lop(Ispecific(Istore_int(n, addr))) ->
516 ` movq ${emit_nativeint n}, {emit_addressing addr i.arg 0}\n`
517 | Lop(Ispecific(Istore_symbol(s, addr))) ->
518 assert (not !pic_code && not !Clflags.dlcode);
519 ` movq ${emit_symbol s}, {emit_addressing addr i.arg 0}\n`
520 | Lop(Ispecific(Ioffset_loc(n, addr))) ->
521 ` addq ${emit_int n}, {emit_addressing addr i.arg 0}\n`
522 | Lop(Ispecific(Ifloatarithmem(op, addr))) ->
523 ` {emit_string(instr_for_floatarithmem op)} {emit_addressing addr i.arg 1}, {emit_reg i.res.(0)}\n`
530 `{emit_Llabel fallthrough lbl}:\n`
532 ` jmp {emit_label lbl}\n`
533 | Lcondbranch(tst, lbl) ->
536 output_test_zero i.arg.(0);
537 ` jne {emit_label lbl}\n`
539 output_test_zero i.arg.(0);
540 ` je {emit_label lbl}\n`
542 ` cmpq {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`;
543 let b = name_for_cond_branch cmp in
544 ` j{emit_string b} {emit_label lbl}\n`
545 | Iinttest_imm((Isigned Ceq | Isigned Cne |
546 Iunsigned Ceq | Iunsigned Cne) as cmp, 0) ->
547 output_test_zero i.arg.(0);
548 let b = name_for_cond_branch cmp in
549 ` j{emit_string b} {emit_label lbl}\n`
550 | Iinttest_imm(cmp, n) ->
551 ` cmpq ${emit_int n}, {emit_reg i.arg.(0)}\n`;
552 let b = name_for_cond_branch cmp in
553 ` j{emit_string b} {emit_label lbl}\n`
554 | Ifloattest(cmp, neg) ->
555 emit_float_test cmp neg i.arg lbl
557 ` testb $1, {emit_reg8 i.arg.(0)}\n`;
558 ` jne {emit_label lbl}\n`
560 ` testb $1, {emit_reg8 i.arg.(0)}\n`;
561 ` je {emit_label lbl}\n`
563 | Lcondbranch3(lbl0, lbl1, lbl2) ->
564 ` cmpq $1, {emit_reg i.arg.(0)}\n`;
565 begin match lbl0 with
567 | Some lbl -> ` jb {emit_label lbl}\n`
569 begin match lbl1 with
571 | Some lbl -> ` je {emit_label lbl}\n`
573 begin match lbl2 with
575 | Some lbl -> ` jg {emit_label lbl}\n`
578 let lbl = new_label() in
579 (* rax and rdx are clobbered by the Lswitch,
580 meaning that no variable that is live across the Lswitch
581 is assigned to rax or rdx. However, the argument to Lswitch
582 can still be assigned to one of these two registers, so
583 we must be careful not to clobber it before use. *)
585 if i.arg.(0).loc = Reg 0 (* rax *)
586 then (phys_reg 4 (*rdx*), phys_reg 0 (*rax*))
587 else (phys_reg 0 (*rax*), phys_reg 4 (*rdx*)) in
588 ` leaq {emit_label lbl}(%rip), {emit_reg tmp1}\n`;
589 ` movslq ({emit_reg tmp1}, {emit_reg i.arg.(0)}, 4), {emit_reg tmp2}\n`;
590 ` addq {emit_reg tmp2}, {emit_reg tmp1}\n`;
591 ` jmp *{emit_reg tmp1}\n`;
594 else ` .section .rodata\n`;
597 for i = 0 to Array.length jumptbl - 1 do
598 ` .long {emit_label jumptbl.(i)} - {emit_label lbl}\n`
602 ` call {emit_label lbl}\n`
605 ` movq %rsp, %r14\n`;
606 stack_offset := !stack_offset + 16
610 stack_offset := !stack_offset - 16
612 if !Clflags.debug then begin
613 ` {emit_call "caml_raise_exn"}\n`;
614 record_frame Reg.Set.empty i.dbg
616 ` movq %r14, %rsp\n`;
621 let rec emit_all fallthrough i =
625 emit_instr fallthrough i;
626 emit_all (Linearize.has_fallthrough i.desc) i.next
628 (* Emission of the floating-point constants *)
630 let emit_float_constant (lbl, cst) =
632 emit_float64_directive ".quad" cst
634 (* Emission of the profiling prelude *)
636 let emit_profile () =
637 match Config.system with
639 (* mcount preserves rax, rcx, rdx, rsi, rdi, r8, r9 explicitly
640 and rbx, rbp, r12-r15 like all C functions.
641 We need to preserve r10 and r11 ourselves, since Caml can
642 use them for argument passing. *)
644 ` movq %rsp, %rbp\n`;
646 ` {emit_call "mcount"}\n`;
650 () (*unsupported yet*)
652 (* Emission of a function declaration *)
654 let fundecl fundecl =
655 function_name := fundecl.fun_name;
656 fastcode_flag := fundecl.fun_fast;
657 tailrec_entry_point := new_label();
659 float_constants := [];
661 bound_error_sites := [];
662 bound_error_call := 0;
666 && not !Clflags.output_c_object
667 && is_generic_function fundecl.fun_name
669 ` .private_extern {emit_symbol fundecl.fun_name}\n`
671 ` .globl {emit_symbol fundecl.fun_name}\n`;
672 `{emit_symbol fundecl.fun_name}:\n`;
673 if !Clflags.gprofile then emit_profile();
674 if frame_required() then begin
675 let n = frame_size() - 8 in
676 ` subq ${emit_int n}, %rsp\n`
678 `{emit_label !tailrec_entry_point}:\n`;
679 emit_all true fundecl.fun_body;
680 List.iter emit_call_gc !call_gc_sites;
681 emit_call_bound_errors ();
682 if !float_constants <> [] then begin
685 else ` .section .rodata.cst8,\"a\",@progbits\n`;
686 List.iter emit_float_constant !float_constants
688 match Config.system with
690 ` .type {emit_symbol fundecl.fun_name},@function\n`;
691 ` .size {emit_symbol fundecl.fun_name},.-{emit_symbol fundecl.fun_name}\n`
694 (* Emission of data *)
696 let emit_item = function
698 ` .globl {emit_symbol s}\n`;
699 | Cdefine_symbol s ->
701 | Cdefine_label lbl ->
702 `{emit_label (100000 + lbl)}:\n`
704 ` .byte {emit_int n}\n`
706 ` .word {emit_int n}\n`
708 ` .long {emit_nativeint n}\n`
710 ` .quad {emit_nativeint n}\n`
712 emit_float32_directive ".long" f
714 emit_float64_directive ".quad" f
715 | Csymbol_address s ->
716 ` .quad {emit_symbol s}\n`
717 | Clabel_address lbl ->
718 ` .quad {emit_label (100000 + lbl)}\n`
720 emit_string_directive " .ascii " s
722 if n > 0 then ` .space {emit_int n}\n`
728 List.iter emit_item l
730 (* Beginning / end of an assembly file *)
732 let begin_assembly() =
733 if !Clflags.dlcode then begin
734 (* from amd64.S; could emit these constants on demand *)
738 `caml_negf_mask: .quad 0x8000000000000000, 0\n`;
740 `caml_absf_mask: .quad 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF\n`;
742 ` .section .rodata.cst8,\"a\",@progbits\n`;
744 `caml_negf_mask: .quad 0x8000000000000000, 0\n`;
746 `caml_absf_mask: .quad 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF\n`;
749 let lbl_begin = Compilenv.make_symbol (Some "data_begin") in
751 ` .globl {emit_symbol lbl_begin}\n`;
752 `{emit_symbol lbl_begin}:\n`;
753 let lbl_begin = Compilenv.make_symbol (Some "code_begin") in
755 ` .globl {emit_symbol lbl_begin}\n`;
756 `{emit_symbol lbl_begin}:\n`;
757 if macosx then ` nop\n` (* PR#4690 *)
760 let lbl_end = Compilenv.make_symbol (Some "code_end") in
762 if macosx then ` nop\n`; (* suppress "ld warning: atom sorting error" *)
763 ` .globl {emit_symbol lbl_end}\n`;
764 `{emit_symbol lbl_end}:\n`;
766 let lbl_end = Compilenv.make_symbol (Some "data_end") in
767 ` .globl {emit_symbol lbl_end}\n`;
768 `{emit_symbol lbl_end}:\n`;
770 let lbl = Compilenv.make_symbol (Some "frametable") in
771 ` .globl {emit_symbol lbl}\n`;
772 `{emit_symbol lbl}:\n`;
774 { efa_label = (fun l -> ` .quad {emit_label l}\n`);
775 efa_16 = (fun n -> ` .word {emit_int n}\n`);
776 efa_32 = (fun n -> ` .long {emit_int32 n}\n`);
777 efa_word = (fun n -> ` .quad {emit_int n}\n`);
778 efa_align = emit_align;
781 let setcnt = ref 0 in
784 ` .set L$set${emit_int !setcnt}, ({emit_label lbl} - .) + {emit_int32 ofs}\n`;
785 ` .long L$set${emit_int !setcnt}\n`
788 ` .long ({emit_label lbl} - .) + {emit_int32 ofs}\n`
790 efa_def_label = (fun l -> `{emit_label l}:\n`);
791 efa_string = (fun s -> emit_string_directive " .asciz " s) };
792 if Config.system = "linux" then
793 (* Mark stack as non-executable, PR#4564 *)
794 ` .section .note.GNU-stack,\"\",%progbits\n`