Long double Constant problem
[tinycc.git] / arm-asm.c
blob5cc212dac581d866b3258c9ee7990c77655fa13b
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? TODO
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_data_processing_opcode(TCCState *s1, int token) {
1906 uint8_t coprocessor = CP_SINGLE_PRECISION_FLOAT;
1907 uint8_t opcode1 = 0;
1908 uint8_t opcode2 = 0; // (0 || 2) | register selection
1909 Operand ops[3];
1910 uint8_t nb_ops = 0;
1911 int vmov = 0;
1912 int nb_arm_regs = 0;
1914 /* TODO:
1915 Instruction opcode opcode2 Reason
1916 =============================================================
1917 - 1?00 ?1? Undefined
1918 VFNMS 1?01 ?0? Must be unconditional
1919 VFNMA 1?01 ?1? Must be unconditional
1920 VFMA 1?10 ?0? Must be unconditional
1921 VFMS 1?10 ?1? Must be unconditional
1923 VCVT*
1925 VMOV Fd, Fm
1926 VMOV Sn, Sm, Rd, Rn
1927 VMOV Rd, Rn, Sn, Sm
1928 VMOV Dn[0], Rd
1929 VMOV Rd, Dn[0]
1930 VMOV Dn[1], Rd
1931 VMOV Rd, Dn[1]
1934 switch (ARM_INSTRUCTION_GROUP(token)) {
1935 case TOK_ASM_vmlaeq_f64:
1936 case TOK_ASM_vmlseq_f64:
1937 case TOK_ASM_vnmlseq_f64:
1938 case TOK_ASM_vnmlaeq_f64:
1939 case TOK_ASM_vmuleq_f64:
1940 case TOK_ASM_vnmuleq_f64:
1941 case TOK_ASM_vaddeq_f64:
1942 case TOK_ASM_vsubeq_f64:
1943 case TOK_ASM_vdiveq_f64:
1944 case TOK_ASM_vnegeq_f64:
1945 case TOK_ASM_vabseq_f64:
1946 case TOK_ASM_vsqrteq_f64:
1947 case TOK_ASM_vcmpeq_f64:
1948 case TOK_ASM_vcmpeeq_f64:
1949 case TOK_ASM_vmoveq_f64:
1950 coprocessor = CP_DOUBLE_PRECISION_FLOAT;
1953 switch (ARM_INSTRUCTION_GROUP(token)) {
1954 case TOK_ASM_vmoveq_f32:
1955 case TOK_ASM_vmoveq_f64:
1956 vmov = 1;
1957 break;
1960 for (nb_ops = 0; nb_ops < 3; ) {
1961 // Note: Necessary because parse_operand can't parse decimal numerals.
1962 if (nb_ops == 1 && (tok == '#' || tok == '$' || tok == TOK_PPNUM || tok == '-')) {
1963 asm_floating_point_immediate_data_processing_opcode_tail(s1, token, coprocessor, ops[0].reg);
1964 return;
1966 parse_operand(s1, &ops[nb_ops]);
1967 if (vmov && ops[nb_ops].type == OP_REG32) {
1968 ++nb_arm_regs;
1969 } else if (ops[nb_ops].type == OP_VREG32) {
1970 if (coprocessor != CP_SINGLE_PRECISION_FLOAT) {
1971 expect("'s<number>'");
1972 return;
1974 } else if (ops[nb_ops].type == OP_VREG64) {
1975 if (coprocessor != CP_DOUBLE_PRECISION_FLOAT) {
1976 expect("'d<number>'");
1977 return;
1979 } else {
1980 expect("floating point register");
1981 return;
1983 ++nb_ops;
1984 if (tok == ',')
1985 next();
1986 else
1987 break;
1990 if (nb_arm_regs == 0) {
1991 if (nb_ops == 2) { // implicit
1992 memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2]
1993 memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit
1994 nb_ops = 3;
1996 if (nb_ops < 3) {
1997 tcc_error("Not enough operands for '%s' (%u)", get_tok_str(token, NULL), nb_ops);
1998 return;
2002 switch (ARM_INSTRUCTION_GROUP(token)) {
2003 case TOK_ASM_vmlaeq_f32:
2004 case TOK_ASM_vmlaeq_f64:
2005 opcode1 = 0;
2006 opcode2 = 0;
2007 break;
2008 case TOK_ASM_vmlseq_f32:
2009 case TOK_ASM_vmlseq_f64:
2010 opcode1 = 0;
2011 opcode2 = 2;
2012 break;
2013 case TOK_ASM_vnmlseq_f32:
2014 case TOK_ASM_vnmlseq_f64:
2015 opcode1 = 1;
2016 opcode2 = 0;
2017 break;
2018 case TOK_ASM_vnmlaeq_f32:
2019 case TOK_ASM_vnmlaeq_f64:
2020 opcode1 = 1;
2021 opcode2 = 2;
2022 break;
2023 case TOK_ASM_vmuleq_f32:
2024 case TOK_ASM_vmuleq_f64:
2025 opcode1 = 2;
2026 opcode2 = 0;
2027 break;
2028 case TOK_ASM_vnmuleq_f32:
2029 case TOK_ASM_vnmuleq_f64:
2030 opcode1 = 2;
2031 opcode2 = 2;
2032 break;
2033 case TOK_ASM_vaddeq_f32:
2034 case TOK_ASM_vaddeq_f64:
2035 opcode1 = 3;
2036 opcode2 = 0;
2037 break;
2038 case TOK_ASM_vsubeq_f32:
2039 case TOK_ASM_vsubeq_f64:
2040 opcode1 = 3;
2041 opcode2 = 2;
2042 break;
2043 case TOK_ASM_vdiveq_f32:
2044 case TOK_ASM_vdiveq_f64:
2045 opcode1 = 8;
2046 opcode2 = 0;
2047 break;
2048 case TOK_ASM_vnegeq_f32:
2049 case TOK_ASM_vnegeq_f64:
2050 opcode1 = 11; // Other" instruction
2051 opcode2 = 2;
2052 ops[1].type = OP_IM8;
2053 ops[1].e.v = 1;
2054 break;
2055 case TOK_ASM_vabseq_f32:
2056 case TOK_ASM_vabseq_f64:
2057 opcode1 = 11; // "Other" instruction
2058 opcode2 = 6;
2059 ops[1].type = OP_IM8;
2060 ops[1].e.v = 0;
2061 break;
2062 case TOK_ASM_vsqrteq_f32:
2063 case TOK_ASM_vsqrteq_f64:
2064 opcode1 = 11; // "Other" instruction
2065 opcode2 = 6;
2066 ops[1].type = OP_IM8;
2067 ops[1].e.v = 1;
2068 break;
2069 case TOK_ASM_vcmpeq_f32:
2070 case TOK_ASM_vcmpeq_f64:
2071 opcode1 = 11; // "Other" instruction
2072 opcode2 = 2;
2073 ops[1].type = OP_IM8;
2074 ops[1].e.v = 4;
2075 break;
2076 case TOK_ASM_vcmpeeq_f32:
2077 case TOK_ASM_vcmpeeq_f64:
2078 opcode1 = 11; // "Other" instruction
2079 opcode2 = 6;
2080 ops[1].type = OP_IM8;
2081 ops[1].e.v = 4;
2082 break;
2083 case TOK_ASM_vmoveq_f32:
2084 case TOK_ASM_vmoveq_f64:
2085 if (nb_arm_regs > 0) { // vmov.f32 r2, s3 or similar
2086 asm_floating_point_reg_arm_reg_transfer_opcode_tail(s1, token, coprocessor, nb_arm_regs, nb_ops, ops);
2087 return;
2088 } else {
2089 opcode1 = 11; // "Other" instruction
2090 opcode2 = 2;
2091 ops[1].type = OP_IM8;
2092 ops[1].e.v = 0;
2094 break;
2095 // TODO: vcvt; vcvtr
2096 default:
2097 expect("known floating point instruction");
2098 return;
2101 if (coprocessor == CP_SINGLE_PRECISION_FLOAT) {
2102 if (ops[2].type == OP_VREG32) {
2103 if (ops[2].reg & 1)
2104 opcode2 |= 1;
2105 ops[2].reg >>= 1;
2108 if (ops[1].type == OP_VREG32) {
2109 if (ops[1].reg & 1)
2110 opcode2 |= 4;
2111 ops[1].reg >>= 1;
2114 if (ops[0].type == OP_VREG32) {
2115 if (ops[0].reg & 1)
2116 opcode1 |= 4;
2117 ops[0].reg >>= 1;
2121 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, ops[0].reg, (ops[1].type == OP_IM8) ? ops[1].e.v : ops[1].reg, (ops[2].type == OP_IM8) ? ops[2].e.v : ops[2].reg, opcode2, 0);
2124 static void asm_floating_point_status_register_opcode(TCCState* s1, int token)
2126 uint8_t coprocessor = CP_SINGLE_PRECISION_FLOAT;
2127 uint8_t opcode;
2128 int vfp_sys_reg = -1;
2129 Operand arm_operand;
2130 switch (ARM_INSTRUCTION_GROUP(token)) {
2131 case TOK_ASM_vmrseq:
2132 opcode = 0xf;
2133 if (tok == TOK_ASM_apsr_nzcv) {
2134 arm_operand.type = OP_REG32;
2135 arm_operand.reg = 15; // not PC
2136 next(); // skip apsr_nzcv
2137 } else {
2138 parse_operand(s1, &arm_operand);
2139 if (arm_operand.type == OP_REG32 && arm_operand.reg == 15) {
2140 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL));
2141 return;
2145 if (tok != ',')
2146 expect("','");
2147 else
2148 next(); // skip ','
2149 vfp_sys_reg = asm_parse_vfp_status_regvar(tok);
2150 next(); // skip vfp sys reg
2151 if (arm_operand.type == OP_REG32 && arm_operand.reg == 15 && vfp_sys_reg != 1) {
2152 tcc_error("'%s' only supports the variant 'vmrs apsr_nzcv, fpscr' here", get_tok_str(token, NULL));
2153 return;
2155 break;
2156 case TOK_ASM_vmsreq:
2157 opcode = 0xe;
2158 vfp_sys_reg = asm_parse_vfp_status_regvar(tok);
2159 next(); // skip vfp sys reg
2160 if (tok != ',')
2161 expect("','");
2162 else
2163 next(); // skip ','
2164 parse_operand(s1, &arm_operand);
2165 if (arm_operand.type == OP_REG32 && arm_operand.reg == 15) {
2166 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL));
2167 return;
2169 break;
2170 default:
2171 expect("floating point status register instruction");
2172 return;
2174 if (vfp_sys_reg == -1) {
2175 expect("VFP system register");
2176 return;
2178 if (arm_operand.type != OP_REG32) {
2179 expect("ARM register");
2180 return;
2182 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode, arm_operand.reg, vfp_sys_reg, 0x10, 0, 0);
2185 #endif
2187 static void asm_misc_single_data_transfer_opcode(TCCState *s1, int token)
2189 Operand ops[3];
2190 int exclam = 0;
2191 int closed_bracket = 0;
2192 int op2_minus = 0;
2193 uint32_t opcode = (1 << 7) | (1 << 4);
2195 /* Note:
2196 The argument syntax is exactly the same as in arm_single_data_transfer_opcode, except that there's no STREX argument form.
2197 The main difference between this function and asm_misc_single_data_transfer_opcode is that the immediate values here must be smaller.
2198 Also, the combination (P=0, W=1) is unpredictable here.
2199 The immediate flag has moved to bit index 22--and its meaning has flipped.
2200 The immediate value itself has been split into two parts: one at bits 11...8, one at bits 3...0
2201 bit 26 (Load/Store instruction) is unset here.
2202 bits 7 and 4 are set here. */
2204 // Here: 0 0 0 P U I W L << 20
2205 // [compare single data transfer: 0 1 I P U B W L << 20]
2207 parse_operand(s1, &ops[0]);
2208 if (ops[0].type == OP_REG32)
2209 opcode |= ENCODE_RD(ops[0].reg);
2210 else {
2211 expect("(destination operand) register");
2212 return;
2214 if (tok != ',')
2215 expect("at least two arguments");
2216 else
2217 next(); // skip ','
2219 if (tok != '[')
2220 expect("'['");
2221 else
2222 next(); // skip '['
2224 parse_operand(s1, &ops[1]);
2225 if (ops[1].type == OP_REG32)
2226 opcode |= ENCODE_RN(ops[1].reg);
2227 else {
2228 expect("(first source operand) register");
2229 return;
2231 if (tok == ']') {
2232 next();
2233 closed_bracket = 1;
2234 // exclam = 1; // implicit in hardware; don't do it in software
2236 if (tok == ',') {
2237 next(); // skip ','
2238 if (tok == '-') {
2239 op2_minus = 1;
2240 next();
2242 parse_operand(s1, &ops[2]);
2243 } else {
2244 // end of input expression in brackets--assume 0 offset
2245 ops[2].type = OP_IM8;
2246 ops[2].e.v = 0;
2247 opcode |= 1 << 24; // add offset before transfer
2249 if (!closed_bracket) {
2250 if (tok != ']')
2251 expect("']'");
2252 else
2253 next(); // skip ']'
2254 opcode |= 1 << 24; // add offset before transfer
2255 if (tok == '!') {
2256 exclam = 1;
2257 next(); // skip '!'
2261 if (exclam) {
2262 if ((opcode & (1 << 24)) == 0) {
2263 tcc_error("result of '%s' would be unpredictable here", get_tok_str(token, NULL));
2264 return;
2266 opcode |= 1 << 21; // write offset back into register
2269 if (ops[2].type == OP_IM32 || ops[2].type == OP_IM8 || ops[2].type == OP_IM8N) {
2270 int v = ops[2].e.v;
2271 if (op2_minus)
2272 tcc_error("minus before '#' not supported for immediate values");
2273 if (v >= 0) {
2274 opcode |= 1 << 23; // up
2275 if (v >= 0x100)
2276 tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
2277 else {
2278 // bits 11...8: immediate hi nibble
2279 // bits 3...0: immediate lo nibble
2280 opcode |= (v & 0xF0) << 4;
2281 opcode |= v & 0xF;
2283 } else { // down
2284 if (v <= -0x100)
2285 tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
2286 else {
2287 v = -v;
2288 // bits 11...8: immediate hi nibble
2289 // bits 3...0: immediate lo nibble
2290 opcode |= (v & 0xF0) << 4;
2291 opcode |= v & 0xF;
2294 opcode |= 1 << 22; // not ENCODE_IMMEDIATE_FLAG;
2295 } else if (ops[2].type == OP_REG32) {
2296 if (!op2_minus)
2297 opcode |= 1 << 23; // up
2298 opcode |= ops[2].reg;
2299 } else
2300 expect("register");
2302 switch (ARM_INSTRUCTION_GROUP(token)) {
2303 case TOK_ASM_ldrsheq:
2304 opcode |= 1 << 5; // halfword, not byte
2305 /* fallthrough */
2306 case TOK_ASM_ldrsbeq:
2307 opcode |= 1 << 6; // sign extend
2308 opcode |= 1 << 20; // L
2309 asm_emit_opcode(token, opcode);
2310 break;
2311 case TOK_ASM_ldrheq:
2312 opcode |= 1 << 5; // halfword, not byte
2313 opcode |= 1 << 20; // L
2314 asm_emit_opcode(token, opcode);
2315 break;
2316 case TOK_ASM_strheq:
2317 opcode |= 1 << 5; // halfword, not byte
2318 asm_emit_opcode(token, opcode);
2319 break;
2323 /* Note: almost dupe of encbranch in arm-gen.c */
2324 static uint32_t encbranchoffset(int pos, int addr, int fail)
2326 addr-=pos+8;
2327 addr/=4;
2328 if(addr>=0x7fffff || addr<-0x800000) {
2329 if(fail)
2330 tcc_error("branch offset is too far");
2331 return 0;
2333 return /*not 0x0A000000|*/(addr&0xffffff);
2336 static void asm_branch_opcode(TCCState *s1, int token)
2338 int jmp_disp = 0;
2339 Operand op;
2340 ExprValue e;
2341 ElfSym *esym;
2343 switch (ARM_INSTRUCTION_GROUP(token)) {
2344 case TOK_ASM_beq:
2345 case TOK_ASM_bleq:
2346 asm_expr(s1, &e);
2347 esym = elfsym(e.sym);
2348 if (!esym || esym->st_shndx != cur_text_section->sh_num) {
2349 tcc_error("invalid branch target");
2350 return;
2352 jmp_disp = encbranchoffset(ind, e.v + esym->st_value, 1);
2353 break;
2354 default:
2355 parse_operand(s1, &op);
2356 break;
2358 switch (ARM_INSTRUCTION_GROUP(token)) {
2359 case TOK_ASM_beq:
2360 asm_emit_opcode(token, (0xa << 24) | (jmp_disp & 0xffffff));
2361 break;
2362 case TOK_ASM_bleq:
2363 asm_emit_opcode(token, (0xb << 24) | (jmp_disp & 0xffffff));
2364 break;
2365 case TOK_ASM_bxeq:
2366 if (op.type != OP_REG32)
2367 expect("register");
2368 else
2369 asm_emit_opcode(token, (0x12fff1 << 4) | op.reg);
2370 break;
2371 case TOK_ASM_blxeq:
2372 if (op.type != OP_REG32)
2373 expect("register");
2374 else
2375 asm_emit_opcode(token, (0x12fff3 << 4) | op.reg);
2376 break;
2377 default:
2378 expect("branch instruction");
2382 ST_FUNC void asm_opcode(TCCState *s1, int token)
2384 while (token == TOK_LINEFEED) {
2385 next();
2386 token = tok;
2388 if (token == TOK_EOF)
2389 return;
2390 if (token < TOK_ASM_nopeq) { // no condition code
2391 switch (token) {
2392 case TOK_ASM_cdp2:
2393 asm_coprocessor_opcode(s1, token);
2394 return;
2395 case TOK_ASM_ldc2:
2396 case TOK_ASM_ldc2l:
2397 case TOK_ASM_stc2:
2398 case TOK_ASM_stc2l:
2399 asm_coprocessor_data_transfer_opcode(s1, token);
2400 return;
2401 default:
2402 expect("instruction");
2403 return;
2407 switch (ARM_INSTRUCTION_GROUP(token)) {
2408 case TOK_ASM_pusheq:
2409 case TOK_ASM_popeq:
2410 case TOK_ASM_stmdaeq:
2411 case TOK_ASM_ldmdaeq:
2412 case TOK_ASM_stmeq:
2413 case TOK_ASM_ldmeq:
2414 case TOK_ASM_stmiaeq:
2415 case TOK_ASM_ldmiaeq:
2416 case TOK_ASM_stmdbeq:
2417 case TOK_ASM_ldmdbeq:
2418 case TOK_ASM_stmibeq:
2419 case TOK_ASM_ldmibeq:
2420 asm_block_data_transfer_opcode(s1, token);
2421 return;
2422 case TOK_ASM_nopeq:
2423 case TOK_ASM_wfeeq:
2424 case TOK_ASM_wfieq:
2425 asm_nullary_opcode(token);
2426 return;
2427 case TOK_ASM_swieq:
2428 case TOK_ASM_svceq:
2429 asm_unary_opcode(s1, token);
2430 return;
2431 case TOK_ASM_beq:
2432 case TOK_ASM_bleq:
2433 case TOK_ASM_bxeq:
2434 case TOK_ASM_blxeq:
2435 asm_branch_opcode(s1, token);
2436 return;
2437 case TOK_ASM_clzeq:
2438 case TOK_ASM_sxtbeq:
2439 case TOK_ASM_sxtheq:
2440 case TOK_ASM_uxtbeq:
2441 case TOK_ASM_uxtheq:
2442 case TOK_ASM_movteq:
2443 case TOK_ASM_movweq:
2444 asm_binary_opcode(s1, token);
2445 return;
2447 case TOK_ASM_ldreq:
2448 case TOK_ASM_ldrbeq:
2449 case TOK_ASM_streq:
2450 case TOK_ASM_strbeq:
2451 case TOK_ASM_ldrexeq:
2452 case TOK_ASM_ldrexbeq:
2453 case TOK_ASM_strexeq:
2454 case TOK_ASM_strexbeq:
2455 asm_single_data_transfer_opcode(s1, token);
2456 return;
2458 case TOK_ASM_ldrheq:
2459 case TOK_ASM_ldrsheq:
2460 case TOK_ASM_ldrsbeq:
2461 case TOK_ASM_strheq:
2462 asm_misc_single_data_transfer_opcode(s1, token);
2463 return;
2465 case TOK_ASM_andeq:
2466 case TOK_ASM_eoreq:
2467 case TOK_ASM_subeq:
2468 case TOK_ASM_rsbeq:
2469 case TOK_ASM_addeq:
2470 case TOK_ASM_adceq:
2471 case TOK_ASM_sbceq:
2472 case TOK_ASM_rsceq:
2473 case TOK_ASM_tsteq:
2474 case TOK_ASM_teqeq:
2475 case TOK_ASM_cmpeq:
2476 case TOK_ASM_cmneq:
2477 case TOK_ASM_orreq:
2478 case TOK_ASM_moveq:
2479 case TOK_ASM_biceq:
2480 case TOK_ASM_mvneq:
2481 case TOK_ASM_andseq:
2482 case TOK_ASM_eorseq:
2483 case TOK_ASM_subseq:
2484 case TOK_ASM_rsbseq:
2485 case TOK_ASM_addseq:
2486 case TOK_ASM_adcseq:
2487 case TOK_ASM_sbcseq:
2488 case TOK_ASM_rscseq:
2489 // case TOK_ASM_tstseq:
2490 // case TOK_ASM_teqseq:
2491 // case TOK_ASM_cmpseq:
2492 // case TOK_ASM_cmnseq:
2493 case TOK_ASM_orrseq:
2494 case TOK_ASM_movseq:
2495 case TOK_ASM_bicseq:
2496 case TOK_ASM_mvnseq:
2497 asm_data_processing_opcode(s1, token);
2498 return;
2500 case TOK_ASM_lsleq:
2501 case TOK_ASM_lslseq:
2502 case TOK_ASM_lsreq:
2503 case TOK_ASM_lsrseq:
2504 case TOK_ASM_asreq:
2505 case TOK_ASM_asrseq:
2506 case TOK_ASM_roreq:
2507 case TOK_ASM_rorseq:
2508 case TOK_ASM_rrxseq:
2509 case TOK_ASM_rrxeq:
2510 asm_shift_opcode(s1, token);
2511 return;
2513 case TOK_ASM_muleq:
2514 case TOK_ASM_mulseq:
2515 case TOK_ASM_mlaeq:
2516 case TOK_ASM_mlaseq:
2517 asm_multiplication_opcode(s1, token);
2518 return;
2520 case TOK_ASM_smulleq:
2521 case TOK_ASM_smullseq:
2522 case TOK_ASM_umulleq:
2523 case TOK_ASM_umullseq:
2524 case TOK_ASM_smlaleq:
2525 case TOK_ASM_smlalseq:
2526 case TOK_ASM_umlaleq:
2527 case TOK_ASM_umlalseq:
2528 asm_long_multiplication_opcode(s1, token);
2529 return;
2531 case TOK_ASM_cdpeq:
2532 case TOK_ASM_mcreq:
2533 case TOK_ASM_mrceq:
2534 asm_coprocessor_opcode(s1, token);
2535 return;
2537 case TOK_ASM_ldceq:
2538 case TOK_ASM_ldcleq:
2539 case TOK_ASM_stceq:
2540 case TOK_ASM_stcleq:
2541 asm_coprocessor_data_transfer_opcode(s1, token);
2542 return;
2544 #if defined(TCC_ARM_VFP)
2545 case TOK_ASM_vldreq:
2546 case TOK_ASM_vstreq:
2547 asm_floating_point_single_data_transfer_opcode(s1, token);
2548 return;
2550 case TOK_ASM_vmlaeq_f32:
2551 case TOK_ASM_vmlseq_f32:
2552 case TOK_ASM_vnmlseq_f32:
2553 case TOK_ASM_vnmlaeq_f32:
2554 case TOK_ASM_vmuleq_f32:
2555 case TOK_ASM_vnmuleq_f32:
2556 case TOK_ASM_vaddeq_f32:
2557 case TOK_ASM_vsubeq_f32:
2558 case TOK_ASM_vdiveq_f32:
2559 case TOK_ASM_vnegeq_f32:
2560 case TOK_ASM_vabseq_f32:
2561 case TOK_ASM_vsqrteq_f32:
2562 case TOK_ASM_vcmpeq_f32:
2563 case TOK_ASM_vcmpeeq_f32:
2564 case TOK_ASM_vmoveq_f32:
2565 case TOK_ASM_vmlaeq_f64:
2566 case TOK_ASM_vmlseq_f64:
2567 case TOK_ASM_vnmlseq_f64:
2568 case TOK_ASM_vnmlaeq_f64:
2569 case TOK_ASM_vmuleq_f64:
2570 case TOK_ASM_vnmuleq_f64:
2571 case TOK_ASM_vaddeq_f64:
2572 case TOK_ASM_vsubeq_f64:
2573 case TOK_ASM_vdiveq_f64:
2574 case TOK_ASM_vnegeq_f64:
2575 case TOK_ASM_vabseq_f64:
2576 case TOK_ASM_vsqrteq_f64:
2577 case TOK_ASM_vcmpeq_f64:
2578 case TOK_ASM_vcmpeeq_f64:
2579 case TOK_ASM_vmoveq_f64:
2580 asm_floating_point_data_processing_opcode(s1, token);
2581 return;
2583 case TOK_ASM_vpusheq:
2584 case TOK_ASM_vpopeq:
2585 case TOK_ASM_vldmeq:
2586 case TOK_ASM_vldmiaeq:
2587 case TOK_ASM_vldmdbeq:
2588 case TOK_ASM_vstmeq:
2589 case TOK_ASM_vstmiaeq:
2590 case TOK_ASM_vstmdbeq:
2591 asm_floating_point_block_data_transfer_opcode(s1, token);
2592 return;
2594 case TOK_ASM_vmsreq:
2595 case TOK_ASM_vmrseq:
2596 asm_floating_point_status_register_opcode(s1, token);
2597 return;
2598 #endif
2600 default:
2601 expect("known instruction");
2605 ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier)
2607 int r, reg, size, val;
2608 char buf[64];
2610 r = sv->r;
2611 if ((r & VT_VALMASK) == VT_CONST) {
2612 if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n' &&
2613 modifier != 'P')
2614 cstr_ccat(add_str, '#');
2615 if (r & VT_SYM) {
2616 const char *name = get_tok_str(sv->sym->v, NULL);
2617 if (sv->sym->v >= SYM_FIRST_ANOM) {
2618 /* In case of anonymous symbols ("L.42", used
2619 for static data labels) we can't find them
2620 in the C symbol table when later looking up
2621 this name. So enter them now into the asm label
2622 list when we still know the symbol. */
2623 get_asm_sym(tok_alloc(name, strlen(name))->tok, sv->sym);
2625 if (tcc_state->leading_underscore)
2626 cstr_ccat(add_str, '_');
2627 cstr_cat(add_str, name, -1);
2628 if ((uint32_t) sv->c.i == 0)
2629 goto no_offset;
2630 cstr_ccat(add_str, '+');
2632 val = sv->c.i;
2633 if (modifier == 'n')
2634 val = -val;
2635 snprintf(buf, sizeof(buf), "%d", (int) sv->c.i);
2636 cstr_cat(add_str, buf, -1);
2637 no_offset:;
2638 } else if ((r & VT_VALMASK) == VT_LOCAL) {
2639 snprintf(buf, sizeof(buf), "[fp,#%d]", (int) sv->c.i);
2640 cstr_cat(add_str, buf, -1);
2641 } else if (r & VT_LVAL) {
2642 reg = r & VT_VALMASK;
2643 if (reg >= VT_CONST)
2644 tcc_internal_error("");
2645 snprintf(buf, sizeof(buf), "[%s]",
2646 get_tok_str(TOK_ASM_r0 + reg, NULL));
2647 cstr_cat(add_str, buf, -1);
2648 } else {
2649 /* register case */
2650 reg = r & VT_VALMASK;
2651 if (reg >= VT_CONST)
2652 tcc_internal_error("");
2654 /* choose register operand size */
2655 if ((sv->type.t & VT_BTYPE) == VT_BYTE ||
2656 (sv->type.t & VT_BTYPE) == VT_BOOL)
2657 size = 1;
2658 else if ((sv->type.t & VT_BTYPE) == VT_SHORT)
2659 size = 2;
2660 else
2661 size = 4;
2663 if (modifier == 'b') {
2664 size = 1;
2665 } else if (modifier == 'w') {
2666 size = 2;
2667 } else if (modifier == 'k') {
2668 size = 4;
2671 switch (size) {
2672 default:
2673 reg = TOK_ASM_r0 + reg;
2674 break;
2676 snprintf(buf, sizeof(buf), "%s", get_tok_str(reg, NULL));
2677 cstr_cat(add_str, buf, -1);
2681 /* generate prolog and epilog code for asm statement */
2682 ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
2683 int nb_outputs, int is_output,
2684 uint8_t *clobber_regs,
2685 int out_reg)
2687 uint8_t regs_allocated[NB_ASM_REGS];
2688 ASMOperand *op;
2689 int i, reg;
2690 uint32_t saved_regset = 0;
2692 // TODO: Check non-E ABI.
2693 // Note: Technically, r13 (sp) is also callee-saved--but that does not matter yet
2694 static uint8_t reg_saved[] = { 4, 5, 6, 7, 8, 9 /* Note: sometimes special reg "sb" */ , 10, 11 };
2696 /* mark all used registers */
2697 memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated));
2698 for(i = 0; i < nb_operands;i++) {
2699 op = &operands[i];
2700 if (op->reg >= 0)
2701 regs_allocated[op->reg] = 1;
2703 for(i = 0; i < sizeof(reg_saved)/sizeof(reg_saved[0]); i++) {
2704 reg = reg_saved[i];
2705 if (regs_allocated[reg])
2706 saved_regset |= 1 << reg;
2709 if (!is_output) { // prolog
2710 /* generate reg save code */
2711 if (saved_regset)
2712 gen_le32(0xe92d0000 | saved_regset); // push {...}
2714 /* generate load code */
2715 for(i = 0; i < nb_operands; i++) {
2716 op = &operands[i];
2717 if (op->reg >= 0) {
2718 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL &&
2719 op->is_memory) {
2720 /* memory reference case (for both input and
2721 output cases) */
2722 SValue sv;
2723 sv = *op->vt;
2724 sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL | VT_LVAL;
2725 sv.type.t = VT_PTR;
2726 load(op->reg, &sv);
2727 } else if (i >= nb_outputs || op->is_rw) { // not write-only
2728 /* load value in register */
2729 load(op->reg, op->vt);
2730 if (op->is_llong)
2731 tcc_error("long long not implemented");
2735 } else { // epilog
2736 /* generate save code */
2737 for(i = 0 ; i < nb_outputs; i++) {
2738 op = &operands[i];
2739 if (op->reg >= 0) {
2740 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
2741 if (!op->is_memory) {
2742 SValue sv;
2743 sv = *op->vt;
2744 sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
2745 sv.type.t = VT_PTR;
2746 load(out_reg, &sv);
2748 sv = *op->vt;
2749 sv.r = (sv.r & ~VT_VALMASK) | out_reg;
2750 store(op->reg, &sv);
2752 } else {
2753 store(op->reg, op->vt);
2754 if (op->is_llong)
2755 tcc_error("long long not implemented");
2760 /* generate reg restore code */
2761 if (saved_regset)
2762 gen_le32(0xe8bd0000 | saved_regset); // pop {...}
2766 /* return the constraint priority (we allocate first the lowest
2767 numbered constraints) */
2768 static inline int constraint_priority(const char *str)
2770 int priority, c, pr;
2772 /* we take the lowest priority */
2773 priority = 0;
2774 for(;;) {
2775 c = *str;
2776 if (c == '\0')
2777 break;
2778 str++;
2779 switch(c) {
2780 case 'l': // in ARM mode, that's an alias for 'r' [ARM].
2781 case 'r': // register [general]
2782 case 'p': // valid memory address for load,store [general]
2783 pr = 3;
2784 break;
2785 case 'M': // integer constant for shifts [ARM]
2786 case 'I': // integer valid for data processing instruction immediate
2787 case 'J': // integer in range -4095...4095
2789 case 'i': // immediate integer operand, including symbolic constants [general]
2790 case 'm': // memory operand [general]
2791 case 'g': // general-purpose-register, memory, immediate integer [general]
2792 pr = 4;
2793 break;
2794 default:
2795 tcc_error("unknown constraint '%c'", c);
2796 pr = 0;
2798 if (pr > priority)
2799 priority = pr;
2801 return priority;
2804 static const char *skip_constraint_modifiers(const char *p)
2806 /* Constraint modifier:
2807 = Operand is written to by this instruction
2808 + Operand is both read and written to by this instruction
2809 % Instruction is commutative for this operand and the following operand.
2811 Per-alternative constraint modifier:
2812 & Operand is clobbered before the instruction is done using the input operands
2814 while (*p == '=' || *p == '&' || *p == '+' || *p == '%')
2815 p++;
2816 return p;
2819 #define REG_OUT_MASK 0x01
2820 #define REG_IN_MASK 0x02
2822 #define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
2824 ST_FUNC void asm_compute_constraints(ASMOperand *operands,
2825 int nb_operands, int nb_outputs,
2826 const uint8_t *clobber_regs,
2827 int *pout_reg)
2829 /* overall format: modifier, then ,-seperated list of alternatives; all operands for a single instruction must have the same number of alternatives */
2830 /* TODO: Simple constraints
2831 whitespace ignored
2832 o memory operand that is offsetable
2833 V memory but not offsetable
2834 < memory operand with autodecrement addressing is allowed. Restrictions apply.
2835 > memory operand with autoincrement addressing is allowed. Restrictions apply.
2836 n immediate integer operand with a known numeric value
2837 E immediate floating operand (const_double) is allowed, but only if target=host
2838 F immediate floating operand (const_double or const_vector) is allowed
2839 s immediate integer operand whose value is not an explicit integer
2840 X any operand whatsoever
2841 0...9 (postfix); (can also be more than 1 digit number); an operand that matches the specified operand number is allowed
2844 /* TODO: ARM constraints:
2845 k the stack pointer register
2846 G the floating-point constant 0.0
2847 Q memory reference where the exact address is in a single register ("m" is preferable for asm statements)
2848 R an item in the constant pool
2849 S symbol in the text segment of the current file
2850 [ Uv memory reference suitable for VFP load/store insns (reg+constant offset)]
2851 [ Uy memory reference suitable for iWMMXt load/store instructions]
2852 Uq memory reference suitable for the ARMv4 ldrsb instruction
2854 ASMOperand *op;
2855 int sorted_op[MAX_ASM_OPERANDS];
2856 int i, j, k, p1, p2, tmp, reg, c, reg_mask;
2857 const char *str;
2858 uint8_t regs_allocated[NB_ASM_REGS];
2860 /* init fields */
2861 for (i = 0; i < nb_operands; i++) {
2862 op = &operands[i];
2863 op->input_index = -1;
2864 op->ref_index = -1;
2865 op->reg = -1;
2866 op->is_memory = 0;
2867 op->is_rw = 0;
2869 /* compute constraint priority and evaluate references to output
2870 constraints if input constraints */
2871 for (i = 0; i < nb_operands; i++) {
2872 op = &operands[i];
2873 str = op->constraint;
2874 str = skip_constraint_modifiers(str);
2875 if (isnum(*str) || *str == '[') {
2876 /* this is a reference to another constraint */
2877 k = find_constraint(operands, nb_operands, str, NULL);
2878 if ((unsigned) k >= i || i < nb_outputs)
2879 tcc_error("invalid reference in constraint %d ('%s')",
2880 i, str);
2881 op->ref_index = k;
2882 if (operands[k].input_index >= 0)
2883 tcc_error("cannot reference twice the same operand");
2884 operands[k].input_index = i;
2885 op->priority = 5;
2886 } else if ((op->vt->r & VT_VALMASK) == VT_LOCAL
2887 && op->vt->sym
2888 && (reg = op->vt->sym->r & VT_VALMASK) < VT_CONST) {
2889 op->priority = 1;
2890 op->reg = reg;
2891 } else {
2892 op->priority = constraint_priority(str);
2896 /* sort operands according to their priority */
2897 for (i = 0; i < nb_operands; i++)
2898 sorted_op[i] = i;
2899 for (i = 0; i < nb_operands - 1; i++) {
2900 for (j = i + 1; j < nb_operands; j++) {
2901 p1 = operands[sorted_op[i]].priority;
2902 p2 = operands[sorted_op[j]].priority;
2903 if (p2 < p1) {
2904 tmp = sorted_op[i];
2905 sorted_op[i] = sorted_op[j];
2906 sorted_op[j] = tmp;
2911 for (i = 0; i < NB_ASM_REGS; i++) {
2912 if (clobber_regs[i])
2913 regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK;
2914 else
2915 regs_allocated[i] = 0;
2917 /* sp cannot be used */
2918 regs_allocated[13] = REG_IN_MASK | REG_OUT_MASK;
2919 /* fp cannot be used yet */
2920 regs_allocated[11] = REG_IN_MASK | REG_OUT_MASK;
2922 /* allocate registers and generate corresponding asm moves */
2923 for (i = 0; i < nb_operands; i++) {
2924 j = sorted_op[i];
2925 op = &operands[j];
2926 str = op->constraint;
2927 /* no need to allocate references */
2928 if (op->ref_index >= 0)
2929 continue;
2930 /* select if register is used for output, input or both */
2931 if (op->input_index >= 0) {
2932 reg_mask = REG_IN_MASK | REG_OUT_MASK;
2933 } else if (j < nb_outputs) {
2934 reg_mask = REG_OUT_MASK;
2935 } else {
2936 reg_mask = REG_IN_MASK;
2938 if (op->reg >= 0) {
2939 if (is_reg_allocated(op->reg))
2940 tcc_error
2941 ("asm regvar requests register that's taken already");
2942 reg = op->reg;
2943 goto reg_found;
2945 try_next:
2946 c = *str++;
2947 switch (c) {
2948 case '=': // Operand is written-to
2949 goto try_next;
2950 case '+': // Operand is both READ and written-to
2951 op->is_rw = 1;
2952 /* FALL THRU */
2953 case '&': // Operand is clobbered before the instruction is done using the input operands
2954 if (j >= nb_outputs)
2955 tcc_error("'%c' modifier can only be applied to outputs",
2957 reg_mask = REG_IN_MASK | REG_OUT_MASK;
2958 goto try_next;
2959 case 'l': // In non-thumb mode, alias for 'r'--otherwise r0-r7 [ARM]
2960 case 'r': // general-purpose register
2961 case 'p': // loadable/storable address
2962 /* any general register */
2963 for (reg = 0; reg <= 8; reg++) {
2964 if (!is_reg_allocated(reg))
2965 goto reg_found;
2967 goto try_next;
2968 reg_found:
2969 /* now we can reload in the register */
2970 op->is_llong = 0;
2971 op->reg = reg;
2972 regs_allocated[reg] |= reg_mask;
2973 break;
2974 case 'I': // integer that is valid as an data processing instruction immediate (0...255, rotated by a multiple of two)
2975 case 'J': // integer in the range -4095 to 4095 [ARM]
2976 case 'K': // integer that satisfies constraint I when inverted (one's complement)
2977 case 'L': // integer that satisfies constraint I when inverted (two's complement)
2978 case 'i': // immediate integer operand, including symbolic constants
2979 if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST))
2980 goto try_next;
2981 break;
2982 case 'M': // integer in the range 0 to 32
2983 if (!
2984 ((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) ==
2985 VT_CONST))
2986 goto try_next;
2987 break;
2988 case 'm': // memory operand
2989 case 'g':
2990 /* nothing special to do because the operand is already in
2991 memory, except if the pointer itself is stored in a
2992 memory variable (VT_LLOCAL case) */
2993 /* XXX: fix constant case */
2994 /* if it is a reference to a memory zone, it must lie
2995 in a register, so we reserve the register in the
2996 input registers and a load will be generated
2997 later */
2998 if (j < nb_outputs || c == 'm') {
2999 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
3000 /* any general register */
3001 for (reg = 0; reg <= 8; reg++) {
3002 if (!(regs_allocated[reg] & REG_IN_MASK))
3003 goto reg_found1;
3005 goto try_next;
3006 reg_found1:
3007 /* now we can reload in the register */
3008 regs_allocated[reg] |= REG_IN_MASK;
3009 op->reg = reg;
3010 op->is_memory = 1;
3013 break;
3014 default:
3015 tcc_error("asm constraint %d ('%s') could not be satisfied",
3016 j, op->constraint);
3017 break;
3019 /* if a reference is present for that operand, we assign it too */
3020 if (op->input_index >= 0) {
3021 operands[op->input_index].reg = op->reg;
3022 operands[op->input_index].is_llong = op->is_llong;
3026 /* compute out_reg. It is used to store outputs registers to memory
3027 locations references by pointers (VT_LLOCAL case) */
3028 *pout_reg = -1;
3029 for (i = 0; i < nb_operands; i++) {
3030 op = &operands[i];
3031 if (op->reg >= 0 &&
3032 (op->vt->r & VT_VALMASK) == VT_LLOCAL && !op->is_memory) {
3033 for (reg = 0; reg <= 8; reg++) {
3034 if (!(regs_allocated[reg] & REG_OUT_MASK))
3035 goto reg_found2;
3037 tcc_error("could not find free output register for reloading");
3038 reg_found2:
3039 *pout_reg = reg;
3040 break;
3044 /* print sorted constraints */
3045 #ifdef ASM_DEBUG
3046 for (i = 0; i < nb_operands; i++) {
3047 j = sorted_op[i];
3048 op = &operands[j];
3049 printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
3051 op->id ? get_tok_str(op->id, NULL) : "",
3052 op->constraint, op->vt->r, op->reg);
3054 if (*pout_reg >= 0)
3055 printf("out_reg=%d\n", *pout_reg);
3056 #endif
3059 ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str)
3061 int reg;
3062 TokenSym *ts;
3064 if (!strcmp(str, "memory") ||
3065 !strcmp(str, "cc") ||
3066 !strcmp(str, "flags"))
3067 return;
3068 ts = tok_alloc(str, strlen(str));
3069 reg = asm_parse_regvar(ts->tok);
3070 if (reg == -1) {
3071 tcc_error("invalid clobber register '%s'", str);
3073 clobber_regs[reg] = 1;
3076 /* If T refers to a register then return the register number and type.
3077 Otherwise return -1. */
3078 ST_FUNC int asm_parse_regvar (int t)
3080 if (t >= TOK_ASM_r0 && t <= TOK_ASM_pc) { /* register name */
3081 switch (t) {
3082 case TOK_ASM_fp:
3083 return TOK_ASM_r11 - TOK_ASM_r0;
3084 case TOK_ASM_ip:
3085 return TOK_ASM_r12 - TOK_ASM_r0;
3086 case TOK_ASM_sp:
3087 return TOK_ASM_r13 - TOK_ASM_r0;
3088 case TOK_ASM_lr:
3089 return TOK_ASM_r14 - TOK_ASM_r0;
3090 case TOK_ASM_pc:
3091 return TOK_ASM_r15 - TOK_ASM_r0;
3092 default:
3093 return t - TOK_ASM_r0;
3095 } else
3096 return -1;
3099 /*************************************************************/
3100 #endif /* ndef TARGET_DEFS_ONLY */