2 * x86_64 specific functions for TCC assembler
4 * Copyright (c) 2009 Frédéric Feret
6 * Based on i386-asm.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 #define MAX_OPERANDS 3
25 typedef struct ASMInstr
{
29 #define OPC_JMP 0x01 /* jmp operand */
30 #define OPC_B 0x02 /* only used zith OPC_WL */
31 #define OPC_WL 0x04 /* accepts w, l or no suffix */
32 #define OPC_BWL (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */
33 #define OPC_REG 0x08 /* register is added to opcode */
34 #define OPC_MODRM 0x10 /* modrm encoding */
35 #define OPC_FWAIT 0x20 /* add fwait opcode */
36 #define OPC_TEST 0x40 /* test opcodes */
37 #define OPC_SHIFT 0x80 /* shift opcodes */
38 #define OPC_D16 0x0100 /* generate data16 prefix */
39 #define OPC_ARITH 0x0200 /* arithmetic opcodes */
40 #define OPC_SHORTJMP 0x0400 /* short jmp operand */
41 #define OPC_FARITH 0x0800 /* FPU arithmetic opcodes */
42 #define OPC_WLQ 0x1000 /* accepts w, l, q or no suffix */
43 #define OPC_BWLQ (OPC_B | OPC_WLQ) /* accepts b, w, l, q or no suffix */
44 #define OPC_GROUP_SHIFT 13
46 /* in order to compress the operand type, we use specific operands and
48 #define OPT_REG8 0 /* warning: value is hardcoded from TOK_ASM_xxx */
49 #define OPT_REG16 1 /* warning: value is hardcoded from TOK_ASM_xxx */
50 #define OPT_REG32 2 /* warning: value is hardcoded from TOK_ASM_xxx */
52 #define OPT_MMX 4 /* warning: value is hardcoded from TOK_ASM_xxx */
53 #define OPT_SSE 5 /* warning: value is hardcoded from TOK_ASM_xxx */
54 #define OPT_CR 6 /* warning: value is hardcoded from TOK_ASM_xxx */
55 #define OPT_TR 7 /* warning: value is hardcoded from TOK_ASM_xxx */
56 #define OPT_DB 8 /* warning: value is hardcoded from TOK_ASM_xxx */
64 #define OPT_EAX 16 /* %al, %ax, %eax or %rax register */
65 #define OPT_ST0 17 /* %st(0) register */
66 #define OPT_CL 18 /* %cl register */
67 #define OPT_DX 19 /* %dx register */
68 #define OPT_ADDR 20 /* OP_EA with only offset */
69 #define OPT_INDIR 21 /* *(expr) */
72 #define OPT_COMPOSITE_FIRST 22
73 #define OPT_IM 23 /* IM8 | IM16 | IM32 | IM64 */
74 #define OPT_REG 24 /* REG8 | REG16 | REG32 | REG64 */
75 #define OPT_REGW 25 /* REG16 | REG32 | REG64 */
76 #define OPT_IMW 26 /* IM16 | IM32 | IM64 */
77 #define OPT_IMNO64 27 /* IM16 | IM32 */
79 /* can be ored with any OPT_xxx */
83 uint8_t op_type
[MAX_OPERANDS
]; /* see OP_xxx */
86 typedef struct Operand
{
88 #define OP_REG8 (1 << OPT_REG8)
89 #define OP_REG16 (1 << OPT_REG16)
90 #define OP_REG32 (1 << OPT_REG32)
91 #define OP_MMX (1 << OPT_MMX)
92 #define OP_SSE (1 << OPT_SSE)
93 #define OP_CR (1 << OPT_CR)
94 #define OP_TR (1 << OPT_TR)
95 #define OP_DB (1 << OPT_DB)
96 #define OP_SEG (1 << OPT_SEG)
97 #define OP_ST (1 << OPT_ST)
98 #define OP_IM8 (1 << OPT_IM8)
99 #define OP_IM8S (1 << OPT_IM8S)
100 #define OP_IM16 (1 << OPT_IM16)
101 #define OP_IM32 (1 << OPT_IM32)
102 #define OP_EAX (1 << OPT_EAX)
103 #define OP_ST0 (1 << OPT_ST0)
104 #define OP_CL (1 << OPT_CL)
105 #define OP_DX (1 << OPT_DX)
106 #define OP_ADDR (1 << OPT_ADDR)
107 #define OP_INDIR (1 << OPT_INDIR)
108 #define OP_REG64 (1 << OPT_REG64)
109 #define OP_IM64 (1 << OPT_IM64)
111 #define OP_EA 0x40000000
112 #define OP_REG (OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64)
113 #define OP_IM OP_IM64
114 int8_t reg
; /* register, -1 if none */
115 int8_t reg2
; /* second register, -1 if none */
120 static const uint8_t reg_to_size
[9] = {
127 0, 0, 1, 0, 2, 0, 0, 0, 3
130 #define NB_TEST_OPCODES 30
132 static const uint8_t test_bits
[NB_TEST_OPCODES
] = {
165 static const uint8_t segment_prefixes
[] = {
174 static const ASMInstr asm_instrs
[] = {
176 #define DEF_ASM_OP0(name, opcode)
177 #define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 0 },
178 #define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 1, { op0 }},
179 #define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 2, { op0, op1 }},
180 #define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 3, { op0, op1, op2 }},
181 #include "x86_64-asm.h"
187 static const uint16_t op0_codes
[] = {
189 #define DEF_ASM_OP0(x, opcode) opcode,
190 #define DEF_ASM_OP0L(name, opcode, group, instr_type)
191 #define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
192 #define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
193 #define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
194 #include "x86_64-asm.h"
197 static inline int get_reg_shift(TCCState
*s1
)
201 v
= asm_int_expr(s1
);
216 expect("1, 2, 4 or 8 constant");
223 static int asm_parse_reg(void)
229 if (tok
>= TOK_ASM_rax
&& tok
<= TOK_ASM_rdi
) {
230 reg
= tok
- TOK_ASM_rax
;
233 } else if (tok
>= TOK_ASM_eax
&& tok
<= TOK_ASM_edi
) {
234 reg
= tok
- TOK_ASM_eax
;
239 expect("64 bit register");
244 static void parse_operand(TCCState
*s1
, Operand
*op
)
258 if (tok
>= TOK_ASM_al
&& tok
<= TOK_ASM_db7
) {
259 reg
= tok
- TOK_ASM_al
;
260 op
->type
= 1 << (reg
>> 3); /* WARNING: do not change constant order */
262 if ((op
->type
& OP_REG
) && op
->reg
== TREG_RAX
)
264 else if (op
->type
== OP_REG8
&& op
->reg
== TREG_RCX
)
266 else if (op
->type
== OP_REG16
&& op
->reg
== TREG_RDX
)
268 } else if (tok
>= TOK_ASM_dr0
&& tok
<= TOK_ASM_dr7
) {
270 op
->reg
= tok
- TOK_ASM_dr0
;
271 } else if (tok
>= TOK_ASM_es
&& tok
<= TOK_ASM_gs
) {
273 op
->reg
= tok
- TOK_ASM_es
;
274 } else if (tok
== TOK_ASM_st
) {
280 if (tok
!= TOK_PPNUM
)
284 if ((unsigned)reg
>= 8 || p
[1] != '\0')
295 error("unknown register");
299 } else if (tok
== '$') {
307 if (op
->e
.v
== (uint8_t)op
->e
.v
)
309 if (op
->e
.v
== (int8_t)op
->e
.v
)
311 if (op
->e
.v
== (uint16_t)op
->e
.v
)
313 if (op
->e
.v
== (uint32_t)op
->e
.v
)
317 /* address(reg,reg2,shift) with all variants */
333 op
->reg
= asm_parse_reg();
338 op
->reg2
= asm_parse_reg();
342 op
->shift
= get_reg_shift(s1
);
347 if (op
->reg
== -1 && op
->reg2
== -1)
353 /* XXX: unify with C code output ? */
354 static void gen_expr32(ExprValue
*pe
)
357 greloc(cur_text_section
, pe
->sym
, ind
, R_X86_64_32
);
361 static void gen_expr64(ExprValue
*pe
)
364 greloc(cur_text_section
, pe
->sym
, ind
, R_X86_64_64
);
368 /* XXX: unify with C code output ? */
369 static void gen_disp32(ExprValue
*pe
)
374 if (sym
->r
== cur_text_section
->sh_num
) {
375 /* same section: we can output an absolute value. Note
376 that the TCC compiler behaves differently here because
377 it always outputs a relocation to ease (future) code
378 elimination in the linker */
379 gen_le32(pe
->v
+ sym
->jnext
- ind
- 4);
381 greloc(cur_text_section
, sym
, ind
, R_X86_64_PC32
);
385 /* put an empty PC32 relocation */
386 put_elf_reloc(symtab_section
, cur_text_section
,
387 ind
, R_X86_64_PC32
, 0);
393 static void gen_le16(int v
)
399 /* generate the modrm operand */
400 static inline void asm_modrm(int reg
, Operand
*op
)
402 int mod
, reg1
, reg2
, sib_reg1
;
404 if (op
->type
& (OP_REG
| OP_MMX
| OP_SSE
)) {
405 g(0xc0 + (reg
<< 3) + op
->reg
);
406 } else if (op
->reg
== -1 && op
->reg2
== -1) {
407 /* displacement only */
408 g(0x05 + (reg
<< 3));
412 /* fist compute displacement encoding */
413 if (sib_reg1
== -1) {
416 } else if (op
->e
.v
== 0 && !op
->e
.sym
&& op
->reg
!= 5) {
418 } else if (op
->e
.v
== (int8_t)op
->e
.v
&& !op
->e
.sym
) {
423 /* compute if sib byte needed */
427 g(mod
+ (reg
<< 3) + reg1
);
432 reg2
= 4; /* indicate no index */
433 g((op
->shift
<< 6) + (reg2
<< 3) + sib_reg1
);
439 } else if (mod
== 0x80 || op
->reg
== -1) {
445 static void asm_opcode(TCCState
*s1
, int opcode
)
448 int i
, modrm_index
, reg
, v
, op1
, is_short_jmp
, seg_prefix
;
450 Operand ops
[MAX_OPERANDS
], *pop
;
451 int op_type
[3]; /* decoded op type */
460 if (tok
== ';' || tok
== TOK_LINEFEED
)
462 if (nb_ops
>= MAX_OPERANDS
) {
463 error("incorrect number of operands");
465 parse_operand(s1
, pop
);
467 if (pop
->type
!= OP_SEG
|| seg_prefix
) {
468 error("incorrect prefix");
470 seg_prefix
= segment_prefixes
[pop
->reg
];
472 parse_operand(s1
, pop
);
473 if (!(pop
->type
& OP_EA
)) {
474 error("segment prefix must be followed by memory reference");
485 s
= 0; /* avoid warning */
487 /* optimize matching by using a lookup table (no hashing is needed
489 for(pa
= asm_instrs
; pa
->sym
!= 0; pa
++) {
491 if (pa
->instr_type
& OPC_FARITH
) {
492 v
= opcode
- pa
->sym
;
493 if (!((unsigned)v
< 8 * 6 && (v
% 6) == 0))
495 } else if (pa
->instr_type
& OPC_ARITH
) {
496 if (!(opcode
>= pa
->sym
&& opcode
< pa
->sym
+ 8 * 5))
498 s
= (opcode
- pa
->sym
) % 5;
499 } else if (pa
->instr_type
& OPC_SHIFT
) {
500 if (!(opcode
>= pa
->sym
&& opcode
< pa
->sym
+ 7 * 5))
502 s
= (opcode
- pa
->sym
) % 5;
503 } else if (pa
->instr_type
& OPC_TEST
) {
504 if (!(opcode
>= pa
->sym
&& opcode
< pa
->sym
+ NB_TEST_OPCODES
))
506 } else if (pa
->instr_type
& OPC_B
) {
507 if (!(opcode
>= pa
->sym
&& opcode
< pa
->sym
+ 5))
509 s
= opcode
- pa
->sym
;
510 } else if (pa
->instr_type
& OPC_WLQ
) {
511 if (!(opcode
>= pa
->sym
&& opcode
< pa
->sym
+ 4))
513 s
= opcode
- pa
->sym
+ 1;
515 if (pa
->sym
!= opcode
)
518 if (pa
->nb_ops
!= nb_ops
)
520 /* now decode and check each operand */
521 for(i
= 0; i
< nb_ops
; i
++) {
523 op1
= pa
->op_type
[i
];
527 v
= OP_IM8
| OP_IM16
| OP_IM32
| OP_IM64
;
530 v
= OP_REG8
| OP_REG16
| OP_REG32
| OP_REG64
;
533 v
= OP_REG16
| OP_REG32
| OP_REG64
;
536 v
= OP_IM16
| OP_IM32
| OP_IM64
;
539 v
= OP_IM16
| OP_IM32
;
548 if ((ops
[i
].type
& v
) == 0)
551 /* all is matching ! */
556 if (opcode
>= TOK_ASM_clc
&& opcode
<= TOK_ASM_emms
) {
558 b
= op0_codes
[opcode
- TOK_ASM_clc
];
564 error("unknown opcode '%s'",
565 get_tok_str(opcode
, NULL
));
568 /* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */
570 for(i
= 0; s
== 4 && i
< nb_ops
; i
++) {
571 if ((ops
[i
].type
& OP_REG
) && !(op_type
[i
] & (OP_CL
| OP_DX
)))
572 s
= reg_to_size
[ops
[i
].type
& OP_REG
];
575 if ((opcode
== TOK_ASM_push
|| opcode
== TOK_ASM_pop
) &&
576 (ops
[0].type
& (OP_SEG
| OP_IM8S
| OP_IM32
| OP_IM64
)))
579 error("cannot infer opcode suffix");
583 /* generate data16 prefix if needed */
585 if (s
== 1 || (pa
->instr_type
& OPC_D16
))
590 /* generate REX prefix */
591 if ((opcode
== TOK_ASM_push
|| opcode
== TOK_ASM_pop
) &&
592 (ops
[0].type
& OP_REG64
))
598 /* now generates the operation */
599 if (pa
->instr_type
& OPC_FWAIT
)
605 if (v
== 0x69 || v
== 0x69) {
606 /* kludge for imul $im, %reg */
609 } else if (v
== 0xcd && ops
[0].e
.v
== 3 && !ops
[0].e
.sym
) {
610 v
--; /* int $3 case */
612 } else if ((v
== 0x06 || v
== 0x07)) {
613 if (ops
[0].reg
>= 4) {
614 /* push/pop %fs or %gs */
615 v
= 0x0fa0 + (v
- 0x06) + ((ops
[0].reg
- 4) << 3);
617 v
+= ops
[0].reg
<< 3;
620 } else if (v
<= 0x05) {
622 v
+= ((opcode
- TOK_ASM_addb
) / 5) << 3;
623 } else if ((pa
->instr_type
& (OPC_FARITH
| OPC_MODRM
)) == OPC_FARITH
) {
625 v
+= ((opcode
- pa
->sym
) / 6) << 3;
627 if (pa
->instr_type
& OPC_REG
) {
628 for(i
= 0; i
< nb_ops
; i
++) {
629 if (op_type
[i
] & (OP_REG
| OP_ST
)) {
634 /* mov $im, %reg case */
635 if (pa
->opcode
== 0xb0 && s
>= 1)
638 if (pa
->instr_type
& OPC_B
)
640 if (pa
->instr_type
& OPC_TEST
)
641 v
+= test_bits
[opcode
- pa
->sym
];
642 if (pa
->instr_type
& OPC_SHORTJMP
) {
646 /* see if we can really generate the jump with a byte offset */
650 if (sym
->r
!= cur_text_section
->sh_num
)
652 jmp_disp
= ops
[0].e
.v
+ sym
->jnext
- ind
- 2;
653 if (jmp_disp
== (int8_t)jmp_disp
) {
654 /* OK to generate jump */
656 ops
[0].e
.v
= jmp_disp
;
659 if (pa
->instr_type
& OPC_JMP
) {
660 /* long jump will be allowed. need to modify the
667 error("invalid displacement");
676 /* search which operand will used for modrm */
678 if (pa
->instr_type
& OPC_SHIFT
) {
679 reg
= (opcode
- pa
->sym
) / 5;
682 } else if (pa
->instr_type
& OPC_ARITH
) {
683 reg
= (opcode
- pa
->sym
) / 5;
684 } else if (pa
->instr_type
& OPC_FARITH
) {
685 reg
= (opcode
- pa
->sym
) / 6;
687 reg
= (pa
->instr_type
>> OPC_GROUP_SHIFT
) & 7;
689 if (pa
->instr_type
& OPC_MODRM
) {
690 /* first look for an ea operand */
691 for(i
= 0;i
< nb_ops
; i
++) {
692 if (op_type
[i
] & OP_EA
)
695 /* then if not found, a register or indirection (shift instructions) */
696 for(i
= 0;i
< nb_ops
; i
++) {
697 if (op_type
[i
] & (OP_REG
| OP_MMX
| OP_SSE
| OP_INDIR
))
701 error("bad op table");
705 /* if a register is used in another operand then it is
706 used instead of group */
707 for(i
= 0;i
< nb_ops
; i
++) {
709 if (i
!= modrm_index
&&
710 (v
& (OP_REG
| OP_MMX
| OP_SSE
| OP_CR
| OP_TR
| OP_DB
| OP_SEG
))) {
716 asm_modrm(reg
, &ops
[modrm_index
]);
721 for(i
= 0;i
< nb_ops
; i
++) {
723 if (v
& (OP_IM8
| OP_IM16
| OP_IM32
| OP_IM64
| OP_IM8S
| OP_ADDR
)) {
724 /* if multiple sizes are given it means we must look
726 if (v
== (OP_IM8
| OP_IM16
| OP_IM32
| OP_IM64
) ||
727 v
== (OP_IM16
| OP_IM32
| OP_IM64
)) {
736 } else if (v
== (OP_IM8
| OP_IM16
| OP_IM32
) ||
737 v
== (OP_IM16
| OP_IM32
)) {
745 if (v
& (OP_IM8
| OP_IM8S
)) {
749 } else if (v
& OP_IM16
) {
752 error("cannot relocate");
754 gen_le16(ops
[i
].e
.v
);
756 if (pa
->instr_type
& (OPC_JMP
| OPC_SHORTJMP
)) {
760 gen_disp32(&ops
[i
].e
);
763 gen_expr64(&ops
[i
].e
);
765 gen_expr32(&ops
[i
].e
);
768 } else if (v
& (OP_REG32
| OP_REG64
)) {
769 if (pa
->instr_type
& (OPC_JMP
| OPC_SHORTJMP
)) {
771 g(0xE0 + ops
[i
].reg
);
778 #define NB_SAVED_REGS 3
779 #define NB_ASM_REGS 8
781 /* return the constraint priority (we allocate first the lowest
782 numbered constraints) */
783 static inline int constraint_priority(const char *str
)
787 /* we take the lowest priority */
821 error("unknown constraint '%c'", c
);
830 static const char *skip_constraint_modifiers(const char *p
)
832 while (*p
== '=' || *p
== '&' || *p
== '+' || *p
== '%')
837 #define REG_OUT_MASK 0x01
838 #define REG_IN_MASK 0x02
840 #define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
842 static void asm_compute_constraints(ASMOperand
*operands
,
843 int nb_operands
, int nb_outputs
,
844 const uint8_t *clobber_regs
,
848 int sorted_op
[MAX_ASM_OPERANDS
];
849 int i
, j
, k
, p1
, p2
, tmp
, reg
, c
, reg_mask
;
851 uint8_t regs_allocated
[NB_ASM_REGS
];
854 for(i
=0;i
<nb_operands
;i
++) {
856 op
->input_index
= -1;
862 /* compute constraint priority and evaluate references to output
863 constraints if input constraints */
864 for(i
=0;i
<nb_operands
;i
++) {
866 str
= op
->constraint
;
867 str
= skip_constraint_modifiers(str
);
868 if (isnum(*str
) || *str
== '[') {
869 /* this is a reference to another constraint */
870 k
= find_constraint(operands
, nb_operands
, str
, NULL
);
871 if ((unsigned)k
>= i
|| i
< nb_outputs
)
872 error("invalid reference in constraint %d ('%s')",
875 if (operands
[k
].input_index
>= 0)
876 error("cannot reference twice the same operand");
877 operands
[k
].input_index
= i
;
880 op
->priority
= constraint_priority(str
);
884 /* sort operands according to their priority */
885 for(i
=0;i
<nb_operands
;i
++)
887 for(i
=0;i
<nb_operands
- 1;i
++) {
888 for(j
=i
+1;j
<nb_operands
;j
++) {
889 p1
= operands
[sorted_op
[i
]].priority
;
890 p2
= operands
[sorted_op
[j
]].priority
;
893 sorted_op
[i
] = sorted_op
[j
];
899 for(i
= 0;i
< NB_ASM_REGS
; i
++) {
901 regs_allocated
[i
] = REG_IN_MASK
| REG_OUT_MASK
;
903 regs_allocated
[i
] = 0;
905 /* esp cannot be used */
906 regs_allocated
[4] = REG_IN_MASK
| REG_OUT_MASK
;
907 /* ebp cannot be used yet */
908 regs_allocated
[5] = REG_IN_MASK
| REG_OUT_MASK
;
910 /* allocate registers and generate corresponding asm moves */
911 for(i
=0;i
<nb_operands
;i
++) {
914 str
= op
->constraint
;
915 /* no need to allocate references */
916 if (op
->ref_index
>= 0)
918 /* select if register is used for output, input or both */
919 if (op
->input_index
>= 0) {
920 reg_mask
= REG_IN_MASK
| REG_OUT_MASK
;
921 } else if (j
< nb_outputs
) {
922 reg_mask
= REG_OUT_MASK
;
924 reg_mask
= REG_IN_MASK
;
936 error("'%c' modifier can only be applied to outputs", c
);
937 reg_mask
= REG_IN_MASK
| REG_OUT_MASK
;
940 /* allocate both eax and edx */
941 if (is_reg_allocated(TREG_RAX
) ||
942 is_reg_allocated(TREG_RDX
))
946 regs_allocated
[TREG_RAX
] |= reg_mask
;
947 regs_allocated
[TREG_RDX
] |= reg_mask
;
967 if (is_reg_allocated(reg
))
971 /* eax, ebx, ecx or edx */
972 for(reg
= 0; reg
< 4; reg
++) {
973 if (!is_reg_allocated(reg
))
978 /* any general register */
979 for(reg
= 0; reg
< 8; reg
++) {
980 if (!is_reg_allocated(reg
))
985 /* now we can reload in the register */
988 regs_allocated
[reg
] |= reg_mask
;
991 if (!((op
->vt
->r
& (VT_VALMASK
| VT_LVAL
)) == VT_CONST
))
997 if (!((op
->vt
->r
& (VT_VALMASK
| VT_LVAL
| VT_SYM
)) == VT_CONST
))
1002 /* nothing special to do because the operand is already in
1003 memory, except if the pointer itself is stored in a
1004 memory variable (VT_LLOCAL case) */
1005 /* XXX: fix constant case */
1006 /* if it is a reference to a memory zone, it must lie
1007 in a register, so we reserve the register in the
1008 input registers and a load will be generated
1010 if (j
< nb_outputs
|| c
== 'm') {
1011 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
) {
1012 /* any general register */
1013 for(reg
= 0; reg
< 8; reg
++) {
1014 if (!(regs_allocated
[reg
] & REG_IN_MASK
))
1019 /* now we can reload in the register */
1020 regs_allocated
[reg
] |= REG_IN_MASK
;
1027 error("asm constraint %d ('%s') could not be satisfied",
1031 /* if a reference is present for that operand, we assign it too */
1032 if (op
->input_index
>= 0) {
1033 operands
[op
->input_index
].reg
= op
->reg
;
1034 operands
[op
->input_index
].is_llong
= op
->is_llong
;
1038 /* compute out_reg. It is used to store outputs registers to memory
1039 locations references by pointers (VT_LLOCAL case) */
1041 for(i
=0;i
<nb_operands
;i
++) {
1044 (op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&&
1046 for(reg
= 0; reg
< 8; reg
++) {
1047 if (!(regs_allocated
[reg
] & REG_OUT_MASK
))
1050 error("could not find free output register for reloading");
1057 /* print sorted constraints */
1059 for(i
=0;i
<nb_operands
;i
++) {
1062 printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
1064 op
->id
? get_tok_str(op
->id
, NULL
) : "",
1070 printf("out_reg=%d\n", *pout_reg
);
1074 static void subst_asm_operand(CString
*add_str
,
1075 SValue
*sv
, int modifier
)
1077 int r
, reg
, size
, val
;
1081 if ((r
& VT_VALMASK
) == VT_CONST
) {
1082 if (!(r
& VT_LVAL
) && modifier
!= 'c' && modifier
!= 'n')
1083 cstr_ccat(add_str
, '$');
1085 cstr_cat(add_str
, get_tok_str(sv
->sym
->v
, NULL
));
1087 cstr_ccat(add_str
, '+');
1093 if (modifier
== 'n')
1095 snprintf(buf
, sizeof(buf
), "%d", sv
->c
.i
);
1096 cstr_cat(add_str
, buf
);
1097 } else if ((r
& VT_VALMASK
) == VT_LOCAL
) {
1098 snprintf(buf
, sizeof(buf
), "%d(%%ebp)", sv
->c
.i
);
1099 cstr_cat(add_str
, buf
);
1100 } else if (r
& VT_LVAL
) {
1101 reg
= r
& VT_VALMASK
;
1102 if (reg
>= VT_CONST
)
1103 error("internal compiler error");
1104 snprintf(buf
, sizeof(buf
), "(%%%s)",
1105 get_tok_str(TOK_ASM_eax
+ reg
, NULL
));
1106 cstr_cat(add_str
, buf
);
1109 reg
= r
& VT_VALMASK
;
1110 if (reg
>= VT_CONST
)
1111 error("internal compiler error");
1113 /* choose register operand size */
1114 if ((sv
->type
.t
& VT_BTYPE
) == VT_BYTE
)
1116 else if ((sv
->type
.t
& VT_BTYPE
) == VT_SHORT
)
1118 else if ((sv
->type
.t
& VT_BTYPE
) == VT_LLONG
)
1122 if (size
== 1 && reg
>= 4)
1125 if (modifier
== 'b') {
1127 error("cannot use byte register");
1129 } else if (modifier
== 'h') {
1131 error("cannot use byte register");
1133 } else if (modifier
== 'w') {
1135 } else if (modifier
== 'q') {
1141 reg
= TOK_ASM_ah
+ reg
;
1144 reg
= TOK_ASM_al
+ reg
;
1147 reg
= TOK_ASM_ax
+ reg
;
1150 reg
= TOK_ASM_eax
+ reg
;
1153 reg
= TOK_ASM_rax
+ reg
;
1156 snprintf(buf
, sizeof(buf
), "%%%s", get_tok_str(reg
, NULL
));
1157 cstr_cat(add_str
, buf
);
1161 /* generate prolog and epilog code for asm statment */
1162 static void asm_gen_code(ASMOperand
*operands
, int nb_operands
,
1163 int nb_outputs
, int is_output
,
1164 uint8_t *clobber_regs
,
1167 uint8_t regs_allocated
[NB_ASM_REGS
];
1170 static uint8_t reg_saved
[NB_SAVED_REGS
] = { 3, 6, 7 };
1172 /* mark all used registers */
1173 memcpy(regs_allocated
, clobber_regs
, sizeof(regs_allocated
));
1174 for(i
= 0; i
< nb_operands
;i
++) {
1177 regs_allocated
[op
->reg
] = 1;
1180 /* generate reg save code */
1181 for(i
= 0; i
< NB_SAVED_REGS
; i
++) {
1183 if (regs_allocated
[reg
])
1187 /* generate load code */
1188 for(i
= 0; i
< nb_operands
; i
++) {
1191 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&&
1193 /* memory reference case (for both input and
1197 sv
.r
= (sv
.r
& ~VT_VALMASK
) | VT_LOCAL
;
1199 } else if (i
>= nb_outputs
|| op
->is_rw
) {
1200 /* load value in register */
1201 load(op
->reg
, op
->vt
);
1206 load(TREG_RDX
, &sv
);
1212 /* generate save code */
1213 for(i
= 0 ; i
< nb_outputs
; i
++) {
1216 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
) {
1217 if (!op
->is_memory
) {
1220 sv
.r
= (sv
.r
& ~VT_VALMASK
) | VT_LOCAL
;
1223 sv
.r
= (sv
.r
& ~VT_VALMASK
) | out_reg
;
1224 store(op
->reg
, &sv
);
1227 store(op
->reg
, op
->vt
);
1232 store(TREG_RDX
, &sv
);
1237 /* generate reg restore code */
1238 for(i
= NB_SAVED_REGS
- 1; i
>= 0; i
--) {
1240 if (regs_allocated
[reg
])
1246 static void asm_clobber(uint8_t *clobber_regs
, const char *str
)
1251 if (!strcmp(str
, "memory") ||
1254 ts
= tok_alloc(str
, strlen(str
));
1256 if (reg
>= TOK_ASM_rax
&& reg
<= TOK_ASM_rdi
) {
1258 } else if (reg
>= TOK_ASM_eax
&& reg
<= TOK_ASM_edi
) {
1260 } else if (reg
>= TOK_ASM_ax
&& reg
<= TOK_ASM_di
) {
1263 error("invalid clobber register '%s'", str
);
1265 clobber_regs
[reg
] = 1;