tccgen: Allow struct init from struct
[tinycc.git] / arm-asm.c
blob45ccab5d77d3e07749a78ddc7dea04f9f3ba4bb8
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 static int asm_parse_vfp_status_regvar(int t)
82 switch (t) {
83 case TOK_ASM_fpsid:
84 return 0;
85 case TOK_ASM_fpscr:
86 return 1;
87 case TOK_ASM_fpexc:
88 return 8;
89 default:
90 return -1;
94 /* Parse a text containing operand and store the result in OP */
95 static void parse_operand(TCCState *s1, Operand *op)
97 ExprValue e;
98 int8_t reg;
99 uint16_t regset = 0;
101 op->type = 0;
103 if (tok == '{') { // regset literal
104 next(); // skip '{'
105 while (tok != '}' && tok != TOK_EOF) {
106 reg = asm_parse_regvar(tok);
107 if (reg == -1) {
108 expect("register");
109 return;
110 } else
111 next(); // skip register name
113 if ((1 << reg) < regset)
114 tcc_warning("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
115 regset |= 1 << reg;
116 if (tok != ',')
117 break;
118 next(); // skip ','
120 if (tok != '}')
121 expect("'}'");
122 next(); // skip '}'
123 if (regset == 0) {
124 // ARM instructions don't support empty regset.
125 tcc_error("empty register list is not supported");
126 } else {
127 op->type = OP_REGSET32;
128 op->regset = regset;
130 return;
131 } else if ((reg = asm_parse_regvar(tok)) != -1) {
132 next(); // skip register name
133 op->type = OP_REG32;
134 op->reg = (uint8_t) reg;
135 return;
136 } else if ((reg = asm_parse_vfp_regvar(tok, 0)) != -1) {
137 next(); // skip register name
138 op->type = OP_VREG32;
139 op->reg = (uint8_t) reg;
140 return;
141 } else if ((reg = asm_parse_vfp_regvar(tok, 1)) != -1) {
142 next(); // skip register name
143 op->type = OP_VREG64;
144 op->reg = (uint8_t) reg;
145 return;
146 } else if (tok == '#' || tok == '$') {
147 /* constant value */
148 next(); // skip '#' or '$'
150 asm_expr(s1, &e);
151 op->type = OP_IM32;
152 op->e = e;
153 if (!op->e.sym) {
154 if ((int) op->e.v < 0 && (int) op->e.v >= -255)
155 op->type = OP_IM8N;
156 else if (op->e.v == (uint8_t)op->e.v)
157 op->type = OP_IM8;
158 } else
159 expect("operand");
162 /* XXX: make it faster ? */
163 ST_FUNC void g(int c)
165 int ind1;
166 if (nocode_wanted)
167 return;
168 ind1 = ind + 1;
169 if (ind1 > cur_text_section->data_allocated)
170 section_realloc(cur_text_section, ind1);
171 cur_text_section->data[ind] = c;
172 ind = ind1;
175 ST_FUNC void gen_le16 (int i)
177 g(i);
178 g(i>>8);
181 ST_FUNC void gen_le32 (int i)
183 int ind1;
184 if (nocode_wanted)
185 return;
186 ind1 = ind + 4;
187 if (ind1 > cur_text_section->data_allocated)
188 section_realloc(cur_text_section, ind1);
189 cur_text_section->data[ind++] = i & 0xFF;
190 cur_text_section->data[ind++] = (i >> 8) & 0xFF;
191 cur_text_section->data[ind++] = (i >> 16) & 0xFF;
192 cur_text_section->data[ind++] = (i >> 24) & 0xFF;
195 ST_FUNC void gen_expr32(ExprValue *pe)
197 gen_le32(pe->v);
200 static uint32_t condition_code_of_token(int token) {
201 if (token < TOK_ASM_nopeq) {
202 expect("condition-enabled instruction");
203 return 0;
204 } else
205 return (token - TOK_ASM_nopeq) & 15;
208 static void asm_emit_opcode(int token, uint32_t opcode) {
209 gen_le32((condition_code_of_token(token) << 28) | opcode);
212 static void asm_emit_unconditional_opcode(uint32_t opcode) {
213 gen_le32(opcode);
216 static void asm_emit_coprocessor_opcode(uint32_t high_nibble, uint8_t cp_number, uint8_t cp_opcode, uint8_t cp_destination_register, uint8_t cp_n_operand_register, uint8_t cp_m_operand_register, uint8_t cp_opcode2, int inter_processor_transfer)
218 uint32_t opcode = 0xe000000;
219 if (inter_processor_transfer)
220 opcode |= 1 << 4;
221 //assert(cp_opcode < 16);
222 opcode |= cp_opcode << 20;
223 //assert(cp_n_operand_register < 16);
224 opcode |= cp_n_operand_register << 16;
225 //assert(cp_destination_register < 16);
226 opcode |= cp_destination_register << 12;
227 //assert(cp_number < 16);
228 opcode |= cp_number << 8;
229 //assert(cp_information < 8);
230 opcode |= cp_opcode2 << 5;
231 //assert(cp_m_operand_register < 16);
232 opcode |= cp_m_operand_register;
233 asm_emit_unconditional_opcode((high_nibble << 28) | opcode);
236 static void asm_nullary_opcode(int token)
238 switch (ARM_INSTRUCTION_GROUP(token)) {
239 case TOK_ASM_nopeq:
240 asm_emit_opcode(token, 0xd << 21); // mov r0, r0
241 break;
242 case TOK_ASM_wfeeq:
243 asm_emit_opcode(token, 0x320f002);
244 case TOK_ASM_wfieq:
245 asm_emit_opcode(token, 0x320f003);
246 break;
247 default:
248 expect("nullary instruction");
252 static void asm_unary_opcode(TCCState *s1, int token)
254 Operand op;
255 parse_operand(s1, &op);
257 switch (ARM_INSTRUCTION_GROUP(token)) {
258 case TOK_ASM_swieq:
259 case TOK_ASM_svceq:
260 if (op.type != OP_IM8)
261 expect("immediate 8-bit unsigned integer");
262 else {
263 /* Note: Dummy operand (ignored by processor): ARM ref documented 0...255, ARM instruction set documented 24 bit */
264 asm_emit_opcode(token, (0xf << 24) | op.e.v);
266 break;
267 default:
268 expect("unary instruction");
272 static void asm_binary_opcode(TCCState *s1, int token)
274 Operand ops[2];
275 Operand rotation;
276 uint32_t encoded_rotation = 0;
277 uint64_t amount;
278 parse_operand(s1, &ops[0]);
279 if (tok == ',')
280 next();
281 else
282 expect("','");
283 parse_operand(s1, &ops[1]);
284 if (ops[0].type != OP_REG32) {
285 expect("(destination operand) register");
286 return;
289 if (ops[0].reg == 15) {
290 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL));
291 return;
294 if (ops[0].reg == 13)
295 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token, NULL));
297 if (ops[1].type != OP_REG32) {
298 switch (ARM_INSTRUCTION_GROUP(token)) {
299 case TOK_ASM_movteq:
300 case TOK_ASM_movweq:
301 if (ops[1].type == OP_IM8 || ops[1].type == OP_IM8N || ops[1].type == OP_IM32) {
302 if (ops[1].e.v >= 0 && ops[1].e.v <= 0xFFFF) {
303 uint16_t immediate_value = ops[1].e.v;
304 switch (ARM_INSTRUCTION_GROUP(token)) {
305 case TOK_ASM_movteq:
306 asm_emit_opcode(token, 0x3400000 | (ops[0].reg << 12) | (immediate_value & 0xF000) << 4 | (immediate_value & 0xFFF));
307 break;
308 case TOK_ASM_movweq:
309 asm_emit_opcode(token, 0x3000000 | (ops[0].reg << 12) | (immediate_value & 0xF000) << 4 | (immediate_value & 0xFFF));
310 break;
312 } else
313 expect("(source operand) immediate 16 bit value");
314 } else
315 expect("(source operand) immediate");
316 break;
317 default:
318 expect("(source operand) register");
320 return;
323 if (ops[1].reg == 15) {
324 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL));
325 return;
328 if (ops[1].reg == 13)
329 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token, NULL));
331 if (tok == ',') {
332 next(); // skip ','
333 if (tok == TOK_ASM_ror) {
334 next(); // skip 'ror'
335 parse_operand(s1, &rotation);
336 if (rotation.type != OP_IM8) {
337 expect("immediate value for rotation");
338 return;
339 } else {
340 amount = rotation.e.v;
341 switch (amount) {
342 case 8:
343 encoded_rotation = 1 << 10;
344 break;
345 case 16:
346 encoded_rotation = 2 << 10;
347 break;
348 case 24:
349 encoded_rotation = 3 << 10;
350 break;
351 default:
352 expect("'8' or '16' or '24'");
353 return;
358 switch (ARM_INSTRUCTION_GROUP(token)) {
359 case TOK_ASM_clzeq:
360 if (encoded_rotation)
361 tcc_error("clz does not support rotation");
362 asm_emit_opcode(token, 0x16f0f10 | (ops[0].reg << 12) | ops[1].reg);
363 break;
364 case TOK_ASM_sxtbeq:
365 asm_emit_opcode(token, 0x6af0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation);
366 break;
367 case TOK_ASM_sxtheq:
368 asm_emit_opcode(token, 0x6bf0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation);
369 break;
370 case TOK_ASM_uxtbeq:
371 asm_emit_opcode(token, 0x6ef0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation);
372 break;
373 case TOK_ASM_uxtheq:
374 asm_emit_opcode(token, 0x6ff0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation);
375 break;
376 default:
377 expect("binary instruction");
381 static void asm_coprocessor_opcode(TCCState *s1, int token) {
382 uint8_t coprocessor;
383 Operand opcode1;
384 Operand opcode2;
385 uint8_t registers[3];
386 unsigned int i;
387 uint8_t high_nibble;
388 uint8_t mrc = 0;
390 if (tok >= TOK_ASM_p0 && tok <= TOK_ASM_p15) {
391 coprocessor = tok - TOK_ASM_p0;
392 next();
393 } else {
394 expect("'p<number>'");
395 return;
398 if (tok == ',')
399 next();
400 else
401 expect("','");
403 parse_operand(s1, &opcode1);
404 if (opcode1.type != OP_IM8 || opcode1.e.v > 15) {
405 tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token, NULL));
406 return;
409 for (i = 0; i < 3; ++i) {
410 if (tok == ',')
411 next();
412 else
413 expect("','");
414 if (i == 0 && token != TOK_ASM_cdp2 && (ARM_INSTRUCTION_GROUP(token) == TOK_ASM_mrceq || ARM_INSTRUCTION_GROUP(token) == TOK_ASM_mcreq)) {
415 if (tok >= TOK_ASM_r0 && tok <= TOK_ASM_r15) {
416 registers[i] = tok - TOK_ASM_r0;
417 next();
418 } else {
419 expect("'r<number>'");
420 return;
422 } else {
423 if (tok >= TOK_ASM_c0 && tok <= TOK_ASM_c15) {
424 registers[i] = tok - TOK_ASM_c0;
425 next();
426 } else {
427 expect("'c<number>'");
428 return;
432 if (tok == ',') {
433 next();
434 parse_operand(s1, &opcode2);
435 } else {
436 opcode2.type = OP_IM8;
437 opcode2.e.v = 0;
439 if (opcode2.type != OP_IM8 || opcode2.e.v > 15) {
440 tcc_error("opcode2 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token, NULL));
441 return;
444 if (token == TOK_ASM_cdp2) {
445 high_nibble = 0xF;
446 asm_emit_coprocessor_opcode(high_nibble, coprocessor, opcode1.e.v, registers[0], registers[1], registers[2], opcode2.e.v, 0);
447 return;
448 } else
449 high_nibble = condition_code_of_token(token);
451 switch (ARM_INSTRUCTION_GROUP(token)) {
452 case TOK_ASM_cdpeq:
453 asm_emit_coprocessor_opcode(high_nibble, coprocessor, opcode1.e.v, registers[0], registers[1], registers[2], opcode2.e.v, 0);
454 break;
455 case TOK_ASM_mrceq:
456 // opcode1 encoding changes! highest and lowest bit gone.
457 mrc = 1;
458 /* fallthrough */
459 case TOK_ASM_mcreq:
460 // opcode1 encoding changes! highest and lowest bit gone.
461 if (opcode1.e.v > 7) {
462 tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 7", get_tok_str(token, NULL));
463 return;
465 asm_emit_coprocessor_opcode(high_nibble, coprocessor, (opcode1.e.v << 1) | mrc, registers[0], registers[1], registers[2], opcode2.e.v, 1);
466 break;
467 default:
468 expect("known instruction");
472 /* data processing and single data transfer instructions only */
473 #define ENCODE_RN(register_index) ((register_index) << 16)
474 #define ENCODE_RD(register_index) ((register_index) << 12)
475 #define ENCODE_SET_CONDITION_CODES (1 << 20)
477 /* Note: For data processing instructions, "1" means immediate.
478 Note: For single data transfer instructions, "0" means immediate. */
479 #define ENCODE_IMMEDIATE_FLAG (1 << 25)
481 #define ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER (1 << 4)
482 #define ENCODE_BARREL_SHIFTER_MODE_LSL (0 << 5)
483 #define ENCODE_BARREL_SHIFTER_MODE_LSR (1 << 5)
484 #define ENCODE_BARREL_SHIFTER_MODE_ASR (2 << 5)
485 #define ENCODE_BARREL_SHIFTER_MODE_ROR (3 << 5)
486 #define ENCODE_BARREL_SHIFTER_REGISTER(register_index) ((register_index) << 8)
487 #define ENCODE_BARREL_SHIFTER_IMMEDIATE(value) ((value) << 7)
489 static void asm_block_data_transfer_opcode(TCCState *s1, int token)
491 uint32_t opcode;
492 int op0_exclam = 0;
493 Operand ops[2];
494 int nb_ops = 1;
495 parse_operand(s1, &ops[0]);
496 if (tok == '!') {
497 op0_exclam = 1;
498 next(); // skip '!'
500 if (tok == ',') {
501 next(); // skip comma
502 parse_operand(s1, &ops[1]);
503 ++nb_ops;
505 if (nb_ops < 1) {
506 expect("at least one operand");
507 return;
508 } else if (ops[nb_ops - 1].type != OP_REGSET32) {
509 expect("(last operand) register list");
510 return;
513 // block data transfer: 1 0 0 P U S W L << 20 (general case):
514 // operands:
515 // Rn: bits 19...16 base register
516 // Register List: bits 15...0
518 switch (ARM_INSTRUCTION_GROUP(token)) {
519 case TOK_ASM_pusheq: // TODO: Optimize 1-register case to: str ?, [sp, #-4]!
520 // Instruction: 1 I=0 P=1 U=0 S=0 W=1 L=0 << 20, op 1101
521 // operands:
522 // Rn: base register
523 // Register List: bits 15...0
524 if (nb_ops != 1)
525 expect("exactly one operand");
526 else
527 asm_emit_opcode(token, (0x92d << 16) | ops[0].regset); // TODO: base register ?
528 break;
529 case TOK_ASM_popeq: // TODO: Optimize 1-register case to: ldr ?, [sp], #4
530 // Instruction: 1 I=0 P=0 U=1 S=0 W=0 L=1 << 20, op 1101
531 // operands:
532 // Rn: base register
533 // Register List: bits 15...0
534 if (nb_ops != 1)
535 expect("exactly one operand");
536 else
537 asm_emit_opcode(token, (0x8bd << 16) | ops[0].regset); // TODO: base register ?
538 break;
539 case TOK_ASM_stmdaeq:
540 case TOK_ASM_ldmdaeq:
541 case TOK_ASM_stmeq:
542 case TOK_ASM_ldmeq:
543 case TOK_ASM_stmiaeq:
544 case TOK_ASM_ldmiaeq:
545 case TOK_ASM_stmdbeq:
546 case TOK_ASM_ldmdbeq:
547 case TOK_ASM_stmibeq:
548 case TOK_ASM_ldmibeq:
549 switch (ARM_INSTRUCTION_GROUP(token)) {
550 case TOK_ASM_stmdaeq: // post-decrement store
551 opcode = 0x80 << 20;
552 break;
553 case TOK_ASM_ldmdaeq: // post-decrement load
554 opcode = 0x81 << 20;
555 break;
556 case TOK_ASM_stmeq: // post-increment store
557 case TOK_ASM_stmiaeq: // post-increment store
558 opcode = 0x88 << 20;
559 break;
560 case TOK_ASM_ldmeq: // post-increment load
561 case TOK_ASM_ldmiaeq: // post-increment load
562 opcode = 0x89 << 20;
563 break;
564 case TOK_ASM_stmdbeq: // pre-decrement store
565 opcode = 0x90 << 20;
566 break;
567 case TOK_ASM_ldmdbeq: // pre-decrement load
568 opcode = 0x91 << 20;
569 break;
570 case TOK_ASM_stmibeq: // pre-increment store
571 opcode = 0x98 << 20;
572 break;
573 case TOK_ASM_ldmibeq: // pre-increment load
574 opcode = 0x99 << 20;
575 break;
576 default:
577 tcc_error("internal error: This place should not be reached (fallback in asm_block_data_transfer_opcode)");
579 // operands:
580 // Rn: first operand
581 // Register List: lower bits
582 if (nb_ops != 2)
583 expect("exactly two operands");
584 else if (ops[0].type != OP_REG32)
585 expect("(first operand) register");
586 else {
587 if (op0_exclam)
588 opcode |= 1 << 21; // writeback
589 asm_emit_opcode(token, opcode | ENCODE_RN(ops[0].reg) | ops[1].regset);
591 break;
592 default:
593 expect("block data transfer instruction");
597 /* Parses shift directive and returns the parts that would have to be set in the opcode because of it.
598 Does not encode the actual shift amount.
599 It's not an error if there is no shift directive.
601 NB_SHIFT: will be set to 1 iff SHIFT is filled. Note that for rrx, there's no need to fill SHIFT.
602 SHIFT: will be filled in with the shift operand to use, if any. */
603 static uint32_t asm_parse_optional_shift(TCCState* s1, int* nb_shift, Operand* shift)
605 uint32_t opcode = 0;
606 *nb_shift = 0;
607 switch (tok) {
608 case TOK_ASM_asl:
609 case TOK_ASM_lsl:
610 case TOK_ASM_asr:
611 case TOK_ASM_lsr:
612 case TOK_ASM_ror:
613 switch (tok) {
614 case TOK_ASM_asl:
615 /* fallthrough */
616 case TOK_ASM_lsl:
617 opcode = ENCODE_BARREL_SHIFTER_MODE_LSL;
618 break;
619 case TOK_ASM_asr:
620 opcode = ENCODE_BARREL_SHIFTER_MODE_ASR;
621 break;
622 case TOK_ASM_lsr:
623 opcode = ENCODE_BARREL_SHIFTER_MODE_LSR;
624 break;
625 case TOK_ASM_ror:
626 opcode = ENCODE_BARREL_SHIFTER_MODE_ROR;
627 break;
629 next();
630 parse_operand(s1, shift);
631 *nb_shift = 1;
632 break;
633 case TOK_ASM_rrx:
634 next();
635 opcode = ENCODE_BARREL_SHIFTER_MODE_ROR;
636 break;
638 return opcode;
641 static uint32_t asm_encode_shift(Operand* shift)
643 uint64_t amount;
644 uint32_t operands = 0;
645 switch (shift->type) {
646 case OP_REG32:
647 if (shift->reg == 15)
648 tcc_error("r15 cannot be used as a shift count");
649 else {
650 operands = ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER;
651 operands |= ENCODE_BARREL_SHIFTER_REGISTER(shift->reg);
653 break;
654 case OP_IM8:
655 amount = shift->e.v;
656 if (amount > 0 && amount < 32)
657 operands = ENCODE_BARREL_SHIFTER_IMMEDIATE(amount);
658 else
659 tcc_error("shift count out of range");
660 break;
661 default:
662 tcc_error("unknown shift amount");
664 return operands;
667 static void asm_data_processing_opcode(TCCState *s1, int token)
669 Operand ops[3];
670 int nb_ops;
671 Operand shift = {0};
672 int nb_shift = 0;
673 uint32_t operands = 0;
675 /* modulo 16 entries per instruction for the different condition codes */
676 uint32_t opcode_idx = (ARM_INSTRUCTION_GROUP(token) - TOK_ASM_andeq) >> 4;
677 uint32_t opcode_nos = opcode_idx >> 1; // without "s"; "OpCode" in ARM docs
679 for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ) {
680 if (tok == TOK_ASM_asl || tok == TOK_ASM_lsl || tok == TOK_ASM_lsr || tok == TOK_ASM_asr || tok == TOK_ASM_ror || tok == TOK_ASM_rrx)
681 break;
682 parse_operand(s1, &ops[nb_ops]);
683 ++nb_ops;
684 if (tok != ',')
685 break;
686 next(); // skip ','
688 if (tok == ',')
689 next();
690 operands |= asm_parse_optional_shift(s1, &nb_shift, &shift);
691 if (nb_ops < 2)
692 expect("at least two operands");
693 else if (nb_ops == 2) {
694 memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2]
695 memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit
696 nb_ops = 3;
697 } else if (nb_ops == 3) {
698 if (opcode_nos == 0xd || opcode_nos == 0xf || opcode_nos == 0xa || opcode_nos == 0xb || opcode_nos == 0x8 || opcode_nos == 0x9) { // mov, mvn, cmp, cmn, tst, teq
699 tcc_error("'%s' cannot be used with three operands", get_tok_str(token, NULL));
700 return;
703 if (nb_ops != 3) {
704 expect("two or three operands");
705 return;
706 } else {
707 uint32_t opcode = 0;
708 uint32_t immediate_value;
709 uint8_t half_immediate_rotation;
710 if (nb_shift && shift.type == OP_REG32) {
711 if ((ops[0].type == OP_REG32 && ops[0].reg == 15) ||
712 (ops[1].type == OP_REG32 && ops[1].reg == 15)) {
713 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
714 return;
718 // data processing (general case):
719 // operands:
720 // Rn: bits 19...16 (first operand)
721 // Rd: bits 15...12 (destination)
722 // Operand2: bits 11...0 (second operand); depending on I that's either a register or an immediate
723 // operator:
724 // bits 24...21: "OpCode"--see below
726 /* operations in the token list are ordered by opcode */
727 opcode = opcode_nos << 21; // drop "s"
728 if (ops[0].type != OP_REG32)
729 expect("(destination operand) register");
730 else if (opcode_nos == 0xa || opcode_nos == 0xb || opcode_nos == 0x8 || opcode_nos == 0x9) // cmp, cmn, tst, teq
731 operands |= ENCODE_SET_CONDITION_CODES; // force S set, otherwise it's a completely different instruction.
732 else
733 operands |= ENCODE_RD(ops[0].reg);
734 if (ops[1].type != OP_REG32)
735 expect("(first source operand) register");
736 else if (!(opcode_nos == 0xd || opcode_nos == 0xf)) // not: mov, mvn (those have only one source operand)
737 operands |= ENCODE_RN(ops[1].reg);
738 switch (ops[2].type) {
739 case OP_REG32:
740 operands |= ops[2].reg;
741 break;
742 case OP_IM8:
743 case OP_IM32:
744 operands |= ENCODE_IMMEDIATE_FLAG;
745 immediate_value = ops[2].e.v;
746 for (half_immediate_rotation = 0; half_immediate_rotation < 16; ++half_immediate_rotation) {
747 if (immediate_value >= 0x00 && immediate_value < 0x100)
748 break;
749 // rotate left by two
750 immediate_value = ((immediate_value & 0x3FFFFFFF) << 2) | ((immediate_value & 0xC0000000) >> 30);
752 if (half_immediate_rotation >= 16) {
753 /* fallthrough */
754 } else {
755 operands |= immediate_value;
756 operands |= half_immediate_rotation << 8;
757 break;
759 case OP_IM8N: // immediate negative value
760 operands |= ENCODE_IMMEDIATE_FLAG;
761 immediate_value = ops[2].e.v;
762 /* Instruction swapping:
763 0001 = EOR - Rd:= Op1 EOR Op2 -> difficult
764 0011 = RSB - Rd:= Op2 - Op1 -> difficult
765 0111 = RSC - Rd:= Op2 - Op1 + C -> difficult
766 1000 = TST - CC on: Op1 AND Op2 -> difficult
767 1001 = TEQ - CC on: Op1 EOR Op2 -> difficult
768 1100 = ORR - Rd:= Op1 OR Op2 -> difficult
770 switch (opcode_nos) {
771 case 0x0: // AND - Rd:= Op1 AND Op2
772 opcode = 0xe << 21; // BIC
773 immediate_value = ~immediate_value;
774 break;
775 case 0x2: // SUB - Rd:= Op1 - Op2
776 opcode = 0x4 << 21; // ADD
777 immediate_value = -immediate_value;
778 break;
779 case 0x4: // ADD - Rd:= Op1 + Op2
780 opcode = 0x2 << 21; // SUB
781 immediate_value = -immediate_value;
782 break;
783 case 0x5: // ADC - Rd:= Op1 + Op2 + C
784 opcode = 0x6 << 21; // SBC
785 immediate_value = ~immediate_value;
786 break;
787 case 0x6: // SBC - Rd:= Op1 - Op2 + C
788 opcode = 0x5 << 21; // ADC
789 immediate_value = ~immediate_value;
790 break;
791 case 0xa: // CMP - CC on: Op1 - Op2
792 opcode = 0xb << 21; // CMN
793 immediate_value = -immediate_value;
794 break;
795 case 0xb: // CMN - CC on: Op1 + Op2
796 opcode = 0xa << 21; // CMP
797 immediate_value = -immediate_value;
798 break;
799 case 0xd: // MOV - Rd:= Op2
800 opcode = 0xf << 21; // MVN
801 immediate_value = ~immediate_value;
802 break;
803 case 0xe: // BIC - Rd:= Op1 AND NOT Op2
804 opcode = 0x0 << 21; // AND
805 immediate_value = ~immediate_value;
806 break;
807 case 0xf: // MVN - Rd:= NOT Op2
808 opcode = 0xd << 21; // MOV
809 immediate_value = ~immediate_value;
810 break;
811 default:
812 tcc_error("cannot use '%s' with a negative immediate value", get_tok_str(token, NULL));
814 for (half_immediate_rotation = 0; half_immediate_rotation < 16; ++half_immediate_rotation) {
815 if (immediate_value >= 0x00 && immediate_value < 0x100)
816 break;
817 // rotate left by two
818 immediate_value = ((immediate_value & 0x3FFFFFFF) << 2) | ((immediate_value & 0xC0000000) >> 30);
820 if (half_immediate_rotation >= 16) {
821 immediate_value = ops[2].e.v;
822 tcc_error("immediate value 0x%X cannot be encoded into ARM immediate", (unsigned) immediate_value);
823 return;
825 operands |= immediate_value;
826 operands |= half_immediate_rotation << 8;
827 break;
828 default:
829 expect("(second source operand) register or immediate value");
832 if (nb_shift) {
833 if (operands & ENCODE_IMMEDIATE_FLAG)
834 tcc_error("immediate rotation not implemented");
835 else
836 operands |= asm_encode_shift(&shift);
839 /* S=0 and S=1 entries alternate one after another, in that order */
840 opcode |= (opcode_idx & 1) ? ENCODE_SET_CONDITION_CODES : 0;
841 asm_emit_opcode(token, opcode | operands);
845 static void asm_shift_opcode(TCCState *s1, int token)
847 Operand ops[3];
848 int nb_ops;
849 int definitely_neutral = 0;
850 uint32_t opcode = 0xd << 21; // MOV
851 uint32_t operands = 0;
853 for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ++nb_ops) {
854 parse_operand(s1, &ops[nb_ops]);
855 if (tok != ',') {
856 ++nb_ops;
857 break;
859 next(); // skip ','
861 if (nb_ops < 2) {
862 expect("at least two operands");
863 return;
866 if (ops[0].type != OP_REG32) {
867 expect("(destination operand) register");
868 return;
869 } else
870 operands |= ENCODE_RD(ops[0].reg);
872 if (nb_ops == 2) {
873 switch (ARM_INSTRUCTION_GROUP(token)) {
874 case TOK_ASM_rrxseq:
875 opcode |= ENCODE_SET_CONDITION_CODES;
876 /* fallthrough */
877 case TOK_ASM_rrxeq:
878 if (ops[1].type == OP_REG32) {
879 operands |= ops[1].reg;
880 operands |= ENCODE_BARREL_SHIFTER_MODE_ROR;
881 asm_emit_opcode(token, opcode | operands);
882 } else
883 tcc_error("(first source operand) register");
884 return;
885 default:
886 memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2]
887 memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit
888 nb_ops = 3;
891 if (nb_ops != 3) {
892 expect("two or three operands");
893 return;
896 switch (ARM_INSTRUCTION_GROUP(token)) {
897 case TOK_ASM_lslseq:
898 case TOK_ASM_lsrseq:
899 case TOK_ASM_asrseq:
900 case TOK_ASM_rorseq:
901 opcode |= ENCODE_SET_CONDITION_CODES;
902 break;
905 switch (ops[1].type) {
906 case OP_REG32:
907 operands |= ops[1].reg;
908 break;
909 case OP_IM8:
910 operands |= ENCODE_IMMEDIATE_FLAG;
911 operands |= ops[1].e.v;
912 tcc_error("Using an immediate value as the source operand is not possible with '%s' instruction on ARM", get_tok_str(token, NULL));
913 return;
916 switch (ops[2].type) {
917 case OP_REG32:
918 if ((ops[0].type == OP_REG32 && ops[0].reg == 15) ||
919 (ops[1].type == OP_REG32 && ops[1].reg == 15)) {
920 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
922 operands |= asm_encode_shift(&ops[2]);
923 break;
924 case OP_IM8:
925 if (ops[2].e.v)
926 operands |= asm_encode_shift(&ops[2]);
927 else
928 definitely_neutral = 1;
929 break;
932 if (!definitely_neutral) switch (ARM_INSTRUCTION_GROUP(token)) {
933 case TOK_ASM_lslseq:
934 case TOK_ASM_lsleq:
935 operands |= ENCODE_BARREL_SHIFTER_MODE_LSL;
936 break;
937 case TOK_ASM_lsrseq:
938 case TOK_ASM_lsreq:
939 operands |= ENCODE_BARREL_SHIFTER_MODE_LSR;
940 break;
941 case TOK_ASM_asrseq:
942 case TOK_ASM_asreq:
943 operands |= ENCODE_BARREL_SHIFTER_MODE_ASR;
944 break;
945 case TOK_ASM_rorseq:
946 case TOK_ASM_roreq:
947 operands |= ENCODE_BARREL_SHIFTER_MODE_ROR;
948 break;
949 default:
950 expect("shift instruction");
951 return;
953 asm_emit_opcode(token, opcode | operands);
956 static void asm_multiplication_opcode(TCCState *s1, int token)
958 Operand ops[4];
959 int nb_ops = 0;
960 uint32_t opcode = 0x90;
962 for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ++nb_ops) {
963 parse_operand(s1, &ops[nb_ops]);
964 if (tok != ',') {
965 ++nb_ops;
966 break;
968 next(); // skip ','
970 if (nb_ops < 2)
971 expect("at least two operands");
972 else if (nb_ops == 2) {
973 switch (ARM_INSTRUCTION_GROUP(token)) {
974 case TOK_ASM_mulseq:
975 case TOK_ASM_muleq:
976 memcpy(&ops[2], &ops[0], sizeof(ops[1])); // ARM is actually like this!
977 break;
978 default:
979 expect("at least three operands");
980 return;
982 nb_ops = 3;
985 // multiply (special case):
986 // operands:
987 // Rd: bits 19...16
988 // Rm: bits 3...0
989 // Rs: bits 11...8
990 // Rn: bits 15...12
992 if (ops[0].type == OP_REG32)
993 opcode |= ops[0].reg << 16;
994 else
995 expect("(destination operand) register");
996 if (ops[1].type == OP_REG32)
997 opcode |= ops[1].reg;
998 else
999 expect("(first source operand) register");
1000 if (ops[2].type == OP_REG32)
1001 opcode |= ops[2].reg << 8;
1002 else
1003 expect("(second source operand) register");
1004 if (nb_ops > 3) {
1005 if (ops[3].type == OP_REG32)
1006 opcode |= ops[3].reg << 12;
1007 else
1008 expect("(third source operand) register");
1011 switch (ARM_INSTRUCTION_GROUP(token)) {
1012 case TOK_ASM_mulseq:
1013 opcode |= 1 << 20; // Status
1014 /* fallthrough */
1015 case TOK_ASM_muleq:
1016 if (nb_ops != 3)
1017 expect("three operands");
1018 else {
1019 asm_emit_opcode(token, opcode);
1021 break;
1022 case TOK_ASM_mlaseq:
1023 opcode |= 1 << 20; // Status
1024 /* fallthrough */
1025 case TOK_ASM_mlaeq:
1026 if (nb_ops != 4)
1027 expect("four operands");
1028 else {
1029 opcode |= 1 << 21; // Accumulate
1030 asm_emit_opcode(token, opcode);
1032 break;
1033 default:
1034 expect("known multiplication instruction");
1038 static void asm_long_multiplication_opcode(TCCState *s1, int token)
1040 Operand ops[4];
1041 int nb_ops = 0;
1042 uint32_t opcode = 0x90 | (1 << 23);
1044 for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ++nb_ops) {
1045 parse_operand(s1, &ops[nb_ops]);
1046 if (tok != ',') {
1047 ++nb_ops;
1048 break;
1050 next(); // skip ','
1052 if (nb_ops != 4) {
1053 expect("four operands");
1054 return;
1057 // long multiply (special case):
1058 // operands:
1059 // RdLo: bits 15...12
1060 // RdHi: bits 19...16
1061 // Rs: bits 11...8
1062 // Rm: bits 3...0
1064 if (ops[0].type == OP_REG32)
1065 opcode |= ops[0].reg << 12;
1066 else
1067 expect("(destination lo accumulator) register");
1068 if (ops[1].type == OP_REG32)
1069 opcode |= ops[1].reg << 16;
1070 else
1071 expect("(destination hi accumulator) register");
1072 if (ops[2].type == OP_REG32)
1073 opcode |= ops[2].reg;
1074 else
1075 expect("(first source operand) register");
1076 if (ops[3].type == OP_REG32)
1077 opcode |= ops[3].reg << 8;
1078 else
1079 expect("(second source operand) register");
1081 switch (ARM_INSTRUCTION_GROUP(token)) {
1082 case TOK_ASM_smullseq:
1083 opcode |= 1 << 20; // Status
1084 /* fallthrough */
1085 case TOK_ASM_smulleq:
1086 opcode |= 1 << 22; // signed
1087 asm_emit_opcode(token, opcode);
1088 break;
1089 case TOK_ASM_umullseq:
1090 opcode |= 1 << 20; // Status
1091 /* fallthrough */
1092 case TOK_ASM_umulleq:
1093 asm_emit_opcode(token, opcode);
1094 break;
1095 case TOK_ASM_smlalseq:
1096 opcode |= 1 << 20; // Status
1097 /* fallthrough */
1098 case TOK_ASM_smlaleq:
1099 opcode |= 1 << 22; // signed
1100 opcode |= 1 << 21; // Accumulate
1101 asm_emit_opcode(token, opcode);
1102 break;
1103 case TOK_ASM_umlalseq:
1104 opcode |= 1 << 20; // Status
1105 /* fallthrough */
1106 case TOK_ASM_umlaleq:
1107 opcode |= 1 << 21; // Accumulate
1108 asm_emit_opcode(token, opcode);
1109 break;
1110 default:
1111 expect("known long multiplication instruction");
1115 static void asm_single_data_transfer_opcode(TCCState *s1, int token)
1117 Operand ops[3];
1118 Operand strex_operand;
1119 Operand shift;
1120 int nb_shift = 0;
1121 int exclam = 0;
1122 int closed_bracket = 0;
1123 int op2_minus = 0;
1124 uint32_t opcode = 0;
1125 // Note: ldr r0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1126 // Note: ldr r0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1127 // Note: ldr r0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1129 parse_operand(s1, &ops[0]);
1130 if (ops[0].type == OP_REG32)
1131 opcode |= ENCODE_RD(ops[0].reg);
1132 else {
1133 expect("(destination operand) register");
1134 return;
1136 if (tok != ',')
1137 expect("at least two arguments");
1138 else
1139 next(); // skip ','
1141 switch (ARM_INSTRUCTION_GROUP(token)) {
1142 case TOK_ASM_strexbeq:
1143 case TOK_ASM_strexeq:
1144 parse_operand(s1, &strex_operand);
1145 if (strex_operand.type != OP_REG32) {
1146 expect("register");
1147 return;
1149 if (tok != ',')
1150 expect("at least three arguments");
1151 else
1152 next(); // skip ','
1153 break;
1156 if (tok != '[')
1157 expect("'['");
1158 else
1159 next(); // skip '['
1161 parse_operand(s1, &ops[1]);
1162 if (ops[1].type == OP_REG32)
1163 opcode |= ENCODE_RN(ops[1].reg);
1164 else {
1165 expect("(first source operand) register");
1166 return;
1168 if (tok == ']') {
1169 next();
1170 closed_bracket = 1;
1171 // exclam = 1; // implicit in hardware; don't do it in software
1173 if (tok == ',') {
1174 next(); // skip ','
1175 if (tok == '-') {
1176 op2_minus = 1;
1177 next();
1179 parse_operand(s1, &ops[2]);
1180 if (ops[2].type == OP_REG32) {
1181 if (ops[2].reg == 15) {
1182 tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token, NULL));
1183 return;
1185 if (tok == ',') {
1186 next();
1187 opcode |= asm_parse_optional_shift(s1, &nb_shift, &shift);
1188 if (opcode == 0)
1189 expect("shift directive, or no comma");
1192 } else {
1193 // end of input expression in brackets--assume 0 offset
1194 ops[2].type = OP_IM8;
1195 ops[2].e.v = 0;
1196 opcode |= 1 << 24; // add offset before transfer
1198 if (!closed_bracket) {
1199 if (tok != ']')
1200 expect("']'");
1201 else
1202 next(); // skip ']'
1203 opcode |= 1 << 24; // add offset before transfer
1204 if (tok == '!') {
1205 exclam = 1;
1206 next(); // skip '!'
1210 // single data transfer: 0 1 I P U B W L << 20 (general case):
1211 // operands:
1212 // Rd: destination operand [ok]
1213 // Rn: first source operand [ok]
1214 // Operand2: bits 11...0 [ok]
1215 // I: immediate operand? [ok]
1216 // P: Pre/post indexing is PRE: Add offset before transfer [ok]
1217 // U: Up/down is up? (*adds* offset to base) [ok]
1218 // B: Byte/word is byte? [ok]
1219 // W: Write address back into base? [ok]
1220 // L: Load/store is load? [ok]
1221 if (exclam)
1222 opcode |= 1 << 21; // write offset back into register
1224 if (ops[2].type == OP_IM32 || ops[2].type == OP_IM8 || ops[2].type == OP_IM8N) {
1225 int v = ops[2].e.v;
1226 if (op2_minus)
1227 tcc_error("minus before '#' not supported for immediate values");
1228 if (v >= 0) {
1229 opcode |= 1 << 23; // up
1230 if (v >= 0x1000)
1231 tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
1232 else
1233 opcode |= v;
1234 } else { // down
1235 if (v <= -0x1000)
1236 tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
1237 else
1238 opcode |= -v;
1240 } else if (ops[2].type == OP_REG32) {
1241 if (!op2_minus)
1242 opcode |= 1 << 23; // up
1243 opcode |= ENCODE_IMMEDIATE_FLAG; /* if set, it means it's NOT immediate */
1244 opcode |= ops[2].reg;
1245 } else
1246 expect("register");
1248 switch (ARM_INSTRUCTION_GROUP(token)) {
1249 case TOK_ASM_strbeq:
1250 opcode |= 1 << 22; // B
1251 /* fallthrough */
1252 case TOK_ASM_streq:
1253 opcode |= 1 << 26; // Load/Store
1254 if (nb_shift)
1255 opcode |= asm_encode_shift(&shift);
1256 asm_emit_opcode(token, opcode);
1257 break;
1258 case TOK_ASM_ldrbeq:
1259 opcode |= 1 << 22; // B
1260 /* fallthrough */
1261 case TOK_ASM_ldreq:
1262 opcode |= 1 << 20; // L
1263 opcode |= 1 << 26; // Load/Store
1264 if (nb_shift)
1265 opcode |= asm_encode_shift(&shift);
1266 asm_emit_opcode(token, opcode);
1267 break;
1268 case TOK_ASM_strexbeq:
1269 opcode |= 1 << 22; // B
1270 /* fallthrough */
1271 case TOK_ASM_strexeq:
1272 if ((opcode & 0xFFF) || nb_shift) {
1273 tcc_error("neither offset nor shift allowed with 'strex'");
1274 return;
1275 } else if (opcode & ENCODE_IMMEDIATE_FLAG) { // if set, it means it's NOT immediate
1276 tcc_error("offset not allowed with 'strex'");
1277 return;
1279 if ((opcode & (1 << 24)) == 0) { // add offset after transfer
1280 tcc_error("adding offset after transfer not allowed with 'strex'");
1281 return;
1284 opcode |= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL
1285 opcode |= strex_operand.reg;
1286 asm_emit_opcode(token, opcode);
1287 break;
1288 case TOK_ASM_ldrexbeq:
1289 opcode |= 1 << 22; // B
1290 /* fallthrough */
1291 case TOK_ASM_ldrexeq:
1292 if ((opcode & 0xFFF) || nb_shift) {
1293 tcc_error("neither offset nor shift allowed with 'ldrex'");
1294 return;
1295 } else if (opcode & ENCODE_IMMEDIATE_FLAG) { // if set, it means it's NOT immediate
1296 tcc_error("offset not allowed with 'ldrex'");
1297 return;
1299 if ((opcode & (1 << 24)) == 0) { // add offset after transfer
1300 tcc_error("adding offset after transfer not allowed with 'ldrex'");
1301 return;
1303 opcode |= 1 << 20; // L
1304 opcode |= 0x00f;
1305 opcode |= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL
1306 asm_emit_opcode(token, opcode);
1307 break;
1308 default:
1309 expect("data transfer instruction");
1313 // Note: Only call this using a VFP register if you know exactly what you are doing (i.e. cp_number is 10 or 11 and you are doing a vmov)
1314 static void asm_emit_coprocessor_data_transfer(uint32_t high_nibble, uint8_t cp_number, uint8_t CRd, const Operand* Rn, const Operand* offset, int offset_minus, int preincrement, int writeback, int long_transfer, int load) {
1315 uint32_t opcode = 0x0;
1316 opcode |= 1 << 26; // Load/Store
1317 opcode |= 1 << 27; // coprocessor
1319 if (long_transfer)
1320 opcode |= 1 << 22; // long transfer
1322 if (load)
1323 opcode |= 1 << 20; // L
1325 opcode |= cp_number << 8;
1327 //assert(CRd < 16);
1328 opcode |= ENCODE_RD(CRd);
1330 if (Rn->type != OP_REG32) {
1331 expect("register");
1332 return;
1334 //assert(Rn->reg < 16);
1335 opcode |= ENCODE_RN(Rn->reg);
1336 if (preincrement)
1337 opcode |= 1 << 24; // add offset before transfer
1339 if (writeback)
1340 opcode |= 1 << 21; // write offset back into register
1342 if (offset->type == OP_IM8 || offset->type == OP_IM8N || offset->type == OP_IM32) {
1343 int v = offset->e.v;
1344 if (offset_minus)
1345 tcc_error("minus before '#' not supported for immediate values");
1346 if (offset->type == OP_IM8N || v < 0)
1347 v = -v;
1348 else
1349 opcode |= 1 << 23; // up
1350 if (v & 3) {
1351 tcc_error("immediate offset must be a multiple of 4");
1352 return;
1354 v >>= 2;
1355 if (v > 255) {
1356 tcc_error("immediate offset must be between -1020 and 1020");
1357 return;
1359 opcode |= v;
1360 } else if (offset->type == OP_REG32) {
1361 if (!offset_minus)
1362 opcode |= 1 << 23; // up
1363 opcode |= ENCODE_IMMEDIATE_FLAG; /* if set, it means it's NOT immediate */
1364 opcode |= offset->reg;
1365 tcc_error("Using register offset to register address is not possible here");
1366 return;
1367 } else if (offset->type == OP_VREG64) {
1368 opcode |= 16;
1369 opcode |= offset->reg;
1370 } else
1371 expect("immediate or register");
1373 asm_emit_unconditional_opcode((high_nibble << 28) | opcode);
1376 // Almost exactly the same as asm_single_data_transfer_opcode.
1377 // Difference: Offsets are smaller and multiples of 4; no shifts, no STREX, ENCODE_IMMEDIATE_FLAG is inverted again.
1378 static void asm_coprocessor_data_transfer_opcode(TCCState *s1, int token)
1380 Operand ops[3];
1381 uint8_t coprocessor;
1382 uint8_t coprocessor_destination_register;
1383 int preincrement = 0;
1384 int exclam = 0;
1385 int closed_bracket = 0;
1386 int op2_minus = 0;
1387 int long_transfer = 0;
1388 // Note: ldc p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1389 // Note: ldc p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1390 // Note: ldc p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1392 if (tok >= TOK_ASM_p0 && tok <= TOK_ASM_p15) {
1393 coprocessor = tok - TOK_ASM_p0;
1394 next();
1395 } else {
1396 expect("'c<number>'");
1397 return;
1400 if (tok == ',')
1401 next();
1402 else
1403 expect("','");
1405 if (tok >= TOK_ASM_c0 && tok <= TOK_ASM_c15) {
1406 coprocessor_destination_register = tok - TOK_ASM_c0;
1407 next();
1408 } else {
1409 expect("'c<number>'");
1410 return;
1413 if (tok == ',')
1414 next();
1415 else
1416 expect("','");
1418 if (tok != '[')
1419 expect("'['");
1420 else
1421 next(); // skip '['
1423 parse_operand(s1, &ops[1]);
1424 if (ops[1].type != OP_REG32) {
1425 expect("(first source operand) register");
1426 return;
1428 if (tok == ']') {
1429 next();
1430 closed_bracket = 1;
1431 // exclam = 1; // implicit in hardware; don't do it in software
1433 if (tok == ',') {
1434 next(); // skip ','
1435 if (tok == '-') {
1436 op2_minus = 1;
1437 next();
1439 parse_operand(s1, &ops[2]);
1440 if (ops[2].type == OP_REG32) {
1441 if (ops[2].reg == 15) {
1442 tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token, NULL));
1443 return;
1445 } else if (ops[2].type == OP_VREG64) {
1446 tcc_error("'%s' does not support VFP register operand", get_tok_str(token, NULL));
1447 return;
1449 } else {
1450 // end of input expression in brackets--assume 0 offset
1451 ops[2].type = OP_IM8;
1452 ops[2].e.v = 0;
1453 preincrement = 1; // add offset before transfer
1455 if (!closed_bracket) {
1456 if (tok != ']')
1457 expect("']'");
1458 else
1459 next(); // skip ']'
1460 preincrement = 1; // add offset before transfer
1461 if (tok == '!') {
1462 exclam = 1;
1463 next(); // skip '!'
1467 // TODO: Support options.
1469 if (token == TOK_ASM_ldc2 || token == TOK_ASM_stc2 || token == TOK_ASM_ldc2l || token == TOK_ASM_stc2l) {
1470 switch (token) {
1471 case TOK_ASM_ldc2l:
1472 long_transfer = 1; // long transfer
1473 /* fallthrough */
1474 case TOK_ASM_ldc2:
1475 asm_emit_coprocessor_data_transfer(0xF, coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 1);
1476 break;
1477 case TOK_ASM_stc2l:
1478 long_transfer = 1; // long transfer
1479 /* fallthrough */
1480 case TOK_ASM_stc2:
1481 asm_emit_coprocessor_data_transfer(0xF, coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 0);
1482 break;
1484 } else switch (ARM_INSTRUCTION_GROUP(token)) {
1485 case TOK_ASM_stcleq:
1486 long_transfer = 1;
1487 /* fallthrough */
1488 case TOK_ASM_stceq:
1489 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 0);
1490 break;
1491 case TOK_ASM_ldcleq:
1492 long_transfer = 1;
1493 /* fallthrough */
1494 case TOK_ASM_ldceq:
1495 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 1);
1496 break;
1497 default:
1498 expect("coprocessor data transfer instruction");
1502 #if defined(TCC_ARM_VFP)
1503 #define CP_SINGLE_PRECISION_FLOAT 10
1504 #define CP_DOUBLE_PRECISION_FLOAT 11
1506 static void asm_floating_point_single_data_transfer_opcode(TCCState *s1, int token)
1508 Operand ops[3];
1509 uint8_t coprocessor = 0;
1510 uint8_t coprocessor_destination_register = 0;
1511 int long_transfer = 0;
1512 // Note: vldr p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1513 // Note: Not allowed: vldr p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1514 // Note: Not allowed: vldr p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1516 parse_operand(s1, &ops[0]);
1517 if (ops[0].type == OP_VREG32) {
1518 coprocessor = CP_SINGLE_PRECISION_FLOAT;
1519 coprocessor_destination_register = ops[0].reg;
1520 long_transfer = coprocessor_destination_register & 1;
1521 coprocessor_destination_register >>= 1;
1522 } else if (ops[0].type == OP_VREG64) {
1523 coprocessor = CP_DOUBLE_PRECISION_FLOAT;
1524 coprocessor_destination_register = ops[0].reg;
1525 next();
1526 } else {
1527 expect("floating point register");
1528 return;
1531 if (tok == ',')
1532 next();
1533 else
1534 expect("','");
1536 if (tok != '[')
1537 expect("'['");
1538 else
1539 next(); // skip '['
1541 parse_operand(s1, &ops[1]);
1542 if (ops[1].type != OP_REG32) {
1543 expect("(first source operand) register");
1544 return;
1546 if (tok == ',') {
1547 next(); // skip ','
1548 parse_operand(s1, &ops[2]);
1549 if (ops[2].type != OP_IM8 && ops[2].type != OP_IM8N) {
1550 expect("immediate offset");
1551 return;
1553 } else {
1554 // end of input expression in brackets--assume 0 offset
1555 ops[2].type = OP_IM8;
1556 ops[2].e.v = 0;
1558 if (tok != ']')
1559 expect("']'");
1560 else
1561 next(); // skip ']'
1563 switch (ARM_INSTRUCTION_GROUP(token)) {
1564 case TOK_ASM_vldreq:
1565 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], 0, 1, 0, long_transfer, 1);
1566 break;
1567 case TOK_ASM_vstreq:
1568 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], 0, 1, 0, long_transfer, 0);
1569 break;
1570 default:
1571 expect("floating point data transfer instruction");
1575 static void asm_floating_point_block_data_transfer_opcode(TCCState *s1, int token)
1577 uint8_t coprocessor = 0;
1578 int first_regset_register;
1579 int last_regset_register;
1580 uint8_t regset_item_count;
1581 uint8_t extra_register_bit = 0;
1582 int op0_exclam = 0;
1583 int load = 0;
1584 int preincrement = 0;
1585 Operand ops[1];
1586 Operand offset;
1587 switch (ARM_INSTRUCTION_GROUP(token)) {
1588 case TOK_ASM_vpusheq:
1589 case TOK_ASM_vpopeq:
1590 ops[0].type = OP_REG32;
1591 ops[0].reg = 13; // sp
1592 op0_exclam = 1;
1593 break;
1594 default:
1595 parse_operand(s1, &ops[0]);
1596 if (tok == '!') {
1597 op0_exclam = 1;
1598 next(); // skip '!'
1600 if (tok == ',')
1601 next(); // skip comma
1602 else {
1603 expect("','");
1604 return;
1608 if (tok != '{') {
1609 expect("'{'");
1610 return;
1612 next(); // skip '{'
1613 first_regset_register = asm_parse_vfp_regvar(tok, 1);
1614 if ((first_regset_register = asm_parse_vfp_regvar(tok, 1)) != -1) {
1615 coprocessor = CP_DOUBLE_PRECISION_FLOAT;
1616 next();
1617 } else if ((first_regset_register = asm_parse_vfp_regvar(tok, 0)) != -1) {
1618 coprocessor = CP_SINGLE_PRECISION_FLOAT;
1619 next();
1620 } else {
1621 expect("floating-point register");
1622 return;
1625 if (tok == '-') {
1626 next();
1627 if ((last_regset_register = asm_parse_vfp_regvar(tok, coprocessor == CP_DOUBLE_PRECISION_FLOAT)) != -1)
1628 next();
1629 else {
1630 expect("floating-point register");
1631 return;
1633 } else
1634 last_regset_register = first_regset_register;
1636 if (last_regset_register < first_regset_register) {
1637 tcc_error("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
1638 return;
1640 if (tok != '}') {
1641 expect("'}'");
1642 return;
1644 next(); // skip '}'
1646 // Note: 0 (one down) is not implemented by us regardless.
1647 regset_item_count = last_regset_register - first_regset_register + 1;
1648 if (coprocessor == CP_DOUBLE_PRECISION_FLOAT)
1649 regset_item_count <<= 1;
1650 else {
1651 extra_register_bit = first_regset_register & 1;
1652 first_regset_register >>= 1;
1654 offset.type = OP_IM8;
1655 offset.e.v = regset_item_count << 2;
1656 switch (ARM_INSTRUCTION_GROUP(token)) {
1657 case TOK_ASM_vstmeq: // post-increment store
1658 case TOK_ASM_vstmiaeq: // post-increment store
1659 break;
1660 case TOK_ASM_vpopeq:
1661 case TOK_ASM_vldmeq: // post-increment load
1662 case TOK_ASM_vldmiaeq: // post-increment load
1663 load = 1;
1664 break;
1665 case TOK_ASM_vldmdbeq: // pre-decrement load
1666 load = 1;
1667 /* fallthrough */
1668 case TOK_ASM_vpusheq:
1669 case TOK_ASM_vstmdbeq: // pre-decrement store
1670 offset.type = OP_IM8N;
1671 offset.e.v = -offset.e.v;
1672 preincrement = 1;
1673 break;
1674 default:
1675 expect("floating point block data transfer instruction");
1676 return;
1678 if (ops[0].type != OP_REG32)
1679 expect("(first operand) register");
1680 else if (ops[0].reg == 15)
1681 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL));
1682 else if (!op0_exclam && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vldmeq && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vldmiaeq && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vstmeq && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vstmiaeq)
1683 tcc_error("first operand of '%s' should have an exclamation mark", get_tok_str(token, NULL));
1684 else
1685 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, first_regset_register, &ops[0], &offset, 0, preincrement, op0_exclam, extra_register_bit, load);
1688 #define VMOV_FRACTIONAL_DIGITS 7
1689 #define VMOV_ONE 10000000 /* pow(10, VMOV_FRACTIONAL_DIGITS) */
1691 static uint32_t vmov_parse_fractional_part(const char* s)
1693 uint32_t result = 0;
1694 int i;
1695 for (i = 0; i < VMOV_FRACTIONAL_DIGITS; ++i) {
1696 char c = *s;
1697 result *= 10;
1698 if (c >= '0' && c <= '9') {
1699 result += (c - '0');
1700 ++s;
1703 if (*s)
1704 expect("decimal numeral");
1705 return result;
1708 static int vmov_linear_approx_index(uint32_t beginning, uint32_t end, uint32_t value)
1710 int i;
1711 uint32_t k;
1712 uint32_t xvalue;
1714 k = (end - beginning)/16;
1715 for (xvalue = beginning, i = 0; i < 16; ++i, xvalue += k) {
1716 if (value == xvalue)
1717 return i;
1719 //assert(0);
1720 return -1;
1723 static uint32_t vmov_parse_immediate_value() {
1724 uint32_t value;
1725 unsigned long integral_value;
1726 const char *p;
1728 if (tok != TOK_PPNUM) {
1729 expect("immediate value");
1730 return 0;
1732 p = tokc.str.data;
1733 errno = 0;
1734 integral_value = strtoul(p, (char **)&p, 0);
1736 if (errno || integral_value >= 32) {
1737 tcc_error("invalid floating-point immediate value");
1738 return 0;
1741 value = (uint32_t) integral_value * VMOV_ONE;
1742 if (*p == '.') {
1743 ++p;
1744 value += vmov_parse_fractional_part(p);
1746 next();
1747 return value;
1750 static uint8_t vmov_encode_immediate_value(uint32_t value)
1752 uint32_t limit;
1753 uint32_t end = 0;
1754 uint32_t beginning = 0;
1755 int r = -1;
1756 int n;
1757 int i;
1759 limit = 32 * VMOV_ONE;
1760 for (i = 0; i < 8; ++i) {
1761 if (value < limit) {
1762 end = limit;
1763 limit >>= 1;
1764 beginning = limit;
1765 r = i;
1766 } else
1767 limit >>= 1;
1769 if (r == -1 || value < beginning || value > end) {
1770 tcc_error("invalid decimal number for vmov: %d", value);
1771 return 0;
1773 n = vmov_linear_approx_index(beginning, end, value);
1774 return n | (((3 - r) & 0x7) << 4);
1777 // Not standalone.
1778 static void asm_floating_point_immediate_data_processing_opcode_tail(TCCState *s1, int token, uint8_t coprocessor, uint8_t CRd) {
1779 uint8_t opcode1 = 0;
1780 uint8_t opcode2 = 0;
1781 uint8_t operands[3] = {0, 0, 0};
1782 uint32_t immediate_value = 0;
1783 int op_minus = 0;
1784 uint8_t code;
1786 operands[0] = CRd;
1788 if (tok == '#' || tok == '$') {
1789 next();
1791 if (tok == '-') {
1792 op_minus = 1;
1793 next();
1795 immediate_value = vmov_parse_immediate_value();
1797 opcode1 = 11; // "Other" instruction
1798 switch (ARM_INSTRUCTION_GROUP(token)) {
1799 case TOK_ASM_vcmpeq_f32:
1800 case TOK_ASM_vcmpeq_f64:
1801 opcode2 = 2;
1802 operands[1] = 5;
1803 if (immediate_value) {
1804 expect("Immediate value 0");
1805 return;
1807 break;
1808 case TOK_ASM_vcmpeeq_f32:
1809 case TOK_ASM_vcmpeeq_f64:
1810 opcode2 = 6;
1811 operands[1] = 5;
1812 if (immediate_value) {
1813 expect("Immediate value 0");
1814 return;
1816 break;
1817 case TOK_ASM_vmoveq_f32:
1818 case TOK_ASM_vmoveq_f64:
1819 opcode2 = 0;
1820 if (op_minus)
1821 operands[1] = 0x8;
1822 else
1823 operands[1] = 0x0;
1824 code = vmov_encode_immediate_value(immediate_value);
1825 operands[1] |= code >> 4;
1826 operands[2] = code & 0xF;
1827 break;
1828 default:
1829 expect("known floating point with immediate instruction");
1830 return;
1833 if (coprocessor == CP_SINGLE_PRECISION_FLOAT) {
1834 if (operands[0] & 1)
1835 opcode1 |= 4;
1836 operands[0] >>= 1;
1839 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, operands[0], operands[1], operands[2], opcode2, 0);
1842 static void asm_floating_point_reg_arm_reg_transfer_opcode_tail(TCCState *s1, int token, int coprocessor, int nb_arm_regs, int nb_ops, Operand ops[3]) {
1843 uint8_t opcode1 = 0;
1844 uint8_t opcode2 = 0;
1845 switch (coprocessor) {
1846 case CP_SINGLE_PRECISION_FLOAT:
1847 // "vmov.f32 r2, s3" or "vmov.f32 s3, r2"
1848 if (nb_ops != 2 || nb_arm_regs != 1) {
1849 tcc_error("vmov.f32 only implemented for one VFP register operand and one ARM register operands");
1850 return;
1852 if (ops[0].type != OP_REG32) { // determine mode: load or store
1853 // need to swap operands 0 and 1
1854 memcpy(&ops[2], &ops[1], sizeof(ops[2]));
1855 memcpy(&ops[1], &ops[0], sizeof(ops[1]));
1856 memcpy(&ops[0], &ops[2], sizeof(ops[0]));
1857 } else
1858 opcode1 |= 1;
1860 if (ops[1].type == OP_VREG32) {
1861 if (ops[1].reg & 1)
1862 opcode2 |= 4;
1863 ops[1].reg >>= 1;
1866 if (ops[0].type == OP_VREG32) {
1867 if (ops[0].reg & 1)
1868 opcode1 |= 4;
1869 ops[0].reg >>= 1;
1872 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, ops[0].reg, (ops[1].type == OP_IM8) ? ops[1].e.v : ops[1].reg, 0x10, opcode2, 0);
1873 break;
1874 case CP_DOUBLE_PRECISION_FLOAT:
1875 if (nb_ops != 3 || nb_arm_regs != 2) {
1876 tcc_error("vmov.f32 only implemented for one VFP register operand and two ARM register operands");
1877 return;
1879 // Determine whether it's a store into a VFP register (vmov "d1, r2, r3") rather than "vmov r2, r3, d1"
1880 if (ops[0].type == OP_VREG64) {
1881 if (ops[2].type == OP_REG32) {
1882 Operand temp;
1883 // need to rotate operand list to the left
1884 memcpy(&temp, &ops[0], sizeof(temp));
1885 memcpy(&ops[0], &ops[1], sizeof(ops[0]));
1886 memcpy(&ops[1], &ops[2], sizeof(ops[1]));
1887 memcpy(&ops[2], &temp, sizeof(ops[2]));
1888 } else {
1889 tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands");
1890 return;
1892 } else if (ops[0].type != OP_REG32 || ops[1].type != OP_REG32 || ops[2].type != OP_VREG64) {
1893 tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands");
1894 return;
1895 } else {
1896 opcode1 |= 1;
1898 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, ops[0].reg, &ops[1], &ops[2], 0, 0, 0, 1, opcode1);
1899 break;
1900 default:
1901 tcc_internal_error("unknown coprocessor");
1905 static void asm_floating_point_vcvt_data_processing_opcode(TCCState *s1, int token) {
1906 uint8_t coprocessor = 0;
1907 Operand ops[3];
1908 uint8_t opcode1 = 11;
1909 uint8_t opcode2 = 2;
1911 switch (ARM_INSTRUCTION_GROUP(token)) {
1912 case TOK_ASM_vcvtreq_s32_f64:
1913 case TOK_ASM_vcvtreq_u32_f64:
1914 case TOK_ASM_vcvteq_s32_f64:
1915 case TOK_ASM_vcvteq_u32_f64:
1916 case TOK_ASM_vcvteq_f64_s32:
1917 case TOK_ASM_vcvteq_f64_u32:
1918 case TOK_ASM_vcvteq_f32_f64:
1919 coprocessor = CP_DOUBLE_PRECISION_FLOAT;
1920 break;
1921 case TOK_ASM_vcvtreq_s32_f32:
1922 case TOK_ASM_vcvtreq_u32_f32:
1923 case TOK_ASM_vcvteq_s32_f32:
1924 case TOK_ASM_vcvteq_u32_f32:
1925 case TOK_ASM_vcvteq_f32_s32:
1926 case TOK_ASM_vcvteq_f32_u32:
1927 case TOK_ASM_vcvteq_f64_f32:
1928 coprocessor = CP_SINGLE_PRECISION_FLOAT;
1929 break;
1930 default:
1931 tcc_error("Unknown coprocessor for instruction '%s'", get_tok_str(token, NULL));
1932 return;
1935 parse_operand(s1, &ops[0]);
1936 ops[1].type = OP_IM8;
1937 ops[1].e.v = 8;
1938 /* floating-point -> integer */
1939 switch (ARM_INSTRUCTION_GROUP(token)) {
1940 case TOK_ASM_vcvtreq_s32_f32:
1941 case TOK_ASM_vcvtreq_s32_f64:
1942 case TOK_ASM_vcvteq_s32_f32:
1943 case TOK_ASM_vcvteq_s32_f64:
1944 ops[1].e.v |= 1; // signed
1945 /* fall through */
1946 case TOK_ASM_vcvteq_u32_f32:
1947 case TOK_ASM_vcvteq_u32_f64:
1948 case TOK_ASM_vcvtreq_u32_f32:
1949 case TOK_ASM_vcvtreq_u32_f64:
1950 ops[1].e.v |= 4; // to_integer (opc2)
1951 break;
1952 /* floating-point size conversion */
1953 case TOK_ASM_vcvteq_f64_f32:
1954 case TOK_ASM_vcvteq_f32_f64:
1955 ops[1].e.v = 7;
1956 break;
1959 if (tok == ',')
1960 next();
1961 else
1962 expect("','");
1963 parse_operand(s1, &ops[2]);
1965 switch (ARM_INSTRUCTION_GROUP(token)) {
1966 /* floating-point -> integer */
1967 case TOK_ASM_vcvteq_s32_f32:
1968 case TOK_ASM_vcvteq_s32_f64:
1969 case TOK_ASM_vcvteq_u32_f32:
1970 case TOK_ASM_vcvteq_u32_f64:
1971 opcode2 |= 4; // round_zero
1972 break;
1974 /* integer -> floating-point */
1975 case TOK_ASM_vcvteq_f64_s32:
1976 case TOK_ASM_vcvteq_f32_s32:
1977 opcode2 |= 4; // signed--special
1978 break;
1980 /* floating-point size conversion */
1981 case TOK_ASM_vcvteq_f64_f32:
1982 case TOK_ASM_vcvteq_f32_f64:
1983 opcode2 |= 4; // always set
1984 break;
1987 switch (ARM_INSTRUCTION_GROUP(token)) {
1988 case TOK_ASM_vcvteq_f64_u32:
1989 case TOK_ASM_vcvteq_f64_s32:
1990 case TOK_ASM_vcvteq_f64_f32:
1991 if (ops[0].type == OP_VREG64 && ops[2].type == OP_VREG32) {
1992 } else {
1993 expect("d<number>, s<number>");
1994 return;
1996 break;
1997 default:
1998 if (coprocessor == CP_SINGLE_PRECISION_FLOAT) {
1999 if (ops[0].type == OP_VREG32 && ops[2].type == OP_VREG32) {
2000 } else {
2001 expect("s<number>, s<number>");
2002 return;
2004 } else if (coprocessor == CP_DOUBLE_PRECISION_FLOAT) {
2005 if (ops[0].type == OP_VREG32 && ops[2].type == OP_VREG64) {
2006 } else {
2007 expect("s<number>, d<number>");
2008 return;
2013 if (ops[2].type == OP_VREG32) {
2014 if (ops[2].reg & 1)
2015 opcode2 |= 1;
2016 ops[2].reg >>= 1;
2018 if (ops[0].type == OP_VREG32) {
2019 if (ops[0].reg & 1)
2020 opcode1 |= 4;
2021 ops[0].reg >>= 1;
2023 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, ops[0].reg, (ops[1].type == OP_IM8) ? ops[1].e.v : ops[1].reg, (ops[2].type == OP_IM8) ? ops[2].e.v : ops[2].reg, opcode2, 0);
2026 static void asm_floating_point_data_processing_opcode(TCCState *s1, int token) {
2027 uint8_t coprocessor = CP_SINGLE_PRECISION_FLOAT;
2028 uint8_t opcode1 = 0;
2029 uint8_t opcode2 = 0; // (0 || 2) | register selection
2030 Operand ops[3];
2031 uint8_t nb_ops = 0;
2032 int vmov = 0;
2033 int nb_arm_regs = 0;
2035 /* TODO:
2036 Instruction opcode opcode2 Reason
2037 =============================================================
2038 - 1?00 ?1? Undefined
2039 VFNMS 1?01 ?0? Must be unconditional
2040 VFNMA 1?01 ?1? Must be unconditional
2041 VFMA 1?10 ?0? Must be unconditional
2042 VFMS 1?10 ?1? Must be unconditional
2044 VMOV Fd, Fm
2045 VMOV Sn, Sm, Rd, Rn
2046 VMOV Rd, Rn, Sn, Sm
2047 VMOV Dn[0], Rd
2048 VMOV Rd, Dn[0]
2049 VMOV Dn[1], Rd
2050 VMOV Rd, Dn[1]
2053 switch (ARM_INSTRUCTION_GROUP(token)) {
2054 case TOK_ASM_vmlaeq_f64:
2055 case TOK_ASM_vmlseq_f64:
2056 case TOK_ASM_vnmlseq_f64:
2057 case TOK_ASM_vnmlaeq_f64:
2058 case TOK_ASM_vmuleq_f64:
2059 case TOK_ASM_vnmuleq_f64:
2060 case TOK_ASM_vaddeq_f64:
2061 case TOK_ASM_vsubeq_f64:
2062 case TOK_ASM_vdiveq_f64:
2063 case TOK_ASM_vnegeq_f64:
2064 case TOK_ASM_vabseq_f64:
2065 case TOK_ASM_vsqrteq_f64:
2066 case TOK_ASM_vcmpeq_f64:
2067 case TOK_ASM_vcmpeeq_f64:
2068 case TOK_ASM_vmoveq_f64:
2069 coprocessor = CP_DOUBLE_PRECISION_FLOAT;
2072 switch (ARM_INSTRUCTION_GROUP(token)) {
2073 case TOK_ASM_vmoveq_f32:
2074 case TOK_ASM_vmoveq_f64:
2075 vmov = 1;
2076 break;
2079 for (nb_ops = 0; nb_ops < 3; ) {
2080 // Note: Necessary because parse_operand can't parse decimal numerals.
2081 if (nb_ops == 1 && (tok == '#' || tok == '$' || tok == TOK_PPNUM || tok == '-')) {
2082 asm_floating_point_immediate_data_processing_opcode_tail(s1, token, coprocessor, ops[0].reg);
2083 return;
2085 parse_operand(s1, &ops[nb_ops]);
2086 if (vmov && ops[nb_ops].type == OP_REG32) {
2087 ++nb_arm_regs;
2088 } else if (ops[nb_ops].type == OP_VREG32) {
2089 if (coprocessor != CP_SINGLE_PRECISION_FLOAT) {
2090 expect("'s<number>'");
2091 return;
2093 } else if (ops[nb_ops].type == OP_VREG64) {
2094 if (coprocessor != CP_DOUBLE_PRECISION_FLOAT) {
2095 expect("'d<number>'");
2096 return;
2098 } else {
2099 expect("floating point register");
2100 return;
2102 ++nb_ops;
2103 if (tok == ',')
2104 next();
2105 else
2106 break;
2109 if (nb_arm_regs == 0) {
2110 if (nb_ops == 2) { // implicit
2111 memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2]
2112 memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit
2113 nb_ops = 3;
2115 if (nb_ops < 3) {
2116 tcc_error("Not enough operands for '%s' (%u)", get_tok_str(token, NULL), nb_ops);
2117 return;
2121 switch (ARM_INSTRUCTION_GROUP(token)) {
2122 case TOK_ASM_vmlaeq_f32:
2123 case TOK_ASM_vmlaeq_f64:
2124 opcode1 = 0;
2125 opcode2 = 0;
2126 break;
2127 case TOK_ASM_vmlseq_f32:
2128 case TOK_ASM_vmlseq_f64:
2129 opcode1 = 0;
2130 opcode2 = 2;
2131 break;
2132 case TOK_ASM_vnmlseq_f32:
2133 case TOK_ASM_vnmlseq_f64:
2134 opcode1 = 1;
2135 opcode2 = 0;
2136 break;
2137 case TOK_ASM_vnmlaeq_f32:
2138 case TOK_ASM_vnmlaeq_f64:
2139 opcode1 = 1;
2140 opcode2 = 2;
2141 break;
2142 case TOK_ASM_vmuleq_f32:
2143 case TOK_ASM_vmuleq_f64:
2144 opcode1 = 2;
2145 opcode2 = 0;
2146 break;
2147 case TOK_ASM_vnmuleq_f32:
2148 case TOK_ASM_vnmuleq_f64:
2149 opcode1 = 2;
2150 opcode2 = 2;
2151 break;
2152 case TOK_ASM_vaddeq_f32:
2153 case TOK_ASM_vaddeq_f64:
2154 opcode1 = 3;
2155 opcode2 = 0;
2156 break;
2157 case TOK_ASM_vsubeq_f32:
2158 case TOK_ASM_vsubeq_f64:
2159 opcode1 = 3;
2160 opcode2 = 2;
2161 break;
2162 case TOK_ASM_vdiveq_f32:
2163 case TOK_ASM_vdiveq_f64:
2164 opcode1 = 8;
2165 opcode2 = 0;
2166 break;
2167 case TOK_ASM_vnegeq_f32:
2168 case TOK_ASM_vnegeq_f64:
2169 opcode1 = 11; // Other" instruction
2170 opcode2 = 2;
2171 ops[1].type = OP_IM8;
2172 ops[1].e.v = 1;
2173 break;
2174 case TOK_ASM_vabseq_f32:
2175 case TOK_ASM_vabseq_f64:
2176 opcode1 = 11; // "Other" instruction
2177 opcode2 = 6;
2178 ops[1].type = OP_IM8;
2179 ops[1].e.v = 0;
2180 break;
2181 case TOK_ASM_vsqrteq_f32:
2182 case TOK_ASM_vsqrteq_f64:
2183 opcode1 = 11; // "Other" instruction
2184 opcode2 = 6;
2185 ops[1].type = OP_IM8;
2186 ops[1].e.v = 1;
2187 break;
2188 case TOK_ASM_vcmpeq_f32:
2189 case TOK_ASM_vcmpeq_f64:
2190 opcode1 = 11; // "Other" instruction
2191 opcode2 = 2;
2192 ops[1].type = OP_IM8;
2193 ops[1].e.v = 4;
2194 break;
2195 case TOK_ASM_vcmpeeq_f32:
2196 case TOK_ASM_vcmpeeq_f64:
2197 opcode1 = 11; // "Other" instruction
2198 opcode2 = 6;
2199 ops[1].type = OP_IM8;
2200 ops[1].e.v = 4;
2201 break;
2202 case TOK_ASM_vmoveq_f32:
2203 case TOK_ASM_vmoveq_f64:
2204 if (nb_arm_regs > 0) { // vmov.f32 r2, s3 or similar
2205 asm_floating_point_reg_arm_reg_transfer_opcode_tail(s1, token, coprocessor, nb_arm_regs, nb_ops, ops);
2206 return;
2207 } else {
2208 opcode1 = 11; // "Other" instruction
2209 opcode2 = 2;
2210 ops[1].type = OP_IM8;
2211 ops[1].e.v = 0;
2213 break;
2214 default:
2215 expect("known floating point instruction");
2216 return;
2219 if (coprocessor == CP_SINGLE_PRECISION_FLOAT) {
2220 if (ops[2].type == OP_VREG32) {
2221 if (ops[2].reg & 1)
2222 opcode2 |= 1;
2223 ops[2].reg >>= 1;
2226 if (ops[1].type == OP_VREG32) {
2227 if (ops[1].reg & 1)
2228 opcode2 |= 4;
2229 ops[1].reg >>= 1;
2232 if (ops[0].type == OP_VREG32) {
2233 if (ops[0].reg & 1)
2234 opcode1 |= 4;
2235 ops[0].reg >>= 1;
2239 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, ops[0].reg, (ops[1].type == OP_IM8) ? ops[1].e.v : ops[1].reg, (ops[2].type == OP_IM8) ? ops[2].e.v : ops[2].reg, opcode2, 0);
2242 static void asm_floating_point_status_register_opcode(TCCState* s1, int token)
2244 uint8_t coprocessor = CP_SINGLE_PRECISION_FLOAT;
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 */