Fix test90 for 32 bits targets
[tinycc.git] / arm-asm.c
bloba6dec4c3d0118a778c4133a6dac0d2a61070f020
1 /*
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 /*************************************************************/
32 #else
33 /*************************************************************/
35 #define USING_GLOBALS
36 #include "tcc.h"
38 enum {
39 OPT_REG32,
40 OPT_REGSET32,
41 OPT_IM8,
42 OPT_IM8N,
43 OPT_IM32,
44 OPT_VREG32,
45 OPT_VREG64,
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 {
57 uint32_t type;
58 union {
59 uint8_t reg;
60 uint16_t regset;
61 ExprValue e;
63 } 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;
73 } else {
74 if (t >= TOK_ASM_s0 && t <= TOK_ASM_s31)
75 return t - TOK_ASM_s0;
77 return -1;
80 /* Parse a text containing operand and store the result in OP */
81 static void parse_operand(TCCState *s1, Operand *op)
83 ExprValue e;
84 int8_t reg;
85 uint16_t regset = 0;
87 op->type = 0;
89 if (tok == '{') { // regset literal
90 next(); // skip '{'
91 while (tok != '}' && tok != TOK_EOF) {
92 reg = asm_parse_regvar(tok);
93 if (reg == -1) {
94 expect("register");
95 return;
96 } else
97 next(); // skip register name
99 if ((1 << reg) < regset)
100 tcc_warning("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
101 regset |= 1 << reg;
102 if (tok != ',')
103 break;
104 next(); // skip ','
106 if (tok != '}')
107 expect("'}'");
108 next(); // skip '}'
109 if (regset == 0) {
110 // ARM instructions don't support empty regset.
111 tcc_error("empty register list is not supported");
112 } else {
113 op->type = OP_REGSET32;
114 op->regset = regset;
116 return;
117 } else if ((reg = asm_parse_regvar(tok)) != -1) {
118 next(); // skip register name
119 op->type = OP_REG32;
120 op->reg = (uint8_t) reg;
121 return;
122 } else if ((reg = asm_parse_vfp_regvar(tok, 0)) != -1) {
123 next(); // skip register name
124 op->type = OP_VREG32;
125 op->reg = (uint8_t) reg;
126 return;
127 } else if ((reg = asm_parse_vfp_regvar(tok, 1)) != -1) {
128 next(); // skip register name
129 op->type = OP_VREG64;
130 op->reg = (uint8_t) reg;
131 return;
132 } else if (tok == '#' || tok == '$') {
133 /* constant value */
134 next(); // skip '#' or '$'
136 asm_expr(s1, &e);
137 op->type = OP_IM32;
138 op->e = e;
139 if (!op->e.sym) {
140 if ((int) op->e.v < 0 && (int) op->e.v >= -255)
141 op->type = OP_IM8N;
142 else if (op->e.v == (uint8_t)op->e.v)
143 op->type = OP_IM8;
144 } else
145 expect("operand");
148 /* XXX: make it faster ? */
149 ST_FUNC void g(int c)
151 int ind1;
152 if (nocode_wanted)
153 return;
154 ind1 = ind + 1;
155 if (ind1 > cur_text_section->data_allocated)
156 section_realloc(cur_text_section, ind1);
157 cur_text_section->data[ind] = c;
158 ind = ind1;
161 ST_FUNC void gen_le16 (int i)
163 g(i);
164 g(i>>8);
167 ST_FUNC void gen_le32 (int i)
169 int ind1;
170 if (nocode_wanted)
171 return;
172 ind1 = ind + 4;
173 if (ind1 > cur_text_section->data_allocated)
174 section_realloc(cur_text_section, ind1);
175 cur_text_section->data[ind++] = i & 0xFF;
176 cur_text_section->data[ind++] = (i >> 8) & 0xFF;
177 cur_text_section->data[ind++] = (i >> 16) & 0xFF;
178 cur_text_section->data[ind++] = (i >> 24) & 0xFF;
181 ST_FUNC void gen_expr32(ExprValue *pe)
183 gen_le32(pe->v);
186 static uint32_t condition_code_of_token(int token) {
187 if (token < TOK_ASM_nopeq) {
188 expect("condition-enabled instruction");
189 return 0;
190 } else
191 return (token - TOK_ASM_nopeq) & 15;
194 static void asm_emit_opcode(int token, uint32_t opcode) {
195 gen_le32((condition_code_of_token(token) << 28) | opcode);
198 static void asm_emit_unconditional_opcode(uint32_t opcode) {
199 gen_le32(opcode);
202 static void asm_emit_coprocessor_opcode(uint32_t high_nibble, uint8_t cp_number, uint8_t cp_opcode, uint8_t cp_destination_register, uint8_t cp_n_operand_register, uint8_t cp_m_operand_register, uint8_t cp_opcode2, int inter_processor_transfer)
204 uint32_t opcode = 0xe000000;
205 if (inter_processor_transfer)
206 opcode |= 1 << 4;
207 //assert(cp_opcode < 16);
208 opcode |= cp_opcode << 20;
209 //assert(cp_n_operand_register < 16);
210 opcode |= cp_n_operand_register << 16;
211 //assert(cp_destination_register < 16);
212 opcode |= cp_destination_register << 12;
213 //assert(cp_number < 16);
214 opcode |= cp_number << 8;
215 //assert(cp_information < 8);
216 opcode |= cp_opcode2 << 5;
217 //assert(cp_m_operand_register < 16);
218 opcode |= cp_m_operand_register;
219 asm_emit_unconditional_opcode((high_nibble << 28) | opcode);
222 static void asm_nullary_opcode(int token)
224 switch (ARM_INSTRUCTION_GROUP(token)) {
225 case TOK_ASM_nopeq:
226 asm_emit_opcode(token, 0xd << 21); // mov r0, r0
227 break;
228 case TOK_ASM_wfeeq:
229 asm_emit_opcode(token, 0x320f002);
230 case TOK_ASM_wfieq:
231 asm_emit_opcode(token, 0x320f003);
232 break;
233 default:
234 expect("nullary instruction");
238 static void asm_unary_opcode(TCCState *s1, int token)
240 Operand op;
241 parse_operand(s1, &op);
243 switch (ARM_INSTRUCTION_GROUP(token)) {
244 case TOK_ASM_swieq:
245 case TOK_ASM_svceq:
246 if (op.type != OP_IM8)
247 expect("immediate 8-bit unsigned integer");
248 else {
249 /* Note: Dummy operand (ignored by processor): ARM ref documented 0...255, ARM instruction set documented 24 bit */
250 asm_emit_opcode(token, (0xf << 24) | op.e.v);
252 break;
253 default:
254 expect("unary instruction");
258 static void asm_binary_opcode(TCCState *s1, int token)
260 Operand ops[2];
261 Operand rotation;
262 uint32_t encoded_rotation = 0;
263 uint64_t amount;
264 parse_operand(s1, &ops[0]);
265 if (tok == ',')
266 next();
267 else
268 expect("','");
269 parse_operand(s1, &ops[1]);
270 if (ops[0].type != OP_REG32) {
271 expect("(destination operand) register");
272 return;
275 if (ops[0].reg == 15) {
276 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL));
277 return;
280 if (ops[0].reg == 13)
281 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token, NULL));
283 if (ops[1].type != OP_REG32) {
284 switch (ARM_INSTRUCTION_GROUP(token)) {
285 case TOK_ASM_movteq:
286 case TOK_ASM_movweq:
287 if (ops[1].type == OP_IM8 || ops[1].type == OP_IM8N || ops[1].type == OP_IM32) {
288 if (ops[1].e.v >= 0 && ops[1].e.v <= 0xFFFF) {
289 uint16_t immediate_value = ops[1].e.v;
290 switch (ARM_INSTRUCTION_GROUP(token)) {
291 case TOK_ASM_movteq:
292 asm_emit_opcode(token, 0x3400000 | (ops[0].reg << 12) | (immediate_value & 0xF000) << 4 | (immediate_value & 0xFFF));
293 break;
294 case TOK_ASM_movweq:
295 asm_emit_opcode(token, 0x3000000 | (ops[0].reg << 12) | (immediate_value & 0xF000) << 4 | (immediate_value & 0xFFF));
296 break;
298 } else
299 expect("(source operand) immediate 16 bit value");
300 } else
301 expect("(source operand) immediate");
302 break;
303 default:
304 expect("(source operand) register");
306 return;
309 if (ops[1].reg == 15) {
310 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL));
311 return;
314 if (ops[1].reg == 13)
315 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token, NULL));
317 if (tok == ',') {
318 next(); // skip ','
319 if (tok == TOK_ASM_ror) {
320 next(); // skip 'ror'
321 parse_operand(s1, &rotation);
322 if (rotation.type != OP_IM8) {
323 expect("immediate value for rotation");
324 return;
325 } else {
326 amount = rotation.e.v;
327 switch (amount) {
328 case 8:
329 encoded_rotation = 1 << 10;
330 break;
331 case 16:
332 encoded_rotation = 2 << 10;
333 break;
334 case 24:
335 encoded_rotation = 3 << 10;
336 break;
337 default:
338 expect("'8' or '16' or '24'");
339 return;
344 switch (ARM_INSTRUCTION_GROUP(token)) {
345 case TOK_ASM_clzeq:
346 if (encoded_rotation)
347 tcc_error("clz does not support rotation");
348 asm_emit_opcode(token, 0x16f0f10 | (ops[0].reg << 12) | ops[1].reg);
349 break;
350 case TOK_ASM_sxtbeq:
351 asm_emit_opcode(token, 0x6af0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation);
352 break;
353 case TOK_ASM_sxtheq:
354 asm_emit_opcode(token, 0x6bf0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation);
355 break;
356 case TOK_ASM_uxtbeq:
357 asm_emit_opcode(token, 0x6ef0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation);
358 break;
359 case TOK_ASM_uxtheq:
360 asm_emit_opcode(token, 0x6ff0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation);
361 break;
362 default:
363 expect("binary instruction");
367 static void asm_coprocessor_opcode(TCCState *s1, int token) {
368 uint8_t coprocessor;
369 Operand opcode1;
370 Operand opcode2;
371 uint8_t registers[3];
372 unsigned int i;
373 uint8_t high_nibble;
374 uint8_t mrc = 0;
376 if (tok >= TOK_ASM_p0 && tok <= TOK_ASM_p15) {
377 coprocessor = tok - TOK_ASM_p0;
378 next();
379 } else {
380 expect("'p<number>'");
381 return;
384 if (tok == ',')
385 next();
386 else
387 expect("','");
389 parse_operand(s1, &opcode1);
390 if (opcode1.type != OP_IM8 || opcode1.e.v > 15) {
391 tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token, NULL));
392 return;
395 for (i = 0; i < 3; ++i) {
396 if (tok == ',')
397 next();
398 else
399 expect("','");
400 if (i == 0 && token != TOK_ASM_cdp2 && (ARM_INSTRUCTION_GROUP(token) == TOK_ASM_mrceq || ARM_INSTRUCTION_GROUP(token) == TOK_ASM_mcreq)) {
401 if (tok >= TOK_ASM_r0 && tok <= TOK_ASM_r15) {
402 registers[i] = tok - TOK_ASM_r0;
403 next();
404 } else {
405 expect("'r<number>'");
406 return;
408 } else {
409 if (tok >= TOK_ASM_c0 && tok <= TOK_ASM_c15) {
410 registers[i] = tok - TOK_ASM_c0;
411 next();
412 } else {
413 expect("'c<number>'");
414 return;
418 if (tok == ',') {
419 next();
420 parse_operand(s1, &opcode2);
421 } else {
422 opcode2.type = OP_IM8;
423 opcode2.e.v = 0;
425 if (opcode2.type != OP_IM8 || opcode2.e.v > 15) {
426 tcc_error("opcode2 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token, NULL));
427 return;
430 if (token == TOK_ASM_cdp2) {
431 high_nibble = 0xF;
432 asm_emit_coprocessor_opcode(high_nibble, coprocessor, opcode1.e.v, registers[0], registers[1], registers[2], opcode2.e.v, 0);
433 return;
434 } else
435 high_nibble = condition_code_of_token(token);
437 switch (ARM_INSTRUCTION_GROUP(token)) {
438 case TOK_ASM_cdpeq:
439 asm_emit_coprocessor_opcode(high_nibble, coprocessor, opcode1.e.v, registers[0], registers[1], registers[2], opcode2.e.v, 0);
440 break;
441 case TOK_ASM_mrceq:
442 // opcode1 encoding changes! highest and lowest bit gone.
443 mrc = 1;
444 /* fallthrough */
445 case TOK_ASM_mcreq:
446 // opcode1 encoding changes! highest and lowest bit gone.
447 if (opcode1.e.v > 7) {
448 tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 7", get_tok_str(token, NULL));
449 return;
451 asm_emit_coprocessor_opcode(high_nibble, coprocessor, (opcode1.e.v << 1) | mrc, registers[0], registers[1], registers[2], opcode2.e.v, 1);
452 break;
453 default:
454 expect("known instruction");
458 /* data processing and single data transfer instructions only */
459 #define ENCODE_RN(register_index) ((register_index) << 16)
460 #define ENCODE_RD(register_index) ((register_index) << 12)
461 #define ENCODE_SET_CONDITION_CODES (1 << 20)
463 /* Note: For data processing instructions, "1" means immediate.
464 Note: For single data transfer instructions, "0" means immediate. */
465 #define ENCODE_IMMEDIATE_FLAG (1 << 25)
467 #define ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER (1 << 4)
468 #define ENCODE_BARREL_SHIFTER_MODE_LSL (0 << 5)
469 #define ENCODE_BARREL_SHIFTER_MODE_LSR (1 << 5)
470 #define ENCODE_BARREL_SHIFTER_MODE_ASR (2 << 5)
471 #define ENCODE_BARREL_SHIFTER_MODE_ROR (3 << 5)
472 #define ENCODE_BARREL_SHIFTER_REGISTER(register_index) ((register_index) << 8)
473 #define ENCODE_BARREL_SHIFTER_IMMEDIATE(value) ((value) << 7)
475 static void asm_block_data_transfer_opcode(TCCState *s1, int token)
477 uint32_t opcode;
478 int op0_exclam = 0;
479 Operand ops[2];
480 int nb_ops = 1;
481 parse_operand(s1, &ops[0]);
482 if (tok == '!') {
483 op0_exclam = 1;
484 next(); // skip '!'
486 if (tok == ',') {
487 next(); // skip comma
488 parse_operand(s1, &ops[1]);
489 ++nb_ops;
491 if (nb_ops < 1) {
492 expect("at least one operand");
493 return;
494 } else if (ops[nb_ops - 1].type != OP_REGSET32) {
495 expect("(last operand) register list");
496 return;
499 // block data transfer: 1 0 0 P U S W L << 20 (general case):
500 // operands:
501 // Rn: bits 19...16 base register
502 // Register List: bits 15...0
504 switch (ARM_INSTRUCTION_GROUP(token)) {
505 case TOK_ASM_pusheq: // TODO: Optimize 1-register case to: str ?, [sp, #-4]!
506 // Instruction: 1 I=0 P=1 U=0 S=0 W=1 L=0 << 20, op 1101
507 // operands:
508 // Rn: base register
509 // Register List: bits 15...0
510 if (nb_ops != 1)
511 expect("exactly one operand");
512 else
513 asm_emit_opcode(token, (0x92d << 16) | ops[0].regset); // TODO: base register ?
514 break;
515 case TOK_ASM_popeq: // TODO: Optimize 1-register case to: ldr ?, [sp], #4
516 // Instruction: 1 I=0 P=0 U=1 S=0 W=0 L=1 << 20, op 1101
517 // operands:
518 // Rn: base register
519 // Register List: bits 15...0
520 if (nb_ops != 1)
521 expect("exactly one operand");
522 else
523 asm_emit_opcode(token, (0x8bd << 16) | ops[0].regset); // TODO: base register ?
524 break;
525 case TOK_ASM_stmdaeq:
526 case TOK_ASM_ldmdaeq:
527 case TOK_ASM_stmeq:
528 case TOK_ASM_ldmeq:
529 case TOK_ASM_stmiaeq:
530 case TOK_ASM_ldmiaeq:
531 case TOK_ASM_stmdbeq:
532 case TOK_ASM_ldmdbeq:
533 case TOK_ASM_stmibeq:
534 case TOK_ASM_ldmibeq:
535 switch (ARM_INSTRUCTION_GROUP(token)) {
536 case TOK_ASM_stmdaeq: // post-decrement store
537 opcode = 0x80 << 20;
538 break;
539 case TOK_ASM_ldmdaeq: // post-decrement load
540 opcode = 0x81 << 20;
541 break;
542 case TOK_ASM_stmeq: // post-increment store
543 case TOK_ASM_stmiaeq: // post-increment store
544 opcode = 0x88 << 20;
545 break;
546 case TOK_ASM_ldmeq: // post-increment load
547 case TOK_ASM_ldmiaeq: // post-increment load
548 opcode = 0x89 << 20;
549 break;
550 case TOK_ASM_stmdbeq: // pre-decrement store
551 opcode = 0x90 << 20;
552 break;
553 case TOK_ASM_ldmdbeq: // pre-decrement load
554 opcode = 0x91 << 20;
555 break;
556 case TOK_ASM_stmibeq: // pre-increment store
557 opcode = 0x98 << 20;
558 break;
559 case TOK_ASM_ldmibeq: // pre-increment load
560 opcode = 0x99 << 20;
561 break;
562 default:
563 tcc_error("internal error: This place should not be reached (fallback in asm_block_data_transfer_opcode)");
565 // operands:
566 // Rn: first operand
567 // Register List: lower bits
568 if (nb_ops != 2)
569 expect("exactly two operands");
570 else if (ops[0].type != OP_REG32)
571 expect("(first operand) register");
572 else {
573 if (op0_exclam)
574 opcode |= 1 << 21; // writeback
575 asm_emit_opcode(token, opcode | ENCODE_RN(ops[0].reg) | ops[1].regset);
577 break;
578 default:
579 expect("block data transfer instruction");
583 /* Parses shift directive and returns the parts that would have to be set in the opcode because of it.
584 Does not encode the actual shift amount.
585 It's not an error if there is no shift directive.
587 NB_SHIFT: will be set to 1 iff SHIFT is filled. Note that for rrx, there's no need to fill SHIFT.
588 SHIFT: will be filled in with the shift operand to use, if any. */
589 static uint32_t asm_parse_optional_shift(TCCState* s1, int* nb_shift, Operand* shift)
591 uint32_t opcode = 0;
592 *nb_shift = 0;
593 switch (tok) {
594 case TOK_ASM_asl:
595 case TOK_ASM_lsl:
596 case TOK_ASM_asr:
597 case TOK_ASM_lsr:
598 case TOK_ASM_ror:
599 switch (tok) {
600 case TOK_ASM_asl:
601 /* fallthrough */
602 case TOK_ASM_lsl:
603 opcode = ENCODE_BARREL_SHIFTER_MODE_LSL;
604 break;
605 case TOK_ASM_asr:
606 opcode = ENCODE_BARREL_SHIFTER_MODE_ASR;
607 break;
608 case TOK_ASM_lsr:
609 opcode = ENCODE_BARREL_SHIFTER_MODE_LSR;
610 break;
611 case TOK_ASM_ror:
612 opcode = ENCODE_BARREL_SHIFTER_MODE_ROR;
613 break;
615 next();
616 parse_operand(s1, shift);
617 *nb_shift = 1;
618 break;
619 case TOK_ASM_rrx:
620 next();
621 opcode = ENCODE_BARREL_SHIFTER_MODE_ROR;
622 break;
624 return opcode;
627 static uint32_t asm_encode_shift(Operand* shift)
629 uint64_t amount;
630 uint32_t operands = 0;
631 switch (shift->type) {
632 case OP_REG32:
633 if (shift->reg == 15)
634 tcc_error("r15 cannot be used as a shift count");
635 else {
636 operands = ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER;
637 operands |= ENCODE_BARREL_SHIFTER_REGISTER(shift->reg);
639 break;
640 case OP_IM8:
641 amount = shift->e.v;
642 if (amount > 0 && amount < 32)
643 operands = ENCODE_BARREL_SHIFTER_IMMEDIATE(amount);
644 else
645 tcc_error("shift count out of range");
646 break;
647 default:
648 tcc_error("unknown shift amount");
650 return operands;
653 static void asm_data_processing_opcode(TCCState *s1, int token)
655 Operand ops[3];
656 int nb_ops;
657 Operand shift = {0};
658 int nb_shift = 0;
659 uint32_t operands = 0;
661 /* modulo 16 entries per instruction for the different condition codes */
662 uint32_t opcode_idx = (ARM_INSTRUCTION_GROUP(token) - TOK_ASM_andeq) >> 4;
663 uint32_t opcode_nos = opcode_idx >> 1; // without "s"; "OpCode" in ARM docs
665 for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ) {
666 if (tok == TOK_ASM_asl || tok == TOK_ASM_lsl || tok == TOK_ASM_lsr || tok == TOK_ASM_asr || tok == TOK_ASM_ror || tok == TOK_ASM_rrx)
667 break;
668 parse_operand(s1, &ops[nb_ops]);
669 ++nb_ops;
670 if (tok != ',')
671 break;
672 next(); // skip ','
674 if (tok == ',')
675 next();
676 operands |= asm_parse_optional_shift(s1, &nb_shift, &shift);
677 if (nb_ops < 2)
678 expect("at least two operands");
679 else if (nb_ops == 2) {
680 memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2]
681 memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit
682 nb_ops = 3;
683 } else if (nb_ops == 3) {
684 if (opcode_nos == 0xd || opcode_nos == 0xf || opcode_nos == 0xa || opcode_nos == 0xb || opcode_nos == 0x8 || opcode_nos == 0x9) { // mov, mvn, cmp, cmn, tst, teq
685 tcc_error("'%s' cannot be used with three operands", get_tok_str(token, NULL));
686 return;
689 if (nb_ops != 3) {
690 expect("two or three operands");
691 return;
692 } else {
693 uint32_t opcode = 0;
694 uint32_t immediate_value;
695 uint8_t half_immediate_rotation;
696 if (nb_shift && shift.type == OP_REG32) {
697 if ((ops[0].type == OP_REG32 && ops[0].reg == 15) ||
698 (ops[1].type == OP_REG32 && ops[1].reg == 15)) {
699 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
700 return;
704 // data processing (general case):
705 // operands:
706 // Rn: bits 19...16 (first operand)
707 // Rd: bits 15...12 (destination)
708 // Operand2: bits 11...0 (second operand); depending on I that's either a register or an immediate
709 // operator:
710 // bits 24...21: "OpCode"--see below
712 /* operations in the token list are ordered by opcode */
713 opcode = opcode_nos << 21; // drop "s"
714 if (ops[0].type != OP_REG32)
715 expect("(destination operand) register");
716 else if (opcode_nos == 0xa || opcode_nos == 0xb || opcode_nos == 0x8 || opcode_nos == 0x9) // cmp, cmn, tst, teq
717 operands |= ENCODE_SET_CONDITION_CODES; // force S set, otherwise it's a completely different instruction.
718 else
719 operands |= ENCODE_RD(ops[0].reg);
720 if (ops[1].type != OP_REG32)
721 expect("(first source operand) register");
722 else if (!(opcode_nos == 0xd || opcode_nos == 0xf)) // not: mov, mvn (those have only one source operand)
723 operands |= ENCODE_RN(ops[1].reg);
724 switch (ops[2].type) {
725 case OP_REG32:
726 operands |= ops[2].reg;
727 break;
728 case OP_IM8:
729 case OP_IM32:
730 operands |= ENCODE_IMMEDIATE_FLAG;
731 immediate_value = ops[2].e.v;
732 for (half_immediate_rotation = 0; half_immediate_rotation < 16; ++half_immediate_rotation) {
733 if (immediate_value >= 0x00 && immediate_value < 0x100)
734 break;
735 // rotate left by two
736 immediate_value = ((immediate_value & 0x3FFFFFFF) << 2) | ((immediate_value & 0xC0000000) >> 30);
738 if (half_immediate_rotation >= 16) {
739 /* fallthrough */
740 } else {
741 operands |= immediate_value;
742 operands |= half_immediate_rotation << 8;
743 break;
745 case OP_IM8N: // immediate negative value
746 operands |= ENCODE_IMMEDIATE_FLAG;
747 immediate_value = ops[2].e.v;
748 /* Instruction swapping:
749 0001 = EOR - Rd:= Op1 EOR Op2 -> difficult
750 0011 = RSB - Rd:= Op2 - Op1 -> difficult
751 0111 = RSC - Rd:= Op2 - Op1 + C -> difficult
752 1000 = TST - CC on: Op1 AND Op2 -> difficult
753 1001 = TEQ - CC on: Op1 EOR Op2 -> difficult
754 1100 = ORR - Rd:= Op1 OR Op2 -> difficult
756 switch (opcode_nos) {
757 case 0x0: // AND - Rd:= Op1 AND Op2
758 opcode = 0xe << 21; // BIC
759 immediate_value = ~immediate_value;
760 break;
761 case 0x2: // SUB - Rd:= Op1 - Op2
762 opcode = 0x4 << 21; // ADD
763 immediate_value = -immediate_value;
764 break;
765 case 0x4: // ADD - Rd:= Op1 + Op2
766 opcode = 0x2 << 21; // SUB
767 immediate_value = -immediate_value;
768 break;
769 case 0x5: // ADC - Rd:= Op1 + Op2 + C
770 opcode = 0x6 << 21; // SBC
771 immediate_value = ~immediate_value;
772 break;
773 case 0x6: // SBC - Rd:= Op1 - Op2 + C
774 opcode = 0x5 << 21; // ADC
775 immediate_value = ~immediate_value;
776 break;
777 case 0xa: // CMP - CC on: Op1 - Op2
778 opcode = 0xb << 21; // CMN
779 immediate_value = -immediate_value;
780 break;
781 case 0xb: // CMN - CC on: Op1 + Op2
782 opcode = 0xa << 21; // CMP
783 immediate_value = -immediate_value;
784 break;
785 case 0xd: // MOV - Rd:= Op2
786 opcode = 0xf << 21; // MVN
787 immediate_value = ~immediate_value;
788 break;
789 case 0xe: // BIC - Rd:= Op1 AND NOT Op2
790 opcode = 0x0 << 21; // AND
791 immediate_value = ~immediate_value;
792 break;
793 case 0xf: // MVN - Rd:= NOT Op2
794 opcode = 0xd << 21; // MOV
795 immediate_value = ~immediate_value;
796 break;
797 default:
798 tcc_error("cannot use '%s' with a negative immediate value", get_tok_str(token, NULL));
800 for (half_immediate_rotation = 0; half_immediate_rotation < 16; ++half_immediate_rotation) {
801 if (immediate_value >= 0x00 && immediate_value < 0x100)
802 break;
803 // rotate left by two
804 immediate_value = ((immediate_value & 0x3FFFFFFF) << 2) | ((immediate_value & 0xC0000000) >> 30);
806 if (half_immediate_rotation >= 16) {
807 immediate_value = ops[2].e.v;
808 tcc_error("immediate value 0x%X cannot be encoded into ARM immediate", (unsigned) immediate_value);
809 return;
811 operands |= immediate_value;
812 operands |= half_immediate_rotation << 8;
813 break;
814 default:
815 expect("(second source operand) register or immediate value");
818 if (nb_shift) {
819 if (operands & ENCODE_IMMEDIATE_FLAG)
820 tcc_error("immediate rotation not implemented");
821 else
822 operands |= asm_encode_shift(&shift);
825 /* S=0 and S=1 entries alternate one after another, in that order */
826 opcode |= (opcode_idx & 1) ? ENCODE_SET_CONDITION_CODES : 0;
827 asm_emit_opcode(token, opcode | operands);
831 static void asm_shift_opcode(TCCState *s1, int token)
833 Operand ops[3];
834 int nb_ops;
835 int definitely_neutral = 0;
836 uint32_t opcode = 0xd << 21; // MOV
837 uint32_t operands = 0;
839 for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ++nb_ops) {
840 parse_operand(s1, &ops[nb_ops]);
841 if (tok != ',') {
842 ++nb_ops;
843 break;
845 next(); // skip ','
847 if (nb_ops < 2) {
848 expect("at least two operands");
849 return;
852 if (ops[0].type != OP_REG32) {
853 expect("(destination operand) register");
854 return;
855 } else
856 operands |= ENCODE_RD(ops[0].reg);
858 if (nb_ops == 2) {
859 switch (ARM_INSTRUCTION_GROUP(token)) {
860 case TOK_ASM_rrxseq:
861 opcode |= ENCODE_SET_CONDITION_CODES;
862 /* fallthrough */
863 case TOK_ASM_rrxeq:
864 if (ops[1].type == OP_REG32) {
865 operands |= ops[1].reg;
866 operands |= ENCODE_BARREL_SHIFTER_MODE_ROR;
867 asm_emit_opcode(token, opcode | operands);
868 } else
869 tcc_error("(first source operand) register");
870 return;
871 default:
872 memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2]
873 memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit
874 nb_ops = 3;
877 if (nb_ops != 3) {
878 expect("two or three operands");
879 return;
882 switch (ARM_INSTRUCTION_GROUP(token)) {
883 case TOK_ASM_lslseq:
884 case TOK_ASM_lsrseq:
885 case TOK_ASM_asrseq:
886 case TOK_ASM_rorseq:
887 opcode |= ENCODE_SET_CONDITION_CODES;
888 break;
891 switch (ops[1].type) {
892 case OP_REG32:
893 operands |= ops[1].reg;
894 break;
895 case OP_IM8:
896 operands |= ENCODE_IMMEDIATE_FLAG;
897 operands |= ops[1].e.v;
898 tcc_error("Using an immediate value as the source operand is not possible with '%s' instruction on ARM", get_tok_str(token, NULL));
899 return;
902 switch (ops[2].type) {
903 case OP_REG32:
904 if ((ops[0].type == OP_REG32 && ops[0].reg == 15) ||
905 (ops[1].type == OP_REG32 && ops[1].reg == 15)) {
906 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
908 operands |= asm_encode_shift(&ops[2]);
909 break;
910 case OP_IM8:
911 if (ops[2].e.v)
912 operands |= asm_encode_shift(&ops[2]);
913 else
914 definitely_neutral = 1;
915 break;
918 if (!definitely_neutral) switch (ARM_INSTRUCTION_GROUP(token)) {
919 case TOK_ASM_lslseq:
920 case TOK_ASM_lsleq:
921 operands |= ENCODE_BARREL_SHIFTER_MODE_LSL;
922 break;
923 case TOK_ASM_lsrseq:
924 case TOK_ASM_lsreq:
925 operands |= ENCODE_BARREL_SHIFTER_MODE_LSR;
926 break;
927 case TOK_ASM_asrseq:
928 case TOK_ASM_asreq:
929 operands |= ENCODE_BARREL_SHIFTER_MODE_ASR;
930 break;
931 case TOK_ASM_rorseq:
932 case TOK_ASM_roreq:
933 operands |= ENCODE_BARREL_SHIFTER_MODE_ROR;
934 break;
935 default:
936 expect("shift instruction");
937 return;
939 asm_emit_opcode(token, opcode | operands);
942 static void asm_multiplication_opcode(TCCState *s1, int token)
944 Operand ops[4];
945 int nb_ops = 0;
946 uint32_t opcode = 0x90;
948 for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ++nb_ops) {
949 parse_operand(s1, &ops[nb_ops]);
950 if (tok != ',') {
951 ++nb_ops;
952 break;
954 next(); // skip ','
956 if (nb_ops < 2)
957 expect("at least two operands");
958 else if (nb_ops == 2) {
959 switch (ARM_INSTRUCTION_GROUP(token)) {
960 case TOK_ASM_mulseq:
961 case TOK_ASM_muleq:
962 memcpy(&ops[2], &ops[0], sizeof(ops[1])); // ARM is actually like this!
963 break;
964 default:
965 expect("at least three operands");
966 return;
968 nb_ops = 3;
971 // multiply (special case):
972 // operands:
973 // Rd: bits 19...16
974 // Rm: bits 3...0
975 // Rs: bits 11...8
976 // Rn: bits 15...12
978 if (ops[0].type == OP_REG32)
979 opcode |= ops[0].reg << 16;
980 else
981 expect("(destination operand) register");
982 if (ops[1].type == OP_REG32)
983 opcode |= ops[1].reg;
984 else
985 expect("(first source operand) register");
986 if (ops[2].type == OP_REG32)
987 opcode |= ops[2].reg << 8;
988 else
989 expect("(second source operand) register");
990 if (nb_ops > 3) {
991 if (ops[3].type == OP_REG32)
992 opcode |= ops[3].reg << 12;
993 else
994 expect("(third source operand) register");
997 switch (ARM_INSTRUCTION_GROUP(token)) {
998 case TOK_ASM_mulseq:
999 opcode |= 1 << 20; // Status
1000 /* fallthrough */
1001 case TOK_ASM_muleq:
1002 if (nb_ops != 3)
1003 expect("three operands");
1004 else {
1005 asm_emit_opcode(token, opcode);
1007 break;
1008 case TOK_ASM_mlaseq:
1009 opcode |= 1 << 20; // Status
1010 /* fallthrough */
1011 case TOK_ASM_mlaeq:
1012 if (nb_ops != 4)
1013 expect("four operands");
1014 else {
1015 opcode |= 1 << 21; // Accumulate
1016 asm_emit_opcode(token, opcode);
1018 break;
1019 default:
1020 expect("known multiplication instruction");
1024 static void asm_long_multiplication_opcode(TCCState *s1, int token)
1026 Operand ops[4];
1027 int nb_ops = 0;
1028 uint32_t opcode = 0x90 | (1 << 23);
1030 for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ++nb_ops) {
1031 parse_operand(s1, &ops[nb_ops]);
1032 if (tok != ',') {
1033 ++nb_ops;
1034 break;
1036 next(); // skip ','
1038 if (nb_ops != 4) {
1039 expect("four operands");
1040 return;
1043 // long multiply (special case):
1044 // operands:
1045 // RdLo: bits 15...12
1046 // RdHi: bits 19...16
1047 // Rs: bits 11...8
1048 // Rm: bits 3...0
1050 if (ops[0].type == OP_REG32)
1051 opcode |= ops[0].reg << 12;
1052 else
1053 expect("(destination lo accumulator) register");
1054 if (ops[1].type == OP_REG32)
1055 opcode |= ops[1].reg << 16;
1056 else
1057 expect("(destination hi accumulator) register");
1058 if (ops[2].type == OP_REG32)
1059 opcode |= ops[2].reg;
1060 else
1061 expect("(first source operand) register");
1062 if (ops[3].type == OP_REG32)
1063 opcode |= ops[3].reg << 8;
1064 else
1065 expect("(second source operand) register");
1067 switch (ARM_INSTRUCTION_GROUP(token)) {
1068 case TOK_ASM_smullseq:
1069 opcode |= 1 << 20; // Status
1070 /* fallthrough */
1071 case TOK_ASM_smulleq:
1072 opcode |= 1 << 22; // signed
1073 asm_emit_opcode(token, opcode);
1074 break;
1075 case TOK_ASM_umullseq:
1076 opcode |= 1 << 20; // Status
1077 /* fallthrough */
1078 case TOK_ASM_umulleq:
1079 asm_emit_opcode(token, opcode);
1080 break;
1081 case TOK_ASM_smlalseq:
1082 opcode |= 1 << 20; // Status
1083 /* fallthrough */
1084 case TOK_ASM_smlaleq:
1085 opcode |= 1 << 22; // signed
1086 opcode |= 1 << 21; // Accumulate
1087 asm_emit_opcode(token, opcode);
1088 break;
1089 case TOK_ASM_umlalseq:
1090 opcode |= 1 << 20; // Status
1091 /* fallthrough */
1092 case TOK_ASM_umlaleq:
1093 opcode |= 1 << 21; // Accumulate
1094 asm_emit_opcode(token, opcode);
1095 break;
1096 default:
1097 expect("known long multiplication instruction");
1101 static void asm_single_data_transfer_opcode(TCCState *s1, int token)
1103 Operand ops[3];
1104 Operand strex_operand;
1105 Operand shift;
1106 int nb_shift = 0;
1107 int exclam = 0;
1108 int closed_bracket = 0;
1109 int op2_minus = 0;
1110 uint32_t opcode = 0;
1111 // Note: ldr r0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1112 // Note: ldr r0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1113 // Note: ldr r0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1115 parse_operand(s1, &ops[0]);
1116 if (ops[0].type == OP_REG32)
1117 opcode |= ENCODE_RD(ops[0].reg);
1118 else {
1119 expect("(destination operand) register");
1120 return;
1122 if (tok != ',')
1123 expect("at least two arguments");
1124 else
1125 next(); // skip ','
1127 switch (ARM_INSTRUCTION_GROUP(token)) {
1128 case TOK_ASM_strexbeq:
1129 case TOK_ASM_strexeq:
1130 parse_operand(s1, &strex_operand);
1131 if (strex_operand.type != OP_REG32) {
1132 expect("register");
1133 return;
1135 if (tok != ',')
1136 expect("at least three arguments");
1137 else
1138 next(); // skip ','
1139 break;
1142 if (tok != '[')
1143 expect("'['");
1144 else
1145 next(); // skip '['
1147 parse_operand(s1, &ops[1]);
1148 if (ops[1].type == OP_REG32)
1149 opcode |= ENCODE_RN(ops[1].reg);
1150 else {
1151 expect("(first source operand) register");
1152 return;
1154 if (tok == ']') {
1155 next();
1156 closed_bracket = 1;
1157 // exclam = 1; // implicit in hardware; don't do it in software
1159 if (tok == ',') {
1160 next(); // skip ','
1161 if (tok == '-') {
1162 op2_minus = 1;
1163 next();
1165 parse_operand(s1, &ops[2]);
1166 if (ops[2].type == OP_REG32) {
1167 if (ops[2].reg == 15) {
1168 tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token, NULL));
1169 return;
1171 if (tok == ',') {
1172 next();
1173 opcode |= asm_parse_optional_shift(s1, &nb_shift, &shift);
1174 if (opcode == 0)
1175 expect("shift directive, or no comma");
1178 } else {
1179 // end of input expression in brackets--assume 0 offset
1180 ops[2].type = OP_IM8;
1181 ops[2].e.v = 0;
1182 opcode |= 1 << 24; // add offset before transfer
1184 if (!closed_bracket) {
1185 if (tok != ']')
1186 expect("']'");
1187 else
1188 next(); // skip ']'
1189 opcode |= 1 << 24; // add offset before transfer
1190 if (tok == '!') {
1191 exclam = 1;
1192 next(); // skip '!'
1196 // single data transfer: 0 1 I P U B W L << 20 (general case):
1197 // operands:
1198 // Rd: destination operand [ok]
1199 // Rn: first source operand [ok]
1200 // Operand2: bits 11...0 [ok]
1201 // I: immediate operand? [ok]
1202 // P: Pre/post indexing is PRE: Add offset before transfer [ok]
1203 // U: Up/down is up? (*adds* offset to base) [ok]
1204 // B: Byte/word is byte? [ok]
1205 // W: Write address back into base? [ok]
1206 // L: Load/store is load? [ok]
1207 if (exclam)
1208 opcode |= 1 << 21; // write offset back into register
1210 if (ops[2].type == OP_IM32 || ops[2].type == OP_IM8 || ops[2].type == OP_IM8N) {
1211 int v = ops[2].e.v;
1212 if (op2_minus)
1213 tcc_error("minus before '#' not supported for immediate values");
1214 if (v >= 0) {
1215 opcode |= 1 << 23; // up
1216 if (v >= 0x1000)
1217 tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
1218 else
1219 opcode |= v;
1220 } else { // down
1221 if (v <= -0x1000)
1222 tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
1223 else
1224 opcode |= -v;
1226 } else if (ops[2].type == OP_REG32) {
1227 if (!op2_minus)
1228 opcode |= 1 << 23; // up
1229 opcode |= ENCODE_IMMEDIATE_FLAG; /* if set, it means it's NOT immediate */
1230 opcode |= ops[2].reg;
1231 } else
1232 expect("register");
1234 switch (ARM_INSTRUCTION_GROUP(token)) {
1235 case TOK_ASM_strbeq:
1236 opcode |= 1 << 22; // B
1237 /* fallthrough */
1238 case TOK_ASM_streq:
1239 opcode |= 1 << 26; // Load/Store
1240 if (nb_shift)
1241 opcode |= asm_encode_shift(&shift);
1242 asm_emit_opcode(token, opcode);
1243 break;
1244 case TOK_ASM_ldrbeq:
1245 opcode |= 1 << 22; // B
1246 /* fallthrough */
1247 case TOK_ASM_ldreq:
1248 opcode |= 1 << 20; // L
1249 opcode |= 1 << 26; // Load/Store
1250 if (nb_shift)
1251 opcode |= asm_encode_shift(&shift);
1252 asm_emit_opcode(token, opcode);
1253 break;
1254 case TOK_ASM_strexbeq:
1255 opcode |= 1 << 22; // B
1256 /* fallthrough */
1257 case TOK_ASM_strexeq:
1258 if ((opcode & 0xFFF) || nb_shift) {
1259 tcc_error("neither offset nor shift allowed with 'strex'");
1260 return;
1261 } else if (opcode & ENCODE_IMMEDIATE_FLAG) { // if set, it means it's NOT immediate
1262 tcc_error("offset not allowed with 'strex'");
1263 return;
1265 if ((opcode & (1 << 24)) == 0) { // add offset after transfer
1266 tcc_error("adding offset after transfer not allowed with 'strex'");
1267 return;
1270 opcode |= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL
1271 opcode |= strex_operand.reg;
1272 asm_emit_opcode(token, opcode);
1273 break;
1274 case TOK_ASM_ldrexbeq:
1275 opcode |= 1 << 22; // B
1276 /* fallthrough */
1277 case TOK_ASM_ldrexeq:
1278 if ((opcode & 0xFFF) || nb_shift) {
1279 tcc_error("neither offset nor shift allowed with 'ldrex'");
1280 return;
1281 } else if (opcode & ENCODE_IMMEDIATE_FLAG) { // if set, it means it's NOT immediate
1282 tcc_error("offset not allowed with 'ldrex'");
1283 return;
1285 if ((opcode & (1 << 24)) == 0) { // add offset after transfer
1286 tcc_error("adding offset after transfer not allowed with 'ldrex'");
1287 return;
1289 opcode |= 1 << 20; // L
1290 opcode |= 0x00f;
1291 opcode |= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL
1292 asm_emit_opcode(token, opcode);
1293 break;
1294 default:
1295 expect("data transfer instruction");
1299 // Note: Only call this using a VFP register if you know exactly what you are doing (i.e. cp_number is 10 or 11 and you are doing a vmov)
1300 static void asm_emit_coprocessor_data_transfer(uint32_t high_nibble, uint8_t cp_number, uint8_t CRd, const Operand* Rn, const Operand* offset, int offset_minus, int preincrement, int writeback, int long_transfer, int load) {
1301 uint32_t opcode = 0x0;
1302 opcode |= 1 << 26; // Load/Store
1303 opcode |= 1 << 27; // coprocessor
1305 if (long_transfer)
1306 opcode |= 1 << 22; // long transfer
1308 if (load)
1309 opcode |= 1 << 20; // L
1311 opcode |= cp_number << 8;
1313 //assert(CRd < 16);
1314 opcode |= ENCODE_RD(CRd);
1316 if (Rn->type != OP_REG32) {
1317 expect("register");
1318 return;
1320 //assert(Rn->reg < 16);
1321 opcode |= ENCODE_RN(Rn->reg);
1322 if (preincrement)
1323 opcode |= 1 << 24; // add offset before transfer
1325 if (writeback)
1326 opcode |= 1 << 21; // write offset back into register
1328 if (offset->type == OP_IM8 || offset->type == OP_IM8N || offset->type == OP_IM32) {
1329 int v = offset->e.v;
1330 if (offset_minus)
1331 tcc_error("minus before '#' not supported for immediate values");
1332 if (offset->type == OP_IM8N || v < 0)
1333 v = -v;
1334 else
1335 opcode |= 1 << 23; // up
1336 if (v & 3) {
1337 tcc_error("immediate offset must be a multiple of 4");
1338 return;
1340 v >>= 2;
1341 if (v > 255) {
1342 tcc_error("immediate offset must be between -1020 and 1020");
1343 return;
1345 opcode |= v;
1346 } else if (offset->type == OP_REG32) {
1347 if (!offset_minus)
1348 opcode |= 1 << 23; // up
1349 opcode |= ENCODE_IMMEDIATE_FLAG; /* if set, it means it's NOT immediate */
1350 opcode |= offset->reg;
1351 tcc_error("Using register offset to register address is not possible here");
1352 return;
1353 } else if (offset->type == OP_VREG64) {
1354 opcode |= 16;
1355 opcode |= offset->reg;
1356 } else
1357 expect("immediate or register");
1359 asm_emit_unconditional_opcode((high_nibble << 28) | opcode);
1362 // Almost exactly the same as asm_single_data_transfer_opcode.
1363 // Difference: Offsets are smaller and multiples of 4; no shifts, no STREX, ENCODE_IMMEDIATE_FLAG is inverted again.
1364 static void asm_coprocessor_data_transfer_opcode(TCCState *s1, int token)
1366 Operand ops[3];
1367 uint8_t coprocessor;
1368 uint8_t coprocessor_destination_register;
1369 int preincrement = 0;
1370 int exclam = 0;
1371 int closed_bracket = 0;
1372 int op2_minus = 0;
1373 int long_transfer = 0;
1374 // Note: ldc p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1375 // Note: ldc p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1376 // Note: ldc p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1378 if (tok >= TOK_ASM_p0 && tok <= TOK_ASM_p15) {
1379 coprocessor = tok - TOK_ASM_p0;
1380 next();
1381 } else {
1382 expect("'c<number>'");
1383 return;
1386 if (tok == ',')
1387 next();
1388 else
1389 expect("','");
1391 if (tok >= TOK_ASM_c0 && tok <= TOK_ASM_c15) {
1392 coprocessor_destination_register = tok - TOK_ASM_c0;
1393 next();
1394 } else {
1395 expect("'c<number>'");
1396 return;
1399 if (tok == ',')
1400 next();
1401 else
1402 expect("','");
1404 if (tok != '[')
1405 expect("'['");
1406 else
1407 next(); // skip '['
1409 parse_operand(s1, &ops[1]);
1410 if (ops[1].type != OP_REG32) {
1411 expect("(first source operand) register");
1412 return;
1414 if (tok == ']') {
1415 next();
1416 closed_bracket = 1;
1417 // exclam = 1; // implicit in hardware; don't do it in software
1419 if (tok == ',') {
1420 next(); // skip ','
1421 if (tok == '-') {
1422 op2_minus = 1;
1423 next();
1425 parse_operand(s1, &ops[2]);
1426 if (ops[2].type == OP_REG32) {
1427 if (ops[2].reg == 15) {
1428 tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token, NULL));
1429 return;
1431 } else if (ops[2].type == OP_VREG64) {
1432 tcc_error("'%s' does not support VFP register operand", get_tok_str(token, NULL));
1433 return;
1435 } else {
1436 // end of input expression in brackets--assume 0 offset
1437 ops[2].type = OP_IM8;
1438 ops[2].e.v = 0;
1439 preincrement = 1; // add offset before transfer
1441 if (!closed_bracket) {
1442 if (tok != ']')
1443 expect("']'");
1444 else
1445 next(); // skip ']'
1446 preincrement = 1; // add offset before transfer
1447 if (tok == '!') {
1448 exclam = 1;
1449 next(); // skip '!'
1453 // TODO: Support options.
1455 if (token == TOK_ASM_ldc2 || token == TOK_ASM_stc2 || token == TOK_ASM_ldc2l || token == TOK_ASM_stc2l) {
1456 switch (token) {
1457 case TOK_ASM_ldc2l:
1458 long_transfer = 1; // long transfer
1459 /* fallthrough */
1460 case TOK_ASM_ldc2:
1461 asm_emit_coprocessor_data_transfer(0xF, coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 1);
1462 break;
1463 case TOK_ASM_stc2l:
1464 long_transfer = 1; // long transfer
1465 /* fallthrough */
1466 case TOK_ASM_stc2:
1467 asm_emit_coprocessor_data_transfer(0xF, coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 0);
1468 break;
1470 } else switch (ARM_INSTRUCTION_GROUP(token)) {
1471 case TOK_ASM_stcleq:
1472 long_transfer = 1;
1473 /* fallthrough */
1474 case TOK_ASM_stceq:
1475 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 0);
1476 break;
1477 case TOK_ASM_ldcleq:
1478 long_transfer = 1;
1479 /* fallthrough */
1480 case TOK_ASM_ldceq:
1481 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 1);
1482 break;
1483 default:
1484 expect("coprocessor data transfer instruction");
1488 #if defined(TCC_ARM_VFP)
1489 #define CP_SINGLE_PRECISION_FLOAT 10
1490 #define CP_DOUBLE_PRECISION_FLOAT 11
1492 static void asm_floating_point_single_data_transfer_opcode(TCCState *s1, int token)
1494 Operand ops[3];
1495 uint8_t coprocessor = 0;
1496 uint8_t coprocessor_destination_register = 0;
1497 int long_transfer = 0;
1498 // Note: vldr p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1499 // Note: Not allowed: vldr p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1500 // Note: Not allowed: vldr p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1502 parse_operand(s1, &ops[0]);
1503 if (ops[0].type == OP_VREG32) {
1504 coprocessor = CP_SINGLE_PRECISION_FLOAT;
1505 coprocessor_destination_register = ops[0].reg;
1506 long_transfer = coprocessor_destination_register & 1;
1507 coprocessor_destination_register >>= 1;
1508 } else if (ops[0].type == OP_VREG64) {
1509 coprocessor = CP_DOUBLE_PRECISION_FLOAT;
1510 coprocessor_destination_register = ops[0].reg;
1511 next();
1512 } else {
1513 expect("floating point register");
1514 return;
1517 if (tok == ',')
1518 next();
1519 else
1520 expect("','");
1522 if (tok != '[')
1523 expect("'['");
1524 else
1525 next(); // skip '['
1527 parse_operand(s1, &ops[1]);
1528 if (ops[1].type != OP_REG32) {
1529 expect("(first source operand) register");
1530 return;
1532 if (tok == ',') {
1533 next(); // skip ','
1534 parse_operand(s1, &ops[2]);
1535 if (ops[2].type != OP_IM8 && ops[2].type != OP_IM8N) {
1536 expect("immediate offset");
1537 return;
1539 } else {
1540 // end of input expression in brackets--assume 0 offset
1541 ops[2].type = OP_IM8;
1542 ops[2].e.v = 0;
1544 if (tok != ']')
1545 expect("']'");
1546 else
1547 next(); // skip ']'
1549 switch (ARM_INSTRUCTION_GROUP(token)) {
1550 case TOK_ASM_vldreq:
1551 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], 0, 1, 0, long_transfer, 1);
1552 break;
1553 case TOK_ASM_vstreq:
1554 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], 0, 1, 0, long_transfer, 0);
1555 break;
1556 default:
1557 expect("floating point data transfer instruction");
1561 static void asm_floating_point_block_data_transfer_opcode(TCCState *s1, int token)
1563 uint8_t coprocessor = 0;
1564 int first_regset_register;
1565 int last_regset_register;
1566 uint8_t regset_item_count;
1567 uint8_t extra_register_bit = 0;
1568 int op0_exclam = 0;
1569 int load = 0;
1570 int preincrement = 0;
1571 Operand ops[1];
1572 Operand offset;
1573 switch (ARM_INSTRUCTION_GROUP(token)) {
1574 case TOK_ASM_vpusheq:
1575 case TOK_ASM_vpopeq:
1576 ops[0].type = OP_REG32;
1577 ops[0].reg = 13; // sp
1578 op0_exclam = 1;
1579 break;
1580 default:
1581 parse_operand(s1, &ops[0]);
1582 if (tok == '!') {
1583 op0_exclam = 1;
1584 next(); // skip '!'
1586 if (tok == ',')
1587 next(); // skip comma
1588 else {
1589 expect("','");
1590 return;
1594 if (tok != '{') {
1595 expect("'{'");
1596 return;
1598 next(); // skip '{'
1599 first_regset_register = asm_parse_vfp_regvar(tok, 1);
1600 if ((first_regset_register = asm_parse_vfp_regvar(tok, 1)) != -1) {
1601 coprocessor = CP_DOUBLE_PRECISION_FLOAT;
1602 next();
1603 } else if ((first_regset_register = asm_parse_vfp_regvar(tok, 0)) != -1) {
1604 coprocessor = CP_SINGLE_PRECISION_FLOAT;
1605 next();
1606 } else {
1607 expect("floating-point register");
1608 return;
1611 if (tok == '-') {
1612 next();
1613 if ((last_regset_register = asm_parse_vfp_regvar(tok, coprocessor == CP_DOUBLE_PRECISION_FLOAT)) != -1)
1614 next();
1615 else {
1616 expect("floating-point register");
1617 return;
1619 } else
1620 last_regset_register = first_regset_register;
1622 if (last_regset_register < first_regset_register) {
1623 tcc_error("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
1624 return;
1626 if (tok != '}') {
1627 expect("'}'");
1628 return;
1630 next(); // skip '}'
1632 // Note: 0 (one down) is not implemented by us regardless.
1633 regset_item_count = last_regset_register - first_regset_register + 1;
1634 if (coprocessor == CP_DOUBLE_PRECISION_FLOAT)
1635 regset_item_count <<= 1;
1636 else {
1637 extra_register_bit = first_regset_register & 1;
1638 first_regset_register >>= 1;
1640 offset.type = OP_IM8;
1641 offset.e.v = regset_item_count << 2;
1642 switch (ARM_INSTRUCTION_GROUP(token)) {
1643 case TOK_ASM_vstmeq: // post-increment store
1644 case TOK_ASM_vstmiaeq: // post-increment store
1645 break;
1646 case TOK_ASM_vpopeq:
1647 case TOK_ASM_vldmeq: // post-increment load
1648 case TOK_ASM_vldmiaeq: // post-increment load
1649 load = 1;
1650 break;
1651 case TOK_ASM_vldmdbeq: // pre-decrement load
1652 load = 1;
1653 /* fallthrough */
1654 case TOK_ASM_vpusheq:
1655 case TOK_ASM_vstmdbeq: // pre-decrement store
1656 offset.type = OP_IM8N;
1657 offset.e.v = -offset.e.v;
1658 preincrement = 1;
1659 break;
1660 default:
1661 expect("floating point block data transfer instruction");
1662 return;
1664 if (ops[0].type != OP_REG32)
1665 expect("(first operand) register");
1666 else if (ops[0].reg == 15)
1667 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL));
1668 else if (!op0_exclam && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vldmeq && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vldmiaeq && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vstmeq && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vstmiaeq)
1669 tcc_error("first operand of '%s' should have an exclamation mark", get_tok_str(token, NULL));
1670 else
1671 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, first_regset_register, &ops[0], &offset, 0, preincrement, op0_exclam, extra_register_bit, load);
1674 #define VMOV_FRACTIONAL_DIGITS 7
1675 #define VMOV_ONE 10000000 /* pow(10, VMOV_FRACTIONAL_DIGITS) */
1677 static uint32_t vmov_parse_fractional_part(const char* s)
1679 uint32_t result = 0;
1680 int i;
1681 for (i = 0; i < VMOV_FRACTIONAL_DIGITS; ++i) {
1682 char c = *s;
1683 result *= 10;
1684 if (c >= '0' && c <= '9') {
1685 result += (c - '0');
1686 ++s;
1689 if (*s)
1690 expect("decimal numeral");
1691 return result;
1694 static int vmov_linear_approx_index(uint32_t beginning, uint32_t end, uint32_t value)
1696 int i;
1697 uint32_t k;
1698 uint32_t xvalue;
1700 k = (end - beginning)/16;
1701 for (xvalue = beginning, i = 0; i < 16; ++i, xvalue += k) {
1702 if (value == xvalue)
1703 return i;
1705 //assert(0);
1706 return -1;
1709 static uint32_t vmov_parse_immediate_value() {
1710 uint32_t value;
1711 unsigned long integral_value;
1712 const char *p;
1714 if (tok != TOK_PPNUM) {
1715 expect("immediate value");
1716 return 0;
1718 p = tokc.str.data;
1719 errno = 0;
1720 integral_value = strtoul(p, (char **)&p, 0);
1722 if (errno || integral_value >= 32) {
1723 tcc_error("invalid floating-point immediate value");
1724 return 0;
1727 value = (uint32_t) integral_value * VMOV_ONE;
1728 if (*p == '.') {
1729 ++p;
1730 value += vmov_parse_fractional_part(p);
1732 next();
1733 return value;
1736 static uint8_t vmov_encode_immediate_value(uint32_t value)
1738 uint32_t limit;
1739 uint32_t end = 0;
1740 uint32_t beginning = 0;
1741 int r = -1;
1742 int n;
1743 int i;
1745 limit = 32 * VMOV_ONE;
1746 for (i = 0; i < 8; ++i) {
1747 if (value < limit) {
1748 end = limit;
1749 limit >>= 1;
1750 beginning = limit;
1751 r = i;
1752 } else
1753 limit >>= 1;
1755 if (r == -1 || value < beginning || value > end) {
1756 tcc_error("invalid decimal number for vmov: %d", value);
1757 return 0;
1759 n = vmov_linear_approx_index(beginning, end, value);
1760 return n | (((3 - r) & 0x7) << 4);
1763 // Not standalone.
1764 static void asm_floating_point_immediate_data_processing_opcode_tail(TCCState *s1, int token, uint8_t coprocessor, uint8_t CRd) {
1765 uint8_t opcode1 = 0;
1766 uint8_t opcode2 = 0;
1767 uint8_t operands[3] = {0, 0, 0};
1768 uint32_t immediate_value = 0;
1769 int op_minus = 0;
1770 uint8_t code;
1772 operands[0] = CRd;
1774 if (tok == '#' || tok == '$') {
1775 next();
1777 if (tok == '-') {
1778 op_minus = 1;
1779 next();
1781 immediate_value = vmov_parse_immediate_value();
1783 opcode1 = 11; // "Other" instruction
1784 switch (ARM_INSTRUCTION_GROUP(token)) {
1785 case TOK_ASM_vcmpeq_f32:
1786 case TOK_ASM_vcmpeq_f64:
1787 opcode2 = 2;
1788 operands[1] = 5;
1789 if (immediate_value) {
1790 expect("Immediate value 0");
1791 return;
1793 break;
1794 case TOK_ASM_vcmpeeq_f32:
1795 case TOK_ASM_vcmpeeq_f64:
1796 opcode2 = 6;
1797 operands[1] = 5;
1798 if (immediate_value) {
1799 expect("Immediate value 0");
1800 return;
1802 break;
1803 case TOK_ASM_vmoveq_f32:
1804 case TOK_ASM_vmoveq_f64:
1805 opcode2 = 0;
1806 if (op_minus)
1807 operands[1] = 0x8;
1808 else
1809 operands[1] = 0x0;
1810 code = vmov_encode_immediate_value(immediate_value);
1811 operands[1] |= code >> 4;
1812 operands[2] = code & 0xF;
1813 break;
1814 default:
1815 expect("known floating point with immediate instruction");
1816 return;
1819 if (coprocessor == CP_SINGLE_PRECISION_FLOAT) {
1820 if (operands[0] & 1)
1821 opcode1 |= 4;
1822 operands[0] >>= 1;
1825 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, operands[0], operands[1], operands[2], opcode2, 0);
1828 static void asm_floating_point_reg_arm_reg_transfer_opcode_tail(TCCState *s1, int token, int coprocessor, int nb_arm_regs, int nb_ops, Operand ops[3]) {
1829 uint8_t opcode1 = 0;
1830 uint8_t opcode2 = 0;
1831 switch (coprocessor) {
1832 case CP_SINGLE_PRECISION_FLOAT:
1833 // "vmov.f32 r2, s3" or "vmov.f32 s3, r2"
1834 if (nb_ops != 2 || nb_arm_regs != 1) {
1835 tcc_error("vmov.f32 only implemented for one VFP register operand and one ARM register operands");
1836 return;
1838 if (ops[0].type != OP_REG32) { // determine mode: load or store
1839 // need to swap operands 0 and 1
1840 memcpy(&ops[2], &ops[1], sizeof(ops[2]));
1841 memcpy(&ops[1], &ops[0], sizeof(ops[1]));
1842 memcpy(&ops[0], &ops[2], sizeof(ops[0]));
1843 } else
1844 opcode1 |= 1;
1846 if (ops[1].type == OP_VREG32) {
1847 if (ops[1].reg & 1)
1848 opcode2 |= 4;
1849 ops[1].reg >>= 1;
1852 if (ops[0].type == OP_VREG32) {
1853 if (ops[0].reg & 1)
1854 opcode1 |= 4;
1855 ops[0].reg >>= 1;
1858 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, ops[0].reg, (ops[1].type == OP_IM8) ? ops[1].e.v : ops[1].reg, 0x10, opcode2, 0);
1859 break;
1860 case CP_DOUBLE_PRECISION_FLOAT:
1861 if (nb_ops != 3 || nb_arm_regs != 2) {
1862 tcc_error("vmov.f32 only implemented for one VFP register operand and two ARM register operands");
1863 return;
1865 // Determine whether it's a store into a VFP register (vmov "d1, r2, r3") rather than "vmov r2, r3, d1"
1866 if (ops[0].type == OP_VREG64) {
1867 if (ops[2].type == OP_REG32) {
1868 Operand temp;
1869 // need to rotate operand list to the left
1870 memcpy(&temp, &ops[0], sizeof(temp));
1871 memcpy(&ops[0], &ops[1], sizeof(ops[0]));
1872 memcpy(&ops[1], &ops[2], sizeof(ops[1]));
1873 memcpy(&ops[2], &temp, sizeof(ops[2]));
1874 } else {
1875 tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands");
1876 return;
1878 } else if (ops[0].type != OP_REG32 || ops[1].type != OP_REG32 || ops[2].type != OP_VREG64) {
1879 tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands");
1880 return;
1881 } else {
1882 opcode1 |= 1;
1884 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, ops[0].reg, &ops[1], &ops[2], 0, 0, 0, 1, opcode1);
1885 break;
1886 default:
1887 tcc_internal_error("unknown coprocessor");
1891 static void asm_floating_point_vcvt_data_processing_opcode(TCCState *s1, int token) {
1892 uint8_t coprocessor = 0;
1893 Operand ops[3];
1894 uint8_t opcode1 = 11;
1895 uint8_t opcode2 = 2;
1897 switch (ARM_INSTRUCTION_GROUP(token)) {
1898 case TOK_ASM_vcvtreq_s32_f64:
1899 case TOK_ASM_vcvtreq_u32_f64:
1900 case TOK_ASM_vcvteq_s32_f64:
1901 case TOK_ASM_vcvteq_u32_f64:
1902 case TOK_ASM_vcvteq_f64_s32:
1903 case TOK_ASM_vcvteq_f64_u32:
1904 case TOK_ASM_vcvteq_f32_f64:
1905 coprocessor = CP_DOUBLE_PRECISION_FLOAT;
1906 break;
1907 case TOK_ASM_vcvtreq_s32_f32:
1908 case TOK_ASM_vcvtreq_u32_f32:
1909 case TOK_ASM_vcvteq_s32_f32:
1910 case TOK_ASM_vcvteq_u32_f32:
1911 case TOK_ASM_vcvteq_f32_s32:
1912 case TOK_ASM_vcvteq_f32_u32:
1913 case TOK_ASM_vcvteq_f64_f32:
1914 coprocessor = CP_SINGLE_PRECISION_FLOAT;
1915 break;
1916 default:
1917 tcc_error("Unknown coprocessor for instruction '%s'", get_tok_str(token, NULL));
1918 return;
1921 parse_operand(s1, &ops[0]);
1922 ops[1].type = OP_IM8;
1923 ops[1].e.v = 8;
1924 /* floating-point -> integer */
1925 switch (ARM_INSTRUCTION_GROUP(token)) {
1926 case TOK_ASM_vcvtreq_s32_f32:
1927 case TOK_ASM_vcvtreq_s32_f64:
1928 case TOK_ASM_vcvteq_s32_f32:
1929 case TOK_ASM_vcvteq_s32_f64:
1930 ops[1].e.v |= 1; // signed
1931 /* fall through */
1932 case TOK_ASM_vcvteq_u32_f32:
1933 case TOK_ASM_vcvteq_u32_f64:
1934 case TOK_ASM_vcvtreq_u32_f32:
1935 case TOK_ASM_vcvtreq_u32_f64:
1936 ops[1].e.v |= 4; // to_integer (opc2)
1937 break;
1938 /* floating-point size conversion */
1939 case TOK_ASM_vcvteq_f64_f32:
1940 case TOK_ASM_vcvteq_f32_f64:
1941 ops[1].e.v = 7;
1942 break;
1945 if (tok == ',')
1946 next();
1947 else
1948 expect("','");
1949 parse_operand(s1, &ops[2]);
1951 switch (ARM_INSTRUCTION_GROUP(token)) {
1952 /* floating-point -> integer */
1953 case TOK_ASM_vcvteq_s32_f32:
1954 case TOK_ASM_vcvteq_s32_f64:
1955 case TOK_ASM_vcvteq_u32_f32:
1956 case TOK_ASM_vcvteq_u32_f64:
1957 opcode2 |= 4; // round_zero
1958 break;
1960 /* integer -> floating-point */
1961 case TOK_ASM_vcvteq_f64_s32:
1962 case TOK_ASM_vcvteq_f32_s32:
1963 opcode2 |= 4; // signed--special
1964 break;
1966 /* floating-point size conversion */
1967 case TOK_ASM_vcvteq_f64_f32:
1968 case TOK_ASM_vcvteq_f32_f64:
1969 opcode2 |= 4; // always set
1970 break;
1973 switch (ARM_INSTRUCTION_GROUP(token)) {
1974 case TOK_ASM_vcvteq_f64_u32:
1975 case TOK_ASM_vcvteq_f64_s32:
1976 case TOK_ASM_vcvteq_f64_f32:
1977 if (ops[0].type == OP_VREG64 && ops[2].type == OP_VREG32) {
1978 } else {
1979 expect("d<number>, s<number>");
1980 return;
1982 break;
1983 default:
1984 if (coprocessor == CP_SINGLE_PRECISION_FLOAT) {
1985 if (ops[0].type == OP_VREG32 && ops[2].type == OP_VREG32) {
1986 } else {
1987 expect("s<number>, s<number>");
1988 return;
1990 } else if (coprocessor == CP_DOUBLE_PRECISION_FLOAT) {
1991 if (ops[0].type == OP_VREG32 && ops[2].type == OP_VREG64) {
1992 } else {
1993 expect("s<number>, d<number>");
1994 return;
1999 if (ops[2].type == OP_VREG32) {
2000 if (ops[2].reg & 1)
2001 opcode2 |= 1;
2002 ops[2].reg >>= 1;
2004 if (ops[0].type == OP_VREG32) {
2005 if (ops[0].reg & 1)
2006 opcode1 |= 4;
2007 ops[0].reg >>= 1;
2009 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, ops[0].reg, (ops[1].type == OP_IM8) ? ops[1].e.v : ops[1].reg, (ops[2].type == OP_IM8) ? ops[2].e.v : ops[2].reg, opcode2, 0);
2012 static void asm_floating_point_data_processing_opcode(TCCState *s1, int token) {
2013 uint8_t coprocessor = CP_SINGLE_PRECISION_FLOAT;
2014 uint8_t opcode1 = 0;
2015 uint8_t opcode2 = 0; // (0 || 2) | register selection
2016 Operand ops[3];
2017 uint8_t nb_ops = 0;
2018 int vmov = 0;
2019 int nb_arm_regs = 0;
2021 /* TODO:
2022 Instruction opcode opcode2 Reason
2023 =============================================================
2024 - 1?00 ?1? Undefined
2025 VFNMS 1?01 ?0? Must be unconditional
2026 VFNMA 1?01 ?1? Must be unconditional
2027 VFMA 1?10 ?0? Must be unconditional
2028 VFMS 1?10 ?1? Must be unconditional
2030 VMOV Fd, Fm
2031 VMOV Sn, Sm, Rd, Rn
2032 VMOV Rd, Rn, Sn, Sm
2033 VMOV Dn[0], Rd
2034 VMOV Rd, Dn[0]
2035 VMOV Dn[1], Rd
2036 VMOV Rd, Dn[1]
2039 switch (ARM_INSTRUCTION_GROUP(token)) {
2040 case TOK_ASM_vmlaeq_f64:
2041 case TOK_ASM_vmlseq_f64:
2042 case TOK_ASM_vnmlseq_f64:
2043 case TOK_ASM_vnmlaeq_f64:
2044 case TOK_ASM_vmuleq_f64:
2045 case TOK_ASM_vnmuleq_f64:
2046 case TOK_ASM_vaddeq_f64:
2047 case TOK_ASM_vsubeq_f64:
2048 case TOK_ASM_vdiveq_f64:
2049 case TOK_ASM_vnegeq_f64:
2050 case TOK_ASM_vabseq_f64:
2051 case TOK_ASM_vsqrteq_f64:
2052 case TOK_ASM_vcmpeq_f64:
2053 case TOK_ASM_vcmpeeq_f64:
2054 case TOK_ASM_vmoveq_f64:
2055 coprocessor = CP_DOUBLE_PRECISION_FLOAT;
2058 switch (ARM_INSTRUCTION_GROUP(token)) {
2059 case TOK_ASM_vmoveq_f32:
2060 case TOK_ASM_vmoveq_f64:
2061 vmov = 1;
2062 break;
2065 for (nb_ops = 0; nb_ops < 3; ) {
2066 // Note: Necessary because parse_operand can't parse decimal numerals.
2067 if (nb_ops == 1 && (tok == '#' || tok == '$' || tok == TOK_PPNUM || tok == '-')) {
2068 asm_floating_point_immediate_data_processing_opcode_tail(s1, token, coprocessor, ops[0].reg);
2069 return;
2071 parse_operand(s1, &ops[nb_ops]);
2072 if (vmov && ops[nb_ops].type == OP_REG32) {
2073 ++nb_arm_regs;
2074 } else if (ops[nb_ops].type == OP_VREG32) {
2075 if (coprocessor != CP_SINGLE_PRECISION_FLOAT) {
2076 expect("'s<number>'");
2077 return;
2079 } else if (ops[nb_ops].type == OP_VREG64) {
2080 if (coprocessor != CP_DOUBLE_PRECISION_FLOAT) {
2081 expect("'d<number>'");
2082 return;
2084 } else {
2085 expect("floating point register");
2086 return;
2088 ++nb_ops;
2089 if (tok == ',')
2090 next();
2091 else
2092 break;
2095 if (nb_arm_regs == 0) {
2096 if (nb_ops == 2) { // implicit
2097 memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2]
2098 memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit
2099 nb_ops = 3;
2101 if (nb_ops < 3) {
2102 tcc_error("Not enough operands for '%s' (%u)", get_tok_str(token, NULL), nb_ops);
2103 return;
2107 switch (ARM_INSTRUCTION_GROUP(token)) {
2108 case TOK_ASM_vmlaeq_f32:
2109 case TOK_ASM_vmlaeq_f64:
2110 opcode1 = 0;
2111 opcode2 = 0;
2112 break;
2113 case TOK_ASM_vmlseq_f32:
2114 case TOK_ASM_vmlseq_f64:
2115 opcode1 = 0;
2116 opcode2 = 2;
2117 break;
2118 case TOK_ASM_vnmlseq_f32:
2119 case TOK_ASM_vnmlseq_f64:
2120 opcode1 = 1;
2121 opcode2 = 0;
2122 break;
2123 case TOK_ASM_vnmlaeq_f32:
2124 case TOK_ASM_vnmlaeq_f64:
2125 opcode1 = 1;
2126 opcode2 = 2;
2127 break;
2128 case TOK_ASM_vmuleq_f32:
2129 case TOK_ASM_vmuleq_f64:
2130 opcode1 = 2;
2131 opcode2 = 0;
2132 break;
2133 case TOK_ASM_vnmuleq_f32:
2134 case TOK_ASM_vnmuleq_f64:
2135 opcode1 = 2;
2136 opcode2 = 2;
2137 break;
2138 case TOK_ASM_vaddeq_f32:
2139 case TOK_ASM_vaddeq_f64:
2140 opcode1 = 3;
2141 opcode2 = 0;
2142 break;
2143 case TOK_ASM_vsubeq_f32:
2144 case TOK_ASM_vsubeq_f64:
2145 opcode1 = 3;
2146 opcode2 = 2;
2147 break;
2148 case TOK_ASM_vdiveq_f32:
2149 case TOK_ASM_vdiveq_f64:
2150 opcode1 = 8;
2151 opcode2 = 0;
2152 break;
2153 case TOK_ASM_vnegeq_f32:
2154 case TOK_ASM_vnegeq_f64:
2155 opcode1 = 11; // Other" instruction
2156 opcode2 = 2;
2157 ops[1].type = OP_IM8;
2158 ops[1].e.v = 1;
2159 break;
2160 case TOK_ASM_vabseq_f32:
2161 case TOK_ASM_vabseq_f64:
2162 opcode1 = 11; // "Other" instruction
2163 opcode2 = 6;
2164 ops[1].type = OP_IM8;
2165 ops[1].e.v = 0;
2166 break;
2167 case TOK_ASM_vsqrteq_f32:
2168 case TOK_ASM_vsqrteq_f64:
2169 opcode1 = 11; // "Other" instruction
2170 opcode2 = 6;
2171 ops[1].type = OP_IM8;
2172 ops[1].e.v = 1;
2173 break;
2174 case TOK_ASM_vcmpeq_f32:
2175 case TOK_ASM_vcmpeq_f64:
2176 opcode1 = 11; // "Other" instruction
2177 opcode2 = 2;
2178 ops[1].type = OP_IM8;
2179 ops[1].e.v = 4;
2180 break;
2181 case TOK_ASM_vcmpeeq_f32:
2182 case TOK_ASM_vcmpeeq_f64:
2183 opcode1 = 11; // "Other" instruction
2184 opcode2 = 6;
2185 ops[1].type = OP_IM8;
2186 ops[1].e.v = 4;
2187 break;
2188 case TOK_ASM_vmoveq_f32:
2189 case TOK_ASM_vmoveq_f64:
2190 if (nb_arm_regs > 0) { // vmov.f32 r2, s3 or similar
2191 asm_floating_point_reg_arm_reg_transfer_opcode_tail(s1, token, coprocessor, nb_arm_regs, nb_ops, ops);
2192 return;
2193 } else {
2194 opcode1 = 11; // "Other" instruction
2195 opcode2 = 2;
2196 ops[1].type = OP_IM8;
2197 ops[1].e.v = 0;
2199 break;
2200 default:
2201 expect("known floating point instruction");
2202 return;
2205 if (coprocessor == CP_SINGLE_PRECISION_FLOAT) {
2206 if (ops[2].type == OP_VREG32) {
2207 if (ops[2].reg & 1)
2208 opcode2 |= 1;
2209 ops[2].reg >>= 1;
2212 if (ops[1].type == OP_VREG32) {
2213 if (ops[1].reg & 1)
2214 opcode2 |= 4;
2215 ops[1].reg >>= 1;
2218 if (ops[0].type == OP_VREG32) {
2219 if (ops[0].reg & 1)
2220 opcode1 |= 4;
2221 ops[0].reg >>= 1;
2225 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, ops[0].reg, (ops[1].type == OP_IM8) ? ops[1].e.v : ops[1].reg, (ops[2].type == OP_IM8) ? ops[2].e.v : ops[2].reg, opcode2, 0);
2228 static int asm_parse_vfp_status_regvar(int t)
2230 switch (t) {
2231 case TOK_ASM_fpsid:
2232 return 0;
2233 case TOK_ASM_fpscr:
2234 return 1;
2235 case TOK_ASM_fpexc:
2236 return 8;
2237 default:
2238 return -1;
2242 static void asm_floating_point_status_register_opcode(TCCState* s1, int token)
2244 uint8_t coprocessor = CP_SINGLE_PRECISION_FLOAT;
2245 uint8_t opcode;
2246 int vfp_sys_reg = -1;
2247 Operand arm_operand;
2248 switch (ARM_INSTRUCTION_GROUP(token)) {
2249 case TOK_ASM_vmrseq:
2250 opcode = 0xf;
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
2255 } else {
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));
2259 return;
2263 if (tok != ',')
2264 expect("','");
2265 else
2266 next(); // skip ','
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));
2271 return;
2273 break;
2274 case TOK_ASM_vmsreq:
2275 opcode = 0xe;
2276 vfp_sys_reg = asm_parse_vfp_status_regvar(tok);
2277 next(); // skip vfp sys reg
2278 if (tok != ',')
2279 expect("','");
2280 else
2281 next(); // skip ','
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));
2285 return;
2287 break;
2288 default:
2289 expect("floating point status register instruction");
2290 return;
2292 if (vfp_sys_reg == -1) {
2293 expect("VFP system register");
2294 return;
2296 if (arm_operand.type != OP_REG32) {
2297 expect("ARM register");
2298 return;
2300 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode, arm_operand.reg, vfp_sys_reg, 0x10, 0, 0);
2303 #endif
2305 static void asm_misc_single_data_transfer_opcode(TCCState *s1, int token)
2307 Operand ops[3];
2308 int exclam = 0;
2309 int closed_bracket = 0;
2310 int op2_minus = 0;
2311 uint32_t opcode = (1 << 7) | (1 << 4);
2313 /* Note:
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);
2328 else {
2329 expect("(destination operand) register");
2330 return;
2332 if (tok != ',')
2333 expect("at least two arguments");
2334 else
2335 next(); // skip ','
2337 if (tok != '[')
2338 expect("'['");
2339 else
2340 next(); // skip '['
2342 parse_operand(s1, &ops[1]);
2343 if (ops[1].type == OP_REG32)
2344 opcode |= ENCODE_RN(ops[1].reg);
2345 else {
2346 expect("(first source operand) register");
2347 return;
2349 if (tok == ']') {
2350 next();
2351 closed_bracket = 1;
2352 // exclam = 1; // implicit in hardware; don't do it in software
2354 if (tok == ',') {
2355 next(); // skip ','
2356 if (tok == '-') {
2357 op2_minus = 1;
2358 next();
2360 parse_operand(s1, &ops[2]);
2361 } else {
2362 // end of input expression in brackets--assume 0 offset
2363 ops[2].type = OP_IM8;
2364 ops[2].e.v = 0;
2365 opcode |= 1 << 24; // add offset before transfer
2367 if (!closed_bracket) {
2368 if (tok != ']')
2369 expect("']'");
2370 else
2371 next(); // skip ']'
2372 opcode |= 1 << 24; // add offset before transfer
2373 if (tok == '!') {
2374 exclam = 1;
2375 next(); // skip '!'
2379 if (exclam) {
2380 if ((opcode & (1 << 24)) == 0) {
2381 tcc_error("result of '%s' would be unpredictable here", get_tok_str(token, NULL));
2382 return;
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) {
2388 int v = ops[2].e.v;
2389 if (op2_minus)
2390 tcc_error("minus before '#' not supported for immediate values");
2391 if (v >= 0) {
2392 opcode |= 1 << 23; // up
2393 if (v >= 0x100)
2394 tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
2395 else {
2396 // bits 11...8: immediate hi nibble
2397 // bits 3...0: immediate lo nibble
2398 opcode |= (v & 0xF0) << 4;
2399 opcode |= v & 0xF;
2401 } else { // down
2402 if (v <= -0x100)
2403 tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
2404 else {
2405 v = -v;
2406 // bits 11...8: immediate hi nibble
2407 // bits 3...0: immediate lo nibble
2408 opcode |= (v & 0xF0) << 4;
2409 opcode |= v & 0xF;
2412 opcode |= 1 << 22; // not ENCODE_IMMEDIATE_FLAG;
2413 } else if (ops[2].type == OP_REG32) {
2414 if (!op2_minus)
2415 opcode |= 1 << 23; // up
2416 opcode |= ops[2].reg;
2417 } else
2418 expect("register");
2420 switch (ARM_INSTRUCTION_GROUP(token)) {
2421 case TOK_ASM_ldrsheq:
2422 opcode |= 1 << 5; // halfword, not byte
2423 /* fallthrough */
2424 case TOK_ASM_ldrsbeq:
2425 opcode |= 1 << 6; // sign extend
2426 opcode |= 1 << 20; // L
2427 asm_emit_opcode(token, opcode);
2428 break;
2429 case TOK_ASM_ldrheq:
2430 opcode |= 1 << 5; // halfword, not byte
2431 opcode |= 1 << 20; // L
2432 asm_emit_opcode(token, opcode);
2433 break;
2434 case TOK_ASM_strheq:
2435 opcode |= 1 << 5; // halfword, not byte
2436 asm_emit_opcode(token, opcode);
2437 break;
2441 /* Note: almost dupe of encbranch in arm-gen.c */
2442 static uint32_t encbranchoffset(int pos, int addr, int fail)
2444 addr-=pos+8;
2445 addr/=4;
2446 if(addr>=0x7fffff || addr<-0x800000) {
2447 if(fail)
2448 tcc_error("branch offset is too far");
2449 return 0;
2451 return /*not 0x0A000000|*/(addr&0xffffff);
2454 static void asm_branch_opcode(TCCState *s1, int token)
2456 int jmp_disp = 0;
2457 Operand op;
2458 ExprValue e;
2459 ElfSym *esym;
2461 switch (ARM_INSTRUCTION_GROUP(token)) {
2462 case TOK_ASM_beq:
2463 case TOK_ASM_bleq:
2464 asm_expr(s1, &e);
2465 esym = elfsym(e.sym);
2466 if (!esym || esym->st_shndx != cur_text_section->sh_num) {
2467 tcc_error("invalid branch target");
2468 return;
2470 jmp_disp = encbranchoffset(ind, e.v + esym->st_value, 1);
2471 break;
2472 default:
2473 parse_operand(s1, &op);
2474 break;
2476 switch (ARM_INSTRUCTION_GROUP(token)) {
2477 case TOK_ASM_beq:
2478 asm_emit_opcode(token, (0xa << 24) | (jmp_disp & 0xffffff));
2479 break;
2480 case TOK_ASM_bleq:
2481 asm_emit_opcode(token, (0xb << 24) | (jmp_disp & 0xffffff));
2482 break;
2483 case TOK_ASM_bxeq:
2484 if (op.type != OP_REG32)
2485 expect("register");
2486 else
2487 asm_emit_opcode(token, (0x12fff1 << 4) | op.reg);
2488 break;
2489 case TOK_ASM_blxeq:
2490 if (op.type != OP_REG32)
2491 expect("register");
2492 else
2493 asm_emit_opcode(token, (0x12fff3 << 4) | op.reg);
2494 break;
2495 default:
2496 expect("branch instruction");
2500 ST_FUNC void asm_opcode(TCCState *s1, int token)
2502 while (token == TOK_LINEFEED) {
2503 next();
2504 token = tok;
2506 if (token == TOK_EOF)
2507 return;
2508 if (token < TOK_ASM_nopeq) { // no condition code
2509 switch (token) {
2510 case TOK_ASM_cdp2:
2511 asm_coprocessor_opcode(s1, token);
2512 return;
2513 case TOK_ASM_ldc2:
2514 case TOK_ASM_ldc2l:
2515 case TOK_ASM_stc2:
2516 case TOK_ASM_stc2l:
2517 asm_coprocessor_data_transfer_opcode(s1, token);
2518 return;
2519 default:
2520 expect("instruction");
2521 return;
2525 switch (ARM_INSTRUCTION_GROUP(token)) {
2526 case TOK_ASM_pusheq:
2527 case TOK_ASM_popeq:
2528 case TOK_ASM_stmdaeq:
2529 case TOK_ASM_ldmdaeq:
2530 case TOK_ASM_stmeq:
2531 case TOK_ASM_ldmeq:
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);
2539 return;
2540 case TOK_ASM_nopeq:
2541 case TOK_ASM_wfeeq:
2542 case TOK_ASM_wfieq:
2543 asm_nullary_opcode(token);
2544 return;
2545 case TOK_ASM_swieq:
2546 case TOK_ASM_svceq:
2547 asm_unary_opcode(s1, token);
2548 return;
2549 case TOK_ASM_beq:
2550 case TOK_ASM_bleq:
2551 case TOK_ASM_bxeq:
2552 case TOK_ASM_blxeq:
2553 asm_branch_opcode(s1, token);
2554 return;
2555 case TOK_ASM_clzeq:
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);
2563 return;
2565 case TOK_ASM_ldreq:
2566 case TOK_ASM_ldrbeq:
2567 case TOK_ASM_streq:
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);
2574 return;
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);
2581 return;
2583 case TOK_ASM_andeq:
2584 case TOK_ASM_eoreq:
2585 case TOK_ASM_subeq:
2586 case TOK_ASM_rsbeq:
2587 case TOK_ASM_addeq:
2588 case TOK_ASM_adceq:
2589 case TOK_ASM_sbceq:
2590 case TOK_ASM_rsceq:
2591 case TOK_ASM_tsteq:
2592 case TOK_ASM_teqeq:
2593 case TOK_ASM_cmpeq:
2594 case TOK_ASM_cmneq:
2595 case TOK_ASM_orreq:
2596 case TOK_ASM_moveq:
2597 case TOK_ASM_biceq:
2598 case TOK_ASM_mvneq:
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);
2616 return;
2618 case TOK_ASM_lsleq:
2619 case TOK_ASM_lslseq:
2620 case TOK_ASM_lsreq:
2621 case TOK_ASM_lsrseq:
2622 case TOK_ASM_asreq:
2623 case TOK_ASM_asrseq:
2624 case TOK_ASM_roreq:
2625 case TOK_ASM_rorseq:
2626 case TOK_ASM_rrxseq:
2627 case TOK_ASM_rrxeq:
2628 asm_shift_opcode(s1, token);
2629 return;
2631 case TOK_ASM_muleq:
2632 case TOK_ASM_mulseq:
2633 case TOK_ASM_mlaeq:
2634 case TOK_ASM_mlaseq:
2635 asm_multiplication_opcode(s1, token);
2636 return;
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);
2647 return;
2649 case TOK_ASM_cdpeq:
2650 case TOK_ASM_mcreq:
2651 case TOK_ASM_mrceq:
2652 asm_coprocessor_opcode(s1, token);
2653 return;
2655 case TOK_ASM_ldceq:
2656 case TOK_ASM_ldcleq:
2657 case TOK_ASM_stceq:
2658 case TOK_ASM_stcleq:
2659 asm_coprocessor_data_transfer_opcode(s1, token);
2660 return;
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);
2666 return;
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);
2699 return;
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);
2716 return;
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);
2727 return;
2729 case TOK_ASM_vmsreq:
2730 case TOK_ASM_vmrseq:
2731 asm_floating_point_status_register_opcode(s1, token);
2732 return;
2733 #endif
2735 default:
2736 expect("known instruction");
2740 ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier)
2742 int r, reg, size, val;
2743 char buf[64];
2745 r = sv->r;
2746 if ((r & VT_VALMASK) == VT_CONST) {
2747 if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n' &&
2748 modifier != 'P')
2749 cstr_ccat(add_str, '#');
2750 if (r & VT_SYM) {
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)
2764 goto no_offset;
2765 cstr_ccat(add_str, '+');
2767 val = sv->c.i;
2768 if (modifier == 'n')
2769 val = -val;
2770 snprintf(buf, sizeof(buf), "%d", (int) sv->c.i);
2771 cstr_cat(add_str, buf, -1);
2772 no_offset:;
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);
2783 } else {
2784 /* register case */
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)
2792 size = 1;
2793 else if ((sv->type.t & VT_BTYPE) == VT_SHORT)
2794 size = 2;
2795 else
2796 size = 4;
2798 if (modifier == 'b') {
2799 size = 1;
2800 } else if (modifier == 'w') {
2801 size = 2;
2802 } else if (modifier == 'k') {
2803 size = 4;
2806 switch (size) {
2807 default:
2808 reg = TOK_ASM_r0 + reg;
2809 break;
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,
2820 int out_reg)
2822 uint8_t regs_allocated[NB_ASM_REGS];
2823 ASMOperand *op;
2824 int i, reg;
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++) {
2834 op = &operands[i];
2835 if (op->reg >= 0)
2836 regs_allocated[op->reg] = 1;
2838 for(i = 0; i < sizeof(reg_saved)/sizeof(reg_saved[0]); i++) {
2839 reg = reg_saved[i];
2840 if (regs_allocated[reg])
2841 saved_regset |= 1 << reg;
2844 if (!is_output) { // prolog
2845 /* generate reg save code */
2846 if (saved_regset)
2847 gen_le32(0xe92d0000 | saved_regset); // push {...}
2849 /* generate load code */
2850 for(i = 0; i < nb_operands; i++) {
2851 op = &operands[i];
2852 if (op->reg >= 0) {
2853 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL &&
2854 op->is_memory) {
2855 /* memory reference case (for both input and
2856 output cases) */
2857 SValue sv;
2858 sv = *op->vt;
2859 sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL | VT_LVAL;
2860 sv.type.t = VT_PTR;
2861 load(op->reg, &sv);
2862 } else if (i >= nb_outputs || op->is_rw) { // not write-only
2863 /* load value in register */
2864 load(op->reg, op->vt);
2865 if (op->is_llong)
2866 tcc_error("long long not implemented");
2870 } else { // epilog
2871 /* generate save code */
2872 for(i = 0 ; i < nb_outputs; i++) {
2873 op = &operands[i];
2874 if (op->reg >= 0) {
2875 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
2876 if (!op->is_memory) {
2877 SValue sv;
2878 sv = *op->vt;
2879 sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
2880 sv.type.t = VT_PTR;
2881 load(out_reg, &sv);
2883 sv = *op->vt;
2884 sv.r = (sv.r & ~VT_VALMASK) | out_reg;
2885 store(op->reg, &sv);
2887 } else {
2888 store(op->reg, op->vt);
2889 if (op->is_llong)
2890 tcc_error("long long not implemented");
2895 /* generate reg restore code */
2896 if (saved_regset)
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 */
2908 priority = 0;
2909 for(;;) {
2910 c = *str;
2911 if (c == '\0')
2912 break;
2913 str++;
2914 switch(c) {
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]
2918 pr = 3;
2919 break;
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]
2927 pr = 4;
2928 break;
2929 default:
2930 tcc_error("unknown constraint '%c'", c);
2931 pr = 0;
2933 if (pr > priority)
2934 priority = pr;
2936 return priority;
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 == '%')
2950 p++;
2951 return 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,
2962 int *pout_reg)
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
2966 whitespace ignored
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
2989 ASMOperand *op;
2990 int sorted_op[MAX_ASM_OPERANDS];
2991 int i, j, k, p1, p2, tmp, reg, c, reg_mask;
2992 const char *str;
2993 uint8_t regs_allocated[NB_ASM_REGS];
2995 /* init fields */
2996 for (i = 0; i < nb_operands; i++) {
2997 op = &operands[i];
2998 op->input_index = -1;
2999 op->ref_index = -1;
3000 op->reg = -1;
3001 op->is_memory = 0;
3002 op->is_rw = 0;
3004 /* compute constraint priority and evaluate references to output
3005 constraints if input constraints */
3006 for (i = 0; i < nb_operands; i++) {
3007 op = &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')",
3015 i, str);
3016 op->ref_index = k;
3017 if (operands[k].input_index >= 0)
3018 tcc_error("cannot reference twice the same operand");
3019 operands[k].input_index = i;
3020 op->priority = 5;
3021 } else if ((op->vt->r & VT_VALMASK) == VT_LOCAL
3022 && op->vt->sym
3023 && (reg = op->vt->sym->r & VT_VALMASK) < VT_CONST) {
3024 op->priority = 1;
3025 op->reg = reg;
3026 } else {
3027 op->priority = constraint_priority(str);
3031 /* sort operands according to their priority */
3032 for (i = 0; i < nb_operands; i++)
3033 sorted_op[i] = 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;
3038 if (p2 < p1) {
3039 tmp = sorted_op[i];
3040 sorted_op[i] = sorted_op[j];
3041 sorted_op[j] = tmp;
3046 for (i = 0; i < NB_ASM_REGS; i++) {
3047 if (clobber_regs[i])
3048 regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK;
3049 else
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++) {
3059 j = sorted_op[i];
3060 op = &operands[j];
3061 str = op->constraint;
3062 /* no need to allocate references */
3063 if (op->ref_index >= 0)
3064 continue;
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;
3070 } else {
3071 reg_mask = REG_IN_MASK;
3073 if (op->reg >= 0) {
3074 if (is_reg_allocated(op->reg))
3075 tcc_error
3076 ("asm regvar requests register that's taken already");
3077 reg = op->reg;
3078 goto reg_found;
3080 try_next:
3081 c = *str++;
3082 switch (c) {
3083 case '=': // Operand is written-to
3084 goto try_next;
3085 case '+': // Operand is both READ and written-to
3086 op->is_rw = 1;
3087 /* FALL THRU */
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;
3093 goto try_next;
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))
3100 goto reg_found;
3102 goto try_next;
3103 reg_found:
3104 /* now we can reload in the register */
3105 op->is_llong = 0;
3106 op->reg = reg;
3107 regs_allocated[reg] |= reg_mask;
3108 break;
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))
3115 goto try_next;
3116 break;
3117 case 'M': // integer in the range 0 to 32
3118 if (!
3119 ((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) ==
3120 VT_CONST))
3121 goto try_next;
3122 break;
3123 case 'm': // memory operand
3124 case 'g':
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
3132 later */
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))
3138 goto reg_found1;
3140 goto try_next;
3141 reg_found1:
3142 /* now we can reload in the register */
3143 regs_allocated[reg] |= REG_IN_MASK;
3144 op->reg = reg;
3145 op->is_memory = 1;
3148 break;
3149 default:
3150 tcc_error("asm constraint %d ('%s') could not be satisfied",
3151 j, op->constraint);
3152 break;
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) */
3163 *pout_reg = -1;
3164 for (i = 0; i < nb_operands; i++) {
3165 op = &operands[i];
3166 if (op->reg >= 0 &&
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))
3170 goto reg_found2;
3172 tcc_error("could not find free output register for reloading");
3173 reg_found2:
3174 *pout_reg = reg;
3175 break;
3179 /* print sorted constraints */
3180 #ifdef ASM_DEBUG
3181 for (i = 0; i < nb_operands; i++) {
3182 j = sorted_op[i];
3183 op = &operands[j];
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);
3189 if (*pout_reg >= 0)
3190 printf("out_reg=%d\n", *pout_reg);
3191 #endif
3194 ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str)
3196 int reg;
3197 TokenSym *ts;
3199 if (!strcmp(str, "memory") ||
3200 !strcmp(str, "cc") ||
3201 !strcmp(str, "flags"))
3202 return;
3203 ts = tok_alloc(str, strlen(str));
3204 reg = asm_parse_regvar(ts->tok);
3205 if (reg == -1) {
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 */
3216 switch (t) {
3217 case TOK_ASM_fp:
3218 return TOK_ASM_r11 - TOK_ASM_r0;
3219 case TOK_ASM_ip:
3220 return TOK_ASM_r12 - TOK_ASM_r0;
3221 case TOK_ASM_sp:
3222 return TOK_ASM_r13 - TOK_ASM_r0;
3223 case TOK_ASM_lr:
3224 return TOK_ASM_r14 - TOK_ASM_r0;
3225 case TOK_ASM_pc:
3226 return TOK_ASM_r15 - TOK_ASM_r0;
3227 default:
3228 return t - TOK_ASM_r0;
3230 } else
3231 return -1;
3234 /*************************************************************/
3235 #endif /* ndef TARGET_DEFS_ONLY */