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 static int asm_parse_vfp_status_regvar(int t
)
94 /* Parse a text containing operand and store the result in OP */
95 static void parse_operand(TCCState
*s1
, Operand
*op
)
103 if (tok
== '{') { // regset literal
105 while (tok
!= '}' && tok
!= TOK_EOF
) {
106 reg
= asm_parse_regvar(tok
);
111 next(); // skip register name
113 if ((1 << reg
) < regset
)
114 tcc_warning("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
124 // ARM instructions don't support empty regset.
125 tcc_error("empty register list is not supported");
127 op
->type
= OP_REGSET32
;
131 } else if ((reg
= asm_parse_regvar(tok
)) != -1) {
132 next(); // skip register name
134 op
->reg
= (uint8_t) reg
;
136 } else if ((reg
= asm_parse_vfp_regvar(tok
, 0)) != -1) {
137 next(); // skip register name
138 op
->type
= OP_VREG32
;
139 op
->reg
= (uint8_t) reg
;
141 } else if ((reg
= asm_parse_vfp_regvar(tok
, 1)) != -1) {
142 next(); // skip register name
143 op
->type
= OP_VREG64
;
144 op
->reg
= (uint8_t) reg
;
146 } else if (tok
== '#' || tok
== '$') {
148 next(); // skip '#' or '$'
154 if ((int) op
->e
.v
< 0 && (int) op
->e
.v
>= -255)
156 else if (op
->e
.v
== (uint8_t)op
->e
.v
)
162 /* XXX: make it faster ? */
163 ST_FUNC
void g(int c
)
169 if (ind1
> cur_text_section
->data_allocated
)
170 section_realloc(cur_text_section
, ind1
);
171 cur_text_section
->data
[ind
] = c
;
175 ST_FUNC
void gen_le16 (int i
)
181 ST_FUNC
void gen_le32 (int i
)
187 if (ind1
> cur_text_section
->data_allocated
)
188 section_realloc(cur_text_section
, ind1
);
189 cur_text_section
->data
[ind
++] = i
& 0xFF;
190 cur_text_section
->data
[ind
++] = (i
>> 8) & 0xFF;
191 cur_text_section
->data
[ind
++] = (i
>> 16) & 0xFF;
192 cur_text_section
->data
[ind
++] = (i
>> 24) & 0xFF;
195 ST_FUNC
void gen_expr32(ExprValue
*pe
)
200 static uint32_t condition_code_of_token(int token
) {
201 if (token
< TOK_ASM_nopeq
) {
202 expect("condition-enabled instruction");
205 return (token
- TOK_ASM_nopeq
) & 15;
208 static void asm_emit_opcode(int token
, uint32_t opcode
) {
209 gen_le32((condition_code_of_token(token
) << 28) | opcode
);
212 static void asm_emit_unconditional_opcode(uint32_t opcode
) {
216 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
)
218 uint32_t opcode
= 0xe000000;
219 if (inter_processor_transfer
)
221 //assert(cp_opcode < 16);
222 opcode
|= cp_opcode
<< 20;
223 //assert(cp_n_operand_register < 16);
224 opcode
|= cp_n_operand_register
<< 16;
225 //assert(cp_destination_register < 16);
226 opcode
|= cp_destination_register
<< 12;
227 //assert(cp_number < 16);
228 opcode
|= cp_number
<< 8;
229 //assert(cp_information < 8);
230 opcode
|= cp_opcode2
<< 5;
231 //assert(cp_m_operand_register < 16);
232 opcode
|= cp_m_operand_register
;
233 asm_emit_unconditional_opcode((high_nibble
<< 28) | opcode
);
236 static void asm_nullary_opcode(int token
)
238 switch (ARM_INSTRUCTION_GROUP(token
)) {
240 asm_emit_opcode(token
, 0xd << 21); // mov r0, r0
243 asm_emit_opcode(token
, 0x320f002);
245 asm_emit_opcode(token
, 0x320f003);
248 expect("nullary instruction");
252 static void asm_unary_opcode(TCCState
*s1
, int token
)
255 parse_operand(s1
, &op
);
257 switch (ARM_INSTRUCTION_GROUP(token
)) {
260 if (op
.type
!= OP_IM8
)
261 expect("immediate 8-bit unsigned integer");
263 /* Note: Dummy operand (ignored by processor): ARM ref documented 0...255, ARM instruction set documented 24 bit */
264 asm_emit_opcode(token
, (0xf << 24) | op
.e
.v
);
268 expect("unary instruction");
272 static void asm_binary_opcode(TCCState
*s1
, int token
)
276 uint32_t encoded_rotation
= 0;
278 parse_operand(s1
, &ops
[0]);
283 parse_operand(s1
, &ops
[1]);
284 if (ops
[0].type
!= OP_REG32
) {
285 expect("(destination operand) register");
289 if (ops
[0].reg
== 15) {
290 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
294 if (ops
[0].reg
== 13)
295 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token
, NULL
));
297 if (ops
[1].type
!= OP_REG32
) {
298 switch (ARM_INSTRUCTION_GROUP(token
)) {
301 if (ops
[1].type
== OP_IM8
|| ops
[1].type
== OP_IM8N
|| ops
[1].type
== OP_IM32
) {
302 if (ops
[1].e
.v
>= 0 && ops
[1].e
.v
<= 0xFFFF) {
303 uint16_t immediate_value
= ops
[1].e
.v
;
304 switch (ARM_INSTRUCTION_GROUP(token
)) {
306 asm_emit_opcode(token
, 0x3400000 | (ops
[0].reg
<< 12) | (immediate_value
& 0xF000) << 4 | (immediate_value
& 0xFFF));
309 asm_emit_opcode(token
, 0x3000000 | (ops
[0].reg
<< 12) | (immediate_value
& 0xF000) << 4 | (immediate_value
& 0xFFF));
313 expect("(source operand) immediate 16 bit value");
315 expect("(source operand) immediate");
318 expect("(source operand) register");
323 if (ops
[1].reg
== 15) {
324 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
328 if (ops
[1].reg
== 13)
329 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token
, NULL
));
333 if (tok
== TOK_ASM_ror
) {
334 next(); // skip 'ror'
335 parse_operand(s1
, &rotation
);
336 if (rotation
.type
!= OP_IM8
) {
337 expect("immediate value for rotation");
340 amount
= rotation
.e
.v
;
343 encoded_rotation
= 1 << 10;
346 encoded_rotation
= 2 << 10;
349 encoded_rotation
= 3 << 10;
352 expect("'8' or '16' or '24'");
358 switch (ARM_INSTRUCTION_GROUP(token
)) {
360 if (encoded_rotation
)
361 tcc_error("clz does not support rotation");
362 asm_emit_opcode(token
, 0x16f0f10 | (ops
[0].reg
<< 12) | ops
[1].reg
);
365 asm_emit_opcode(token
, 0x6af0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
368 asm_emit_opcode(token
, 0x6bf0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
371 asm_emit_opcode(token
, 0x6ef0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
374 asm_emit_opcode(token
, 0x6ff0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
377 expect("binary instruction");
381 static void asm_coprocessor_opcode(TCCState
*s1
, int token
) {
385 uint8_t registers
[3];
390 if (tok
>= TOK_ASM_p0
&& tok
<= TOK_ASM_p15
) {
391 coprocessor
= tok
- TOK_ASM_p0
;
394 expect("'p<number>'");
403 parse_operand(s1
, &opcode1
);
404 if (opcode1
.type
!= OP_IM8
|| opcode1
.e
.v
> 15) {
405 tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token
, NULL
));
409 for (i
= 0; i
< 3; ++i
) {
414 if (i
== 0 && token
!= TOK_ASM_cdp2
&& (ARM_INSTRUCTION_GROUP(token
) == TOK_ASM_mrceq
|| ARM_INSTRUCTION_GROUP(token
) == TOK_ASM_mcreq
)) {
415 if (tok
>= TOK_ASM_r0
&& tok
<= TOK_ASM_r15
) {
416 registers
[i
] = tok
- TOK_ASM_r0
;
419 expect("'r<number>'");
423 if (tok
>= TOK_ASM_c0
&& tok
<= TOK_ASM_c15
) {
424 registers
[i
] = tok
- TOK_ASM_c0
;
427 expect("'c<number>'");
434 parse_operand(s1
, &opcode2
);
436 opcode2
.type
= OP_IM8
;
439 if (opcode2
.type
!= OP_IM8
|| opcode2
.e
.v
> 15) {
440 tcc_error("opcode2 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token
, NULL
));
444 if (token
== TOK_ASM_cdp2
) {
446 asm_emit_coprocessor_opcode(high_nibble
, coprocessor
, opcode1
.e
.v
, registers
[0], registers
[1], registers
[2], opcode2
.e
.v
, 0);
449 high_nibble
= condition_code_of_token(token
);
451 switch (ARM_INSTRUCTION_GROUP(token
)) {
453 asm_emit_coprocessor_opcode(high_nibble
, coprocessor
, opcode1
.e
.v
, registers
[0], registers
[1], registers
[2], opcode2
.e
.v
, 0);
456 // opcode1 encoding changes! highest and lowest bit gone.
460 // opcode1 encoding changes! highest and lowest bit gone.
461 if (opcode1
.e
.v
> 7) {
462 tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 7", get_tok_str(token
, NULL
));
465 asm_emit_coprocessor_opcode(high_nibble
, coprocessor
, (opcode1
.e
.v
<< 1) | mrc
, registers
[0], registers
[1], registers
[2], opcode2
.e
.v
, 1);
468 expect("known instruction");
472 /* data processing and single data transfer instructions only */
473 #define ENCODE_RN(register_index) ((register_index) << 16)
474 #define ENCODE_RD(register_index) ((register_index) << 12)
475 #define ENCODE_SET_CONDITION_CODES (1 << 20)
477 /* Note: For data processing instructions, "1" means immediate.
478 Note: For single data transfer instructions, "0" means immediate. */
479 #define ENCODE_IMMEDIATE_FLAG (1 << 25)
481 #define ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER (1 << 4)
482 #define ENCODE_BARREL_SHIFTER_MODE_LSL (0 << 5)
483 #define ENCODE_BARREL_SHIFTER_MODE_LSR (1 << 5)
484 #define ENCODE_BARREL_SHIFTER_MODE_ASR (2 << 5)
485 #define ENCODE_BARREL_SHIFTER_MODE_ROR (3 << 5)
486 #define ENCODE_BARREL_SHIFTER_REGISTER(register_index) ((register_index) << 8)
487 #define ENCODE_BARREL_SHIFTER_IMMEDIATE(value) ((value) << 7)
489 static void asm_block_data_transfer_opcode(TCCState
*s1
, int token
)
495 parse_operand(s1
, &ops
[0]);
501 next(); // skip comma
502 parse_operand(s1
, &ops
[1]);
506 expect("at least one operand");
508 } else if (ops
[nb_ops
- 1].type
!= OP_REGSET32
) {
509 expect("(last operand) register list");
513 // block data transfer: 1 0 0 P U S W L << 20 (general case):
515 // Rn: bits 19...16 base register
516 // Register List: bits 15...0
518 switch (ARM_INSTRUCTION_GROUP(token
)) {
519 case TOK_ASM_pusheq
: // TODO: Optimize 1-register case to: str ?, [sp, #-4]!
520 // Instruction: 1 I=0 P=1 U=0 S=0 W=1 L=0 << 20, op 1101
523 // Register List: bits 15...0
525 expect("exactly one operand");
527 asm_emit_opcode(token
, (0x92d << 16) | ops
[0].regset
); // TODO: base register ?
529 case TOK_ASM_popeq
: // TODO: Optimize 1-register case to: ldr ?, [sp], #4
530 // Instruction: 1 I=0 P=0 U=1 S=0 W=0 L=1 << 20, op 1101
533 // Register List: bits 15...0
535 expect("exactly one operand");
537 asm_emit_opcode(token
, (0x8bd << 16) | ops
[0].regset
); // TODO: base register ?
539 case TOK_ASM_stmdaeq
:
540 case TOK_ASM_ldmdaeq
:
543 case TOK_ASM_stmiaeq
:
544 case TOK_ASM_ldmiaeq
:
545 case TOK_ASM_stmdbeq
:
546 case TOK_ASM_ldmdbeq
:
547 case TOK_ASM_stmibeq
:
548 case TOK_ASM_ldmibeq
:
549 switch (ARM_INSTRUCTION_GROUP(token
)) {
550 case TOK_ASM_stmdaeq
: // post-decrement store
553 case TOK_ASM_ldmdaeq
: // post-decrement load
556 case TOK_ASM_stmeq
: // post-increment store
557 case TOK_ASM_stmiaeq
: // post-increment store
560 case TOK_ASM_ldmeq
: // post-increment load
561 case TOK_ASM_ldmiaeq
: // post-increment load
564 case TOK_ASM_stmdbeq
: // pre-decrement store
567 case TOK_ASM_ldmdbeq
: // pre-decrement load
570 case TOK_ASM_stmibeq
: // pre-increment store
573 case TOK_ASM_ldmibeq
: // pre-increment load
577 tcc_error("internal error: This place should not be reached (fallback in asm_block_data_transfer_opcode)");
581 // Register List: lower bits
583 expect("exactly two operands");
584 else if (ops
[0].type
!= OP_REG32
)
585 expect("(first operand) register");
588 opcode
|= 1 << 21; // writeback
589 asm_emit_opcode(token
, opcode
| ENCODE_RN(ops
[0].reg
) | ops
[1].regset
);
593 expect("block data transfer instruction");
597 /* Parses shift directive and returns the parts that would have to be set in the opcode because of it.
598 Does not encode the actual shift amount.
599 It's not an error if there is no shift directive.
601 NB_SHIFT: will be set to 1 iff SHIFT is filled. Note that for rrx, there's no need to fill SHIFT.
602 SHIFT: will be filled in with the shift operand to use, if any. */
603 static uint32_t asm_parse_optional_shift(TCCState
* s1
, int* nb_shift
, Operand
* shift
)
617 opcode
= ENCODE_BARREL_SHIFTER_MODE_LSL
;
620 opcode
= ENCODE_BARREL_SHIFTER_MODE_ASR
;
623 opcode
= ENCODE_BARREL_SHIFTER_MODE_LSR
;
626 opcode
= ENCODE_BARREL_SHIFTER_MODE_ROR
;
630 parse_operand(s1
, shift
);
635 opcode
= ENCODE_BARREL_SHIFTER_MODE_ROR
;
641 static uint32_t asm_encode_shift(Operand
* shift
)
644 uint32_t operands
= 0;
645 switch (shift
->type
) {
647 if (shift
->reg
== 15)
648 tcc_error("r15 cannot be used as a shift count");
650 operands
= ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER
;
651 operands
|= ENCODE_BARREL_SHIFTER_REGISTER(shift
->reg
);
656 if (amount
> 0 && amount
< 32)
657 operands
= ENCODE_BARREL_SHIFTER_IMMEDIATE(amount
);
659 tcc_error("shift count out of range");
662 tcc_error("unknown shift amount");
667 static void asm_data_processing_opcode(TCCState
*s1
, int token
)
673 uint32_t operands
= 0;
675 /* modulo 16 entries per instruction for the different condition codes */
676 uint32_t opcode_idx
= (ARM_INSTRUCTION_GROUP(token
) - TOK_ASM_andeq
) >> 4;
677 uint32_t opcode_nos
= opcode_idx
>> 1; // without "s"; "OpCode" in ARM docs
679 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ) {
680 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
)
682 parse_operand(s1
, &ops
[nb_ops
]);
690 operands
|= asm_parse_optional_shift(s1
, &nb_shift
, &shift
);
692 expect("at least two operands");
693 else if (nb_ops
== 2) {
694 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
695 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
697 } else if (nb_ops
== 3) {
698 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
699 tcc_error("'%s' cannot be used with three operands", get_tok_str(token
, NULL
));
704 expect("two or three operands");
708 uint32_t immediate_value
;
709 uint8_t half_immediate_rotation
;
710 if (nb_shift
&& shift
.type
== OP_REG32
) {
711 if ((ops
[0].type
== OP_REG32
&& ops
[0].reg
== 15) ||
712 (ops
[1].type
== OP_REG32
&& ops
[1].reg
== 15)) {
713 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
718 // data processing (general case):
720 // Rn: bits 19...16 (first operand)
721 // Rd: bits 15...12 (destination)
722 // Operand2: bits 11...0 (second operand); depending on I that's either a register or an immediate
724 // bits 24...21: "OpCode"--see below
726 /* operations in the token list are ordered by opcode */
727 opcode
= opcode_nos
<< 21; // drop "s"
728 if (ops
[0].type
!= OP_REG32
)
729 expect("(destination operand) register");
730 else if (opcode_nos
== 0xa || opcode_nos
== 0xb || opcode_nos
== 0x8 || opcode_nos
== 0x9) // cmp, cmn, tst, teq
731 operands
|= ENCODE_SET_CONDITION_CODES
; // force S set, otherwise it's a completely different instruction.
733 operands
|= ENCODE_RD(ops
[0].reg
);
734 if (ops
[1].type
!= OP_REG32
)
735 expect("(first source operand) register");
736 else if (!(opcode_nos
== 0xd || opcode_nos
== 0xf)) // not: mov, mvn (those have only one source operand)
737 operands
|= ENCODE_RN(ops
[1].reg
);
738 switch (ops
[2].type
) {
740 operands
|= ops
[2].reg
;
744 operands
|= ENCODE_IMMEDIATE_FLAG
;
745 immediate_value
= ops
[2].e
.v
;
746 for (half_immediate_rotation
= 0; half_immediate_rotation
< 16; ++half_immediate_rotation
) {
747 if (immediate_value
>= 0x00 && immediate_value
< 0x100)
749 // rotate left by two
750 immediate_value
= ((immediate_value
& 0x3FFFFFFF) << 2) | ((immediate_value
& 0xC0000000) >> 30);
752 if (half_immediate_rotation
>= 16) {
755 operands
|= immediate_value
;
756 operands
|= half_immediate_rotation
<< 8;
759 case OP_IM8N
: // immediate negative value
760 operands
|= ENCODE_IMMEDIATE_FLAG
;
761 immediate_value
= ops
[2].e
.v
;
762 /* Instruction swapping:
763 0001 = EOR - Rd:= Op1 EOR Op2 -> difficult
764 0011 = RSB - Rd:= Op2 - Op1 -> difficult
765 0111 = RSC - Rd:= Op2 - Op1 + C -> difficult
766 1000 = TST - CC on: Op1 AND Op2 -> difficult
767 1001 = TEQ - CC on: Op1 EOR Op2 -> difficult
768 1100 = ORR - Rd:= Op1 OR Op2 -> difficult
770 switch (opcode_nos
) {
771 case 0x0: // AND - Rd:= Op1 AND Op2
772 opcode
= 0xe << 21; // BIC
773 immediate_value
= ~immediate_value
;
775 case 0x2: // SUB - Rd:= Op1 - Op2
776 opcode
= 0x4 << 21; // ADD
777 immediate_value
= -immediate_value
;
779 case 0x4: // ADD - Rd:= Op1 + Op2
780 opcode
= 0x2 << 21; // SUB
781 immediate_value
= -immediate_value
;
783 case 0x5: // ADC - Rd:= Op1 + Op2 + C
784 opcode
= 0x6 << 21; // SBC
785 immediate_value
= ~immediate_value
;
787 case 0x6: // SBC - Rd:= Op1 - Op2 + C
788 opcode
= 0x5 << 21; // ADC
789 immediate_value
= ~immediate_value
;
791 case 0xa: // CMP - CC on: Op1 - Op2
792 opcode
= 0xb << 21; // CMN
793 immediate_value
= -immediate_value
;
795 case 0xb: // CMN - CC on: Op1 + Op2
796 opcode
= 0xa << 21; // CMP
797 immediate_value
= -immediate_value
;
799 case 0xd: // MOV - Rd:= Op2
800 opcode
= 0xf << 21; // MVN
801 immediate_value
= ~immediate_value
;
803 case 0xe: // BIC - Rd:= Op1 AND NOT Op2
804 opcode
= 0x0 << 21; // AND
805 immediate_value
= ~immediate_value
;
807 case 0xf: // MVN - Rd:= NOT Op2
808 opcode
= 0xd << 21; // MOV
809 immediate_value
= ~immediate_value
;
812 tcc_error("cannot use '%s' with a negative immediate value", get_tok_str(token
, NULL
));
814 for (half_immediate_rotation
= 0; half_immediate_rotation
< 16; ++half_immediate_rotation
) {
815 if (immediate_value
>= 0x00 && immediate_value
< 0x100)
817 // rotate left by two
818 immediate_value
= ((immediate_value
& 0x3FFFFFFF) << 2) | ((immediate_value
& 0xC0000000) >> 30);
820 if (half_immediate_rotation
>= 16) {
821 immediate_value
= ops
[2].e
.v
;
822 tcc_error("immediate value 0x%X cannot be encoded into ARM immediate", (unsigned) immediate_value
);
825 operands
|= immediate_value
;
826 operands
|= half_immediate_rotation
<< 8;
829 expect("(second source operand) register or immediate value");
833 if (operands
& ENCODE_IMMEDIATE_FLAG
)
834 tcc_error("immediate rotation not implemented");
836 operands
|= asm_encode_shift(&shift
);
839 /* S=0 and S=1 entries alternate one after another, in that order */
840 opcode
|= (opcode_idx
& 1) ? ENCODE_SET_CONDITION_CODES
: 0;
841 asm_emit_opcode(token
, opcode
| operands
);
845 static void asm_shift_opcode(TCCState
*s1
, int token
)
849 int definitely_neutral
= 0;
850 uint32_t opcode
= 0xd << 21; // MOV
851 uint32_t operands
= 0;
853 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
854 parse_operand(s1
, &ops
[nb_ops
]);
862 expect("at least two operands");
866 if (ops
[0].type
!= OP_REG32
) {
867 expect("(destination operand) register");
870 operands
|= ENCODE_RD(ops
[0].reg
);
873 switch (ARM_INSTRUCTION_GROUP(token
)) {
875 opcode
|= ENCODE_SET_CONDITION_CODES
;
878 if (ops
[1].type
== OP_REG32
) {
879 operands
|= ops
[1].reg
;
880 operands
|= ENCODE_BARREL_SHIFTER_MODE_ROR
;
881 asm_emit_opcode(token
, opcode
| operands
);
883 tcc_error("(first source operand) register");
886 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
887 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
892 expect("two or three operands");
896 switch (ARM_INSTRUCTION_GROUP(token
)) {
901 opcode
|= ENCODE_SET_CONDITION_CODES
;
905 switch (ops
[1].type
) {
907 operands
|= ops
[1].reg
;
910 operands
|= ENCODE_IMMEDIATE_FLAG
;
911 operands
|= ops
[1].e
.v
;
912 tcc_error("Using an immediate value as the source operand is not possible with '%s' instruction on ARM", get_tok_str(token
, NULL
));
916 switch (ops
[2].type
) {
918 if ((ops
[0].type
== OP_REG32
&& ops
[0].reg
== 15) ||
919 (ops
[1].type
== OP_REG32
&& ops
[1].reg
== 15)) {
920 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
922 operands
|= asm_encode_shift(&ops
[2]);
926 operands
|= asm_encode_shift(&ops
[2]);
928 definitely_neutral
= 1;
932 if (!definitely_neutral
) switch (ARM_INSTRUCTION_GROUP(token
)) {
935 operands
|= ENCODE_BARREL_SHIFTER_MODE_LSL
;
939 operands
|= ENCODE_BARREL_SHIFTER_MODE_LSR
;
943 operands
|= ENCODE_BARREL_SHIFTER_MODE_ASR
;
947 operands
|= ENCODE_BARREL_SHIFTER_MODE_ROR
;
950 expect("shift instruction");
953 asm_emit_opcode(token
, opcode
| operands
);
956 static void asm_multiplication_opcode(TCCState
*s1
, int token
)
960 uint32_t opcode
= 0x90;
962 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
963 parse_operand(s1
, &ops
[nb_ops
]);
971 expect("at least two operands");
972 else if (nb_ops
== 2) {
973 switch (ARM_INSTRUCTION_GROUP(token
)) {
976 memcpy(&ops
[2], &ops
[0], sizeof(ops
[1])); // ARM is actually like this!
979 expect("at least three operands");
985 // multiply (special case):
992 if (ops
[0].type
== OP_REG32
)
993 opcode
|= ops
[0].reg
<< 16;
995 expect("(destination operand) register");
996 if (ops
[1].type
== OP_REG32
)
997 opcode
|= ops
[1].reg
;
999 expect("(first source operand) register");
1000 if (ops
[2].type
== OP_REG32
)
1001 opcode
|= ops
[2].reg
<< 8;
1003 expect("(second source operand) register");
1005 if (ops
[3].type
== OP_REG32
)
1006 opcode
|= ops
[3].reg
<< 12;
1008 expect("(third source operand) register");
1011 switch (ARM_INSTRUCTION_GROUP(token
)) {
1012 case TOK_ASM_mulseq
:
1013 opcode
|= 1 << 20; // Status
1017 expect("three operands");
1019 asm_emit_opcode(token
, opcode
);
1022 case TOK_ASM_mlaseq
:
1023 opcode
|= 1 << 20; // Status
1027 expect("four operands");
1029 opcode
|= 1 << 21; // Accumulate
1030 asm_emit_opcode(token
, opcode
);
1034 expect("known multiplication instruction");
1038 static void asm_long_multiplication_opcode(TCCState
*s1
, int token
)
1042 uint32_t opcode
= 0x90 | (1 << 23);
1044 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
1045 parse_operand(s1
, &ops
[nb_ops
]);
1053 expect("four operands");
1057 // long multiply (special case):
1059 // RdLo: bits 15...12
1060 // RdHi: bits 19...16
1064 if (ops
[0].type
== OP_REG32
)
1065 opcode
|= ops
[0].reg
<< 12;
1067 expect("(destination lo accumulator) register");
1068 if (ops
[1].type
== OP_REG32
)
1069 opcode
|= ops
[1].reg
<< 16;
1071 expect("(destination hi accumulator) register");
1072 if (ops
[2].type
== OP_REG32
)
1073 opcode
|= ops
[2].reg
;
1075 expect("(first source operand) register");
1076 if (ops
[3].type
== OP_REG32
)
1077 opcode
|= ops
[3].reg
<< 8;
1079 expect("(second source operand) register");
1081 switch (ARM_INSTRUCTION_GROUP(token
)) {
1082 case TOK_ASM_smullseq
:
1083 opcode
|= 1 << 20; // Status
1085 case TOK_ASM_smulleq
:
1086 opcode
|= 1 << 22; // signed
1087 asm_emit_opcode(token
, opcode
);
1089 case TOK_ASM_umullseq
:
1090 opcode
|= 1 << 20; // Status
1092 case TOK_ASM_umulleq
:
1093 asm_emit_opcode(token
, opcode
);
1095 case TOK_ASM_smlalseq
:
1096 opcode
|= 1 << 20; // Status
1098 case TOK_ASM_smlaleq
:
1099 opcode
|= 1 << 22; // signed
1100 opcode
|= 1 << 21; // Accumulate
1101 asm_emit_opcode(token
, opcode
);
1103 case TOK_ASM_umlalseq
:
1104 opcode
|= 1 << 20; // Status
1106 case TOK_ASM_umlaleq
:
1107 opcode
|= 1 << 21; // Accumulate
1108 asm_emit_opcode(token
, opcode
);
1111 expect("known long multiplication instruction");
1115 static void asm_single_data_transfer_opcode(TCCState
*s1
, int token
)
1118 Operand strex_operand
;
1122 int closed_bracket
= 0;
1124 uint32_t opcode
= 0;
1125 // Note: ldr r0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1126 // Note: ldr r0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1127 // Note: ldr r0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1129 parse_operand(s1
, &ops
[0]);
1130 if (ops
[0].type
== OP_REG32
)
1131 opcode
|= ENCODE_RD(ops
[0].reg
);
1133 expect("(destination operand) register");
1137 expect("at least two arguments");
1141 switch (ARM_INSTRUCTION_GROUP(token
)) {
1142 case TOK_ASM_strexbeq
:
1143 case TOK_ASM_strexeq
:
1144 parse_operand(s1
, &strex_operand
);
1145 if (strex_operand
.type
!= OP_REG32
) {
1150 expect("at least three arguments");
1161 parse_operand(s1
, &ops
[1]);
1162 if (ops
[1].type
== OP_REG32
)
1163 opcode
|= ENCODE_RN(ops
[1].reg
);
1165 expect("(first source operand) register");
1171 // exclam = 1; // implicit in hardware; don't do it in software
1179 parse_operand(s1
, &ops
[2]);
1180 if (ops
[2].type
== OP_REG32
) {
1181 if (ops
[2].reg
== 15) {
1182 tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token
, NULL
));
1187 opcode
|= asm_parse_optional_shift(s1
, &nb_shift
, &shift
);
1189 expect("shift directive, or no comma");
1193 // end of input expression in brackets--assume 0 offset
1194 ops
[2].type
= OP_IM8
;
1196 opcode
|= 1 << 24; // add offset before transfer
1198 if (!closed_bracket
) {
1203 opcode
|= 1 << 24; // add offset before transfer
1210 // single data transfer: 0 1 I P U B W L << 20 (general case):
1212 // Rd: destination operand [ok]
1213 // Rn: first source operand [ok]
1214 // Operand2: bits 11...0 [ok]
1215 // I: immediate operand? [ok]
1216 // P: Pre/post indexing is PRE: Add offset before transfer [ok]
1217 // U: Up/down is up? (*adds* offset to base) [ok]
1218 // B: Byte/word is byte? TODO
1219 // W: Write address back into base? [ok]
1220 // L: Load/store is load? [ok]
1222 opcode
|= 1 << 21; // write offset back into register
1224 if (ops
[2].type
== OP_IM32
|| ops
[2].type
== OP_IM8
|| ops
[2].type
== OP_IM8N
) {
1227 tcc_error("minus before '#' not supported for immediate values");
1229 opcode
|= 1 << 23; // up
1231 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
1236 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
1240 } else if (ops
[2].type
== OP_REG32
) {
1242 opcode
|= 1 << 23; // up
1243 opcode
|= ENCODE_IMMEDIATE_FLAG
; /* if set, it means it's NOT immediate */
1244 opcode
|= ops
[2].reg
;
1248 switch (ARM_INSTRUCTION_GROUP(token
)) {
1249 case TOK_ASM_strbeq
:
1250 opcode
|= 1 << 22; // B
1253 opcode
|= 1 << 26; // Load/Store
1255 opcode
|= asm_encode_shift(&shift
);
1256 asm_emit_opcode(token
, opcode
);
1258 case TOK_ASM_ldrbeq
:
1259 opcode
|= 1 << 22; // B
1262 opcode
|= 1 << 20; // L
1263 opcode
|= 1 << 26; // Load/Store
1265 opcode
|= asm_encode_shift(&shift
);
1266 asm_emit_opcode(token
, opcode
);
1268 case TOK_ASM_strexbeq
:
1269 opcode
|= 1 << 22; // B
1271 case TOK_ASM_strexeq
:
1272 if ((opcode
& 0xFFF) || nb_shift
) {
1273 tcc_error("neither offset nor shift allowed with 'strex'");
1275 } else if (opcode
& ENCODE_IMMEDIATE_FLAG
) { // if set, it means it's NOT immediate
1276 tcc_error("offset not allowed with 'strex'");
1279 if ((opcode
& (1 << 24)) == 0) { // add offset after transfer
1280 tcc_error("adding offset after transfer not allowed with 'strex'");
1284 opcode
|= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL
1285 opcode
|= strex_operand
.reg
;
1286 asm_emit_opcode(token
, opcode
);
1288 case TOK_ASM_ldrexbeq
:
1289 opcode
|= 1 << 22; // B
1291 case TOK_ASM_ldrexeq
:
1292 if ((opcode
& 0xFFF) || nb_shift
) {
1293 tcc_error("neither offset nor shift allowed with 'ldrex'");
1295 } else if (opcode
& ENCODE_IMMEDIATE_FLAG
) { // if set, it means it's NOT immediate
1296 tcc_error("offset not allowed with 'ldrex'");
1299 if ((opcode
& (1 << 24)) == 0) { // add offset after transfer
1300 tcc_error("adding offset after transfer not allowed with 'ldrex'");
1303 opcode
|= 1 << 20; // L
1305 opcode
|= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL
1306 asm_emit_opcode(token
, opcode
);
1309 expect("data transfer instruction");
1313 // 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)
1314 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
) {
1315 uint32_t opcode
= 0x0;
1316 opcode
|= 1 << 26; // Load/Store
1317 opcode
|= 1 << 27; // coprocessor
1320 opcode
|= 1 << 22; // long transfer
1323 opcode
|= 1 << 20; // L
1325 opcode
|= cp_number
<< 8;
1328 opcode
|= ENCODE_RD(CRd
);
1330 if (Rn
->type
!= OP_REG32
) {
1334 //assert(Rn->reg < 16);
1335 opcode
|= ENCODE_RN(Rn
->reg
);
1337 opcode
|= 1 << 24; // add offset before transfer
1340 opcode
|= 1 << 21; // write offset back into register
1342 if (offset
->type
== OP_IM8
|| offset
->type
== OP_IM8N
|| offset
->type
== OP_IM32
) {
1343 int v
= offset
->e
.v
;
1345 tcc_error("minus before '#' not supported for immediate values");
1346 if (offset
->type
== OP_IM8N
|| v
< 0)
1349 opcode
|= 1 << 23; // up
1351 tcc_error("immediate offset must be a multiple of 4");
1356 tcc_error("immediate offset must be between -1020 and 1020");
1360 } else if (offset
->type
== OP_REG32
) {
1362 opcode
|= 1 << 23; // up
1363 opcode
|= ENCODE_IMMEDIATE_FLAG
; /* if set, it means it's NOT immediate */
1364 opcode
|= offset
->reg
;
1365 tcc_error("Using register offset to register address is not possible here");
1367 } else if (offset
->type
== OP_VREG64
) {
1369 opcode
|= offset
->reg
;
1371 expect("immediate or register");
1373 asm_emit_unconditional_opcode((high_nibble
<< 28) | opcode
);
1376 // Almost exactly the same as asm_single_data_transfer_opcode.
1377 // Difference: Offsets are smaller and multiples of 4; no shifts, no STREX, ENCODE_IMMEDIATE_FLAG is inverted again.
1378 static void asm_coprocessor_data_transfer_opcode(TCCState
*s1
, int token
)
1381 uint8_t coprocessor
;
1382 uint8_t coprocessor_destination_register
;
1383 int preincrement
= 0;
1385 int closed_bracket
= 0;
1387 int long_transfer
= 0;
1388 // Note: ldc p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1389 // Note: ldc p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1390 // Note: ldc p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1392 if (tok
>= TOK_ASM_p0
&& tok
<= TOK_ASM_p15
) {
1393 coprocessor
= tok
- TOK_ASM_p0
;
1396 expect("'c<number>'");
1405 if (tok
>= TOK_ASM_c0
&& tok
<= TOK_ASM_c15
) {
1406 coprocessor_destination_register
= tok
- TOK_ASM_c0
;
1409 expect("'c<number>'");
1423 parse_operand(s1
, &ops
[1]);
1424 if (ops
[1].type
!= OP_REG32
) {
1425 expect("(first source operand) register");
1431 // exclam = 1; // implicit in hardware; don't do it in software
1439 parse_operand(s1
, &ops
[2]);
1440 if (ops
[2].type
== OP_REG32
) {
1441 if (ops
[2].reg
== 15) {
1442 tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token
, NULL
));
1445 } else if (ops
[2].type
== OP_VREG64
) {
1446 tcc_error("'%s' does not support VFP register operand", get_tok_str(token
, NULL
));
1450 // end of input expression in brackets--assume 0 offset
1451 ops
[2].type
= OP_IM8
;
1453 preincrement
= 1; // add offset before transfer
1455 if (!closed_bracket
) {
1460 preincrement
= 1; // add offset before transfer
1467 // TODO: Support options.
1469 if (token
== TOK_ASM_ldc2
|| token
== TOK_ASM_stc2
|| token
== TOK_ASM_ldc2l
|| token
== TOK_ASM_stc2l
) {
1472 long_transfer
= 1; // long transfer
1475 asm_emit_coprocessor_data_transfer(0xF, coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], op2_minus
, preincrement
, exclam
, long_transfer
, 1);
1478 long_transfer
= 1; // long transfer
1481 asm_emit_coprocessor_data_transfer(0xF, coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], op2_minus
, preincrement
, exclam
, long_transfer
, 0);
1484 } else switch (ARM_INSTRUCTION_GROUP(token
)) {
1485 case TOK_ASM_stcleq
:
1489 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);
1491 case TOK_ASM_ldcleq
:
1495 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);
1498 expect("coprocessor data transfer instruction");
1502 #if defined(TCC_ARM_VFP)
1503 #define CP_SINGLE_PRECISION_FLOAT 10
1504 #define CP_DOUBLE_PRECISION_FLOAT 11
1506 static void asm_floating_point_single_data_transfer_opcode(TCCState
*s1
, int token
)
1509 uint8_t coprocessor
= 0;
1510 uint8_t coprocessor_destination_register
= 0;
1511 int long_transfer
= 0;
1512 // Note: vldr p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1513 // Note: Not allowed: vldr p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1514 // Note: Not allowed: vldr p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1516 parse_operand(s1
, &ops
[0]);
1517 if (ops
[0].type
== OP_VREG32
) {
1518 coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
1519 coprocessor_destination_register
= ops
[0].reg
;
1520 long_transfer
= coprocessor_destination_register
& 1;
1521 coprocessor_destination_register
>>= 1;
1522 } else if (ops
[0].type
== OP_VREG64
) {
1523 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
1524 coprocessor_destination_register
= ops
[0].reg
;
1527 expect("floating point register");
1541 parse_operand(s1
, &ops
[1]);
1542 if (ops
[1].type
!= OP_REG32
) {
1543 expect("(first source operand) register");
1548 parse_operand(s1
, &ops
[2]);
1549 if (ops
[2].type
!= OP_IM8
&& ops
[2].type
!= OP_IM8N
) {
1550 expect("immediate offset");
1554 // end of input expression in brackets--assume 0 offset
1555 ops
[2].type
= OP_IM8
;
1563 switch (ARM_INSTRUCTION_GROUP(token
)) {
1564 case TOK_ASM_vldreq
:
1565 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], 0, 1, 0, long_transfer
, 1);
1567 case TOK_ASM_vstreq
:
1568 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], 0, 1, 0, long_transfer
, 0);
1571 expect("floating point data transfer instruction");
1575 static void asm_floating_point_block_data_transfer_opcode(TCCState
*s1
, int token
)
1577 uint8_t coprocessor
= 0;
1578 int first_regset_register
;
1579 int last_regset_register
;
1580 uint8_t regset_item_count
;
1581 uint8_t extra_register_bit
= 0;
1584 int preincrement
= 0;
1587 switch (ARM_INSTRUCTION_GROUP(token
)) {
1588 case TOK_ASM_vpusheq
:
1589 case TOK_ASM_vpopeq
:
1590 ops
[0].type
= OP_REG32
;
1591 ops
[0].reg
= 13; // sp
1595 parse_operand(s1
, &ops
[0]);
1601 next(); // skip comma
1613 first_regset_register
= asm_parse_vfp_regvar(tok
, 1);
1614 if ((first_regset_register
= asm_parse_vfp_regvar(tok
, 1)) != -1) {
1615 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
1617 } else if ((first_regset_register
= asm_parse_vfp_regvar(tok
, 0)) != -1) {
1618 coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
1621 expect("floating-point register");
1627 if ((last_regset_register
= asm_parse_vfp_regvar(tok
, coprocessor
== CP_DOUBLE_PRECISION_FLOAT
)) != -1)
1630 expect("floating-point register");
1634 last_regset_register
= first_regset_register
;
1636 if (last_regset_register
< first_regset_register
) {
1637 tcc_error("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
1646 // Note: 0 (one down) is not implemented by us regardless.
1647 regset_item_count
= last_regset_register
- first_regset_register
+ 1;
1648 if (coprocessor
== CP_DOUBLE_PRECISION_FLOAT
)
1649 regset_item_count
<<= 1;
1651 extra_register_bit
= first_regset_register
& 1;
1652 first_regset_register
>>= 1;
1654 offset
.type
= OP_IM8
;
1655 offset
.e
.v
= regset_item_count
<< 2;
1656 switch (ARM_INSTRUCTION_GROUP(token
)) {
1657 case TOK_ASM_vstmeq
: // post-increment store
1658 case TOK_ASM_vstmiaeq
: // post-increment store
1660 case TOK_ASM_vpopeq
:
1661 case TOK_ASM_vldmeq
: // post-increment load
1662 case TOK_ASM_vldmiaeq
: // post-increment load
1665 case TOK_ASM_vldmdbeq
: // pre-decrement load
1668 case TOK_ASM_vpusheq
:
1669 case TOK_ASM_vstmdbeq
: // pre-decrement store
1670 offset
.type
= OP_IM8N
;
1671 offset
.e
.v
= -offset
.e
.v
;
1675 expect("floating point block data transfer instruction");
1678 if (ops
[0].type
!= OP_REG32
)
1679 expect("(first operand) register");
1680 else if (ops
[0].reg
== 15)
1681 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
1682 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
)
1683 tcc_error("first operand of '%s' should have an exclamation mark", get_tok_str(token
, NULL
));
1685 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
);
1688 #define VMOV_FRACTIONAL_DIGITS 7
1689 #define VMOV_ONE 10000000 /* pow(10, VMOV_FRACTIONAL_DIGITS) */
1691 static uint32_t vmov_parse_fractional_part(const char* s
)
1693 uint32_t result
= 0;
1695 for (i
= 0; i
< VMOV_FRACTIONAL_DIGITS
; ++i
) {
1698 if (c
>= '0' && c
<= '9') {
1699 result
+= (c
- '0');
1704 expect("decimal numeral");
1708 static int vmov_linear_approx_index(uint32_t beginning
, uint32_t end
, uint32_t value
)
1714 k
= (end
- beginning
)/16;
1715 for (xvalue
= beginning
, i
= 0; i
< 16; ++i
, xvalue
+= k
) {
1716 if (value
== xvalue
)
1723 static uint32_t vmov_parse_immediate_value() {
1725 unsigned long integral_value
;
1728 if (tok
!= TOK_PPNUM
) {
1729 expect("immediate value");
1734 integral_value
= strtoul(p
, (char **)&p
, 0);
1736 if (errno
|| integral_value
>= 32) {
1737 tcc_error("invalid floating-point immediate value");
1741 value
= (uint32_t) integral_value
* VMOV_ONE
;
1744 value
+= vmov_parse_fractional_part(p
);
1750 static uint8_t vmov_encode_immediate_value(uint32_t value
)
1754 uint32_t beginning
= 0;
1759 limit
= 32 * VMOV_ONE
;
1760 for (i
= 0; i
< 8; ++i
) {
1761 if (value
< limit
) {
1769 if (r
== -1 || value
< beginning
|| value
> end
) {
1770 tcc_error("invalid decimal number for vmov: %d", value
);
1773 n
= vmov_linear_approx_index(beginning
, end
, value
);
1774 return n
| (((3 - r
) & 0x7) << 4);
1778 static void asm_floating_point_immediate_data_processing_opcode_tail(TCCState
*s1
, int token
, uint8_t coprocessor
, uint8_t CRd
) {
1779 uint8_t opcode1
= 0;
1780 uint8_t opcode2
= 0;
1781 uint8_t operands
[3] = {0, 0, 0};
1782 uint32_t immediate_value
= 0;
1788 if (tok
== '#' || tok
== '$') {
1795 immediate_value
= vmov_parse_immediate_value();
1797 opcode1
= 11; // "Other" instruction
1798 switch (ARM_INSTRUCTION_GROUP(token
)) {
1799 case TOK_ASM_vcmpeq_f32
:
1800 case TOK_ASM_vcmpeq_f64
:
1803 if (immediate_value
) {
1804 expect("Immediate value 0");
1808 case TOK_ASM_vcmpeeq_f32
:
1809 case TOK_ASM_vcmpeeq_f64
:
1812 if (immediate_value
) {
1813 expect("Immediate value 0");
1817 case TOK_ASM_vmoveq_f32
:
1818 case TOK_ASM_vmoveq_f64
:
1824 code
= vmov_encode_immediate_value(immediate_value
);
1825 operands
[1] |= code
>> 4;
1826 operands
[2] = code
& 0xF;
1829 expect("known floating point with immediate instruction");
1833 if (coprocessor
== CP_SINGLE_PRECISION_FLOAT
) {
1834 if (operands
[0] & 1)
1839 asm_emit_coprocessor_opcode(condition_code_of_token(token
), coprocessor
, opcode1
, operands
[0], operands
[1], operands
[2], opcode2
, 0);
1842 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]) {
1843 uint8_t opcode1
= 0;
1844 uint8_t opcode2
= 0;
1845 switch (coprocessor
) {
1846 case CP_SINGLE_PRECISION_FLOAT
:
1847 // "vmov.f32 r2, s3" or "vmov.f32 s3, r2"
1848 if (nb_ops
!= 2 || nb_arm_regs
!= 1) {
1849 tcc_error("vmov.f32 only implemented for one VFP register operand and one ARM register operands");
1852 if (ops
[0].type
!= OP_REG32
) { // determine mode: load or store
1853 // need to swap operands 0 and 1
1854 memcpy(&ops
[2], &ops
[1], sizeof(ops
[2]));
1855 memcpy(&ops
[1], &ops
[0], sizeof(ops
[1]));
1856 memcpy(&ops
[0], &ops
[2], sizeof(ops
[0]));
1860 if (ops
[1].type
== OP_VREG32
) {
1866 if (ops
[0].type
== OP_VREG32
) {
1872 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);
1874 case CP_DOUBLE_PRECISION_FLOAT
:
1875 if (nb_ops
!= 3 || nb_arm_regs
!= 2) {
1876 tcc_error("vmov.f32 only implemented for one VFP register operand and two ARM register operands");
1879 // Determine whether it's a store into a VFP register (vmov "d1, r2, r3") rather than "vmov r2, r3, d1"
1880 if (ops
[0].type
== OP_VREG64
) {
1881 if (ops
[2].type
== OP_REG32
) {
1883 // need to rotate operand list to the left
1884 memcpy(&temp
, &ops
[0], sizeof(temp
));
1885 memcpy(&ops
[0], &ops
[1], sizeof(ops
[0]));
1886 memcpy(&ops
[1], &ops
[2], sizeof(ops
[1]));
1887 memcpy(&ops
[2], &temp
, sizeof(ops
[2]));
1889 tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands");
1892 } else if (ops
[0].type
!= OP_REG32
|| ops
[1].type
!= OP_REG32
|| ops
[2].type
!= OP_VREG64
) {
1893 tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands");
1898 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, ops
[0].reg
, &ops
[1], &ops
[2], 0, 0, 0, 1, opcode1
);
1901 tcc_internal_error("unknown coprocessor");
1905 static void asm_floating_point_data_processing_opcode(TCCState
*s1
, int token
) {
1906 uint8_t coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
1907 uint8_t opcode1
= 0;
1908 uint8_t opcode2
= 0; // (0 || 2) | register selection
1912 int nb_arm_regs
= 0;
1915 Instruction opcode opcode2 Reason
1916 =============================================================
1917 - 1?00 ?1? Undefined
1918 VFNMS 1?01 ?0? Must be unconditional
1919 VFNMA 1?01 ?1? Must be unconditional
1920 VFMA 1?10 ?0? Must be unconditional
1921 VFMS 1?10 ?1? Must be unconditional
1934 switch (ARM_INSTRUCTION_GROUP(token
)) {
1935 case TOK_ASM_vmlaeq_f64
:
1936 case TOK_ASM_vmlseq_f64
:
1937 case TOK_ASM_vnmlseq_f64
:
1938 case TOK_ASM_vnmlaeq_f64
:
1939 case TOK_ASM_vmuleq_f64
:
1940 case TOK_ASM_vnmuleq_f64
:
1941 case TOK_ASM_vaddeq_f64
:
1942 case TOK_ASM_vsubeq_f64
:
1943 case TOK_ASM_vdiveq_f64
:
1944 case TOK_ASM_vnegeq_f64
:
1945 case TOK_ASM_vabseq_f64
:
1946 case TOK_ASM_vsqrteq_f64
:
1947 case TOK_ASM_vcmpeq_f64
:
1948 case TOK_ASM_vcmpeeq_f64
:
1949 case TOK_ASM_vmoveq_f64
:
1950 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
1953 switch (ARM_INSTRUCTION_GROUP(token
)) {
1954 case TOK_ASM_vmoveq_f32
:
1955 case TOK_ASM_vmoveq_f64
:
1960 for (nb_ops
= 0; nb_ops
< 3; ) {
1961 // Note: Necessary because parse_operand can't parse decimal numerals.
1962 if (nb_ops
== 1 && (tok
== '#' || tok
== '$' || tok
== TOK_PPNUM
|| tok
== '-')) {
1963 asm_floating_point_immediate_data_processing_opcode_tail(s1
, token
, coprocessor
, ops
[0].reg
);
1966 parse_operand(s1
, &ops
[nb_ops
]);
1967 if (vmov
&& ops
[nb_ops
].type
== OP_REG32
) {
1969 } else if (ops
[nb_ops
].type
== OP_VREG32
) {
1970 if (coprocessor
!= CP_SINGLE_PRECISION_FLOAT
) {
1971 expect("'s<number>'");
1974 } else if (ops
[nb_ops
].type
== OP_VREG64
) {
1975 if (coprocessor
!= CP_DOUBLE_PRECISION_FLOAT
) {
1976 expect("'d<number>'");
1980 expect("floating point register");
1990 if (nb_arm_regs
== 0) {
1991 if (nb_ops
== 2) { // implicit
1992 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
1993 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
1997 tcc_error("Not enough operands for '%s' (%u)", get_tok_str(token
, NULL
), nb_ops
);
2002 switch (ARM_INSTRUCTION_GROUP(token
)) {
2003 case TOK_ASM_vmlaeq_f32
:
2004 case TOK_ASM_vmlaeq_f64
:
2008 case TOK_ASM_vmlseq_f32
:
2009 case TOK_ASM_vmlseq_f64
:
2013 case TOK_ASM_vnmlseq_f32
:
2014 case TOK_ASM_vnmlseq_f64
:
2018 case TOK_ASM_vnmlaeq_f32
:
2019 case TOK_ASM_vnmlaeq_f64
:
2023 case TOK_ASM_vmuleq_f32
:
2024 case TOK_ASM_vmuleq_f64
:
2028 case TOK_ASM_vnmuleq_f32
:
2029 case TOK_ASM_vnmuleq_f64
:
2033 case TOK_ASM_vaddeq_f32
:
2034 case TOK_ASM_vaddeq_f64
:
2038 case TOK_ASM_vsubeq_f32
:
2039 case TOK_ASM_vsubeq_f64
:
2043 case TOK_ASM_vdiveq_f32
:
2044 case TOK_ASM_vdiveq_f64
:
2048 case TOK_ASM_vnegeq_f32
:
2049 case TOK_ASM_vnegeq_f64
:
2050 opcode1
= 11; // Other" instruction
2052 ops
[1].type
= OP_IM8
;
2055 case TOK_ASM_vabseq_f32
:
2056 case TOK_ASM_vabseq_f64
:
2057 opcode1
= 11; // "Other" instruction
2059 ops
[1].type
= OP_IM8
;
2062 case TOK_ASM_vsqrteq_f32
:
2063 case TOK_ASM_vsqrteq_f64
:
2064 opcode1
= 11; // "Other" instruction
2066 ops
[1].type
= OP_IM8
;
2069 case TOK_ASM_vcmpeq_f32
:
2070 case TOK_ASM_vcmpeq_f64
:
2071 opcode1
= 11; // "Other" instruction
2073 ops
[1].type
= OP_IM8
;
2076 case TOK_ASM_vcmpeeq_f32
:
2077 case TOK_ASM_vcmpeeq_f64
:
2078 opcode1
= 11; // "Other" instruction
2080 ops
[1].type
= OP_IM8
;
2083 case TOK_ASM_vmoveq_f32
:
2084 case TOK_ASM_vmoveq_f64
:
2085 if (nb_arm_regs
> 0) { // vmov.f32 r2, s3 or similar
2086 asm_floating_point_reg_arm_reg_transfer_opcode_tail(s1
, token
, coprocessor
, nb_arm_regs
, nb_ops
, ops
);
2089 opcode1
= 11; // "Other" instruction
2091 ops
[1].type
= OP_IM8
;
2095 // TODO: vcvt; vcvtr
2097 expect("known floating point instruction");
2101 if (coprocessor
== CP_SINGLE_PRECISION_FLOAT
) {
2102 if (ops
[2].type
== OP_VREG32
) {
2108 if (ops
[1].type
== OP_VREG32
) {
2114 if (ops
[0].type
== OP_VREG32
) {
2121 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);
2124 static void asm_floating_point_status_register_opcode(TCCState
* s1
, int token
)
2126 uint8_t coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
2128 int vfp_sys_reg
= -1;
2129 Operand arm_operand
;
2130 switch (ARM_INSTRUCTION_GROUP(token
)) {
2131 case TOK_ASM_vmrseq
:
2133 if (tok
== TOK_ASM_apsr_nzcv
) {
2134 arm_operand
.type
= OP_REG32
;
2135 arm_operand
.reg
= 15; // not PC
2136 next(); // skip apsr_nzcv
2138 parse_operand(s1
, &arm_operand
);
2139 if (arm_operand
.type
== OP_REG32
&& arm_operand
.reg
== 15) {
2140 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
2149 vfp_sys_reg
= asm_parse_vfp_status_regvar(tok
);
2150 next(); // skip vfp sys reg
2151 if (arm_operand
.type
== OP_REG32
&& arm_operand
.reg
== 15 && vfp_sys_reg
!= 1) {
2152 tcc_error("'%s' only supports the variant 'vmrs apsr_nzcv, fpscr' here", get_tok_str(token
, NULL
));
2156 case TOK_ASM_vmsreq
:
2158 vfp_sys_reg
= asm_parse_vfp_status_regvar(tok
);
2159 next(); // skip vfp sys reg
2164 parse_operand(s1
, &arm_operand
);
2165 if (arm_operand
.type
== OP_REG32
&& arm_operand
.reg
== 15) {
2166 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
2171 expect("floating point status register instruction");
2174 if (vfp_sys_reg
== -1) {
2175 expect("VFP system register");
2178 if (arm_operand
.type
!= OP_REG32
) {
2179 expect("ARM register");
2182 asm_emit_coprocessor_opcode(condition_code_of_token(token
), coprocessor
, opcode
, arm_operand
.reg
, vfp_sys_reg
, 0x10, 0, 0);
2187 static void asm_misc_single_data_transfer_opcode(TCCState
*s1
, int token
)
2191 int closed_bracket
= 0;
2193 uint32_t opcode
= (1 << 7) | (1 << 4);
2196 The argument syntax is exactly the same as in arm_single_data_transfer_opcode, except that there's no STREX argument form.
2197 The main difference between this function and asm_misc_single_data_transfer_opcode is that the immediate values here must be smaller.
2198 Also, the combination (P=0, W=1) is unpredictable here.
2199 The immediate flag has moved to bit index 22--and its meaning has flipped.
2200 The immediate value itself has been split into two parts: one at bits 11...8, one at bits 3...0
2201 bit 26 (Load/Store instruction) is unset here.
2202 bits 7 and 4 are set here. */
2204 // Here: 0 0 0 P U I W L << 20
2205 // [compare single data transfer: 0 1 I P U B W L << 20]
2207 parse_operand(s1
, &ops
[0]);
2208 if (ops
[0].type
== OP_REG32
)
2209 opcode
|= ENCODE_RD(ops
[0].reg
);
2211 expect("(destination operand) register");
2215 expect("at least two arguments");
2224 parse_operand(s1
, &ops
[1]);
2225 if (ops
[1].type
== OP_REG32
)
2226 opcode
|= ENCODE_RN(ops
[1].reg
);
2228 expect("(first source operand) register");
2234 // exclam = 1; // implicit in hardware; don't do it in software
2242 parse_operand(s1
, &ops
[2]);
2244 // end of input expression in brackets--assume 0 offset
2245 ops
[2].type
= OP_IM8
;
2247 opcode
|= 1 << 24; // add offset before transfer
2249 if (!closed_bracket
) {
2254 opcode
|= 1 << 24; // add offset before transfer
2262 if ((opcode
& (1 << 24)) == 0) {
2263 tcc_error("result of '%s' would be unpredictable here", get_tok_str(token
, NULL
));
2266 opcode
|= 1 << 21; // write offset back into register
2269 if (ops
[2].type
== OP_IM32
|| ops
[2].type
== OP_IM8
|| ops
[2].type
== OP_IM8N
) {
2272 tcc_error("minus before '#' not supported for immediate values");
2274 opcode
|= 1 << 23; // up
2276 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
2278 // bits 11...8: immediate hi nibble
2279 // bits 3...0: immediate lo nibble
2280 opcode
|= (v
& 0xF0) << 4;
2285 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
2288 // bits 11...8: immediate hi nibble
2289 // bits 3...0: immediate lo nibble
2290 opcode
|= (v
& 0xF0) << 4;
2294 opcode
|= 1 << 22; // not ENCODE_IMMEDIATE_FLAG;
2295 } else if (ops
[2].type
== OP_REG32
) {
2297 opcode
|= 1 << 23; // up
2298 opcode
|= ops
[2].reg
;
2302 switch (ARM_INSTRUCTION_GROUP(token
)) {
2303 case TOK_ASM_ldrsheq
:
2304 opcode
|= 1 << 5; // halfword, not byte
2306 case TOK_ASM_ldrsbeq
:
2307 opcode
|= 1 << 6; // sign extend
2308 opcode
|= 1 << 20; // L
2309 asm_emit_opcode(token
, opcode
);
2311 case TOK_ASM_ldrheq
:
2312 opcode
|= 1 << 5; // halfword, not byte
2313 opcode
|= 1 << 20; // L
2314 asm_emit_opcode(token
, opcode
);
2316 case TOK_ASM_strheq
:
2317 opcode
|= 1 << 5; // halfword, not byte
2318 asm_emit_opcode(token
, opcode
);
2323 /* Note: almost dupe of encbranch in arm-gen.c */
2324 static uint32_t encbranchoffset(int pos
, int addr
, int fail
)
2328 if(addr
>=0x7fffff || addr
<-0x800000) {
2330 tcc_error("branch offset is too far");
2333 return /*not 0x0A000000|*/(addr
&0xffffff);
2336 static void asm_branch_opcode(TCCState
*s1
, int token
)
2343 switch (ARM_INSTRUCTION_GROUP(token
)) {
2347 esym
= elfsym(e
.sym
);
2348 if (!esym
|| esym
->st_shndx
!= cur_text_section
->sh_num
) {
2349 tcc_error("invalid branch target");
2352 jmp_disp
= encbranchoffset(ind
, e
.v
+ esym
->st_value
, 1);
2355 parse_operand(s1
, &op
);
2358 switch (ARM_INSTRUCTION_GROUP(token
)) {
2360 asm_emit_opcode(token
, (0xa << 24) | (jmp_disp
& 0xffffff));
2363 asm_emit_opcode(token
, (0xb << 24) | (jmp_disp
& 0xffffff));
2366 if (op
.type
!= OP_REG32
)
2369 asm_emit_opcode(token
, (0x12fff1 << 4) | op
.reg
);
2372 if (op
.type
!= OP_REG32
)
2375 asm_emit_opcode(token
, (0x12fff3 << 4) | op
.reg
);
2378 expect("branch instruction");
2382 ST_FUNC
void asm_opcode(TCCState
*s1
, int token
)
2384 while (token
== TOK_LINEFEED
) {
2388 if (token
== TOK_EOF
)
2390 if (token
< TOK_ASM_nopeq
) { // no condition code
2393 asm_coprocessor_opcode(s1
, token
);
2399 asm_coprocessor_data_transfer_opcode(s1
, token
);
2402 expect("instruction");
2407 switch (ARM_INSTRUCTION_GROUP(token
)) {
2408 case TOK_ASM_pusheq
:
2410 case TOK_ASM_stmdaeq
:
2411 case TOK_ASM_ldmdaeq
:
2414 case TOK_ASM_stmiaeq
:
2415 case TOK_ASM_ldmiaeq
:
2416 case TOK_ASM_stmdbeq
:
2417 case TOK_ASM_ldmdbeq
:
2418 case TOK_ASM_stmibeq
:
2419 case TOK_ASM_ldmibeq
:
2420 asm_block_data_transfer_opcode(s1
, token
);
2425 asm_nullary_opcode(token
);
2429 asm_unary_opcode(s1
, token
);
2435 asm_branch_opcode(s1
, token
);
2438 case TOK_ASM_sxtbeq
:
2439 case TOK_ASM_sxtheq
:
2440 case TOK_ASM_uxtbeq
:
2441 case TOK_ASM_uxtheq
:
2442 case TOK_ASM_movteq
:
2443 case TOK_ASM_movweq
:
2444 asm_binary_opcode(s1
, token
);
2448 case TOK_ASM_ldrbeq
:
2450 case TOK_ASM_strbeq
:
2451 case TOK_ASM_ldrexeq
:
2452 case TOK_ASM_ldrexbeq
:
2453 case TOK_ASM_strexeq
:
2454 case TOK_ASM_strexbeq
:
2455 asm_single_data_transfer_opcode(s1
, token
);
2458 case TOK_ASM_ldrheq
:
2459 case TOK_ASM_ldrsheq
:
2460 case TOK_ASM_ldrsbeq
:
2461 case TOK_ASM_strheq
:
2462 asm_misc_single_data_transfer_opcode(s1
, token
);
2481 case TOK_ASM_andseq
:
2482 case TOK_ASM_eorseq
:
2483 case TOK_ASM_subseq
:
2484 case TOK_ASM_rsbseq
:
2485 case TOK_ASM_addseq
:
2486 case TOK_ASM_adcseq
:
2487 case TOK_ASM_sbcseq
:
2488 case TOK_ASM_rscseq
:
2489 // case TOK_ASM_tstseq:
2490 // case TOK_ASM_teqseq:
2491 // case TOK_ASM_cmpseq:
2492 // case TOK_ASM_cmnseq:
2493 case TOK_ASM_orrseq
:
2494 case TOK_ASM_movseq
:
2495 case TOK_ASM_bicseq
:
2496 case TOK_ASM_mvnseq
:
2497 asm_data_processing_opcode(s1
, token
);
2501 case TOK_ASM_lslseq
:
2503 case TOK_ASM_lsrseq
:
2505 case TOK_ASM_asrseq
:
2507 case TOK_ASM_rorseq
:
2508 case TOK_ASM_rrxseq
:
2510 asm_shift_opcode(s1
, token
);
2514 case TOK_ASM_mulseq
:
2516 case TOK_ASM_mlaseq
:
2517 asm_multiplication_opcode(s1
, token
);
2520 case TOK_ASM_smulleq
:
2521 case TOK_ASM_smullseq
:
2522 case TOK_ASM_umulleq
:
2523 case TOK_ASM_umullseq
:
2524 case TOK_ASM_smlaleq
:
2525 case TOK_ASM_smlalseq
:
2526 case TOK_ASM_umlaleq
:
2527 case TOK_ASM_umlalseq
:
2528 asm_long_multiplication_opcode(s1
, token
);
2534 asm_coprocessor_opcode(s1
, token
);
2538 case TOK_ASM_ldcleq
:
2540 case TOK_ASM_stcleq
:
2541 asm_coprocessor_data_transfer_opcode(s1
, token
);
2544 #if defined(TCC_ARM_VFP)
2545 case TOK_ASM_vldreq
:
2546 case TOK_ASM_vstreq
:
2547 asm_floating_point_single_data_transfer_opcode(s1
, token
);
2550 case TOK_ASM_vmlaeq_f32
:
2551 case TOK_ASM_vmlseq_f32
:
2552 case TOK_ASM_vnmlseq_f32
:
2553 case TOK_ASM_vnmlaeq_f32
:
2554 case TOK_ASM_vmuleq_f32
:
2555 case TOK_ASM_vnmuleq_f32
:
2556 case TOK_ASM_vaddeq_f32
:
2557 case TOK_ASM_vsubeq_f32
:
2558 case TOK_ASM_vdiveq_f32
:
2559 case TOK_ASM_vnegeq_f32
:
2560 case TOK_ASM_vabseq_f32
:
2561 case TOK_ASM_vsqrteq_f32
:
2562 case TOK_ASM_vcmpeq_f32
:
2563 case TOK_ASM_vcmpeeq_f32
:
2564 case TOK_ASM_vmoveq_f32
:
2565 case TOK_ASM_vmlaeq_f64
:
2566 case TOK_ASM_vmlseq_f64
:
2567 case TOK_ASM_vnmlseq_f64
:
2568 case TOK_ASM_vnmlaeq_f64
:
2569 case TOK_ASM_vmuleq_f64
:
2570 case TOK_ASM_vnmuleq_f64
:
2571 case TOK_ASM_vaddeq_f64
:
2572 case TOK_ASM_vsubeq_f64
:
2573 case TOK_ASM_vdiveq_f64
:
2574 case TOK_ASM_vnegeq_f64
:
2575 case TOK_ASM_vabseq_f64
:
2576 case TOK_ASM_vsqrteq_f64
:
2577 case TOK_ASM_vcmpeq_f64
:
2578 case TOK_ASM_vcmpeeq_f64
:
2579 case TOK_ASM_vmoveq_f64
:
2580 asm_floating_point_data_processing_opcode(s1
, token
);
2583 case TOK_ASM_vpusheq
:
2584 case TOK_ASM_vpopeq
:
2585 case TOK_ASM_vldmeq
:
2586 case TOK_ASM_vldmiaeq
:
2587 case TOK_ASM_vldmdbeq
:
2588 case TOK_ASM_vstmeq
:
2589 case TOK_ASM_vstmiaeq
:
2590 case TOK_ASM_vstmdbeq
:
2591 asm_floating_point_block_data_transfer_opcode(s1
, token
);
2594 case TOK_ASM_vmsreq
:
2595 case TOK_ASM_vmrseq
:
2596 asm_floating_point_status_register_opcode(s1
, token
);
2601 expect("known instruction");
2605 ST_FUNC
void subst_asm_operand(CString
*add_str
, SValue
*sv
, int modifier
)
2607 int r
, reg
, size
, val
;
2611 if ((r
& VT_VALMASK
) == VT_CONST
) {
2612 if (!(r
& VT_LVAL
) && modifier
!= 'c' && modifier
!= 'n' &&
2614 cstr_ccat(add_str
, '#');
2616 const char *name
= get_tok_str(sv
->sym
->v
, NULL
);
2617 if (sv
->sym
->v
>= SYM_FIRST_ANOM
) {
2618 /* In case of anonymous symbols ("L.42", used
2619 for static data labels) we can't find them
2620 in the C symbol table when later looking up
2621 this name. So enter them now into the asm label
2622 list when we still know the symbol. */
2623 get_asm_sym(tok_alloc(name
, strlen(name
))->tok
, sv
->sym
);
2625 if (tcc_state
->leading_underscore
)
2626 cstr_ccat(add_str
, '_');
2627 cstr_cat(add_str
, name
, -1);
2628 if ((uint32_t) sv
->c
.i
== 0)
2630 cstr_ccat(add_str
, '+');
2633 if (modifier
== 'n')
2635 snprintf(buf
, sizeof(buf
), "%d", (int) sv
->c
.i
);
2636 cstr_cat(add_str
, buf
, -1);
2638 } else if ((r
& VT_VALMASK
) == VT_LOCAL
) {
2639 snprintf(buf
, sizeof(buf
), "[fp,#%d]", (int) sv
->c
.i
);
2640 cstr_cat(add_str
, buf
, -1);
2641 } else if (r
& VT_LVAL
) {
2642 reg
= r
& VT_VALMASK
;
2643 if (reg
>= VT_CONST
)
2644 tcc_internal_error("");
2645 snprintf(buf
, sizeof(buf
), "[%s]",
2646 get_tok_str(TOK_ASM_r0
+ reg
, NULL
));
2647 cstr_cat(add_str
, buf
, -1);
2650 reg
= r
& VT_VALMASK
;
2651 if (reg
>= VT_CONST
)
2652 tcc_internal_error("");
2654 /* choose register operand size */
2655 if ((sv
->type
.t
& VT_BTYPE
) == VT_BYTE
||
2656 (sv
->type
.t
& VT_BTYPE
) == VT_BOOL
)
2658 else if ((sv
->type
.t
& VT_BTYPE
) == VT_SHORT
)
2663 if (modifier
== 'b') {
2665 } else if (modifier
== 'w') {
2667 } else if (modifier
== 'k') {
2673 reg
= TOK_ASM_r0
+ reg
;
2676 snprintf(buf
, sizeof(buf
), "%s", get_tok_str(reg
, NULL
));
2677 cstr_cat(add_str
, buf
, -1);
2681 /* generate prolog and epilog code for asm statement */
2682 ST_FUNC
void asm_gen_code(ASMOperand
*operands
, int nb_operands
,
2683 int nb_outputs
, int is_output
,
2684 uint8_t *clobber_regs
,
2687 uint8_t regs_allocated
[NB_ASM_REGS
];
2690 uint32_t saved_regset
= 0;
2692 // TODO: Check non-E ABI.
2693 // Note: Technically, r13 (sp) is also callee-saved--but that does not matter yet
2694 static uint8_t reg_saved
[] = { 4, 5, 6, 7, 8, 9 /* Note: sometimes special reg "sb" */ , 10, 11 };
2696 /* mark all used registers */
2697 memcpy(regs_allocated
, clobber_regs
, sizeof(regs_allocated
));
2698 for(i
= 0; i
< nb_operands
;i
++) {
2701 regs_allocated
[op
->reg
] = 1;
2703 for(i
= 0; i
< sizeof(reg_saved
)/sizeof(reg_saved
[0]); i
++) {
2705 if (regs_allocated
[reg
])
2706 saved_regset
|= 1 << reg
;
2709 if (!is_output
) { // prolog
2710 /* generate reg save code */
2712 gen_le32(0xe92d0000 | saved_regset
); // push {...}
2714 /* generate load code */
2715 for(i
= 0; i
< nb_operands
; i
++) {
2718 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&&
2720 /* memory reference case (for both input and
2724 sv
.r
= (sv
.r
& ~VT_VALMASK
) | VT_LOCAL
| VT_LVAL
;
2727 } else if (i
>= nb_outputs
|| op
->is_rw
) { // not write-only
2728 /* load value in register */
2729 load(op
->reg
, op
->vt
);
2731 tcc_error("long long not implemented");
2736 /* generate save code */
2737 for(i
= 0 ; i
< nb_outputs
; i
++) {
2740 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
) {
2741 if (!op
->is_memory
) {
2744 sv
.r
= (sv
.r
& ~VT_VALMASK
) | VT_LOCAL
;
2749 sv
.r
= (sv
.r
& ~VT_VALMASK
) | out_reg
;
2750 store(op
->reg
, &sv
);
2753 store(op
->reg
, op
->vt
);
2755 tcc_error("long long not implemented");
2760 /* generate reg restore code */
2762 gen_le32(0xe8bd0000 | saved_regset
); // pop {...}
2766 /* return the constraint priority (we allocate first the lowest
2767 numbered constraints) */
2768 static inline int constraint_priority(const char *str
)
2770 int priority
, c
, pr
;
2772 /* we take the lowest priority */
2780 case 'l': // in ARM mode, that's an alias for 'r' [ARM].
2781 case 'r': // register [general]
2782 case 'p': // valid memory address for load,store [general]
2785 case 'M': // integer constant for shifts [ARM]
2786 case 'I': // integer valid for data processing instruction immediate
2787 case 'J': // integer in range -4095...4095
2789 case 'i': // immediate integer operand, including symbolic constants [general]
2790 case 'm': // memory operand [general]
2791 case 'g': // general-purpose-register, memory, immediate integer [general]
2795 tcc_error("unknown constraint '%c'", c
);
2804 static const char *skip_constraint_modifiers(const char *p
)
2806 /* Constraint modifier:
2807 = Operand is written to by this instruction
2808 + Operand is both read and written to by this instruction
2809 % Instruction is commutative for this operand and the following operand.
2811 Per-alternative constraint modifier:
2812 & Operand is clobbered before the instruction is done using the input operands
2814 while (*p
== '=' || *p
== '&' || *p
== '+' || *p
== '%')
2819 #define REG_OUT_MASK 0x01
2820 #define REG_IN_MASK 0x02
2822 #define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
2824 ST_FUNC
void asm_compute_constraints(ASMOperand
*operands
,
2825 int nb_operands
, int nb_outputs
,
2826 const uint8_t *clobber_regs
,
2829 /* overall format: modifier, then ,-seperated list of alternatives; all operands for a single instruction must have the same number of alternatives */
2830 /* TODO: Simple constraints
2832 o memory operand that is offsetable
2833 V memory but not offsetable
2834 < memory operand with autodecrement addressing is allowed. Restrictions apply.
2835 > memory operand with autoincrement addressing is allowed. Restrictions apply.
2836 n immediate integer operand with a known numeric value
2837 E immediate floating operand (const_double) is allowed, but only if target=host
2838 F immediate floating operand (const_double or const_vector) is allowed
2839 s immediate integer operand whose value is not an explicit integer
2840 X any operand whatsoever
2841 0...9 (postfix); (can also be more than 1 digit number); an operand that matches the specified operand number is allowed
2844 /* TODO: ARM constraints:
2845 k the stack pointer register
2846 G the floating-point constant 0.0
2847 Q memory reference where the exact address is in a single register ("m" is preferable for asm statements)
2848 R an item in the constant pool
2849 S symbol in the text segment of the current file
2850 [ Uv memory reference suitable for VFP load/store insns (reg+constant offset)]
2851 [ Uy memory reference suitable for iWMMXt load/store instructions]
2852 Uq memory reference suitable for the ARMv4 ldrsb instruction
2855 int sorted_op
[MAX_ASM_OPERANDS
];
2856 int i
, j
, k
, p1
, p2
, tmp
, reg
, c
, reg_mask
;
2858 uint8_t regs_allocated
[NB_ASM_REGS
];
2861 for (i
= 0; i
< nb_operands
; i
++) {
2863 op
->input_index
= -1;
2869 /* compute constraint priority and evaluate references to output
2870 constraints if input constraints */
2871 for (i
= 0; i
< nb_operands
; i
++) {
2873 str
= op
->constraint
;
2874 str
= skip_constraint_modifiers(str
);
2875 if (isnum(*str
) || *str
== '[') {
2876 /* this is a reference to another constraint */
2877 k
= find_constraint(operands
, nb_operands
, str
, NULL
);
2878 if ((unsigned) k
>= i
|| i
< nb_outputs
)
2879 tcc_error("invalid reference in constraint %d ('%s')",
2882 if (operands
[k
].input_index
>= 0)
2883 tcc_error("cannot reference twice the same operand");
2884 operands
[k
].input_index
= i
;
2886 } else if ((op
->vt
->r
& VT_VALMASK
) == VT_LOCAL
2888 && (reg
= op
->vt
->sym
->r
& VT_VALMASK
) < VT_CONST
) {
2892 op
->priority
= constraint_priority(str
);
2896 /* sort operands according to their priority */
2897 for (i
= 0; i
< nb_operands
; i
++)
2899 for (i
= 0; i
< nb_operands
- 1; i
++) {
2900 for (j
= i
+ 1; j
< nb_operands
; j
++) {
2901 p1
= operands
[sorted_op
[i
]].priority
;
2902 p2
= operands
[sorted_op
[j
]].priority
;
2905 sorted_op
[i
] = sorted_op
[j
];
2911 for (i
= 0; i
< NB_ASM_REGS
; i
++) {
2912 if (clobber_regs
[i
])
2913 regs_allocated
[i
] = REG_IN_MASK
| REG_OUT_MASK
;
2915 regs_allocated
[i
] = 0;
2917 /* sp cannot be used */
2918 regs_allocated
[13] = REG_IN_MASK
| REG_OUT_MASK
;
2919 /* fp cannot be used yet */
2920 regs_allocated
[11] = REG_IN_MASK
| REG_OUT_MASK
;
2922 /* allocate registers and generate corresponding asm moves */
2923 for (i
= 0; i
< nb_operands
; i
++) {
2926 str
= op
->constraint
;
2927 /* no need to allocate references */
2928 if (op
->ref_index
>= 0)
2930 /* select if register is used for output, input or both */
2931 if (op
->input_index
>= 0) {
2932 reg_mask
= REG_IN_MASK
| REG_OUT_MASK
;
2933 } else if (j
< nb_outputs
) {
2934 reg_mask
= REG_OUT_MASK
;
2936 reg_mask
= REG_IN_MASK
;
2939 if (is_reg_allocated(op
->reg
))
2941 ("asm regvar requests register that's taken already");
2948 case '=': // Operand is written-to
2950 case '+': // Operand is both READ and written-to
2953 case '&': // Operand is clobbered before the instruction is done using the input operands
2954 if (j
>= nb_outputs
)
2955 tcc_error("'%c' modifier can only be applied to outputs",
2957 reg_mask
= REG_IN_MASK
| REG_OUT_MASK
;
2959 case 'l': // In non-thumb mode, alias for 'r'--otherwise r0-r7 [ARM]
2960 case 'r': // general-purpose register
2961 case 'p': // loadable/storable address
2962 /* any general register */
2963 for (reg
= 0; reg
<= 8; reg
++) {
2964 if (!is_reg_allocated(reg
))
2969 /* now we can reload in the register */
2972 regs_allocated
[reg
] |= reg_mask
;
2974 case 'I': // integer that is valid as an data processing instruction immediate (0...255, rotated by a multiple of two)
2975 case 'J': // integer in the range -4095 to 4095 [ARM]
2976 case 'K': // integer that satisfies constraint I when inverted (one's complement)
2977 case 'L': // integer that satisfies constraint I when inverted (two's complement)
2978 case 'i': // immediate integer operand, including symbolic constants
2979 if (!((op
->vt
->r
& (VT_VALMASK
| VT_LVAL
)) == VT_CONST
))
2982 case 'M': // integer in the range 0 to 32
2984 ((op
->vt
->r
& (VT_VALMASK
| VT_LVAL
| VT_SYM
)) ==
2988 case 'm': // memory operand
2990 /* nothing special to do because the operand is already in
2991 memory, except if the pointer itself is stored in a
2992 memory variable (VT_LLOCAL case) */
2993 /* XXX: fix constant case */
2994 /* if it is a reference to a memory zone, it must lie
2995 in a register, so we reserve the register in the
2996 input registers and a load will be generated
2998 if (j
< nb_outputs
|| c
== 'm') {
2999 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
) {
3000 /* any general register */
3001 for (reg
= 0; reg
<= 8; reg
++) {
3002 if (!(regs_allocated
[reg
] & REG_IN_MASK
))
3007 /* now we can reload in the register */
3008 regs_allocated
[reg
] |= REG_IN_MASK
;
3015 tcc_error("asm constraint %d ('%s') could not be satisfied",
3019 /* if a reference is present for that operand, we assign it too */
3020 if (op
->input_index
>= 0) {
3021 operands
[op
->input_index
].reg
= op
->reg
;
3022 operands
[op
->input_index
].is_llong
= op
->is_llong
;
3026 /* compute out_reg. It is used to store outputs registers to memory
3027 locations references by pointers (VT_LLOCAL case) */
3029 for (i
= 0; i
< nb_operands
; i
++) {
3032 (op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& !op
->is_memory
) {
3033 for (reg
= 0; reg
<= 8; reg
++) {
3034 if (!(regs_allocated
[reg
] & REG_OUT_MASK
))
3037 tcc_error("could not find free output register for reloading");
3044 /* print sorted constraints */
3046 for (i
= 0; i
< nb_operands
; i
++) {
3049 printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
3051 op
->id
? get_tok_str(op
->id
, NULL
) : "",
3052 op
->constraint
, op
->vt
->r
, op
->reg
);
3055 printf("out_reg=%d\n", *pout_reg
);
3059 ST_FUNC
void asm_clobber(uint8_t *clobber_regs
, const char *str
)
3064 if (!strcmp(str
, "memory") ||
3065 !strcmp(str
, "cc") ||
3066 !strcmp(str
, "flags"))
3068 ts
= tok_alloc(str
, strlen(str
));
3069 reg
= asm_parse_regvar(ts
->tok
);
3071 tcc_error("invalid clobber register '%s'", str
);
3073 clobber_regs
[reg
] = 1;
3076 /* If T refers to a register then return the register number and type.
3077 Otherwise return -1. */
3078 ST_FUNC
int asm_parse_regvar (int t
)
3080 if (t
>= TOK_ASM_r0
&& t
<= TOK_ASM_pc
) { /* register name */
3083 return TOK_ASM_r11
- TOK_ASM_r0
;
3085 return TOK_ASM_r12
- TOK_ASM_r0
;
3087 return TOK_ASM_r13
- TOK_ASM_r0
;
3089 return TOK_ASM_r14
- TOK_ASM_r0
;
3091 return TOK_ASM_r15
- TOK_ASM_r0
;
3093 return t
- TOK_ASM_r0
;
3099 /*************************************************************/
3100 #endif /* ndef TARGET_DEFS_ONLY */