2 * ARM specific functions for TCC assembler
4 * Copyright (c) 2001, 2002 Fabrice Bellard
5 * Copyright (c) 2020 Danny Milosavljevic
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #ifdef TARGET_DEFS_ONLY
24 #define CONFIG_TCC_ASM
25 #define NB_ASM_REGS 16
27 ST_FUNC
void g(int c
);
28 ST_FUNC
void gen_le16(int c
);
29 ST_FUNC
void gen_le32(int c
);
31 /*************************************************************/
33 /*************************************************************/
47 #define OP_REG32 (1 << OPT_REG32)
48 #define OP_VREG32 (1 << OPT_VREG32)
49 #define OP_VREG64 (1 << OPT_VREG64)
50 #define OP_REG (OP_REG32 | OP_VREG32 | OP_VREG64)
51 #define OP_IM32 (1 << OPT_IM32)
52 #define OP_IM8 (1 << OPT_IM8)
53 #define OP_IM8N (1 << OPT_IM8N)
54 #define OP_REGSET32 (1 << OPT_REGSET32)
56 typedef struct Operand
{
65 /* Read the VFP register referred to by token T.
66 If OK, returns its number.
67 If not OK, returns -1. */
68 static int asm_parse_vfp_regvar(int t
, int double_precision
)
70 if (double_precision
) {
71 if (t
>= TOK_ASM_d0
&& t
<= TOK_ASM_d15
)
72 return t
- TOK_ASM_d0
;
74 if (t
>= TOK_ASM_s0
&& t
<= TOK_ASM_s31
)
75 return t
- TOK_ASM_s0
;
80 /* Parse a text containing operand and store the result in OP */
81 static void parse_operand(TCCState
*s1
, Operand
*op
)
89 if (tok
== '{') { // regset literal
91 while (tok
!= '}' && tok
!= TOK_EOF
) {
92 reg
= asm_parse_regvar(tok
);
96 next(); // skip register name
98 if ((1 << reg
) < regset
)
99 tcc_warning("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
107 // ARM instructions don't support empty regset.
108 tcc_error("empty register list is not supported");
110 op
->type
= OP_REGSET32
;
114 } else if ((reg
= asm_parse_regvar(tok
)) != -1) {
115 next(); // skip register name
117 op
->reg
= (uint8_t) reg
;
119 } else if ((reg
= asm_parse_vfp_regvar(tok
, 0)) != -1) {
120 next(); // skip register name
121 op
->type
= OP_VREG32
;
122 op
->reg
= (uint8_t) reg
;
124 } else if ((reg
= asm_parse_vfp_regvar(tok
, 1)) != -1) {
125 next(); // skip register name
126 op
->type
= OP_VREG64
;
127 op
->reg
= (uint8_t) reg
;
129 } else if (tok
== '#' || tok
== '$') {
131 next(); // skip '#' or '$'
137 if ((int) op
->e
.v
< 0 && (int) op
->e
.v
>= -255)
139 else if (op
->e
.v
== (uint8_t)op
->e
.v
)
145 /* XXX: make it faster ? */
146 ST_FUNC
void g(int c
)
152 if (ind1
> cur_text_section
->data_allocated
)
153 section_realloc(cur_text_section
, ind1
);
154 cur_text_section
->data
[ind
] = c
;
158 ST_FUNC
void gen_le16 (int i
)
164 ST_FUNC
void gen_le32 (int i
)
170 if (ind1
> cur_text_section
->data_allocated
)
171 section_realloc(cur_text_section
, ind1
);
172 cur_text_section
->data
[ind
++] = i
& 0xFF;
173 cur_text_section
->data
[ind
++] = (i
>> 8) & 0xFF;
174 cur_text_section
->data
[ind
++] = (i
>> 16) & 0xFF;
175 cur_text_section
->data
[ind
++] = (i
>> 24) & 0xFF;
178 ST_FUNC
void gen_expr32(ExprValue
*pe
)
183 static uint32_t condition_code_of_token(int token
) {
184 if (token
< TOK_ASM_nopeq
) {
185 expect("condition-enabled instruction");
187 return (token
- TOK_ASM_nopeq
) & 15;
190 static void asm_emit_opcode(int token
, uint32_t opcode
) {
191 gen_le32((condition_code_of_token(token
) << 28) | opcode
);
194 static void asm_emit_unconditional_opcode(uint32_t opcode
) {
198 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
)
200 uint32_t opcode
= 0xe000000;
201 if (inter_processor_transfer
)
203 //assert(cp_opcode < 16);
204 opcode
|= cp_opcode
<< 20;
205 //assert(cp_n_operand_register < 16);
206 opcode
|= cp_n_operand_register
<< 16;
207 //assert(cp_destination_register < 16);
208 opcode
|= cp_destination_register
<< 12;
209 //assert(cp_number < 16);
210 opcode
|= cp_number
<< 8;
211 //assert(cp_information < 8);
212 opcode
|= cp_opcode2
<< 5;
213 //assert(cp_m_operand_register < 16);
214 opcode
|= cp_m_operand_register
;
215 asm_emit_unconditional_opcode((high_nibble
<< 28) | opcode
);
218 static void asm_nullary_opcode(int token
)
220 switch (ARM_INSTRUCTION_GROUP(token
)) {
222 asm_emit_opcode(token
, 0xd << 21); // mov r0, r0
225 asm_emit_opcode(token
, 0x320f002);
227 asm_emit_opcode(token
, 0x320f003);
230 expect("nullary instruction");
234 static void asm_unary_opcode(TCCState
*s1
, int token
)
237 parse_operand(s1
, &op
);
239 switch (ARM_INSTRUCTION_GROUP(token
)) {
242 if (op
.type
!= OP_IM8
)
243 expect("immediate 8-bit unsigned integer");
245 /* Note: Dummy operand (ignored by processor): ARM ref documented 0...255, ARM instruction set documented 24 bit */
246 asm_emit_opcode(token
, (0xf << 24) | op
.e
.v
);
250 expect("unary instruction");
254 static void asm_binary_opcode(TCCState
*s1
, int token
)
258 uint32_t encoded_rotation
= 0;
260 parse_operand(s1
, &ops
[0]);
262 parse_operand(s1
, &ops
[1]);
263 if (ops
[0].type
!= OP_REG32
) {
264 expect("(destination operand) register");
267 if (ops
[0].reg
== 15) {
268 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
271 if (ops
[0].reg
== 13)
272 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token
, NULL
));
274 if (ops
[1].type
!= OP_REG32
) {
275 switch (ARM_INSTRUCTION_GROUP(token
)) {
278 if (ops
[1].type
== OP_IM8
|| ops
[1].type
== OP_IM8N
|| ops
[1].type
== OP_IM32
) {
279 if (ops
[1].e
.v
>= 0 && ops
[1].e
.v
<= 0xFFFF) {
280 uint16_t immediate_value
= ops
[1].e
.v
;
281 switch (ARM_INSTRUCTION_GROUP(token
)) {
283 asm_emit_opcode(token
, 0x3400000 | (ops
[0].reg
<< 12) | (immediate_value
& 0xF000) << 4 | (immediate_value
& 0xFFF));
286 asm_emit_opcode(token
, 0x3000000 | (ops
[0].reg
<< 12) | (immediate_value
& 0xF000) << 4 | (immediate_value
& 0xFFF));
290 expect("(source operand) immediate 16 bit value");
292 expect("(source operand) immediate");
295 expect("(source operand) register");
300 if (ops
[1].reg
== 15) {
301 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
304 if (ops
[1].reg
== 13)
305 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token
, NULL
));
309 if (tok
== TOK_ASM_ror
) {
310 next(); // skip 'ror'
311 parse_operand(s1
, &rotation
);
312 if (rotation
.type
!= OP_IM8
) {
313 expect("immediate value for rotation");
315 amount
= rotation
.e
.v
;
318 encoded_rotation
= 1 << 10;
321 encoded_rotation
= 2 << 10;
324 encoded_rotation
= 3 << 10;
327 expect("'8' or '16' or '24'");
332 switch (ARM_INSTRUCTION_GROUP(token
)) {
334 if (encoded_rotation
)
335 tcc_error("clz does not support rotation");
336 asm_emit_opcode(token
, 0x16f0f10 | (ops
[0].reg
<< 12) | ops
[1].reg
);
339 asm_emit_opcode(token
, 0x6af0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
342 asm_emit_opcode(token
, 0x6bf0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
345 asm_emit_opcode(token
, 0x6ef0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
348 asm_emit_opcode(token
, 0x6ff0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
351 expect("binary instruction");
355 static void asm_coprocessor_opcode(TCCState
*s1
, int token
) {
359 uint8_t registers
[3];
364 if (tok
>= TOK_ASM_p0
&& tok
<= TOK_ASM_p15
) {
365 coprocessor
= tok
- TOK_ASM_p0
;
368 expect("'p<number>'");
371 parse_operand(s1
, &opcode1
);
372 if (opcode1
.type
!= OP_IM8
|| opcode1
.e
.v
> 15) {
373 tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token
, NULL
));
376 for (i
= 0; i
< 3; ++i
) {
378 if (i
== 0 && token
!= TOK_ASM_cdp2
&& (ARM_INSTRUCTION_GROUP(token
) == TOK_ASM_mrceq
|| ARM_INSTRUCTION_GROUP(token
) == TOK_ASM_mcreq
)) {
379 if (tok
>= TOK_ASM_r0
&& tok
<= TOK_ASM_r15
) {
380 registers
[i
] = tok
- TOK_ASM_r0
;
383 expect("'r<number>'");
386 if (tok
>= TOK_ASM_c0
&& tok
<= TOK_ASM_c15
) {
387 registers
[i
] = tok
- TOK_ASM_c0
;
390 expect("'c<number>'");
396 parse_operand(s1
, &opcode2
);
398 opcode2
.type
= OP_IM8
;
401 if (opcode2
.type
!= OP_IM8
|| opcode2
.e
.v
> 15) {
402 tcc_error("opcode2 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token
, NULL
));
405 if (token
== TOK_ASM_cdp2
) {
407 asm_emit_coprocessor_opcode(high_nibble
, coprocessor
, opcode1
.e
.v
, registers
[0], registers
[1], registers
[2], opcode2
.e
.v
, 0);
410 high_nibble
= condition_code_of_token(token
);
412 switch (ARM_INSTRUCTION_GROUP(token
)) {
414 asm_emit_coprocessor_opcode(high_nibble
, coprocessor
, opcode1
.e
.v
, registers
[0], registers
[1], registers
[2], opcode2
.e
.v
, 0);
417 // opcode1 encoding changes! highest and lowest bit gone.
421 // opcode1 encoding changes! highest and lowest bit gone.
422 if (opcode1
.e
.v
> 7) {
423 tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 7", get_tok_str(token
, NULL
));
425 asm_emit_coprocessor_opcode(high_nibble
, coprocessor
, (opcode1
.e
.v
<< 1) | mrc
, registers
[0], registers
[1], registers
[2], opcode2
.e
.v
, 1);
428 expect("known instruction");
432 /* data processing and single data transfer instructions only */
433 #define ENCODE_RN(register_index) ((register_index) << 16)
434 #define ENCODE_RD(register_index) ((register_index) << 12)
435 #define ENCODE_SET_CONDITION_CODES (1 << 20)
437 /* Note: For data processing instructions, "1" means immediate.
438 Note: For single data transfer instructions, "0" means immediate. */
439 #define ENCODE_IMMEDIATE_FLAG (1 << 25)
441 #define ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER (1 << 4)
442 #define ENCODE_BARREL_SHIFTER_MODE_LSL (0 << 5)
443 #define ENCODE_BARREL_SHIFTER_MODE_LSR (1 << 5)
444 #define ENCODE_BARREL_SHIFTER_MODE_ASR (2 << 5)
445 #define ENCODE_BARREL_SHIFTER_MODE_ROR (3 << 5)
446 #define ENCODE_BARREL_SHIFTER_REGISTER(register_index) ((register_index) << 8)
447 #define ENCODE_BARREL_SHIFTER_IMMEDIATE(value) ((value) << 7)
449 static void asm_block_data_transfer_opcode(TCCState
*s1
, int token
)
455 parse_operand(s1
, &ops
[0]);
461 next(); // skip comma
462 parse_operand(s1
, &ops
[1]);
466 expect("at least one operand");
467 } else if (ops
[nb_ops
- 1].type
!= OP_REGSET32
) {
468 expect("(last operand) register list");
471 // block data transfer: 1 0 0 P U S W L << 20 (general case):
473 // Rn: bits 19...16 base register
474 // Register List: bits 15...0
476 switch (ARM_INSTRUCTION_GROUP(token
)) {
477 case TOK_ASM_pusheq
: // TODO: Optimize 1-register case to: str ?, [sp, #-4]!
478 // Instruction: 1 I=0 P=1 U=0 S=0 W=1 L=0 << 20, op 1101
481 // Register List: bits 15...0
483 expect("exactly one operand");
485 asm_emit_opcode(token
, (0x92d << 16) | ops
[0].regset
); // TODO: base register ?
487 case TOK_ASM_popeq
: // TODO: Optimize 1-register case to: ldr ?, [sp], #4
488 // Instruction: 1 I=0 P=0 U=1 S=0 W=0 L=1 << 20, op 1101
491 // Register List: bits 15...0
493 expect("exactly one operand");
495 asm_emit_opcode(token
, (0x8bd << 16) | ops
[0].regset
); // TODO: base register ?
497 case TOK_ASM_stmdaeq
:
498 case TOK_ASM_ldmdaeq
:
501 case TOK_ASM_stmiaeq
:
502 case TOK_ASM_ldmiaeq
:
503 case TOK_ASM_stmdbeq
:
504 case TOK_ASM_ldmdbeq
:
505 case TOK_ASM_stmibeq
:
506 case TOK_ASM_ldmibeq
:
507 switch (ARM_INSTRUCTION_GROUP(token
)) {
508 case TOK_ASM_stmdaeq
: // post-decrement store
511 case TOK_ASM_ldmdaeq
: // post-decrement load
514 case TOK_ASM_stmeq
: // post-increment store
515 case TOK_ASM_stmiaeq
: // post-increment store
518 case TOK_ASM_ldmeq
: // post-increment load
519 case TOK_ASM_ldmiaeq
: // post-increment load
522 case TOK_ASM_stmdbeq
: // pre-decrement store
525 case TOK_ASM_ldmdbeq
: // pre-decrement load
528 case TOK_ASM_stmibeq
: // pre-increment store
531 case TOK_ASM_ldmibeq
: // pre-increment load
535 tcc_error("internal error: This place should not be reached (fallback in asm_block_data_transfer_opcode)");
539 // Register List: lower bits
541 expect("exactly two operands");
542 else if (ops
[0].type
!= OP_REG32
)
543 expect("(first operand) register");
546 opcode
|= 1 << 21; // writeback
547 asm_emit_opcode(token
, opcode
| ENCODE_RN(ops
[0].reg
) | ops
[1].regset
);
551 expect("block data transfer instruction");
555 /* Parses shift directive and returns the parts that would have to be set in the opcode because of it.
556 Does not encode the actual shift amount.
557 It's not an error if there is no shift directive.
559 NB_SHIFT: will be set to 1 iff SHIFT is filled. Note that for rrx, there's no need to fill SHIFT.
560 SHIFT: will be filled in with the shift operand to use, if any. */
561 static uint32_t asm_parse_optional_shift(TCCState
* s1
, int* nb_shift
, Operand
* shift
)
575 opcode
= ENCODE_BARREL_SHIFTER_MODE_LSL
;
578 opcode
= ENCODE_BARREL_SHIFTER_MODE_ASR
;
581 opcode
= ENCODE_BARREL_SHIFTER_MODE_LSR
;
584 opcode
= ENCODE_BARREL_SHIFTER_MODE_ROR
;
588 parse_operand(s1
, shift
);
593 opcode
= ENCODE_BARREL_SHIFTER_MODE_ROR
;
599 static uint32_t asm_encode_shift(Operand
* shift
)
602 uint32_t operands
= 0;
603 switch (shift
->type
) {
605 if (shift
->reg
== 15)
606 tcc_error("r15 cannot be used as a shift count");
608 operands
= ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER
;
609 operands
|= ENCODE_BARREL_SHIFTER_REGISTER(shift
->reg
);
614 if (amount
> 0 && amount
< 32)
615 operands
= ENCODE_BARREL_SHIFTER_IMMEDIATE(amount
);
617 tcc_error("shift count out of range");
620 tcc_error("unknown shift amount");
625 static void asm_data_processing_opcode(TCCState
*s1
, int token
)
631 uint32_t operands
= 0;
633 /* modulo 16 entries per instruction for the different condition codes */
634 uint32_t opcode_idx
= (ARM_INSTRUCTION_GROUP(token
) - TOK_ASM_andeq
) >> 4;
635 uint32_t opcode_nos
= opcode_idx
>> 1; // without "s"; "OpCode" in ARM docs
637 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ) {
638 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
)
640 parse_operand(s1
, &ops
[nb_ops
]);
648 operands
|= asm_parse_optional_shift(s1
, &nb_shift
, &shift
);
650 expect("at least two operands");
651 else if (nb_ops
== 2) {
652 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
653 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
655 } else if (nb_ops
== 3) {
656 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
657 tcc_error("'%s' cannot be used with three operands", get_tok_str(token
, NULL
));
661 expect("two or three operands");
664 uint32_t immediate_value
;
665 uint8_t half_immediate_rotation
;
666 if (nb_shift
&& shift
.type
== OP_REG32
) {
667 if ((ops
[0].type
== OP_REG32
&& ops
[0].reg
== 15) ||
668 (ops
[1].type
== OP_REG32
&& ops
[1].reg
== 15)) {
669 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
673 // data processing (general case):
675 // Rn: bits 19...16 (first operand)
676 // Rd: bits 15...12 (destination)
677 // Operand2: bits 11...0 (second operand); depending on I that's either a register or an immediate
679 // bits 24...21: "OpCode"--see below
681 /* operations in the token list are ordered by opcode */
682 opcode
= opcode_nos
<< 21; // drop "s"
683 if (ops
[0].type
!= OP_REG32
)
684 expect("(destination operand) register");
685 else if (opcode_nos
== 0xa || opcode_nos
== 0xb || opcode_nos
== 0x8 || opcode_nos
== 0x9) // cmp, cmn, tst, teq
686 operands
|= ENCODE_SET_CONDITION_CODES
; // force S set, otherwise it's a completely different instruction.
688 operands
|= ENCODE_RD(ops
[0].reg
);
689 if (ops
[1].type
!= OP_REG32
)
690 expect("(first source operand) register");
691 else if (!(opcode_nos
== 0xd || opcode_nos
== 0xf)) // not: mov, mvn (those have only one source operand)
692 operands
|= ENCODE_RN(ops
[1].reg
);
693 switch (ops
[2].type
) {
695 operands
|= ops
[2].reg
;
699 operands
|= ENCODE_IMMEDIATE_FLAG
;
700 immediate_value
= ops
[2].e
.v
;
701 for (half_immediate_rotation
= 0; half_immediate_rotation
< 16; ++half_immediate_rotation
) {
702 if (immediate_value
>= 0x00 && immediate_value
< 0x100)
704 // rotate left by two
705 immediate_value
= ((immediate_value
& 0x3FFFFFFF) << 2) | ((immediate_value
& 0xC0000000) >> 30);
707 if (half_immediate_rotation
>= 16) {
710 operands
|= immediate_value
;
711 operands
|= half_immediate_rotation
<< 8;
714 case OP_IM8N
: // immediate negative value
715 operands
|= ENCODE_IMMEDIATE_FLAG
;
716 immediate_value
= ops
[2].e
.v
;
717 /* Instruction swapping:
718 0001 = EOR - Rd:= Op1 EOR Op2 -> difficult
719 0011 = RSB - Rd:= Op2 - Op1 -> difficult
720 0111 = RSC - Rd:= Op2 - Op1 + C -> difficult
721 1000 = TST - CC on: Op1 AND Op2 -> difficult
722 1001 = TEQ - CC on: Op1 EOR Op2 -> difficult
723 1100 = ORR - Rd:= Op1 OR Op2 -> difficult
725 switch (opcode_nos
) {
726 case 0x0: // AND - Rd:= Op1 AND Op2
727 opcode
= 0xe << 21; // BIC
728 immediate_value
= ~immediate_value
;
730 case 0x2: // SUB - Rd:= Op1 - Op2
731 opcode
= 0x4 << 21; // ADD
732 immediate_value
= -immediate_value
;
734 case 0x4: // ADD - Rd:= Op1 + Op2
735 opcode
= 0x2 << 21; // SUB
736 immediate_value
= -immediate_value
;
738 case 0x5: // ADC - Rd:= Op1 + Op2 + C
739 opcode
= 0x6 << 21; // SBC
740 immediate_value
= ~immediate_value
;
742 case 0x6: // SBC - Rd:= Op1 - Op2 + C
743 opcode
= 0x5 << 21; // ADC
744 immediate_value
= ~immediate_value
;
746 case 0xa: // CMP - CC on: Op1 - Op2
747 opcode
= 0xb << 21; // CMN
748 immediate_value
= -immediate_value
;
750 case 0xb: // CMN - CC on: Op1 + Op2
751 opcode
= 0xa << 21; // CMP
752 immediate_value
= -immediate_value
;
754 case 0xd: // MOV - Rd:= Op2
755 opcode
= 0xf << 21; // MVN
756 immediate_value
= ~immediate_value
;
758 case 0xe: // BIC - Rd:= Op1 AND NOT Op2
759 opcode
= 0x0 << 21; // AND
760 immediate_value
= ~immediate_value
;
762 case 0xf: // MVN - Rd:= NOT Op2
763 opcode
= 0xd << 21; // MOV
764 immediate_value
= ~immediate_value
;
767 tcc_error("cannot use '%s' with a negative immediate value", get_tok_str(token
, NULL
));
769 for (half_immediate_rotation
= 0; half_immediate_rotation
< 16; ++half_immediate_rotation
) {
770 if (immediate_value
>= 0x00 && immediate_value
< 0x100)
772 // rotate left by two
773 immediate_value
= ((immediate_value
& 0x3FFFFFFF) << 2) | ((immediate_value
& 0xC0000000) >> 30);
775 if (half_immediate_rotation
>= 16) {
776 immediate_value
= ops
[2].e
.v
;
777 tcc_error("immediate value 0x%X cannot be encoded into ARM immediate", (unsigned) immediate_value
);
779 operands
|= immediate_value
;
780 operands
|= half_immediate_rotation
<< 8;
783 expect("(second source operand) register or immediate value");
787 if (operands
& ENCODE_IMMEDIATE_FLAG
)
788 tcc_error("immediate rotation not implemented");
790 operands
|= asm_encode_shift(&shift
);
793 /* S=0 and S=1 entries alternate one after another, in that order */
794 opcode
|= (opcode_idx
& 1) ? ENCODE_SET_CONDITION_CODES
: 0;
795 asm_emit_opcode(token
, opcode
| operands
);
799 static void asm_shift_opcode(TCCState
*s1
, int token
)
803 int definitely_neutral
= 0;
804 uint32_t opcode
= 0xd << 21; // MOV
805 uint32_t operands
= 0;
807 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
808 parse_operand(s1
, &ops
[nb_ops
]);
816 expect("at least two operands");
819 if (ops
[0].type
!= OP_REG32
) {
820 expect("(destination operand) register");
822 operands
|= ENCODE_RD(ops
[0].reg
);
825 switch (ARM_INSTRUCTION_GROUP(token
)) {
827 opcode
|= ENCODE_SET_CONDITION_CODES
;
830 if (ops
[1].type
== OP_REG32
) {
831 operands
|= ops
[1].reg
;
832 operands
|= ENCODE_BARREL_SHIFTER_MODE_ROR
;
833 asm_emit_opcode(token
, opcode
| operands
);
835 tcc_error("(first source operand) register");
838 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
839 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
844 expect("two or three operands");
847 switch (ARM_INSTRUCTION_GROUP(token
)) {
852 opcode
|= ENCODE_SET_CONDITION_CODES
;
856 switch (ops
[1].type
) {
858 operands
|= ops
[1].reg
;
861 operands
|= ENCODE_IMMEDIATE_FLAG
;
862 operands
|= ops
[1].e
.v
;
863 tcc_error("Using an immediate value as the source operand is not possible with '%s' instruction on ARM", get_tok_str(token
, NULL
));
866 switch (ops
[2].type
) {
868 if ((ops
[0].type
== OP_REG32
&& ops
[0].reg
== 15) ||
869 (ops
[1].type
== OP_REG32
&& ops
[1].reg
== 15)) {
870 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
872 operands
|= asm_encode_shift(&ops
[2]);
876 operands
|= asm_encode_shift(&ops
[2]);
878 definitely_neutral
= 1;
882 if (!definitely_neutral
) switch (ARM_INSTRUCTION_GROUP(token
)) {
885 operands
|= ENCODE_BARREL_SHIFTER_MODE_LSL
;
889 operands
|= ENCODE_BARREL_SHIFTER_MODE_LSR
;
893 operands
|= ENCODE_BARREL_SHIFTER_MODE_ASR
;
897 operands
|= ENCODE_BARREL_SHIFTER_MODE_ROR
;
900 expect("shift instruction");
902 asm_emit_opcode(token
, opcode
| operands
);
905 static void asm_multiplication_opcode(TCCState
*s1
, int token
)
909 uint32_t opcode
= 0x90;
911 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
912 parse_operand(s1
, &ops
[nb_ops
]);
920 expect("at least two operands");
921 else if (nb_ops
== 2) {
922 switch (ARM_INSTRUCTION_GROUP(token
)) {
925 memcpy(&ops
[2], &ops
[0], sizeof(ops
[1])); // ARM is actually like this!
928 expect("at least three operands");
933 // multiply (special case):
940 if (ops
[0].type
== OP_REG32
)
941 opcode
|= ops
[0].reg
<< 16;
943 expect("(destination operand) register");
944 if (ops
[1].type
== OP_REG32
)
945 opcode
|= ops
[1].reg
;
947 expect("(first source operand) register");
948 if (ops
[2].type
== OP_REG32
)
949 opcode
|= ops
[2].reg
<< 8;
951 expect("(second source operand) register");
953 if (ops
[3].type
== OP_REG32
)
954 opcode
|= ops
[3].reg
<< 12;
956 expect("(third source operand) register");
959 switch (ARM_INSTRUCTION_GROUP(token
)) {
961 opcode
|= 1 << 20; // Status
965 expect("three operands");
967 asm_emit_opcode(token
, opcode
);
971 opcode
|= 1 << 20; // Status
975 expect("four operands");
977 opcode
|= 1 << 21; // Accumulate
978 asm_emit_opcode(token
, opcode
);
982 expect("known multiplication instruction");
986 static void asm_long_multiplication_opcode(TCCState
*s1
, int token
)
990 uint32_t opcode
= 0x90 | (1 << 23);
992 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
993 parse_operand(s1
, &ops
[nb_ops
]);
1001 expect("four operands");
1004 // long multiply (special case):
1006 // RdLo: bits 15...12
1007 // RdHi: bits 19...16
1011 if (ops
[0].type
== OP_REG32
)
1012 opcode
|= ops
[0].reg
<< 12;
1014 expect("(destination lo accumulator) register");
1015 if (ops
[1].type
== OP_REG32
)
1016 opcode
|= ops
[1].reg
<< 16;
1018 expect("(destination hi accumulator) register");
1019 if (ops
[2].type
== OP_REG32
)
1020 opcode
|= ops
[2].reg
;
1022 expect("(first source operand) register");
1023 if (ops
[3].type
== OP_REG32
)
1024 opcode
|= ops
[3].reg
<< 8;
1026 expect("(second source operand) register");
1028 switch (ARM_INSTRUCTION_GROUP(token
)) {
1029 case TOK_ASM_smullseq
:
1030 opcode
|= 1 << 20; // Status
1032 case TOK_ASM_smulleq
:
1033 opcode
|= 1 << 22; // signed
1034 asm_emit_opcode(token
, opcode
);
1036 case TOK_ASM_umullseq
:
1037 opcode
|= 1 << 20; // Status
1039 case TOK_ASM_umulleq
:
1040 asm_emit_opcode(token
, opcode
);
1042 case TOK_ASM_smlalseq
:
1043 opcode
|= 1 << 20; // Status
1045 case TOK_ASM_smlaleq
:
1046 opcode
|= 1 << 22; // signed
1047 opcode
|= 1 << 21; // Accumulate
1048 asm_emit_opcode(token
, opcode
);
1050 case TOK_ASM_umlalseq
:
1051 opcode
|= 1 << 20; // Status
1053 case TOK_ASM_umlaleq
:
1054 opcode
|= 1 << 21; // Accumulate
1055 asm_emit_opcode(token
, opcode
);
1058 expect("known long multiplication instruction");
1062 static void asm_single_data_transfer_opcode(TCCState
*s1
, int token
)
1065 Operand strex_operand
;
1069 int closed_bracket
= 0;
1071 uint32_t opcode
= 0;
1072 // Note: ldr r0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1073 // Note: ldr r0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1074 // Note: ldr r0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1076 parse_operand(s1
, &ops
[0]);
1077 if (ops
[0].type
== OP_REG32
)
1078 opcode
|= ENCODE_RD(ops
[0].reg
);
1080 expect("(destination operand) register");
1083 expect("at least two arguments");
1086 switch (ARM_INSTRUCTION_GROUP(token
)) {
1087 case TOK_ASM_strexbeq
:
1088 case TOK_ASM_strexeq
:
1089 parse_operand(s1
, &strex_operand
);
1090 if (strex_operand
.type
!= OP_REG32
) {
1094 expect("at least three arguments");
1101 parse_operand(s1
, &ops
[1]);
1102 if (ops
[1].type
== OP_REG32
)
1103 opcode
|= ENCODE_RN(ops
[1].reg
);
1105 expect("(first source operand) register");
1110 // exclam = 1; // implicit in hardware; don't do it in software
1118 parse_operand(s1
, &ops
[2]);
1119 if (ops
[2].type
== OP_REG32
) {
1120 if (ops
[2].reg
== 15) {
1121 tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token
, NULL
));
1125 opcode
|= asm_parse_optional_shift(s1
, &nb_shift
, &shift
);
1127 expect("shift directive, or no comma");
1131 // end of input expression in brackets--assume 0 offset
1132 ops
[2].type
= OP_IM8
;
1134 opcode
|= 1 << 24; // add offset before transfer
1136 if (!closed_bracket
) {
1138 opcode
|= 1 << 24; // add offset before transfer
1145 // single data transfer: 0 1 I P U B W L << 20 (general case):
1147 // Rd: destination operand [ok]
1148 // Rn: first source operand [ok]
1149 // Operand2: bits 11...0 [ok]
1150 // I: immediate operand? [ok]
1151 // P: Pre/post indexing is PRE: Add offset before transfer [ok]
1152 // U: Up/down is up? (*adds* offset to base) [ok]
1153 // B: Byte/word is byte? [ok]
1154 // W: Write address back into base? [ok]
1155 // L: Load/store is load? [ok]
1157 opcode
|= 1 << 21; // write offset back into register
1159 if (ops
[2].type
== OP_IM32
|| ops
[2].type
== OP_IM8
|| ops
[2].type
== OP_IM8N
) {
1162 tcc_error("minus before '#' not supported for immediate values");
1164 opcode
|= 1 << 23; // up
1166 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
1171 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
1175 } else if (ops
[2].type
== OP_REG32
) {
1177 opcode
|= 1 << 23; // up
1178 opcode
|= ENCODE_IMMEDIATE_FLAG
; /* if set, it means it's NOT immediate */
1179 opcode
|= ops
[2].reg
;
1183 switch (ARM_INSTRUCTION_GROUP(token
)) {
1184 case TOK_ASM_strbeq
:
1185 opcode
|= 1 << 22; // B
1188 opcode
|= 1 << 26; // Load/Store
1190 opcode
|= asm_encode_shift(&shift
);
1191 asm_emit_opcode(token
, opcode
);
1193 case TOK_ASM_ldrbeq
:
1194 opcode
|= 1 << 22; // B
1197 opcode
|= 1 << 20; // L
1198 opcode
|= 1 << 26; // Load/Store
1200 opcode
|= asm_encode_shift(&shift
);
1201 asm_emit_opcode(token
, opcode
);
1203 case TOK_ASM_strexbeq
:
1204 opcode
|= 1 << 22; // B
1206 case TOK_ASM_strexeq
:
1207 if ((opcode
& 0xFFF) || nb_shift
) {
1208 tcc_error("neither offset nor shift allowed with 'strex'");
1209 } else if (opcode
& ENCODE_IMMEDIATE_FLAG
) { // if set, it means it's NOT immediate
1210 tcc_error("offset not allowed with 'strex'");
1212 if ((opcode
& (1 << 24)) == 0) { // add offset after transfer
1213 tcc_error("adding offset after transfer not allowed with 'strex'");
1216 opcode
|= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL
1217 opcode
|= strex_operand
.reg
;
1218 asm_emit_opcode(token
, opcode
);
1220 case TOK_ASM_ldrexbeq
:
1221 opcode
|= 1 << 22; // B
1223 case TOK_ASM_ldrexeq
:
1224 if ((opcode
& 0xFFF) || nb_shift
) {
1225 tcc_error("neither offset nor shift allowed with 'ldrex'");
1226 } else if (opcode
& ENCODE_IMMEDIATE_FLAG
) { // if set, it means it's NOT immediate
1227 tcc_error("offset not allowed with 'ldrex'");
1229 if ((opcode
& (1 << 24)) == 0) { // add offset after transfer
1230 tcc_error("adding offset after transfer not allowed with 'ldrex'");
1232 opcode
|= 1 << 20; // L
1234 opcode
|= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL
1235 asm_emit_opcode(token
, opcode
);
1238 expect("data transfer instruction");
1242 // 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)
1243 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
) {
1244 uint32_t opcode
= 0x0;
1245 opcode
|= 1 << 26; // Load/Store
1246 opcode
|= 1 << 27; // coprocessor
1249 opcode
|= 1 << 22; // long transfer
1252 opcode
|= 1 << 20; // L
1254 opcode
|= cp_number
<< 8;
1257 opcode
|= ENCODE_RD(CRd
);
1259 if (Rn
->type
!= OP_REG32
)
1262 //assert(Rn->reg < 16);
1263 opcode
|= ENCODE_RN(Rn
->reg
);
1265 opcode
|= 1 << 24; // add offset before transfer
1268 opcode
|= 1 << 21; // write offset back into register
1270 if (offset
->type
== OP_IM8
|| offset
->type
== OP_IM8N
|| offset
->type
== OP_IM32
) {
1271 int v
= offset
->e
.v
;
1273 tcc_error("minus before '#' not supported for immediate values");
1274 if (offset
->type
== OP_IM8N
|| v
< 0)
1277 opcode
|= 1 << 23; // up
1279 tcc_error("immediate offset must be a multiple of 4");
1283 tcc_error("immediate offset must be between -1020 and 1020");
1286 } else if (offset
->type
== OP_REG32
) {
1288 opcode
|= 1 << 23; // up
1289 opcode
|= ENCODE_IMMEDIATE_FLAG
; /* if set, it means it's NOT immediate */
1290 opcode
|= offset
->reg
;
1291 tcc_error("Using register offset to register address is not possible here");
1292 } else if (offset
->type
== OP_VREG64
) {
1294 opcode
|= offset
->reg
;
1296 expect("immediate or register");
1298 asm_emit_unconditional_opcode((high_nibble
<< 28) | opcode
);
1301 // Almost exactly the same as asm_single_data_transfer_opcode.
1302 // Difference: Offsets are smaller and multiples of 4; no shifts, no STREX, ENCODE_IMMEDIATE_FLAG is inverted again.
1303 static void asm_coprocessor_data_transfer_opcode(TCCState
*s1
, int token
)
1306 uint8_t coprocessor
;
1307 uint8_t coprocessor_destination_register
;
1308 int preincrement
= 0;
1310 int closed_bracket
= 0;
1312 int long_transfer
= 0;
1313 // Note: ldc p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1314 // Note: ldc p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1315 // Note: ldc p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1317 if (tok
>= TOK_ASM_p0
&& tok
<= TOK_ASM_p15
) {
1318 coprocessor
= tok
- TOK_ASM_p0
;
1321 expect("'c<number>'");
1326 if (tok
>= TOK_ASM_c0
&& tok
<= TOK_ASM_c15
) {
1327 coprocessor_destination_register
= tok
- TOK_ASM_c0
;
1330 expect("'c<number>'");
1335 parse_operand(s1
, &ops
[1]);
1336 if (ops
[1].type
!= OP_REG32
) {
1337 expect("(first source operand) register");
1342 // exclam = 1; // implicit in hardware; don't do it in software
1350 parse_operand(s1
, &ops
[2]);
1351 if (ops
[2].type
== OP_REG32
) {
1352 if (ops
[2].reg
== 15) {
1353 tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token
, NULL
));
1355 } else if (ops
[2].type
== OP_VREG64
) {
1356 tcc_error("'%s' does not support VFP register operand", get_tok_str(token
, NULL
));
1359 // end of input expression in brackets--assume 0 offset
1360 ops
[2].type
= OP_IM8
;
1362 preincrement
= 1; // add offset before transfer
1364 if (!closed_bracket
) {
1366 preincrement
= 1; // add offset before transfer
1373 // TODO: Support options.
1375 if (token
== TOK_ASM_ldc2
|| token
== TOK_ASM_stc2
|| token
== TOK_ASM_ldc2l
|| token
== TOK_ASM_stc2l
) {
1378 long_transfer
= 1; // long transfer
1381 asm_emit_coprocessor_data_transfer(0xF, coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], op2_minus
, preincrement
, exclam
, long_transfer
, 1);
1384 long_transfer
= 1; // long transfer
1387 asm_emit_coprocessor_data_transfer(0xF, coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], op2_minus
, preincrement
, exclam
, long_transfer
, 0);
1390 } else switch (ARM_INSTRUCTION_GROUP(token
)) {
1391 case TOK_ASM_stcleq
:
1395 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);
1397 case TOK_ASM_ldcleq
:
1401 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);
1404 expect("coprocessor data transfer instruction");
1408 #if defined(TCC_ARM_VFP)
1409 #define CP_SINGLE_PRECISION_FLOAT 10
1410 #define CP_DOUBLE_PRECISION_FLOAT 11
1412 static void asm_floating_point_single_data_transfer_opcode(TCCState
*s1
, int token
)
1415 uint8_t coprocessor
= 0;
1416 uint8_t coprocessor_destination_register
= 0;
1417 int long_transfer
= 0;
1418 // Note: vldr p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1419 // Note: Not allowed: vldr p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1420 // Note: Not allowed: vldr p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1422 parse_operand(s1
, &ops
[0]);
1423 if (ops
[0].type
== OP_VREG32
) {
1424 coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
1425 coprocessor_destination_register
= ops
[0].reg
;
1426 long_transfer
= coprocessor_destination_register
& 1;
1427 coprocessor_destination_register
>>= 1;
1428 } else if (ops
[0].type
== OP_VREG64
) {
1429 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
1430 coprocessor_destination_register
= ops
[0].reg
;
1433 expect("floating point register");
1438 parse_operand(s1
, &ops
[1]);
1439 if (ops
[1].type
!= OP_REG32
) {
1440 expect("(first source operand) register");
1444 parse_operand(s1
, &ops
[2]);
1445 if (ops
[2].type
!= OP_IM8
&& ops
[2].type
!= OP_IM8N
) {
1446 expect("immediate offset");
1449 // end of input expression in brackets--assume 0 offset
1450 ops
[2].type
= OP_IM8
;
1455 switch (ARM_INSTRUCTION_GROUP(token
)) {
1456 case TOK_ASM_vldreq
:
1457 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], 0, 1, 0, long_transfer
, 1);
1459 case TOK_ASM_vstreq
:
1460 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, coprocessor_destination_register
, &ops
[1], &ops
[2], 0, 1, 0, long_transfer
, 0);
1463 expect("floating point data transfer instruction");
1467 static void asm_floating_point_block_data_transfer_opcode(TCCState
*s1
, int token
)
1469 uint8_t coprocessor
= 0;
1470 int first_regset_register
;
1471 int last_regset_register
;
1472 uint8_t regset_item_count
;
1473 uint8_t extra_register_bit
= 0;
1476 int preincrement
= 0;
1479 switch (ARM_INSTRUCTION_GROUP(token
)) {
1480 case TOK_ASM_vpusheq
:
1481 case TOK_ASM_vpopeq
:
1482 ops
[0].type
= OP_REG32
;
1483 ops
[0].reg
= 13; // sp
1487 parse_operand(s1
, &ops
[0]);
1496 first_regset_register
= asm_parse_vfp_regvar(tok
, 1);
1497 if ((first_regset_register
= asm_parse_vfp_regvar(tok
, 1)) != -1) {
1498 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
1500 } else if ((first_regset_register
= asm_parse_vfp_regvar(tok
, 0)) != -1) {
1501 coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
1504 expect("floating-point register");
1509 if ((last_regset_register
= asm_parse_vfp_regvar(tok
, coprocessor
== CP_DOUBLE_PRECISION_FLOAT
)) != -1)
1512 expect("floating-point register");
1515 last_regset_register
= first_regset_register
;
1517 if (last_regset_register
< first_regset_register
) {
1518 tcc_error("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
1521 // Note: 0 (one down) is not implemented by us regardless.
1522 regset_item_count
= last_regset_register
- first_regset_register
+ 1;
1523 if (coprocessor
== CP_DOUBLE_PRECISION_FLOAT
)
1524 regset_item_count
<<= 1;
1526 extra_register_bit
= first_regset_register
& 1;
1527 first_regset_register
>>= 1;
1529 offset
.type
= OP_IM8
;
1530 offset
.e
.v
= regset_item_count
<< 2;
1531 switch (ARM_INSTRUCTION_GROUP(token
)) {
1532 case TOK_ASM_vstmeq
: // post-increment store
1533 case TOK_ASM_vstmiaeq
: // post-increment store
1535 case TOK_ASM_vpopeq
:
1536 case TOK_ASM_vldmeq
: // post-increment load
1537 case TOK_ASM_vldmiaeq
: // post-increment load
1540 case TOK_ASM_vldmdbeq
: // pre-decrement load
1543 case TOK_ASM_vpusheq
:
1544 case TOK_ASM_vstmdbeq
: // pre-decrement store
1545 offset
.type
= OP_IM8N
;
1546 offset
.e
.v
= -offset
.e
.v
;
1550 expect("floating point block data transfer instruction");
1552 if (ops
[0].type
!= OP_REG32
)
1553 expect("(first operand) register");
1554 else if (ops
[0].reg
== 15)
1555 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
1556 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
)
1557 tcc_error("first operand of '%s' should have an exclamation mark", get_tok_str(token
, NULL
));
1559 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
);
1562 #define VMOV_FRACTIONAL_DIGITS 7
1563 #define VMOV_ONE 10000000 /* pow(10, VMOV_FRACTIONAL_DIGITS) */
1565 static uint32_t vmov_parse_fractional_part(const char* s
)
1567 uint32_t result
= 0;
1569 for (i
= 0; i
< VMOV_FRACTIONAL_DIGITS
; ++i
) {
1572 if (c
>= '0' && c
<= '9') {
1573 result
+= (c
- '0');
1578 expect("decimal numeral");
1582 static int vmov_linear_approx_index(uint32_t beginning
, uint32_t end
, uint32_t value
)
1588 k
= (end
- beginning
)/16;
1589 for (xvalue
= beginning
, i
= 0; i
< 16; ++i
, xvalue
+= k
) {
1590 if (value
== xvalue
)
1597 static uint32_t vmov_parse_immediate_value() {
1599 unsigned long integral_value
;
1602 if (tok
!= TOK_PPNUM
) {
1603 expect("immediate value");
1607 integral_value
= strtoul(p
, (char **)&p
, 0);
1609 if (errno
|| integral_value
>= 32) {
1610 tcc_error("invalid floating-point immediate value");
1613 value
= (uint32_t) integral_value
* VMOV_ONE
;
1616 value
+= vmov_parse_fractional_part(p
);
1622 static uint8_t vmov_encode_immediate_value(uint32_t value
)
1626 uint32_t beginning
= 0;
1631 limit
= 32 * VMOV_ONE
;
1632 for (i
= 0; i
< 8; ++i
) {
1633 if (value
< limit
) {
1641 if (r
== -1 || value
< beginning
|| value
> end
) {
1642 tcc_error("invalid decimal number for vmov: %d", value
);
1644 n
= vmov_linear_approx_index(beginning
, end
, value
);
1645 return n
| (((3 - r
) & 0x7) << 4);
1649 static void asm_floating_point_immediate_data_processing_opcode_tail(TCCState
*s1
, int token
, uint8_t coprocessor
, uint8_t CRd
) {
1650 uint8_t opcode1
= 0;
1651 uint8_t opcode2
= 0;
1652 uint8_t operands
[3] = {0, 0, 0};
1653 uint32_t immediate_value
= 0;
1659 if (tok
== '#' || tok
== '$') {
1666 immediate_value
= vmov_parse_immediate_value();
1668 opcode1
= 11; // "Other" instruction
1669 switch (ARM_INSTRUCTION_GROUP(token
)) {
1670 case TOK_ASM_vcmpeq_f32
:
1671 case TOK_ASM_vcmpeq_f64
:
1674 if (immediate_value
) {
1675 expect("Immediate value 0");
1678 case TOK_ASM_vcmpeeq_f32
:
1679 case TOK_ASM_vcmpeeq_f64
:
1682 if (immediate_value
) {
1683 expect("Immediate value 0");
1686 case TOK_ASM_vmoveq_f32
:
1687 case TOK_ASM_vmoveq_f64
:
1693 code
= vmov_encode_immediate_value(immediate_value
);
1694 operands
[1] |= code
>> 4;
1695 operands
[2] = code
& 0xF;
1698 expect("known floating point with immediate instruction");
1701 if (coprocessor
== CP_SINGLE_PRECISION_FLOAT
) {
1702 if (operands
[0] & 1)
1707 asm_emit_coprocessor_opcode(condition_code_of_token(token
), coprocessor
, opcode1
, operands
[0], operands
[1], operands
[2], opcode2
, 0);
1710 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]) {
1711 uint8_t opcode1
= 0;
1712 uint8_t opcode2
= 0;
1713 switch (coprocessor
) {
1714 case CP_SINGLE_PRECISION_FLOAT
:
1715 // "vmov.f32 r2, s3" or "vmov.f32 s3, r2"
1716 if (nb_ops
!= 2 || nb_arm_regs
!= 1) {
1717 tcc_error("vmov.f32 only implemented for one VFP register operand and one ARM register operands");
1719 if (ops
[0].type
!= OP_REG32
) { // determine mode: load or store
1720 // need to swap operands 0 and 1
1721 memcpy(&ops
[2], &ops
[1], sizeof(ops
[2]));
1722 memcpy(&ops
[1], &ops
[0], sizeof(ops
[1]));
1723 memcpy(&ops
[0], &ops
[2], sizeof(ops
[0]));
1727 if (ops
[1].type
== OP_VREG32
) {
1733 if (ops
[0].type
== OP_VREG32
) {
1739 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);
1741 case CP_DOUBLE_PRECISION_FLOAT
:
1742 if (nb_ops
!= 3 || nb_arm_regs
!= 2) {
1743 tcc_error("vmov.f32 only implemented for one VFP register operand and two ARM register operands");
1745 // Determine whether it's a store into a VFP register (vmov "d1, r2, r3") rather than "vmov r2, r3, d1"
1746 if (ops
[0].type
== OP_VREG64
) {
1747 if (ops
[2].type
== OP_REG32
) {
1749 // need to rotate operand list to the left
1750 memcpy(&temp
, &ops
[0], sizeof(temp
));
1751 memcpy(&ops
[0], &ops
[1], sizeof(ops
[0]));
1752 memcpy(&ops
[1], &ops
[2], sizeof(ops
[1]));
1753 memcpy(&ops
[2], &temp
, sizeof(ops
[2]));
1755 tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands");
1757 } else if (ops
[0].type
!= OP_REG32
|| ops
[1].type
!= OP_REG32
|| ops
[2].type
!= OP_VREG64
) {
1758 tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands");
1762 asm_emit_coprocessor_data_transfer(condition_code_of_token(token
), coprocessor
, ops
[0].reg
, &ops
[1], &ops
[2], 0, 0, 0, 1, opcode1
);
1765 tcc_internal_error("unknown coprocessor");
1769 static void asm_floating_point_vcvt_data_processing_opcode(TCCState
*s1
, int token
) {
1770 uint8_t coprocessor
= 0;
1772 uint8_t opcode1
= 11;
1773 uint8_t opcode2
= 2;
1775 switch (ARM_INSTRUCTION_GROUP(token
)) {
1776 case TOK_ASM_vcvtreq_s32_f64
:
1777 case TOK_ASM_vcvtreq_u32_f64
:
1778 case TOK_ASM_vcvteq_s32_f64
:
1779 case TOK_ASM_vcvteq_u32_f64
:
1780 case TOK_ASM_vcvteq_f64_s32
:
1781 case TOK_ASM_vcvteq_f64_u32
:
1782 case TOK_ASM_vcvteq_f32_f64
:
1783 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
1785 case TOK_ASM_vcvtreq_s32_f32
:
1786 case TOK_ASM_vcvtreq_u32_f32
:
1787 case TOK_ASM_vcvteq_s32_f32
:
1788 case TOK_ASM_vcvteq_u32_f32
:
1789 case TOK_ASM_vcvteq_f32_s32
:
1790 case TOK_ASM_vcvteq_f32_u32
:
1791 case TOK_ASM_vcvteq_f64_f32
:
1792 coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
1795 tcc_error("Unknown coprocessor for instruction '%s'", get_tok_str(token
, NULL
));
1798 parse_operand(s1
, &ops
[0]);
1799 ops
[1].type
= OP_IM8
;
1801 /* floating-point -> integer */
1802 switch (ARM_INSTRUCTION_GROUP(token
)) {
1803 case TOK_ASM_vcvtreq_s32_f32
:
1804 case TOK_ASM_vcvtreq_s32_f64
:
1805 case TOK_ASM_vcvteq_s32_f32
:
1806 case TOK_ASM_vcvteq_s32_f64
:
1807 ops
[1].e
.v
|= 1; // signed
1809 case TOK_ASM_vcvteq_u32_f32
:
1810 case TOK_ASM_vcvteq_u32_f64
:
1811 case TOK_ASM_vcvtreq_u32_f32
:
1812 case TOK_ASM_vcvtreq_u32_f64
:
1813 ops
[1].e
.v
|= 4; // to_integer (opc2)
1815 /* floating-point size conversion */
1816 case TOK_ASM_vcvteq_f64_f32
:
1817 case TOK_ASM_vcvteq_f32_f64
:
1823 parse_operand(s1
, &ops
[2]);
1825 switch (ARM_INSTRUCTION_GROUP(token
)) {
1826 /* floating-point -> integer */
1827 case TOK_ASM_vcvteq_s32_f32
:
1828 case TOK_ASM_vcvteq_s32_f64
:
1829 case TOK_ASM_vcvteq_u32_f32
:
1830 case TOK_ASM_vcvteq_u32_f64
:
1831 opcode2
|= 4; // round_zero
1834 /* integer -> floating-point */
1835 case TOK_ASM_vcvteq_f64_s32
:
1836 case TOK_ASM_vcvteq_f32_s32
:
1837 opcode2
|= 4; // signed--special
1840 /* floating-point size conversion */
1841 case TOK_ASM_vcvteq_f64_f32
:
1842 case TOK_ASM_vcvteq_f32_f64
:
1843 opcode2
|= 4; // always set
1847 switch (ARM_INSTRUCTION_GROUP(token
)) {
1848 case TOK_ASM_vcvteq_f64_u32
:
1849 case TOK_ASM_vcvteq_f64_s32
:
1850 case TOK_ASM_vcvteq_f64_f32
:
1851 if (ops
[0].type
== OP_VREG64
&& ops
[2].type
== OP_VREG32
) {
1853 expect("d<number>, s<number>");
1857 if (coprocessor
== CP_SINGLE_PRECISION_FLOAT
) {
1858 if (ops
[0].type
== OP_VREG32
&& ops
[2].type
== OP_VREG32
) {
1860 expect("s<number>, s<number>");
1862 } else if (coprocessor
== CP_DOUBLE_PRECISION_FLOAT
) {
1863 if (ops
[0].type
== OP_VREG32
&& ops
[2].type
== OP_VREG64
) {
1865 expect("s<number>, d<number>");
1870 if (ops
[2].type
== OP_VREG32
) {
1875 if (ops
[0].type
== OP_VREG32
) {
1880 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);
1883 static void asm_floating_point_data_processing_opcode(TCCState
*s1
, int token
) {
1884 uint8_t coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
1885 uint8_t opcode1
= 0;
1886 uint8_t opcode2
= 0; // (0 || 2) | register selection
1890 int nb_arm_regs
= 0;
1893 Instruction opcode opcode2 Reason
1894 =============================================================
1895 - 1?00 ?1? Undefined
1896 VFNMS 1?01 ?0? Must be unconditional
1897 VFNMA 1?01 ?1? Must be unconditional
1898 VFMA 1?10 ?0? Must be unconditional
1899 VFMS 1?10 ?1? Must be unconditional
1910 switch (ARM_INSTRUCTION_GROUP(token
)) {
1911 case TOK_ASM_vmlaeq_f64
:
1912 case TOK_ASM_vmlseq_f64
:
1913 case TOK_ASM_vnmlseq_f64
:
1914 case TOK_ASM_vnmlaeq_f64
:
1915 case TOK_ASM_vmuleq_f64
:
1916 case TOK_ASM_vnmuleq_f64
:
1917 case TOK_ASM_vaddeq_f64
:
1918 case TOK_ASM_vsubeq_f64
:
1919 case TOK_ASM_vdiveq_f64
:
1920 case TOK_ASM_vnegeq_f64
:
1921 case TOK_ASM_vabseq_f64
:
1922 case TOK_ASM_vsqrteq_f64
:
1923 case TOK_ASM_vcmpeq_f64
:
1924 case TOK_ASM_vcmpeeq_f64
:
1925 case TOK_ASM_vmoveq_f64
:
1926 coprocessor
= CP_DOUBLE_PRECISION_FLOAT
;
1929 switch (ARM_INSTRUCTION_GROUP(token
)) {
1930 case TOK_ASM_vmoveq_f32
:
1931 case TOK_ASM_vmoveq_f64
:
1936 for (nb_ops
= 0; nb_ops
< 3; ) {
1937 // Note: Necessary because parse_operand can't parse decimal numerals.
1938 if (nb_ops
== 1 && (tok
== '#' || tok
== '$' || tok
== TOK_PPNUM
|| tok
== '-')) {
1939 asm_floating_point_immediate_data_processing_opcode_tail(s1
, token
, coprocessor
, ops
[0].reg
);
1942 parse_operand(s1
, &ops
[nb_ops
]);
1943 if (vmov
&& ops
[nb_ops
].type
== OP_REG32
) {
1945 } else if (ops
[nb_ops
].type
== OP_VREG32
) {
1946 if (coprocessor
!= CP_SINGLE_PRECISION_FLOAT
) {
1947 expect("'s<number>'");
1949 } else if (ops
[nb_ops
].type
== OP_VREG64
) {
1950 if (coprocessor
!= CP_DOUBLE_PRECISION_FLOAT
) {
1951 expect("'d<number>'");
1954 expect("floating point register");
1963 if (nb_arm_regs
== 0) {
1964 if (nb_ops
== 2) { // implicit
1965 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
1966 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
1970 tcc_error("Not enough operands for '%s' (%u)", get_tok_str(token
, NULL
), nb_ops
);
1974 switch (ARM_INSTRUCTION_GROUP(token
)) {
1975 case TOK_ASM_vmlaeq_f32
:
1976 case TOK_ASM_vmlaeq_f64
:
1980 case TOK_ASM_vmlseq_f32
:
1981 case TOK_ASM_vmlseq_f64
:
1985 case TOK_ASM_vnmlseq_f32
:
1986 case TOK_ASM_vnmlseq_f64
:
1990 case TOK_ASM_vnmlaeq_f32
:
1991 case TOK_ASM_vnmlaeq_f64
:
1995 case TOK_ASM_vmuleq_f32
:
1996 case TOK_ASM_vmuleq_f64
:
2000 case TOK_ASM_vnmuleq_f32
:
2001 case TOK_ASM_vnmuleq_f64
:
2005 case TOK_ASM_vaddeq_f32
:
2006 case TOK_ASM_vaddeq_f64
:
2010 case TOK_ASM_vsubeq_f32
:
2011 case TOK_ASM_vsubeq_f64
:
2015 case TOK_ASM_vdiveq_f32
:
2016 case TOK_ASM_vdiveq_f64
:
2020 case TOK_ASM_vnegeq_f32
:
2021 case TOK_ASM_vnegeq_f64
:
2022 opcode1
= 11; // Other" instruction
2024 ops
[1].type
= OP_IM8
;
2027 case TOK_ASM_vabseq_f32
:
2028 case TOK_ASM_vabseq_f64
:
2029 opcode1
= 11; // "Other" instruction
2031 ops
[1].type
= OP_IM8
;
2034 case TOK_ASM_vsqrteq_f32
:
2035 case TOK_ASM_vsqrteq_f64
:
2036 opcode1
= 11; // "Other" instruction
2038 ops
[1].type
= OP_IM8
;
2041 case TOK_ASM_vcmpeq_f32
:
2042 case TOK_ASM_vcmpeq_f64
:
2043 opcode1
= 11; // "Other" instruction
2045 ops
[1].type
= OP_IM8
;
2048 case TOK_ASM_vcmpeeq_f32
:
2049 case TOK_ASM_vcmpeeq_f64
:
2050 opcode1
= 11; // "Other" instruction
2052 ops
[1].type
= OP_IM8
;
2055 case TOK_ASM_vmoveq_f32
:
2056 case TOK_ASM_vmoveq_f64
:
2057 if (nb_arm_regs
> 0) { // vmov.f32 r2, s3 or similar
2058 asm_floating_point_reg_arm_reg_transfer_opcode_tail(s1
, token
, coprocessor
, nb_arm_regs
, nb_ops
, ops
);
2061 opcode1
= 11; // "Other" instruction
2063 ops
[1].type
= OP_IM8
;
2068 expect("known floating point instruction");
2071 if (coprocessor
== CP_SINGLE_PRECISION_FLOAT
) {
2072 if (ops
[2].type
== OP_VREG32
) {
2078 if (ops
[1].type
== OP_VREG32
) {
2084 if (ops
[0].type
== OP_VREG32
) {
2091 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);
2094 static int asm_parse_vfp_status_regvar(int t
)
2108 static void asm_floating_point_status_register_opcode(TCCState
* s1
, int token
)
2110 uint8_t coprocessor
= CP_SINGLE_PRECISION_FLOAT
;
2112 int vfp_sys_reg
= -1;
2113 Operand arm_operand
;
2114 switch (ARM_INSTRUCTION_GROUP(token
)) {
2115 case TOK_ASM_vmrseq
:
2117 if (tok
== TOK_ASM_apsr_nzcv
) {
2118 arm_operand
.type
= OP_REG32
;
2119 arm_operand
.reg
= 15; // not PC
2120 next(); // skip apsr_nzcv
2122 parse_operand(s1
, &arm_operand
);
2123 if (arm_operand
.type
== OP_REG32
&& arm_operand
.reg
== 15) {
2124 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
2129 vfp_sys_reg
= asm_parse_vfp_status_regvar(tok
);
2130 next(); // skip vfp sys reg
2131 if (arm_operand
.type
== OP_REG32
&& arm_operand
.reg
== 15 && vfp_sys_reg
!= 1) {
2132 tcc_error("'%s' only supports the variant 'vmrs apsr_nzcv, fpscr' here", get_tok_str(token
, NULL
));
2135 case TOK_ASM_vmsreq
:
2137 vfp_sys_reg
= asm_parse_vfp_status_regvar(tok
);
2138 next(); // skip vfp sys reg
2140 parse_operand(s1
, &arm_operand
);
2141 if (arm_operand
.type
== OP_REG32
&& arm_operand
.reg
== 15) {
2142 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
2146 expect("floating point status register instruction");
2148 if (vfp_sys_reg
== -1) {
2149 expect("VFP system register");
2151 if (arm_operand
.type
!= OP_REG32
) {
2152 expect("ARM register");
2154 asm_emit_coprocessor_opcode(condition_code_of_token(token
), coprocessor
, opcode
, arm_operand
.reg
, vfp_sys_reg
, 0x10, 0, 0);
2159 static void asm_misc_single_data_transfer_opcode(TCCState
*s1
, int token
)
2163 int closed_bracket
= 0;
2165 uint32_t opcode
= (1 << 7) | (1 << 4);
2168 The argument syntax is exactly the same as in arm_single_data_transfer_opcode, except that there's no STREX argument form.
2169 The main difference between this function and asm_misc_single_data_transfer_opcode is that the immediate values here must be smaller.
2170 Also, the combination (P=0, W=1) is unpredictable here.
2171 The immediate flag has moved to bit index 22--and its meaning has flipped.
2172 The immediate value itself has been split into two parts: one at bits 11...8, one at bits 3...0
2173 bit 26 (Load/Store instruction) is unset here.
2174 bits 7 and 4 are set here. */
2176 // Here: 0 0 0 P U I W L << 20
2177 // [compare single data transfer: 0 1 I P U B W L << 20]
2179 parse_operand(s1
, &ops
[0]);
2180 if (ops
[0].type
== OP_REG32
)
2181 opcode
|= ENCODE_RD(ops
[0].reg
);
2183 expect("(destination operand) register");
2186 expect("at least two arguments");
2190 parse_operand(s1
, &ops
[1]);
2191 if (ops
[1].type
== OP_REG32
)
2192 opcode
|= ENCODE_RN(ops
[1].reg
);
2194 expect("(first source operand) register");
2199 // exclam = 1; // implicit in hardware; don't do it in software
2207 parse_operand(s1
, &ops
[2]);
2209 // end of input expression in brackets--assume 0 offset
2210 ops
[2].type
= OP_IM8
;
2212 opcode
|= 1 << 24; // add offset before transfer
2214 if (!closed_bracket
) {
2216 opcode
|= 1 << 24; // add offset before transfer
2224 if ((opcode
& (1 << 24)) == 0) {
2225 tcc_error("result of '%s' would be unpredictable here", get_tok_str(token
, NULL
));
2227 opcode
|= 1 << 21; // write offset back into register
2230 if (ops
[2].type
== OP_IM32
|| ops
[2].type
== OP_IM8
|| ops
[2].type
== OP_IM8N
) {
2233 tcc_error("minus before '#' not supported for immediate values");
2235 opcode
|= 1 << 23; // up
2237 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
2239 // bits 11...8: immediate hi nibble
2240 // bits 3...0: immediate lo nibble
2241 opcode
|= (v
& 0xF0) << 4;
2246 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
2249 // bits 11...8: immediate hi nibble
2250 // bits 3...0: immediate lo nibble
2251 opcode
|= (v
& 0xF0) << 4;
2255 opcode
|= 1 << 22; // not ENCODE_IMMEDIATE_FLAG;
2256 } else if (ops
[2].type
== OP_REG32
) {
2258 opcode
|= 1 << 23; // up
2259 opcode
|= ops
[2].reg
;
2263 switch (ARM_INSTRUCTION_GROUP(token
)) {
2264 case TOK_ASM_ldrsheq
:
2265 opcode
|= 1 << 5; // halfword, not byte
2267 case TOK_ASM_ldrsbeq
:
2268 opcode
|= 1 << 6; // sign extend
2269 opcode
|= 1 << 20; // L
2270 asm_emit_opcode(token
, opcode
);
2272 case TOK_ASM_ldrheq
:
2273 opcode
|= 1 << 5; // halfword, not byte
2274 opcode
|= 1 << 20; // L
2275 asm_emit_opcode(token
, opcode
);
2277 case TOK_ASM_strheq
:
2278 opcode
|= 1 << 5; // halfword, not byte
2279 asm_emit_opcode(token
, opcode
);
2284 /* Note: almost dupe of encbranch in arm-gen.c */
2285 static uint32_t encbranchoffset(int pos
, int addr
, int fail
)
2289 if(addr
>=0x7fffff || addr
<-0x800000) {
2291 tcc_error("branch offset is too far");
2294 return /*not 0x0A000000|*/(addr
&0xffffff);
2297 static void asm_branch_opcode(TCCState
*s1
, int token
)
2304 switch (ARM_INSTRUCTION_GROUP(token
)) {
2308 esym
= elfsym(e
.sym
);
2309 if (!esym
|| esym
->st_shndx
!= cur_text_section
->sh_num
) {
2310 tcc_error("invalid branch target");
2312 jmp_disp
= encbranchoffset(ind
, e
.v
+ esym
->st_value
, 1);
2315 parse_operand(s1
, &op
);
2318 switch (ARM_INSTRUCTION_GROUP(token
)) {
2320 asm_emit_opcode(token
, (0xa << 24) | (jmp_disp
& 0xffffff));
2323 asm_emit_opcode(token
, (0xb << 24) | (jmp_disp
& 0xffffff));
2326 if (op
.type
!= OP_REG32
)
2329 asm_emit_opcode(token
, (0x12fff1 << 4) | op
.reg
);
2332 if (op
.type
!= OP_REG32
)
2335 asm_emit_opcode(token
, (0x12fff3 << 4) | op
.reg
);
2338 expect("branch instruction");
2342 ST_FUNC
void asm_opcode(TCCState
*s1
, int token
)
2344 while (token
== TOK_LINEFEED
) {
2348 if (token
== TOK_EOF
)
2350 if (token
< TOK_ASM_nopeq
) { // no condition code
2353 asm_coprocessor_opcode(s1
, token
);
2359 asm_coprocessor_data_transfer_opcode(s1
, token
);
2362 expect("instruction");
2366 switch (ARM_INSTRUCTION_GROUP(token
)) {
2367 case TOK_ASM_pusheq
:
2369 case TOK_ASM_stmdaeq
:
2370 case TOK_ASM_ldmdaeq
:
2373 case TOK_ASM_stmiaeq
:
2374 case TOK_ASM_ldmiaeq
:
2375 case TOK_ASM_stmdbeq
:
2376 case TOK_ASM_ldmdbeq
:
2377 case TOK_ASM_stmibeq
:
2378 case TOK_ASM_ldmibeq
:
2379 asm_block_data_transfer_opcode(s1
, token
);
2384 asm_nullary_opcode(token
);
2388 asm_unary_opcode(s1
, token
);
2394 asm_branch_opcode(s1
, token
);
2397 case TOK_ASM_sxtbeq
:
2398 case TOK_ASM_sxtheq
:
2399 case TOK_ASM_uxtbeq
:
2400 case TOK_ASM_uxtheq
:
2401 case TOK_ASM_movteq
:
2402 case TOK_ASM_movweq
:
2403 asm_binary_opcode(s1
, token
);
2407 case TOK_ASM_ldrbeq
:
2409 case TOK_ASM_strbeq
:
2410 case TOK_ASM_ldrexeq
:
2411 case TOK_ASM_ldrexbeq
:
2412 case TOK_ASM_strexeq
:
2413 case TOK_ASM_strexbeq
:
2414 asm_single_data_transfer_opcode(s1
, token
);
2417 case TOK_ASM_ldrheq
:
2418 case TOK_ASM_ldrsheq
:
2419 case TOK_ASM_ldrsbeq
:
2420 case TOK_ASM_strheq
:
2421 asm_misc_single_data_transfer_opcode(s1
, token
);
2440 case TOK_ASM_andseq
:
2441 case TOK_ASM_eorseq
:
2442 case TOK_ASM_subseq
:
2443 case TOK_ASM_rsbseq
:
2444 case TOK_ASM_addseq
:
2445 case TOK_ASM_adcseq
:
2446 case TOK_ASM_sbcseq
:
2447 case TOK_ASM_rscseq
:
2448 // case TOK_ASM_tstseq:
2449 // case TOK_ASM_teqseq:
2450 // case TOK_ASM_cmpseq:
2451 // case TOK_ASM_cmnseq:
2452 case TOK_ASM_orrseq
:
2453 case TOK_ASM_movseq
:
2454 case TOK_ASM_bicseq
:
2455 case TOK_ASM_mvnseq
:
2456 asm_data_processing_opcode(s1
, token
);
2460 case TOK_ASM_lslseq
:
2462 case TOK_ASM_lsrseq
:
2464 case TOK_ASM_asrseq
:
2466 case TOK_ASM_rorseq
:
2467 case TOK_ASM_rrxseq
:
2469 asm_shift_opcode(s1
, token
);
2473 case TOK_ASM_mulseq
:
2475 case TOK_ASM_mlaseq
:
2476 asm_multiplication_opcode(s1
, token
);
2479 case TOK_ASM_smulleq
:
2480 case TOK_ASM_smullseq
:
2481 case TOK_ASM_umulleq
:
2482 case TOK_ASM_umullseq
:
2483 case TOK_ASM_smlaleq
:
2484 case TOK_ASM_smlalseq
:
2485 case TOK_ASM_umlaleq
:
2486 case TOK_ASM_umlalseq
:
2487 asm_long_multiplication_opcode(s1
, token
);
2493 asm_coprocessor_opcode(s1
, token
);
2497 case TOK_ASM_ldcleq
:
2499 case TOK_ASM_stcleq
:
2500 asm_coprocessor_data_transfer_opcode(s1
, token
);
2503 #if defined(TCC_ARM_VFP)
2504 case TOK_ASM_vldreq
:
2505 case TOK_ASM_vstreq
:
2506 asm_floating_point_single_data_transfer_opcode(s1
, token
);
2509 case TOK_ASM_vmlaeq_f32
:
2510 case TOK_ASM_vmlseq_f32
:
2511 case TOK_ASM_vnmlseq_f32
:
2512 case TOK_ASM_vnmlaeq_f32
:
2513 case TOK_ASM_vmuleq_f32
:
2514 case TOK_ASM_vnmuleq_f32
:
2515 case TOK_ASM_vaddeq_f32
:
2516 case TOK_ASM_vsubeq_f32
:
2517 case TOK_ASM_vdiveq_f32
:
2518 case TOK_ASM_vnegeq_f32
:
2519 case TOK_ASM_vabseq_f32
:
2520 case TOK_ASM_vsqrteq_f32
:
2521 case TOK_ASM_vcmpeq_f32
:
2522 case TOK_ASM_vcmpeeq_f32
:
2523 case TOK_ASM_vmoveq_f32
:
2524 case TOK_ASM_vmlaeq_f64
:
2525 case TOK_ASM_vmlseq_f64
:
2526 case TOK_ASM_vnmlseq_f64
:
2527 case TOK_ASM_vnmlaeq_f64
:
2528 case TOK_ASM_vmuleq_f64
:
2529 case TOK_ASM_vnmuleq_f64
:
2530 case TOK_ASM_vaddeq_f64
:
2531 case TOK_ASM_vsubeq_f64
:
2532 case TOK_ASM_vdiveq_f64
:
2533 case TOK_ASM_vnegeq_f64
:
2534 case TOK_ASM_vabseq_f64
:
2535 case TOK_ASM_vsqrteq_f64
:
2536 case TOK_ASM_vcmpeq_f64
:
2537 case TOK_ASM_vcmpeeq_f64
:
2538 case TOK_ASM_vmoveq_f64
:
2539 asm_floating_point_data_processing_opcode(s1
, token
);
2542 case TOK_ASM_vcvtreq_s32_f32
:
2543 case TOK_ASM_vcvtreq_s32_f64
:
2544 case TOK_ASM_vcvteq_s32_f32
:
2545 case TOK_ASM_vcvteq_s32_f64
:
2546 case TOK_ASM_vcvtreq_u32_f32
:
2547 case TOK_ASM_vcvtreq_u32_f64
:
2548 case TOK_ASM_vcvteq_u32_f32
:
2549 case TOK_ASM_vcvteq_u32_f64
:
2550 case TOK_ASM_vcvteq_f64_s32
:
2551 case TOK_ASM_vcvteq_f32_s32
:
2552 case TOK_ASM_vcvteq_f64_u32
:
2553 case TOK_ASM_vcvteq_f32_u32
:
2554 case TOK_ASM_vcvteq_f64_f32
:
2555 case TOK_ASM_vcvteq_f32_f64
:
2556 asm_floating_point_vcvt_data_processing_opcode(s1
, token
);
2559 case TOK_ASM_vpusheq
:
2560 case TOK_ASM_vpopeq
:
2561 case TOK_ASM_vldmeq
:
2562 case TOK_ASM_vldmiaeq
:
2563 case TOK_ASM_vldmdbeq
:
2564 case TOK_ASM_vstmeq
:
2565 case TOK_ASM_vstmiaeq
:
2566 case TOK_ASM_vstmdbeq
:
2567 asm_floating_point_block_data_transfer_opcode(s1
, token
);
2570 case TOK_ASM_vmsreq
:
2571 case TOK_ASM_vmrseq
:
2572 asm_floating_point_status_register_opcode(s1
, token
);
2577 expect("known instruction");
2581 ST_FUNC
void subst_asm_operand(CString
*add_str
, SValue
*sv
, int modifier
)
2583 int r
, reg
, size
, val
;
2586 if ((r
& VT_VALMASK
) == VT_CONST
) {
2587 if (!(r
& VT_LVAL
) && modifier
!= 'c' && modifier
!= 'n' &&
2589 cstr_ccat(add_str
, '#');
2591 const char *name
= get_tok_str(sv
->sym
->v
, NULL
);
2592 if (sv
->sym
->v
>= SYM_FIRST_ANOM
) {
2593 /* In case of anonymous symbols ("L.42", used
2594 for static data labels) we can't find them
2595 in the C symbol table when later looking up
2596 this name. So enter them now into the asm label
2597 list when we still know the symbol. */
2598 get_asm_sym(tok_alloc(name
, strlen(name
))->tok
, sv
->sym
);
2600 if (tcc_state
->leading_underscore
)
2601 cstr_ccat(add_str
, '_');
2602 cstr_cat(add_str
, name
, -1);
2603 if ((uint32_t) sv
->c
.i
== 0)
2605 cstr_ccat(add_str
, '+');
2608 if (modifier
== 'n')
2610 cstr_printf(add_str
, "%d", (int) sv
->c
.i
);
2612 } else if ((r
& VT_VALMASK
) == VT_LOCAL
) {
2613 cstr_printf(add_str
, "[fp,#%d]", (int) sv
->c
.i
);
2614 } else if (r
& VT_LVAL
) {
2615 reg
= r
& VT_VALMASK
;
2616 if (reg
>= VT_CONST
)
2617 tcc_internal_error("");
2618 cstr_printf(add_str
, "[%s]",
2619 get_tok_str(TOK_ASM_r0
+ reg
, NULL
));
2622 reg
= r
& VT_VALMASK
;
2623 if (reg
>= VT_CONST
)
2624 tcc_internal_error("");
2626 /* choose register operand size */
2627 if ((sv
->type
.t
& VT_BTYPE
) == VT_BYTE
||
2628 (sv
->type
.t
& VT_BTYPE
) == VT_BOOL
)
2630 else if ((sv
->type
.t
& VT_BTYPE
) == VT_SHORT
)
2635 if (modifier
== 'b') {
2637 } else if (modifier
== 'w') {
2639 } else if (modifier
== 'k') {
2645 reg
= TOK_ASM_r0
+ reg
;
2648 cstr_printf(add_str
, "%s", get_tok_str(reg
, NULL
));
2652 /* generate prolog and epilog code for asm statement */
2653 ST_FUNC
void asm_gen_code(ASMOperand
*operands
, int nb_operands
,
2654 int nb_outputs
, int is_output
,
2655 uint8_t *clobber_regs
,
2658 uint8_t regs_allocated
[NB_ASM_REGS
];
2661 uint32_t saved_regset
= 0;
2663 // TODO: Check non-E ABI.
2664 // Note: Technically, r13 (sp) is also callee-saved--but that does not matter yet
2665 static const uint8_t reg_saved
[] = { 4, 5, 6, 7, 8, 9 /* Note: sometimes special reg "sb" */ , 10, 11 };
2667 /* mark all used registers */
2668 memcpy(regs_allocated
, clobber_regs
, sizeof(regs_allocated
));
2669 for(i
= 0; i
< nb_operands
;i
++) {
2672 regs_allocated
[op
->reg
] = 1;
2674 for(i
= 0; i
< sizeof(reg_saved
)/sizeof(reg_saved
[0]); i
++) {
2676 if (regs_allocated
[reg
])
2677 saved_regset
|= 1 << reg
;
2680 if (!is_output
) { // prolog
2681 /* generate reg save code */
2683 gen_le32(0xe92d0000 | saved_regset
); // push {...}
2685 /* generate load code */
2686 for(i
= 0; i
< nb_operands
; i
++) {
2689 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&&
2691 /* memory reference case (for both input and
2695 sv
.r
= (sv
.r
& ~VT_VALMASK
) | VT_LOCAL
| VT_LVAL
;
2698 } else if (i
>= nb_outputs
|| op
->is_rw
) { // not write-only
2699 /* load value in register */
2700 load(op
->reg
, op
->vt
);
2702 tcc_error("long long not implemented");
2707 /* generate save code */
2708 for(i
= 0 ; i
< nb_outputs
; i
++) {
2711 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
) {
2712 if (!op
->is_memory
) {
2715 sv
.r
= (sv
.r
& ~VT_VALMASK
) | VT_LOCAL
;
2720 sv
.r
= (sv
.r
& ~VT_VALMASK
) | out_reg
;
2721 store(op
->reg
, &sv
);
2724 store(op
->reg
, op
->vt
);
2726 tcc_error("long long not implemented");
2731 /* generate reg restore code */
2733 gen_le32(0xe8bd0000 | saved_regset
); // pop {...}
2737 /* return the constraint priority (we allocate first the lowest
2738 numbered constraints) */
2739 static inline int constraint_priority(const char *str
)
2741 int priority
, c
, pr
;
2743 /* we take the lowest priority */
2751 case 'l': // in ARM mode, that's an alias for 'r' [ARM].
2752 case 'r': // register [general]
2753 case 'p': // valid memory address for load,store [general]
2756 case 'M': // integer constant for shifts [ARM]
2757 case 'I': // integer valid for data processing instruction immediate
2758 case 'J': // integer in range -4095...4095
2760 case 'i': // immediate integer operand, including symbolic constants [general]
2761 case 'm': // memory operand [general]
2762 case 'g': // general-purpose-register, memory, immediate integer [general]
2766 tcc_error("unknown constraint '%c'", c
);
2774 static const char *skip_constraint_modifiers(const char *p
)
2776 /* Constraint modifier:
2777 = Operand is written to by this instruction
2778 + Operand is both read and written to by this instruction
2779 % Instruction is commutative for this operand and the following operand.
2781 Per-alternative constraint modifier:
2782 & Operand is clobbered before the instruction is done using the input operands
2784 while (*p
== '=' || *p
== '&' || *p
== '+' || *p
== '%')
2789 #define REG_OUT_MASK 0x01
2790 #define REG_IN_MASK 0x02
2792 #define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
2794 ST_FUNC
void asm_compute_constraints(ASMOperand
*operands
,
2795 int nb_operands
, int nb_outputs
,
2796 const uint8_t *clobber_regs
,
2799 /* overall format: modifier, then ,-seperated list of alternatives; all operands for a single instruction must have the same number of alternatives */
2800 /* TODO: Simple constraints
2802 o memory operand that is offsetable
2803 V memory but not offsetable
2804 < memory operand with autodecrement addressing is allowed. Restrictions apply.
2805 > memory operand with autoincrement addressing is allowed. Restrictions apply.
2806 n immediate integer operand with a known numeric value
2807 E immediate floating operand (const_double) is allowed, but only if target=host
2808 F immediate floating operand (const_double or const_vector) is allowed
2809 s immediate integer operand whose value is not an explicit integer
2810 X any operand whatsoever
2811 0...9 (postfix); (can also be more than 1 digit number); an operand that matches the specified operand number is allowed
2814 /* TODO: ARM constraints:
2815 k the stack pointer register
2816 G the floating-point constant 0.0
2817 Q memory reference where the exact address is in a single register ("m" is preferable for asm statements)
2818 R an item in the constant pool
2819 S symbol in the text segment of the current file
2820 [ Uv memory reference suitable for VFP load/store insns (reg+constant offset)]
2821 [ Uy memory reference suitable for iWMMXt load/store instructions]
2822 Uq memory reference suitable for the ARMv4 ldrsb instruction
2825 int sorted_op
[MAX_ASM_OPERANDS
];
2826 int i
, j
, k
, p1
, p2
, tmp
, reg
, c
, reg_mask
;
2828 uint8_t regs_allocated
[NB_ASM_REGS
];
2831 for (i
= 0; i
< nb_operands
; i
++) {
2833 op
->input_index
= -1;
2839 /* compute constraint priority and evaluate references to output
2840 constraints if input constraints */
2841 for (i
= 0; i
< nb_operands
; i
++) {
2843 str
= op
->constraint
;
2844 str
= skip_constraint_modifiers(str
);
2845 if (isnum(*str
) || *str
== '[') {
2846 /* this is a reference to another constraint */
2847 k
= find_constraint(operands
, nb_operands
, str
, NULL
);
2848 if ((unsigned) k
>= i
|| i
< nb_outputs
)
2849 tcc_error("invalid reference in constraint %d ('%s')",
2852 if (operands
[k
].input_index
>= 0)
2853 tcc_error("cannot reference twice the same operand");
2854 operands
[k
].input_index
= i
;
2856 } else if ((op
->vt
->r
& VT_VALMASK
) == VT_LOCAL
2858 && (reg
= op
->vt
->sym
->r
& VT_VALMASK
) < VT_CONST
) {
2862 op
->priority
= constraint_priority(str
);
2866 /* sort operands according to their priority */
2867 for (i
= 0; i
< nb_operands
; i
++)
2869 for (i
= 0; i
< nb_operands
- 1; i
++) {
2870 for (j
= i
+ 1; j
< nb_operands
; j
++) {
2871 p1
= operands
[sorted_op
[i
]].priority
;
2872 p2
= operands
[sorted_op
[j
]].priority
;
2875 sorted_op
[i
] = sorted_op
[j
];
2881 for (i
= 0; i
< NB_ASM_REGS
; i
++) {
2882 if (clobber_regs
[i
])
2883 regs_allocated
[i
] = REG_IN_MASK
| REG_OUT_MASK
;
2885 regs_allocated
[i
] = 0;
2887 /* sp cannot be used */
2888 regs_allocated
[13] = REG_IN_MASK
| REG_OUT_MASK
;
2889 /* fp cannot be used yet */
2890 regs_allocated
[11] = REG_IN_MASK
| REG_OUT_MASK
;
2892 /* allocate registers and generate corresponding asm moves */
2893 for (i
= 0; i
< nb_operands
; i
++) {
2896 str
= op
->constraint
;
2897 /* no need to allocate references */
2898 if (op
->ref_index
>= 0)
2900 /* select if register is used for output, input or both */
2901 if (op
->input_index
>= 0) {
2902 reg_mask
= REG_IN_MASK
| REG_OUT_MASK
;
2903 } else if (j
< nb_outputs
) {
2904 reg_mask
= REG_OUT_MASK
;
2906 reg_mask
= REG_IN_MASK
;
2909 if (is_reg_allocated(op
->reg
))
2911 ("asm regvar requests register that's taken already");
2917 case '=': // Operand is written-to
2919 case '+': // Operand is both READ and written-to
2922 case '&': // Operand is clobbered before the instruction is done using the input operands
2923 if (j
>= nb_outputs
)
2924 tcc_error("'%c' modifier can only be applied to outputs",
2926 reg_mask
= REG_IN_MASK
| REG_OUT_MASK
;
2928 case 'l': // In non-thumb mode, alias for 'r'--otherwise r0-r7 [ARM]
2929 case 'r': // general-purpose register
2930 case 'p': // loadable/storable address
2931 /* any general register */
2932 if ((reg
= op
->reg
) >= 0)
2934 else for (reg
= 0; reg
<= 8; reg
++) {
2935 if (!is_reg_allocated(reg
))
2940 /* now we can reload in the register */
2943 regs_allocated
[reg
] |= reg_mask
;
2945 case 'I': // integer that is valid as an data processing instruction immediate (0...255, rotated by a multiple of two)
2946 case 'J': // integer in the range -4095 to 4095 [ARM]
2947 case 'K': // integer that satisfies constraint I when inverted (one's complement)
2948 case 'L': // integer that satisfies constraint I when inverted (two's complement)
2949 case 'i': // immediate integer operand, including symbolic constants
2950 if (!((op
->vt
->r
& (VT_VALMASK
| VT_LVAL
)) == VT_CONST
))
2953 case 'M': // integer in the range 0 to 32
2955 ((op
->vt
->r
& (VT_VALMASK
| VT_LVAL
| VT_SYM
)) ==
2959 case 'm': // memory operand
2961 /* nothing special to do because the operand is already in
2962 memory, except if the pointer itself is stored in a
2963 memory variable (VT_LLOCAL case) */
2964 /* XXX: fix constant case */
2965 /* if it is a reference to a memory zone, it must lie
2966 in a register, so we reserve the register in the
2967 input registers and a load will be generated
2969 if (j
< nb_outputs
|| c
== 'm') {
2970 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
) {
2971 /* any general register */
2972 for (reg
= 0; reg
<= 8; reg
++) {
2973 if (!(regs_allocated
[reg
] & REG_IN_MASK
))
2978 /* now we can reload in the register */
2979 regs_allocated
[reg
] |= REG_IN_MASK
;
2986 tcc_error("asm constraint %d ('%s') could not be satisfied",
2990 /* if a reference is present for that operand, we assign it too */
2991 if (op
->input_index
>= 0) {
2992 operands
[op
->input_index
].reg
= op
->reg
;
2993 operands
[op
->input_index
].is_llong
= op
->is_llong
;
2997 /* compute out_reg. It is used to store outputs registers to memory
2998 locations references by pointers (VT_LLOCAL case) */
3000 for (i
= 0; i
< nb_operands
; i
++) {
3003 (op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& !op
->is_memory
) {
3004 for (reg
= 0; reg
<= 8; reg
++) {
3005 if (!(regs_allocated
[reg
] & REG_OUT_MASK
))
3008 tcc_error("could not find free output register for reloading");
3015 /* print sorted constraints */
3017 for (i
= 0; i
< nb_operands
; i
++) {
3020 printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
3022 op
->id
? get_tok_str(op
->id
, NULL
) : "",
3023 op
->constraint
, op
->vt
->r
, op
->reg
);
3026 printf("out_reg=%d\n", *pout_reg
);
3030 ST_FUNC
void asm_clobber(uint8_t *clobber_regs
, const char *str
)
3035 if (!strcmp(str
, "memory") ||
3036 !strcmp(str
, "cc") ||
3037 !strcmp(str
, "flags"))
3039 ts
= tok_alloc(str
, strlen(str
));
3040 reg
= asm_parse_regvar(ts
->tok
);
3042 tcc_error("invalid clobber register '%s'", str
);
3044 clobber_regs
[reg
] = 1;
3047 /* If T refers to a register then return the register number and type.
3048 Otherwise return -1. */
3049 ST_FUNC
int asm_parse_regvar (int t
)
3051 if (t
>= TOK_ASM_r0
&& t
<= TOK_ASM_pc
) { /* register name */
3054 return TOK_ASM_r11
- TOK_ASM_r0
;
3056 return TOK_ASM_r12
- TOK_ASM_r0
;
3058 return TOK_ASM_r13
- TOK_ASM_r0
;
3060 return TOK_ASM_r14
- TOK_ASM_r0
;
3062 return TOK_ASM_r15
- TOK_ASM_r0
;
3064 return t
- TOK_ASM_r0
;
3070 /*************************************************************/
3071 #endif /* ndef TARGET_DEFS_ONLY */