fix UB in constant folding of double -> signed integer conversion
[tinycc.git] / arm-asm.c
blob54c063aeba01b241cf69498845091327809703f5
1 /*
2 * ARM specific functions for TCC assembler
4 * Copyright (c) 2001, 2002 Fabrice Bellard
5 * Copyright (c) 2020 Danny Milosavljevic
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #ifdef TARGET_DEFS_ONLY
24 #define CONFIG_TCC_ASM
25 #define NB_ASM_REGS 16
27 ST_FUNC void g(int c);
28 ST_FUNC void gen_le16(int c);
29 ST_FUNC void gen_le32(int c);
31 /*************************************************************/
32 #else
33 /*************************************************************/
35 #define USING_GLOBALS
36 #include "tcc.h"
38 enum {
39 OPT_REG32,
40 OPT_REGSET32,
41 OPT_IM8,
42 OPT_IM8N,
43 OPT_IM32,
44 OPT_VREG32,
45 OPT_VREG64,
47 #define OP_REG32 (1 << OPT_REG32)
48 #define OP_VREG32 (1 << OPT_VREG32)
49 #define OP_VREG64 (1 << OPT_VREG64)
50 #define OP_REG (OP_REG32 | OP_VREG32 | OP_VREG64)
51 #define OP_IM32 (1 << OPT_IM32)
52 #define OP_IM8 (1 << OPT_IM8)
53 #define OP_IM8N (1 << OPT_IM8N)
54 #define OP_REGSET32 (1 << OPT_REGSET32)
56 typedef struct Operand {
57 uint32_t type;
58 union {
59 uint8_t reg;
60 uint16_t regset;
61 ExprValue e;
63 } Operand;
65 /* Read the VFP register referred to by token T.
66 If OK, returns its number.
67 If not OK, returns -1. */
68 static int asm_parse_vfp_regvar(int t, int double_precision)
70 if (double_precision) {
71 if (t >= TOK_ASM_d0 && t <= TOK_ASM_d15)
72 return t - TOK_ASM_d0;
73 } else {
74 if (t >= TOK_ASM_s0 && t <= TOK_ASM_s31)
75 return t - TOK_ASM_s0;
77 return -1;
80 /* Parse a text containing operand and store the result in OP */
81 static void parse_operand(TCCState *s1, Operand *op)
83 ExprValue e;
84 int8_t reg;
85 uint16_t regset = 0;
87 op->type = 0;
89 if (tok == '{') { // regset literal
90 next(); // skip '{'
91 while (tok != '}' && tok != TOK_EOF) {
92 reg = asm_parse_regvar(tok);
93 if (reg == -1) {
94 expect("register");
95 } else
96 next(); // skip register name
98 if ((1 << reg) < regset)
99 tcc_warning("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
100 regset |= 1 << reg;
101 if (tok != ',')
102 break;
103 next(); // skip ','
105 skip('}');
106 if (regset == 0) {
107 // ARM instructions don't support empty regset.
108 tcc_error("empty register list is not supported");
109 } else {
110 op->type = OP_REGSET32;
111 op->regset = regset;
113 return;
114 } else if ((reg = asm_parse_regvar(tok)) != -1) {
115 next(); // skip register name
116 op->type = OP_REG32;
117 op->reg = (uint8_t) reg;
118 return;
119 } else if ((reg = asm_parse_vfp_regvar(tok, 0)) != -1) {
120 next(); // skip register name
121 op->type = OP_VREG32;
122 op->reg = (uint8_t) reg;
123 return;
124 } else if ((reg = asm_parse_vfp_regvar(tok, 1)) != -1) {
125 next(); // skip register name
126 op->type = OP_VREG64;
127 op->reg = (uint8_t) reg;
128 return;
129 } else if (tok == '#' || tok == '$') {
130 /* constant value */
131 next(); // skip '#' or '$'
133 asm_expr(s1, &e);
134 op->type = OP_IM32;
135 op->e = e;
136 if (!op->e.sym) {
137 if ((int) op->e.v < 0 && (int) op->e.v >= -255)
138 op->type = OP_IM8N;
139 else if (op->e.v == (uint8_t)op->e.v)
140 op->type = OP_IM8;
141 } else
142 expect("operand");
145 /* XXX: make it faster ? */
146 ST_FUNC void g(int c)
148 int ind1;
149 if (nocode_wanted)
150 return;
151 ind1 = ind + 1;
152 if (ind1 > cur_text_section->data_allocated)
153 section_realloc(cur_text_section, ind1);
154 cur_text_section->data[ind] = c;
155 ind = ind1;
158 ST_FUNC void gen_le16 (int i)
160 g(i);
161 g(i>>8);
164 ST_FUNC void gen_le32 (int i)
166 int ind1;
167 if (nocode_wanted)
168 return;
169 ind1 = ind + 4;
170 if (ind1 > cur_text_section->data_allocated)
171 section_realloc(cur_text_section, ind1);
172 cur_text_section->data[ind++] = i & 0xFF;
173 cur_text_section->data[ind++] = (i >> 8) & 0xFF;
174 cur_text_section->data[ind++] = (i >> 16) & 0xFF;
175 cur_text_section->data[ind++] = (i >> 24) & 0xFF;
178 ST_FUNC void gen_expr32(ExprValue *pe)
180 gen_le32(pe->v);
183 static uint32_t condition_code_of_token(int token) {
184 if (token < TOK_ASM_nopeq) {
185 expect("condition-enabled instruction");
186 } else
187 return (token - TOK_ASM_nopeq) & 15;
190 static void asm_emit_opcode(int token, uint32_t opcode) {
191 gen_le32((condition_code_of_token(token) << 28) | opcode);
194 static void asm_emit_unconditional_opcode(uint32_t opcode) {
195 gen_le32(opcode);
198 static void asm_emit_coprocessor_opcode(uint32_t high_nibble, uint8_t cp_number, uint8_t cp_opcode, uint8_t cp_destination_register, uint8_t cp_n_operand_register, uint8_t cp_m_operand_register, uint8_t cp_opcode2, int inter_processor_transfer)
200 uint32_t opcode = 0xe000000;
201 if (inter_processor_transfer)
202 opcode |= 1 << 4;
203 //assert(cp_opcode < 16);
204 opcode |= cp_opcode << 20;
205 //assert(cp_n_operand_register < 16);
206 opcode |= cp_n_operand_register << 16;
207 //assert(cp_destination_register < 16);
208 opcode |= cp_destination_register << 12;
209 //assert(cp_number < 16);
210 opcode |= cp_number << 8;
211 //assert(cp_information < 8);
212 opcode |= cp_opcode2 << 5;
213 //assert(cp_m_operand_register < 16);
214 opcode |= cp_m_operand_register;
215 asm_emit_unconditional_opcode((high_nibble << 28) | opcode);
218 static void asm_nullary_opcode(int token)
220 switch (ARM_INSTRUCTION_GROUP(token)) {
221 case TOK_ASM_nopeq:
222 asm_emit_opcode(token, 0xd << 21); // mov r0, r0
223 break;
224 case TOK_ASM_wfeeq:
225 asm_emit_opcode(token, 0x320f002);
226 case TOK_ASM_wfieq:
227 asm_emit_opcode(token, 0x320f003);
228 break;
229 default:
230 expect("nullary instruction");
234 static void asm_unary_opcode(TCCState *s1, int token)
236 Operand op;
237 parse_operand(s1, &op);
239 switch (ARM_INSTRUCTION_GROUP(token)) {
240 case TOK_ASM_swieq:
241 case TOK_ASM_svceq:
242 if (op.type != OP_IM8)
243 expect("immediate 8-bit unsigned integer");
244 else {
245 /* Note: Dummy operand (ignored by processor): ARM ref documented 0...255, ARM instruction set documented 24 bit */
246 asm_emit_opcode(token, (0xf << 24) | op.e.v);
248 break;
249 default:
250 expect("unary instruction");
254 static void asm_binary_opcode(TCCState *s1, int token)
256 Operand ops[2];
257 Operand rotation;
258 uint32_t encoded_rotation = 0;
259 uint64_t amount;
260 parse_operand(s1, &ops[0]);
261 skip(',');
262 parse_operand(s1, &ops[1]);
263 if (ops[0].type != OP_REG32) {
264 expect("(destination operand) register");
267 if (ops[0].reg == 15) {
268 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL));
271 if (ops[0].reg == 13)
272 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token, NULL));
274 if (ops[1].type != OP_REG32) {
275 switch (ARM_INSTRUCTION_GROUP(token)) {
276 case TOK_ASM_movteq:
277 case TOK_ASM_movweq:
278 if (ops[1].type == OP_IM8 || ops[1].type == OP_IM8N || ops[1].type == OP_IM32) {
279 if (ops[1].e.v >= 0 && ops[1].e.v <= 0xFFFF) {
280 uint16_t immediate_value = ops[1].e.v;
281 switch (ARM_INSTRUCTION_GROUP(token)) {
282 case TOK_ASM_movteq:
283 asm_emit_opcode(token, 0x3400000 | (ops[0].reg << 12) | (immediate_value & 0xF000) << 4 | (immediate_value & 0xFFF));
284 break;
285 case TOK_ASM_movweq:
286 asm_emit_opcode(token, 0x3000000 | (ops[0].reg << 12) | (immediate_value & 0xF000) << 4 | (immediate_value & 0xFFF));
287 break;
289 } else
290 expect("(source operand) immediate 16 bit value");
291 } else
292 expect("(source operand) immediate");
293 break;
294 default:
295 expect("(source operand) register");
297 return;
300 if (ops[1].reg == 15) {
301 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL));
304 if (ops[1].reg == 13)
305 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token, NULL));
307 if (tok == ',') {
308 next(); // skip ','
309 if (tok == TOK_ASM_ror) {
310 next(); // skip 'ror'
311 parse_operand(s1, &rotation);
312 if (rotation.type != OP_IM8) {
313 expect("immediate value for rotation");
314 } else {
315 amount = rotation.e.v;
316 switch (amount) {
317 case 8:
318 encoded_rotation = 1 << 10;
319 break;
320 case 16:
321 encoded_rotation = 2 << 10;
322 break;
323 case 24:
324 encoded_rotation = 3 << 10;
325 break;
326 default:
327 expect("'8' or '16' or '24'");
332 switch (ARM_INSTRUCTION_GROUP(token)) {
333 case TOK_ASM_clzeq:
334 if (encoded_rotation)
335 tcc_error("clz does not support rotation");
336 asm_emit_opcode(token, 0x16f0f10 | (ops[0].reg << 12) | ops[1].reg);
337 break;
338 case TOK_ASM_sxtbeq:
339 asm_emit_opcode(token, 0x6af0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation);
340 break;
341 case TOK_ASM_sxtheq:
342 asm_emit_opcode(token, 0x6bf0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation);
343 break;
344 case TOK_ASM_uxtbeq:
345 asm_emit_opcode(token, 0x6ef0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation);
346 break;
347 case TOK_ASM_uxtheq:
348 asm_emit_opcode(token, 0x6ff0070 | (ops[0].reg << 12) | ops[1].reg | encoded_rotation);
349 break;
350 default:
351 expect("binary instruction");
355 static void asm_coprocessor_opcode(TCCState *s1, int token) {
356 uint8_t coprocessor;
357 Operand opcode1;
358 Operand opcode2;
359 uint8_t registers[3];
360 unsigned int i;
361 uint8_t high_nibble;
362 uint8_t mrc = 0;
364 if (tok >= TOK_ASM_p0 && tok <= TOK_ASM_p15) {
365 coprocessor = tok - TOK_ASM_p0;
366 next();
367 } else {
368 expect("'p<number>'");
370 skip(',');
371 parse_operand(s1, &opcode1);
372 if (opcode1.type != OP_IM8 || opcode1.e.v > 15) {
373 tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token, NULL));
376 for (i = 0; i < 3; ++i) {
377 skip(',');
378 if (i == 0 && token != TOK_ASM_cdp2 && (ARM_INSTRUCTION_GROUP(token) == TOK_ASM_mrceq || ARM_INSTRUCTION_GROUP(token) == TOK_ASM_mcreq)) {
379 if (tok >= TOK_ASM_r0 && tok <= TOK_ASM_r15) {
380 registers[i] = tok - TOK_ASM_r0;
381 next();
382 } else {
383 expect("'r<number>'");
385 } else {
386 if (tok >= TOK_ASM_c0 && tok <= TOK_ASM_c15) {
387 registers[i] = tok - TOK_ASM_c0;
388 next();
389 } else {
390 expect("'c<number>'");
394 if (tok == ',') {
395 next();
396 parse_operand(s1, &opcode2);
397 } else {
398 opcode2.type = OP_IM8;
399 opcode2.e.v = 0;
401 if (opcode2.type != OP_IM8 || opcode2.e.v > 15) {
402 tcc_error("opcode2 of instruction '%s' must be an immediate value between 0 and 15", get_tok_str(token, NULL));
405 if (token == TOK_ASM_cdp2) {
406 high_nibble = 0xF;
407 asm_emit_coprocessor_opcode(high_nibble, coprocessor, opcode1.e.v, registers[0], registers[1], registers[2], opcode2.e.v, 0);
408 return;
409 } else
410 high_nibble = condition_code_of_token(token);
412 switch (ARM_INSTRUCTION_GROUP(token)) {
413 case TOK_ASM_cdpeq:
414 asm_emit_coprocessor_opcode(high_nibble, coprocessor, opcode1.e.v, registers[0], registers[1], registers[2], opcode2.e.v, 0);
415 break;
416 case TOK_ASM_mrceq:
417 // opcode1 encoding changes! highest and lowest bit gone.
418 mrc = 1;
419 /* fallthrough */
420 case TOK_ASM_mcreq:
421 // opcode1 encoding changes! highest and lowest bit gone.
422 if (opcode1.e.v > 7) {
423 tcc_error("opcode1 of instruction '%s' must be an immediate value between 0 and 7", get_tok_str(token, NULL));
425 asm_emit_coprocessor_opcode(high_nibble, coprocessor, (opcode1.e.v << 1) | mrc, registers[0], registers[1], registers[2], opcode2.e.v, 1);
426 break;
427 default:
428 expect("known instruction");
432 /* data processing and single data transfer instructions only */
433 #define ENCODE_RN(register_index) ((register_index) << 16)
434 #define ENCODE_RD(register_index) ((register_index) << 12)
435 #define ENCODE_SET_CONDITION_CODES (1 << 20)
437 /* Note: For data processing instructions, "1" means immediate.
438 Note: For single data transfer instructions, "0" means immediate. */
439 #define ENCODE_IMMEDIATE_FLAG (1 << 25)
441 #define ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER (1 << 4)
442 #define ENCODE_BARREL_SHIFTER_MODE_LSL (0 << 5)
443 #define ENCODE_BARREL_SHIFTER_MODE_LSR (1 << 5)
444 #define ENCODE_BARREL_SHIFTER_MODE_ASR (2 << 5)
445 #define ENCODE_BARREL_SHIFTER_MODE_ROR (3 << 5)
446 #define ENCODE_BARREL_SHIFTER_REGISTER(register_index) ((register_index) << 8)
447 #define ENCODE_BARREL_SHIFTER_IMMEDIATE(value) ((value) << 7)
449 static void asm_block_data_transfer_opcode(TCCState *s1, int token)
451 uint32_t opcode;
452 int op0_exclam = 0;
453 Operand ops[2];
454 int nb_ops = 1;
455 parse_operand(s1, &ops[0]);
456 if (tok == '!') {
457 op0_exclam = 1;
458 next(); // skip '!'
460 if (tok == ',') {
461 next(); // skip comma
462 parse_operand(s1, &ops[1]);
463 ++nb_ops;
465 if (nb_ops < 1) {
466 expect("at least one operand");
467 } else if (ops[nb_ops - 1].type != OP_REGSET32) {
468 expect("(last operand) register list");
471 // block data transfer: 1 0 0 P U S W L << 20 (general case):
472 // operands:
473 // Rn: bits 19...16 base register
474 // Register List: bits 15...0
476 switch (ARM_INSTRUCTION_GROUP(token)) {
477 case TOK_ASM_pusheq: // TODO: Optimize 1-register case to: str ?, [sp, #-4]!
478 // Instruction: 1 I=0 P=1 U=0 S=0 W=1 L=0 << 20, op 1101
479 // operands:
480 // Rn: base register
481 // Register List: bits 15...0
482 if (nb_ops != 1)
483 expect("exactly one operand");
484 else
485 asm_emit_opcode(token, (0x92d << 16) | ops[0].regset); // TODO: base register ?
486 break;
487 case TOK_ASM_popeq: // TODO: Optimize 1-register case to: ldr ?, [sp], #4
488 // Instruction: 1 I=0 P=0 U=1 S=0 W=0 L=1 << 20, op 1101
489 // operands:
490 // Rn: base register
491 // Register List: bits 15...0
492 if (nb_ops != 1)
493 expect("exactly one operand");
494 else
495 asm_emit_opcode(token, (0x8bd << 16) | ops[0].regset); // TODO: base register ?
496 break;
497 case TOK_ASM_stmdaeq:
498 case TOK_ASM_ldmdaeq:
499 case TOK_ASM_stmeq:
500 case TOK_ASM_ldmeq:
501 case TOK_ASM_stmiaeq:
502 case TOK_ASM_ldmiaeq:
503 case TOK_ASM_stmdbeq:
504 case TOK_ASM_ldmdbeq:
505 case TOK_ASM_stmibeq:
506 case TOK_ASM_ldmibeq:
507 switch (ARM_INSTRUCTION_GROUP(token)) {
508 case TOK_ASM_stmdaeq: // post-decrement store
509 opcode = 0x80 << 20;
510 break;
511 case TOK_ASM_ldmdaeq: // post-decrement load
512 opcode = 0x81 << 20;
513 break;
514 case TOK_ASM_stmeq: // post-increment store
515 case TOK_ASM_stmiaeq: // post-increment store
516 opcode = 0x88 << 20;
517 break;
518 case TOK_ASM_ldmeq: // post-increment load
519 case TOK_ASM_ldmiaeq: // post-increment load
520 opcode = 0x89 << 20;
521 break;
522 case TOK_ASM_stmdbeq: // pre-decrement store
523 opcode = 0x90 << 20;
524 break;
525 case TOK_ASM_ldmdbeq: // pre-decrement load
526 opcode = 0x91 << 20;
527 break;
528 case TOK_ASM_stmibeq: // pre-increment store
529 opcode = 0x98 << 20;
530 break;
531 case TOK_ASM_ldmibeq: // pre-increment load
532 opcode = 0x99 << 20;
533 break;
534 default:
535 tcc_error("internal error: This place should not be reached (fallback in asm_block_data_transfer_opcode)");
537 // operands:
538 // Rn: first operand
539 // Register List: lower bits
540 if (nb_ops != 2)
541 expect("exactly two operands");
542 else if (ops[0].type != OP_REG32)
543 expect("(first operand) register");
544 else {
545 if (op0_exclam)
546 opcode |= 1 << 21; // writeback
547 asm_emit_opcode(token, opcode | ENCODE_RN(ops[0].reg) | ops[1].regset);
549 break;
550 default:
551 expect("block data transfer instruction");
555 /* Parses shift directive and returns the parts that would have to be set in the opcode because of it.
556 Does not encode the actual shift amount.
557 It's not an error if there is no shift directive.
559 NB_SHIFT: will be set to 1 iff SHIFT is filled. Note that for rrx, there's no need to fill SHIFT.
560 SHIFT: will be filled in with the shift operand to use, if any. */
561 static uint32_t asm_parse_optional_shift(TCCState* s1, int* nb_shift, Operand* shift)
563 uint32_t opcode = 0;
564 *nb_shift = 0;
565 switch (tok) {
566 case TOK_ASM_asl:
567 case TOK_ASM_lsl:
568 case TOK_ASM_asr:
569 case TOK_ASM_lsr:
570 case TOK_ASM_ror:
571 switch (tok) {
572 case TOK_ASM_asl:
573 /* fallthrough */
574 case TOK_ASM_lsl:
575 opcode = ENCODE_BARREL_SHIFTER_MODE_LSL;
576 break;
577 case TOK_ASM_asr:
578 opcode = ENCODE_BARREL_SHIFTER_MODE_ASR;
579 break;
580 case TOK_ASM_lsr:
581 opcode = ENCODE_BARREL_SHIFTER_MODE_LSR;
582 break;
583 case TOK_ASM_ror:
584 opcode = ENCODE_BARREL_SHIFTER_MODE_ROR;
585 break;
587 next();
588 parse_operand(s1, shift);
589 *nb_shift = 1;
590 break;
591 case TOK_ASM_rrx:
592 next();
593 opcode = ENCODE_BARREL_SHIFTER_MODE_ROR;
594 break;
596 return opcode;
599 static uint32_t asm_encode_shift(Operand* shift)
601 uint64_t amount;
602 uint32_t operands = 0;
603 switch (shift->type) {
604 case OP_REG32:
605 if (shift->reg == 15)
606 tcc_error("r15 cannot be used as a shift count");
607 else {
608 operands = ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER;
609 operands |= ENCODE_BARREL_SHIFTER_REGISTER(shift->reg);
611 break;
612 case OP_IM8:
613 amount = shift->e.v;
614 if (amount > 0 && amount < 32)
615 operands = ENCODE_BARREL_SHIFTER_IMMEDIATE(amount);
616 else
617 tcc_error("shift count out of range");
618 break;
619 default:
620 tcc_error("unknown shift amount");
622 return operands;
625 static void asm_data_processing_opcode(TCCState *s1, int token)
627 Operand ops[3];
628 int nb_ops;
629 Operand shift = {0};
630 int nb_shift = 0;
631 uint32_t operands = 0;
633 /* modulo 16 entries per instruction for the different condition codes */
634 uint32_t opcode_idx = (ARM_INSTRUCTION_GROUP(token) - TOK_ASM_andeq) >> 4;
635 uint32_t opcode_nos = opcode_idx >> 1; // without "s"; "OpCode" in ARM docs
637 for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ) {
638 if (tok == TOK_ASM_asl || tok == TOK_ASM_lsl || tok == TOK_ASM_lsr || tok == TOK_ASM_asr || tok == TOK_ASM_ror || tok == TOK_ASM_rrx)
639 break;
640 parse_operand(s1, &ops[nb_ops]);
641 ++nb_ops;
642 if (tok != ',')
643 break;
644 next(); // skip ','
646 if (tok == ',')
647 next();
648 operands |= asm_parse_optional_shift(s1, &nb_shift, &shift);
649 if (nb_ops < 2)
650 expect("at least two operands");
651 else if (nb_ops == 2) {
652 memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2]
653 memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit
654 nb_ops = 3;
655 } else if (nb_ops == 3) {
656 if (opcode_nos == 0xd || opcode_nos == 0xf || opcode_nos == 0xa || opcode_nos == 0xb || opcode_nos == 0x8 || opcode_nos == 0x9) { // mov, mvn, cmp, cmn, tst, teq
657 tcc_error("'%s' cannot be used with three operands", get_tok_str(token, NULL));
660 if (nb_ops != 3) {
661 expect("two or three operands");
662 } else {
663 uint32_t opcode = 0;
664 uint32_t immediate_value;
665 uint8_t half_immediate_rotation;
666 if (nb_shift && shift.type == OP_REG32) {
667 if ((ops[0].type == OP_REG32 && ops[0].reg == 15) ||
668 (ops[1].type == OP_REG32 && ops[1].reg == 15)) {
669 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
673 // data processing (general case):
674 // operands:
675 // Rn: bits 19...16 (first operand)
676 // Rd: bits 15...12 (destination)
677 // Operand2: bits 11...0 (second operand); depending on I that's either a register or an immediate
678 // operator:
679 // bits 24...21: "OpCode"--see below
681 /* operations in the token list are ordered by opcode */
682 opcode = opcode_nos << 21; // drop "s"
683 if (ops[0].type != OP_REG32)
684 expect("(destination operand) register");
685 else if (opcode_nos == 0xa || opcode_nos == 0xb || opcode_nos == 0x8 || opcode_nos == 0x9) // cmp, cmn, tst, teq
686 operands |= ENCODE_SET_CONDITION_CODES; // force S set, otherwise it's a completely different instruction.
687 else
688 operands |= ENCODE_RD(ops[0].reg);
689 if (ops[1].type != OP_REG32)
690 expect("(first source operand) register");
691 else if (!(opcode_nos == 0xd || opcode_nos == 0xf)) // not: mov, mvn (those have only one source operand)
692 operands |= ENCODE_RN(ops[1].reg);
693 switch (ops[2].type) {
694 case OP_REG32:
695 operands |= ops[2].reg;
696 break;
697 case OP_IM8:
698 case OP_IM32:
699 operands |= ENCODE_IMMEDIATE_FLAG;
700 immediate_value = ops[2].e.v;
701 for (half_immediate_rotation = 0; half_immediate_rotation < 16; ++half_immediate_rotation) {
702 if (immediate_value >= 0x00 && immediate_value < 0x100)
703 break;
704 // rotate left by two
705 immediate_value = ((immediate_value & 0x3FFFFFFF) << 2) | ((immediate_value & 0xC0000000) >> 30);
707 if (half_immediate_rotation >= 16) {
708 /* fallthrough */
709 } else {
710 operands |= immediate_value;
711 operands |= half_immediate_rotation << 8;
712 break;
714 case OP_IM8N: // immediate negative value
715 operands |= ENCODE_IMMEDIATE_FLAG;
716 immediate_value = ops[2].e.v;
717 /* Instruction swapping:
718 0001 = EOR - Rd:= Op1 EOR Op2 -> difficult
719 0011 = RSB - Rd:= Op2 - Op1 -> difficult
720 0111 = RSC - Rd:= Op2 - Op1 + C -> difficult
721 1000 = TST - CC on: Op1 AND Op2 -> difficult
722 1001 = TEQ - CC on: Op1 EOR Op2 -> difficult
723 1100 = ORR - Rd:= Op1 OR Op2 -> difficult
725 switch (opcode_nos) {
726 case 0x0: // AND - Rd:= Op1 AND Op2
727 opcode = 0xe << 21; // BIC
728 immediate_value = ~immediate_value;
729 break;
730 case 0x2: // SUB - Rd:= Op1 - Op2
731 opcode = 0x4 << 21; // ADD
732 immediate_value = -immediate_value;
733 break;
734 case 0x4: // ADD - Rd:= Op1 + Op2
735 opcode = 0x2 << 21; // SUB
736 immediate_value = -immediate_value;
737 break;
738 case 0x5: // ADC - Rd:= Op1 + Op2 + C
739 opcode = 0x6 << 21; // SBC
740 immediate_value = ~immediate_value;
741 break;
742 case 0x6: // SBC - Rd:= Op1 - Op2 + C
743 opcode = 0x5 << 21; // ADC
744 immediate_value = ~immediate_value;
745 break;
746 case 0xa: // CMP - CC on: Op1 - Op2
747 opcode = 0xb << 21; // CMN
748 immediate_value = -immediate_value;
749 break;
750 case 0xb: // CMN - CC on: Op1 + Op2
751 opcode = 0xa << 21; // CMP
752 immediate_value = -immediate_value;
753 break;
754 case 0xd: // MOV - Rd:= Op2
755 opcode = 0xf << 21; // MVN
756 immediate_value = ~immediate_value;
757 break;
758 case 0xe: // BIC - Rd:= Op1 AND NOT Op2
759 opcode = 0x0 << 21; // AND
760 immediate_value = ~immediate_value;
761 break;
762 case 0xf: // MVN - Rd:= NOT Op2
763 opcode = 0xd << 21; // MOV
764 immediate_value = ~immediate_value;
765 break;
766 default:
767 tcc_error("cannot use '%s' with a negative immediate value", get_tok_str(token, NULL));
769 for (half_immediate_rotation = 0; half_immediate_rotation < 16; ++half_immediate_rotation) {
770 if (immediate_value >= 0x00 && immediate_value < 0x100)
771 break;
772 // rotate left by two
773 immediate_value = ((immediate_value & 0x3FFFFFFF) << 2) | ((immediate_value & 0xC0000000) >> 30);
775 if (half_immediate_rotation >= 16) {
776 immediate_value = ops[2].e.v;
777 tcc_error("immediate value 0x%X cannot be encoded into ARM immediate", (unsigned) immediate_value);
779 operands |= immediate_value;
780 operands |= half_immediate_rotation << 8;
781 break;
782 default:
783 expect("(second source operand) register or immediate value");
786 if (nb_shift) {
787 if (operands & ENCODE_IMMEDIATE_FLAG)
788 tcc_error("immediate rotation not implemented");
789 else
790 operands |= asm_encode_shift(&shift);
793 /* S=0 and S=1 entries alternate one after another, in that order */
794 opcode |= (opcode_idx & 1) ? ENCODE_SET_CONDITION_CODES : 0;
795 asm_emit_opcode(token, opcode | operands);
799 static void asm_shift_opcode(TCCState *s1, int token)
801 Operand ops[3];
802 int nb_ops;
803 int definitely_neutral = 0;
804 uint32_t opcode = 0xd << 21; // MOV
805 uint32_t operands = 0;
807 for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ++nb_ops) {
808 parse_operand(s1, &ops[nb_ops]);
809 if (tok != ',') {
810 ++nb_ops;
811 break;
813 next(); // skip ','
815 if (nb_ops < 2) {
816 expect("at least two operands");
819 if (ops[0].type != OP_REG32) {
820 expect("(destination operand) register");
821 } else
822 operands |= ENCODE_RD(ops[0].reg);
824 if (nb_ops == 2) {
825 switch (ARM_INSTRUCTION_GROUP(token)) {
826 case TOK_ASM_rrxseq:
827 opcode |= ENCODE_SET_CONDITION_CODES;
828 /* fallthrough */
829 case TOK_ASM_rrxeq:
830 if (ops[1].type == OP_REG32) {
831 operands |= ops[1].reg;
832 operands |= ENCODE_BARREL_SHIFTER_MODE_ROR;
833 asm_emit_opcode(token, opcode | operands);
834 } else
835 tcc_error("(first source operand) register");
836 return;
837 default:
838 memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2]
839 memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit
840 nb_ops = 3;
843 if (nb_ops != 3) {
844 expect("two or three operands");
847 switch (ARM_INSTRUCTION_GROUP(token)) {
848 case TOK_ASM_lslseq:
849 case TOK_ASM_lsrseq:
850 case TOK_ASM_asrseq:
851 case TOK_ASM_rorseq:
852 opcode |= ENCODE_SET_CONDITION_CODES;
853 break;
856 switch (ops[1].type) {
857 case OP_REG32:
858 operands |= ops[1].reg;
859 break;
860 case OP_IM8:
861 operands |= ENCODE_IMMEDIATE_FLAG;
862 operands |= ops[1].e.v;
863 tcc_error("Using an immediate value as the source operand is not possible with '%s' instruction on ARM", get_tok_str(token, NULL));
866 switch (ops[2].type) {
867 case OP_REG32:
868 if ((ops[0].type == OP_REG32 && ops[0].reg == 15) ||
869 (ops[1].type == OP_REG32 && ops[1].reg == 15)) {
870 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
872 operands |= asm_encode_shift(&ops[2]);
873 break;
874 case OP_IM8:
875 if (ops[2].e.v)
876 operands |= asm_encode_shift(&ops[2]);
877 else
878 definitely_neutral = 1;
879 break;
882 if (!definitely_neutral) switch (ARM_INSTRUCTION_GROUP(token)) {
883 case TOK_ASM_lslseq:
884 case TOK_ASM_lsleq:
885 operands |= ENCODE_BARREL_SHIFTER_MODE_LSL;
886 break;
887 case TOK_ASM_lsrseq:
888 case TOK_ASM_lsreq:
889 operands |= ENCODE_BARREL_SHIFTER_MODE_LSR;
890 break;
891 case TOK_ASM_asrseq:
892 case TOK_ASM_asreq:
893 operands |= ENCODE_BARREL_SHIFTER_MODE_ASR;
894 break;
895 case TOK_ASM_rorseq:
896 case TOK_ASM_roreq:
897 operands |= ENCODE_BARREL_SHIFTER_MODE_ROR;
898 break;
899 default:
900 expect("shift instruction");
902 asm_emit_opcode(token, opcode | operands);
905 static void asm_multiplication_opcode(TCCState *s1, int token)
907 Operand ops[4];
908 int nb_ops = 0;
909 uint32_t opcode = 0x90;
911 for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ++nb_ops) {
912 parse_operand(s1, &ops[nb_ops]);
913 if (tok != ',') {
914 ++nb_ops;
915 break;
917 next(); // skip ','
919 if (nb_ops < 2)
920 expect("at least two operands");
921 else if (nb_ops == 2) {
922 switch (ARM_INSTRUCTION_GROUP(token)) {
923 case TOK_ASM_mulseq:
924 case TOK_ASM_muleq:
925 memcpy(&ops[2], &ops[0], sizeof(ops[1])); // ARM is actually like this!
926 break;
927 default:
928 expect("at least three operands");
930 nb_ops = 3;
933 // multiply (special case):
934 // operands:
935 // Rd: bits 19...16
936 // Rm: bits 3...0
937 // Rs: bits 11...8
938 // Rn: bits 15...12
940 if (ops[0].type == OP_REG32)
941 opcode |= ops[0].reg << 16;
942 else
943 expect("(destination operand) register");
944 if (ops[1].type == OP_REG32)
945 opcode |= ops[1].reg;
946 else
947 expect("(first source operand) register");
948 if (ops[2].type == OP_REG32)
949 opcode |= ops[2].reg << 8;
950 else
951 expect("(second source operand) register");
952 if (nb_ops > 3) {
953 if (ops[3].type == OP_REG32)
954 opcode |= ops[3].reg << 12;
955 else
956 expect("(third source operand) register");
959 switch (ARM_INSTRUCTION_GROUP(token)) {
960 case TOK_ASM_mulseq:
961 opcode |= 1 << 20; // Status
962 /* fallthrough */
963 case TOK_ASM_muleq:
964 if (nb_ops != 3)
965 expect("three operands");
966 else {
967 asm_emit_opcode(token, opcode);
969 break;
970 case TOK_ASM_mlaseq:
971 opcode |= 1 << 20; // Status
972 /* fallthrough */
973 case TOK_ASM_mlaeq:
974 if (nb_ops != 4)
975 expect("four operands");
976 else {
977 opcode |= 1 << 21; // Accumulate
978 asm_emit_opcode(token, opcode);
980 break;
981 default:
982 expect("known multiplication instruction");
986 static void asm_long_multiplication_opcode(TCCState *s1, int token)
988 Operand ops[4];
989 int nb_ops = 0;
990 uint32_t opcode = 0x90 | (1 << 23);
992 for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ++nb_ops) {
993 parse_operand(s1, &ops[nb_ops]);
994 if (tok != ',') {
995 ++nb_ops;
996 break;
998 next(); // skip ','
1000 if (nb_ops != 4) {
1001 expect("four operands");
1004 // long multiply (special case):
1005 // operands:
1006 // RdLo: bits 15...12
1007 // RdHi: bits 19...16
1008 // Rs: bits 11...8
1009 // Rm: bits 3...0
1011 if (ops[0].type == OP_REG32)
1012 opcode |= ops[0].reg << 12;
1013 else
1014 expect("(destination lo accumulator) register");
1015 if (ops[1].type == OP_REG32)
1016 opcode |= ops[1].reg << 16;
1017 else
1018 expect("(destination hi accumulator) register");
1019 if (ops[2].type == OP_REG32)
1020 opcode |= ops[2].reg;
1021 else
1022 expect("(first source operand) register");
1023 if (ops[3].type == OP_REG32)
1024 opcode |= ops[3].reg << 8;
1025 else
1026 expect("(second source operand) register");
1028 switch (ARM_INSTRUCTION_GROUP(token)) {
1029 case TOK_ASM_smullseq:
1030 opcode |= 1 << 20; // Status
1031 /* fallthrough */
1032 case TOK_ASM_smulleq:
1033 opcode |= 1 << 22; // signed
1034 asm_emit_opcode(token, opcode);
1035 break;
1036 case TOK_ASM_umullseq:
1037 opcode |= 1 << 20; // Status
1038 /* fallthrough */
1039 case TOK_ASM_umulleq:
1040 asm_emit_opcode(token, opcode);
1041 break;
1042 case TOK_ASM_smlalseq:
1043 opcode |= 1 << 20; // Status
1044 /* fallthrough */
1045 case TOK_ASM_smlaleq:
1046 opcode |= 1 << 22; // signed
1047 opcode |= 1 << 21; // Accumulate
1048 asm_emit_opcode(token, opcode);
1049 break;
1050 case TOK_ASM_umlalseq:
1051 opcode |= 1 << 20; // Status
1052 /* fallthrough */
1053 case TOK_ASM_umlaleq:
1054 opcode |= 1 << 21; // Accumulate
1055 asm_emit_opcode(token, opcode);
1056 break;
1057 default:
1058 expect("known long multiplication instruction");
1062 static void asm_single_data_transfer_opcode(TCCState *s1, int token)
1064 Operand ops[3];
1065 Operand strex_operand;
1066 Operand shift;
1067 int nb_shift = 0;
1068 int exclam = 0;
1069 int closed_bracket = 0;
1070 int op2_minus = 0;
1071 uint32_t opcode = 0;
1072 // Note: ldr r0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1073 // Note: ldr r0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1074 // Note: ldr r0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1076 parse_operand(s1, &ops[0]);
1077 if (ops[0].type == OP_REG32)
1078 opcode |= ENCODE_RD(ops[0].reg);
1079 else {
1080 expect("(destination operand) register");
1082 if (tok != ',')
1083 expect("at least two arguments");
1084 next(); // skip ','
1086 switch (ARM_INSTRUCTION_GROUP(token)) {
1087 case TOK_ASM_strexbeq:
1088 case TOK_ASM_strexeq:
1089 parse_operand(s1, &strex_operand);
1090 if (strex_operand.type != OP_REG32) {
1091 expect("register");
1093 if (tok != ',')
1094 expect("at least three arguments");
1095 else
1096 next(); // skip ','
1097 break;
1100 skip('[');
1101 parse_operand(s1, &ops[1]);
1102 if (ops[1].type == OP_REG32)
1103 opcode |= ENCODE_RN(ops[1].reg);
1104 else {
1105 expect("(first source operand) register");
1107 if (tok == ']') {
1108 next();
1109 closed_bracket = 1;
1110 // exclam = 1; // implicit in hardware; don't do it in software
1112 if (tok == ',') {
1113 next(); // skip ','
1114 if (tok == '-') {
1115 op2_minus = 1;
1116 next();
1118 parse_operand(s1, &ops[2]);
1119 if (ops[2].type == OP_REG32) {
1120 if (ops[2].reg == 15) {
1121 tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token, NULL));
1123 if (tok == ',') {
1124 next();
1125 opcode |= asm_parse_optional_shift(s1, &nb_shift, &shift);
1126 if (opcode == 0)
1127 expect("shift directive, or no comma");
1130 } else {
1131 // end of input expression in brackets--assume 0 offset
1132 ops[2].type = OP_IM8;
1133 ops[2].e.v = 0;
1134 opcode |= 1 << 24; // add offset before transfer
1136 if (!closed_bracket) {
1137 skip(']');
1138 opcode |= 1 << 24; // add offset before transfer
1139 if (tok == '!') {
1140 exclam = 1;
1141 next(); // skip '!'
1145 // single data transfer: 0 1 I P U B W L << 20 (general case):
1146 // operands:
1147 // Rd: destination operand [ok]
1148 // Rn: first source operand [ok]
1149 // Operand2: bits 11...0 [ok]
1150 // I: immediate operand? [ok]
1151 // P: Pre/post indexing is PRE: Add offset before transfer [ok]
1152 // U: Up/down is up? (*adds* offset to base) [ok]
1153 // B: Byte/word is byte? [ok]
1154 // W: Write address back into base? [ok]
1155 // L: Load/store is load? [ok]
1156 if (exclam)
1157 opcode |= 1 << 21; // write offset back into register
1159 if (ops[2].type == OP_IM32 || ops[2].type == OP_IM8 || ops[2].type == OP_IM8N) {
1160 int v = ops[2].e.v;
1161 if (op2_minus)
1162 tcc_error("minus before '#' not supported for immediate values");
1163 if (v >= 0) {
1164 opcode |= 1 << 23; // up
1165 if (v >= 0x1000)
1166 tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
1167 else
1168 opcode |= v;
1169 } else { // down
1170 if (v <= -0x1000)
1171 tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
1172 else
1173 opcode |= -v;
1175 } else if (ops[2].type == OP_REG32) {
1176 if (!op2_minus)
1177 opcode |= 1 << 23; // up
1178 opcode |= ENCODE_IMMEDIATE_FLAG; /* if set, it means it's NOT immediate */
1179 opcode |= ops[2].reg;
1180 } else
1181 expect("register");
1183 switch (ARM_INSTRUCTION_GROUP(token)) {
1184 case TOK_ASM_strbeq:
1185 opcode |= 1 << 22; // B
1186 /* fallthrough */
1187 case TOK_ASM_streq:
1188 opcode |= 1 << 26; // Load/Store
1189 if (nb_shift)
1190 opcode |= asm_encode_shift(&shift);
1191 asm_emit_opcode(token, opcode);
1192 break;
1193 case TOK_ASM_ldrbeq:
1194 opcode |= 1 << 22; // B
1195 /* fallthrough */
1196 case TOK_ASM_ldreq:
1197 opcode |= 1 << 20; // L
1198 opcode |= 1 << 26; // Load/Store
1199 if (nb_shift)
1200 opcode |= asm_encode_shift(&shift);
1201 asm_emit_opcode(token, opcode);
1202 break;
1203 case TOK_ASM_strexbeq:
1204 opcode |= 1 << 22; // B
1205 /* fallthrough */
1206 case TOK_ASM_strexeq:
1207 if ((opcode & 0xFFF) || nb_shift) {
1208 tcc_error("neither offset nor shift allowed with 'strex'");
1209 } else if (opcode & ENCODE_IMMEDIATE_FLAG) { // if set, it means it's NOT immediate
1210 tcc_error("offset not allowed with 'strex'");
1212 if ((opcode & (1 << 24)) == 0) { // add offset after transfer
1213 tcc_error("adding offset after transfer not allowed with 'strex'");
1216 opcode |= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL
1217 opcode |= strex_operand.reg;
1218 asm_emit_opcode(token, opcode);
1219 break;
1220 case TOK_ASM_ldrexbeq:
1221 opcode |= 1 << 22; // B
1222 /* fallthrough */
1223 case TOK_ASM_ldrexeq:
1224 if ((opcode & 0xFFF) || nb_shift) {
1225 tcc_error("neither offset nor shift allowed with 'ldrex'");
1226 } else if (opcode & ENCODE_IMMEDIATE_FLAG) { // if set, it means it's NOT immediate
1227 tcc_error("offset not allowed with 'ldrex'");
1229 if ((opcode & (1 << 24)) == 0) { // add offset after transfer
1230 tcc_error("adding offset after transfer not allowed with 'ldrex'");
1232 opcode |= 1 << 20; // L
1233 opcode |= 0x00f;
1234 opcode |= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL
1235 asm_emit_opcode(token, opcode);
1236 break;
1237 default:
1238 expect("data transfer instruction");
1242 // Note: Only call this using a VFP register if you know exactly what you are doing (i.e. cp_number is 10 or 11 and you are doing a vmov)
1243 static void asm_emit_coprocessor_data_transfer(uint32_t high_nibble, uint8_t cp_number, uint8_t CRd, const Operand* Rn, const Operand* offset, int offset_minus, int preincrement, int writeback, int long_transfer, int load) {
1244 uint32_t opcode = 0x0;
1245 opcode |= 1 << 26; // Load/Store
1246 opcode |= 1 << 27; // coprocessor
1248 if (long_transfer)
1249 opcode |= 1 << 22; // long transfer
1251 if (load)
1252 opcode |= 1 << 20; // L
1254 opcode |= cp_number << 8;
1256 //assert(CRd < 16);
1257 opcode |= ENCODE_RD(CRd);
1259 if (Rn->type != OP_REG32)
1260 expect("register");
1262 //assert(Rn->reg < 16);
1263 opcode |= ENCODE_RN(Rn->reg);
1264 if (preincrement)
1265 opcode |= 1 << 24; // add offset before transfer
1267 if (writeback)
1268 opcode |= 1 << 21; // write offset back into register
1270 if (offset->type == OP_IM8 || offset->type == OP_IM8N || offset->type == OP_IM32) {
1271 int v = offset->e.v;
1272 if (offset_minus)
1273 tcc_error("minus before '#' not supported for immediate values");
1274 if (offset->type == OP_IM8N || v < 0)
1275 v = -v;
1276 else
1277 opcode |= 1 << 23; // up
1278 if (v & 3) {
1279 tcc_error("immediate offset must be a multiple of 4");
1281 v >>= 2;
1282 if (v > 255) {
1283 tcc_error("immediate offset must be between -1020 and 1020");
1285 opcode |= v;
1286 } else if (offset->type == OP_REG32) {
1287 if (!offset_minus)
1288 opcode |= 1 << 23; // up
1289 opcode |= ENCODE_IMMEDIATE_FLAG; /* if set, it means it's NOT immediate */
1290 opcode |= offset->reg;
1291 tcc_error("Using register offset to register address is not possible here");
1292 } else if (offset->type == OP_VREG64) {
1293 opcode |= 16;
1294 opcode |= offset->reg;
1295 } else
1296 expect("immediate or register");
1298 asm_emit_unconditional_opcode((high_nibble << 28) | opcode);
1301 // Almost exactly the same as asm_single_data_transfer_opcode.
1302 // Difference: Offsets are smaller and multiples of 4; no shifts, no STREX, ENCODE_IMMEDIATE_FLAG is inverted again.
1303 static void asm_coprocessor_data_transfer_opcode(TCCState *s1, int token)
1305 Operand ops[3];
1306 uint8_t coprocessor;
1307 uint8_t coprocessor_destination_register;
1308 int preincrement = 0;
1309 int exclam = 0;
1310 int closed_bracket = 0;
1311 int op2_minus = 0;
1312 int long_transfer = 0;
1313 // Note: ldc p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1314 // Note: ldc p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1315 // Note: ldc p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1317 if (tok >= TOK_ASM_p0 && tok <= TOK_ASM_p15) {
1318 coprocessor = tok - TOK_ASM_p0;
1319 next();
1320 } else {
1321 expect("'c<number>'");
1324 skip(',');
1326 if (tok >= TOK_ASM_c0 && tok <= TOK_ASM_c15) {
1327 coprocessor_destination_register = tok - TOK_ASM_c0;
1328 next();
1329 } else {
1330 expect("'c<number>'");
1333 skip(',');
1334 skip('[');
1335 parse_operand(s1, &ops[1]);
1336 if (ops[1].type != OP_REG32) {
1337 expect("(first source operand) register");
1339 if (tok == ']') {
1340 next();
1341 closed_bracket = 1;
1342 // exclam = 1; // implicit in hardware; don't do it in software
1344 if (tok == ',') {
1345 next(); // skip ','
1346 if (tok == '-') {
1347 op2_minus = 1;
1348 next();
1350 parse_operand(s1, &ops[2]);
1351 if (ops[2].type == OP_REG32) {
1352 if (ops[2].reg == 15) {
1353 tcc_error("Using 'pc' for register offset in '%s' is not implemented by ARM", get_tok_str(token, NULL));
1355 } else if (ops[2].type == OP_VREG64) {
1356 tcc_error("'%s' does not support VFP register operand", get_tok_str(token, NULL));
1358 } else {
1359 // end of input expression in brackets--assume 0 offset
1360 ops[2].type = OP_IM8;
1361 ops[2].e.v = 0;
1362 preincrement = 1; // add offset before transfer
1364 if (!closed_bracket) {
1365 skip(']');
1366 preincrement = 1; // add offset before transfer
1367 if (tok == '!') {
1368 exclam = 1;
1369 next(); // skip '!'
1373 // TODO: Support options.
1375 if (token == TOK_ASM_ldc2 || token == TOK_ASM_stc2 || token == TOK_ASM_ldc2l || token == TOK_ASM_stc2l) {
1376 switch (token) {
1377 case TOK_ASM_ldc2l:
1378 long_transfer = 1; // long transfer
1379 /* fallthrough */
1380 case TOK_ASM_ldc2:
1381 asm_emit_coprocessor_data_transfer(0xF, coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 1);
1382 break;
1383 case TOK_ASM_stc2l:
1384 long_transfer = 1; // long transfer
1385 /* fallthrough */
1386 case TOK_ASM_stc2:
1387 asm_emit_coprocessor_data_transfer(0xF, coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 0);
1388 break;
1390 } else switch (ARM_INSTRUCTION_GROUP(token)) {
1391 case TOK_ASM_stcleq:
1392 long_transfer = 1;
1393 /* fallthrough */
1394 case TOK_ASM_stceq:
1395 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 0);
1396 break;
1397 case TOK_ASM_ldcleq:
1398 long_transfer = 1;
1399 /* fallthrough */
1400 case TOK_ASM_ldceq:
1401 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus, preincrement, exclam, long_transfer, 1);
1402 break;
1403 default:
1404 expect("coprocessor data transfer instruction");
1408 #if defined(TCC_ARM_VFP)
1409 #define CP_SINGLE_PRECISION_FLOAT 10
1410 #define CP_DOUBLE_PRECISION_FLOAT 11
1412 static void asm_floating_point_single_data_transfer_opcode(TCCState *s1, int token)
1414 Operand ops[3];
1415 uint8_t coprocessor = 0;
1416 uint8_t coprocessor_destination_register = 0;
1417 int long_transfer = 0;
1418 // Note: vldr p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
1419 // Note: Not allowed: vldr p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
1420 // Note: Not allowed: vldr p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
1422 parse_operand(s1, &ops[0]);
1423 if (ops[0].type == OP_VREG32) {
1424 coprocessor = CP_SINGLE_PRECISION_FLOAT;
1425 coprocessor_destination_register = ops[0].reg;
1426 long_transfer = coprocessor_destination_register & 1;
1427 coprocessor_destination_register >>= 1;
1428 } else if (ops[0].type == OP_VREG64) {
1429 coprocessor = CP_DOUBLE_PRECISION_FLOAT;
1430 coprocessor_destination_register = ops[0].reg;
1431 next();
1432 } else {
1433 expect("floating point register");
1436 skip(',');
1437 skip('[');
1438 parse_operand(s1, &ops[1]);
1439 if (ops[1].type != OP_REG32) {
1440 expect("(first source operand) register");
1442 if (tok == ',') {
1443 next(); // skip ','
1444 parse_operand(s1, &ops[2]);
1445 if (ops[2].type != OP_IM8 && ops[2].type != OP_IM8N) {
1446 expect("immediate offset");
1448 } else {
1449 // end of input expression in brackets--assume 0 offset
1450 ops[2].type = OP_IM8;
1451 ops[2].e.v = 0;
1453 skip(']');
1455 switch (ARM_INSTRUCTION_GROUP(token)) {
1456 case TOK_ASM_vldreq:
1457 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], 0, 1, 0, long_transfer, 1);
1458 break;
1459 case TOK_ASM_vstreq:
1460 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, coprocessor_destination_register, &ops[1], &ops[2], 0, 1, 0, long_transfer, 0);
1461 break;
1462 default:
1463 expect("floating point data transfer instruction");
1467 static void asm_floating_point_block_data_transfer_opcode(TCCState *s1, int token)
1469 uint8_t coprocessor = 0;
1470 int first_regset_register;
1471 int last_regset_register;
1472 uint8_t regset_item_count;
1473 uint8_t extra_register_bit = 0;
1474 int op0_exclam = 0;
1475 int load = 0;
1476 int preincrement = 0;
1477 Operand ops[1];
1478 Operand offset;
1479 switch (ARM_INSTRUCTION_GROUP(token)) {
1480 case TOK_ASM_vpusheq:
1481 case TOK_ASM_vpopeq:
1482 ops[0].type = OP_REG32;
1483 ops[0].reg = 13; // sp
1484 op0_exclam = 1;
1485 break;
1486 default:
1487 parse_operand(s1, &ops[0]);
1488 if (tok == '!') {
1489 op0_exclam = 1;
1490 next(); // skip '!'
1492 skip(',');
1495 skip('{');
1496 first_regset_register = asm_parse_vfp_regvar(tok, 1);
1497 if ((first_regset_register = asm_parse_vfp_regvar(tok, 1)) != -1) {
1498 coprocessor = CP_DOUBLE_PRECISION_FLOAT;
1499 next();
1500 } else if ((first_regset_register = asm_parse_vfp_regvar(tok, 0)) != -1) {
1501 coprocessor = CP_SINGLE_PRECISION_FLOAT;
1502 next();
1503 } else {
1504 expect("floating-point register");
1507 if (tok == '-') {
1508 next();
1509 if ((last_regset_register = asm_parse_vfp_regvar(tok, coprocessor == CP_DOUBLE_PRECISION_FLOAT)) != -1)
1510 next();
1511 else {
1512 expect("floating-point register");
1514 } else
1515 last_regset_register = first_regset_register;
1517 if (last_regset_register < first_regset_register) {
1518 tcc_error("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
1520 skip('}');
1521 // Note: 0 (one down) is not implemented by us regardless.
1522 regset_item_count = last_regset_register - first_regset_register + 1;
1523 if (coprocessor == CP_DOUBLE_PRECISION_FLOAT)
1524 regset_item_count <<= 1;
1525 else {
1526 extra_register_bit = first_regset_register & 1;
1527 first_regset_register >>= 1;
1529 offset.type = OP_IM8;
1530 offset.e.v = regset_item_count << 2;
1531 switch (ARM_INSTRUCTION_GROUP(token)) {
1532 case TOK_ASM_vstmeq: // post-increment store
1533 case TOK_ASM_vstmiaeq: // post-increment store
1534 break;
1535 case TOK_ASM_vpopeq:
1536 case TOK_ASM_vldmeq: // post-increment load
1537 case TOK_ASM_vldmiaeq: // post-increment load
1538 load = 1;
1539 break;
1540 case TOK_ASM_vldmdbeq: // pre-decrement load
1541 load = 1;
1542 /* fallthrough */
1543 case TOK_ASM_vpusheq:
1544 case TOK_ASM_vstmdbeq: // pre-decrement store
1545 offset.type = OP_IM8N;
1546 offset.e.v = -offset.e.v;
1547 preincrement = 1;
1548 break;
1549 default:
1550 expect("floating point block data transfer instruction");
1552 if (ops[0].type != OP_REG32)
1553 expect("(first operand) register");
1554 else if (ops[0].reg == 15)
1555 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL));
1556 else if (!op0_exclam && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vldmeq && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vldmiaeq && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vstmeq && ARM_INSTRUCTION_GROUP(token) != TOK_ASM_vstmiaeq)
1557 tcc_error("first operand of '%s' should have an exclamation mark", get_tok_str(token, NULL));
1558 else
1559 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, first_regset_register, &ops[0], &offset, 0, preincrement, op0_exclam, extra_register_bit, load);
1562 #define VMOV_FRACTIONAL_DIGITS 7
1563 #define VMOV_ONE 10000000 /* pow(10, VMOV_FRACTIONAL_DIGITS) */
1565 static uint32_t vmov_parse_fractional_part(const char* s)
1567 uint32_t result = 0;
1568 int i;
1569 for (i = 0; i < VMOV_FRACTIONAL_DIGITS; ++i) {
1570 char c = *s;
1571 result *= 10;
1572 if (c >= '0' && c <= '9') {
1573 result += (c - '0');
1574 ++s;
1577 if (*s)
1578 expect("decimal numeral");
1579 return result;
1582 static int vmov_linear_approx_index(uint32_t beginning, uint32_t end, uint32_t value)
1584 int i;
1585 uint32_t k;
1586 uint32_t xvalue;
1588 k = (end - beginning)/16;
1589 for (xvalue = beginning, i = 0; i < 16; ++i, xvalue += k) {
1590 if (value == xvalue)
1591 return i;
1593 //assert(0);
1594 return -1;
1597 static uint32_t vmov_parse_immediate_value() {
1598 uint32_t value;
1599 unsigned long integral_value;
1600 const char *p;
1602 if (tok != TOK_PPNUM) {
1603 expect("immediate value");
1605 p = tokc.str.data;
1606 errno = 0;
1607 integral_value = strtoul(p, (char **)&p, 0);
1609 if (errno || integral_value >= 32) {
1610 tcc_error("invalid floating-point immediate value");
1613 value = (uint32_t) integral_value * VMOV_ONE;
1614 if (*p == '.') {
1615 ++p;
1616 value += vmov_parse_fractional_part(p);
1618 next();
1619 return value;
1622 static uint8_t vmov_encode_immediate_value(uint32_t value)
1624 uint32_t limit;
1625 uint32_t end = 0;
1626 uint32_t beginning = 0;
1627 int r = -1;
1628 int n;
1629 int i;
1631 limit = 32 * VMOV_ONE;
1632 for (i = 0; i < 8; ++i) {
1633 if (value < limit) {
1634 end = limit;
1635 limit >>= 1;
1636 beginning = limit;
1637 r = i;
1638 } else
1639 limit >>= 1;
1641 if (r == -1 || value < beginning || value > end) {
1642 tcc_error("invalid decimal number for vmov: %d", value);
1644 n = vmov_linear_approx_index(beginning, end, value);
1645 return n | (((3 - r) & 0x7) << 4);
1648 // Not standalone.
1649 static void asm_floating_point_immediate_data_processing_opcode_tail(TCCState *s1, int token, uint8_t coprocessor, uint8_t CRd) {
1650 uint8_t opcode1 = 0;
1651 uint8_t opcode2 = 0;
1652 uint8_t operands[3] = {0, 0, 0};
1653 uint32_t immediate_value = 0;
1654 int op_minus = 0;
1655 uint8_t code;
1657 operands[0] = CRd;
1659 if (tok == '#' || tok == '$') {
1660 next();
1662 if (tok == '-') {
1663 op_minus = 1;
1664 next();
1666 immediate_value = vmov_parse_immediate_value();
1668 opcode1 = 11; // "Other" instruction
1669 switch (ARM_INSTRUCTION_GROUP(token)) {
1670 case TOK_ASM_vcmpeq_f32:
1671 case TOK_ASM_vcmpeq_f64:
1672 opcode2 = 2;
1673 operands[1] = 5;
1674 if (immediate_value) {
1675 expect("Immediate value 0");
1677 break;
1678 case TOK_ASM_vcmpeeq_f32:
1679 case TOK_ASM_vcmpeeq_f64:
1680 opcode2 = 6;
1681 operands[1] = 5;
1682 if (immediate_value) {
1683 expect("Immediate value 0");
1685 break;
1686 case TOK_ASM_vmoveq_f32:
1687 case TOK_ASM_vmoveq_f64:
1688 opcode2 = 0;
1689 if (op_minus)
1690 operands[1] = 0x8;
1691 else
1692 operands[1] = 0x0;
1693 code = vmov_encode_immediate_value(immediate_value);
1694 operands[1] |= code >> 4;
1695 operands[2] = code & 0xF;
1696 break;
1697 default:
1698 expect("known floating point with immediate instruction");
1701 if (coprocessor == CP_SINGLE_PRECISION_FLOAT) {
1702 if (operands[0] & 1)
1703 opcode1 |= 4;
1704 operands[0] >>= 1;
1707 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, operands[0], operands[1], operands[2], opcode2, 0);
1710 static void asm_floating_point_reg_arm_reg_transfer_opcode_tail(TCCState *s1, int token, int coprocessor, int nb_arm_regs, int nb_ops, Operand ops[3]) {
1711 uint8_t opcode1 = 0;
1712 uint8_t opcode2 = 0;
1713 switch (coprocessor) {
1714 case CP_SINGLE_PRECISION_FLOAT:
1715 // "vmov.f32 r2, s3" or "vmov.f32 s3, r2"
1716 if (nb_ops != 2 || nb_arm_regs != 1) {
1717 tcc_error("vmov.f32 only implemented for one VFP register operand and one ARM register operands");
1719 if (ops[0].type != OP_REG32) { // determine mode: load or store
1720 // need to swap operands 0 and 1
1721 memcpy(&ops[2], &ops[1], sizeof(ops[2]));
1722 memcpy(&ops[1], &ops[0], sizeof(ops[1]));
1723 memcpy(&ops[0], &ops[2], sizeof(ops[0]));
1724 } else
1725 opcode1 |= 1;
1727 if (ops[1].type == OP_VREG32) {
1728 if (ops[1].reg & 1)
1729 opcode2 |= 4;
1730 ops[1].reg >>= 1;
1733 if (ops[0].type == OP_VREG32) {
1734 if (ops[0].reg & 1)
1735 opcode1 |= 4;
1736 ops[0].reg >>= 1;
1739 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, ops[0].reg, (ops[1].type == OP_IM8) ? ops[1].e.v : ops[1].reg, 0x10, opcode2, 0);
1740 break;
1741 case CP_DOUBLE_PRECISION_FLOAT:
1742 if (nb_ops != 3 || nb_arm_regs != 2) {
1743 tcc_error("vmov.f32 only implemented for one VFP register operand and two ARM register operands");
1745 // Determine whether it's a store into a VFP register (vmov "d1, r2, r3") rather than "vmov r2, r3, d1"
1746 if (ops[0].type == OP_VREG64) {
1747 if (ops[2].type == OP_REG32) {
1748 Operand temp;
1749 // need to rotate operand list to the left
1750 memcpy(&temp, &ops[0], sizeof(temp));
1751 memcpy(&ops[0], &ops[1], sizeof(ops[0]));
1752 memcpy(&ops[1], &ops[2], sizeof(ops[1]));
1753 memcpy(&ops[2], &temp, sizeof(ops[2]));
1754 } else {
1755 tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands");
1757 } else if (ops[0].type != OP_REG32 || ops[1].type != OP_REG32 || ops[2].type != OP_VREG64) {
1758 tcc_error("vmov.f64 only implemented for one VFP register operand and two ARM register operands");
1759 } else {
1760 opcode1 |= 1;
1762 asm_emit_coprocessor_data_transfer(condition_code_of_token(token), coprocessor, ops[0].reg, &ops[1], &ops[2], 0, 0, 0, 1, opcode1);
1763 break;
1764 default:
1765 tcc_internal_error("unknown coprocessor");
1769 static void asm_floating_point_vcvt_data_processing_opcode(TCCState *s1, int token) {
1770 uint8_t coprocessor = 0;
1771 Operand ops[3];
1772 uint8_t opcode1 = 11;
1773 uint8_t opcode2 = 2;
1775 switch (ARM_INSTRUCTION_GROUP(token)) {
1776 case TOK_ASM_vcvtreq_s32_f64:
1777 case TOK_ASM_vcvtreq_u32_f64:
1778 case TOK_ASM_vcvteq_s32_f64:
1779 case TOK_ASM_vcvteq_u32_f64:
1780 case TOK_ASM_vcvteq_f64_s32:
1781 case TOK_ASM_vcvteq_f64_u32:
1782 case TOK_ASM_vcvteq_f32_f64:
1783 coprocessor = CP_DOUBLE_PRECISION_FLOAT;
1784 break;
1785 case TOK_ASM_vcvtreq_s32_f32:
1786 case TOK_ASM_vcvtreq_u32_f32:
1787 case TOK_ASM_vcvteq_s32_f32:
1788 case TOK_ASM_vcvteq_u32_f32:
1789 case TOK_ASM_vcvteq_f32_s32:
1790 case TOK_ASM_vcvteq_f32_u32:
1791 case TOK_ASM_vcvteq_f64_f32:
1792 coprocessor = CP_SINGLE_PRECISION_FLOAT;
1793 break;
1794 default:
1795 tcc_error("Unknown coprocessor for instruction '%s'", get_tok_str(token, NULL));
1798 parse_operand(s1, &ops[0]);
1799 ops[1].type = OP_IM8;
1800 ops[1].e.v = 8;
1801 /* floating-point -> integer */
1802 switch (ARM_INSTRUCTION_GROUP(token)) {
1803 case TOK_ASM_vcvtreq_s32_f32:
1804 case TOK_ASM_vcvtreq_s32_f64:
1805 case TOK_ASM_vcvteq_s32_f32:
1806 case TOK_ASM_vcvteq_s32_f64:
1807 ops[1].e.v |= 1; // signed
1808 /* fall through */
1809 case TOK_ASM_vcvteq_u32_f32:
1810 case TOK_ASM_vcvteq_u32_f64:
1811 case TOK_ASM_vcvtreq_u32_f32:
1812 case TOK_ASM_vcvtreq_u32_f64:
1813 ops[1].e.v |= 4; // to_integer (opc2)
1814 break;
1815 /* floating-point size conversion */
1816 case TOK_ASM_vcvteq_f64_f32:
1817 case TOK_ASM_vcvteq_f32_f64:
1818 ops[1].e.v = 7;
1819 break;
1822 skip(',');
1823 parse_operand(s1, &ops[2]);
1825 switch (ARM_INSTRUCTION_GROUP(token)) {
1826 /* floating-point -> integer */
1827 case TOK_ASM_vcvteq_s32_f32:
1828 case TOK_ASM_vcvteq_s32_f64:
1829 case TOK_ASM_vcvteq_u32_f32:
1830 case TOK_ASM_vcvteq_u32_f64:
1831 opcode2 |= 4; // round_zero
1832 break;
1834 /* integer -> floating-point */
1835 case TOK_ASM_vcvteq_f64_s32:
1836 case TOK_ASM_vcvteq_f32_s32:
1837 opcode2 |= 4; // signed--special
1838 break;
1840 /* floating-point size conversion */
1841 case TOK_ASM_vcvteq_f64_f32:
1842 case TOK_ASM_vcvteq_f32_f64:
1843 opcode2 |= 4; // always set
1844 break;
1847 switch (ARM_INSTRUCTION_GROUP(token)) {
1848 case TOK_ASM_vcvteq_f64_u32:
1849 case TOK_ASM_vcvteq_f64_s32:
1850 case TOK_ASM_vcvteq_f64_f32:
1851 if (ops[0].type == OP_VREG64 && ops[2].type == OP_VREG32) {
1852 } else {
1853 expect("d<number>, s<number>");
1855 break;
1856 default:
1857 if (coprocessor == CP_SINGLE_PRECISION_FLOAT) {
1858 if (ops[0].type == OP_VREG32 && ops[2].type == OP_VREG32) {
1859 } else {
1860 expect("s<number>, s<number>");
1862 } else if (coprocessor == CP_DOUBLE_PRECISION_FLOAT) {
1863 if (ops[0].type == OP_VREG32 && ops[2].type == OP_VREG64) {
1864 } else {
1865 expect("s<number>, d<number>");
1870 if (ops[2].type == OP_VREG32) {
1871 if (ops[2].reg & 1)
1872 opcode2 |= 1;
1873 ops[2].reg >>= 1;
1875 if (ops[0].type == OP_VREG32) {
1876 if (ops[0].reg & 1)
1877 opcode1 |= 4;
1878 ops[0].reg >>= 1;
1880 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, ops[0].reg, (ops[1].type == OP_IM8) ? ops[1].e.v : ops[1].reg, (ops[2].type == OP_IM8) ? ops[2].e.v : ops[2].reg, opcode2, 0);
1883 static void asm_floating_point_data_processing_opcode(TCCState *s1, int token) {
1884 uint8_t coprocessor = CP_SINGLE_PRECISION_FLOAT;
1885 uint8_t opcode1 = 0;
1886 uint8_t opcode2 = 0; // (0 || 2) | register selection
1887 Operand ops[3];
1888 uint8_t nb_ops = 0;
1889 int vmov = 0;
1890 int nb_arm_regs = 0;
1892 /* TODO:
1893 Instruction opcode opcode2 Reason
1894 =============================================================
1895 - 1?00 ?1? Undefined
1896 VFNMS 1?01 ?0? Must be unconditional
1897 VFNMA 1?01 ?1? Must be unconditional
1898 VFMA 1?10 ?0? Must be unconditional
1899 VFMS 1?10 ?1? Must be unconditional
1901 VMOV Fd, Fm
1902 VMOV Sn, Sm, Rd, Rn
1903 VMOV Rd, Rn, Sn, Sm
1904 VMOV Dn[0], Rd
1905 VMOV Rd, Dn[0]
1906 VMOV Dn[1], Rd
1907 VMOV Rd, Dn[1]
1910 switch (ARM_INSTRUCTION_GROUP(token)) {
1911 case TOK_ASM_vmlaeq_f64:
1912 case TOK_ASM_vmlseq_f64:
1913 case TOK_ASM_vnmlseq_f64:
1914 case TOK_ASM_vnmlaeq_f64:
1915 case TOK_ASM_vmuleq_f64:
1916 case TOK_ASM_vnmuleq_f64:
1917 case TOK_ASM_vaddeq_f64:
1918 case TOK_ASM_vsubeq_f64:
1919 case TOK_ASM_vdiveq_f64:
1920 case TOK_ASM_vnegeq_f64:
1921 case TOK_ASM_vabseq_f64:
1922 case TOK_ASM_vsqrteq_f64:
1923 case TOK_ASM_vcmpeq_f64:
1924 case TOK_ASM_vcmpeeq_f64:
1925 case TOK_ASM_vmoveq_f64:
1926 coprocessor = CP_DOUBLE_PRECISION_FLOAT;
1929 switch (ARM_INSTRUCTION_GROUP(token)) {
1930 case TOK_ASM_vmoveq_f32:
1931 case TOK_ASM_vmoveq_f64:
1932 vmov = 1;
1933 break;
1936 for (nb_ops = 0; nb_ops < 3; ) {
1937 // Note: Necessary because parse_operand can't parse decimal numerals.
1938 if (nb_ops == 1 && (tok == '#' || tok == '$' || tok == TOK_PPNUM || tok == '-')) {
1939 asm_floating_point_immediate_data_processing_opcode_tail(s1, token, coprocessor, ops[0].reg);
1940 return;
1942 parse_operand(s1, &ops[nb_ops]);
1943 if (vmov && ops[nb_ops].type == OP_REG32) {
1944 ++nb_arm_regs;
1945 } else if (ops[nb_ops].type == OP_VREG32) {
1946 if (coprocessor != CP_SINGLE_PRECISION_FLOAT) {
1947 expect("'s<number>'");
1949 } else if (ops[nb_ops].type == OP_VREG64) {
1950 if (coprocessor != CP_DOUBLE_PRECISION_FLOAT) {
1951 expect("'d<number>'");
1953 } else {
1954 expect("floating point register");
1956 ++nb_ops;
1957 if (tok == ',')
1958 next();
1959 else
1960 break;
1963 if (nb_arm_regs == 0) {
1964 if (nb_ops == 2) { // implicit
1965 memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2]
1966 memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit
1967 nb_ops = 3;
1969 if (nb_ops < 3) {
1970 tcc_error("Not enough operands for '%s' (%u)", get_tok_str(token, NULL), nb_ops);
1974 switch (ARM_INSTRUCTION_GROUP(token)) {
1975 case TOK_ASM_vmlaeq_f32:
1976 case TOK_ASM_vmlaeq_f64:
1977 opcode1 = 0;
1978 opcode2 = 0;
1979 break;
1980 case TOK_ASM_vmlseq_f32:
1981 case TOK_ASM_vmlseq_f64:
1982 opcode1 = 0;
1983 opcode2 = 2;
1984 break;
1985 case TOK_ASM_vnmlseq_f32:
1986 case TOK_ASM_vnmlseq_f64:
1987 opcode1 = 1;
1988 opcode2 = 0;
1989 break;
1990 case TOK_ASM_vnmlaeq_f32:
1991 case TOK_ASM_vnmlaeq_f64:
1992 opcode1 = 1;
1993 opcode2 = 2;
1994 break;
1995 case TOK_ASM_vmuleq_f32:
1996 case TOK_ASM_vmuleq_f64:
1997 opcode1 = 2;
1998 opcode2 = 0;
1999 break;
2000 case TOK_ASM_vnmuleq_f32:
2001 case TOK_ASM_vnmuleq_f64:
2002 opcode1 = 2;
2003 opcode2 = 2;
2004 break;
2005 case TOK_ASM_vaddeq_f32:
2006 case TOK_ASM_vaddeq_f64:
2007 opcode1 = 3;
2008 opcode2 = 0;
2009 break;
2010 case TOK_ASM_vsubeq_f32:
2011 case TOK_ASM_vsubeq_f64:
2012 opcode1 = 3;
2013 opcode2 = 2;
2014 break;
2015 case TOK_ASM_vdiveq_f32:
2016 case TOK_ASM_vdiveq_f64:
2017 opcode1 = 8;
2018 opcode2 = 0;
2019 break;
2020 case TOK_ASM_vnegeq_f32:
2021 case TOK_ASM_vnegeq_f64:
2022 opcode1 = 11; // Other" instruction
2023 opcode2 = 2;
2024 ops[1].type = OP_IM8;
2025 ops[1].e.v = 1;
2026 break;
2027 case TOK_ASM_vabseq_f32:
2028 case TOK_ASM_vabseq_f64:
2029 opcode1 = 11; // "Other" instruction
2030 opcode2 = 6;
2031 ops[1].type = OP_IM8;
2032 ops[1].e.v = 0;
2033 break;
2034 case TOK_ASM_vsqrteq_f32:
2035 case TOK_ASM_vsqrteq_f64:
2036 opcode1 = 11; // "Other" instruction
2037 opcode2 = 6;
2038 ops[1].type = OP_IM8;
2039 ops[1].e.v = 1;
2040 break;
2041 case TOK_ASM_vcmpeq_f32:
2042 case TOK_ASM_vcmpeq_f64:
2043 opcode1 = 11; // "Other" instruction
2044 opcode2 = 2;
2045 ops[1].type = OP_IM8;
2046 ops[1].e.v = 4;
2047 break;
2048 case TOK_ASM_vcmpeeq_f32:
2049 case TOK_ASM_vcmpeeq_f64:
2050 opcode1 = 11; // "Other" instruction
2051 opcode2 = 6;
2052 ops[1].type = OP_IM8;
2053 ops[1].e.v = 4;
2054 break;
2055 case TOK_ASM_vmoveq_f32:
2056 case TOK_ASM_vmoveq_f64:
2057 if (nb_arm_regs > 0) { // vmov.f32 r2, s3 or similar
2058 asm_floating_point_reg_arm_reg_transfer_opcode_tail(s1, token, coprocessor, nb_arm_regs, nb_ops, ops);
2059 return;
2060 } else {
2061 opcode1 = 11; // "Other" instruction
2062 opcode2 = 2;
2063 ops[1].type = OP_IM8;
2064 ops[1].e.v = 0;
2066 break;
2067 default:
2068 expect("known floating point instruction");
2071 if (coprocessor == CP_SINGLE_PRECISION_FLOAT) {
2072 if (ops[2].type == OP_VREG32) {
2073 if (ops[2].reg & 1)
2074 opcode2 |= 1;
2075 ops[2].reg >>= 1;
2078 if (ops[1].type == OP_VREG32) {
2079 if (ops[1].reg & 1)
2080 opcode2 |= 4;
2081 ops[1].reg >>= 1;
2084 if (ops[0].type == OP_VREG32) {
2085 if (ops[0].reg & 1)
2086 opcode1 |= 4;
2087 ops[0].reg >>= 1;
2091 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode1, ops[0].reg, (ops[1].type == OP_IM8) ? ops[1].e.v : ops[1].reg, (ops[2].type == OP_IM8) ? ops[2].e.v : ops[2].reg, opcode2, 0);
2094 static int asm_parse_vfp_status_regvar(int t)
2096 switch (t) {
2097 case TOK_ASM_fpsid:
2098 return 0;
2099 case TOK_ASM_fpscr:
2100 return 1;
2101 case TOK_ASM_fpexc:
2102 return 8;
2103 default:
2104 return -1;
2108 static void asm_floating_point_status_register_opcode(TCCState* s1, int token)
2110 uint8_t coprocessor = CP_SINGLE_PRECISION_FLOAT;
2111 uint8_t opcode;
2112 int vfp_sys_reg = -1;
2113 Operand arm_operand;
2114 switch (ARM_INSTRUCTION_GROUP(token)) {
2115 case TOK_ASM_vmrseq:
2116 opcode = 0xf;
2117 if (tok == TOK_ASM_apsr_nzcv) {
2118 arm_operand.type = OP_REG32;
2119 arm_operand.reg = 15; // not PC
2120 next(); // skip apsr_nzcv
2121 } else {
2122 parse_operand(s1, &arm_operand);
2123 if (arm_operand.type == OP_REG32 && arm_operand.reg == 15) {
2124 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL));
2128 skip(',');
2129 vfp_sys_reg = asm_parse_vfp_status_regvar(tok);
2130 next(); // skip vfp sys reg
2131 if (arm_operand.type == OP_REG32 && arm_operand.reg == 15 && vfp_sys_reg != 1) {
2132 tcc_error("'%s' only supports the variant 'vmrs apsr_nzcv, fpscr' here", get_tok_str(token, NULL));
2134 break;
2135 case TOK_ASM_vmsreq:
2136 opcode = 0xe;
2137 vfp_sys_reg = asm_parse_vfp_status_regvar(tok);
2138 next(); // skip vfp sys reg
2139 skip(',');
2140 parse_operand(s1, &arm_operand);
2141 if (arm_operand.type == OP_REG32 && arm_operand.reg == 15) {
2142 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token, NULL));
2144 break;
2145 default:
2146 expect("floating point status register instruction");
2148 if (vfp_sys_reg == -1) {
2149 expect("VFP system register");
2151 if (arm_operand.type != OP_REG32) {
2152 expect("ARM register");
2154 asm_emit_coprocessor_opcode(condition_code_of_token(token), coprocessor, opcode, arm_operand.reg, vfp_sys_reg, 0x10, 0, 0);
2157 #endif
2159 static void asm_misc_single_data_transfer_opcode(TCCState *s1, int token)
2161 Operand ops[3];
2162 int exclam = 0;
2163 int closed_bracket = 0;
2164 int op2_minus = 0;
2165 uint32_t opcode = (1 << 7) | (1 << 4);
2167 /* Note:
2168 The argument syntax is exactly the same as in arm_single_data_transfer_opcode, except that there's no STREX argument form.
2169 The main difference between this function and asm_misc_single_data_transfer_opcode is that the immediate values here must be smaller.
2170 Also, the combination (P=0, W=1) is unpredictable here.
2171 The immediate flag has moved to bit index 22--and its meaning has flipped.
2172 The immediate value itself has been split into two parts: one at bits 11...8, one at bits 3...0
2173 bit 26 (Load/Store instruction) is unset here.
2174 bits 7 and 4 are set here. */
2176 // Here: 0 0 0 P U I W L << 20
2177 // [compare single data transfer: 0 1 I P U B W L << 20]
2179 parse_operand(s1, &ops[0]);
2180 if (ops[0].type == OP_REG32)
2181 opcode |= ENCODE_RD(ops[0].reg);
2182 else {
2183 expect("(destination operand) register");
2185 if (tok != ',')
2186 expect("at least two arguments");
2187 else
2188 next(); // skip ','
2189 skip('[');
2190 parse_operand(s1, &ops[1]);
2191 if (ops[1].type == OP_REG32)
2192 opcode |= ENCODE_RN(ops[1].reg);
2193 else {
2194 expect("(first source operand) register");
2196 if (tok == ']') {
2197 next();
2198 closed_bracket = 1;
2199 // exclam = 1; // implicit in hardware; don't do it in software
2201 if (tok == ',') {
2202 next(); // skip ','
2203 if (tok == '-') {
2204 op2_minus = 1;
2205 next();
2207 parse_operand(s1, &ops[2]);
2208 } else {
2209 // end of input expression in brackets--assume 0 offset
2210 ops[2].type = OP_IM8;
2211 ops[2].e.v = 0;
2212 opcode |= 1 << 24; // add offset before transfer
2214 if (!closed_bracket) {
2215 skip(']');
2216 opcode |= 1 << 24; // add offset before transfer
2217 if (tok == '!') {
2218 exclam = 1;
2219 next(); // skip '!'
2223 if (exclam) {
2224 if ((opcode & (1 << 24)) == 0) {
2225 tcc_error("result of '%s' would be unpredictable here", get_tok_str(token, NULL));
2227 opcode |= 1 << 21; // write offset back into register
2230 if (ops[2].type == OP_IM32 || ops[2].type == OP_IM8 || ops[2].type == OP_IM8N) {
2231 int v = ops[2].e.v;
2232 if (op2_minus)
2233 tcc_error("minus before '#' not supported for immediate values");
2234 if (v >= 0) {
2235 opcode |= 1 << 23; // up
2236 if (v >= 0x100)
2237 tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
2238 else {
2239 // bits 11...8: immediate hi nibble
2240 // bits 3...0: immediate lo nibble
2241 opcode |= (v & 0xF0) << 4;
2242 opcode |= v & 0xF;
2244 } else { // down
2245 if (v <= -0x100)
2246 tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
2247 else {
2248 v = -v;
2249 // bits 11...8: immediate hi nibble
2250 // bits 3...0: immediate lo nibble
2251 opcode |= (v & 0xF0) << 4;
2252 opcode |= v & 0xF;
2255 opcode |= 1 << 22; // not ENCODE_IMMEDIATE_FLAG;
2256 } else if (ops[2].type == OP_REG32) {
2257 if (!op2_minus)
2258 opcode |= 1 << 23; // up
2259 opcode |= ops[2].reg;
2260 } else
2261 expect("register");
2263 switch (ARM_INSTRUCTION_GROUP(token)) {
2264 case TOK_ASM_ldrsheq:
2265 opcode |= 1 << 5; // halfword, not byte
2266 /* fallthrough */
2267 case TOK_ASM_ldrsbeq:
2268 opcode |= 1 << 6; // sign extend
2269 opcode |= 1 << 20; // L
2270 asm_emit_opcode(token, opcode);
2271 break;
2272 case TOK_ASM_ldrheq:
2273 opcode |= 1 << 5; // halfword, not byte
2274 opcode |= 1 << 20; // L
2275 asm_emit_opcode(token, opcode);
2276 break;
2277 case TOK_ASM_strheq:
2278 opcode |= 1 << 5; // halfword, not byte
2279 asm_emit_opcode(token, opcode);
2280 break;
2284 /* Note: almost dupe of encbranch in arm-gen.c */
2285 static uint32_t encbranchoffset(int pos, int addr, int fail)
2287 addr-=pos+8;
2288 addr/=4;
2289 if(addr>=0x7fffff || addr<-0x800000) {
2290 if(fail)
2291 tcc_error("branch offset is too far");
2292 return 0;
2294 return /*not 0x0A000000|*/(addr&0xffffff);
2297 static void asm_branch_opcode(TCCState *s1, int token)
2299 int jmp_disp = 0;
2300 Operand op;
2301 ExprValue e;
2302 ElfSym *esym;
2304 switch (ARM_INSTRUCTION_GROUP(token)) {
2305 case TOK_ASM_beq:
2306 case TOK_ASM_bleq:
2307 asm_expr(s1, &e);
2308 esym = elfsym(e.sym);
2309 if (!esym || esym->st_shndx != cur_text_section->sh_num) {
2310 tcc_error("invalid branch target");
2312 jmp_disp = encbranchoffset(ind, e.v + esym->st_value, 1);
2313 break;
2314 default:
2315 parse_operand(s1, &op);
2316 break;
2318 switch (ARM_INSTRUCTION_GROUP(token)) {
2319 case TOK_ASM_beq:
2320 asm_emit_opcode(token, (0xa << 24) | (jmp_disp & 0xffffff));
2321 break;
2322 case TOK_ASM_bleq:
2323 asm_emit_opcode(token, (0xb << 24) | (jmp_disp & 0xffffff));
2324 break;
2325 case TOK_ASM_bxeq:
2326 if (op.type != OP_REG32)
2327 expect("register");
2328 else
2329 asm_emit_opcode(token, (0x12fff1 << 4) | op.reg);
2330 break;
2331 case TOK_ASM_blxeq:
2332 if (op.type != OP_REG32)
2333 expect("register");
2334 else
2335 asm_emit_opcode(token, (0x12fff3 << 4) | op.reg);
2336 break;
2337 default:
2338 expect("branch instruction");
2342 ST_FUNC void asm_opcode(TCCState *s1, int token)
2344 while (token == TOK_LINEFEED) {
2345 next();
2346 token = tok;
2348 if (token == TOK_EOF)
2349 return;
2350 if (token < TOK_ASM_nopeq) { // no condition code
2351 switch (token) {
2352 case TOK_ASM_cdp2:
2353 asm_coprocessor_opcode(s1, token);
2354 return;
2355 case TOK_ASM_ldc2:
2356 case TOK_ASM_ldc2l:
2357 case TOK_ASM_stc2:
2358 case TOK_ASM_stc2l:
2359 asm_coprocessor_data_transfer_opcode(s1, token);
2360 return;
2361 default:
2362 expect("instruction");
2366 switch (ARM_INSTRUCTION_GROUP(token)) {
2367 case TOK_ASM_pusheq:
2368 case TOK_ASM_popeq:
2369 case TOK_ASM_stmdaeq:
2370 case TOK_ASM_ldmdaeq:
2371 case TOK_ASM_stmeq:
2372 case TOK_ASM_ldmeq:
2373 case TOK_ASM_stmiaeq:
2374 case TOK_ASM_ldmiaeq:
2375 case TOK_ASM_stmdbeq:
2376 case TOK_ASM_ldmdbeq:
2377 case TOK_ASM_stmibeq:
2378 case TOK_ASM_ldmibeq:
2379 asm_block_data_transfer_opcode(s1, token);
2380 return;
2381 case TOK_ASM_nopeq:
2382 case TOK_ASM_wfeeq:
2383 case TOK_ASM_wfieq:
2384 asm_nullary_opcode(token);
2385 return;
2386 case TOK_ASM_swieq:
2387 case TOK_ASM_svceq:
2388 asm_unary_opcode(s1, token);
2389 return;
2390 case TOK_ASM_beq:
2391 case TOK_ASM_bleq:
2392 case TOK_ASM_bxeq:
2393 case TOK_ASM_blxeq:
2394 asm_branch_opcode(s1, token);
2395 return;
2396 case TOK_ASM_clzeq:
2397 case TOK_ASM_sxtbeq:
2398 case TOK_ASM_sxtheq:
2399 case TOK_ASM_uxtbeq:
2400 case TOK_ASM_uxtheq:
2401 case TOK_ASM_movteq:
2402 case TOK_ASM_movweq:
2403 asm_binary_opcode(s1, token);
2404 return;
2406 case TOK_ASM_ldreq:
2407 case TOK_ASM_ldrbeq:
2408 case TOK_ASM_streq:
2409 case TOK_ASM_strbeq:
2410 case TOK_ASM_ldrexeq:
2411 case TOK_ASM_ldrexbeq:
2412 case TOK_ASM_strexeq:
2413 case TOK_ASM_strexbeq:
2414 asm_single_data_transfer_opcode(s1, token);
2415 return;
2417 case TOK_ASM_ldrheq:
2418 case TOK_ASM_ldrsheq:
2419 case TOK_ASM_ldrsbeq:
2420 case TOK_ASM_strheq:
2421 asm_misc_single_data_transfer_opcode(s1, token);
2422 return;
2424 case TOK_ASM_andeq:
2425 case TOK_ASM_eoreq:
2426 case TOK_ASM_subeq:
2427 case TOK_ASM_rsbeq:
2428 case TOK_ASM_addeq:
2429 case TOK_ASM_adceq:
2430 case TOK_ASM_sbceq:
2431 case TOK_ASM_rsceq:
2432 case TOK_ASM_tsteq:
2433 case TOK_ASM_teqeq:
2434 case TOK_ASM_cmpeq:
2435 case TOK_ASM_cmneq:
2436 case TOK_ASM_orreq:
2437 case TOK_ASM_moveq:
2438 case TOK_ASM_biceq:
2439 case TOK_ASM_mvneq:
2440 case TOK_ASM_andseq:
2441 case TOK_ASM_eorseq:
2442 case TOK_ASM_subseq:
2443 case TOK_ASM_rsbseq:
2444 case TOK_ASM_addseq:
2445 case TOK_ASM_adcseq:
2446 case TOK_ASM_sbcseq:
2447 case TOK_ASM_rscseq:
2448 // case TOK_ASM_tstseq:
2449 // case TOK_ASM_teqseq:
2450 // case TOK_ASM_cmpseq:
2451 // case TOK_ASM_cmnseq:
2452 case TOK_ASM_orrseq:
2453 case TOK_ASM_movseq:
2454 case TOK_ASM_bicseq:
2455 case TOK_ASM_mvnseq:
2456 asm_data_processing_opcode(s1, token);
2457 return;
2459 case TOK_ASM_lsleq:
2460 case TOK_ASM_lslseq:
2461 case TOK_ASM_lsreq:
2462 case TOK_ASM_lsrseq:
2463 case TOK_ASM_asreq:
2464 case TOK_ASM_asrseq:
2465 case TOK_ASM_roreq:
2466 case TOK_ASM_rorseq:
2467 case TOK_ASM_rrxseq:
2468 case TOK_ASM_rrxeq:
2469 asm_shift_opcode(s1, token);
2470 return;
2472 case TOK_ASM_muleq:
2473 case TOK_ASM_mulseq:
2474 case TOK_ASM_mlaeq:
2475 case TOK_ASM_mlaseq:
2476 asm_multiplication_opcode(s1, token);
2477 return;
2479 case TOK_ASM_smulleq:
2480 case TOK_ASM_smullseq:
2481 case TOK_ASM_umulleq:
2482 case TOK_ASM_umullseq:
2483 case TOK_ASM_smlaleq:
2484 case TOK_ASM_smlalseq:
2485 case TOK_ASM_umlaleq:
2486 case TOK_ASM_umlalseq:
2487 asm_long_multiplication_opcode(s1, token);
2488 return;
2490 case TOK_ASM_cdpeq:
2491 case TOK_ASM_mcreq:
2492 case TOK_ASM_mrceq:
2493 asm_coprocessor_opcode(s1, token);
2494 return;
2496 case TOK_ASM_ldceq:
2497 case TOK_ASM_ldcleq:
2498 case TOK_ASM_stceq:
2499 case TOK_ASM_stcleq:
2500 asm_coprocessor_data_transfer_opcode(s1, token);
2501 return;
2503 #if defined(TCC_ARM_VFP)
2504 case TOK_ASM_vldreq:
2505 case TOK_ASM_vstreq:
2506 asm_floating_point_single_data_transfer_opcode(s1, token);
2507 return;
2509 case TOK_ASM_vmlaeq_f32:
2510 case TOK_ASM_vmlseq_f32:
2511 case TOK_ASM_vnmlseq_f32:
2512 case TOK_ASM_vnmlaeq_f32:
2513 case TOK_ASM_vmuleq_f32:
2514 case TOK_ASM_vnmuleq_f32:
2515 case TOK_ASM_vaddeq_f32:
2516 case TOK_ASM_vsubeq_f32:
2517 case TOK_ASM_vdiveq_f32:
2518 case TOK_ASM_vnegeq_f32:
2519 case TOK_ASM_vabseq_f32:
2520 case TOK_ASM_vsqrteq_f32:
2521 case TOK_ASM_vcmpeq_f32:
2522 case TOK_ASM_vcmpeeq_f32:
2523 case TOK_ASM_vmoveq_f32:
2524 case TOK_ASM_vmlaeq_f64:
2525 case TOK_ASM_vmlseq_f64:
2526 case TOK_ASM_vnmlseq_f64:
2527 case TOK_ASM_vnmlaeq_f64:
2528 case TOK_ASM_vmuleq_f64:
2529 case TOK_ASM_vnmuleq_f64:
2530 case TOK_ASM_vaddeq_f64:
2531 case TOK_ASM_vsubeq_f64:
2532 case TOK_ASM_vdiveq_f64:
2533 case TOK_ASM_vnegeq_f64:
2534 case TOK_ASM_vabseq_f64:
2535 case TOK_ASM_vsqrteq_f64:
2536 case TOK_ASM_vcmpeq_f64:
2537 case TOK_ASM_vcmpeeq_f64:
2538 case TOK_ASM_vmoveq_f64:
2539 asm_floating_point_data_processing_opcode(s1, token);
2540 return;
2542 case TOK_ASM_vcvtreq_s32_f32:
2543 case TOK_ASM_vcvtreq_s32_f64:
2544 case TOK_ASM_vcvteq_s32_f32:
2545 case TOK_ASM_vcvteq_s32_f64:
2546 case TOK_ASM_vcvtreq_u32_f32:
2547 case TOK_ASM_vcvtreq_u32_f64:
2548 case TOK_ASM_vcvteq_u32_f32:
2549 case TOK_ASM_vcvteq_u32_f64:
2550 case TOK_ASM_vcvteq_f64_s32:
2551 case TOK_ASM_vcvteq_f32_s32:
2552 case TOK_ASM_vcvteq_f64_u32:
2553 case TOK_ASM_vcvteq_f32_u32:
2554 case TOK_ASM_vcvteq_f64_f32:
2555 case TOK_ASM_vcvteq_f32_f64:
2556 asm_floating_point_vcvt_data_processing_opcode(s1, token);
2557 return;
2559 case TOK_ASM_vpusheq:
2560 case TOK_ASM_vpopeq:
2561 case TOK_ASM_vldmeq:
2562 case TOK_ASM_vldmiaeq:
2563 case TOK_ASM_vldmdbeq:
2564 case TOK_ASM_vstmeq:
2565 case TOK_ASM_vstmiaeq:
2566 case TOK_ASM_vstmdbeq:
2567 asm_floating_point_block_data_transfer_opcode(s1, token);
2568 return;
2570 case TOK_ASM_vmsreq:
2571 case TOK_ASM_vmrseq:
2572 asm_floating_point_status_register_opcode(s1, token);
2573 return;
2574 #endif
2576 default:
2577 expect("known instruction");
2581 ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier)
2583 int r, reg, size, val;
2585 r = sv->r;
2586 if ((r & VT_VALMASK) == VT_CONST) {
2587 if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n' &&
2588 modifier != 'P')
2589 cstr_ccat(add_str, '#');
2590 if (r & VT_SYM) {
2591 const char *name = get_tok_str(sv->sym->v, NULL);
2592 if (sv->sym->v >= SYM_FIRST_ANOM) {
2593 /* In case of anonymous symbols ("L.42", used
2594 for static data labels) we can't find them
2595 in the C symbol table when later looking up
2596 this name. So enter them now into the asm label
2597 list when we still know the symbol. */
2598 get_asm_sym(tok_alloc(name, strlen(name))->tok, sv->sym);
2600 if (tcc_state->leading_underscore)
2601 cstr_ccat(add_str, '_');
2602 cstr_cat(add_str, name, -1);
2603 if ((uint32_t) sv->c.i == 0)
2604 goto no_offset;
2605 cstr_ccat(add_str, '+');
2607 val = sv->c.i;
2608 if (modifier == 'n')
2609 val = -val;
2610 cstr_printf(add_str, "%d", (int) sv->c.i);
2611 no_offset:;
2612 } else if ((r & VT_VALMASK) == VT_LOCAL) {
2613 cstr_printf(add_str, "[fp,#%d]", (int) sv->c.i);
2614 } else if (r & VT_LVAL) {
2615 reg = r & VT_VALMASK;
2616 if (reg >= VT_CONST)
2617 tcc_internal_error("");
2618 cstr_printf(add_str, "[%s]",
2619 get_tok_str(TOK_ASM_r0 + reg, NULL));
2620 } else {
2621 /* register case */
2622 reg = r & VT_VALMASK;
2623 if (reg >= VT_CONST)
2624 tcc_internal_error("");
2626 /* choose register operand size */
2627 if ((sv->type.t & VT_BTYPE) == VT_BYTE ||
2628 (sv->type.t & VT_BTYPE) == VT_BOOL)
2629 size = 1;
2630 else if ((sv->type.t & VT_BTYPE) == VT_SHORT)
2631 size = 2;
2632 else
2633 size = 4;
2635 if (modifier == 'b') {
2636 size = 1;
2637 } else if (modifier == 'w') {
2638 size = 2;
2639 } else if (modifier == 'k') {
2640 size = 4;
2643 switch (size) {
2644 default:
2645 reg = TOK_ASM_r0 + reg;
2646 break;
2648 cstr_printf(add_str, "%s", get_tok_str(reg, NULL));
2652 /* generate prolog and epilog code for asm statement */
2653 ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
2654 int nb_outputs, int is_output,
2655 uint8_t *clobber_regs,
2656 int out_reg)
2658 uint8_t regs_allocated[NB_ASM_REGS];
2659 ASMOperand *op;
2660 int i, reg;
2661 uint32_t saved_regset = 0;
2663 // TODO: Check non-E ABI.
2664 // Note: Technically, r13 (sp) is also callee-saved--but that does not matter yet
2665 static const uint8_t reg_saved[] = { 4, 5, 6, 7, 8, 9 /* Note: sometimes special reg "sb" */ , 10, 11 };
2667 /* mark all used registers */
2668 memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated));
2669 for(i = 0; i < nb_operands;i++) {
2670 op = &operands[i];
2671 if (op->reg >= 0)
2672 regs_allocated[op->reg] = 1;
2674 for(i = 0; i < sizeof(reg_saved)/sizeof(reg_saved[0]); i++) {
2675 reg = reg_saved[i];
2676 if (regs_allocated[reg])
2677 saved_regset |= 1 << reg;
2680 if (!is_output) { // prolog
2681 /* generate reg save code */
2682 if (saved_regset)
2683 gen_le32(0xe92d0000 | saved_regset); // push {...}
2685 /* generate load code */
2686 for(i = 0; i < nb_operands; i++) {
2687 op = &operands[i];
2688 if (op->reg >= 0) {
2689 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL &&
2690 op->is_memory) {
2691 /* memory reference case (for both input and
2692 output cases) */
2693 SValue sv;
2694 sv = *op->vt;
2695 sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL | VT_LVAL;
2696 sv.type.t = VT_PTR;
2697 load(op->reg, &sv);
2698 } else if (i >= nb_outputs || op->is_rw) { // not write-only
2699 /* load value in register */
2700 load(op->reg, op->vt);
2701 if (op->is_llong)
2702 tcc_error("long long not implemented");
2706 } else { // epilog
2707 /* generate save code */
2708 for(i = 0 ; i < nb_outputs; i++) {
2709 op = &operands[i];
2710 if (op->reg >= 0) {
2711 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
2712 if (!op->is_memory) {
2713 SValue sv;
2714 sv = *op->vt;
2715 sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
2716 sv.type.t = VT_PTR;
2717 load(out_reg, &sv);
2719 sv = *op->vt;
2720 sv.r = (sv.r & ~VT_VALMASK) | out_reg;
2721 store(op->reg, &sv);
2723 } else {
2724 store(op->reg, op->vt);
2725 if (op->is_llong)
2726 tcc_error("long long not implemented");
2731 /* generate reg restore code */
2732 if (saved_regset)
2733 gen_le32(0xe8bd0000 | saved_regset); // pop {...}
2737 /* return the constraint priority (we allocate first the lowest
2738 numbered constraints) */
2739 static inline int constraint_priority(const char *str)
2741 int priority, c, pr;
2743 /* we take the lowest priority */
2744 priority = 0;
2745 for(;;) {
2746 c = *str;
2747 if (c == '\0')
2748 break;
2749 str++;
2750 switch(c) {
2751 case 'l': // in ARM mode, that's an alias for 'r' [ARM].
2752 case 'r': // register [general]
2753 case 'p': // valid memory address for load,store [general]
2754 pr = 3;
2755 break;
2756 case 'M': // integer constant for shifts [ARM]
2757 case 'I': // integer valid for data processing instruction immediate
2758 case 'J': // integer in range -4095...4095
2760 case 'i': // immediate integer operand, including symbolic constants [general]
2761 case 'm': // memory operand [general]
2762 case 'g': // general-purpose-register, memory, immediate integer [general]
2763 pr = 4;
2764 break;
2765 default:
2766 tcc_error("unknown constraint '%c'", c);
2768 if (pr > priority)
2769 priority = pr;
2771 return priority;
2774 static const char *skip_constraint_modifiers(const char *p)
2776 /* Constraint modifier:
2777 = Operand is written to by this instruction
2778 + Operand is both read and written to by this instruction
2779 % Instruction is commutative for this operand and the following operand.
2781 Per-alternative constraint modifier:
2782 & Operand is clobbered before the instruction is done using the input operands
2784 while (*p == '=' || *p == '&' || *p == '+' || *p == '%')
2785 p++;
2786 return p;
2789 #define REG_OUT_MASK 0x01
2790 #define REG_IN_MASK 0x02
2792 #define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
2794 ST_FUNC void asm_compute_constraints(ASMOperand *operands,
2795 int nb_operands, int nb_outputs,
2796 const uint8_t *clobber_regs,
2797 int *pout_reg)
2799 /* overall format: modifier, then ,-seperated list of alternatives; all operands for a single instruction must have the same number of alternatives */
2800 /* TODO: Simple constraints
2801 whitespace ignored
2802 o memory operand that is offsetable
2803 V memory but not offsetable
2804 < memory operand with autodecrement addressing is allowed. Restrictions apply.
2805 > memory operand with autoincrement addressing is allowed. Restrictions apply.
2806 n immediate integer operand with a known numeric value
2807 E immediate floating operand (const_double) is allowed, but only if target=host
2808 F immediate floating operand (const_double or const_vector) is allowed
2809 s immediate integer operand whose value is not an explicit integer
2810 X any operand whatsoever
2811 0...9 (postfix); (can also be more than 1 digit number); an operand that matches the specified operand number is allowed
2814 /* TODO: ARM constraints:
2815 k the stack pointer register
2816 G the floating-point constant 0.0
2817 Q memory reference where the exact address is in a single register ("m" is preferable for asm statements)
2818 R an item in the constant pool
2819 S symbol in the text segment of the current file
2820 [ Uv memory reference suitable for VFP load/store insns (reg+constant offset)]
2821 [ Uy memory reference suitable for iWMMXt load/store instructions]
2822 Uq memory reference suitable for the ARMv4 ldrsb instruction
2824 ASMOperand *op;
2825 int sorted_op[MAX_ASM_OPERANDS];
2826 int i, j, k, p1, p2, tmp, reg, c, reg_mask;
2827 const char *str;
2828 uint8_t regs_allocated[NB_ASM_REGS];
2830 /* init fields */
2831 for (i = 0; i < nb_operands; i++) {
2832 op = &operands[i];
2833 op->input_index = -1;
2834 op->ref_index = -1;
2835 op->reg = -1;
2836 op->is_memory = 0;
2837 op->is_rw = 0;
2839 /* compute constraint priority and evaluate references to output
2840 constraints if input constraints */
2841 for (i = 0; i < nb_operands; i++) {
2842 op = &operands[i];
2843 str = op->constraint;
2844 str = skip_constraint_modifiers(str);
2845 if (isnum(*str) || *str == '[') {
2846 /* this is a reference to another constraint */
2847 k = find_constraint(operands, nb_operands, str, NULL);
2848 if ((unsigned) k >= i || i < nb_outputs)
2849 tcc_error("invalid reference in constraint %d ('%s')",
2850 i, str);
2851 op->ref_index = k;
2852 if (operands[k].input_index >= 0)
2853 tcc_error("cannot reference twice the same operand");
2854 operands[k].input_index = i;
2855 op->priority = 5;
2856 } else if ((op->vt->r & VT_VALMASK) == VT_LOCAL
2857 && op->vt->sym
2858 && (reg = op->vt->sym->r & VT_VALMASK) < VT_CONST) {
2859 op->priority = 1;
2860 op->reg = reg;
2861 } else {
2862 op->priority = constraint_priority(str);
2866 /* sort operands according to their priority */
2867 for (i = 0; i < nb_operands; i++)
2868 sorted_op[i] = i;
2869 for (i = 0; i < nb_operands - 1; i++) {
2870 for (j = i + 1; j < nb_operands; j++) {
2871 p1 = operands[sorted_op[i]].priority;
2872 p2 = operands[sorted_op[j]].priority;
2873 if (p2 < p1) {
2874 tmp = sorted_op[i];
2875 sorted_op[i] = sorted_op[j];
2876 sorted_op[j] = tmp;
2881 for (i = 0; i < NB_ASM_REGS; i++) {
2882 if (clobber_regs[i])
2883 regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK;
2884 else
2885 regs_allocated[i] = 0;
2887 /* sp cannot be used */
2888 regs_allocated[13] = REG_IN_MASK | REG_OUT_MASK;
2889 /* fp cannot be used yet */
2890 regs_allocated[11] = REG_IN_MASK | REG_OUT_MASK;
2892 /* allocate registers and generate corresponding asm moves */
2893 for (i = 0; i < nb_operands; i++) {
2894 j = sorted_op[i];
2895 op = &operands[j];
2896 str = op->constraint;
2897 /* no need to allocate references */
2898 if (op->ref_index >= 0)
2899 continue;
2900 /* select if register is used for output, input or both */
2901 if (op->input_index >= 0) {
2902 reg_mask = REG_IN_MASK | REG_OUT_MASK;
2903 } else if (j < nb_outputs) {
2904 reg_mask = REG_OUT_MASK;
2905 } else {
2906 reg_mask = REG_IN_MASK;
2908 if (op->reg >= 0) {
2909 if (is_reg_allocated(op->reg))
2910 tcc_error
2911 ("asm regvar requests register that's taken already");
2912 reg = op->reg;
2914 try_next:
2915 c = *str++;
2916 switch (c) {
2917 case '=': // Operand is written-to
2918 goto try_next;
2919 case '+': // Operand is both READ and written-to
2920 op->is_rw = 1;
2921 /* FALL THRU */
2922 case '&': // Operand is clobbered before the instruction is done using the input operands
2923 if (j >= nb_outputs)
2924 tcc_error("'%c' modifier can only be applied to outputs",
2926 reg_mask = REG_IN_MASK | REG_OUT_MASK;
2927 goto try_next;
2928 case 'l': // In non-thumb mode, alias for 'r'--otherwise r0-r7 [ARM]
2929 case 'r': // general-purpose register
2930 case 'p': // loadable/storable address
2931 /* any general register */
2932 if ((reg = op->reg) >= 0)
2933 goto reg_found;
2934 else for (reg = 0; reg <= 8; reg++) {
2935 if (!is_reg_allocated(reg))
2936 goto reg_found;
2938 goto try_next;
2939 reg_found:
2940 /* now we can reload in the register */
2941 op->is_llong = 0;
2942 op->reg = reg;
2943 regs_allocated[reg] |= reg_mask;
2944 break;
2945 case 'I': // integer that is valid as an data processing instruction immediate (0...255, rotated by a multiple of two)
2946 case 'J': // integer in the range -4095 to 4095 [ARM]
2947 case 'K': // integer that satisfies constraint I when inverted (one's complement)
2948 case 'L': // integer that satisfies constraint I when inverted (two's complement)
2949 case 'i': // immediate integer operand, including symbolic constants
2950 if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST))
2951 goto try_next;
2952 break;
2953 case 'M': // integer in the range 0 to 32
2954 if (!
2955 ((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) ==
2956 VT_CONST))
2957 goto try_next;
2958 break;
2959 case 'm': // memory operand
2960 case 'g':
2961 /* nothing special to do because the operand is already in
2962 memory, except if the pointer itself is stored in a
2963 memory variable (VT_LLOCAL case) */
2964 /* XXX: fix constant case */
2965 /* if it is a reference to a memory zone, it must lie
2966 in a register, so we reserve the register in the
2967 input registers and a load will be generated
2968 later */
2969 if (j < nb_outputs || c == 'm') {
2970 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
2971 /* any general register */
2972 for (reg = 0; reg <= 8; reg++) {
2973 if (!(regs_allocated[reg] & REG_IN_MASK))
2974 goto reg_found1;
2976 goto try_next;
2977 reg_found1:
2978 /* now we can reload in the register */
2979 regs_allocated[reg] |= REG_IN_MASK;
2980 op->reg = reg;
2981 op->is_memory = 1;
2984 break;
2985 default:
2986 tcc_error("asm constraint %d ('%s') could not be satisfied",
2987 j, op->constraint);
2988 break;
2990 /* if a reference is present for that operand, we assign it too */
2991 if (op->input_index >= 0) {
2992 operands[op->input_index].reg = op->reg;
2993 operands[op->input_index].is_llong = op->is_llong;
2997 /* compute out_reg. It is used to store outputs registers to memory
2998 locations references by pointers (VT_LLOCAL case) */
2999 *pout_reg = -1;
3000 for (i = 0; i < nb_operands; i++) {
3001 op = &operands[i];
3002 if (op->reg >= 0 &&
3003 (op->vt->r & VT_VALMASK) == VT_LLOCAL && !op->is_memory) {
3004 for (reg = 0; reg <= 8; reg++) {
3005 if (!(regs_allocated[reg] & REG_OUT_MASK))
3006 goto reg_found2;
3008 tcc_error("could not find free output register for reloading");
3009 reg_found2:
3010 *pout_reg = reg;
3011 break;
3015 /* print sorted constraints */
3016 #ifdef ASM_DEBUG
3017 for (i = 0; i < nb_operands; i++) {
3018 j = sorted_op[i];
3019 op = &operands[j];
3020 printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
3022 op->id ? get_tok_str(op->id, NULL) : "",
3023 op->constraint, op->vt->r, op->reg);
3025 if (*pout_reg >= 0)
3026 printf("out_reg=%d\n", *pout_reg);
3027 #endif
3030 ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str)
3032 int reg;
3033 TokenSym *ts;
3035 if (!strcmp(str, "memory") ||
3036 !strcmp(str, "cc") ||
3037 !strcmp(str, "flags"))
3038 return;
3039 ts = tok_alloc(str, strlen(str));
3040 reg = asm_parse_regvar(ts->tok);
3041 if (reg == -1) {
3042 tcc_error("invalid clobber register '%s'", str);
3044 clobber_regs[reg] = 1;
3047 /* If T refers to a register then return the register number and type.
3048 Otherwise return -1. */
3049 ST_FUNC int asm_parse_regvar (int t)
3051 if (t >= TOK_ASM_r0 && t <= TOK_ASM_pc) { /* register name */
3052 switch (t) {
3053 case TOK_ASM_fp:
3054 return TOK_ASM_r11 - TOK_ASM_r0;
3055 case TOK_ASM_ip:
3056 return TOK_ASM_r12 - TOK_ASM_r0;
3057 case TOK_ASM_sp:
3058 return TOK_ASM_r13 - TOK_ASM_r0;
3059 case TOK_ASM_lr:
3060 return TOK_ASM_r14 - TOK_ASM_r0;
3061 case TOK_ASM_pc:
3062 return TOK_ASM_r15 - TOK_ASM_r0;
3063 default:
3064 return t - TOK_ASM_r0;
3066 } else
3067 return -1;
3070 /*************************************************************/
3071 #endif /* ndef TARGET_DEFS_ONLY */