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? [ok]
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_vcvt_data_processing_opcode(TCCState
*s1
, int token
) {
1906 uint8_t coprocessor
= 0;
1908 uint8_t opcode1
= 11;
1909 uint8_t opcode2
= 2;
1911 switch (ARM_INSTRUCTION_GROUP(token
)) {
1912 case TOK_ASM_vcvtreq_s32_f64
:
1913 case TOK_ASM_vcvtreq_u32_f64
:
1914 case TOK_ASM_vcvteq_s32_f64
:
1915 case TOK_ASM_vcvteq_u32_f64
:
1916 case TOK_ASM_vcvteq_f64_s32
:
1917 case TOK_ASM_vcvteq_f64_u32
:
1918 case TOK_ASM_vcvteq_f32_f64
:
1919 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
1921 case TOK_ASM_vcvtreq_s32_f32
:
1922 case TOK_ASM_vcvtreq_u32_f32
:
1923 case TOK_ASM_vcvteq_s32_f32
:
1924 case TOK_ASM_vcvteq_u32_f32
:
1925 case TOK_ASM_vcvteq_f32_s32
:
1926 case TOK_ASM_vcvteq_f32_u32
:
1927 case TOK_ASM_vcvteq_f64_f32
:
1928 coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
1931 tcc_error("Unknown coprocessor for instruction '%s'", get_tok_str(token
, NULL
));
1935 parse_operand(s1
, &ops
[0]);
1936 ops
[1].type
= OP_IM8
;
1938 /* floating-point -> integer */
1939 switch (ARM_INSTRUCTION_GROUP(token
)) {
1940 case TOK_ASM_vcvtreq_s32_f32
:
1941 case TOK_ASM_vcvtreq_s32_f64
:
1942 case TOK_ASM_vcvteq_s32_f32
:
1943 case TOK_ASM_vcvteq_s32_f64
:
1944 ops
[1].e
.v
|= 1; // signed
1946 case TOK_ASM_vcvteq_u32_f32
:
1947 case TOK_ASM_vcvteq_u32_f64
:
1948 case TOK_ASM_vcvtreq_u32_f32
:
1949 case TOK_ASM_vcvtreq_u32_f64
:
1950 ops
[1].e
.v
|= 4; // to_integer (opc2)
1952 /* floating-point size conversion */
1953 case TOK_ASM_vcvteq_f64_f32
:
1954 case TOK_ASM_vcvteq_f32_f64
:
1963 parse_operand(s1
, &ops
[2]);
1965 switch (ARM_INSTRUCTION_GROUP(token
)) {
1966 /* floating-point -> integer */
1967 case TOK_ASM_vcvteq_s32_f32
:
1968 case TOK_ASM_vcvteq_s32_f64
:
1969 case TOK_ASM_vcvteq_u32_f32
:
1970 case TOK_ASM_vcvteq_u32_f64
:
1971 opcode2
|= 4; // round_zero
1974 /* integer -> floating-point */
1975 case TOK_ASM_vcvteq_f64_s32
:
1976 case TOK_ASM_vcvteq_f32_s32
:
1977 opcode2
|= 4; // signed--special
1980 /* floating-point size conversion */
1981 case TOK_ASM_vcvteq_f64_f32
:
1982 case TOK_ASM_vcvteq_f32_f64
:
1983 opcode2
|= 4; // always set
1987 switch (ARM_INSTRUCTION_GROUP(token
)) {
1988 case TOK_ASM_vcvteq_f64_u32
:
1989 case TOK_ASM_vcvteq_f64_s32
:
1990 case TOK_ASM_vcvteq_f64_f32
:
1991 if (ops
[0].type
== OP_VREG64
&& ops
[2].type
== OP_VREG32
) {
1993 expect("d<number>, s<number>");
1998 if (coprocessor
== CP_SINGLE_PRECISION_FLOAT
) {
1999 if (ops
[0].type
== OP_VREG32
&& ops
[2].type
== OP_VREG32
) {
2001 expect("s<number>, s<number>");
2004 } else if (coprocessor
== CP_DOUBLE_PRECISION_FLOAT
) {
2005 if (ops
[0].type
== OP_VREG32
&& ops
[2].type
== OP_VREG64
) {
2007 expect("s<number>, d<number>");
2013 if (ops
[2].type
== OP_VREG32
) {
2018 if (ops
[0].type
== OP_VREG32
) {
2023 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);
2026 static void asm_floating_point_data_processing_opcode(TCCState
*s1
, int token
) {
2027 uint8_t coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
2028 uint8_t opcode1
= 0;
2029 uint8_t opcode2
= 0; // (0 || 2) | register selection
2033 int nb_arm_regs
= 0;
2036 Instruction opcode opcode2 Reason
2037 =============================================================
2038 - 1?00 ?1? Undefined
2039 VFNMS 1?01 ?0? Must be unconditional
2040 VFNMA 1?01 ?1? Must be unconditional
2041 VFMA 1?10 ?0? Must be unconditional
2042 VFMS 1?10 ?1? Must be unconditional
2053 switch (ARM_INSTRUCTION_GROUP(token
)) {
2054 case TOK_ASM_vmlaeq_f64
:
2055 case TOK_ASM_vmlseq_f64
:
2056 case TOK_ASM_vnmlseq_f64
:
2057 case TOK_ASM_vnmlaeq_f64
:
2058 case TOK_ASM_vmuleq_f64
:
2059 case TOK_ASM_vnmuleq_f64
:
2060 case TOK_ASM_vaddeq_f64
:
2061 case TOK_ASM_vsubeq_f64
:
2062 case TOK_ASM_vdiveq_f64
:
2063 case TOK_ASM_vnegeq_f64
:
2064 case TOK_ASM_vabseq_f64
:
2065 case TOK_ASM_vsqrteq_f64
:
2066 case TOK_ASM_vcmpeq_f64
:
2067 case TOK_ASM_vcmpeeq_f64
:
2068 case TOK_ASM_vmoveq_f64
:
2069 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
2072 switch (ARM_INSTRUCTION_GROUP(token
)) {
2073 case TOK_ASM_vmoveq_f32
:
2074 case TOK_ASM_vmoveq_f64
:
2079 for (nb_ops
= 0; nb_ops
< 3; ) {
2080 // Note: Necessary because parse_operand can't parse decimal numerals.
2081 if (nb_ops
== 1 && (tok
== '#' || tok
== '$' || tok
== TOK_PPNUM
|| tok
== '-')) {
2082 asm_floating_point_immediate_data_processing_opcode_tail(s1
, token
, coprocessor
, ops
[0].reg
);
2085 parse_operand(s1
, &ops
[nb_ops
]);
2086 if (vmov
&& ops
[nb_ops
].type
== OP_REG32
) {
2088 } else if (ops
[nb_ops
].type
== OP_VREG32
) {
2089 if (coprocessor
!= CP_SINGLE_PRECISION_FLOAT
) {
2090 expect("'s<number>'");
2093 } else if (ops
[nb_ops
].type
== OP_VREG64
) {
2094 if (coprocessor
!= CP_DOUBLE_PRECISION_FLOAT
) {
2095 expect("'d<number>'");
2099 expect("floating point register");
2109 if (nb_arm_regs
== 0) {
2110 if (nb_ops
== 2) { // implicit
2111 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
2112 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
2116 tcc_error("Not enough operands for '%s' (%u)", get_tok_str(token
, NULL
), nb_ops
);
2121 switch (ARM_INSTRUCTION_GROUP(token
)) {
2122 case TOK_ASM_vmlaeq_f32
:
2123 case TOK_ASM_vmlaeq_f64
:
2127 case TOK_ASM_vmlseq_f32
:
2128 case TOK_ASM_vmlseq_f64
:
2132 case TOK_ASM_vnmlseq_f32
:
2133 case TOK_ASM_vnmlseq_f64
:
2137 case TOK_ASM_vnmlaeq_f32
:
2138 case TOK_ASM_vnmlaeq_f64
:
2142 case TOK_ASM_vmuleq_f32
:
2143 case TOK_ASM_vmuleq_f64
:
2147 case TOK_ASM_vnmuleq_f32
:
2148 case TOK_ASM_vnmuleq_f64
:
2152 case TOK_ASM_vaddeq_f32
:
2153 case TOK_ASM_vaddeq_f64
:
2157 case TOK_ASM_vsubeq_f32
:
2158 case TOK_ASM_vsubeq_f64
:
2162 case TOK_ASM_vdiveq_f32
:
2163 case TOK_ASM_vdiveq_f64
:
2167 case TOK_ASM_vnegeq_f32
:
2168 case TOK_ASM_vnegeq_f64
:
2169 opcode1
= 11; // Other" instruction
2171 ops
[1].type
= OP_IM8
;
2174 case TOK_ASM_vabseq_f32
:
2175 case TOK_ASM_vabseq_f64
:
2176 opcode1
= 11; // "Other" instruction
2178 ops
[1].type
= OP_IM8
;
2181 case TOK_ASM_vsqrteq_f32
:
2182 case TOK_ASM_vsqrteq_f64
:
2183 opcode1
= 11; // "Other" instruction
2185 ops
[1].type
= OP_IM8
;
2188 case TOK_ASM_vcmpeq_f32
:
2189 case TOK_ASM_vcmpeq_f64
:
2190 opcode1
= 11; // "Other" instruction
2192 ops
[1].type
= OP_IM8
;
2195 case TOK_ASM_vcmpeeq_f32
:
2196 case TOK_ASM_vcmpeeq_f64
:
2197 opcode1
= 11; // "Other" instruction
2199 ops
[1].type
= OP_IM8
;
2202 case TOK_ASM_vmoveq_f32
:
2203 case TOK_ASM_vmoveq_f64
:
2204 if (nb_arm_regs
> 0) { // vmov.f32 r2, s3 or similar
2205 asm_floating_point_reg_arm_reg_transfer_opcode_tail(s1
, token
, coprocessor
, nb_arm_regs
, nb_ops
, ops
);
2208 opcode1
= 11; // "Other" instruction
2210 ops
[1].type
= OP_IM8
;
2215 expect("known floating point instruction");
2219 if (coprocessor
== CP_SINGLE_PRECISION_FLOAT
) {
2220 if (ops
[2].type
== OP_VREG32
) {
2226 if (ops
[1].type
== OP_VREG32
) {
2232 if (ops
[0].type
== OP_VREG32
) {
2239 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);
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 */