2 * ARM specific functions for TCC assembler
4 * Copyright (c) 2001, 2002 Fabrice Bellard
5 * Copyright (c) 2020 Danny Milosavljevic
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #ifdef TARGET_DEFS_ONLY
24 #define CONFIG_TCC_ASM
25 #define NB_ASM_REGS 16
27 ST_FUNC
void g(int c
);
28 ST_FUNC
void gen_le16(int c
);
29 ST_FUNC
void gen_le32(int c
);
31 /*************************************************************/
33 /*************************************************************/
47 #define OP_REG32 (1 << OPT_REG32)
48 #define OP_VREG32 (1 << OPT_VREG32)
49 #define OP_VREG64 (1 << OPT_VREG64)
50 #define OP_REG (OP_REG32 | OP_VREG32 | OP_VREG64)
51 #define OP_IM32 (1 << OPT_IM32)
52 #define OP_IM8 (1 << OPT_IM8)
53 #define OP_IM8N (1 << OPT_IM8N)
54 #define OP_REGSET32 (1 << OPT_REGSET32)
56 typedef struct Operand
{
65 /* Read the VFP register referred to by token T.
66 If OK, returns its number.
67 If not OK, returns -1. */
68 static int asm_parse_vfp_regvar(int t
, int double_precision
)
70 if (double_precision
) {
71 if (t
>= TOK_ASM_d0
&& t
<= TOK_ASM_d15
)
72 return t
- TOK_ASM_d0
;
74 if (t
>= TOK_ASM_s0
&& t
<= TOK_ASM_s31
)
75 return t
- TOK_ASM_s0
;
80 __attribute__((unused
))
81 static int asm_parse_vfp_status_regvar(int t
)
95 /* Parse a text containing operand and store the result in OP */
96 static void parse_operand(TCCState
*s1
, Operand
*op
)
104 if (tok
== '{') { // regset literal
106 while (tok
!= '}' && tok
!= TOK_EOF
) {
107 reg
= asm_parse_regvar(tok
);
112 next(); // skip register name
114 if ((1 << reg
) < regset
)
115 tcc_warning("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
125 // ARM instructions don't support empty regset.
126 tcc_error("empty register list is not supported");
128 op
->type
= OP_REGSET32
;
132 } else if ((reg
= asm_parse_regvar(tok
)) != -1) {
133 next(); // skip register name
135 op
->reg
= (uint8_t) reg
;
137 } else if ((reg
= asm_parse_vfp_regvar(tok
, 0)) != -1) {
138 next(); // skip register name
139 op
->type
= OP_VREG32
;
140 op
->reg
= (uint8_t) reg
;
142 } else if ((reg
= asm_parse_vfp_regvar(tok
, 1)) != -1) {
143 next(); // skip register name
144 op
->type
= OP_VREG64
;
145 op
->reg
= (uint8_t) reg
;
147 } else if (tok
== '#' || tok
== '$') {
149 next(); // skip '#' or '$'
155 if ((int) op
->e
.v
< 0 && (int) op
->e
.v
>= -255)
157 else if (op
->e
.v
== (uint8_t)op
->e
.v
)
163 /* XXX: make it faster ? */
164 ST_FUNC
void g(int c
)
170 if (ind1
> cur_text_section
->data_allocated
)
171 section_realloc(cur_text_section
, ind1
);
172 cur_text_section
->data
[ind
] = c
;
176 ST_FUNC
void gen_le16 (int i
)
182 ST_FUNC
void gen_le32 (int i
)
188 if (ind1
> cur_text_section
->data_allocated
)
189 section_realloc(cur_text_section
, ind1
);
190 cur_text_section
->data
[ind
++] = i
& 0xFF;
191 cur_text_section
->data
[ind
++] = (i
>> 8) & 0xFF;
192 cur_text_section
->data
[ind
++] = (i
>> 16) & 0xFF;
193 cur_text_section
->data
[ind
++] = (i
>> 24) & 0xFF;
196 ST_FUNC
void gen_expr32(ExprValue
*pe
)
201 static uint32_t condition_code_of_token(int token
) {
202 if (token
< TOK_ASM_nopeq
) {
203 expect("condition-enabled instruction");
206 return (token
- TOK_ASM_nopeq
) & 15;
209 static void asm_emit_opcode(int token
, uint32_t opcode
) {
210 gen_le32((condition_code_of_token(token
) << 28) | opcode
);
213 static void asm_emit_unconditional_opcode(uint32_t opcode
) {
217 static void asm_emit_coprocessor_opcode(uint32_t high_nibble
, uint8_t cp_number
, uint8_t cp_opcode
, uint8_t cp_destination_register
, uint8_t cp_n_operand_register
, uint8_t cp_m_operand_register
, uint8_t cp_opcode2
, int inter_processor_transfer
)
219 uint32_t opcode
= 0xe000000;
220 if (inter_processor_transfer
)
222 //assert(cp_opcode < 16);
223 opcode
|= cp_opcode
<< 20;
224 //assert(cp_n_operand_register < 16);
225 opcode
|= cp_n_operand_register
<< 16;
226 //assert(cp_destination_register < 16);
227 opcode
|= cp_destination_register
<< 12;
228 //assert(cp_number < 16);
229 opcode
|= cp_number
<< 8;
230 //assert(cp_information < 8);
231 opcode
|= cp_opcode2
<< 5;
232 //assert(cp_m_operand_register < 16);
233 opcode
|= cp_m_operand_register
;
234 asm_emit_unconditional_opcode((high_nibble
<< 28) | opcode
);
237 static void asm_nullary_opcode(int token
)
239 switch (ARM_INSTRUCTION_GROUP(token
)) {
241 asm_emit_opcode(token
, 0xd << 21); // mov r0, r0
244 asm_emit_opcode(token
, 0x320f002);
246 asm_emit_opcode(token
, 0x320f003);
249 expect("nullary instruction");
253 static void asm_unary_opcode(TCCState
*s1
, int token
)
256 parse_operand(s1
, &op
);
258 switch (ARM_INSTRUCTION_GROUP(token
)) {
261 if (op
.type
!= OP_IM8
)
262 expect("immediate 8-bit unsigned integer");
264 /* Note: Dummy operand (ignored by processor): ARM ref documented 0...255, ARM instruction set documented 24 bit */
265 asm_emit_opcode(token
, (0xf << 24) | op
.e
.v
);
269 expect("unary instruction");
273 static void asm_binary_opcode(TCCState
*s1
, int token
)
277 uint32_t encoded_rotation
= 0;
279 parse_operand(s1
, &ops
[0]);
284 parse_operand(s1
, &ops
[1]);
285 if (ops
[0].type
!= OP_REG32
) {
286 expect("(destination operand) register");
290 if (ops
[0].reg
== 15) {
291 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
295 if (ops
[0].reg
== 13)
296 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token
, NULL
));
298 if (ops
[1].type
!= OP_REG32
) {
299 switch (ARM_INSTRUCTION_GROUP(token
)) {
302 if (ops
[1].type
== OP_IM8
|| ops
[1].type
== OP_IM8N
|| ops
[1].type
== OP_IM32
) {
303 if (ops
[1].e
.v
>= 0 && ops
[1].e
.v
<= 0xFFFF) {
304 uint16_t immediate_value
= ops
[1].e
.v
;
305 switch (ARM_INSTRUCTION_GROUP(token
)) {
307 asm_emit_opcode(token
, 0x3400000 | (ops
[0].reg
<< 12) | (immediate_value
& 0xF000) << 4 | (immediate_value
& 0xFFF));
310 asm_emit_opcode(token
, 0x3000000 | (ops
[0].reg
<< 12) | (immediate_value
& 0xF000) << 4 | (immediate_value
& 0xFFF));
314 expect("(source operand) immediate 16 bit value");
316 expect("(source operand) immediate");
319 expect("(source operand) register");
324 if (ops
[1].reg
== 15) {
325 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
329 if (ops
[1].reg
== 13)
330 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token
, NULL
));
334 if (tok
== TOK_ASM_ror
) {
335 next(); // skip 'ror'
336 parse_operand(s1
, &rotation
);
337 if (rotation
.type
!= OP_IM8
) {
338 expect("immediate value for rotation");
341 amount
= rotation
.e
.v
;
344 encoded_rotation
= 1 << 10;
347 encoded_rotation
= 2 << 10;
350 encoded_rotation
= 3 << 10;
353 expect("'8' or '16' or '24'");
359 switch (ARM_INSTRUCTION_GROUP(token
)) {
361 if (encoded_rotation
)
362 tcc_error("clz does not support rotation");
363 asm_emit_opcode(token
, 0x16f0f10 | (ops
[0].reg
<< 12) | ops
[1].reg
);
366 asm_emit_opcode(token
, 0x6af0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
369 asm_emit_opcode(token
, 0x6bf0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
372 asm_emit_opcode(token
, 0x6ef0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
375 asm_emit_opcode(token
, 0x6ff0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
378 expect("binary instruction");
382 static void asm_coprocessor_opcode(TCCState
*s1
, int token
) {
386 uint8_t registers
[3];
391 if (tok
>= TOK_ASM_p0
&& tok
<= TOK_ASM_p15
) {
392 coprocessor
= tok
- TOK_ASM_p0
;
395 expect("'p<number>'");
404 parse_operand(s1
, &opcode1
);
405 if (opcode1
.type
!= OP_IM8
|| opcode1
.e
.v
> 15) {
406 tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token
, NULL
));
410 for (i
= 0; i
< 3; ++i
) {
415 if (i
== 0 && token
!= TOK_ASM_cdp2
&& (ARM_INSTRUCTION_GROUP(token
) == TOK_ASM_mrceq
|| ARM_INSTRUCTION_GROUP(token
) == TOK_ASM_mcreq
)) {
416 if (tok
>= TOK_ASM_r0
&& tok
<= TOK_ASM_r15
) {
417 registers
[i
] = tok
- TOK_ASM_r0
;
420 expect("'r<number>'");
424 if (tok
>= TOK_ASM_c0
&& tok
<= TOK_ASM_c15
) {
425 registers
[i
] = tok
- TOK_ASM_c0
;
428 expect("'c<number>'");
435 parse_operand(s1
, &opcode2
);
437 opcode2
.type
= OP_IM8
;
440 if (opcode2
.type
!= OP_IM8
|| opcode2
.e
.v
> 15) {
441 tcc_error("opcode2 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token
, NULL
));
445 if (token
== TOK_ASM_cdp2
) {
447 asm_emit_coprocessor_opcode(high_nibble
, coprocessor
, opcode1
.e
.v
, registers
[0], registers
[1], registers
[2], opcode2
.e
.v
, 0);
450 high_nibble
= condition_code_of_token(token
);
452 switch (ARM_INSTRUCTION_GROUP(token
)) {
454 asm_emit_coprocessor_opcode(high_nibble
, coprocessor
, opcode1
.e
.v
, registers
[0], registers
[1], registers
[2], opcode2
.e
.v
, 0);
457 // opcode1 encoding changes! highest and lowest bit gone.
461 // opcode1 encoding changes! highest and lowest bit gone.
462 if (opcode1
.e
.v
> 7) {
463 tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 7", get_tok_str(token
, NULL
));
466 asm_emit_coprocessor_opcode(high_nibble
, coprocessor
, (opcode1
.e
.v
<< 1) | mrc
, registers
[0], registers
[1], registers
[2], opcode2
.e
.v
, 1);
469 expect("known instruction");
473 /* data processing and single data transfer instructions only */
474 #define ENCODE_RN(register_index) ((register_index) << 16)
475 #define ENCODE_RD(register_index) ((register_index) << 12)
476 #define ENCODE_SET_CONDITION_CODES (1 << 20)
478 /* Note: For data processing instructions, "1" means immediate.
479 Note: For single data transfer instructions, "0" means immediate. */
480 #define ENCODE_IMMEDIATE_FLAG (1 << 25)
482 #define ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER (1 << 4)
483 #define ENCODE_BARREL_SHIFTER_MODE_LSL (0 << 5)
484 #define ENCODE_BARREL_SHIFTER_MODE_LSR (1 << 5)
485 #define ENCODE_BARREL_SHIFTER_MODE_ASR (2 << 5)
486 #define ENCODE_BARREL_SHIFTER_MODE_ROR (3 << 5)
487 #define ENCODE_BARREL_SHIFTER_REGISTER(register_index) ((register_index) << 8)
488 #define ENCODE_BARREL_SHIFTER_IMMEDIATE(value) ((value) << 7)
490 static void asm_block_data_transfer_opcode(TCCState
*s1
, int token
)
496 parse_operand(s1
, &ops
[0]);
502 next(); // skip comma
503 parse_operand(s1
, &ops
[1]);
507 expect("at least one operand");
509 } else if (ops
[nb_ops
- 1].type
!= OP_REGSET32
) {
510 expect("(last operand) register list");
514 // block data transfer: 1 0 0 P U S W L << 20 (general case):
516 // Rn: bits 19...16 base register
517 // Register List: bits 15...0
519 switch (ARM_INSTRUCTION_GROUP(token
)) {
520 case TOK_ASM_pusheq
: // TODO: Optimize 1-register case to: str ?, [sp, #-4]!
521 // Instruction: 1 I=0 P=1 U=0 S=0 W=1 L=0 << 20, op 1101
524 // Register List: bits 15...0
526 expect("exactly one operand");
528 asm_emit_opcode(token
, (0x92d << 16) | ops
[0].regset
); // TODO: base register ?
530 case TOK_ASM_popeq
: // TODO: Optimize 1-register case to: ldr ?, [sp], #4
531 // Instruction: 1 I=0 P=0 U=1 S=0 W=0 L=1 << 20, op 1101
534 // Register List: bits 15...0
536 expect("exactly one operand");
538 asm_emit_opcode(token
, (0x8bd << 16) | ops
[0].regset
); // TODO: base register ?
540 case TOK_ASM_stmdaeq
:
541 case TOK_ASM_ldmdaeq
:
544 case TOK_ASM_stmiaeq
:
545 case TOK_ASM_ldmiaeq
:
546 case TOK_ASM_stmdbeq
:
547 case TOK_ASM_ldmdbeq
:
548 case TOK_ASM_stmibeq
:
549 case TOK_ASM_ldmibeq
:
550 switch (ARM_INSTRUCTION_GROUP(token
)) {
551 case TOK_ASM_stmdaeq
: // post-decrement store
554 case TOK_ASM_ldmdaeq
: // post-decrement load
557 case TOK_ASM_stmeq
: // post-increment store
558 case TOK_ASM_stmiaeq
: // post-increment store
561 case TOK_ASM_ldmeq
: // post-increment load
562 case TOK_ASM_ldmiaeq
: // post-increment load
565 case TOK_ASM_stmdbeq
: // pre-decrement store
568 case TOK_ASM_ldmdbeq
: // pre-decrement load
571 case TOK_ASM_stmibeq
: // pre-increment store
574 case TOK_ASM_ldmibeq
: // pre-increment load
578 tcc_error("internal error: This place should not be reached (fallback in asm_block_data_transfer_opcode)");
582 // Register List: lower bits
584 expect("exactly two operands");
585 else if (ops
[0].type
!= OP_REG32
)
586 expect("(first operand) register");
589 opcode
|= 1 << 21; // writeback
590 asm_emit_opcode(token
, opcode
| ENCODE_RN(ops
[0].reg
) | ops
[1].regset
);
594 expect("block data transfer instruction");
598 /* Parses shift directive and returns the parts that would have to be set in the opcode because of it.
599 Does not encode the actual shift amount.
600 It's not an error if there is no shift directive.
602 NB_SHIFT: will be set to 1 iff SHIFT is filled. Note that for rrx, there's no need to fill SHIFT.
603 SHIFT: will be filled in with the shift operand to use, if any. */
604 static uint32_t asm_parse_optional_shift(TCCState
* s1
, int* nb_shift
, Operand
* shift
)
618 opcode
= ENCODE_BARREL_SHIFTER_MODE_LSL
;
621 opcode
= ENCODE_BARREL_SHIFTER_MODE_ASR
;
624 opcode
= ENCODE_BARREL_SHIFTER_MODE_LSR
;
627 opcode
= ENCODE_BARREL_SHIFTER_MODE_ROR
;
631 parse_operand(s1
, shift
);
636 opcode
= ENCODE_BARREL_SHIFTER_MODE_ROR
;
642 static uint32_t asm_encode_shift(Operand
* shift
)
645 uint32_t operands
= 0;
646 switch (shift
->type
) {
648 if (shift
->reg
== 15)
649 tcc_error("r15 cannot be used as a shift count");
651 operands
= ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER
;
652 operands
|= ENCODE_BARREL_SHIFTER_REGISTER(shift
->reg
);
657 if (amount
> 0 && amount
< 32)
658 operands
= ENCODE_BARREL_SHIFTER_IMMEDIATE(amount
);
660 tcc_error("shift count out of range");
663 tcc_error("unknown shift amount");
668 static void asm_data_processing_opcode(TCCState
*s1
, int token
)
674 uint32_t operands
= 0;
676 /* modulo 16 entries per instruction for the different condition codes */
677 uint32_t opcode_idx
= (ARM_INSTRUCTION_GROUP(token
) - TOK_ASM_andeq
) >> 4;
678 uint32_t opcode_nos
= opcode_idx
>> 1; // without "s"; "OpCode" in ARM docs
680 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ) {
681 if (tok
== TOK_ASM_asl
|| tok
== TOK_ASM_lsl
|| tok
== TOK_ASM_lsr
|| tok
== TOK_ASM_asr
|| tok
== TOK_ASM_ror
|| tok
== TOK_ASM_rrx
)
683 parse_operand(s1
, &ops
[nb_ops
]);
691 operands
|= asm_parse_optional_shift(s1
, &nb_shift
, &shift
);
693 expect("at least two operands");
694 else if (nb_ops
== 2) {
695 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
696 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
698 } else if (nb_ops
== 3) {
699 if (opcode_nos
== 0xd || opcode_nos
== 0xf || opcode_nos
== 0xa || opcode_nos
== 0xb || opcode_nos
== 0x8 || opcode_nos
== 0x9) { // mov, mvn, cmp, cmn, tst, teq
700 tcc_error("'%s' cannot be used with three operands", get_tok_str(token
, NULL
));
705 expect("two or three operands");
709 uint32_t immediate_value
;
710 uint8_t half_immediate_rotation
;
711 if (nb_shift
&& shift
.type
== OP_REG32
) {
712 if ((ops
[0].type
== OP_REG32
&& ops
[0].reg
== 15) ||
713 (ops
[1].type
== OP_REG32
&& ops
[1].reg
== 15)) {
714 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
719 // data processing (general case):
721 // Rn: bits 19...16 (first operand)
722 // Rd: bits 15...12 (destination)
723 // Operand2: bits 11...0 (second operand); depending on I that's either a register or an immediate
725 // bits 24...21: "OpCode"--see below
727 /* operations in the token list are ordered by opcode */
728 opcode
= opcode_nos
<< 21; // drop "s"
729 if (ops
[0].type
!= OP_REG32
)
730 expect("(destination operand) register");
731 else if (opcode_nos
== 0xa || opcode_nos
== 0xb || opcode_nos
== 0x8 || opcode_nos
== 0x9) // cmp, cmn, tst, teq
732 operands
|= ENCODE_SET_CONDITION_CODES
; // force S set, otherwise it's a completely different instruction.
734 operands
|= ENCODE_RD(ops
[0].reg
);
735 if (ops
[1].type
!= OP_REG32
)
736 expect("(first source operand) register");
737 else if (!(opcode_nos
== 0xd || opcode_nos
== 0xf)) // not: mov, mvn (those have only one source operand)
738 operands
|= ENCODE_RN(ops
[1].reg
);
739 switch (ops
[2].type
) {
741 operands
|= ops
[2].reg
;
745 operands
|= ENCODE_IMMEDIATE_FLAG
;
746 immediate_value
= ops
[2].e
.v
;
747 for (half_immediate_rotation
= 0; half_immediate_rotation
< 16; ++half_immediate_rotation
) {
748 if (immediate_value
>= 0x00 && immediate_value
< 0x100)
750 // rotate left by two
751 immediate_value
= ((immediate_value
& 0x3FFFFFFF) << 2) | ((immediate_value
& 0xC0000000) >> 30);
753 if (half_immediate_rotation
>= 16) {
756 operands
|= immediate_value
;
757 operands
|= half_immediate_rotation
<< 8;
760 case OP_IM8N
: // immediate negative value
761 operands
|= ENCODE_IMMEDIATE_FLAG
;
762 immediate_value
= ops
[2].e
.v
;
763 /* Instruction swapping:
764 0001 = EOR - Rd:= Op1 EOR Op2 -> difficult
765 0011 = RSB - Rd:= Op2 - Op1 -> difficult
766 0111 = RSC - Rd:= Op2 - Op1 + C -> difficult
767 1000 = TST - CC on: Op1 AND Op2 -> difficult
768 1001 = TEQ - CC on: Op1 EOR Op2 -> difficult
769 1100 = ORR - Rd:= Op1 OR Op2 -> difficult
771 switch (opcode_nos
) {
772 case 0x0: // AND - Rd:= Op1 AND Op2
773 opcode
= 0xe << 21; // BIC
774 immediate_value
= ~immediate_value
;
776 case 0x2: // SUB - Rd:= Op1 - Op2
777 opcode
= 0x4 << 21; // ADD
778 immediate_value
= -immediate_value
;
780 case 0x4: // ADD - Rd:= Op1 + Op2
781 opcode
= 0x2 << 21; // SUB
782 immediate_value
= -immediate_value
;
784 case 0x5: // ADC - Rd:= Op1 + Op2 + C
785 opcode
= 0x6 << 21; // SBC
786 immediate_value
= ~immediate_value
;
788 case 0x6: // SBC - Rd:= Op1 - Op2 + C
789 opcode
= 0x5 << 21; // ADC
790 immediate_value
= ~immediate_value
;
792 case 0xa: // CMP - CC on: Op1 - Op2
793 opcode
= 0xb << 21; // CMN
794 immediate_value
= -immediate_value
;
796 case 0xb: // CMN - CC on: Op1 + Op2
797 opcode
= 0xa << 21; // CMP
798 immediate_value
= -immediate_value
;
800 case 0xd: // MOV - Rd:= Op2
801 opcode
= 0xf << 21; // MVN
802 immediate_value
= ~immediate_value
;
804 case 0xe: // BIC - Rd:= Op1 AND NOT Op2
805 opcode
= 0x0 << 21; // AND
806 immediate_value
= ~immediate_value
;
808 case 0xf: // MVN - Rd:= NOT Op2
809 opcode
= 0xd << 21; // MOV
810 immediate_value
= ~immediate_value
;
813 tcc_error("cannot use '%s' with a negative immediate value", get_tok_str(token
, NULL
));
815 for (half_immediate_rotation
= 0; half_immediate_rotation
< 16; ++half_immediate_rotation
) {
816 if (immediate_value
>= 0x00 && immediate_value
< 0x100)
818 // rotate left by two
819 immediate_value
= ((immediate_value
& 0x3FFFFFFF) << 2) | ((immediate_value
& 0xC0000000) >> 30);
821 if (half_immediate_rotation
>= 16) {
822 immediate_value
= ops
[2].e
.v
;
823 tcc_error("immediate value 0x%X cannot be encoded into ARM immediate", (unsigned) immediate_value
);
826 operands
|= immediate_value
;
827 operands
|= half_immediate_rotation
<< 8;
830 expect("(second source operand) register or immediate value");
834 if (operands
& ENCODE_IMMEDIATE_FLAG
)
835 tcc_error("immediate rotation not implemented");
837 operands
|= asm_encode_shift(&shift
);
840 /* S=0 and S=1 entries alternate one after another, in that order */
841 opcode
|= (opcode_idx
& 1) ? ENCODE_SET_CONDITION_CODES
: 0;
842 asm_emit_opcode(token
, opcode
| operands
);
846 static void asm_shift_opcode(TCCState
*s1
, int token
)
850 int definitely_neutral
= 0;
851 uint32_t opcode
= 0xd << 21; // MOV
852 uint32_t operands
= 0;
854 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
855 parse_operand(s1
, &ops
[nb_ops
]);
863 expect("at least two operands");
867 if (ops
[0].type
!= OP_REG32
) {
868 expect("(destination operand) register");
871 operands
|= ENCODE_RD(ops
[0].reg
);
874 switch (ARM_INSTRUCTION_GROUP(token
)) {
876 opcode
|= ENCODE_SET_CONDITION_CODES
;
879 if (ops
[1].type
== OP_REG32
) {
880 operands
|= ops
[1].reg
;
881 operands
|= ENCODE_BARREL_SHIFTER_MODE_ROR
;
882 asm_emit_opcode(token
, opcode
| operands
);
884 tcc_error("(first source operand) register");
887 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
888 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
893 expect("two or three operands");
897 switch (ARM_INSTRUCTION_GROUP(token
)) {
902 opcode
|= ENCODE_SET_CONDITION_CODES
;
906 switch (ops
[1].type
) {
908 operands
|= ops
[1].reg
;
911 operands
|= ENCODE_IMMEDIATE_FLAG
;
912 operands
|= ops
[1].e
.v
;
913 tcc_error("Using an immediate value as the source operand is not possible with '%s' instruction on ARM", get_tok_str(token
, NULL
));
917 switch (ops
[2].type
) {
919 if ((ops
[0].type
== OP_REG32
&& ops
[0].reg
== 15) ||
920 (ops
[1].type
== OP_REG32
&& ops
[1].reg
== 15)) {
921 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
923 operands
|= asm_encode_shift(&ops
[2]);
927 operands
|= asm_encode_shift(&ops
[2]);
929 definitely_neutral
= 1;
933 if (!definitely_neutral
) switch (ARM_INSTRUCTION_GROUP(token
)) {
936 operands
|= ENCODE_BARREL_SHIFTER_MODE_LSL
;
940 operands
|= ENCODE_BARREL_SHIFTER_MODE_LSR
;
944 operands
|= ENCODE_BARREL_SHIFTER_MODE_ASR
;
948 operands
|= ENCODE_BARREL_SHIFTER_MODE_ROR
;
951 expect("shift instruction");
954 asm_emit_opcode(token
, opcode
| operands
);
957 static void asm_multiplication_opcode(TCCState
*s1
, int token
)
961 uint32_t opcode
= 0x90;
963 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
964 parse_operand(s1
, &ops
[nb_ops
]);
972 expect("at least two operands");
973 else if (nb_ops
== 2) {
974 switch (ARM_INSTRUCTION_GROUP(token
)) {
977 memcpy(&ops
[2], &ops
[0], sizeof(ops
[1])); // ARM is actually like this!
980 expect("at least three operands");
986 // multiply (special case):
993 if (ops
[0].type
== OP_REG32
)
994 opcode
|= ops
[0].reg
<< 16;
996 expect("(destination operand) register");
997 if (ops
[1].type
== OP_REG32
)
998 opcode
|= ops
[1].reg
;
1000 expect("(first source operand) register");
1001 if (ops
[2].type
== OP_REG32
)
1002 opcode
|= ops
[2].reg
<< 8;
1004 expect("(second source operand) register");
1006 if (ops
[3].type
== OP_REG32
)
1007 opcode
|= ops
[3].reg
<< 12;
1009 expect("(third source operand) register");
1012 switch (ARM_INSTRUCTION_GROUP(token
)) {
1013 case TOK_ASM_mulseq
:
1014 opcode
|= 1 << 20; // Status
1018 expect("three operands");
1020 asm_emit_opcode(token
, opcode
);
1023 case TOK_ASM_mlaseq
:
1024 opcode
|= 1 << 20; // Status
1028 expect("four operands");
1030 opcode
|= 1 << 21; // Accumulate
1031 asm_emit_opcode(token
, opcode
);
1035 expect("known multiplication instruction");
1039 static void asm_long_multiplication_opcode(TCCState
*s1
, int token
)
1043 uint32_t opcode
= 0x90 | (1 << 23);
1045 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
1046 parse_operand(s1
, &ops
[nb_ops
]);
1054 expect("four operands");
1058 // long multiply (special case):
1060 // RdLo: bits 15...12
1061 // RdHi: bits 19...16
1065 if (ops
[0].type
== OP_REG32
)
1066 opcode
|= ops
[0].reg
<< 12;
1068 expect("(destination lo accumulator) register");
1069 if (ops
[1].type
== OP_REG32
)
1070 opcode
|= ops
[1].reg
<< 16;
1072 expect("(destination hi accumulator) register");
1073 if (ops
[2].type
== OP_REG32
)
1074 opcode
|= ops
[2].reg
;
1076 expect("(first source operand) register");
1077 if (ops
[3].type
== OP_REG32
)
1078 opcode
|= ops
[3].reg
<< 8;
1080 expect("(second source operand) register");
1082 switch (ARM_INSTRUCTION_GROUP(token
)) {
1083 case TOK_ASM_smullseq
:
1084 opcode
|= 1 << 20; // Status
1086 case TOK_ASM_smulleq
:
1087 opcode
|= 1 << 22; // signed
1088 asm_emit_opcode(token
, opcode
);
1090 case TOK_ASM_umullseq
:
1091 opcode
|= 1 << 20; // Status
1093 case TOK_ASM_umulleq
:
1094 asm_emit_opcode(token
, opcode
);
1096 case TOK_ASM_smlalseq
:
1097 opcode
|= 1 << 20; // Status
1099 case TOK_ASM_smlaleq
:
1100 opcode
|= 1 << 22; // signed
1101 opcode
|= 1 << 21; // Accumulate
1102 asm_emit_opcode(token
, opcode
);
1104 case TOK_ASM_umlalseq
:
1105 opcode
|= 1 << 20; // Status
1107 case TOK_ASM_umlaleq
:
1108 opcode
|= 1 << 21; // Accumulate
1109 asm_emit_opcode(token
, opcode
);
1112 expect("known long multiplication instruction");
1116 static void asm_single_data_transfer_opcode(TCCState
*s1
, int token
)
1119 Operand strex_operand
;
1123 int closed_bracket
= 0;
1125 uint32_t opcode
= 0;
1126 // Note: ldr r0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1127 // Note: ldr r0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1128 // Note: ldr r0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1130 parse_operand(s1
, &ops
[0]);
1131 if (ops
[0].type
== OP_REG32
)
1132 opcode
|= ENCODE_RD(ops
[0].reg
);
1134 expect("(destination operand) register");
1138 expect("at least two arguments");
1142 switch (ARM_INSTRUCTION_GROUP(token
)) {
1143 case TOK_ASM_strexbeq
:
1144 case TOK_ASM_strexeq
:
1145 parse_operand(s1
, &strex_operand
);
1146 if (strex_operand
.type
!= OP_REG32
) {
1151 expect("at least three arguments");
1162 parse_operand(s1
, &ops
[1]);
1163 if (ops
[1].type
== OP_REG32
)
1164 opcode
|= ENCODE_RN(ops
[1].reg
);
1166 expect("(first source operand) register");
1172 // exclam = 1; // implicit in hardware; don't do it in software
1180 parse_operand(s1
, &ops
[2]);
1181 if (ops
[2].type
== OP_REG32
) {
1182 if (ops
[2].reg
== 15) {
1183 tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token
, NULL
));
1188 opcode
|= asm_parse_optional_shift(s1
, &nb_shift
, &shift
);
1190 expect("shift directive, or no comma");
1194 // end of input expression in brackets--assume 0 offset
1195 ops
[2].type
= OP_IM8
;
1197 opcode
|= 1 << 24; // add offset before transfer
1199 if (!closed_bracket
) {
1204 opcode
|= 1 << 24; // add offset before transfer
1211 // single data transfer: 0 1 I P U B W L << 20 (general case):
1213 // Rd: destination operand [ok]
1214 // Rn: first source operand [ok]
1215 // Operand2: bits 11...0 [ok]
1216 // I: immediate operand? [ok]
1217 // P: Pre/post indexing is PRE: Add offset before transfer [ok]
1218 // U: Up/down is up? (*adds* offset to base) [ok]
1219 // B: Byte/word is byte? [ok]
1220 // W: Write address back into base? [ok]
1221 // L: Load/store is load? [ok]
1223 opcode
|= 1 << 21; // write offset back into register
1225 if (ops
[2].type
== OP_IM32
|| ops
[2].type
== OP_IM8
|| ops
[2].type
== OP_IM8N
) {
1228 tcc_error("minus before '#' not supported for immediate values");
1230 opcode
|= 1 << 23; // up
1232 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
1237 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
1241 } else if (ops
[2].type
== OP_REG32
) {
1243 opcode
|= 1 << 23; // up
1244 opcode
|= ENCODE_IMMEDIATE_FLAG
; /* if set, it means it's NOT immediate */
1245 opcode
|= ops
[2].reg
;
1249 switch (ARM_INSTRUCTION_GROUP(token
)) {
1250 case TOK_ASM_strbeq
:
1251 opcode
|= 1 << 22; // B
1254 opcode
|= 1 << 26; // Load/Store
1256 opcode
|= asm_encode_shift(&shift
);
1257 asm_emit_opcode(token
, opcode
);
1259 case TOK_ASM_ldrbeq
:
1260 opcode
|= 1 << 22; // B
1263 opcode
|= 1 << 20; // L
1264 opcode
|= 1 << 26; // Load/Store
1266 opcode
|= asm_encode_shift(&shift
);
1267 asm_emit_opcode(token
, opcode
);
1269 case TOK_ASM_strexbeq
:
1270 opcode
|= 1 << 22; // B
1272 case TOK_ASM_strexeq
:
1273 if ((opcode
& 0xFFF) || nb_shift
) {
1274 tcc_error("neither offset nor shift allowed with 'strex'");
1276 } else if (opcode
& ENCODE_IMMEDIATE_FLAG
) { // if set, it means it's NOT immediate
1277 tcc_error("offset not allowed with 'strex'");
1280 if ((opcode
& (1 << 24)) == 0) { // add offset after transfer
1281 tcc_error("adding offset after transfer not allowed with 'strex'");
1285 opcode
|= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL
1286 opcode
|= strex_operand
.reg
;
1287 asm_emit_opcode(token
, opcode
);
1289 case TOK_ASM_ldrexbeq
:
1290 opcode
|= 1 << 22; // B
1292 case TOK_ASM_ldrexeq
:
1293 if ((opcode
& 0xFFF) || nb_shift
) {
1294 tcc_error("neither offset nor shift allowed with 'ldrex'");
1296 } else if (opcode
& ENCODE_IMMEDIATE_FLAG
) { // if set, it means it's NOT immediate
1297 tcc_error("offset not allowed with 'ldrex'");
1300 if ((opcode
& (1 << 24)) == 0) { // add offset after transfer
1301 tcc_error("adding offset after transfer not allowed with 'ldrex'");
1304 opcode
|= 1 << 20; // L
1306 opcode
|= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL
1307 asm_emit_opcode(token
, opcode
);
1310 expect("data transfer instruction");
1314 // Note: Only call this using a VFP register if you know exactly what you are doing (i.e. cp_number is 10 or 11 and you are doing a vmov)
1315 static void asm_emit_coprocessor_data_transfer(uint32_t high_nibble
, uint8_t cp_number
, uint8_t CRd
, const Operand
* Rn
, const Operand
* offset
, int offset_minus
, int preincrement
, int writeback
, int long_transfer
, int load
) {
1316 uint32_t opcode
= 0x0;
1317 opcode
|= 1 << 26; // Load/Store
1318 opcode
|= 1 << 27; // coprocessor
1321 opcode
|= 1 << 22; // long transfer
1324 opcode
|= 1 << 20; // L
1326 opcode
|= cp_number
<< 8;
1329 opcode
|= ENCODE_RD(CRd
);
1331 if (Rn
->type
!= OP_REG32
) {
1335 //assert(Rn->reg < 16);
1336 opcode
|= ENCODE_RN(Rn
->reg
);
1338 opcode
|= 1 << 24; // add offset before transfer
1341 opcode
|= 1 << 21; // write offset back into register
1343 if (offset
->type
== OP_IM8
|| offset
->type
== OP_IM8N
|| offset
->type
== OP_IM32
) {
1344 int v
= offset
->e
.v
;
1346 tcc_error("minus before '#' not supported for immediate values");
1347 if (offset
->type
== OP_IM8N
|| v
< 0)
1350 opcode
|= 1 << 23; // up
1352 tcc_error("immediate offset must be a multiple of 4");
1357 tcc_error("immediate offset must be between -1020 and 1020");
1361 } else if (offset
->type
== OP_REG32
) {
1363 opcode
|= 1 << 23; // up
1364 opcode
|= ENCODE_IMMEDIATE_FLAG
; /* if set, it means it's NOT immediate */
1365 opcode
|= offset
->reg
;
1366 tcc_error("Using register offset to register address is not possible here");
1368 } else if (offset
->type
== OP_VREG64
) {
1370 opcode
|= offset
->reg
;
1372 expect("immediate or register");
1374 asm_emit_unconditional_opcode((high_nibble
<< 28) | opcode
);
1377 // Almost exactly the same as asm_single_data_transfer_opcode.
1378 // Difference: Offsets are smaller and multiples of 4; no shifts, no STREX, ENCODE_IMMEDIATE_FLAG is inverted again.
1379 static void asm_coprocessor_data_transfer_opcode(TCCState
*s1
, int token
)
1382 uint8_t coprocessor
;
1383 uint8_t coprocessor_destination_register
;
1384 int preincrement
= 0;
1386 int closed_bracket
= 0;
1388 int long_transfer
= 0;
1389 // Note: ldc p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1390 // Note: ldc p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1391 // Note: ldc p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1393 if (tok
>= TOK_ASM_p0
&& tok
<= TOK_ASM_p15
) {
1394 coprocessor
= tok
- TOK_ASM_p0
;
1397 expect("'c<number>'");
1406 if (tok
>= TOK_ASM_c0
&& tok
<= TOK_ASM_c15
) {
1407 coprocessor_destination_register
= tok
- TOK_ASM_c0
;
1410 expect("'c<number>'");
1424 parse_operand(s1
, &ops
[1]);
1425 if (ops
[1].type
!= OP_REG32
) {
1426 expect("(first source operand) register");
1432 // exclam = 1; // implicit in hardware; don't do it in software
1440 parse_operand(s1
, &ops
[2]);
1441 if (ops
[2].type
== OP_REG32
) {
1442 if (ops
[2].reg
== 15) {
1443 tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token
, NULL
));
1446 } else if (ops
[2].type
== OP_VREG64
) {
1447 tcc_error("'%s' does not support VFP register operand", get_tok_str(token
, NULL
));
1451 // end of input expression in brackets--assume 0 offset
1452 ops
[2].type
= OP_IM8
;
1454 preincrement
= 1; // add offset before transfer
1456 if (!closed_bracket
) {
1461 preincrement
= 1; // add offset before transfer
1468 // TODO: Support options.
1470 if (token
== TOK_ASM_ldc2
|| token
== TOK_ASM_stc2
|| token
== TOK_ASM_ldc2l
|| token
== TOK_ASM_stc2l
) {
1473 long_transfer
= 1; // long transfer
1476 asm_emit_coprocessor_data_transfer(0xF, coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], op2_minus
, preincrement
, exclam
, long_transfer
, 1);
1479 long_transfer
= 1; // long transfer
1482 asm_emit_coprocessor_data_transfer(0xF, coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], op2_minus
, preincrement
, exclam
, long_transfer
, 0);
1485 } else switch (ARM_INSTRUCTION_GROUP(token
)) {
1486 case TOK_ASM_stcleq
:
1490 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], op2_minus
, preincrement
, exclam
, long_transfer
, 0);
1492 case TOK_ASM_ldcleq
:
1496 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], op2_minus
, preincrement
, exclam
, long_transfer
, 1);
1499 expect("coprocessor data transfer instruction");
1503 #if defined(TCC_ARM_VFP)
1504 #define CP_SINGLE_PRECISION_FLOAT 10
1505 #define CP_DOUBLE_PRECISION_FLOAT 11
1507 static void asm_floating_point_single_data_transfer_opcode(TCCState
*s1
, int token
)
1510 uint8_t coprocessor
= 0;
1511 uint8_t coprocessor_destination_register
= 0;
1512 int long_transfer
= 0;
1513 // Note: vldr p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1514 // Note: Not allowed: vldr p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1515 // Note: Not allowed: vldr p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1517 parse_operand(s1
, &ops
[0]);
1518 if (ops
[0].type
== OP_VREG32
) {
1519 coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
1520 coprocessor_destination_register
= ops
[0].reg
;
1521 long_transfer
= coprocessor_destination_register
& 1;
1522 coprocessor_destination_register
>>= 1;
1523 } else if (ops
[0].type
== OP_VREG64
) {
1524 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
1525 coprocessor_destination_register
= ops
[0].reg
;
1528 expect("floating point register");
1542 parse_operand(s1
, &ops
[1]);
1543 if (ops
[1].type
!= OP_REG32
) {
1544 expect("(first source operand) register");
1549 parse_operand(s1
, &ops
[2]);
1550 if (ops
[2].type
!= OP_IM8
&& ops
[2].type
!= OP_IM8N
) {
1551 expect("immediate offset");
1555 // end of input expression in brackets--assume 0 offset
1556 ops
[2].type
= OP_IM8
;
1564 switch (ARM_INSTRUCTION_GROUP(token
)) {
1565 case TOK_ASM_vldreq
:
1566 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], 0, 1, 0, long_transfer
, 1);
1568 case TOK_ASM_vstreq
:
1569 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], 0, 1, 0, long_transfer
, 0);
1572 expect("floating point data transfer instruction");
1576 static void asm_floating_point_block_data_transfer_opcode(TCCState
*s1
, int token
)
1578 uint8_t coprocessor
= 0;
1579 int first_regset_register
;
1580 int last_regset_register
;
1581 uint8_t regset_item_count
;
1582 uint8_t extra_register_bit
= 0;
1585 int preincrement
= 0;
1588 switch (ARM_INSTRUCTION_GROUP(token
)) {
1589 case TOK_ASM_vpusheq
:
1590 case TOK_ASM_vpopeq
:
1591 ops
[0].type
= OP_REG32
;
1592 ops
[0].reg
= 13; // sp
1596 parse_operand(s1
, &ops
[0]);
1602 next(); // skip comma
1614 first_regset_register
= asm_parse_vfp_regvar(tok
, 1);
1615 if ((first_regset_register
= asm_parse_vfp_regvar(tok
, 1)) != -1) {
1616 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
1618 } else if ((first_regset_register
= asm_parse_vfp_regvar(tok
, 0)) != -1) {
1619 coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
1622 expect("floating-point register");
1628 if ((last_regset_register
= asm_parse_vfp_regvar(tok
, coprocessor
== CP_DOUBLE_PRECISION_FLOAT
)) != -1)
1631 expect("floating-point register");
1635 last_regset_register
= first_regset_register
;
1637 if (last_regset_register
< first_regset_register
) {
1638 tcc_error("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
1647 // Note: 0 (one down) is not implemented by us regardless.
1648 regset_item_count
= last_regset_register
- first_regset_register
+ 1;
1649 if (coprocessor
== CP_DOUBLE_PRECISION_FLOAT
)
1650 regset_item_count
<<= 1;
1652 extra_register_bit
= first_regset_register
& 1;
1653 first_regset_register
>>= 1;
1655 offset
.type
= OP_IM8
;
1656 offset
.e
.v
= regset_item_count
<< 2;
1657 switch (ARM_INSTRUCTION_GROUP(token
)) {
1658 case TOK_ASM_vstmeq
: // post-increment store
1659 case TOK_ASM_vstmiaeq
: // post-increment store
1661 case TOK_ASM_vpopeq
:
1662 case TOK_ASM_vldmeq
: // post-increment load
1663 case TOK_ASM_vldmiaeq
: // post-increment load
1666 case TOK_ASM_vldmdbeq
: // pre-decrement load
1669 case TOK_ASM_vpusheq
:
1670 case TOK_ASM_vstmdbeq
: // pre-decrement store
1671 offset
.type
= OP_IM8N
;
1672 offset
.e
.v
= -offset
.e
.v
;
1676 expect("floating point block data transfer instruction");
1679 if (ops
[0].type
!= OP_REG32
)
1680 expect("(first operand) register");
1681 else if (ops
[0].reg
== 15)
1682 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
1683 else if (!op0_exclam
&& ARM_INSTRUCTION_GROUP(token
) != TOK_ASM_vldmeq
&& ARM_INSTRUCTION_GROUP(token
) != TOK_ASM_vldmiaeq
&& ARM_INSTRUCTION_GROUP(token
) != TOK_ASM_vstmeq
&& ARM_INSTRUCTION_GROUP(token
) != TOK_ASM_vstmiaeq
)
1684 tcc_error("first operand of '%s' should have an exclamation mark", get_tok_str(token
, NULL
));
1686 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, first_regset_register
, &ops
[0], &offset
, 0, preincrement
, op0_exclam
, extra_register_bit
, load
);
1689 #define VMOV_FRACTIONAL_DIGITS 7
1690 #define VMOV_ONE 10000000 /* pow(10, VMOV_FRACTIONAL_DIGITS) */
1692 static uint32_t vmov_parse_fractional_part(const char* s
)
1694 uint32_t result
= 0;
1696 for (i
= 0; i
< VMOV_FRACTIONAL_DIGITS
; ++i
) {
1699 if (c
>= '0' && c
<= '9') {
1700 result
+= (c
- '0');
1705 expect("decimal numeral");
1709 static int vmov_linear_approx_index(uint32_t beginning
, uint32_t end
, uint32_t value
)
1715 k
= (end
- beginning
)/16;
1716 for (xvalue
= beginning
, i
= 0; i
< 16; ++i
, xvalue
+= k
) {
1717 if (value
== xvalue
)
1724 static uint32_t vmov_parse_immediate_value() {
1726 unsigned long integral_value
;
1729 if (tok
!= TOK_PPNUM
) {
1730 expect("immediate value");
1735 integral_value
= strtoul(p
, (char **)&p
, 0);
1737 if (errno
|| integral_value
>= 32) {
1738 tcc_error("invalid floating-point immediate value");
1742 value
= (uint32_t) integral_value
* VMOV_ONE
;
1745 value
+= vmov_parse_fractional_part(p
);
1751 static uint8_t vmov_encode_immediate_value(uint32_t value
)
1755 uint32_t beginning
= 0;
1760 limit
= 32 * VMOV_ONE
;
1761 for (i
= 0; i
< 8; ++i
) {
1762 if (value
< limit
) {
1770 if (r
== -1 || value
< beginning
|| value
> end
) {
1771 tcc_error("invalid decimal number for vmov: %d", value
);
1774 n
= vmov_linear_approx_index(beginning
, end
, value
);
1775 return n
| (((3 - r
) & 0x7) << 4);
1779 static void asm_floating_point_immediate_data_processing_opcode_tail(TCCState
*s1
, int token
, uint8_t coprocessor
, uint8_t CRd
) {
1780 uint8_t opcode1
= 0;
1781 uint8_t opcode2
= 0;
1782 uint8_t operands
[3] = {0, 0, 0};
1783 uint32_t immediate_value
= 0;
1789 if (tok
== '#' || tok
== '$') {
1796 immediate_value
= vmov_parse_immediate_value();
1798 opcode1
= 11; // "Other" instruction
1799 switch (ARM_INSTRUCTION_GROUP(token
)) {
1800 case TOK_ASM_vcmpeq_f32
:
1801 case TOK_ASM_vcmpeq_f64
:
1804 if (immediate_value
) {
1805 expect("Immediate value 0");
1809 case TOK_ASM_vcmpeeq_f32
:
1810 case TOK_ASM_vcmpeeq_f64
:
1813 if (immediate_value
) {
1814 expect("Immediate value 0");
1818 case TOK_ASM_vmoveq_f32
:
1819 case TOK_ASM_vmoveq_f64
:
1825 code
= vmov_encode_immediate_value(immediate_value
);
1826 operands
[1] |= code
>> 4;
1827 operands
[2] = code
& 0xF;
1830 expect("known floating point with immediate instruction");
1834 if (coprocessor
== CP_SINGLE_PRECISION_FLOAT
) {
1835 if (operands
[0] & 1)
1840 asm_emit_coprocessor_opcode(condition_code_of_token(token
), coprocessor
, opcode1
, operands
[0], operands
[1], operands
[2], opcode2
, 0);
1843 static void asm_floating_point_reg_arm_reg_transfer_opcode_tail(TCCState
*s1
, int token
, int coprocessor
, int nb_arm_regs
, int nb_ops
, Operand ops
[3]) {
1844 uint8_t opcode1
= 0;
1845 uint8_t opcode2
= 0;
1846 switch (coprocessor
) {
1847 case CP_SINGLE_PRECISION_FLOAT
:
1848 // "vmov.f32 r2, s3" or "vmov.f32 s3, r2"
1849 if (nb_ops
!= 2 || nb_arm_regs
!= 1) {
1850 tcc_error("vmov.f32 only implemented for one VFP register operand and one ARM register operands");
1853 if (ops
[0].type
!= OP_REG32
) { // determine mode: load or store
1854 // need to swap operands 0 and 1
1855 memcpy(&ops
[2], &ops
[1], sizeof(ops
[2]));
1856 memcpy(&ops
[1], &ops
[0], sizeof(ops
[1]));
1857 memcpy(&ops
[0], &ops
[2], sizeof(ops
[0]));
1861 if (ops
[1].type
== OP_VREG32
) {
1867 if (ops
[0].type
== OP_VREG32
) {
1873 asm_emit_coprocessor_opcode(condition_code_of_token(token
), coprocessor
, opcode1
, ops
[0].reg
, (ops
[1].type
== OP_IM8
) ? ops
[1].e
.v
: ops
[1].reg
, 0x10, opcode2
, 0);
1875 case CP_DOUBLE_PRECISION_FLOAT
:
1876 if (nb_ops
!= 3 || nb_arm_regs
!= 2) {
1877 tcc_error("vmov.f32 only implemented for one VFP register operand and two ARM register operands");
1880 // Determine whether it's a store into a VFP register (vmov "d1, r2, r3") rather than "vmov r2, r3, d1"
1881 if (ops
[0].type
== OP_VREG64
) {
1882 if (ops
[2].type
== OP_REG32
) {
1884 // need to rotate operand list to the left
1885 memcpy(&temp
, &ops
[0], sizeof(temp
));
1886 memcpy(&ops
[0], &ops
[1], sizeof(ops
[0]));
1887 memcpy(&ops
[1], &ops
[2], sizeof(ops
[1]));
1888 memcpy(&ops
[2], &temp
, sizeof(ops
[2]));
1890 tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands");
1893 } else if (ops
[0].type
!= OP_REG32
|| ops
[1].type
!= OP_REG32
|| ops
[2].type
!= OP_VREG64
) {
1894 tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands");
1899 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, ops
[0].reg
, &ops
[1], &ops
[2], 0, 0, 0, 1, opcode1
);
1902 tcc_internal_error("unknown coprocessor");
1906 static void asm_floating_point_vcvt_data_processing_opcode(TCCState
*s1
, int token
) {
1907 uint8_t coprocessor
= 0;
1909 uint8_t opcode1
= 11;
1910 uint8_t opcode2
= 2;
1912 switch (ARM_INSTRUCTION_GROUP(token
)) {
1913 case TOK_ASM_vcvtreq_s32_f64
:
1914 case TOK_ASM_vcvtreq_u32_f64
:
1915 case TOK_ASM_vcvteq_s32_f64
:
1916 case TOK_ASM_vcvteq_u32_f64
:
1917 case TOK_ASM_vcvteq_f64_s32
:
1918 case TOK_ASM_vcvteq_f64_u32
:
1919 case TOK_ASM_vcvteq_f32_f64
:
1920 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
1922 case TOK_ASM_vcvtreq_s32_f32
:
1923 case TOK_ASM_vcvtreq_u32_f32
:
1924 case TOK_ASM_vcvteq_s32_f32
:
1925 case TOK_ASM_vcvteq_u32_f32
:
1926 case TOK_ASM_vcvteq_f32_s32
:
1927 case TOK_ASM_vcvteq_f32_u32
:
1928 case TOK_ASM_vcvteq_f64_f32
:
1929 coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
1932 tcc_error("Unknown coprocessor for instruction '%s'", get_tok_str(token
, NULL
));
1936 parse_operand(s1
, &ops
[0]);
1937 ops
[1].type
= OP_IM8
;
1939 /* floating-point -> integer */
1940 switch (ARM_INSTRUCTION_GROUP(token
)) {
1941 case TOK_ASM_vcvtreq_s32_f32
:
1942 case TOK_ASM_vcvtreq_s32_f64
:
1943 case TOK_ASM_vcvteq_s32_f32
:
1944 case TOK_ASM_vcvteq_s32_f64
:
1945 ops
[1].e
.v
|= 1; // signed
1947 case TOK_ASM_vcvteq_u32_f32
:
1948 case TOK_ASM_vcvteq_u32_f64
:
1949 case TOK_ASM_vcvtreq_u32_f32
:
1950 case TOK_ASM_vcvtreq_u32_f64
:
1951 ops
[1].e
.v
|= 4; // to_integer (opc2)
1953 /* floating-point size conversion */
1954 case TOK_ASM_vcvteq_f64_f32
:
1955 case TOK_ASM_vcvteq_f32_f64
:
1964 parse_operand(s1
, &ops
[2]);
1966 switch (ARM_INSTRUCTION_GROUP(token
)) {
1967 /* floating-point -> integer */
1968 case TOK_ASM_vcvteq_s32_f32
:
1969 case TOK_ASM_vcvteq_s32_f64
:
1970 case TOK_ASM_vcvteq_u32_f32
:
1971 case TOK_ASM_vcvteq_u32_f64
:
1972 opcode2
|= 4; // round_zero
1975 /* integer -> floating-point */
1976 case TOK_ASM_vcvteq_f64_s32
:
1977 case TOK_ASM_vcvteq_f32_s32
:
1978 opcode2
|= 4; // signed--special
1981 /* floating-point size conversion */
1982 case TOK_ASM_vcvteq_f64_f32
:
1983 case TOK_ASM_vcvteq_f32_f64
:
1984 opcode2
|= 4; // always set
1988 switch (ARM_INSTRUCTION_GROUP(token
)) {
1989 case TOK_ASM_vcvteq_f64_u32
:
1990 case TOK_ASM_vcvteq_f64_s32
:
1991 case TOK_ASM_vcvteq_f64_f32
:
1992 if (ops
[0].type
== OP_VREG64
&& ops
[2].type
== OP_VREG32
) {
1994 expect("d<number>, s<number>");
1999 if (coprocessor
== CP_SINGLE_PRECISION_FLOAT
) {
2000 if (ops
[0].type
== OP_VREG32
&& ops
[2].type
== OP_VREG32
) {
2002 expect("s<number>, s<number>");
2005 } else if (coprocessor
== CP_DOUBLE_PRECISION_FLOAT
) {
2006 if (ops
[0].type
== OP_VREG32
&& ops
[2].type
== OP_VREG64
) {
2008 expect("s<number>, d<number>");
2014 if (ops
[2].type
== OP_VREG32
) {
2019 if (ops
[0].type
== OP_VREG32
) {
2024 asm_emit_coprocessor_opcode(condition_code_of_token(token
), coprocessor
, opcode1
, ops
[0].reg
, (ops
[1].type
== OP_IM8
) ? ops
[1].e
.v
: ops
[1].reg
, (ops
[2].type
== OP_IM8
) ? ops
[2].e
.v
: ops
[2].reg
, opcode2
, 0);
2027 static void asm_floating_point_data_processing_opcode(TCCState
*s1
, int token
) {
2028 uint8_t coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
2029 uint8_t opcode1
= 0;
2030 uint8_t opcode2
= 0; // (0 || 2) | register selection
2034 int nb_arm_regs
= 0;
2037 Instruction opcode opcode2 Reason
2038 =============================================================
2039 - 1?00 ?1? Undefined
2040 VFNMS 1?01 ?0? Must be unconditional
2041 VFNMA 1?01 ?1? Must be unconditional
2042 VFMA 1?10 ?0? Must be unconditional
2043 VFMS 1?10 ?1? Must be unconditional
2054 switch (ARM_INSTRUCTION_GROUP(token
)) {
2055 case TOK_ASM_vmlaeq_f64
:
2056 case TOK_ASM_vmlseq_f64
:
2057 case TOK_ASM_vnmlseq_f64
:
2058 case TOK_ASM_vnmlaeq_f64
:
2059 case TOK_ASM_vmuleq_f64
:
2060 case TOK_ASM_vnmuleq_f64
:
2061 case TOK_ASM_vaddeq_f64
:
2062 case TOK_ASM_vsubeq_f64
:
2063 case TOK_ASM_vdiveq_f64
:
2064 case TOK_ASM_vnegeq_f64
:
2065 case TOK_ASM_vabseq_f64
:
2066 case TOK_ASM_vsqrteq_f64
:
2067 case TOK_ASM_vcmpeq_f64
:
2068 case TOK_ASM_vcmpeeq_f64
:
2069 case TOK_ASM_vmoveq_f64
:
2070 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
2073 switch (ARM_INSTRUCTION_GROUP(token
)) {
2074 case TOK_ASM_vmoveq_f32
:
2075 case TOK_ASM_vmoveq_f64
:
2080 for (nb_ops
= 0; nb_ops
< 3; ) {
2081 // Note: Necessary because parse_operand can't parse decimal numerals.
2082 if (nb_ops
== 1 && (tok
== '#' || tok
== '$' || tok
== TOK_PPNUM
|| tok
== '-')) {
2083 asm_floating_point_immediate_data_processing_opcode_tail(s1
, token
, coprocessor
, ops
[0].reg
);
2086 parse_operand(s1
, &ops
[nb_ops
]);
2087 if (vmov
&& ops
[nb_ops
].type
== OP_REG32
) {
2089 } else if (ops
[nb_ops
].type
== OP_VREG32
) {
2090 if (coprocessor
!= CP_SINGLE_PRECISION_FLOAT
) {
2091 expect("'s<number>'");
2094 } else if (ops
[nb_ops
].type
== OP_VREG64
) {
2095 if (coprocessor
!= CP_DOUBLE_PRECISION_FLOAT
) {
2096 expect("'d<number>'");
2100 expect("floating point register");
2110 if (nb_arm_regs
== 0) {
2111 if (nb_ops
== 2) { // implicit
2112 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
2113 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
2117 tcc_error("Not enough operands for '%s' (%u)", get_tok_str(token
, NULL
), nb_ops
);
2122 switch (ARM_INSTRUCTION_GROUP(token
)) {
2123 case TOK_ASM_vmlaeq_f32
:
2124 case TOK_ASM_vmlaeq_f64
:
2128 case TOK_ASM_vmlseq_f32
:
2129 case TOK_ASM_vmlseq_f64
:
2133 case TOK_ASM_vnmlseq_f32
:
2134 case TOK_ASM_vnmlseq_f64
:
2138 case TOK_ASM_vnmlaeq_f32
:
2139 case TOK_ASM_vnmlaeq_f64
:
2143 case TOK_ASM_vmuleq_f32
:
2144 case TOK_ASM_vmuleq_f64
:
2148 case TOK_ASM_vnmuleq_f32
:
2149 case TOK_ASM_vnmuleq_f64
:
2153 case TOK_ASM_vaddeq_f32
:
2154 case TOK_ASM_vaddeq_f64
:
2158 case TOK_ASM_vsubeq_f32
:
2159 case TOK_ASM_vsubeq_f64
:
2163 case TOK_ASM_vdiveq_f32
:
2164 case TOK_ASM_vdiveq_f64
:
2168 case TOK_ASM_vnegeq_f32
:
2169 case TOK_ASM_vnegeq_f64
:
2170 opcode1
= 11; // Other" instruction
2172 ops
[1].type
= OP_IM8
;
2175 case TOK_ASM_vabseq_f32
:
2176 case TOK_ASM_vabseq_f64
:
2177 opcode1
= 11; // "Other" instruction
2179 ops
[1].type
= OP_IM8
;
2182 case TOK_ASM_vsqrteq_f32
:
2183 case TOK_ASM_vsqrteq_f64
:
2184 opcode1
= 11; // "Other" instruction
2186 ops
[1].type
= OP_IM8
;
2189 case TOK_ASM_vcmpeq_f32
:
2190 case TOK_ASM_vcmpeq_f64
:
2191 opcode1
= 11; // "Other" instruction
2193 ops
[1].type
= OP_IM8
;
2196 case TOK_ASM_vcmpeeq_f32
:
2197 case TOK_ASM_vcmpeeq_f64
:
2198 opcode1
= 11; // "Other" instruction
2200 ops
[1].type
= OP_IM8
;
2203 case TOK_ASM_vmoveq_f32
:
2204 case TOK_ASM_vmoveq_f64
:
2205 if (nb_arm_regs
> 0) { // vmov.f32 r2, s3 or similar
2206 asm_floating_point_reg_arm_reg_transfer_opcode_tail(s1
, token
, coprocessor
, nb_arm_regs
, nb_ops
, ops
);
2209 opcode1
= 11; // "Other" instruction
2211 ops
[1].type
= OP_IM8
;
2216 expect("known floating point instruction");
2220 if (coprocessor
== CP_SINGLE_PRECISION_FLOAT
) {
2221 if (ops
[2].type
== OP_VREG32
) {
2227 if (ops
[1].type
== OP_VREG32
) {
2233 if (ops
[0].type
== OP_VREG32
) {
2240 asm_emit_coprocessor_opcode(condition_code_of_token(token
), coprocessor
, opcode1
, ops
[0].reg
, (ops
[1].type
== OP_IM8
) ? ops
[1].e
.v
: ops
[1].reg
, (ops
[2].type
== OP_IM8
) ? ops
[2].e
.v
: ops
[2].reg
, opcode2
, 0);
2243 static void asm_floating_point_status_register_opcode(TCCState
* s1
, int token
)
2245 uint8_t coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
2247 int vfp_sys_reg
= -1;
2248 Operand arm_operand
;
2249 switch (ARM_INSTRUCTION_GROUP(token
)) {
2250 case TOK_ASM_vmrseq
:
2252 if (tok
== TOK_ASM_apsr_nzcv
) {
2253 arm_operand
.type
= OP_REG32
;
2254 arm_operand
.reg
= 15; // not PC
2255 next(); // skip apsr_nzcv
2257 parse_operand(s1
, &arm_operand
);
2258 if (arm_operand
.type
== OP_REG32
&& arm_operand
.reg
== 15) {
2259 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
2268 vfp_sys_reg
= asm_parse_vfp_status_regvar(tok
);
2269 next(); // skip vfp sys reg
2270 if (arm_operand
.type
== OP_REG32
&& arm_operand
.reg
== 15 && vfp_sys_reg
!= 1) {
2271 tcc_error("'%s' only supports the variant 'vmrs apsr_nzcv, fpscr' here", get_tok_str(token
, NULL
));
2275 case TOK_ASM_vmsreq
:
2277 vfp_sys_reg
= asm_parse_vfp_status_regvar(tok
);
2278 next(); // skip vfp sys reg
2283 parse_operand(s1
, &arm_operand
);
2284 if (arm_operand
.type
== OP_REG32
&& arm_operand
.reg
== 15) {
2285 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
2290 expect("floating point status register instruction");
2293 if (vfp_sys_reg
== -1) {
2294 expect("VFP system register");
2297 if (arm_operand
.type
!= OP_REG32
) {
2298 expect("ARM register");
2301 asm_emit_coprocessor_opcode(condition_code_of_token(token
), coprocessor
, opcode
, arm_operand
.reg
, vfp_sys_reg
, 0x10, 0, 0);
2306 static void asm_misc_single_data_transfer_opcode(TCCState
*s1
, int token
)
2310 int closed_bracket
= 0;
2312 uint32_t opcode
= (1 << 7) | (1 << 4);
2315 The argument syntax is exactly the same as in arm_single_data_transfer_opcode, except that there's no STREX argument form.
2316 The main difference between this function and asm_misc_single_data_transfer_opcode is that the immediate values here must be smaller.
2317 Also, the combination (P=0, W=1) is unpredictable here.
2318 The immediate flag has moved to bit index 22--and its meaning has flipped.
2319 The immediate value itself has been split into two parts: one at bits 11...8, one at bits 3...0
2320 bit 26 (Load/Store instruction) is unset here.
2321 bits 7 and 4 are set here. */
2323 // Here: 0 0 0 P U I W L << 20
2324 // [compare single data transfer: 0 1 I P U B W L << 20]
2326 parse_operand(s1
, &ops
[0]);
2327 if (ops
[0].type
== OP_REG32
)
2328 opcode
|= ENCODE_RD(ops
[0].reg
);
2330 expect("(destination operand) register");
2334 expect("at least two arguments");
2343 parse_operand(s1
, &ops
[1]);
2344 if (ops
[1].type
== OP_REG32
)
2345 opcode
|= ENCODE_RN(ops
[1].reg
);
2347 expect("(first source operand) register");
2353 // exclam = 1; // implicit in hardware; don't do it in software
2361 parse_operand(s1
, &ops
[2]);
2363 // end of input expression in brackets--assume 0 offset
2364 ops
[2].type
= OP_IM8
;
2366 opcode
|= 1 << 24; // add offset before transfer
2368 if (!closed_bracket
) {
2373 opcode
|= 1 << 24; // add offset before transfer
2381 if ((opcode
& (1 << 24)) == 0) {
2382 tcc_error("result of '%s' would be unpredictable here", get_tok_str(token
, NULL
));
2385 opcode
|= 1 << 21; // write offset back into register
2388 if (ops
[2].type
== OP_IM32
|| ops
[2].type
== OP_IM8
|| ops
[2].type
== OP_IM8N
) {
2391 tcc_error("minus before '#' not supported for immediate values");
2393 opcode
|= 1 << 23; // up
2395 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
2397 // bits 11...8: immediate hi nibble
2398 // bits 3...0: immediate lo nibble
2399 opcode
|= (v
& 0xF0) << 4;
2404 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
2407 // bits 11...8: immediate hi nibble
2408 // bits 3...0: immediate lo nibble
2409 opcode
|= (v
& 0xF0) << 4;
2413 opcode
|= 1 << 22; // not ENCODE_IMMEDIATE_FLAG;
2414 } else if (ops
[2].type
== OP_REG32
) {
2416 opcode
|= 1 << 23; // up
2417 opcode
|= ops
[2].reg
;
2421 switch (ARM_INSTRUCTION_GROUP(token
)) {
2422 case TOK_ASM_ldrsheq
:
2423 opcode
|= 1 << 5; // halfword, not byte
2425 case TOK_ASM_ldrsbeq
:
2426 opcode
|= 1 << 6; // sign extend
2427 opcode
|= 1 << 20; // L
2428 asm_emit_opcode(token
, opcode
);
2430 case TOK_ASM_ldrheq
:
2431 opcode
|= 1 << 5; // halfword, not byte
2432 opcode
|= 1 << 20; // L
2433 asm_emit_opcode(token
, opcode
);
2435 case TOK_ASM_strheq
:
2436 opcode
|= 1 << 5; // halfword, not byte
2437 asm_emit_opcode(token
, opcode
);
2442 /* Note: almost dupe of encbranch in arm-gen.c */
2443 static uint32_t encbranchoffset(int pos
, int addr
, int fail
)
2447 if(addr
>=0x7fffff || addr
<-0x800000) {
2449 tcc_error("branch offset is too far");
2452 return /*not 0x0A000000|*/(addr
&0xffffff);
2455 static void asm_branch_opcode(TCCState
*s1
, int token
)
2462 switch (ARM_INSTRUCTION_GROUP(token
)) {
2466 esym
= elfsym(e
.sym
);
2467 if (!esym
|| esym
->st_shndx
!= cur_text_section
->sh_num
) {
2468 tcc_error("invalid branch target");
2471 jmp_disp
= encbranchoffset(ind
, e
.v
+ esym
->st_value
, 1);
2474 parse_operand(s1
, &op
);
2477 switch (ARM_INSTRUCTION_GROUP(token
)) {
2479 asm_emit_opcode(token
, (0xa << 24) | (jmp_disp
& 0xffffff));
2482 asm_emit_opcode(token
, (0xb << 24) | (jmp_disp
& 0xffffff));
2485 if (op
.type
!= OP_REG32
)
2488 asm_emit_opcode(token
, (0x12fff1 << 4) | op
.reg
);
2491 if (op
.type
!= OP_REG32
)
2494 asm_emit_opcode(token
, (0x12fff3 << 4) | op
.reg
);
2497 expect("branch instruction");
2501 ST_FUNC
void asm_opcode(TCCState
*s1
, int token
)
2503 while (token
== TOK_LINEFEED
) {
2507 if (token
== TOK_EOF
)
2509 if (token
< TOK_ASM_nopeq
) { // no condition code
2512 asm_coprocessor_opcode(s1
, token
);
2518 asm_coprocessor_data_transfer_opcode(s1
, token
);
2521 expect("instruction");
2526 switch (ARM_INSTRUCTION_GROUP(token
)) {
2527 case TOK_ASM_pusheq
:
2529 case TOK_ASM_stmdaeq
:
2530 case TOK_ASM_ldmdaeq
:
2533 case TOK_ASM_stmiaeq
:
2534 case TOK_ASM_ldmiaeq
:
2535 case TOK_ASM_stmdbeq
:
2536 case TOK_ASM_ldmdbeq
:
2537 case TOK_ASM_stmibeq
:
2538 case TOK_ASM_ldmibeq
:
2539 asm_block_data_transfer_opcode(s1
, token
);
2544 asm_nullary_opcode(token
);
2548 asm_unary_opcode(s1
, token
);
2554 asm_branch_opcode(s1
, token
);
2557 case TOK_ASM_sxtbeq
:
2558 case TOK_ASM_sxtheq
:
2559 case TOK_ASM_uxtbeq
:
2560 case TOK_ASM_uxtheq
:
2561 case TOK_ASM_movteq
:
2562 case TOK_ASM_movweq
:
2563 asm_binary_opcode(s1
, token
);
2567 case TOK_ASM_ldrbeq
:
2569 case TOK_ASM_strbeq
:
2570 case TOK_ASM_ldrexeq
:
2571 case TOK_ASM_ldrexbeq
:
2572 case TOK_ASM_strexeq
:
2573 case TOK_ASM_strexbeq
:
2574 asm_single_data_transfer_opcode(s1
, token
);
2577 case TOK_ASM_ldrheq
:
2578 case TOK_ASM_ldrsheq
:
2579 case TOK_ASM_ldrsbeq
:
2580 case TOK_ASM_strheq
:
2581 asm_misc_single_data_transfer_opcode(s1
, token
);
2600 case TOK_ASM_andseq
:
2601 case TOK_ASM_eorseq
:
2602 case TOK_ASM_subseq
:
2603 case TOK_ASM_rsbseq
:
2604 case TOK_ASM_addseq
:
2605 case TOK_ASM_adcseq
:
2606 case TOK_ASM_sbcseq
:
2607 case TOK_ASM_rscseq
:
2608 // case TOK_ASM_tstseq:
2609 // case TOK_ASM_teqseq:
2610 // case TOK_ASM_cmpseq:
2611 // case TOK_ASM_cmnseq:
2612 case TOK_ASM_orrseq
:
2613 case TOK_ASM_movseq
:
2614 case TOK_ASM_bicseq
:
2615 case TOK_ASM_mvnseq
:
2616 asm_data_processing_opcode(s1
, token
);
2620 case TOK_ASM_lslseq
:
2622 case TOK_ASM_lsrseq
:
2624 case TOK_ASM_asrseq
:
2626 case TOK_ASM_rorseq
:
2627 case TOK_ASM_rrxseq
:
2629 asm_shift_opcode(s1
, token
);
2633 case TOK_ASM_mulseq
:
2635 case TOK_ASM_mlaseq
:
2636 asm_multiplication_opcode(s1
, token
);
2639 case TOK_ASM_smulleq
:
2640 case TOK_ASM_smullseq
:
2641 case TOK_ASM_umulleq
:
2642 case TOK_ASM_umullseq
:
2643 case TOK_ASM_smlaleq
:
2644 case TOK_ASM_smlalseq
:
2645 case TOK_ASM_umlaleq
:
2646 case TOK_ASM_umlalseq
:
2647 asm_long_multiplication_opcode(s1
, token
);
2653 asm_coprocessor_opcode(s1
, token
);
2657 case TOK_ASM_ldcleq
:
2659 case TOK_ASM_stcleq
:
2660 asm_coprocessor_data_transfer_opcode(s1
, token
);
2663 #if defined(TCC_ARM_VFP)
2664 case TOK_ASM_vldreq
:
2665 case TOK_ASM_vstreq
:
2666 asm_floating_point_single_data_transfer_opcode(s1
, token
);
2669 case TOK_ASM_vmlaeq_f32
:
2670 case TOK_ASM_vmlseq_f32
:
2671 case TOK_ASM_vnmlseq_f32
:
2672 case TOK_ASM_vnmlaeq_f32
:
2673 case TOK_ASM_vmuleq_f32
:
2674 case TOK_ASM_vnmuleq_f32
:
2675 case TOK_ASM_vaddeq_f32
:
2676 case TOK_ASM_vsubeq_f32
:
2677 case TOK_ASM_vdiveq_f32
:
2678 case TOK_ASM_vnegeq_f32
:
2679 case TOK_ASM_vabseq_f32
:
2680 case TOK_ASM_vsqrteq_f32
:
2681 case TOK_ASM_vcmpeq_f32
:
2682 case TOK_ASM_vcmpeeq_f32
:
2683 case TOK_ASM_vmoveq_f32
:
2684 case TOK_ASM_vmlaeq_f64
:
2685 case TOK_ASM_vmlseq_f64
:
2686 case TOK_ASM_vnmlseq_f64
:
2687 case TOK_ASM_vnmlaeq_f64
:
2688 case TOK_ASM_vmuleq_f64
:
2689 case TOK_ASM_vnmuleq_f64
:
2690 case TOK_ASM_vaddeq_f64
:
2691 case TOK_ASM_vsubeq_f64
:
2692 case TOK_ASM_vdiveq_f64
:
2693 case TOK_ASM_vnegeq_f64
:
2694 case TOK_ASM_vabseq_f64
:
2695 case TOK_ASM_vsqrteq_f64
:
2696 case TOK_ASM_vcmpeq_f64
:
2697 case TOK_ASM_vcmpeeq_f64
:
2698 case TOK_ASM_vmoveq_f64
:
2699 asm_floating_point_data_processing_opcode(s1
, token
);
2702 case TOK_ASM_vcvtreq_s32_f32
:
2703 case TOK_ASM_vcvtreq_s32_f64
:
2704 case TOK_ASM_vcvteq_s32_f32
:
2705 case TOK_ASM_vcvteq_s32_f64
:
2706 case TOK_ASM_vcvtreq_u32_f32
:
2707 case TOK_ASM_vcvtreq_u32_f64
:
2708 case TOK_ASM_vcvteq_u32_f32
:
2709 case TOK_ASM_vcvteq_u32_f64
:
2710 case TOK_ASM_vcvteq_f64_s32
:
2711 case TOK_ASM_vcvteq_f32_s32
:
2712 case TOK_ASM_vcvteq_f64_u32
:
2713 case TOK_ASM_vcvteq_f32_u32
:
2714 case TOK_ASM_vcvteq_f64_f32
:
2715 case TOK_ASM_vcvteq_f32_f64
:
2716 asm_floating_point_vcvt_data_processing_opcode(s1
, token
);
2719 case TOK_ASM_vpusheq
:
2720 case TOK_ASM_vpopeq
:
2721 case TOK_ASM_vldmeq
:
2722 case TOK_ASM_vldmiaeq
:
2723 case TOK_ASM_vldmdbeq
:
2724 case TOK_ASM_vstmeq
:
2725 case TOK_ASM_vstmiaeq
:
2726 case TOK_ASM_vstmdbeq
:
2727 asm_floating_point_block_data_transfer_opcode(s1
, token
);
2730 case TOK_ASM_vmsreq
:
2731 case TOK_ASM_vmrseq
:
2732 asm_floating_point_status_register_opcode(s1
, token
);
2737 expect("known instruction");
2741 ST_FUNC
void subst_asm_operand(CString
*add_str
, SValue
*sv
, int modifier
)
2743 int r
, reg
, size
, val
;
2747 if ((r
& VT_VALMASK
) == VT_CONST
) {
2748 if (!(r
& VT_LVAL
) && modifier
!= 'c' && modifier
!= 'n' &&
2750 cstr_ccat(add_str
, '#');
2752 const char *name
= get_tok_str(sv
->sym
->v
, NULL
);
2753 if (sv
->sym
->v
>= SYM_FIRST_ANOM
) {
2754 /* In case of anonymous symbols ("L.42", used
2755 for static data labels) we can't find them
2756 in the C symbol table when later looking up
2757 this name. So enter them now into the asm label
2758 list when we still know the symbol. */
2759 get_asm_sym(tok_alloc(name
, strlen(name
))->tok
, sv
->sym
);
2761 if (tcc_state
->leading_underscore
)
2762 cstr_ccat(add_str
, '_');
2763 cstr_cat(add_str
, name
, -1);
2764 if ((uint32_t) sv
->c
.i
== 0)
2766 cstr_ccat(add_str
, '+');
2769 if (modifier
== 'n')
2771 snprintf(buf
, sizeof(buf
), "%d", (int) sv
->c
.i
);
2772 cstr_cat(add_str
, buf
, -1);
2774 } else if ((r
& VT_VALMASK
) == VT_LOCAL
) {
2775 snprintf(buf
, sizeof(buf
), "[fp,#%d]", (int) sv
->c
.i
);
2776 cstr_cat(add_str
, buf
, -1);
2777 } else if (r
& VT_LVAL
) {
2778 reg
= r
& VT_VALMASK
;
2779 if (reg
>= VT_CONST
)
2780 tcc_internal_error("");
2781 snprintf(buf
, sizeof(buf
), "[%s]",
2782 get_tok_str(TOK_ASM_r0
+ reg
, NULL
));
2783 cstr_cat(add_str
, buf
, -1);
2786 reg
= r
& VT_VALMASK
;
2787 if (reg
>= VT_CONST
)
2788 tcc_internal_error("");
2790 /* choose register operand size */
2791 if ((sv
->type
.t
& VT_BTYPE
) == VT_BYTE
||
2792 (sv
->type
.t
& VT_BTYPE
) == VT_BOOL
)
2794 else if ((sv
->type
.t
& VT_BTYPE
) == VT_SHORT
)
2799 if (modifier
== 'b') {
2801 } else if (modifier
== 'w') {
2803 } else if (modifier
== 'k') {
2809 reg
= TOK_ASM_r0
+ reg
;
2812 snprintf(buf
, sizeof(buf
), "%s", get_tok_str(reg
, NULL
));
2813 cstr_cat(add_str
, buf
, -1);
2817 /* generate prolog and epilog code for asm statement */
2818 ST_FUNC
void asm_gen_code(ASMOperand
*operands
, int nb_operands
,
2819 int nb_outputs
, int is_output
,
2820 uint8_t *clobber_regs
,
2823 uint8_t regs_allocated
[NB_ASM_REGS
];
2826 uint32_t saved_regset
= 0;
2828 // TODO: Check non-E ABI.
2829 // Note: Technically, r13 (sp) is also callee-saved--but that does not matter yet
2830 static const uint8_t reg_saved
[] = { 4, 5, 6, 7, 8, 9 /* Note: sometimes special reg "sb" */ , 10, 11 };
2832 /* mark all used registers */
2833 memcpy(regs_allocated
, clobber_regs
, sizeof(regs_allocated
));
2834 for(i
= 0; i
< nb_operands
;i
++) {
2837 regs_allocated
[op
->reg
] = 1;
2839 for(i
= 0; i
< sizeof(reg_saved
)/sizeof(reg_saved
[0]); i
++) {
2841 if (regs_allocated
[reg
])
2842 saved_regset
|= 1 << reg
;
2845 if (!is_output
) { // prolog
2846 /* generate reg save code */
2848 gen_le32(0xe92d0000 | saved_regset
); // push {...}
2850 /* generate load code */
2851 for(i
= 0; i
< nb_operands
; i
++) {
2854 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&&
2856 /* memory reference case (for both input and
2860 sv
.r
= (sv
.r
& ~VT_VALMASK
) | VT_LOCAL
| VT_LVAL
;
2863 } else if (i
>= nb_outputs
|| op
->is_rw
) { // not write-only
2864 /* load value in register */
2865 load(op
->reg
, op
->vt
);
2867 tcc_error("long long not implemented");
2872 /* generate save code */
2873 for(i
= 0 ; i
< nb_outputs
; i
++) {
2876 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
) {
2877 if (!op
->is_memory
) {
2880 sv
.r
= (sv
.r
& ~VT_VALMASK
) | VT_LOCAL
;
2885 sv
.r
= (sv
.r
& ~VT_VALMASK
) | out_reg
;
2886 store(op
->reg
, &sv
);
2889 store(op
->reg
, op
->vt
);
2891 tcc_error("long long not implemented");
2896 /* generate reg restore code */
2898 gen_le32(0xe8bd0000 | saved_regset
); // pop {...}
2902 /* return the constraint priority (we allocate first the lowest
2903 numbered constraints) */
2904 static inline int constraint_priority(const char *str
)
2906 int priority
, c
, pr
;
2908 /* we take the lowest priority */
2916 case 'l': // in ARM mode, that's an alias for 'r' [ARM].
2917 case 'r': // register [general]
2918 case 'p': // valid memory address for load,store [general]
2921 case 'M': // integer constant for shifts [ARM]
2922 case 'I': // integer valid for data processing instruction immediate
2923 case 'J': // integer in range -4095...4095
2925 case 'i': // immediate integer operand, including symbolic constants [general]
2926 case 'm': // memory operand [general]
2927 case 'g': // general-purpose-register, memory, immediate integer [general]
2931 tcc_error("unknown constraint '%c'", c
);
2940 static const char *skip_constraint_modifiers(const char *p
)
2942 /* Constraint modifier:
2943 = Operand is written to by this instruction
2944 + Operand is both read and written to by this instruction
2945 % Instruction is commutative for this operand and the following operand.
2947 Per-alternative constraint modifier:
2948 & Operand is clobbered before the instruction is done using the input operands
2950 while (*p
== '=' || *p
== '&' || *p
== '+' || *p
== '%')
2955 #define REG_OUT_MASK 0x01
2956 #define REG_IN_MASK 0x02
2958 #define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
2960 ST_FUNC
void asm_compute_constraints(ASMOperand
*operands
,
2961 int nb_operands
, int nb_outputs
,
2962 const uint8_t *clobber_regs
,
2965 /* overall format: modifier, then ,-seperated list of alternatives; all operands for a single instruction must have the same number of alternatives */
2966 /* TODO: Simple constraints
2968 o memory operand that is offsetable
2969 V memory but not offsetable
2970 < memory operand with autodecrement addressing is allowed. Restrictions apply.
2971 > memory operand with autoincrement addressing is allowed. Restrictions apply.
2972 n immediate integer operand with a known numeric value
2973 E immediate floating operand (const_double) is allowed, but only if target=host
2974 F immediate floating operand (const_double or const_vector) is allowed
2975 s immediate integer operand whose value is not an explicit integer
2976 X any operand whatsoever
2977 0...9 (postfix); (can also be more than 1 digit number); an operand that matches the specified operand number is allowed
2980 /* TODO: ARM constraints:
2981 k the stack pointer register
2982 G the floating-point constant 0.0
2983 Q memory reference where the exact address is in a single register ("m" is preferable for asm statements)
2984 R an item in the constant pool
2985 S symbol in the text segment of the current file
2986 [ Uv memory reference suitable for VFP load/store insns (reg+constant offset)]
2987 [ Uy memory reference suitable for iWMMXt load/store instructions]
2988 Uq memory reference suitable for the ARMv4 ldrsb instruction
2991 int sorted_op
[MAX_ASM_OPERANDS
];
2992 int i
, j
, k
, p1
, p2
, tmp
, reg
, c
, reg_mask
;
2994 uint8_t regs_allocated
[NB_ASM_REGS
];
2997 for (i
= 0; i
< nb_operands
; i
++) {
2999 op
->input_index
= -1;
3005 /* compute constraint priority and evaluate references to output
3006 constraints if input constraints */
3007 for (i
= 0; i
< nb_operands
; i
++) {
3009 str
= op
->constraint
;
3010 str
= skip_constraint_modifiers(str
);
3011 if (isnum(*str
) || *str
== '[') {
3012 /* this is a reference to another constraint */
3013 k
= find_constraint(operands
, nb_operands
, str
, NULL
);
3014 if ((unsigned) k
>= i
|| i
< nb_outputs
)
3015 tcc_error("invalid reference in constraint %d ('%s')",
3018 if (operands
[k
].input_index
>= 0)
3019 tcc_error("cannot reference twice the same operand");
3020 operands
[k
].input_index
= i
;
3022 } else if ((op
->vt
->r
& VT_VALMASK
) == VT_LOCAL
3024 && (reg
= op
->vt
->sym
->r
& VT_VALMASK
) < VT_CONST
) {
3028 op
->priority
= constraint_priority(str
);
3032 /* sort operands according to their priority */
3033 for (i
= 0; i
< nb_operands
; i
++)
3035 for (i
= 0; i
< nb_operands
- 1; i
++) {
3036 for (j
= i
+ 1; j
< nb_operands
; j
++) {
3037 p1
= operands
[sorted_op
[i
]].priority
;
3038 p2
= operands
[sorted_op
[j
]].priority
;
3041 sorted_op
[i
] = sorted_op
[j
];
3047 for (i
= 0; i
< NB_ASM_REGS
; i
++) {
3048 if (clobber_regs
[i
])
3049 regs_allocated
[i
] = REG_IN_MASK
| REG_OUT_MASK
;
3051 regs_allocated
[i
] = 0;
3053 /* sp cannot be used */
3054 regs_allocated
[13] = REG_IN_MASK
| REG_OUT_MASK
;
3055 /* fp cannot be used yet */
3056 regs_allocated
[11] = REG_IN_MASK
| REG_OUT_MASK
;
3058 /* allocate registers and generate corresponding asm moves */
3059 for (i
= 0; i
< nb_operands
; i
++) {
3062 str
= op
->constraint
;
3063 /* no need to allocate references */
3064 if (op
->ref_index
>= 0)
3066 /* select if register is used for output, input or both */
3067 if (op
->input_index
>= 0) {
3068 reg_mask
= REG_IN_MASK
| REG_OUT_MASK
;
3069 } else if (j
< nb_outputs
) {
3070 reg_mask
= REG_OUT_MASK
;
3072 reg_mask
= REG_IN_MASK
;
3075 if (is_reg_allocated(op
->reg
))
3077 ("asm regvar requests register that's taken already");
3084 case '=': // Operand is written-to
3086 case '+': // Operand is both READ and written-to
3089 case '&': // Operand is clobbered before the instruction is done using the input operands
3090 if (j
>= nb_outputs
)
3091 tcc_error("'%c' modifier can only be applied to outputs",
3093 reg_mask
= REG_IN_MASK
| REG_OUT_MASK
;
3095 case 'l': // In non-thumb mode, alias for 'r'--otherwise r0-r7 [ARM]
3096 case 'r': // general-purpose register
3097 case 'p': // loadable/storable address
3098 /* any general register */
3099 for (reg
= 0; reg
<= 8; reg
++) {
3100 if (!is_reg_allocated(reg
))
3105 /* now we can reload in the register */
3108 regs_allocated
[reg
] |= reg_mask
;
3110 case 'I': // integer that is valid as an data processing instruction immediate (0...255, rotated by a multiple of two)
3111 case 'J': // integer in the range -4095 to 4095 [ARM]
3112 case 'K': // integer that satisfies constraint I when inverted (one's complement)
3113 case 'L': // integer that satisfies constraint I when inverted (two's complement)
3114 case 'i': // immediate integer operand, including symbolic constants
3115 if (!((op
->vt
->r
& (VT_VALMASK
| VT_LVAL
)) == VT_CONST
))
3118 case 'M': // integer in the range 0 to 32
3120 ((op
->vt
->r
& (VT_VALMASK
| VT_LVAL
| VT_SYM
)) ==
3124 case 'm': // memory operand
3126 /* nothing special to do because the operand is already in
3127 memory, except if the pointer itself is stored in a
3128 memory variable (VT_LLOCAL case) */
3129 /* XXX: fix constant case */
3130 /* if it is a reference to a memory zone, it must lie
3131 in a register, so we reserve the register in the
3132 input registers and a load will be generated
3134 if (j
< nb_outputs
|| c
== 'm') {
3135 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
) {
3136 /* any general register */
3137 for (reg
= 0; reg
<= 8; reg
++) {
3138 if (!(regs_allocated
[reg
] & REG_IN_MASK
))
3143 /* now we can reload in the register */
3144 regs_allocated
[reg
] |= REG_IN_MASK
;
3151 tcc_error("asm constraint %d ('%s') could not be satisfied",
3155 /* if a reference is present for that operand, we assign it too */
3156 if (op
->input_index
>= 0) {
3157 operands
[op
->input_index
].reg
= op
->reg
;
3158 operands
[op
->input_index
].is_llong
= op
->is_llong
;
3162 /* compute out_reg. It is used to store outputs registers to memory
3163 locations references by pointers (VT_LLOCAL case) */
3165 for (i
= 0; i
< nb_operands
; i
++) {
3168 (op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& !op
->is_memory
) {
3169 for (reg
= 0; reg
<= 8; reg
++) {
3170 if (!(regs_allocated
[reg
] & REG_OUT_MASK
))
3173 tcc_error("could not find free output register for reloading");
3180 /* print sorted constraints */
3182 for (i
= 0; i
< nb_operands
; i
++) {
3185 printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
3187 op
->id
? get_tok_str(op
->id
, NULL
) : "",
3188 op
->constraint
, op
->vt
->r
, op
->reg
);
3191 printf("out_reg=%d\n", *pout_reg
);
3195 ST_FUNC
void asm_clobber(uint8_t *clobber_regs
, const char *str
)
3200 if (!strcmp(str
, "memory") ||
3201 !strcmp(str
, "cc") ||
3202 !strcmp(str
, "flags"))
3204 ts
= tok_alloc(str
, strlen(str
));
3205 reg
= asm_parse_regvar(ts
->tok
);
3207 tcc_error("invalid clobber register '%s'", str
);
3209 clobber_regs
[reg
] = 1;
3212 /* If T refers to a register then return the register number and type.
3213 Otherwise return -1. */
3214 ST_FUNC
int asm_parse_regvar (int t
)
3216 if (t
>= TOK_ASM_r0
&& t
<= TOK_ASM_pc
) { /* register name */
3219 return TOK_ASM_r11
- TOK_ASM_r0
;
3221 return TOK_ASM_r12
- TOK_ASM_r0
;
3223 return TOK_ASM_r13
- TOK_ASM_r0
;
3225 return TOK_ASM_r14
- TOK_ASM_r0
;
3227 return TOK_ASM_r15
- TOK_ASM_r0
;
3229 return t
- TOK_ASM_r0
;
3235 /*************************************************************/
3236 #endif /* ndef TARGET_DEFS_ONLY */