2 * x86-64 code generator for TCC
4 * Copyright (c) 2008 Shinichiro Hamaji
6 * Based on i386-gen.c by Fabrice Bellard
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #ifdef TARGET_DEFS_ONLY
25 /* number of available registers */
29 /* a register can belong to several classes. The classes must be
30 sorted from more general to more precise (see gv2() code which does
31 assumptions on it). */
32 #define RC_INT 0x0001 /* generic integer register */
33 #define RC_FLOAT 0x0002 /* generic float register */
41 #define RC_XMM0 0x0020
42 #define RC_XMM1 0x0040
43 #define RC_ST0 0x0080 /* only for long double */
44 #define RC_IRET RC_RAX /* function return: integer register */
45 #define RC_LRET RC_RDX /* function return: second integer register */
46 #define RC_FRET RC_XMM0 /* function return: float register */
47 #define RC_QRET RC_XMM1 /* function return: second float register */
49 /* pretty names for the registers */
65 TREG_ST0
= 4, // SP slot won't be used
70 #define REX_BASE(reg) (((reg) >> 3) & 1)
71 #define REG_VALUE(reg) ((reg) & 7)
73 /* return registers for function */
74 #define REG_IRET TREG_RAX /* single word int return register */
75 #define REG_LRET TREG_RDX /* second word return register (for long long) */
76 #define REG_FRET TREG_XMM0 /* float return register */
77 #define REG_QRET TREG_XMM1 /* second float return register */
79 /* defined if function parameters must be evaluated in reverse order */
80 #define INVERT_FUNC_PARAMS
82 /* pointer size, in bytes */
85 /* long double size and alignment, in bytes */
86 #define LDOUBLE_SIZE 16
87 #define LDOUBLE_ALIGN 8
88 /* maximum alignment (for aligned attribute support) */
91 /******************************************************/
94 #define EM_TCC_TARGET EM_X86_64
96 /* relocation type for 32 bit data relocation */
97 #define R_DATA_32 R_X86_64_32
98 #define R_DATA_PTR R_X86_64_64
99 #define R_JMP_SLOT R_X86_64_JUMP_SLOT
100 #define R_COPY R_X86_64_COPY
102 #define ELF_START_ADDR 0x08048000
103 #define ELF_PAGE_SIZE 0x1000
105 /******************************************************/
106 #else /* ! TARGET_DEFS_ONLY */
107 /******************************************************/
111 ST_DATA
const int reg_classes
[NB_REGS
] = {
112 /* eax */ RC_INT
| RC_RAX
,
113 /* ecx */ RC_INT
| RC_RCX
,
114 /* edx */ RC_INT
| RC_RDX
,
128 /* xmm0 */ RC_FLOAT
| RC_XMM0
,
129 /* xmm1 */ RC_FLOAT
| RC_XMM1
,
132 static unsigned long func_sub_sp_offset
;
133 static int func_ret_sub
;
135 /* XXX: make it faster ? */
140 if (ind1
> cur_text_section
->data_allocated
)
141 section_realloc(cur_text_section
, ind1
);
142 cur_text_section
->data
[ind
] = c
;
144 assert((ind
< 4) || (cur_text_section
->data
[ind
-4] != ('\362'&0xFF)) || (cur_text_section
->data
[ind
-3] != '\017')
145 || (cur_text_section
->data
[ind
-2] != 'X') || (cur_text_section
->data
[ind
-1] != '\001'));
148 void o(unsigned int c
)
170 void gen_le64(int64_t c
)
182 void orex(int ll
, int r
, int r2
, int b
)
184 if ((r
& VT_VALMASK
) >= VT_CONST
)
186 if ((r2
& VT_VALMASK
) >= VT_CONST
)
188 if (ll
|| REX_BASE(r
) || REX_BASE(r2
))
189 o(0x40 | REX_BASE(r
) | (REX_BASE(r2
) << 2) | (ll
<< 3));
193 /* output a symbol and patch all calls to it */
194 void gsym_addr(int t
, int a
)
198 ptr
= (int *)(cur_text_section
->data
+ t
);
199 n
= *ptr
; /* next value */
210 /* psym is used to put an instruction with a data field which is a
211 reference to a symbol. It is in fact the same as oad ! */
214 static int is64_type(int t
)
216 return ((t
& VT_BTYPE
) == VT_PTR
||
217 (t
& VT_BTYPE
) == VT_FUNC
||
218 (t
& VT_BTYPE
) == VT_LLONG
);
221 static int is_sse_float(int t
) {
224 return bt
== VT_DOUBLE
|| bt
== VT_FLOAT
;
228 /* instruction + 4 bytes data. Return the address of the data */
229 ST_FUNC
int oad(int c
, int s
)
235 if (ind1
> cur_text_section
->data_allocated
)
236 section_realloc(cur_text_section
, ind1
);
237 *(int *)(cur_text_section
->data
+ ind
) = s
;
243 ST_FUNC
void gen_addr32(int r
, Sym
*sym
, int c
)
246 greloc(cur_text_section
, sym
, ind
, R_X86_64_32
);
250 /* output constant with relocation if 'r & VT_SYM' is true */
251 ST_FUNC
void gen_addr64(int r
, Sym
*sym
, int64_t c
)
254 greloc(cur_text_section
, sym
, ind
, R_X86_64_64
);
258 /* output constant with relocation if 'r & VT_SYM' is true */
259 ST_FUNC
void gen_addrpc32(int r
, Sym
*sym
, int c
)
262 greloc(cur_text_section
, sym
, ind
, R_X86_64_PC32
);
266 /* output got address with relocation */
267 static void gen_gotpcrel(int r
, Sym
*sym
, int c
)
269 #ifndef TCC_TARGET_PE
272 greloc(cur_text_section
, sym
, ind
, R_X86_64_GOTPCREL
);
273 sr
= cur_text_section
->reloc
;
274 rel
= (ElfW(Rela
) *)(sr
->data
+ sr
->data_offset
- sizeof(ElfW(Rela
)));
277 printf("picpic: %s %x %x | %02x %02x %02x\n", get_tok_str(sym
->v
, NULL
), c
, r
,
278 cur_text_section
->data
[ind
-3],
279 cur_text_section
->data
[ind
-2],
280 cur_text_section
->data
[ind
-1]
282 greloc(cur_text_section
, sym
, ind
, R_X86_64_PC32
);
286 /* we use add c, %xxx for displacement */
288 o(0xc0 + REG_VALUE(r
));
293 static void gen_modrm_impl(int op_reg
, int r
, Sym
*sym
, int c
, int is_got
)
295 op_reg
= REG_VALUE(op_reg
) << 3;
296 if ((r
& VT_VALMASK
) == VT_CONST
) {
297 /* constant memory reference */
300 gen_gotpcrel(r
, sym
, c
);
302 gen_addrpc32(r
, sym
, c
);
304 } else if ((r
& VT_VALMASK
) == VT_LOCAL
) {
305 /* currently, we use only ebp as base */
307 /* short reference */
311 oad(0x85 | op_reg
, c
);
313 } else if ((r
& VT_VALMASK
) >= TREG_MEM
) {
315 g(0x80 | op_reg
| REG_VALUE(r
));
318 g(0x00 | op_reg
| REG_VALUE(r
));
321 g(0x00 | op_reg
| REG_VALUE(r
));
325 /* generate a modrm reference. 'op_reg' contains the addtionnal 3
327 static void gen_modrm(int op_reg
, int r
, Sym
*sym
, int c
)
329 gen_modrm_impl(op_reg
, r
, sym
, c
, 0);
332 /* generate a modrm reference. 'op_reg' contains the addtionnal 3
334 static void gen_modrm64(int opcode
, int op_reg
, int r
, Sym
*sym
, int c
)
337 is_got
= (op_reg
& TREG_MEM
) && !(sym
->type
.t
& VT_STATIC
);
338 orex(1, r
, op_reg
, opcode
);
339 gen_modrm_impl(op_reg
, r
, sym
, c
, is_got
);
343 /* load 'r' from value 'sv' */
344 void load(int r
, SValue
*sv
)
346 int v
, t
, ft
, fc
, fr
;
351 sv
= pe_getimport(sv
, &v2
);
358 #ifndef TCC_TARGET_PE
359 /* we use indirect access via got */
360 if ((fr
& VT_VALMASK
) == VT_CONST
&& (fr
& VT_SYM
) &&
361 (fr
& VT_LVAL
) && !(sv
->sym
->type
.t
& VT_STATIC
)) {
362 /* use the result register as a temporal register */
363 int tr
= r
| TREG_MEM
;
365 /* we cannot use float registers as a temporal register */
366 tr
= get_reg(RC_INT
) | TREG_MEM
;
368 gen_modrm64(0x8b, tr
, fr
, sv
->sym
, 0);
370 /* load from the temporal register */
378 if (v
== VT_LLOCAL
) {
380 v1
.r
= VT_LOCAL
| VT_LVAL
;
383 if (!(reg_classes
[fr
] & RC_INT
))
384 fr
= get_reg(RC_INT
);
388 if ((ft
& VT_BTYPE
) == VT_FLOAT
) {
390 r
= REG_VALUE(r
); /* movd */
391 } else if ((ft
& VT_BTYPE
) == VT_DOUBLE
) {
392 b
= 0x7e0ff3; /* movq */
394 } else if ((ft
& VT_BTYPE
) == VT_LDOUBLE
) {
395 b
= 0xdb, r
= 5; /* fldt */
396 } else if ((ft
& VT_TYPE
) == VT_BYTE
) {
397 b
= 0xbe0f; /* movsbl */
398 } else if ((ft
& VT_TYPE
) == (VT_BYTE
| VT_UNSIGNED
)) {
399 b
= 0xb60f; /* movzbl */
400 } else if ((ft
& VT_TYPE
) == VT_SHORT
) {
401 b
= 0xbf0f; /* movswl */
402 } else if ((ft
& VT_TYPE
) == (VT_SHORT
| VT_UNSIGNED
)) {
403 b
= 0xb70f; /* movzwl */
405 assert(((ft
& VT_BTYPE
) == VT_INT
) || ((ft
& VT_BTYPE
) == VT_LLONG
)
406 || ((ft
& VT_BTYPE
) == VT_PTR
) || ((ft
& VT_BTYPE
) == VT_ENUM
)
407 || ((ft
& VT_BTYPE
) == VT_FUNC
));
412 gen_modrm64(b
, r
, fr
, sv
->sym
, fc
);
415 gen_modrm(r
, fr
, sv
->sym
, fc
);
422 o(0x05 + REG_VALUE(r
) * 8); /* lea xx(%rip), r */
423 gen_addrpc32(fr
, sv
->sym
, fc
);
425 if (sv
->sym
->type
.t
& VT_STATIC
) {
427 o(0x05 + REG_VALUE(r
) * 8); /* lea xx(%rip), r */
428 gen_addrpc32(fr
, sv
->sym
, fc
);
431 o(0x05 + REG_VALUE(r
) * 8); /* mov xx(%rip), r */
432 gen_gotpcrel(r
, sv
->sym
, fc
);
435 } else if (is64_type(ft
)) {
436 orex(1,r
,0, 0xb8 + REG_VALUE(r
)); /* mov $xx, r */
439 orex(0,r
,0, 0xb8 + REG_VALUE(r
)); /* mov $xx, r */
442 } else if (v
== VT_LOCAL
) {
443 orex(1,0,r
,0x8d); /* lea xxx(%ebp), r */
444 gen_modrm(r
, VT_LOCAL
, sv
->sym
, fc
);
445 } else if (v
== VT_CMP
) {
447 if ((fc
& ~0x100) != TOK_NE
)
448 oad(0xb8 + REG_VALUE(r
), 0); /* mov $0, r */
450 oad(0xb8 + REG_VALUE(r
), 1); /* mov $1, r */
453 /* This was a float compare. If the parity bit is
454 set the result was unordered, meaning false for everything
455 except TOK_NE, and true for TOK_NE. */
457 o(0x037a + (REX_BASE(r
) << 8));
459 orex(0,r
,0, 0x0f); /* setxx %br */
461 o(0xc0 + REG_VALUE(r
));
462 } else if (v
== VT_JMP
|| v
== VT_JMPI
) {
465 oad(0xb8 + REG_VALUE(r
), t
); /* mov $1, r */
466 o(0x05eb + (REX_BASE(r
) << 8)); /* jmp after */
469 oad(0xb8 + REG_VALUE(r
), t
^ 1); /* mov $0, r */
471 if ((r
== TREG_XMM0
) || (r
== TREG_XMM1
)) {
473 /* gen_cvt_ftof(VT_DOUBLE); */
474 o(0xf0245cdd); /* fstpl -0x10(%rsp) */
475 /* movsd -0x10(%rsp),%xmmN */
477 o(0x44 + REG_VALUE(r
)*8); /* %xmmN */
480 assert((v
== TREG_XMM0
) || (v
== TREG_XMM1
));
481 if ((ft
& VT_BTYPE
) == VT_FLOAT
) {
484 assert((ft
& VT_BTYPE
) == VT_DOUBLE
);
487 o(0xc0 + REG_VALUE(v
) + REG_VALUE(r
)*8);
489 } else if (r
== TREG_ST0
) {
490 assert((v
== TREG_XMM0
) || (v
== TREG_XMM1
));
491 /* gen_cvt_ftof(VT_LDOUBLE); */
492 /* movsd %xmmN,-0x10(%rsp) */
494 o(0x44 + REG_VALUE(r
)*8); /* %xmmN */
496 o(0xf02444dd); /* fldl -0x10(%rsp) */
499 o(0xc0 + REG_VALUE(r
) + REG_VALUE(v
) * 8); /* mov v, r */
505 /* store register 'r' in lvalue 'v' */
506 void store(int r
, SValue
*v
)
510 /* store the REX prefix in this variable when PIC is enabled */
515 v
= pe_getimport(v
, &v2
);
520 fr
= v
->r
& VT_VALMASK
;
523 #ifndef TCC_TARGET_PE
524 /* we need to access the variable via got */
525 if (fr
== VT_CONST
&& (v
->r
& VT_SYM
)) {
526 /* mov xx(%rip), %r11 */
528 gen_gotpcrel(TREG_R11
, v
->sym
, v
->c
.ul
);
529 pic
= is64_type(bt
) ? 0x49 : 0x41;
533 /* XXX: incorrect if float reg to reg */
534 if (bt
== VT_FLOAT
) {
537 o(0x7e0f); /* movd */
539 } else if (bt
== VT_DOUBLE
) {
542 o(0xd60f); /* movq */
544 } else if (bt
== VT_LDOUBLE
) {
545 o(0xc0d9); /* fld %st(0) */
553 if (bt
== VT_BYTE
|| bt
== VT_BOOL
)
555 else if (is64_type(bt
))
561 /* xxx r, (%r11) where xxx is mov, movq, fld, or etc */
566 if (fr
== VT_CONST
|| fr
== VT_LOCAL
|| (v
->r
& VT_LVAL
)) {
567 gen_modrm64(op64
, r
, v
->r
, v
->sym
, fc
);
568 } else if (fr
!= r
) {
569 /* XXX: don't we really come here? */
571 o(0xc0 + fr
+ r
* 8); /* mov r, fr */
574 if (fr
== VT_CONST
|| fr
== VT_LOCAL
|| (v
->r
& VT_LVAL
)) {
575 gen_modrm(r
, v
->r
, v
->sym
, fc
);
576 } else if (fr
!= r
) {
577 /* XXX: don't we really come here? */
579 o(0xc0 + fr
+ r
* 8); /* mov r, fr */
584 /* 'is_jmp' is '1' if it is a jump */
585 static void gcall_or_jmp(int is_jmp
)
588 if ((vtop
->r
& (VT_VALMASK
| VT_LVAL
)) == VT_CONST
) {
590 if (vtop
->r
& VT_SYM
) {
591 /* relocation case */
592 greloc(cur_text_section
, vtop
->sym
,
593 ind
+ 1, R_X86_64_PC32
);
595 /* put an empty PC32 relocation */
596 put_elf_reloc(symtab_section
, cur_text_section
,
597 ind
+ 1, R_X86_64_PC32
, 0);
599 oad(0xe8 + is_jmp
, vtop
->c
.ul
- 4); /* call/jmp im */
601 /* otherwise, indirect call */
605 o(0xff); /* call/jmp *r */
606 o(0xd0 + REG_VALUE(r
) + (is_jmp
<< 4));
613 static const uint8_t arg_regs
[] = {
614 TREG_RCX
, TREG_RDX
, TREG_R8
, TREG_R9
617 static int func_scratch
;
619 /* Generate function call. The function address is pushed first, then
620 all the parameters in call order. This functions pops all the
621 parameters and the function address. */
623 void gen_offs_sp(int b
, int r
, int d
)
625 orex(1,0,r
& 0x100 ? 0 : r
, b
);
627 o(0x2444 | (REG_VALUE(r
) << 3));
630 o(0x2484 | (REG_VALUE(r
) << 3));
635 /* Return 1 if this function returns via an sret pointer, 0 otherwise */
636 ST_FUNC
int gfunc_sret(CType
*vt
, CType
*ret
, int *ret_align
) {
637 *ret_align
= 1; // Never have to re-align return values for x86-64
641 void gfunc_call(int nb_args
)
643 int size
, align
, r
, args_size
, i
, d
, j
, bt
, struct_size
;
644 int nb_reg_args
, gen_reg
;
646 nb_reg_args
= nb_args
;
647 args_size
= (nb_reg_args
< REGN
? REGN
: nb_reg_args
) * PTR_SIZE
;
649 /* for struct arguments, we need to call memcpy and the function
650 call breaks register passing arguments we are preparing.
651 So, we process arguments which will be passed by stack first. */
652 struct_size
= args_size
;
653 for(i
= 0; i
< nb_args
; i
++) {
654 SValue
*sv
= &vtop
[-i
];
655 bt
= (sv
->type
.t
& VT_BTYPE
);
656 if (bt
== VT_STRUCT
) {
657 size
= type_size(&sv
->type
, &align
);
658 /* align to stack align size */
659 size
= (size
+ 15) & ~15;
660 /* generate structure store */
662 gen_offs_sp(0x8d, r
, struct_size
);
665 /* generate memcpy call */
666 vset(&sv
->type
, r
| VT_LVAL
, 0);
671 } else if (bt
== VT_LDOUBLE
) {
674 gen_offs_sp(0xdb, 0x107, struct_size
);
680 if (func_scratch
< struct_size
)
681 func_scratch
= struct_size
;
683 for (i
= 0; i
< REGN
; ++i
)
684 save_reg(arg_regs
[i
]);
687 gen_reg
= nb_reg_args
;
688 struct_size
= args_size
;
690 for(i
= 0; i
< nb_args
; i
++) {
691 bt
= (vtop
->type
.t
& VT_BTYPE
);
693 if (bt
== VT_STRUCT
|| bt
== VT_LDOUBLE
) {
694 if (bt
== VT_LDOUBLE
)
697 size
= type_size(&vtop
->type
, &align
);
698 /* align to stack align size */
699 size
= (size
+ 15) & ~15;
703 gen_offs_sp(0x8d, d
, struct_size
);
704 gen_offs_sp(0x89, d
, j
*8);
707 gen_offs_sp(0x8d, d
, struct_size
);
711 } else if (is_sse_float(vtop
->type
.t
)) {
712 gv(RC_XMM0
); /* only one float register */
715 /* movq %xmm0, j*8(%rsp) */
716 gen_offs_sp(0xd60f66, 0x100, j
*8);
718 /* movaps %xmm0, %xmmN */
722 /* mov %xmm0, %rxx */
725 o(0xc0 + REG_VALUE(d
));
731 gen_offs_sp(0x89, r
, j
*8);
735 gv(reg_classes
[d
] & ~RC_INT
);
740 o(0xc0 + REG_VALUE(d
) + REG_VALUE(r
) * 8);
754 #define FUNC_PROLOG_SIZE 11
756 /* generate function prolog of type 't' */
757 void gfunc_prolog(CType
*func_type
)
759 int addr
, reg_param_index
, bt
;
768 ind
+= FUNC_PROLOG_SIZE
;
769 func_sub_sp_offset
= ind
;
772 sym
= func_type
->ref
;
774 /* if the function returns a structure, then add an
775 implicit pointer parameter */
777 if ((func_vt
.t
& VT_BTYPE
) == VT_STRUCT
) {
778 gen_modrm64(0x89, arg_regs
[reg_param_index
], VT_LOCAL
, NULL
, addr
);
783 /* define parameters */
784 while ((sym
= sym
->next
) != NULL
) {
786 bt
= type
->t
& VT_BTYPE
;
787 if (reg_param_index
< REGN
) {
788 /* save arguments passed by register */
789 gen_modrm64(0x89, arg_regs
[reg_param_index
], VT_LOCAL
, NULL
, addr
);
791 if (bt
== VT_STRUCT
|| bt
== VT_LDOUBLE
) {
792 sym_push(sym
->v
& ~SYM_FIELD
, type
, VT_LOCAL
| VT_LVAL
| VT_REF
, addr
);
794 sym_push(sym
->v
& ~SYM_FIELD
, type
, VT_LOCAL
| VT_LVAL
, addr
);
800 while (reg_param_index
< REGN
) {
801 if (func_type
->ref
->c
== FUNC_ELLIPSIS
)
802 gen_modrm64(0x89, arg_regs
[reg_param_index
], VT_LOCAL
, NULL
, addr
);
808 /* generate function epilog */
809 void gfunc_epilog(void)
814 if (func_ret_sub
== 0) {
819 g(func_ret_sub
>> 8);
823 ind
= func_sub_sp_offset
- FUNC_PROLOG_SIZE
;
824 /* align local size to word & save local variables */
825 v
= (func_scratch
+ -loc
+ 15) & -16;
828 Sym
*sym
= external_global_sym(TOK___chkstk
, &func_old_type
, 0);
829 oad(0xb8, v
); /* mov stacksize, %eax */
830 oad(0xe8, -4); /* call __chkstk, (does the stackframe too) */
831 greloc(cur_text_section
, sym
, ind
-4, R_X86_64_PC32
);
832 o(0x90); /* fill for FUNC_PROLOG_SIZE = 11 bytes */
834 o(0xe5894855); /* push %rbp, mov %rsp, %rbp */
835 o(0xec8148); /* sub rsp, stacksize */
839 cur_text_section
->data_offset
= saved_ind
;
840 pe_add_unwind_data(ind
, saved_ind
, v
);
841 ind
= cur_text_section
->data_offset
;
846 static void gadd_sp(int val
)
848 if (val
== (char)val
) {
852 oad(0xc48148, val
); /* add $xxx, %rsp */
856 typedef enum X86_64_Mode
{
864 static X86_64_Mode
classify_x86_64_merge(X86_64_Mode a
, X86_64_Mode b
) {
867 else if (a
== x86_64_mode_none
)
869 else if (b
== x86_64_mode_none
)
871 else if ((a
== x86_64_mode_memory
) || (b
== x86_64_mode_memory
))
872 return x86_64_mode_memory
;
873 else if ((a
== x86_64_mode_integer
) || (b
== x86_64_mode_integer
))
874 return x86_64_mode_integer
;
875 else if ((a
== x86_64_mode_x87
) || (b
== x86_64_mode_x87
))
876 return x86_64_mode_memory
;
878 return x86_64_mode_sse
;
881 static X86_64_Mode
classify_x86_64_inner(CType
*ty
) {
885 switch (ty
->t
& VT_BTYPE
) {
886 case VT_VOID
: return x86_64_mode_none
;
895 case VT_ENUM
: return x86_64_mode_integer
;
898 case VT_DOUBLE
: return x86_64_mode_sse
;
900 case VT_LDOUBLE
: return x86_64_mode_x87
;
906 if (f
->next
&& (f
->c
== f
->next
->c
))
907 return x86_64_mode_memory
;
909 mode
= x86_64_mode_none
;
910 for (; f
; f
= f
->next
)
911 mode
= classify_x86_64_merge(mode
, classify_x86_64_inner(&f
->type
));
919 static X86_64_Mode
classify_x86_64_arg(CType
*ty
, CType
*ret
, int *psize
, int *reg_count
) {
921 int size
, align
, ret_t
;
923 if (ty
->t
& (VT_BITFIELD
|VT_ARRAY
)) {
927 mode
= x86_64_mode_integer
;
929 size
= type_size(ty
, &align
);
930 *psize
= (size
+ 7) & ~7;
933 mode
= x86_64_mode_memory
;
935 mode
= classify_x86_64_inner(ty
);
937 case x86_64_mode_integer
:
943 ret_t
= (size
> 4) ? VT_LLONG
: VT_INT
;
947 case x86_64_mode_x87
:
952 case x86_64_mode_sse
:
958 ret_t
= (size
> 4) ? VT_DOUBLE
: VT_FLOAT
;
973 ST_FUNC
int classify_x86_64_va_arg(CType
*ty
) {
974 /* This definition must be synced with stdarg.h */
976 __va_gen_reg
, __va_float_reg
, __va_stack
979 X86_64_Mode mode
= classify_x86_64_arg(ty
, NULL
, &size
, ®_count
);
981 default: return __va_stack
;
982 case x86_64_mode_integer
: return __va_gen_reg
;
983 case x86_64_mode_sse
: return __va_float_reg
;
987 /* Return 1 if this function returns via an sret pointer, 0 otherwise */
988 int gfunc_sret(CType
*vt
, CType
*ret
, int *ret_align
) {
990 *ret_align
= 1; // Never have to re-align return values for x86-64
991 return (classify_x86_64_arg(vt
, ret
, &size
, ®_count
) == x86_64_mode_memory
);
995 static const uint8_t arg_regs
[REGN
] = {
996 TREG_RDI
, TREG_RSI
, TREG_RDX
, TREG_RCX
, TREG_R8
, TREG_R9
999 static int arg_prepare_reg(int idx
) {
1000 if (idx
== 2 || idx
== 3)
1001 /* idx=2: r10, idx=3: r11 */
1004 return arg_regs
[idx
];
1007 /* Generate function call. The function address is pushed first, then
1008 all the parameters in call order. This functions pops all the
1009 parameters and the function address. */
1010 void gfunc_call(int nb_args
)
1014 int size
, align
, r
, args_size
, i
, j
, reg_count
;
1015 int nb_reg_args
= 0;
1016 int nb_sse_args
= 0;
1017 int sse_reg
, gen_reg
;
1019 /* calculate the number of integer/float arguments */
1021 for(i
= 0; i
< nb_args
; i
++) {
1022 mode
= classify_x86_64_arg(&vtop
[-i
].type
, NULL
, &size
, ®_count
);
1024 case x86_64_mode_memory
:
1025 case x86_64_mode_x87
:
1029 case x86_64_mode_sse
:
1030 nb_sse_args
+= reg_count
;
1031 if (nb_sse_args
> 8) args_size
+= size
;
1034 case x86_64_mode_integer
:
1035 nb_reg_args
+= reg_count
;
1036 if (nb_reg_args
> REGN
) args_size
+= size
;
1041 /* for struct arguments, we need to call memcpy and the function
1042 call breaks register passing arguments we are preparing.
1043 So, we process arguments which will be passed by stack first. */
1044 gen_reg
= nb_reg_args
;
1045 sse_reg
= nb_sse_args
;
1047 /* adjust stack to align SSE boundary */
1048 if (args_size
&= 15) {
1049 /* fetch cpu flag before the following sub will change the value */
1050 if (vtop
>= vstack
&& (vtop
->r
& VT_VALMASK
) == VT_CMP
)
1053 args_size
= 16 - args_size
;
1055 oad(0xec81, args_size
); /* sub $xxx, %rsp */
1058 for(i
= 0; i
< nb_args
; i
++) {
1059 /* Swap argument to top, it will possibly be changed here,
1060 and might use more temps. All arguments must remain on the
1061 stack, so that get_reg can correctly evict some of them onto
1062 stack. We could use also use a vrott(nb_args) at the end
1063 of this loop, but this seems faster. */
1064 SValue tmp
= vtop
[0];
1067 mode
= classify_x86_64_arg(&vtop
->type
, NULL
, &size
, ®_count
);
1069 case x86_64_mode_memory
:
1070 /* allocate the necessary size on stack */
1072 oad(0xec81, size
); /* sub $xxx, %rsp */
1073 /* generate structure store */
1074 r
= get_reg(RC_INT
);
1075 orex(1, r
, 0, 0x89); /* mov %rsp, r */
1076 o(0xe0 + REG_VALUE(r
));
1077 vset(&vtop
->type
, r
| VT_LVAL
, 0);
1083 case x86_64_mode_x87
:
1085 size
= LDOUBLE_SIZE
;
1086 oad(0xec8148, size
); /* sub $xxx, %rsp */
1087 o(0x7cdb); /* fstpt 0(%rsp) */
1093 case x86_64_mode_sse
:
1096 o(0x50); /* push $rax */
1097 /* movq %xmm0, (%rsp) */
1099 o(0x04 + REG_VALUE(r
)*8);
1103 sse_reg
-= reg_count
;
1106 case x86_64_mode_integer
:
1108 /* XXX: implicit cast ? */
1109 if (gen_reg
> REGN
) {
1111 orex(0,r
,0,0x50 + REG_VALUE(r
)); /* push r */
1114 gen_reg
-= reg_count
;
1118 /* And swap the argument back to it's original position. */
1124 /* XXX This should be superfluous. */
1125 save_regs(0); /* save used temporary registers */
1127 /* then, we prepare register passing arguments.
1128 Note that we cannot set RDX and RCX in this loop because gv()
1129 may break these temporary registers. Let's use R10 and R11
1131 gen_reg
= nb_reg_args
;
1132 sse_reg
= nb_sse_args
;
1133 for(i
= 0; i
< nb_args
; i
++) {
1134 mode
= classify_x86_64_arg(&vtop
->type
, &type
, &size
, ®_count
);
1135 /* Alter stack entry type so that gv() knows how to treat it */
1141 case x86_64_mode_sse
:
1142 sse_reg
-= reg_count
;
1143 if (sse_reg
+ reg_count
<= 8) {
1144 gv(RC_FRET
); /* only one float register */
1145 if (sse_reg
) { /* avoid redundant movaps %xmm0, %xmm0 */
1146 /* movaps %xmm0, %xmmN */
1148 o(0xc0 + (sse_reg
<< 3));
1149 if (reg_count
== 2) {
1150 /* movaps %xmm1, %xmmN */
1152 o(0xc1 + ((sse_reg
+1) << 3));
1158 case x86_64_mode_integer
:
1160 /* XXX: implicit cast ? */
1161 gen_reg
-= reg_count
;
1162 if (gen_reg
+ reg_count
<= REGN
) {
1163 r
= gv((reg_count
== 1) ? RC_INT
: RC_IRET
);
1164 int d
= arg_prepare_reg(gen_reg
);
1165 orex(1,d
,r
,0x89); /* mov */
1166 o(0xc0 + REG_VALUE(r
) * 8 + REG_VALUE(d
));
1167 if (reg_count
== 2) {
1168 /* Second word of two-word value should always be in rdx
1169 this case is handled via RC_IRET */
1170 assert(vtop
->r2
== TREG_RDX
);
1171 d
= arg_prepare_reg(gen_reg
+1);
1172 orex(1,d
,vtop
->r2
,0x89); /* mov */
1173 o(0xc0 + REG_VALUE(vtop
->r2
) * 8 + REG_VALUE(d
));
1181 /* We shouldn't have many operands on the stack anymore, but the
1182 call address itself is still there, and it might be in %eax
1183 (or edx/ecx) currently, which the below writes would clobber.
1184 So evict all remaining operands here. */
1187 /* Copy R10 and R11 into RDX and RCX, respectively */
1188 if (nb_reg_args
> 2) {
1189 o(0xd2894c); /* mov %r10, %rdx */
1190 if (nb_reg_args
> 3) {
1191 o(0xd9894c); /* mov %r11, %rcx */
1195 oad(0xb8, nb_sse_args
< 8 ? nb_sse_args
: 8); /* mov nb_sse_args, %eax */
1203 #define FUNC_PROLOG_SIZE 11
1205 static void push_arg_reg(int i
) {
1207 gen_modrm64(0x89, arg_regs
[i
], VT_LOCAL
, NULL
, loc
);
1210 /* generate function prolog of type 't' */
1211 void gfunc_prolog(CType
*func_type
)
1214 int i
, addr
, align
, size
, reg_count
;
1215 int param_index
, param_addr
, reg_param_index
, sse_param_index
;
1219 sym
= func_type
->ref
;
1220 addr
= PTR_SIZE
* 2;
1222 ind
+= FUNC_PROLOG_SIZE
;
1223 func_sub_sp_offset
= ind
;
1226 if (func_type
->ref
->c
== FUNC_ELLIPSIS
) {
1227 int seen_reg_num
, seen_sse_num
, seen_stack_size
;
1228 seen_reg_num
= seen_sse_num
= 0;
1229 /* frame pointer and return address */
1230 seen_stack_size
= PTR_SIZE
* 2;
1231 /* count the number of seen parameters */
1232 sym
= func_type
->ref
;
1233 while ((sym
= sym
->next
) != NULL
) {
1235 mode
= classify_x86_64_arg(type
, NULL
, &size
, ®_count
);
1238 seen_stack_size
+= size
;
1241 case x86_64_mode_integer
:
1242 if (seen_reg_num
+ reg_count
<= 8) {
1243 seen_reg_num
+= reg_count
;
1246 seen_stack_size
+= size
;
1250 case x86_64_mode_sse
:
1251 if (seen_sse_num
+ reg_count
<= 8) {
1252 seen_sse_num
+= reg_count
;
1255 seen_stack_size
+= size
;
1262 /* movl $0x????????, -0x10(%rbp) */
1264 gen_le32(seen_reg_num
* 8);
1265 /* movl $0x????????, -0xc(%rbp) */
1267 gen_le32(seen_sse_num
* 16 + 48);
1268 /* movl $0x????????, -0x8(%rbp) */
1270 gen_le32(seen_stack_size
);
1272 /* save all register passing arguments */
1273 for (i
= 0; i
< 8; i
++) {
1275 o(0xd60f66); /* movq */
1276 gen_modrm(7 - i
, VT_LOCAL
, NULL
, loc
);
1277 /* movq $0, loc+8(%rbp) */
1282 for (i
= 0; i
< REGN
; i
++) {
1283 push_arg_reg(REGN
-1-i
);
1287 sym
= func_type
->ref
;
1289 reg_param_index
= 0;
1290 sse_param_index
= 0;
1292 /* if the function returns a structure, then add an
1293 implicit pointer parameter */
1294 func_vt
= sym
->type
;
1295 mode
= classify_x86_64_arg(&func_vt
, NULL
, &size
, ®_count
);
1296 if (mode
== x86_64_mode_memory
) {
1297 push_arg_reg(reg_param_index
);
1304 /* define parameters */
1305 while ((sym
= sym
->next
) != NULL
) {
1307 mode
= classify_x86_64_arg(type
, NULL
, &size
, ®_count
);
1309 case x86_64_mode_sse
:
1310 if (sse_param_index
+ reg_count
<= 8) {
1311 /* save arguments passed by register */
1312 loc
-= reg_count
* 8;
1314 for (i
= 0; i
< reg_count
; ++i
) {
1315 o(0xd60f66); /* movq */
1316 gen_modrm(sse_param_index
, VT_LOCAL
, NULL
, param_addr
+ i
*8);
1322 sse_param_index
+= reg_count
;
1326 case x86_64_mode_memory
:
1327 case x86_64_mode_x87
:
1332 case x86_64_mode_integer
: {
1333 if (reg_param_index
+ reg_count
<= REGN
) {
1334 /* save arguments passed by register */
1335 loc
-= reg_count
* 8;
1337 for (i
= 0; i
< reg_count
; ++i
) {
1338 gen_modrm64(0x89, arg_regs
[reg_param_index
], VT_LOCAL
, NULL
, param_addr
+ i
*8);
1344 reg_param_index
+= reg_count
;
1349 sym_push(sym
->v
& ~SYM_FIELD
, type
,
1350 VT_LOCAL
| VT_LVAL
, param_addr
);
1355 /* generate function epilog */
1356 void gfunc_epilog(void)
1360 o(0xc9); /* leave */
1361 if (func_ret_sub
== 0) {
1364 o(0xc2); /* ret n */
1366 g(func_ret_sub
>> 8);
1368 /* align local size to word & save local variables */
1369 v
= (-loc
+ 15) & -16;
1371 ind
= func_sub_sp_offset
- FUNC_PROLOG_SIZE
;
1372 o(0xe5894855); /* push %rbp, mov %rsp, %rbp */
1373 o(0xec8148); /* sub rsp, stacksize */
1380 /* generate a jump to a label */
1383 return psym(0xe9, t
);
1386 /* generate a jump to a fixed address */
1387 void gjmp_addr(int a
)
1395 oad(0xe9, a
- ind
- 5);
1399 /* generate a test. set 'inv' to invert test. Stack entry is popped */
1400 int gtst(int inv
, int t
)
1404 v
= vtop
->r
& VT_VALMASK
;
1406 /* fast case : can jump directly since flags are set */
1407 if (vtop
->c
.i
& 0x100)
1409 /* This was a float compare. If the parity flag is set
1410 the result was unordered. For anything except != this
1411 means false and we don't jump (anding both conditions).
1412 For != this means true (oring both).
1413 Take care about inverting the test. We need to jump
1414 to our target if the result was unordered and test wasn't NE,
1415 otherwise if unordered we don't want to jump. */
1416 vtop
->c
.i
&= ~0x100;
1417 if (!inv
== (vtop
->c
.i
!= TOK_NE
))
1418 o(0x067a); /* jp +6 */
1422 t
= psym(0x8a, t
); /* jp t */
1426 t
= psym((vtop
->c
.i
- 16) ^ inv
, t
);
1427 } else if (v
== VT_JMP
|| v
== VT_JMPI
) {
1428 /* && or || optimization */
1429 if ((v
& 1) == inv
) {
1430 /* insert vtop->c jump list in t */
1433 p
= (int *)(cur_text_section
->data
+ *p
);
1441 if (is_float(vtop
->type
.t
) ||
1442 (vtop
->type
.t
& VT_BTYPE
) == VT_LLONG
) {
1446 if ((vtop
->r
& (VT_VALMASK
| VT_LVAL
| VT_SYM
)) == VT_CONST
) {
1447 /* constant jmp optimization */
1448 if ((vtop
->c
.i
!= 0) != inv
)
1453 o(0xc0 + REG_VALUE(v
) * 9);
1455 t
= psym(0x85 ^ inv
, t
);
1462 /* generate an integer binary operation */
1463 void gen_opi(int op
)
1468 ll
= is64_type(vtop
[-1].type
.t
);
1469 uu
= (vtop
[-1].type
.t
& VT_UNSIGNED
) != 0;
1470 cc
= (vtop
->r
& (VT_VALMASK
| VT_LVAL
| VT_SYM
)) == VT_CONST
;
1474 case TOK_ADDC1
: /* add with carry generation */
1477 if (cc
&& (!ll
|| (int)vtop
->c
.ll
== vtop
->c
.ll
)) {
1484 /* XXX: generate inc and dec for smaller code ? */
1485 orex(ll
, r
, 0, 0x83);
1486 o(0xc0 | (opc
<< 3) | REG_VALUE(r
));
1489 orex(ll
, r
, 0, 0x81);
1490 oad(0xc0 | (opc
<< 3) | REG_VALUE(r
), c
);
1493 gv2(RC_INT
, RC_INT
);
1496 orex(ll
, r
, fr
, (opc
<< 3) | 0x01);
1497 o(0xc0 + REG_VALUE(r
) + REG_VALUE(fr
) * 8);
1500 if (op
>= TOK_ULT
&& op
<= TOK_GT
) {
1506 case TOK_SUBC1
: /* sub with carry generation */
1509 case TOK_ADDC2
: /* add with carry use */
1512 case TOK_SUBC2
: /* sub with carry use */
1525 gv2(RC_INT
, RC_INT
);
1528 orex(ll
, fr
, r
, 0xaf0f); /* imul fr, r */
1529 o(0xc0 + REG_VALUE(fr
) + REG_VALUE(r
) * 8);
1541 opc
= 0xc0 | (opc
<< 3);
1547 orex(ll
, r
, 0, 0xc1); /* shl/shr/sar $xxx, r */
1548 o(opc
| REG_VALUE(r
));
1549 g(vtop
->c
.i
& (ll
? 63 : 31));
1551 /* we generate the shift in ecx */
1552 gv2(RC_INT
, RC_RCX
);
1554 orex(ll
, r
, 0, 0xd3); /* shl/shr/sar %cl, r */
1555 o(opc
| REG_VALUE(r
));
1568 /* first operand must be in eax */
1569 /* XXX: need better constraint for second operand */
1570 gv2(RC_RAX
, RC_RCX
);
1575 orex(ll
, 0, 0, uu
? 0xd231 : 0x99); /* xor %edx,%edx : cqto */
1576 orex(ll
, fr
, 0, 0xf7); /* div fr, %eax */
1577 o((uu
? 0xf0 : 0xf8) + REG_VALUE(fr
));
1578 if (op
== '%' || op
== TOK_UMOD
)
1590 void gen_opl(int op
)
1595 /* generate a floating point operation 'v = t1 op t2' instruction. The
1596 two operands are guaranted to have the same floating point type */
1597 /* XXX: need to use ST1 too */
1598 void gen_opf(int op
)
1600 int a
, ft
, fc
, swapped
, r
;
1602 (vtop
->type
.t
& VT_BTYPE
) == VT_LDOUBLE
? RC_ST0
: RC_FLOAT
;
1604 /* convert constants to memory references */
1605 if ((vtop
[-1].r
& (VT_VALMASK
| VT_LVAL
)) == VT_CONST
) {
1610 if ((vtop
[0].r
& (VT_VALMASK
| VT_LVAL
)) == VT_CONST
)
1613 /* must put at least one value in the floating point register */
1614 if ((vtop
[-1].r
& VT_LVAL
) &&
1615 (vtop
[0].r
& VT_LVAL
)) {
1621 /* swap the stack if needed so that t1 is the register and t2 is
1622 the memory reference */
1623 if (vtop
[-1].r
& VT_LVAL
) {
1627 if ((vtop
->type
.t
& VT_BTYPE
) == VT_LDOUBLE
) {
1628 if (op
>= TOK_ULT
&& op
<= TOK_GT
) {
1629 /* load on stack second operand */
1630 load(TREG_ST0
, vtop
);
1631 save_reg(TREG_RAX
); /* eax is used by FP comparison code */
1632 if (op
== TOK_GE
|| op
== TOK_GT
)
1634 else if (op
== TOK_EQ
|| op
== TOK_NE
)
1637 o(0xc9d9); /* fxch %st(1) */
1638 o(0xe9da); /* fucompp */
1639 o(0xe0df); /* fnstsw %ax */
1641 o(0x45e480); /* and $0x45, %ah */
1642 o(0x40fC80); /* cmp $0x40, %ah */
1643 } else if (op
== TOK_NE
) {
1644 o(0x45e480); /* and $0x45, %ah */
1645 o(0x40f480); /* xor $0x40, %ah */
1647 } else if (op
== TOK_GE
|| op
== TOK_LE
) {
1648 o(0x05c4f6); /* test $0x05, %ah */
1651 o(0x45c4f6); /* test $0x45, %ah */
1658 /* no memory reference possible for long double operations */
1659 load(TREG_ST0
, vtop
);
1683 o(0xde); /* fxxxp %st, %st(1) */
1688 if (op
>= TOK_ULT
&& op
<= TOK_GT
) {
1689 /* if saved lvalue, then we must reload it */
1692 if ((r
& VT_VALMASK
) == VT_LLOCAL
) {
1694 r
= get_reg(RC_INT
);
1696 v1
.r
= VT_LOCAL
| VT_LVAL
;
1702 if (op
== TOK_EQ
|| op
== TOK_NE
) {
1705 if (op
== TOK_LE
|| op
== TOK_LT
)
1707 if (op
== TOK_LE
|| op
== TOK_GE
) {
1708 op
= 0x93; /* setae */
1710 op
= 0x97; /* seta */
1718 assert(!(vtop
[-1].r
& VT_LVAL
));
1720 if ((vtop
->type
.t
& VT_BTYPE
) == VT_DOUBLE
)
1722 o(0x2e0f); /* ucomisd */
1724 if (vtop
->r
& VT_LVAL
) {
1725 gen_modrm(vtop
[-1].r
, r
, vtop
->sym
, fc
);
1727 o(0xc0 + REG_VALUE(vtop
[0].r
) + REG_VALUE(vtop
[-1].r
)*8);
1732 vtop
->c
.i
= op
| 0x100;
1734 assert((vtop
->type
.t
& VT_BTYPE
) != VT_LDOUBLE
);
1752 assert((ft
& VT_BTYPE
) != VT_LDOUBLE
);
1755 /* if saved lvalue, then we must reload it */
1756 if ((vtop
->r
& VT_VALMASK
) == VT_LLOCAL
) {
1758 r
= get_reg(RC_INT
);
1760 v1
.r
= VT_LOCAL
| VT_LVAL
;
1766 assert(!(vtop
[-1].r
& VT_LVAL
));
1768 assert(vtop
->r
& VT_LVAL
);
1773 if ((ft
& VT_BTYPE
) == VT_DOUBLE
) {
1781 if (vtop
->r
& VT_LVAL
) {
1782 gen_modrm(vtop
[-1].r
, r
, vtop
->sym
, fc
);
1784 o(0xc0 + REG_VALUE(vtop
[0].r
) + REG_VALUE(vtop
[-1].r
)*8);
1792 /* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
1793 and 'long long' cases. */
1794 void gen_cvt_itof(int t
)
1796 if ((t
& VT_BTYPE
) == VT_LDOUBLE
) {
1799 if ((vtop
->type
.t
& VT_BTYPE
) == VT_LLONG
) {
1800 /* signed long long to float/double/long double (unsigned case
1801 is handled generically) */
1802 o(0x50 + (vtop
->r
& VT_VALMASK
)); /* push r */
1803 o(0x242cdf); /* fildll (%rsp) */
1804 o(0x08c48348); /* add $8, %rsp */
1805 } else if ((vtop
->type
.t
& (VT_BTYPE
| VT_UNSIGNED
)) ==
1806 (VT_INT
| VT_UNSIGNED
)) {
1807 /* unsigned int to float/double/long double */
1808 o(0x6a); /* push $0 */
1810 o(0x50 + (vtop
->r
& VT_VALMASK
)); /* push r */
1811 o(0x242cdf); /* fildll (%rsp) */
1812 o(0x10c48348); /* add $16, %rsp */
1814 /* int to float/double/long double */
1815 o(0x50 + (vtop
->r
& VT_VALMASK
)); /* push r */
1816 o(0x2404db); /* fildl (%rsp) */
1817 o(0x08c48348); /* add $8, %rsp */
1821 int r
= get_reg(RC_FLOAT
);
1823 o(0xf2 + ((t
& VT_BTYPE
) == VT_FLOAT
?1:0));
1824 if ((vtop
->type
.t
& (VT_BTYPE
| VT_UNSIGNED
)) ==
1825 (VT_INT
| VT_UNSIGNED
) ||
1826 (vtop
->type
.t
& VT_BTYPE
) == VT_LLONG
) {
1830 o(0xc0 + (vtop
->r
& VT_VALMASK
) + REG_VALUE(r
)*8); /* cvtsi2sd */
1835 /* convert from one floating point type to another */
1836 void gen_cvt_ftof(int t
)
1844 if (bt
== VT_FLOAT
) {
1846 if (tbt
== VT_DOUBLE
) {
1847 o(0x140f); /* unpcklps */
1848 o(0xc0 + REG_VALUE(vtop
->r
)*9);
1849 o(0x5a0f); /* cvtps2pd */
1850 o(0xc0 + REG_VALUE(vtop
->r
)*9);
1851 } else if (tbt
== VT_LDOUBLE
) {
1853 /* movss %xmm0,-0x10(%rsp) */
1855 o(0x44 + REG_VALUE(vtop
->r
)*8);
1857 o(0xf02444d9); /* flds -0x10(%rsp) */
1860 } else if (bt
== VT_DOUBLE
) {
1862 if (tbt
== VT_FLOAT
) {
1863 o(0x140f66); /* unpcklpd */
1864 o(0xc0 + REG_VALUE(vtop
->r
)*9);
1865 o(0x5a0f66); /* cvtpd2ps */
1866 o(0xc0 + REG_VALUE(vtop
->r
)*9);
1867 } else if (tbt
== VT_LDOUBLE
) {
1869 /* movsd %xmm0,-0x10(%rsp) */
1871 o(0x44 + REG_VALUE(vtop
->r
)*8);
1873 o(0xf02444dd); /* fldl -0x10(%rsp) */
1878 int r
= get_reg(RC_FLOAT
);
1879 if (tbt
== VT_DOUBLE
) {
1880 o(0xf0245cdd); /* fstpl -0x10(%rsp) */
1881 /* movsd -0x10(%rsp),%xmm0 */
1883 o(0x44 + REG_VALUE(r
)*8);
1886 } else if (tbt
== VT_FLOAT
) {
1887 o(0xf0245cd9); /* fstps -0x10(%rsp) */
1888 /* movss -0x10(%rsp),%xmm0 */
1890 o(0x44 + REG_VALUE(r
)*8);
1897 /* convert fp to int 't' type */
1898 void gen_cvt_ftoi(int t
)
1900 int ft
, bt
, size
, r
;
1903 if (bt
== VT_LDOUBLE
) {
1904 gen_cvt_ftof(VT_DOUBLE
);
1914 r
= get_reg(RC_INT
);
1915 if (bt
== VT_FLOAT
) {
1917 } else if (bt
== VT_DOUBLE
) {
1922 orex(size
== 8, r
, 0, 0x2c0f); /* cvttss2si or cvttsd2si */
1923 o(0xc0 + REG_VALUE(vtop
->r
) + REG_VALUE(r
)*8);
1927 /* computed goto support */
1934 /* end of x86-64 code generator */
1935 /*************************************************************/
1936 #endif /* ! TARGET_DEFS_ONLY */
1937 /******************************************************/