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 /* Parse a text containing operand and store the result in OP */
81 static void parse_operand(TCCState
*s1
, Operand
*op
)
89 if (tok
== '{') { // regset literal
91 while (tok
!= '}' && tok
!= TOK_EOF
) {
92 reg
= asm_parse_regvar(tok
);
97 next(); // skip register name
99 if ((1 << reg
) < regset
)
100 tcc_warning("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
110 // ARM instructions don't support empty regset.
111 tcc_error("empty register list is not supported");
113 op
->type
= OP_REGSET32
;
117 } else if ((reg
= asm_parse_regvar(tok
)) != -1) {
118 next(); // skip register name
120 op
->reg
= (uint8_t) reg
;
122 } else if ((reg
= asm_parse_vfp_regvar(tok
, 0)) != -1) {
123 next(); // skip register name
124 op
->type
= OP_VREG32
;
125 op
->reg
= (uint8_t) reg
;
127 } else if ((reg
= asm_parse_vfp_regvar(tok
, 1)) != -1) {
128 next(); // skip register name
129 op
->type
= OP_VREG64
;
130 op
->reg
= (uint8_t) reg
;
132 } else if (tok
== '#' || tok
== '$') {
134 next(); // skip '#' or '$'
140 if ((int) op
->e
.v
< 0 && (int) op
->e
.v
>= -255)
142 else if (op
->e
.v
== (uint8_t)op
->e
.v
)
148 /* XXX: make it faster ? */
149 ST_FUNC
void g(int c
)
155 if (ind1
> cur_text_section
->data_allocated
)
156 section_realloc(cur_text_section
, ind1
);
157 cur_text_section
->data
[ind
] = c
;
161 ST_FUNC
void gen_le16 (int i
)
167 ST_FUNC
void gen_le32 (int i
)
173 if (ind1
> cur_text_section
->data_allocated
)
174 section_realloc(cur_text_section
, ind1
);
175 cur_text_section
->data
[ind
++] = i
& 0xFF;
176 cur_text_section
->data
[ind
++] = (i
>> 8) & 0xFF;
177 cur_text_section
->data
[ind
++] = (i
>> 16) & 0xFF;
178 cur_text_section
->data
[ind
++] = (i
>> 24) & 0xFF;
181 ST_FUNC
void gen_expr32(ExprValue
*pe
)
186 static uint32_t condition_code_of_token(int token
) {
187 if (token
< TOK_ASM_nopeq
) {
188 expect("condition-enabled instruction");
191 return (token
- TOK_ASM_nopeq
) & 15;
194 static void asm_emit_opcode(int token
, uint32_t opcode
) {
195 gen_le32((condition_code_of_token(token
) << 28) | opcode
);
198 static void asm_emit_unconditional_opcode(uint32_t opcode
) {
202 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
)
204 uint32_t opcode
= 0xe000000;
205 if (inter_processor_transfer
)
207 //assert(cp_opcode < 16);
208 opcode
|= cp_opcode
<< 20;
209 //assert(cp_n_operand_register < 16);
210 opcode
|= cp_n_operand_register
<< 16;
211 //assert(cp_destination_register < 16);
212 opcode
|= cp_destination_register
<< 12;
213 //assert(cp_number < 16);
214 opcode
|= cp_number
<< 8;
215 //assert(cp_information < 8);
216 opcode
|= cp_opcode2
<< 5;
217 //assert(cp_m_operand_register < 16);
218 opcode
|= cp_m_operand_register
;
219 asm_emit_unconditional_opcode((high_nibble
<< 28) | opcode
);
222 static void asm_nullary_opcode(int token
)
224 switch (ARM_INSTRUCTION_GROUP(token
)) {
226 asm_emit_opcode(token
, 0xd << 21); // mov r0, r0
229 asm_emit_opcode(token
, 0x320f002);
231 asm_emit_opcode(token
, 0x320f003);
234 expect("nullary instruction");
238 static void asm_unary_opcode(TCCState
*s1
, int token
)
241 parse_operand(s1
, &op
);
243 switch (ARM_INSTRUCTION_GROUP(token
)) {
246 if (op
.type
!= OP_IM8
)
247 expect("immediate 8-bit unsigned integer");
249 /* Note: Dummy operand (ignored by processor): ARM ref documented 0...255, ARM instruction set documented 24 bit */
250 asm_emit_opcode(token
, (0xf << 24) | op
.e
.v
);
254 expect("unary instruction");
258 static void asm_binary_opcode(TCCState
*s1
, int token
)
262 uint32_t encoded_rotation
= 0;
264 parse_operand(s1
, &ops
[0]);
269 parse_operand(s1
, &ops
[1]);
270 if (ops
[0].type
!= OP_REG32
) {
271 expect("(destination operand) register");
275 if (ops
[0].reg
== 15) {
276 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
280 if (ops
[0].reg
== 13)
281 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token
, NULL
));
283 if (ops
[1].type
!= OP_REG32
) {
284 switch (ARM_INSTRUCTION_GROUP(token
)) {
287 if (ops
[1].type
== OP_IM8
|| ops
[1].type
== OP_IM8N
|| ops
[1].type
== OP_IM32
) {
288 if (ops
[1].e
.v
>= 0 && ops
[1].e
.v
<= 0xFFFF) {
289 uint16_t immediate_value
= ops
[1].e
.v
;
290 switch (ARM_INSTRUCTION_GROUP(token
)) {
292 asm_emit_opcode(token
, 0x3400000 | (ops
[0].reg
<< 12) | (immediate_value
& 0xF000) << 4 | (immediate_value
& 0xFFF));
295 asm_emit_opcode(token
, 0x3000000 | (ops
[0].reg
<< 12) | (immediate_value
& 0xF000) << 4 | (immediate_value
& 0xFFF));
299 expect("(source operand) immediate 16 bit value");
301 expect("(source operand) immediate");
304 expect("(source operand) register");
309 if (ops
[1].reg
== 15) {
310 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
314 if (ops
[1].reg
== 13)
315 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token
, NULL
));
319 if (tok
== TOK_ASM_ror
) {
320 next(); // skip 'ror'
321 parse_operand(s1
, &rotation
);
322 if (rotation
.type
!= OP_IM8
) {
323 expect("immediate value for rotation");
326 amount
= rotation
.e
.v
;
329 encoded_rotation
= 1 << 10;
332 encoded_rotation
= 2 << 10;
335 encoded_rotation
= 3 << 10;
338 expect("'8' or '16' or '24'");
344 switch (ARM_INSTRUCTION_GROUP(token
)) {
346 if (encoded_rotation
)
347 tcc_error("clz does not support rotation");
348 asm_emit_opcode(token
, 0x16f0f10 | (ops
[0].reg
<< 12) | ops
[1].reg
);
351 asm_emit_opcode(token
, 0x6af0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
354 asm_emit_opcode(token
, 0x6bf0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
357 asm_emit_opcode(token
, 0x6ef0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
360 asm_emit_opcode(token
, 0x6ff0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
363 expect("binary instruction");
367 static void asm_coprocessor_opcode(TCCState
*s1
, int token
) {
371 uint8_t registers
[3];
376 if (tok
>= TOK_ASM_p0
&& tok
<= TOK_ASM_p15
) {
377 coprocessor
= tok
- TOK_ASM_p0
;
380 expect("'p<number>'");
389 parse_operand(s1
, &opcode1
);
390 if (opcode1
.type
!= OP_IM8
|| opcode1
.e
.v
> 15) {
391 tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token
, NULL
));
395 for (i
= 0; i
< 3; ++i
) {
400 if (i
== 0 && token
!= TOK_ASM_cdp2
&& (ARM_INSTRUCTION_GROUP(token
) == TOK_ASM_mrceq
|| ARM_INSTRUCTION_GROUP(token
) == TOK_ASM_mcreq
)) {
401 if (tok
>= TOK_ASM_r0
&& tok
<= TOK_ASM_r15
) {
402 registers
[i
] = tok
- TOK_ASM_r0
;
405 expect("'r<number>'");
409 if (tok
>= TOK_ASM_c0
&& tok
<= TOK_ASM_c15
) {
410 registers
[i
] = tok
- TOK_ASM_c0
;
413 expect("'c<number>'");
420 parse_operand(s1
, &opcode2
);
422 opcode2
.type
= OP_IM8
;
425 if (opcode2
.type
!= OP_IM8
|| opcode2
.e
.v
> 15) {
426 tcc_error("opcode2 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token
, NULL
));
430 if (token
== TOK_ASM_cdp2
) {
432 asm_emit_coprocessor_opcode(high_nibble
, coprocessor
, opcode1
.e
.v
, registers
[0], registers
[1], registers
[2], opcode2
.e
.v
, 0);
435 high_nibble
= condition_code_of_token(token
);
437 switch (ARM_INSTRUCTION_GROUP(token
)) {
439 asm_emit_coprocessor_opcode(high_nibble
, coprocessor
, opcode1
.e
.v
, registers
[0], registers
[1], registers
[2], opcode2
.e
.v
, 0);
442 // opcode1 encoding changes! highest and lowest bit gone.
446 // opcode1 encoding changes! highest and lowest bit gone.
447 if (opcode1
.e
.v
> 7) {
448 tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 7", get_tok_str(token
, NULL
));
451 asm_emit_coprocessor_opcode(high_nibble
, coprocessor
, (opcode1
.e
.v
<< 1) | mrc
, registers
[0], registers
[1], registers
[2], opcode2
.e
.v
, 1);
454 expect("known instruction");
458 /* data processing and single data transfer instructions only */
459 #define ENCODE_RN(register_index) ((register_index) << 16)
460 #define ENCODE_RD(register_index) ((register_index) << 12)
461 #define ENCODE_SET_CONDITION_CODES (1 << 20)
463 /* Note: For data processing instructions, "1" means immediate.
464 Note: For single data transfer instructions, "0" means immediate. */
465 #define ENCODE_IMMEDIATE_FLAG (1 << 25)
467 #define ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER (1 << 4)
468 #define ENCODE_BARREL_SHIFTER_MODE_LSL (0 << 5)
469 #define ENCODE_BARREL_SHIFTER_MODE_LSR (1 << 5)
470 #define ENCODE_BARREL_SHIFTER_MODE_ASR (2 << 5)
471 #define ENCODE_BARREL_SHIFTER_MODE_ROR (3 << 5)
472 #define ENCODE_BARREL_SHIFTER_REGISTER(register_index) ((register_index) << 8)
473 #define ENCODE_BARREL_SHIFTER_IMMEDIATE(value) ((value) << 7)
475 static void asm_block_data_transfer_opcode(TCCState
*s1
, int token
)
481 parse_operand(s1
, &ops
[0]);
487 next(); // skip comma
488 parse_operand(s1
, &ops
[1]);
492 expect("at least one operand");
494 } else if (ops
[nb_ops
- 1].type
!= OP_REGSET32
) {
495 expect("(last operand) register list");
499 // block data transfer: 1 0 0 P U S W L << 20 (general case):
501 // Rn: bits 19...16 base register
502 // Register List: bits 15...0
504 switch (ARM_INSTRUCTION_GROUP(token
)) {
505 case TOK_ASM_pusheq
: // TODO: Optimize 1-register case to: str ?, [sp, #-4]!
506 // Instruction: 1 I=0 P=1 U=0 S=0 W=1 L=0 << 20, op 1101
509 // Register List: bits 15...0
511 expect("exactly one operand");
513 asm_emit_opcode(token
, (0x92d << 16) | ops
[0].regset
); // TODO: base register ?
515 case TOK_ASM_popeq
: // TODO: Optimize 1-register case to: ldr ?, [sp], #4
516 // Instruction: 1 I=0 P=0 U=1 S=0 W=0 L=1 << 20, op 1101
519 // Register List: bits 15...0
521 expect("exactly one operand");
523 asm_emit_opcode(token
, (0x8bd << 16) | ops
[0].regset
); // TODO: base register ?
525 case TOK_ASM_stmdaeq
:
526 case TOK_ASM_ldmdaeq
:
529 case TOK_ASM_stmiaeq
:
530 case TOK_ASM_ldmiaeq
:
531 case TOK_ASM_stmdbeq
:
532 case TOK_ASM_ldmdbeq
:
533 case TOK_ASM_stmibeq
:
534 case TOK_ASM_ldmibeq
:
535 switch (ARM_INSTRUCTION_GROUP(token
)) {
536 case TOK_ASM_stmdaeq
: // post-decrement store
539 case TOK_ASM_ldmdaeq
: // post-decrement load
542 case TOK_ASM_stmeq
: // post-increment store
543 case TOK_ASM_stmiaeq
: // post-increment store
546 case TOK_ASM_ldmeq
: // post-increment load
547 case TOK_ASM_ldmiaeq
: // post-increment load
550 case TOK_ASM_stmdbeq
: // pre-decrement store
553 case TOK_ASM_ldmdbeq
: // pre-decrement load
556 case TOK_ASM_stmibeq
: // pre-increment store
559 case TOK_ASM_ldmibeq
: // pre-increment load
563 tcc_error("internal error: This place should not be reached (fallback in asm_block_data_transfer_opcode)");
567 // Register List: lower bits
569 expect("exactly two operands");
570 else if (ops
[0].type
!= OP_REG32
)
571 expect("(first operand) register");
574 opcode
|= 1 << 21; // writeback
575 asm_emit_opcode(token
, opcode
| ENCODE_RN(ops
[0].reg
) | ops
[1].regset
);
579 expect("block data transfer instruction");
583 /* Parses shift directive and returns the parts that would have to be set in the opcode because of it.
584 Does not encode the actual shift amount.
585 It's not an error if there is no shift directive.
587 NB_SHIFT: will be set to 1 iff SHIFT is filled. Note that for rrx, there's no need to fill SHIFT.
588 SHIFT: will be filled in with the shift operand to use, if any. */
589 static uint32_t asm_parse_optional_shift(TCCState
* s1
, int* nb_shift
, Operand
* shift
)
603 opcode
= ENCODE_BARREL_SHIFTER_MODE_LSL
;
606 opcode
= ENCODE_BARREL_SHIFTER_MODE_ASR
;
609 opcode
= ENCODE_BARREL_SHIFTER_MODE_LSR
;
612 opcode
= ENCODE_BARREL_SHIFTER_MODE_ROR
;
616 parse_operand(s1
, shift
);
621 opcode
= ENCODE_BARREL_SHIFTER_MODE_ROR
;
627 static uint32_t asm_encode_shift(Operand
* shift
)
630 uint32_t operands
= 0;
631 switch (shift
->type
) {
633 if (shift
->reg
== 15)
634 tcc_error("r15 cannot be used as a shift count");
636 operands
= ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER
;
637 operands
|= ENCODE_BARREL_SHIFTER_REGISTER(shift
->reg
);
642 if (amount
> 0 && amount
< 32)
643 operands
= ENCODE_BARREL_SHIFTER_IMMEDIATE(amount
);
645 tcc_error("shift count out of range");
648 tcc_error("unknown shift amount");
653 static void asm_data_processing_opcode(TCCState
*s1
, int token
)
659 uint32_t operands
= 0;
661 /* modulo 16 entries per instruction for the different condition codes */
662 uint32_t opcode_idx
= (ARM_INSTRUCTION_GROUP(token
) - TOK_ASM_andeq
) >> 4;
663 uint32_t opcode_nos
= opcode_idx
>> 1; // without "s"; "OpCode" in ARM docs
665 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ) {
666 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
)
668 parse_operand(s1
, &ops
[nb_ops
]);
676 operands
|= asm_parse_optional_shift(s1
, &nb_shift
, &shift
);
678 expect("at least two operands");
679 else if (nb_ops
== 2) {
680 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
681 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
683 } else if (nb_ops
== 3) {
684 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
685 tcc_error("'%s' cannot be used with three operands", get_tok_str(token
, NULL
));
690 expect("two or three operands");
694 uint32_t immediate_value
;
695 uint8_t half_immediate_rotation
;
696 if (nb_shift
&& shift
.type
== OP_REG32
) {
697 if ((ops
[0].type
== OP_REG32
&& ops
[0].reg
== 15) ||
698 (ops
[1].type
== OP_REG32
&& ops
[1].reg
== 15)) {
699 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
704 // data processing (general case):
706 // Rn: bits 19...16 (first operand)
707 // Rd: bits 15...12 (destination)
708 // Operand2: bits 11...0 (second operand); depending on I that's either a register or an immediate
710 // bits 24...21: "OpCode"--see below
712 /* operations in the token list are ordered by opcode */
713 opcode
= opcode_nos
<< 21; // drop "s"
714 if (ops
[0].type
!= OP_REG32
)
715 expect("(destination operand) register");
716 else if (opcode_nos
== 0xa || opcode_nos
== 0xb || opcode_nos
== 0x8 || opcode_nos
== 0x9) // cmp, cmn, tst, teq
717 operands
|= ENCODE_SET_CONDITION_CODES
; // force S set, otherwise it's a completely different instruction.
719 operands
|= ENCODE_RD(ops
[0].reg
);
720 if (ops
[1].type
!= OP_REG32
)
721 expect("(first source operand) register");
722 else if (!(opcode_nos
== 0xd || opcode_nos
== 0xf)) // not: mov, mvn (those have only one source operand)
723 operands
|= ENCODE_RN(ops
[1].reg
);
724 switch (ops
[2].type
) {
726 operands
|= ops
[2].reg
;
730 operands
|= ENCODE_IMMEDIATE_FLAG
;
731 immediate_value
= ops
[2].e
.v
;
732 for (half_immediate_rotation
= 0; half_immediate_rotation
< 16; ++half_immediate_rotation
) {
733 if (immediate_value
>= 0x00 && immediate_value
< 0x100)
735 // rotate left by two
736 immediate_value
= ((immediate_value
& 0x3FFFFFFF) << 2) | ((immediate_value
& 0xC0000000) >> 30);
738 if (half_immediate_rotation
>= 16) {
741 operands
|= immediate_value
;
742 operands
|= half_immediate_rotation
<< 8;
745 case OP_IM8N
: // immediate negative value
746 operands
|= ENCODE_IMMEDIATE_FLAG
;
747 immediate_value
= ops
[2].e
.v
;
748 /* Instruction swapping:
749 0001 = EOR - Rd:= Op1 EOR Op2 -> difficult
750 0011 = RSB - Rd:= Op2 - Op1 -> difficult
751 0111 = RSC - Rd:= Op2 - Op1 + C -> difficult
752 1000 = TST - CC on: Op1 AND Op2 -> difficult
753 1001 = TEQ - CC on: Op1 EOR Op2 -> difficult
754 1100 = ORR - Rd:= Op1 OR Op2 -> difficult
756 switch (opcode_nos
) {
757 case 0x0: // AND - Rd:= Op1 AND Op2
758 opcode
= 0xe << 21; // BIC
759 immediate_value
= ~immediate_value
;
761 case 0x2: // SUB - Rd:= Op1 - Op2
762 opcode
= 0x4 << 21; // ADD
763 immediate_value
= -immediate_value
;
765 case 0x4: // ADD - Rd:= Op1 + Op2
766 opcode
= 0x2 << 21; // SUB
767 immediate_value
= -immediate_value
;
769 case 0x5: // ADC - Rd:= Op1 + Op2 + C
770 opcode
= 0x6 << 21; // SBC
771 immediate_value
= ~immediate_value
;
773 case 0x6: // SBC - Rd:= Op1 - Op2 + C
774 opcode
= 0x5 << 21; // ADC
775 immediate_value
= ~immediate_value
;
777 case 0xa: // CMP - CC on: Op1 - Op2
778 opcode
= 0xb << 21; // CMN
779 immediate_value
= -immediate_value
;
781 case 0xb: // CMN - CC on: Op1 + Op2
782 opcode
= 0xa << 21; // CMP
783 immediate_value
= -immediate_value
;
785 case 0xd: // MOV - Rd:= Op2
786 opcode
= 0xf << 21; // MVN
787 immediate_value
= ~immediate_value
;
789 case 0xe: // BIC - Rd:= Op1 AND NOT Op2
790 opcode
= 0x0 << 21; // AND
791 immediate_value
= ~immediate_value
;
793 case 0xf: // MVN - Rd:= NOT Op2
794 opcode
= 0xd << 21; // MOV
795 immediate_value
= ~immediate_value
;
798 tcc_error("cannot use '%s' with a negative immediate value", get_tok_str(token
, NULL
));
800 for (half_immediate_rotation
= 0; half_immediate_rotation
< 16; ++half_immediate_rotation
) {
801 if (immediate_value
>= 0x00 && immediate_value
< 0x100)
803 // rotate left by two
804 immediate_value
= ((immediate_value
& 0x3FFFFFFF) << 2) | ((immediate_value
& 0xC0000000) >> 30);
806 if (half_immediate_rotation
>= 16) {
807 immediate_value
= ops
[2].e
.v
;
808 tcc_error("immediate value 0x%X cannot be encoded into ARM immediate", (unsigned) immediate_value
);
811 operands
|= immediate_value
;
812 operands
|= half_immediate_rotation
<< 8;
815 expect("(second source operand) register or immediate value");
819 if (operands
& ENCODE_IMMEDIATE_FLAG
)
820 tcc_error("immediate rotation not implemented");
822 operands
|= asm_encode_shift(&shift
);
825 /* S=0 and S=1 entries alternate one after another, in that order */
826 opcode
|= (opcode_idx
& 1) ? ENCODE_SET_CONDITION_CODES
: 0;
827 asm_emit_opcode(token
, opcode
| operands
);
831 static void asm_shift_opcode(TCCState
*s1
, int token
)
835 int definitely_neutral
= 0;
836 uint32_t opcode
= 0xd << 21; // MOV
837 uint32_t operands
= 0;
839 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
840 parse_operand(s1
, &ops
[nb_ops
]);
848 expect("at least two operands");
852 if (ops
[0].type
!= OP_REG32
) {
853 expect("(destination operand) register");
856 operands
|= ENCODE_RD(ops
[0].reg
);
859 switch (ARM_INSTRUCTION_GROUP(token
)) {
861 opcode
|= ENCODE_SET_CONDITION_CODES
;
864 if (ops
[1].type
== OP_REG32
) {
865 operands
|= ops
[1].reg
;
866 operands
|= ENCODE_BARREL_SHIFTER_MODE_ROR
;
867 asm_emit_opcode(token
, opcode
| operands
);
869 tcc_error("(first source operand) register");
872 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
873 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
878 expect("two or three operands");
882 switch (ARM_INSTRUCTION_GROUP(token
)) {
887 opcode
|= ENCODE_SET_CONDITION_CODES
;
891 switch (ops
[1].type
) {
893 operands
|= ops
[1].reg
;
896 operands
|= ENCODE_IMMEDIATE_FLAG
;
897 operands
|= ops
[1].e
.v
;
898 tcc_error("Using an immediate value as the source operand is not possible with '%s' instruction on ARM", get_tok_str(token
, NULL
));
902 switch (ops
[2].type
) {
904 if ((ops
[0].type
== OP_REG32
&& ops
[0].reg
== 15) ||
905 (ops
[1].type
== OP_REG32
&& ops
[1].reg
== 15)) {
906 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
908 operands
|= asm_encode_shift(&ops
[2]);
912 operands
|= asm_encode_shift(&ops
[2]);
914 definitely_neutral
= 1;
918 if (!definitely_neutral
) switch (ARM_INSTRUCTION_GROUP(token
)) {
921 operands
|= ENCODE_BARREL_SHIFTER_MODE_LSL
;
925 operands
|= ENCODE_BARREL_SHIFTER_MODE_LSR
;
929 operands
|= ENCODE_BARREL_SHIFTER_MODE_ASR
;
933 operands
|= ENCODE_BARREL_SHIFTER_MODE_ROR
;
936 expect("shift instruction");
939 asm_emit_opcode(token
, opcode
| operands
);
942 static void asm_multiplication_opcode(TCCState
*s1
, int token
)
946 uint32_t opcode
= 0x90;
948 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
949 parse_operand(s1
, &ops
[nb_ops
]);
957 expect("at least two operands");
958 else if (nb_ops
== 2) {
959 switch (ARM_INSTRUCTION_GROUP(token
)) {
962 memcpy(&ops
[2], &ops
[0], sizeof(ops
[1])); // ARM is actually like this!
965 expect("at least three operands");
971 // multiply (special case):
978 if (ops
[0].type
== OP_REG32
)
979 opcode
|= ops
[0].reg
<< 16;
981 expect("(destination operand) register");
982 if (ops
[1].type
== OP_REG32
)
983 opcode
|= ops
[1].reg
;
985 expect("(first source operand) register");
986 if (ops
[2].type
== OP_REG32
)
987 opcode
|= ops
[2].reg
<< 8;
989 expect("(second source operand) register");
991 if (ops
[3].type
== OP_REG32
)
992 opcode
|= ops
[3].reg
<< 12;
994 expect("(third source operand) register");
997 switch (ARM_INSTRUCTION_GROUP(token
)) {
999 opcode
|= 1 << 20; // Status
1003 expect("three operands");
1005 asm_emit_opcode(token
, opcode
);
1008 case TOK_ASM_mlaseq
:
1009 opcode
|= 1 << 20; // Status
1013 expect("four operands");
1015 opcode
|= 1 << 21; // Accumulate
1016 asm_emit_opcode(token
, opcode
);
1020 expect("known multiplication instruction");
1024 static void asm_long_multiplication_opcode(TCCState
*s1
, int token
)
1028 uint32_t opcode
= 0x90 | (1 << 23);
1030 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
1031 parse_operand(s1
, &ops
[nb_ops
]);
1039 expect("four operands");
1043 // long multiply (special case):
1045 // RdLo: bits 15...12
1046 // RdHi: bits 19...16
1050 if (ops
[0].type
== OP_REG32
)
1051 opcode
|= ops
[0].reg
<< 12;
1053 expect("(destination lo accumulator) register");
1054 if (ops
[1].type
== OP_REG32
)
1055 opcode
|= ops
[1].reg
<< 16;
1057 expect("(destination hi accumulator) register");
1058 if (ops
[2].type
== OP_REG32
)
1059 opcode
|= ops
[2].reg
;
1061 expect("(first source operand) register");
1062 if (ops
[3].type
== OP_REG32
)
1063 opcode
|= ops
[3].reg
<< 8;
1065 expect("(second source operand) register");
1067 switch (ARM_INSTRUCTION_GROUP(token
)) {
1068 case TOK_ASM_smullseq
:
1069 opcode
|= 1 << 20; // Status
1071 case TOK_ASM_smulleq
:
1072 opcode
|= 1 << 22; // signed
1073 asm_emit_opcode(token
, opcode
);
1075 case TOK_ASM_umullseq
:
1076 opcode
|= 1 << 20; // Status
1078 case TOK_ASM_umulleq
:
1079 asm_emit_opcode(token
, opcode
);
1081 case TOK_ASM_smlalseq
:
1082 opcode
|= 1 << 20; // Status
1084 case TOK_ASM_smlaleq
:
1085 opcode
|= 1 << 22; // signed
1086 opcode
|= 1 << 21; // Accumulate
1087 asm_emit_opcode(token
, opcode
);
1089 case TOK_ASM_umlalseq
:
1090 opcode
|= 1 << 20; // Status
1092 case TOK_ASM_umlaleq
:
1093 opcode
|= 1 << 21; // Accumulate
1094 asm_emit_opcode(token
, opcode
);
1097 expect("known long multiplication instruction");
1101 static void asm_single_data_transfer_opcode(TCCState
*s1
, int token
)
1104 Operand strex_operand
;
1108 int closed_bracket
= 0;
1110 uint32_t opcode
= 0;
1111 // Note: ldr r0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1112 // Note: ldr r0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1113 // Note: ldr r0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1115 parse_operand(s1
, &ops
[0]);
1116 if (ops
[0].type
== OP_REG32
)
1117 opcode
|= ENCODE_RD(ops
[0].reg
);
1119 expect("(destination operand) register");
1123 expect("at least two arguments");
1127 switch (ARM_INSTRUCTION_GROUP(token
)) {
1128 case TOK_ASM_strexbeq
:
1129 case TOK_ASM_strexeq
:
1130 parse_operand(s1
, &strex_operand
);
1131 if (strex_operand
.type
!= OP_REG32
) {
1136 expect("at least three arguments");
1147 parse_operand(s1
, &ops
[1]);
1148 if (ops
[1].type
== OP_REG32
)
1149 opcode
|= ENCODE_RN(ops
[1].reg
);
1151 expect("(first source operand) register");
1157 // exclam = 1; // implicit in hardware; don't do it in software
1165 parse_operand(s1
, &ops
[2]);
1166 if (ops
[2].type
== OP_REG32
) {
1167 if (ops
[2].reg
== 15) {
1168 tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token
, NULL
));
1173 opcode
|= asm_parse_optional_shift(s1
, &nb_shift
, &shift
);
1175 expect("shift directive, or no comma");
1179 // end of input expression in brackets--assume 0 offset
1180 ops
[2].type
= OP_IM8
;
1182 opcode
|= 1 << 24; // add offset before transfer
1184 if (!closed_bracket
) {
1189 opcode
|= 1 << 24; // add offset before transfer
1196 // single data transfer: 0 1 I P U B W L << 20 (general case):
1198 // Rd: destination operand [ok]
1199 // Rn: first source operand [ok]
1200 // Operand2: bits 11...0 [ok]
1201 // I: immediate operand? [ok]
1202 // P: Pre/post indexing is PRE: Add offset before transfer [ok]
1203 // U: Up/down is up? (*adds* offset to base) [ok]
1204 // B: Byte/word is byte? [ok]
1205 // W: Write address back into base? [ok]
1206 // L: Load/store is load? [ok]
1208 opcode
|= 1 << 21; // write offset back into register
1210 if (ops
[2].type
== OP_IM32
|| ops
[2].type
== OP_IM8
|| ops
[2].type
== OP_IM8N
) {
1213 tcc_error("minus before '#' not supported for immediate values");
1215 opcode
|= 1 << 23; // up
1217 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
1222 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
1226 } else if (ops
[2].type
== OP_REG32
) {
1228 opcode
|= 1 << 23; // up
1229 opcode
|= ENCODE_IMMEDIATE_FLAG
; /* if set, it means it's NOT immediate */
1230 opcode
|= ops
[2].reg
;
1234 switch (ARM_INSTRUCTION_GROUP(token
)) {
1235 case TOK_ASM_strbeq
:
1236 opcode
|= 1 << 22; // B
1239 opcode
|= 1 << 26; // Load/Store
1241 opcode
|= asm_encode_shift(&shift
);
1242 asm_emit_opcode(token
, opcode
);
1244 case TOK_ASM_ldrbeq
:
1245 opcode
|= 1 << 22; // B
1248 opcode
|= 1 << 20; // L
1249 opcode
|= 1 << 26; // Load/Store
1251 opcode
|= asm_encode_shift(&shift
);
1252 asm_emit_opcode(token
, opcode
);
1254 case TOK_ASM_strexbeq
:
1255 opcode
|= 1 << 22; // B
1257 case TOK_ASM_strexeq
:
1258 if ((opcode
& 0xFFF) || nb_shift
) {
1259 tcc_error("neither offset nor shift allowed with 'strex'");
1261 } else if (opcode
& ENCODE_IMMEDIATE_FLAG
) { // if set, it means it's NOT immediate
1262 tcc_error("offset not allowed with 'strex'");
1265 if ((opcode
& (1 << 24)) == 0) { // add offset after transfer
1266 tcc_error("adding offset after transfer not allowed with 'strex'");
1270 opcode
|= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL
1271 opcode
|= strex_operand
.reg
;
1272 asm_emit_opcode(token
, opcode
);
1274 case TOK_ASM_ldrexbeq
:
1275 opcode
|= 1 << 22; // B
1277 case TOK_ASM_ldrexeq
:
1278 if ((opcode
& 0xFFF) || nb_shift
) {
1279 tcc_error("neither offset nor shift allowed with 'ldrex'");
1281 } else if (opcode
& ENCODE_IMMEDIATE_FLAG
) { // if set, it means it's NOT immediate
1282 tcc_error("offset not allowed with 'ldrex'");
1285 if ((opcode
& (1 << 24)) == 0) { // add offset after transfer
1286 tcc_error("adding offset after transfer not allowed with 'ldrex'");
1289 opcode
|= 1 << 20; // L
1291 opcode
|= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL
1292 asm_emit_opcode(token
, opcode
);
1295 expect("data transfer instruction");
1299 // 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)
1300 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
) {
1301 uint32_t opcode
= 0x0;
1302 opcode
|= 1 << 26; // Load/Store
1303 opcode
|= 1 << 27; // coprocessor
1306 opcode
|= 1 << 22; // long transfer
1309 opcode
|= 1 << 20; // L
1311 opcode
|= cp_number
<< 8;
1314 opcode
|= ENCODE_RD(CRd
);
1316 if (Rn
->type
!= OP_REG32
) {
1320 //assert(Rn->reg < 16);
1321 opcode
|= ENCODE_RN(Rn
->reg
);
1323 opcode
|= 1 << 24; // add offset before transfer
1326 opcode
|= 1 << 21; // write offset back into register
1328 if (offset
->type
== OP_IM8
|| offset
->type
== OP_IM8N
|| offset
->type
== OP_IM32
) {
1329 int v
= offset
->e
.v
;
1331 tcc_error("minus before '#' not supported for immediate values");
1332 if (offset
->type
== OP_IM8N
|| v
< 0)
1335 opcode
|= 1 << 23; // up
1337 tcc_error("immediate offset must be a multiple of 4");
1342 tcc_error("immediate offset must be between -1020 and 1020");
1346 } else if (offset
->type
== OP_REG32
) {
1348 opcode
|= 1 << 23; // up
1349 opcode
|= ENCODE_IMMEDIATE_FLAG
; /* if set, it means it's NOT immediate */
1350 opcode
|= offset
->reg
;
1351 tcc_error("Using register offset to register address is not possible here");
1353 } else if (offset
->type
== OP_VREG64
) {
1355 opcode
|= offset
->reg
;
1357 expect("immediate or register");
1359 asm_emit_unconditional_opcode((high_nibble
<< 28) | opcode
);
1362 // Almost exactly the same as asm_single_data_transfer_opcode.
1363 // Difference: Offsets are smaller and multiples of 4; no shifts, no STREX, ENCODE_IMMEDIATE_FLAG is inverted again.
1364 static void asm_coprocessor_data_transfer_opcode(TCCState
*s1
, int token
)
1367 uint8_t coprocessor
;
1368 uint8_t coprocessor_destination_register
;
1369 int preincrement
= 0;
1371 int closed_bracket
= 0;
1373 int long_transfer
= 0;
1374 // Note: ldc p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1375 // Note: ldc p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1376 // Note: ldc p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1378 if (tok
>= TOK_ASM_p0
&& tok
<= TOK_ASM_p15
) {
1379 coprocessor
= tok
- TOK_ASM_p0
;
1382 expect("'c<number>'");
1391 if (tok
>= TOK_ASM_c0
&& tok
<= TOK_ASM_c15
) {
1392 coprocessor_destination_register
= tok
- TOK_ASM_c0
;
1395 expect("'c<number>'");
1409 parse_operand(s1
, &ops
[1]);
1410 if (ops
[1].type
!= OP_REG32
) {
1411 expect("(first source operand) register");
1417 // exclam = 1; // implicit in hardware; don't do it in software
1425 parse_operand(s1
, &ops
[2]);
1426 if (ops
[2].type
== OP_REG32
) {
1427 if (ops
[2].reg
== 15) {
1428 tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token
, NULL
));
1431 } else if (ops
[2].type
== OP_VREG64
) {
1432 tcc_error("'%s' does not support VFP register operand", get_tok_str(token
, NULL
));
1436 // end of input expression in brackets--assume 0 offset
1437 ops
[2].type
= OP_IM8
;
1439 preincrement
= 1; // add offset before transfer
1441 if (!closed_bracket
) {
1446 preincrement
= 1; // add offset before transfer
1453 // TODO: Support options.
1455 if (token
== TOK_ASM_ldc2
|| token
== TOK_ASM_stc2
|| token
== TOK_ASM_ldc2l
|| token
== TOK_ASM_stc2l
) {
1458 long_transfer
= 1; // long transfer
1461 asm_emit_coprocessor_data_transfer(0xF, coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], op2_minus
, preincrement
, exclam
, long_transfer
, 1);
1464 long_transfer
= 1; // long transfer
1467 asm_emit_coprocessor_data_transfer(0xF, coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], op2_minus
, preincrement
, exclam
, long_transfer
, 0);
1470 } else switch (ARM_INSTRUCTION_GROUP(token
)) {
1471 case TOK_ASM_stcleq
:
1475 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);
1477 case TOK_ASM_ldcleq
:
1481 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);
1484 expect("coprocessor data transfer instruction");
1488 #if defined(TCC_ARM_VFP)
1489 #define CP_SINGLE_PRECISION_FLOAT 10
1490 #define CP_DOUBLE_PRECISION_FLOAT 11
1492 static void asm_floating_point_single_data_transfer_opcode(TCCState
*s1
, int token
)
1495 uint8_t coprocessor
= 0;
1496 uint8_t coprocessor_destination_register
= 0;
1497 int long_transfer
= 0;
1498 // Note: vldr p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1499 // Note: Not allowed: vldr p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1500 // Note: Not allowed: vldr p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1502 parse_operand(s1
, &ops
[0]);
1503 if (ops
[0].type
== OP_VREG32
) {
1504 coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
1505 coprocessor_destination_register
= ops
[0].reg
;
1506 long_transfer
= coprocessor_destination_register
& 1;
1507 coprocessor_destination_register
>>= 1;
1508 } else if (ops
[0].type
== OP_VREG64
) {
1509 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
1510 coprocessor_destination_register
= ops
[0].reg
;
1513 expect("floating point register");
1527 parse_operand(s1
, &ops
[1]);
1528 if (ops
[1].type
!= OP_REG32
) {
1529 expect("(first source operand) register");
1534 parse_operand(s1
, &ops
[2]);
1535 if (ops
[2].type
!= OP_IM8
&& ops
[2].type
!= OP_IM8N
) {
1536 expect("immediate offset");
1540 // end of input expression in brackets--assume 0 offset
1541 ops
[2].type
= OP_IM8
;
1549 switch (ARM_INSTRUCTION_GROUP(token
)) {
1550 case TOK_ASM_vldreq
:
1551 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], 0, 1, 0, long_transfer
, 1);
1553 case TOK_ASM_vstreq
:
1554 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], 0, 1, 0, long_transfer
, 0);
1557 expect("floating point data transfer instruction");
1561 static void asm_floating_point_block_data_transfer_opcode(TCCState
*s1
, int token
)
1563 uint8_t coprocessor
= 0;
1564 int first_regset_register
;
1565 int last_regset_register
;
1566 uint8_t regset_item_count
;
1567 uint8_t extra_register_bit
= 0;
1570 int preincrement
= 0;
1573 switch (ARM_INSTRUCTION_GROUP(token
)) {
1574 case TOK_ASM_vpusheq
:
1575 case TOK_ASM_vpopeq
:
1576 ops
[0].type
= OP_REG32
;
1577 ops
[0].reg
= 13; // sp
1581 parse_operand(s1
, &ops
[0]);
1587 next(); // skip comma
1599 first_regset_register
= asm_parse_vfp_regvar(tok
, 1);
1600 if ((first_regset_register
= asm_parse_vfp_regvar(tok
, 1)) != -1) {
1601 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
1603 } else if ((first_regset_register
= asm_parse_vfp_regvar(tok
, 0)) != -1) {
1604 coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
1607 expect("floating-point register");
1613 if ((last_regset_register
= asm_parse_vfp_regvar(tok
, coprocessor
== CP_DOUBLE_PRECISION_FLOAT
)) != -1)
1616 expect("floating-point register");
1620 last_regset_register
= first_regset_register
;
1622 if (last_regset_register
< first_regset_register
) {
1623 tcc_error("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
1632 // Note: 0 (one down) is not implemented by us regardless.
1633 regset_item_count
= last_regset_register
- first_regset_register
+ 1;
1634 if (coprocessor
== CP_DOUBLE_PRECISION_FLOAT
)
1635 regset_item_count
<<= 1;
1637 extra_register_bit
= first_regset_register
& 1;
1638 first_regset_register
>>= 1;
1640 offset
.type
= OP_IM8
;
1641 offset
.e
.v
= regset_item_count
<< 2;
1642 switch (ARM_INSTRUCTION_GROUP(token
)) {
1643 case TOK_ASM_vstmeq
: // post-increment store
1644 case TOK_ASM_vstmiaeq
: // post-increment store
1646 case TOK_ASM_vpopeq
:
1647 case TOK_ASM_vldmeq
: // post-increment load
1648 case TOK_ASM_vldmiaeq
: // post-increment load
1651 case TOK_ASM_vldmdbeq
: // pre-decrement load
1654 case TOK_ASM_vpusheq
:
1655 case TOK_ASM_vstmdbeq
: // pre-decrement store
1656 offset
.type
= OP_IM8N
;
1657 offset
.e
.v
= -offset
.e
.v
;
1661 expect("floating point block data transfer instruction");
1664 if (ops
[0].type
!= OP_REG32
)
1665 expect("(first operand) register");
1666 else if (ops
[0].reg
== 15)
1667 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
1668 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
)
1669 tcc_error("first operand of '%s' should have an exclamation mark", get_tok_str(token
, NULL
));
1671 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
);
1674 #define VMOV_FRACTIONAL_DIGITS 7
1675 #define VMOV_ONE 10000000 /* pow(10, VMOV_FRACTIONAL_DIGITS) */
1677 static uint32_t vmov_parse_fractional_part(const char* s
)
1679 uint32_t result
= 0;
1681 for (i
= 0; i
< VMOV_FRACTIONAL_DIGITS
; ++i
) {
1684 if (c
>= '0' && c
<= '9') {
1685 result
+= (c
- '0');
1690 expect("decimal numeral");
1694 static int vmov_linear_approx_index(uint32_t beginning
, uint32_t end
, uint32_t value
)
1700 k
= (end
- beginning
)/16;
1701 for (xvalue
= beginning
, i
= 0; i
< 16; ++i
, xvalue
+= k
) {
1702 if (value
== xvalue
)
1709 static uint32_t vmov_parse_immediate_value() {
1711 unsigned long integral_value
;
1714 if (tok
!= TOK_PPNUM
) {
1715 expect("immediate value");
1720 integral_value
= strtoul(p
, (char **)&p
, 0);
1722 if (errno
|| integral_value
>= 32) {
1723 tcc_error("invalid floating-point immediate value");
1727 value
= (uint32_t) integral_value
* VMOV_ONE
;
1730 value
+= vmov_parse_fractional_part(p
);
1736 static uint8_t vmov_encode_immediate_value(uint32_t value
)
1740 uint32_t beginning
= 0;
1745 limit
= 32 * VMOV_ONE
;
1746 for (i
= 0; i
< 8; ++i
) {
1747 if (value
< limit
) {
1755 if (r
== -1 || value
< beginning
|| value
> end
) {
1756 tcc_error("invalid decimal number for vmov: %d", value
);
1759 n
= vmov_linear_approx_index(beginning
, end
, value
);
1760 return n
| (((3 - r
) & 0x7) << 4);
1764 static void asm_floating_point_immediate_data_processing_opcode_tail(TCCState
*s1
, int token
, uint8_t coprocessor
, uint8_t CRd
) {
1765 uint8_t opcode1
= 0;
1766 uint8_t opcode2
= 0;
1767 uint8_t operands
[3] = {0, 0, 0};
1768 uint32_t immediate_value
= 0;
1774 if (tok
== '#' || tok
== '$') {
1781 immediate_value
= vmov_parse_immediate_value();
1783 opcode1
= 11; // "Other" instruction
1784 switch (ARM_INSTRUCTION_GROUP(token
)) {
1785 case TOK_ASM_vcmpeq_f32
:
1786 case TOK_ASM_vcmpeq_f64
:
1789 if (immediate_value
) {
1790 expect("Immediate value 0");
1794 case TOK_ASM_vcmpeeq_f32
:
1795 case TOK_ASM_vcmpeeq_f64
:
1798 if (immediate_value
) {
1799 expect("Immediate value 0");
1803 case TOK_ASM_vmoveq_f32
:
1804 case TOK_ASM_vmoveq_f64
:
1810 code
= vmov_encode_immediate_value(immediate_value
);
1811 operands
[1] |= code
>> 4;
1812 operands
[2] = code
& 0xF;
1815 expect("known floating point with immediate instruction");
1819 if (coprocessor
== CP_SINGLE_PRECISION_FLOAT
) {
1820 if (operands
[0] & 1)
1825 asm_emit_coprocessor_opcode(condition_code_of_token(token
), coprocessor
, opcode1
, operands
[0], operands
[1], operands
[2], opcode2
, 0);
1828 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]) {
1829 uint8_t opcode1
= 0;
1830 uint8_t opcode2
= 0;
1831 switch (coprocessor
) {
1832 case CP_SINGLE_PRECISION_FLOAT
:
1833 // "vmov.f32 r2, s3" or "vmov.f32 s3, r2"
1834 if (nb_ops
!= 2 || nb_arm_regs
!= 1) {
1835 tcc_error("vmov.f32 only implemented for one VFP register operand and one ARM register operands");
1838 if (ops
[0].type
!= OP_REG32
) { // determine mode: load or store
1839 // need to swap operands 0 and 1
1840 memcpy(&ops
[2], &ops
[1], sizeof(ops
[2]));
1841 memcpy(&ops
[1], &ops
[0], sizeof(ops
[1]));
1842 memcpy(&ops
[0], &ops
[2], sizeof(ops
[0]));
1846 if (ops
[1].type
== OP_VREG32
) {
1852 if (ops
[0].type
== OP_VREG32
) {
1858 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);
1860 case CP_DOUBLE_PRECISION_FLOAT
:
1861 if (nb_ops
!= 3 || nb_arm_regs
!= 2) {
1862 tcc_error("vmov.f32 only implemented for one VFP register operand and two ARM register operands");
1865 // Determine whether it's a store into a VFP register (vmov "d1, r2, r3") rather than "vmov r2, r3, d1"
1866 if (ops
[0].type
== OP_VREG64
) {
1867 if (ops
[2].type
== OP_REG32
) {
1869 // need to rotate operand list to the left
1870 memcpy(&temp
, &ops
[0], sizeof(temp
));
1871 memcpy(&ops
[0], &ops
[1], sizeof(ops
[0]));
1872 memcpy(&ops
[1], &ops
[2], sizeof(ops
[1]));
1873 memcpy(&ops
[2], &temp
, sizeof(ops
[2]));
1875 tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands");
1878 } else if (ops
[0].type
!= OP_REG32
|| ops
[1].type
!= OP_REG32
|| ops
[2].type
!= OP_VREG64
) {
1879 tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands");
1884 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, ops
[0].reg
, &ops
[1], &ops
[2], 0, 0, 0, 1, opcode1
);
1887 tcc_internal_error("unknown coprocessor");
1891 static void asm_floating_point_vcvt_data_processing_opcode(TCCState
*s1
, int token
) {
1892 uint8_t coprocessor
= 0;
1894 uint8_t opcode1
= 11;
1895 uint8_t opcode2
= 2;
1897 switch (ARM_INSTRUCTION_GROUP(token
)) {
1898 case TOK_ASM_vcvtreq_s32_f64
:
1899 case TOK_ASM_vcvtreq_u32_f64
:
1900 case TOK_ASM_vcvteq_s32_f64
:
1901 case TOK_ASM_vcvteq_u32_f64
:
1902 case TOK_ASM_vcvteq_f64_s32
:
1903 case TOK_ASM_vcvteq_f64_u32
:
1904 case TOK_ASM_vcvteq_f32_f64
:
1905 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
1907 case TOK_ASM_vcvtreq_s32_f32
:
1908 case TOK_ASM_vcvtreq_u32_f32
:
1909 case TOK_ASM_vcvteq_s32_f32
:
1910 case TOK_ASM_vcvteq_u32_f32
:
1911 case TOK_ASM_vcvteq_f32_s32
:
1912 case TOK_ASM_vcvteq_f32_u32
:
1913 case TOK_ASM_vcvteq_f64_f32
:
1914 coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
1917 tcc_error("Unknown coprocessor for instruction '%s'", get_tok_str(token
, NULL
));
1921 parse_operand(s1
, &ops
[0]);
1922 ops
[1].type
= OP_IM8
;
1924 /* floating-point -> integer */
1925 switch (ARM_INSTRUCTION_GROUP(token
)) {
1926 case TOK_ASM_vcvtreq_s32_f32
:
1927 case TOK_ASM_vcvtreq_s32_f64
:
1928 case TOK_ASM_vcvteq_s32_f32
:
1929 case TOK_ASM_vcvteq_s32_f64
:
1930 ops
[1].e
.v
|= 1; // signed
1932 case TOK_ASM_vcvteq_u32_f32
:
1933 case TOK_ASM_vcvteq_u32_f64
:
1934 case TOK_ASM_vcvtreq_u32_f32
:
1935 case TOK_ASM_vcvtreq_u32_f64
:
1936 ops
[1].e
.v
|= 4; // to_integer (opc2)
1938 /* floating-point size conversion */
1939 case TOK_ASM_vcvteq_f64_f32
:
1940 case TOK_ASM_vcvteq_f32_f64
:
1949 parse_operand(s1
, &ops
[2]);
1951 switch (ARM_INSTRUCTION_GROUP(token
)) {
1952 /* floating-point -> integer */
1953 case TOK_ASM_vcvteq_s32_f32
:
1954 case TOK_ASM_vcvteq_s32_f64
:
1955 case TOK_ASM_vcvteq_u32_f32
:
1956 case TOK_ASM_vcvteq_u32_f64
:
1957 opcode2
|= 4; // round_zero
1960 /* integer -> floating-point */
1961 case TOK_ASM_vcvteq_f64_s32
:
1962 case TOK_ASM_vcvteq_f32_s32
:
1963 opcode2
|= 4; // signed--special
1966 /* floating-point size conversion */
1967 case TOK_ASM_vcvteq_f64_f32
:
1968 case TOK_ASM_vcvteq_f32_f64
:
1969 opcode2
|= 4; // always set
1973 switch (ARM_INSTRUCTION_GROUP(token
)) {
1974 case TOK_ASM_vcvteq_f64_u32
:
1975 case TOK_ASM_vcvteq_f64_s32
:
1976 case TOK_ASM_vcvteq_f64_f32
:
1977 if (ops
[0].type
== OP_VREG64
&& ops
[2].type
== OP_VREG32
) {
1979 expect("d<number>, s<number>");
1984 if (coprocessor
== CP_SINGLE_PRECISION_FLOAT
) {
1985 if (ops
[0].type
== OP_VREG32
&& ops
[2].type
== OP_VREG32
) {
1987 expect("s<number>, s<number>");
1990 } else if (coprocessor
== CP_DOUBLE_PRECISION_FLOAT
) {
1991 if (ops
[0].type
== OP_VREG32
&& ops
[2].type
== OP_VREG64
) {
1993 expect("s<number>, d<number>");
1999 if (ops
[2].type
== OP_VREG32
) {
2004 if (ops
[0].type
== OP_VREG32
) {
2009 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);
2012 static void asm_floating_point_data_processing_opcode(TCCState
*s1
, int token
) {
2013 uint8_t coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
2014 uint8_t opcode1
= 0;
2015 uint8_t opcode2
= 0; // (0 || 2) | register selection
2019 int nb_arm_regs
= 0;
2022 Instruction opcode opcode2 Reason
2023 =============================================================
2024 - 1?00 ?1? Undefined
2025 VFNMS 1?01 ?0? Must be unconditional
2026 VFNMA 1?01 ?1? Must be unconditional
2027 VFMA 1?10 ?0? Must be unconditional
2028 VFMS 1?10 ?1? Must be unconditional
2039 switch (ARM_INSTRUCTION_GROUP(token
)) {
2040 case TOK_ASM_vmlaeq_f64
:
2041 case TOK_ASM_vmlseq_f64
:
2042 case TOK_ASM_vnmlseq_f64
:
2043 case TOK_ASM_vnmlaeq_f64
:
2044 case TOK_ASM_vmuleq_f64
:
2045 case TOK_ASM_vnmuleq_f64
:
2046 case TOK_ASM_vaddeq_f64
:
2047 case TOK_ASM_vsubeq_f64
:
2048 case TOK_ASM_vdiveq_f64
:
2049 case TOK_ASM_vnegeq_f64
:
2050 case TOK_ASM_vabseq_f64
:
2051 case TOK_ASM_vsqrteq_f64
:
2052 case TOK_ASM_vcmpeq_f64
:
2053 case TOK_ASM_vcmpeeq_f64
:
2054 case TOK_ASM_vmoveq_f64
:
2055 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
2058 switch (ARM_INSTRUCTION_GROUP(token
)) {
2059 case TOK_ASM_vmoveq_f32
:
2060 case TOK_ASM_vmoveq_f64
:
2065 for (nb_ops
= 0; nb_ops
< 3; ) {
2066 // Note: Necessary because parse_operand can't parse decimal numerals.
2067 if (nb_ops
== 1 && (tok
== '#' || tok
== '$' || tok
== TOK_PPNUM
|| tok
== '-')) {
2068 asm_floating_point_immediate_data_processing_opcode_tail(s1
, token
, coprocessor
, ops
[0].reg
);
2071 parse_operand(s1
, &ops
[nb_ops
]);
2072 if (vmov
&& ops
[nb_ops
].type
== OP_REG32
) {
2074 } else if (ops
[nb_ops
].type
== OP_VREG32
) {
2075 if (coprocessor
!= CP_SINGLE_PRECISION_FLOAT
) {
2076 expect("'s<number>'");
2079 } else if (ops
[nb_ops
].type
== OP_VREG64
) {
2080 if (coprocessor
!= CP_DOUBLE_PRECISION_FLOAT
) {
2081 expect("'d<number>'");
2085 expect("floating point register");
2095 if (nb_arm_regs
== 0) {
2096 if (nb_ops
== 2) { // implicit
2097 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
2098 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
2102 tcc_error("Not enough operands for '%s' (%u)", get_tok_str(token
, NULL
), nb_ops
);
2107 switch (ARM_INSTRUCTION_GROUP(token
)) {
2108 case TOK_ASM_vmlaeq_f32
:
2109 case TOK_ASM_vmlaeq_f64
:
2113 case TOK_ASM_vmlseq_f32
:
2114 case TOK_ASM_vmlseq_f64
:
2118 case TOK_ASM_vnmlseq_f32
:
2119 case TOK_ASM_vnmlseq_f64
:
2123 case TOK_ASM_vnmlaeq_f32
:
2124 case TOK_ASM_vnmlaeq_f64
:
2128 case TOK_ASM_vmuleq_f32
:
2129 case TOK_ASM_vmuleq_f64
:
2133 case TOK_ASM_vnmuleq_f32
:
2134 case TOK_ASM_vnmuleq_f64
:
2138 case TOK_ASM_vaddeq_f32
:
2139 case TOK_ASM_vaddeq_f64
:
2143 case TOK_ASM_vsubeq_f32
:
2144 case TOK_ASM_vsubeq_f64
:
2148 case TOK_ASM_vdiveq_f32
:
2149 case TOK_ASM_vdiveq_f64
:
2153 case TOK_ASM_vnegeq_f32
:
2154 case TOK_ASM_vnegeq_f64
:
2155 opcode1
= 11; // Other" instruction
2157 ops
[1].type
= OP_IM8
;
2160 case TOK_ASM_vabseq_f32
:
2161 case TOK_ASM_vabseq_f64
:
2162 opcode1
= 11; // "Other" instruction
2164 ops
[1].type
= OP_IM8
;
2167 case TOK_ASM_vsqrteq_f32
:
2168 case TOK_ASM_vsqrteq_f64
:
2169 opcode1
= 11; // "Other" instruction
2171 ops
[1].type
= OP_IM8
;
2174 case TOK_ASM_vcmpeq_f32
:
2175 case TOK_ASM_vcmpeq_f64
:
2176 opcode1
= 11; // "Other" instruction
2178 ops
[1].type
= OP_IM8
;
2181 case TOK_ASM_vcmpeeq_f32
:
2182 case TOK_ASM_vcmpeeq_f64
:
2183 opcode1
= 11; // "Other" instruction
2185 ops
[1].type
= OP_IM8
;
2188 case TOK_ASM_vmoveq_f32
:
2189 case TOK_ASM_vmoveq_f64
:
2190 if (nb_arm_regs
> 0) { // vmov.f32 r2, s3 or similar
2191 asm_floating_point_reg_arm_reg_transfer_opcode_tail(s1
, token
, coprocessor
, nb_arm_regs
, nb_ops
, ops
);
2194 opcode1
= 11; // "Other" instruction
2196 ops
[1].type
= OP_IM8
;
2201 expect("known floating point instruction");
2205 if (coprocessor
== CP_SINGLE_PRECISION_FLOAT
) {
2206 if (ops
[2].type
== OP_VREG32
) {
2212 if (ops
[1].type
== OP_VREG32
) {
2218 if (ops
[0].type
== OP_VREG32
) {
2225 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);
2228 static int asm_parse_vfp_status_regvar(int t
)
2242 static void asm_floating_point_status_register_opcode(TCCState
* s1
, int token
)
2244 uint8_t coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
2246 int vfp_sys_reg
= -1;
2247 Operand arm_operand
;
2248 switch (ARM_INSTRUCTION_GROUP(token
)) {
2249 case TOK_ASM_vmrseq
:
2251 if (tok
== TOK_ASM_apsr_nzcv
) {
2252 arm_operand
.type
= OP_REG32
;
2253 arm_operand
.reg
= 15; // not PC
2254 next(); // skip apsr_nzcv
2256 parse_operand(s1
, &arm_operand
);
2257 if (arm_operand
.type
== OP_REG32
&& arm_operand
.reg
== 15) {
2258 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
2267 vfp_sys_reg
= asm_parse_vfp_status_regvar(tok
);
2268 next(); // skip vfp sys reg
2269 if (arm_operand
.type
== OP_REG32
&& arm_operand
.reg
== 15 && vfp_sys_reg
!= 1) {
2270 tcc_error("'%s' only supports the variant 'vmrs apsr_nzcv, fpscr' here", get_tok_str(token
, NULL
));
2274 case TOK_ASM_vmsreq
:
2276 vfp_sys_reg
= asm_parse_vfp_status_regvar(tok
);
2277 next(); // skip vfp sys reg
2282 parse_operand(s1
, &arm_operand
);
2283 if (arm_operand
.type
== OP_REG32
&& arm_operand
.reg
== 15) {
2284 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
2289 expect("floating point status register instruction");
2292 if (vfp_sys_reg
== -1) {
2293 expect("VFP system register");
2296 if (arm_operand
.type
!= OP_REG32
) {
2297 expect("ARM register");
2300 asm_emit_coprocessor_opcode(condition_code_of_token(token
), coprocessor
, opcode
, arm_operand
.reg
, vfp_sys_reg
, 0x10, 0, 0);
2305 static void asm_misc_single_data_transfer_opcode(TCCState
*s1
, int token
)
2309 int closed_bracket
= 0;
2311 uint32_t opcode
= (1 << 7) | (1 << 4);
2314 The argument syntax is exactly the same as in arm_single_data_transfer_opcode, except that there's no STREX argument form.
2315 The main difference between this function and asm_misc_single_data_transfer_opcode is that the immediate values here must be smaller.
2316 Also, the combination (P=0, W=1) is unpredictable here.
2317 The immediate flag has moved to bit index 22--and its meaning has flipped.
2318 The immediate value itself has been split into two parts: one at bits 11...8, one at bits 3...0
2319 bit 26 (Load/Store instruction) is unset here.
2320 bits 7 and 4 are set here. */
2322 // Here: 0 0 0 P U I W L << 20
2323 // [compare single data transfer: 0 1 I P U B W L << 20]
2325 parse_operand(s1
, &ops
[0]);
2326 if (ops
[0].type
== OP_REG32
)
2327 opcode
|= ENCODE_RD(ops
[0].reg
);
2329 expect("(destination operand) register");
2333 expect("at least two arguments");
2342 parse_operand(s1
, &ops
[1]);
2343 if (ops
[1].type
== OP_REG32
)
2344 opcode
|= ENCODE_RN(ops
[1].reg
);
2346 expect("(first source operand) register");
2352 // exclam = 1; // implicit in hardware; don't do it in software
2360 parse_operand(s1
, &ops
[2]);
2362 // end of input expression in brackets--assume 0 offset
2363 ops
[2].type
= OP_IM8
;
2365 opcode
|= 1 << 24; // add offset before transfer
2367 if (!closed_bracket
) {
2372 opcode
|= 1 << 24; // add offset before transfer
2380 if ((opcode
& (1 << 24)) == 0) {
2381 tcc_error("result of '%s' would be unpredictable here", get_tok_str(token
, NULL
));
2384 opcode
|= 1 << 21; // write offset back into register
2387 if (ops
[2].type
== OP_IM32
|| ops
[2].type
== OP_IM8
|| ops
[2].type
== OP_IM8N
) {
2390 tcc_error("minus before '#' not supported for immediate values");
2392 opcode
|= 1 << 23; // up
2394 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
2396 // bits 11...8: immediate hi nibble
2397 // bits 3...0: immediate lo nibble
2398 opcode
|= (v
& 0xF0) << 4;
2403 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
2406 // bits 11...8: immediate hi nibble
2407 // bits 3...0: immediate lo nibble
2408 opcode
|= (v
& 0xF0) << 4;
2412 opcode
|= 1 << 22; // not ENCODE_IMMEDIATE_FLAG;
2413 } else if (ops
[2].type
== OP_REG32
) {
2415 opcode
|= 1 << 23; // up
2416 opcode
|= ops
[2].reg
;
2420 switch (ARM_INSTRUCTION_GROUP(token
)) {
2421 case TOK_ASM_ldrsheq
:
2422 opcode
|= 1 << 5; // halfword, not byte
2424 case TOK_ASM_ldrsbeq
:
2425 opcode
|= 1 << 6; // sign extend
2426 opcode
|= 1 << 20; // L
2427 asm_emit_opcode(token
, opcode
);
2429 case TOK_ASM_ldrheq
:
2430 opcode
|= 1 << 5; // halfword, not byte
2431 opcode
|= 1 << 20; // L
2432 asm_emit_opcode(token
, opcode
);
2434 case TOK_ASM_strheq
:
2435 opcode
|= 1 << 5; // halfword, not byte
2436 asm_emit_opcode(token
, opcode
);
2441 /* Note: almost dupe of encbranch in arm-gen.c */
2442 static uint32_t encbranchoffset(int pos
, int addr
, int fail
)
2446 if(addr
>=0x7fffff || addr
<-0x800000) {
2448 tcc_error("branch offset is too far");
2451 return /*not 0x0A000000|*/(addr
&0xffffff);
2454 static void asm_branch_opcode(TCCState
*s1
, int token
)
2461 switch (ARM_INSTRUCTION_GROUP(token
)) {
2465 esym
= elfsym(e
.sym
);
2466 if (!esym
|| esym
->st_shndx
!= cur_text_section
->sh_num
) {
2467 tcc_error("invalid branch target");
2470 jmp_disp
= encbranchoffset(ind
, e
.v
+ esym
->st_value
, 1);
2473 parse_operand(s1
, &op
);
2476 switch (ARM_INSTRUCTION_GROUP(token
)) {
2478 asm_emit_opcode(token
, (0xa << 24) | (jmp_disp
& 0xffffff));
2481 asm_emit_opcode(token
, (0xb << 24) | (jmp_disp
& 0xffffff));
2484 if (op
.type
!= OP_REG32
)
2487 asm_emit_opcode(token
, (0x12fff1 << 4) | op
.reg
);
2490 if (op
.type
!= OP_REG32
)
2493 asm_emit_opcode(token
, (0x12fff3 << 4) | op
.reg
);
2496 expect("branch instruction");
2500 ST_FUNC
void asm_opcode(TCCState
*s1
, int token
)
2502 while (token
== TOK_LINEFEED
) {
2506 if (token
== TOK_EOF
)
2508 if (token
< TOK_ASM_nopeq
) { // no condition code
2511 asm_coprocessor_opcode(s1
, token
);
2517 asm_coprocessor_data_transfer_opcode(s1
, token
);
2520 expect("instruction");
2525 switch (ARM_INSTRUCTION_GROUP(token
)) {
2526 case TOK_ASM_pusheq
:
2528 case TOK_ASM_stmdaeq
:
2529 case TOK_ASM_ldmdaeq
:
2532 case TOK_ASM_stmiaeq
:
2533 case TOK_ASM_ldmiaeq
:
2534 case TOK_ASM_stmdbeq
:
2535 case TOK_ASM_ldmdbeq
:
2536 case TOK_ASM_stmibeq
:
2537 case TOK_ASM_ldmibeq
:
2538 asm_block_data_transfer_opcode(s1
, token
);
2543 asm_nullary_opcode(token
);
2547 asm_unary_opcode(s1
, token
);
2553 asm_branch_opcode(s1
, token
);
2556 case TOK_ASM_sxtbeq
:
2557 case TOK_ASM_sxtheq
:
2558 case TOK_ASM_uxtbeq
:
2559 case TOK_ASM_uxtheq
:
2560 case TOK_ASM_movteq
:
2561 case TOK_ASM_movweq
:
2562 asm_binary_opcode(s1
, token
);
2566 case TOK_ASM_ldrbeq
:
2568 case TOK_ASM_strbeq
:
2569 case TOK_ASM_ldrexeq
:
2570 case TOK_ASM_ldrexbeq
:
2571 case TOK_ASM_strexeq
:
2572 case TOK_ASM_strexbeq
:
2573 asm_single_data_transfer_opcode(s1
, token
);
2576 case TOK_ASM_ldrheq
:
2577 case TOK_ASM_ldrsheq
:
2578 case TOK_ASM_ldrsbeq
:
2579 case TOK_ASM_strheq
:
2580 asm_misc_single_data_transfer_opcode(s1
, token
);
2599 case TOK_ASM_andseq
:
2600 case TOK_ASM_eorseq
:
2601 case TOK_ASM_subseq
:
2602 case TOK_ASM_rsbseq
:
2603 case TOK_ASM_addseq
:
2604 case TOK_ASM_adcseq
:
2605 case TOK_ASM_sbcseq
:
2606 case TOK_ASM_rscseq
:
2607 // case TOK_ASM_tstseq:
2608 // case TOK_ASM_teqseq:
2609 // case TOK_ASM_cmpseq:
2610 // case TOK_ASM_cmnseq:
2611 case TOK_ASM_orrseq
:
2612 case TOK_ASM_movseq
:
2613 case TOK_ASM_bicseq
:
2614 case TOK_ASM_mvnseq
:
2615 asm_data_processing_opcode(s1
, token
);
2619 case TOK_ASM_lslseq
:
2621 case TOK_ASM_lsrseq
:
2623 case TOK_ASM_asrseq
:
2625 case TOK_ASM_rorseq
:
2626 case TOK_ASM_rrxseq
:
2628 asm_shift_opcode(s1
, token
);
2632 case TOK_ASM_mulseq
:
2634 case TOK_ASM_mlaseq
:
2635 asm_multiplication_opcode(s1
, token
);
2638 case TOK_ASM_smulleq
:
2639 case TOK_ASM_smullseq
:
2640 case TOK_ASM_umulleq
:
2641 case TOK_ASM_umullseq
:
2642 case TOK_ASM_smlaleq
:
2643 case TOK_ASM_smlalseq
:
2644 case TOK_ASM_umlaleq
:
2645 case TOK_ASM_umlalseq
:
2646 asm_long_multiplication_opcode(s1
, token
);
2652 asm_coprocessor_opcode(s1
, token
);
2656 case TOK_ASM_ldcleq
:
2658 case TOK_ASM_stcleq
:
2659 asm_coprocessor_data_transfer_opcode(s1
, token
);
2662 #if defined(TCC_ARM_VFP)
2663 case TOK_ASM_vldreq
:
2664 case TOK_ASM_vstreq
:
2665 asm_floating_point_single_data_transfer_opcode(s1
, token
);
2668 case TOK_ASM_vmlaeq_f32
:
2669 case TOK_ASM_vmlseq_f32
:
2670 case TOK_ASM_vnmlseq_f32
:
2671 case TOK_ASM_vnmlaeq_f32
:
2672 case TOK_ASM_vmuleq_f32
:
2673 case TOK_ASM_vnmuleq_f32
:
2674 case TOK_ASM_vaddeq_f32
:
2675 case TOK_ASM_vsubeq_f32
:
2676 case TOK_ASM_vdiveq_f32
:
2677 case TOK_ASM_vnegeq_f32
:
2678 case TOK_ASM_vabseq_f32
:
2679 case TOK_ASM_vsqrteq_f32
:
2680 case TOK_ASM_vcmpeq_f32
:
2681 case TOK_ASM_vcmpeeq_f32
:
2682 case TOK_ASM_vmoveq_f32
:
2683 case TOK_ASM_vmlaeq_f64
:
2684 case TOK_ASM_vmlseq_f64
:
2685 case TOK_ASM_vnmlseq_f64
:
2686 case TOK_ASM_vnmlaeq_f64
:
2687 case TOK_ASM_vmuleq_f64
:
2688 case TOK_ASM_vnmuleq_f64
:
2689 case TOK_ASM_vaddeq_f64
:
2690 case TOK_ASM_vsubeq_f64
:
2691 case TOK_ASM_vdiveq_f64
:
2692 case TOK_ASM_vnegeq_f64
:
2693 case TOK_ASM_vabseq_f64
:
2694 case TOK_ASM_vsqrteq_f64
:
2695 case TOK_ASM_vcmpeq_f64
:
2696 case TOK_ASM_vcmpeeq_f64
:
2697 case TOK_ASM_vmoveq_f64
:
2698 asm_floating_point_data_processing_opcode(s1
, token
);
2701 case TOK_ASM_vcvtreq_s32_f32
:
2702 case TOK_ASM_vcvtreq_s32_f64
:
2703 case TOK_ASM_vcvteq_s32_f32
:
2704 case TOK_ASM_vcvteq_s32_f64
:
2705 case TOK_ASM_vcvtreq_u32_f32
:
2706 case TOK_ASM_vcvtreq_u32_f64
:
2707 case TOK_ASM_vcvteq_u32_f32
:
2708 case TOK_ASM_vcvteq_u32_f64
:
2709 case TOK_ASM_vcvteq_f64_s32
:
2710 case TOK_ASM_vcvteq_f32_s32
:
2711 case TOK_ASM_vcvteq_f64_u32
:
2712 case TOK_ASM_vcvteq_f32_u32
:
2713 case TOK_ASM_vcvteq_f64_f32
:
2714 case TOK_ASM_vcvteq_f32_f64
:
2715 asm_floating_point_vcvt_data_processing_opcode(s1
, token
);
2718 case TOK_ASM_vpusheq
:
2719 case TOK_ASM_vpopeq
:
2720 case TOK_ASM_vldmeq
:
2721 case TOK_ASM_vldmiaeq
:
2722 case TOK_ASM_vldmdbeq
:
2723 case TOK_ASM_vstmeq
:
2724 case TOK_ASM_vstmiaeq
:
2725 case TOK_ASM_vstmdbeq
:
2726 asm_floating_point_block_data_transfer_opcode(s1
, token
);
2729 case TOK_ASM_vmsreq
:
2730 case TOK_ASM_vmrseq
:
2731 asm_floating_point_status_register_opcode(s1
, token
);
2736 expect("known instruction");
2740 ST_FUNC
void subst_asm_operand(CString
*add_str
, SValue
*sv
, int modifier
)
2742 int r
, reg
, size
, val
;
2746 if ((r
& VT_VALMASK
) == VT_CONST
) {
2747 if (!(r
& VT_LVAL
) && modifier
!= 'c' && modifier
!= 'n' &&
2749 cstr_ccat(add_str
, '#');
2751 const char *name
= get_tok_str(sv
->sym
->v
, NULL
);
2752 if (sv
->sym
->v
>= SYM_FIRST_ANOM
) {
2753 /* In case of anonymous symbols ("L.42", used
2754 for static data labels) we can't find them
2755 in the C symbol table when later looking up
2756 this name. So enter them now into the asm label
2757 list when we still know the symbol. */
2758 get_asm_sym(tok_alloc(name
, strlen(name
))->tok
, sv
->sym
);
2760 if (tcc_state
->leading_underscore
)
2761 cstr_ccat(add_str
, '_');
2762 cstr_cat(add_str
, name
, -1);
2763 if ((uint32_t) sv
->c
.i
== 0)
2765 cstr_ccat(add_str
, '+');
2768 if (modifier
== 'n')
2770 snprintf(buf
, sizeof(buf
), "%d", (int) sv
->c
.i
);
2771 cstr_cat(add_str
, buf
, -1);
2773 } else if ((r
& VT_VALMASK
) == VT_LOCAL
) {
2774 snprintf(buf
, sizeof(buf
), "[fp,#%d]", (int) sv
->c
.i
);
2775 cstr_cat(add_str
, buf
, -1);
2776 } else if (r
& VT_LVAL
) {
2777 reg
= r
& VT_VALMASK
;
2778 if (reg
>= VT_CONST
)
2779 tcc_internal_error("");
2780 snprintf(buf
, sizeof(buf
), "[%s]",
2781 get_tok_str(TOK_ASM_r0
+ reg
, NULL
));
2782 cstr_cat(add_str
, buf
, -1);
2785 reg
= r
& VT_VALMASK
;
2786 if (reg
>= VT_CONST
)
2787 tcc_internal_error("");
2789 /* choose register operand size */
2790 if ((sv
->type
.t
& VT_BTYPE
) == VT_BYTE
||
2791 (sv
->type
.t
& VT_BTYPE
) == VT_BOOL
)
2793 else if ((sv
->type
.t
& VT_BTYPE
) == VT_SHORT
)
2798 if (modifier
== 'b') {
2800 } else if (modifier
== 'w') {
2802 } else if (modifier
== 'k') {
2808 reg
= TOK_ASM_r0
+ reg
;
2811 snprintf(buf
, sizeof(buf
), "%s", get_tok_str(reg
, NULL
));
2812 cstr_cat(add_str
, buf
, -1);
2816 /* generate prolog and epilog code for asm statement */
2817 ST_FUNC
void asm_gen_code(ASMOperand
*operands
, int nb_operands
,
2818 int nb_outputs
, int is_output
,
2819 uint8_t *clobber_regs
,
2822 uint8_t regs_allocated
[NB_ASM_REGS
];
2825 uint32_t saved_regset
= 0;
2827 // TODO: Check non-E ABI.
2828 // Note: Technically, r13 (sp) is also callee-saved--but that does not matter yet
2829 static const uint8_t reg_saved
[] = { 4, 5, 6, 7, 8, 9 /* Note: sometimes special reg "sb" */ , 10, 11 };
2831 /* mark all used registers */
2832 memcpy(regs_allocated
, clobber_regs
, sizeof(regs_allocated
));
2833 for(i
= 0; i
< nb_operands
;i
++) {
2836 regs_allocated
[op
->reg
] = 1;
2838 for(i
= 0; i
< sizeof(reg_saved
)/sizeof(reg_saved
[0]); i
++) {
2840 if (regs_allocated
[reg
])
2841 saved_regset
|= 1 << reg
;
2844 if (!is_output
) { // prolog
2845 /* generate reg save code */
2847 gen_le32(0xe92d0000 | saved_regset
); // push {...}
2849 /* generate load code */
2850 for(i
= 0; i
< nb_operands
; i
++) {
2853 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&&
2855 /* memory reference case (for both input and
2859 sv
.r
= (sv
.r
& ~VT_VALMASK
) | VT_LOCAL
| VT_LVAL
;
2862 } else if (i
>= nb_outputs
|| op
->is_rw
) { // not write-only
2863 /* load value in register */
2864 load(op
->reg
, op
->vt
);
2866 tcc_error("long long not implemented");
2871 /* generate save code */
2872 for(i
= 0 ; i
< nb_outputs
; i
++) {
2875 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
) {
2876 if (!op
->is_memory
) {
2879 sv
.r
= (sv
.r
& ~VT_VALMASK
) | VT_LOCAL
;
2884 sv
.r
= (sv
.r
& ~VT_VALMASK
) | out_reg
;
2885 store(op
->reg
, &sv
);
2888 store(op
->reg
, op
->vt
);
2890 tcc_error("long long not implemented");
2895 /* generate reg restore code */
2897 gen_le32(0xe8bd0000 | saved_regset
); // pop {...}
2901 /* return the constraint priority (we allocate first the lowest
2902 numbered constraints) */
2903 static inline int constraint_priority(const char *str
)
2905 int priority
, c
, pr
;
2907 /* we take the lowest priority */
2915 case 'l': // in ARM mode, that's an alias for 'r' [ARM].
2916 case 'r': // register [general]
2917 case 'p': // valid memory address for load,store [general]
2920 case 'M': // integer constant for shifts [ARM]
2921 case 'I': // integer valid for data processing instruction immediate
2922 case 'J': // integer in range -4095...4095
2924 case 'i': // immediate integer operand, including symbolic constants [general]
2925 case 'm': // memory operand [general]
2926 case 'g': // general-purpose-register, memory, immediate integer [general]
2930 tcc_error("unknown constraint '%c'", c
);
2939 static const char *skip_constraint_modifiers(const char *p
)
2941 /* Constraint modifier:
2942 = Operand is written to by this instruction
2943 + Operand is both read and written to by this instruction
2944 % Instruction is commutative for this operand and the following operand.
2946 Per-alternative constraint modifier:
2947 & Operand is clobbered before the instruction is done using the input operands
2949 while (*p
== '=' || *p
== '&' || *p
== '+' || *p
== '%')
2954 #define REG_OUT_MASK 0x01
2955 #define REG_IN_MASK 0x02
2957 #define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
2959 ST_FUNC
void asm_compute_constraints(ASMOperand
*operands
,
2960 int nb_operands
, int nb_outputs
,
2961 const uint8_t *clobber_regs
,
2964 /* overall format: modifier, then ,-seperated list of alternatives; all operands for a single instruction must have the same number of alternatives */
2965 /* TODO: Simple constraints
2967 o memory operand that is offsetable
2968 V memory but not offsetable
2969 < memory operand with autodecrement addressing is allowed. Restrictions apply.
2970 > memory operand with autoincrement addressing is allowed. Restrictions apply.
2971 n immediate integer operand with a known numeric value
2972 E immediate floating operand (const_double) is allowed, but only if target=host
2973 F immediate floating operand (const_double or const_vector) is allowed
2974 s immediate integer operand whose value is not an explicit integer
2975 X any operand whatsoever
2976 0...9 (postfix); (can also be more than 1 digit number); an operand that matches the specified operand number is allowed
2979 /* TODO: ARM constraints:
2980 k the stack pointer register
2981 G the floating-point constant 0.0
2982 Q memory reference where the exact address is in a single register ("m" is preferable for asm statements)
2983 R an item in the constant pool
2984 S symbol in the text segment of the current file
2985 [ Uv memory reference suitable for VFP load/store insns (reg+constant offset)]
2986 [ Uy memory reference suitable for iWMMXt load/store instructions]
2987 Uq memory reference suitable for the ARMv4 ldrsb instruction
2990 int sorted_op
[MAX_ASM_OPERANDS
];
2991 int i
, j
, k
, p1
, p2
, tmp
, reg
, c
, reg_mask
;
2993 uint8_t regs_allocated
[NB_ASM_REGS
];
2996 for (i
= 0; i
< nb_operands
; i
++) {
2998 op
->input_index
= -1;
3004 /* compute constraint priority and evaluate references to output
3005 constraints if input constraints */
3006 for (i
= 0; i
< nb_operands
; i
++) {
3008 str
= op
->constraint
;
3009 str
= skip_constraint_modifiers(str
);
3010 if (isnum(*str
) || *str
== '[') {
3011 /* this is a reference to another constraint */
3012 k
= find_constraint(operands
, nb_operands
, str
, NULL
);
3013 if ((unsigned) k
>= i
|| i
< nb_outputs
)
3014 tcc_error("invalid reference in constraint %d ('%s')",
3017 if (operands
[k
].input_index
>= 0)
3018 tcc_error("cannot reference twice the same operand");
3019 operands
[k
].input_index
= i
;
3021 } else if ((op
->vt
->r
& VT_VALMASK
) == VT_LOCAL
3023 && (reg
= op
->vt
->sym
->r
& VT_VALMASK
) < VT_CONST
) {
3027 op
->priority
= constraint_priority(str
);
3031 /* sort operands according to their priority */
3032 for (i
= 0; i
< nb_operands
; i
++)
3034 for (i
= 0; i
< nb_operands
- 1; i
++) {
3035 for (j
= i
+ 1; j
< nb_operands
; j
++) {
3036 p1
= operands
[sorted_op
[i
]].priority
;
3037 p2
= operands
[sorted_op
[j
]].priority
;
3040 sorted_op
[i
] = sorted_op
[j
];
3046 for (i
= 0; i
< NB_ASM_REGS
; i
++) {
3047 if (clobber_regs
[i
])
3048 regs_allocated
[i
] = REG_IN_MASK
| REG_OUT_MASK
;
3050 regs_allocated
[i
] = 0;
3052 /* sp cannot be used */
3053 regs_allocated
[13] = REG_IN_MASK
| REG_OUT_MASK
;
3054 /* fp cannot be used yet */
3055 regs_allocated
[11] = REG_IN_MASK
| REG_OUT_MASK
;
3057 /* allocate registers and generate corresponding asm moves */
3058 for (i
= 0; i
< nb_operands
; i
++) {
3061 str
= op
->constraint
;
3062 /* no need to allocate references */
3063 if (op
->ref_index
>= 0)
3065 /* select if register is used for output, input or both */
3066 if (op
->input_index
>= 0) {
3067 reg_mask
= REG_IN_MASK
| REG_OUT_MASK
;
3068 } else if (j
< nb_outputs
) {
3069 reg_mask
= REG_OUT_MASK
;
3071 reg_mask
= REG_IN_MASK
;
3074 if (is_reg_allocated(op
->reg
))
3076 ("asm regvar requests register that's taken already");
3083 case '=': // Operand is written-to
3085 case '+': // Operand is both READ and written-to
3088 case '&': // Operand is clobbered before the instruction is done using the input operands
3089 if (j
>= nb_outputs
)
3090 tcc_error("'%c' modifier can only be applied to outputs",
3092 reg_mask
= REG_IN_MASK
| REG_OUT_MASK
;
3094 case 'l': // In non-thumb mode, alias for 'r'--otherwise r0-r7 [ARM]
3095 case 'r': // general-purpose register
3096 case 'p': // loadable/storable address
3097 /* any general register */
3098 for (reg
= 0; reg
<= 8; reg
++) {
3099 if (!is_reg_allocated(reg
))
3104 /* now we can reload in the register */
3107 regs_allocated
[reg
] |= reg_mask
;
3109 case 'I': // integer that is valid as an data processing instruction immediate (0...255, rotated by a multiple of two)
3110 case 'J': // integer in the range -4095 to 4095 [ARM]
3111 case 'K': // integer that satisfies constraint I when inverted (one's complement)
3112 case 'L': // integer that satisfies constraint I when inverted (two's complement)
3113 case 'i': // immediate integer operand, including symbolic constants
3114 if (!((op
->vt
->r
& (VT_VALMASK
| VT_LVAL
)) == VT_CONST
))
3117 case 'M': // integer in the range 0 to 32
3119 ((op
->vt
->r
& (VT_VALMASK
| VT_LVAL
| VT_SYM
)) ==
3123 case 'm': // memory operand
3125 /* nothing special to do because the operand is already in
3126 memory, except if the pointer itself is stored in a
3127 memory variable (VT_LLOCAL case) */
3128 /* XXX: fix constant case */
3129 /* if it is a reference to a memory zone, it must lie
3130 in a register, so we reserve the register in the
3131 input registers and a load will be generated
3133 if (j
< nb_outputs
|| c
== 'm') {
3134 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
) {
3135 /* any general register */
3136 for (reg
= 0; reg
<= 8; reg
++) {
3137 if (!(regs_allocated
[reg
] & REG_IN_MASK
))
3142 /* now we can reload in the register */
3143 regs_allocated
[reg
] |= REG_IN_MASK
;
3150 tcc_error("asm constraint %d ('%s') could not be satisfied",
3154 /* if a reference is present for that operand, we assign it too */
3155 if (op
->input_index
>= 0) {
3156 operands
[op
->input_index
].reg
= op
->reg
;
3157 operands
[op
->input_index
].is_llong
= op
->is_llong
;
3161 /* compute out_reg. It is used to store outputs registers to memory
3162 locations references by pointers (VT_LLOCAL case) */
3164 for (i
= 0; i
< nb_operands
; i
++) {
3167 (op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& !op
->is_memory
) {
3168 for (reg
= 0; reg
<= 8; reg
++) {
3169 if (!(regs_allocated
[reg
] & REG_OUT_MASK
))
3172 tcc_error("could not find free output register for reloading");
3179 /* print sorted constraints */
3181 for (i
= 0; i
< nb_operands
; i
++) {
3184 printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
3186 op
->id
? get_tok_str(op
->id
, NULL
) : "",
3187 op
->constraint
, op
->vt
->r
, op
->reg
);
3190 printf("out_reg=%d\n", *pout_reg
);
3194 ST_FUNC
void asm_clobber(uint8_t *clobber_regs
, const char *str
)
3199 if (!strcmp(str
, "memory") ||
3200 !strcmp(str
, "cc") ||
3201 !strcmp(str
, "flags"))
3203 ts
= tok_alloc(str
, strlen(str
));
3204 reg
= asm_parse_regvar(ts
->tok
);
3206 tcc_error("invalid clobber register '%s'", str
);
3208 clobber_regs
[reg
] = 1;
3211 /* If T refers to a register then return the register number and type.
3212 Otherwise return -1. */
3213 ST_FUNC
int asm_parse_regvar (int t
)
3215 if (t
>= TOK_ASM_r0
&& t
<= TOK_ASM_pc
) { /* register name */
3218 return TOK_ASM_r11
- TOK_ASM_r0
;
3220 return TOK_ASM_r12
- TOK_ASM_r0
;
3222 return TOK_ASM_r13
- TOK_ASM_r0
;
3224 return TOK_ASM_r14
- TOK_ASM_r0
;
3226 return TOK_ASM_r15
- TOK_ASM_r0
;
3228 return t
- TOK_ASM_r0
;
3234 /*************************************************************/
3235 #endif /* ndef TARGET_DEFS_ONLY */