2 * ARM specific functions for TCC assembler
4 * Copyright (c) 2001, 2002 Fabrice Bellard
5 * Copyright (c) 2020 Danny Milosavljevic
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #ifdef TARGET_DEFS_ONLY
24 #define CONFIG_TCC_ASM
25 #define NB_ASM_REGS 16
27 ST_FUNC
void g(int c
);
28 ST_FUNC
void gen_le16(int c
);
29 ST_FUNC
void gen_le32(int c
);
31 /*************************************************************/
33 /*************************************************************/
45 #define OP_REG32 (1 << OPT_REG32)
46 #define OP_REG (OP_REG32)
47 #define OP_IM32 (1 << OPT_IM32)
48 #define OP_IM8 (1 << OPT_IM8)
49 #define OP_IM8N (1 << OPT_IM8N)
50 #define OP_REGSET32 (1 << OPT_REGSET32)
52 typedef struct Operand
{
61 /* Parse a text containing operand and store the result in OP */
62 static void parse_operand(TCCState
*s1
, Operand
*op
)
70 if (tok
== '{') { // regset literal
72 while (tok
!= '}' && tok
!= TOK_EOF
) {
73 reg
= asm_parse_regvar(tok
);
78 next(); // skip register name
80 if ((1 << reg
) < regset
)
81 tcc_warning("registers will be processed in ascending order by hardware--but are not specified in ascending order here");
91 // ARM instructions don't support empty regset.
92 tcc_error("empty register list is not supported");
94 op
->type
= OP_REGSET32
;
97 } else if (tok
== '#' || tok
== '$') {
99 next(); // skip '#' or '$'
104 if ((int) op
->e
.v
< 0 && (int) op
->e
.v
>= -255)
106 else if (op
->e
.v
== (uint8_t)op
->e
.v
)
110 } else if ((reg
= asm_parse_regvar(tok
)) != -1) {
111 next(); // skip register name
113 op
->reg
= (uint8_t) reg
;
118 /* XXX: make it faster ? */
119 ST_FUNC
void g(int c
)
125 if (ind1
> cur_text_section
->data_allocated
)
126 section_realloc(cur_text_section
, ind1
);
127 cur_text_section
->data
[ind
] = c
;
131 ST_FUNC
void gen_le16 (int i
)
137 ST_FUNC
void gen_le32 (int i
)
143 if (ind1
> cur_text_section
->data_allocated
)
144 section_realloc(cur_text_section
, ind1
);
145 cur_text_section
->data
[ind
++] = i
& 0xFF;
146 cur_text_section
->data
[ind
++] = (i
>> 8) & 0xFF;
147 cur_text_section
->data
[ind
++] = (i
>> 16) & 0xFF;
148 cur_text_section
->data
[ind
++] = (i
>> 24) & 0xFF;
151 ST_FUNC
void gen_expr32(ExprValue
*pe
)
156 static uint32_t condition_code_of_token(int token
) {
157 if (token
< TOK_ASM_nopeq
) {
158 expect("instruction");
161 return (token
- TOK_ASM_nopeq
) & 15;
164 static void asm_emit_opcode(int token
, uint32_t opcode
) {
165 gen_le32((condition_code_of_token(token
) << 28) | opcode
);
168 static void asm_nullary_opcode(int token
)
170 switch (ARM_INSTRUCTION_GROUP(token
)) {
172 asm_emit_opcode(token
, 0xd << 21); // mov r0, r0
175 asm_emit_opcode(token
, 0x320f002);
177 asm_emit_opcode(token
, 0x320f003);
180 expect("nullary instruction");
184 static void asm_unary_opcode(TCCState
*s1
, int token
)
187 parse_operand(s1
, &op
);
189 switch (ARM_INSTRUCTION_GROUP(token
)) {
191 if (op
.type
!= OP_IM8
)
192 expect("immediate 8-bit unsigned integer");
194 /* Note: Dummy operand (ignored by processor): ARM ref documented 0...255, ARM instruction set documented 24 bit */
195 asm_emit_opcode(token
, (0xf << 24) | op
.e
.v
);
199 expect("unary instruction");
203 static void asm_binary_opcode(TCCState
*s1
, int token
)
207 uint32_t encoded_rotation
= 0;
209 parse_operand(s1
, &ops
[0]);
214 parse_operand(s1
, &ops
[1]);
215 if (ops
[0].type
!= OP_REG32
) {
216 expect("(destination operand) register");
220 if (ops
[0].reg
== 15) {
221 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
225 if (ops
[0].reg
== 13)
226 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token
, NULL
));
228 if (ops
[1].type
!= OP_REG32
) {
229 switch (ARM_INSTRUCTION_GROUP(token
)) {
232 if (ops
[1].type
== OP_IM8
|| ops
[1].type
== OP_IM8N
|| ops
[1].type
== OP_IM32
) {
233 if (ops
[1].e
.v
>= 0 && ops
[1].e
.v
<= 0xFFFF) {
234 uint16_t immediate_value
= ops
[1].e
.v
;
235 switch (ARM_INSTRUCTION_GROUP(token
)) {
237 asm_emit_opcode(token
, 0x3400000 | (ops
[0].reg
<< 12) | (immediate_value
& 0xF000) << 4 | (immediate_value
& 0xFFF));
240 asm_emit_opcode(token
, 0x3000000 | (ops
[0].reg
<< 12) | (immediate_value
& 0xF000) << 4 | (immediate_value
& 0xFFF));
244 expect("(source operand) immediate 16 bit value");
246 expect("(source operand) immediate");
249 expect("(source operand) register");
254 if (ops
[1].reg
== 15) {
255 tcc_error("'%s' does not support 'pc' as operand", get_tok_str(token
, NULL
));
259 if (ops
[1].reg
== 13)
260 tcc_warning("Using 'sp' as operand with '%s' is deprecated by ARM", get_tok_str(token
, NULL
));
264 if (tok
== TOK_ASM_ror
) {
265 next(); // skip 'ror'
266 parse_operand(s1
, &rotation
);
267 if (rotation
.type
!= OP_IM8
) {
268 expect("immediate value for rotation");
271 amount
= rotation
.e
.v
;
274 encoded_rotation
= 1 << 10;
277 encoded_rotation
= 2 << 10;
280 encoded_rotation
= 3 << 10;
283 expect("'8' or '16' or '24'");
289 switch (ARM_INSTRUCTION_GROUP(token
)) {
291 if (encoded_rotation
)
292 tcc_error("clz does not support rotation");
293 asm_emit_opcode(token
, 0x16f0f10 | (ops
[0].reg
<< 12) | ops
[1].reg
);
296 asm_emit_opcode(token
, 0x6af0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
299 asm_emit_opcode(token
, 0x6bf0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
302 asm_emit_opcode(token
, 0x6ef0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
305 asm_emit_opcode(token
, 0x6ff0070 | (ops
[0].reg
<< 12) | ops
[1].reg
| encoded_rotation
);
308 expect("binary instruction");
312 /* data processing and single data transfer instructions only */
313 #define ENCODE_RN(register_index) ((register_index) << 16)
314 #define ENCODE_RD(register_index) ((register_index) << 12)
315 #define ENCODE_SET_CONDITION_CODES (1 << 20)
317 /* Note: For data processing instructions, "1" means immediate.
318 Note: For single data transfer instructions, "0" means immediate. */
319 #define ENCODE_IMMEDIATE_FLAG (1 << 25)
321 #define ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER (1 << 4)
322 #define ENCODE_BARREL_SHIFTER_MODE_LSL (0 << 5)
323 #define ENCODE_BARREL_SHIFTER_MODE_LSR (1 << 5)
324 #define ENCODE_BARREL_SHIFTER_MODE_ASR (2 << 5)
325 #define ENCODE_BARREL_SHIFTER_MODE_ROR (3 << 5)
326 #define ENCODE_BARREL_SHIFTER_REGISTER(register_index) ((register_index) << 8)
327 #define ENCODE_BARREL_SHIFTER_IMMEDIATE(value) ((value) << 7)
329 static void asm_block_data_transfer_opcode(TCCState
*s1
, int token
)
335 parse_operand(s1
, &ops
[0]);
341 next(); // skip comma
342 parse_operand(s1
, &ops
[1]);
346 expect("at least one operand");
348 } else if (ops
[nb_ops
- 1].type
!= OP_REGSET32
) {
349 expect("(last operand) register list");
353 // block data transfer: 1 0 0 P U S W L << 20 (general case):
355 // Rn: bits 19...16 base register
356 // Register List: bits 15...0
358 switch (ARM_INSTRUCTION_GROUP(token
)) {
359 case TOK_ASM_pusheq
: // TODO: Optimize 1-register case to: str ?, [sp, #-4]!
360 // Instruction: 1 I=0 P=1 U=0 S=0 W=1 L=0 << 20, op 1101
363 // Register List: bits 15...0
365 expect("exactly one operand");
367 asm_emit_opcode(token
, (0x92d << 16) | ops
[0].regset
); // TODO: base register ?
369 case TOK_ASM_popeq
: // TODO: Optimize 1-register case to: ldr ?, [sp], #4
370 // Instruction: 1 I=0 P=0 U=1 S=0 W=0 L=1 << 20, op 1101
373 // Register List: bits 15...0
375 expect("exactly one operand");
377 asm_emit_opcode(token
, (0x8bd << 16) | ops
[0].regset
); // TODO: base register ?
379 case TOK_ASM_stmdaeq
:
380 case TOK_ASM_ldmdaeq
:
383 case TOK_ASM_stmiaeq
:
384 case TOK_ASM_ldmiaeq
:
385 case TOK_ASM_stmdbeq
:
386 case TOK_ASM_ldmdbeq
:
387 case TOK_ASM_stmibeq
:
388 case TOK_ASM_ldmibeq
:
389 switch (ARM_INSTRUCTION_GROUP(token
)) {
390 case TOK_ASM_stmdaeq
: // post-decrement store
393 case TOK_ASM_ldmdaeq
: // post-decrement load
396 case TOK_ASM_stmeq
: // post-increment store
397 case TOK_ASM_stmiaeq
: // post-increment store
400 case TOK_ASM_ldmeq
: // post-increment load
401 case TOK_ASM_ldmiaeq
: // post-increment load
404 case TOK_ASM_stmdbeq
: // pre-decrement store
407 case TOK_ASM_ldmdbeq
: // pre-decrement load
410 case TOK_ASM_stmibeq
: // pre-increment store
413 case TOK_ASM_ldmibeq
: // pre-increment load
417 tcc_error("internal error: This place should not be reached (fallback in asm_block_data_transfer_opcode)");
421 // Register List: lower bits
423 expect("exactly two operands");
424 else if (ops
[0].type
!= OP_REG32
)
425 expect("(first operand) register");
426 else if (!op0_exclam
)
427 tcc_error("first operand of '%s' should have an exclamation mark", get_tok_str(token
, NULL
));
429 asm_emit_opcode(token
, opcode
| ENCODE_RN(ops
[0].reg
) | ops
[1].regset
);
432 expect("block data transfer instruction");
436 static uint32_t asm_encode_shift(Operand
* shift
)
439 uint32_t operands
= 0;
440 switch (shift
->type
) {
442 if (shift
->reg
== 15)
443 tcc_error("r15 cannot be used as a shift count");
445 operands
= ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER
;
446 operands
|= ENCODE_BARREL_SHIFTER_REGISTER(shift
->reg
);
451 if (amount
> 0 && amount
< 32)
452 operands
= ENCODE_BARREL_SHIFTER_IMMEDIATE(amount
);
454 tcc_error("shift count out of range");
457 tcc_error("unknown shift amount");
462 static void asm_data_processing_opcode(TCCState
*s1
, int token
)
468 uint32_t operands
= 0;
470 /* modulo 16 entries per instruction for the different condition codes */
471 uint32_t opcode_idx
= (ARM_INSTRUCTION_GROUP(token
) - TOK_ASM_andeq
) >> 4;
472 uint32_t opcode_nos
= opcode_idx
>> 1; // without "s"; "OpCode" in ARM docs
474 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ) {
475 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
)
477 parse_operand(s1
, &ops
[nb_ops
]);
495 operands
|= ENCODE_BARREL_SHIFTER_MODE_LSL
;
498 operands
|= ENCODE_BARREL_SHIFTER_MODE_ASR
;
501 operands
|= ENCODE_BARREL_SHIFTER_MODE_LSR
;
504 operands
|= ENCODE_BARREL_SHIFTER_MODE_ROR
;
508 parse_operand(s1
, &shift
);
513 operands
|= ENCODE_BARREL_SHIFTER_MODE_ROR
;
517 expect("at least two operands");
518 else if (nb_ops
== 2) {
519 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
520 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
522 } else if (nb_ops
== 3) {
523 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
524 tcc_error("'%s' cannot be used with three operands", get_tok_str(token
, NULL
));
529 expect("two or three operands");
533 uint32_t immediate_value
;
534 uint8_t half_immediate_rotation
;
535 if (nb_shift
&& shift
.type
== OP_REG32
) {
536 if ((ops
[0].type
== OP_REG32
&& ops
[0].reg
== 15) ||
537 (ops
[1].type
== OP_REG32
&& ops
[1].reg
== 15)) {
538 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
543 // data processing (general case):
545 // Rn: bits 19...16 (first operand)
546 // Rd: bits 15...12 (destination)
547 // Operand2: bits 11...0 (second operand); depending on I that's either a register or an immediate
549 // bits 24...21: "OpCode"--see below
551 /* operations in the token list are ordered by opcode */
552 opcode
= opcode_nos
<< 21; // drop "s"
553 if (ops
[0].type
!= OP_REG32
)
554 expect("(destination operand) register");
555 else if (opcode_nos
== 0xa || opcode_nos
== 0xb || opcode_nos
== 0x8 || opcode_nos
== 0x9) // cmp, cmn, tst, teq
556 operands
|= ENCODE_SET_CONDITION_CODES
; // force S set, otherwise it's a completely different instruction.
558 operands
|= ENCODE_RD(ops
[0].reg
);
559 if (ops
[1].type
!= OP_REG32
)
560 expect("(first source operand) register");
561 else if (!(opcode_nos
== 0xd || opcode_nos
== 0xf)) // not: mov, mvn (those have only one source operand)
562 operands
|= ENCODE_RN(ops
[1].reg
);
563 switch (ops
[2].type
) {
565 operands
|= ops
[2].reg
;
569 operands
|= ENCODE_IMMEDIATE_FLAG
;
570 immediate_value
= ops
[2].e
.v
;
571 for (half_immediate_rotation
= 0; half_immediate_rotation
< 16; ++half_immediate_rotation
) {
572 if (immediate_value
>= 0x00 && immediate_value
< 0x100)
574 // rotate left by two
575 immediate_value
= ((immediate_value
& 0x3FFFFFFF) << 2) | ((immediate_value
& 0xC0000000) >> 30);
577 if (half_immediate_rotation
>= 16) {
580 operands
|= immediate_value
;
581 operands
|= half_immediate_rotation
<< 8;
584 case OP_IM8N
: // immediate negative value
585 operands
|= ENCODE_IMMEDIATE_FLAG
;
586 immediate_value
= ops
[2].e
.v
;
587 /* Instruction swapping:
588 0001 = EOR - Rd:= Op1 EOR Op2 -> difficult
589 0011 = RSB - Rd:= Op2 - Op1 -> difficult
590 0111 = RSC - Rd:= Op2 - Op1 + C -> difficult
591 1000 = TST - CC on: Op1 AND Op2 -> difficult
592 1001 = TEQ - CC on: Op1 EOR Op2 -> difficult
593 1100 = ORR - Rd:= Op1 OR Op2 -> difficult
595 switch (opcode_nos
) {
596 case 0x0: // AND - Rd:= Op1 AND Op2
597 opcode
= 0xe << 21; // BIC
598 immediate_value
= ~immediate_value
;
600 case 0x2: // SUB - Rd:= Op1 - Op2
601 opcode
= 0x4 << 21; // ADD
602 immediate_value
= -immediate_value
;
604 case 0x4: // ADD - Rd:= Op1 + Op2
605 opcode
= 0x2 << 21; // SUB
606 immediate_value
= -immediate_value
;
608 case 0x5: // ADC - Rd:= Op1 + Op2 + C
609 opcode
= 0x6 << 21; // SBC
610 immediate_value
= ~immediate_value
;
612 case 0x6: // SBC - Rd:= Op1 - Op2 + C
613 opcode
= 0x5 << 21; // ADC
614 immediate_value
= ~immediate_value
;
616 case 0xa: // CMP - CC on: Op1 - Op2
617 opcode
= 0xb << 21; // CMN
618 immediate_value
= -immediate_value
;
620 case 0xb: // CMN - CC on: Op1 + Op2
621 opcode
= 0xa << 21; // CMP
622 immediate_value
= -immediate_value
;
624 case 0xd: // MOV - Rd:= Op2
625 opcode
= 0xf << 21; // MVN
626 immediate_value
= ~immediate_value
;
628 case 0xe: // BIC - Rd:= Op1 AND NOT Op2
629 opcode
= 0x0 << 21; // AND
630 immediate_value
= ~immediate_value
;
632 case 0xf: // MVN - Rd:= NOT Op2
633 opcode
= 0xd << 21; // MOV
634 immediate_value
= ~immediate_value
;
637 tcc_error("cannot use '%s' with a negative immediate value", get_tok_str(token
, NULL
));
639 for (half_immediate_rotation
= 0; half_immediate_rotation
< 16; ++half_immediate_rotation
) {
640 if (immediate_value
>= 0x00 && immediate_value
< 0x100)
642 // rotate left by two
643 immediate_value
= ((immediate_value
& 0x3FFFFFFF) << 2) | ((immediate_value
& 0xC0000000) >> 30);
645 if (half_immediate_rotation
>= 16) {
646 immediate_value
= ops
[2].e
.v
;
647 tcc_error("immediate value 0x%X cannot be encoded into ARM immediate", (unsigned) immediate_value
);
650 operands
|= immediate_value
;
651 operands
|= half_immediate_rotation
<< 8;
654 expect("(second source operand) register or immediate value");
658 if (operands
& ENCODE_IMMEDIATE_FLAG
)
659 tcc_error("immediate rotation not implemented");
661 operands
|= asm_encode_shift(&shift
);
664 /* S=0 and S=1 entries alternate one after another, in that order */
665 opcode
|= (opcode_idx
& 1) ? ENCODE_SET_CONDITION_CODES
: 0;
666 asm_emit_opcode(token
, opcode
| operands
);
670 static void asm_shift_opcode(TCCState
*s1
, int token
)
674 int definitely_neutral
= 0;
675 uint32_t opcode
= 0xd << 21; // MOV
676 uint32_t operands
= 0;
678 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
679 parse_operand(s1
, &ops
[nb_ops
]);
687 expect("at least two operands");
691 if (ops
[0].type
!= OP_REG32
) {
692 expect("(destination operand) register");
695 operands
|= ENCODE_RD(ops
[0].reg
);
698 switch (ARM_INSTRUCTION_GROUP(token
)) {
700 opcode
|= ENCODE_SET_CONDITION_CODES
;
703 if (ops
[1].type
== OP_REG32
) {
704 operands
|= ops
[1].reg
;
705 operands
|= ENCODE_BARREL_SHIFTER_MODE_ROR
;
706 asm_emit_opcode(token
, opcode
| operands
);
708 tcc_error("(first source operand) register");
711 memcpy(&ops
[2], &ops
[1], sizeof(ops
[1])); // move ops[2]
712 memcpy(&ops
[1], &ops
[0], sizeof(ops
[0])); // ops[1] was implicit
717 expect("two or three operands");
721 switch (ARM_INSTRUCTION_GROUP(token
)) {
726 opcode
|= ENCODE_SET_CONDITION_CODES
;
730 switch (ops
[1].type
) {
732 operands
|= ops
[1].reg
;
735 operands
|= ENCODE_IMMEDIATE_FLAG
;
736 operands
|= ops
[1].e
.v
;
740 switch (ops
[2].type
) {
742 if ((ops
[0].type
== OP_REG32
&& ops
[0].reg
== 15) ||
743 (ops
[1].type
== OP_REG32
&& ops
[1].reg
== 15)) {
744 tcc_error("Using the 'pc' register in data processing instructions that have a register-controlled shift is not implemented by ARM");
746 operands
|= asm_encode_shift(&ops
[2]);
750 operands
|= asm_encode_shift(&ops
[2]);
752 definitely_neutral
= 1;
756 if (!definitely_neutral
) switch (ARM_INSTRUCTION_GROUP(token
)) {
759 operands
|= ENCODE_BARREL_SHIFTER_MODE_LSL
;
763 operands
|= ENCODE_BARREL_SHIFTER_MODE_LSR
;
767 operands
|= ENCODE_BARREL_SHIFTER_MODE_ASR
;
771 operands
|= ENCODE_BARREL_SHIFTER_MODE_ROR
;
774 expect("shift instruction");
777 asm_emit_opcode(token
, opcode
| operands
);
780 static void asm_multiplication_opcode(TCCState
*s1
, int token
)
784 uint32_t opcode
= 0x90;
786 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
787 parse_operand(s1
, &ops
[nb_ops
]);
795 expect("at least two operands");
796 else if (nb_ops
== 2) {
797 switch (ARM_INSTRUCTION_GROUP(token
)) {
800 memcpy(&ops
[2], &ops
[0], sizeof(ops
[1])); // ARM is actually like this!
803 expect("at least three operands");
809 // multiply (special case):
816 if (ops
[0].type
== OP_REG32
)
817 opcode
|= ops
[0].reg
<< 16;
819 expect("(destination operand) register");
820 if (ops
[1].type
== OP_REG32
)
821 opcode
|= ops
[1].reg
;
823 expect("(first source operand) register");
824 if (ops
[2].type
== OP_REG32
)
825 opcode
|= ops
[2].reg
<< 8;
827 expect("(second source operand) register");
829 if (ops
[3].type
== OP_REG32
)
830 opcode
|= ops
[3].reg
<< 12;
832 expect("(third source operand) register");
835 switch (ARM_INSTRUCTION_GROUP(token
)) {
837 opcode
|= 1 << 20; // Status
841 expect("three operands");
843 asm_emit_opcode(token
, opcode
);
847 opcode
|= 1 << 20; // Status
851 expect("four operands");
853 opcode
|= 1 << 21; // Accumulate
854 asm_emit_opcode(token
, opcode
);
858 expect("known multiplication instruction");
862 static void asm_long_multiplication_opcode(TCCState
*s1
, int token
)
866 uint32_t opcode
= 0x90 | (1 << 23);
868 for (nb_ops
= 0; nb_ops
< sizeof(ops
)/sizeof(ops
[0]); ++nb_ops
) {
869 parse_operand(s1
, &ops
[nb_ops
]);
877 expect("four operands");
881 // long multiply (special case):
883 // RdLo: bits 15...12
884 // RdHi: bits 19...16
888 if (ops
[0].type
== OP_REG32
)
889 opcode
|= ops
[0].reg
<< 12;
891 expect("(destination lo accumulator) register");
892 if (ops
[1].type
== OP_REG32
)
893 opcode
|= ops
[1].reg
<< 16;
895 expect("(destination hi accumulator) register");
896 if (ops
[2].type
== OP_REG32
)
897 opcode
|= ops
[2].reg
;
899 expect("(first source operand) register");
900 if (ops
[3].type
== OP_REG32
)
901 opcode
|= ops
[3].reg
<< 8;
903 expect("(second source operand) register");
905 switch (ARM_INSTRUCTION_GROUP(token
)) {
906 case TOK_ASM_smullseq
:
907 opcode
|= 1 << 20; // Status
909 case TOK_ASM_smulleq
:
910 opcode
|= 1 << 22; // signed
911 asm_emit_opcode(token
, opcode
);
913 case TOK_ASM_umullseq
:
914 opcode
|= 1 << 20; // Status
916 case TOK_ASM_umulleq
:
917 asm_emit_opcode(token
, opcode
);
919 case TOK_ASM_smlalseq
:
920 opcode
|= 1 << 20; // Status
922 case TOK_ASM_smlaleq
:
923 opcode
|= 1 << 22; // signed
924 opcode
|= 1 << 21; // Accumulate
925 asm_emit_opcode(token
, opcode
);
927 case TOK_ASM_umlalseq
:
928 opcode
|= 1 << 20; // Status
930 case TOK_ASM_umlaleq
:
931 opcode
|= 1 << 21; // Accumulate
932 asm_emit_opcode(token
, opcode
);
935 expect("known long multiplication instruction");
939 static void asm_single_data_transfer_opcode(TCCState
*s1
, int token
)
943 int closed_bracket
= 0;
945 uint32_t opcode
= 1 << 26;
946 // Note: ldr r0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
947 // Note: ldr r0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 = r4+4
948 // Note: ldr r0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 = r4+4
950 parse_operand(s1
, &ops
[0]);
951 if (ops
[0].type
== OP_REG32
)
952 opcode
|= ENCODE_RD(ops
[0].reg
);
954 expect("(destination operand) register");
958 expect("two arguments");
966 parse_operand(s1
, &ops
[1]);
967 if (ops
[1].type
== OP_REG32
)
968 opcode
|= ENCODE_RN(ops
[1].reg
);
970 expect("(first source operand) register");
976 // exclam = 1; // implicit in hardware; don't do it in software
986 parse_operand(s1
, &ops
[2]);
987 if (!closed_bracket
) {
992 opcode
|= 1 << 24; // add offset before transfer
999 // single data transfer: 0 1 I P U B W L << 20 (general case):
1001 // Rd: destination operand [ok]
1002 // Rn: first source operand [ok]
1003 // Operand2: bits 11...0 [ok]
1004 // I: immediate operand? [ok]
1005 // P: Pre/post indexing is PRE: Add offset before transfer [ok]
1006 // U: Up/down is up? (*adds* offset to base) [ok]
1007 // B: Byte/word is byte? TODO
1008 // W: Write address back into base? [ok]
1009 // L: Load/store is load? [ok]
1011 opcode
|= 1 << 21; // write offset back into register
1013 if (ops
[2].type
== OP_IM32
|| ops
[2].type
== OP_IM8
|| ops
[2].type
== OP_IM8N
) {
1016 tcc_error("minus before '#' not supported for immediate values");
1018 opcode
|= 1 << 23; // up
1020 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
1025 tcc_error("offset out of range for '%s'", get_tok_str(token
, NULL
));
1029 } else if (ops
[2].type
== OP_REG32
) {
1031 opcode
|= 1 << 23; // up
1032 opcode
|= ENCODE_IMMEDIATE_FLAG
; /* if set, it means it's NOT immediate */
1033 opcode
|= ops
[2].reg
;
1037 switch (ARM_INSTRUCTION_GROUP(token
)) {
1038 case TOK_ASM_strbeq
:
1039 opcode
|= 1 << 22; // B
1042 asm_emit_opcode(token
, opcode
);
1044 case TOK_ASM_ldrbeq
:
1045 opcode
|= 1 << 22; // B
1048 opcode
|= 1 << 20; // L
1049 asm_emit_opcode(token
, opcode
);
1052 expect("data transfer instruction");
1056 /* Note: almost dupe of encbranch in arm-gen.c */
1057 static uint32_t encbranchoffset(int pos
, int addr
, int fail
)
1061 if(addr
>=0x1000000 || addr
<-0x1000000) { // FIXME: Is that correct?
1063 tcc_error("function bigger than 32MB");
1066 return /*not 0x0A000000|*/(addr
&0xffffff);
1069 static void asm_branch_opcode(TCCState
*s1
, int token
)
1073 parse_operand(s1
, &op
);
1074 if (op
.type
== OP_IM32
|| op
.type
== OP_IM8
|| op
.type
== OP_IM8N
) {
1075 jmp_disp
= encbranchoffset(ind
, op
.e
.v
, 0);
1076 if (jmp_disp
< -0x800000 || jmp_disp
> 0x7fffff) {
1077 tcc_error("branch is too far");
1081 switch (ARM_INSTRUCTION_GROUP(token
)) {
1083 if (op
.type
== OP_IM32
|| op
.type
== OP_IM8
|| op
.type
== OP_IM8N
)
1084 asm_emit_opcode(token
, (0xa << 24) | (jmp_disp
& 0xffffff));
1086 expect("branch target");
1089 if (op
.type
== OP_IM32
|| op
.type
== OP_IM8
|| op
.type
== OP_IM8N
)
1090 asm_emit_opcode(token
, (0xb << 24) | (jmp_disp
& 0xffffff));
1092 expect("branch target");
1095 if (op
.type
!= OP_REG32
)
1098 asm_emit_opcode(token
, (0x12fff1 << 4) | op
.reg
);
1101 if (op
.type
!= OP_REG32
)
1104 asm_emit_opcode(token
, (0x12fff3 << 4) | op
.reg
);
1107 expect("branch instruction");
1111 ST_FUNC
void asm_opcode(TCCState
*s1
, int token
)
1113 while (token
== TOK_LINEFEED
) {
1117 if (token
== TOK_EOF
)
1119 if (token
< TOK_ASM_nopeq
) {
1120 expect("instruction");
1124 switch (ARM_INSTRUCTION_GROUP(token
)) {
1125 case TOK_ASM_pusheq
:
1127 case TOK_ASM_stmdaeq
:
1128 case TOK_ASM_ldmdaeq
:
1131 case TOK_ASM_stmiaeq
:
1132 case TOK_ASM_ldmiaeq
:
1133 case TOK_ASM_stmdbeq
:
1134 case TOK_ASM_ldmdbeq
:
1135 case TOK_ASM_stmibeq
:
1136 case TOK_ASM_ldmibeq
:
1137 return asm_block_data_transfer_opcode(s1
, token
);
1141 return asm_nullary_opcode(token
);
1143 return asm_unary_opcode(s1
, token
);
1148 return asm_branch_opcode(s1
, token
);
1150 case TOK_ASM_sxtbeq
:
1151 case TOK_ASM_sxtheq
:
1152 case TOK_ASM_uxtbeq
:
1153 case TOK_ASM_uxtheq
:
1154 case TOK_ASM_movteq
:
1155 case TOK_ASM_movweq
:
1156 return asm_binary_opcode(s1
, token
);
1159 case TOK_ASM_ldrbeq
:
1161 case TOK_ASM_strbeq
:
1162 return asm_single_data_transfer_opcode(s1
, token
);
1180 case TOK_ASM_andseq
:
1181 case TOK_ASM_eorseq
:
1182 case TOK_ASM_subseq
:
1183 case TOK_ASM_rsbseq
:
1184 case TOK_ASM_addseq
:
1185 case TOK_ASM_adcseq
:
1186 case TOK_ASM_sbcseq
:
1187 case TOK_ASM_rscseq
:
1188 // case TOK_ASM_tstseq:
1189 // case TOK_ASM_teqseq:
1190 // case TOK_ASM_cmpseq:
1191 // case TOK_ASM_cmnseq:
1192 case TOK_ASM_orrseq
:
1193 case TOK_ASM_movseq
:
1194 case TOK_ASM_bicseq
:
1195 case TOK_ASM_mvnseq
:
1196 return asm_data_processing_opcode(s1
, token
);
1199 case TOK_ASM_lslseq
:
1201 case TOK_ASM_lsrseq
:
1203 case TOK_ASM_asrseq
:
1205 case TOK_ASM_rorseq
:
1206 case TOK_ASM_rrxseq
:
1208 return asm_shift_opcode(s1
, token
);
1211 case TOK_ASM_mulseq
:
1213 case TOK_ASM_mlaseq
:
1214 return asm_multiplication_opcode(s1
, token
);
1216 case TOK_ASM_smulleq
:
1217 case TOK_ASM_smullseq
:
1218 case TOK_ASM_umulleq
:
1219 case TOK_ASM_umullseq
:
1220 case TOK_ASM_smlaleq
:
1221 case TOK_ASM_smlalseq
:
1222 case TOK_ASM_umlaleq
:
1223 case TOK_ASM_umlalseq
:
1224 return asm_long_multiplication_opcode(s1
, token
);
1226 expect("known instruction");
1230 ST_FUNC
void subst_asm_operand(CString
*add_str
, SValue
*sv
, int modifier
)
1232 int r
, reg
, size
, val
;
1236 if ((r
& VT_VALMASK
) == VT_CONST
) {
1237 if (!(r
& VT_LVAL
) && modifier
!= 'c' && modifier
!= 'n' &&
1239 cstr_ccat(add_str
, '#');
1241 const char *name
= get_tok_str(sv
->sym
->v
, NULL
);
1242 if (sv
->sym
->v
>= SYM_FIRST_ANOM
) {
1243 /* In case of anonymous symbols ("L.42", used
1244 for static data labels) we can't find them
1245 in the C symbol table when later looking up
1246 this name. So enter them now into the asm label
1247 list when we still know the symbol. */
1248 get_asm_sym(tok_alloc(name
, strlen(name
))->tok
, sv
->sym
);
1250 if (tcc_state
->leading_underscore
)
1251 cstr_ccat(add_str
, '_');
1252 cstr_cat(add_str
, name
, -1);
1253 if ((uint32_t) sv
->c
.i
== 0)
1255 cstr_ccat(add_str
, '+');
1258 if (modifier
== 'n')
1260 snprintf(buf
, sizeof(buf
), "%d", (int) sv
->c
.i
);
1261 cstr_cat(add_str
, buf
, -1);
1263 } else if ((r
& VT_VALMASK
) == VT_LOCAL
) {
1264 snprintf(buf
, sizeof(buf
), "[fp,#%d]", (int) sv
->c
.i
);
1265 cstr_cat(add_str
, buf
, -1);
1266 } else if (r
& VT_LVAL
) {
1267 reg
= r
& VT_VALMASK
;
1268 if (reg
>= VT_CONST
)
1269 tcc_internal_error("");
1270 snprintf(buf
, sizeof(buf
), "[%s]",
1271 get_tok_str(TOK_ASM_r0
+ reg
, NULL
));
1272 cstr_cat(add_str
, buf
, -1);
1275 reg
= r
& VT_VALMASK
;
1276 if (reg
>= VT_CONST
)
1277 tcc_internal_error("");
1279 /* choose register operand size */
1280 if ((sv
->type
.t
& VT_BTYPE
) == VT_BYTE
||
1281 (sv
->type
.t
& VT_BTYPE
) == VT_BOOL
)
1283 else if ((sv
->type
.t
& VT_BTYPE
) == VT_SHORT
)
1288 if (modifier
== 'b') {
1290 } else if (modifier
== 'w') {
1292 } else if (modifier
== 'k') {
1298 reg
= TOK_ASM_r0
+ reg
;
1301 snprintf(buf
, sizeof(buf
), "%s", get_tok_str(reg
, NULL
));
1302 cstr_cat(add_str
, buf
, -1);
1306 /* generate prolog and epilog code for asm statement */
1307 ST_FUNC
void asm_gen_code(ASMOperand
*operands
, int nb_operands
,
1308 int nb_outputs
, int is_output
,
1309 uint8_t *clobber_regs
,
1312 uint8_t regs_allocated
[NB_ASM_REGS
];
1315 uint32_t saved_regset
= 0;
1317 // TODO: Check non-E ABI.
1318 // Note: Technically, r13 (sp) is also callee-saved--but that does not matter yet
1319 static uint8_t reg_saved
[] = { 4, 5, 6, 7, 8, 9 /* Note: sometimes special reg "sb" */ , 10, 11 };
1321 /* mark all used registers */
1322 memcpy(regs_allocated
, clobber_regs
, sizeof(regs_allocated
));
1323 for(i
= 0; i
< nb_operands
;i
++) {
1326 regs_allocated
[op
->reg
] = 1;
1328 for(i
= 0; i
< sizeof(reg_saved
)/sizeof(reg_saved
[0]); i
++) {
1330 if (regs_allocated
[reg
])
1331 saved_regset
|= 1 << reg
;
1334 if (!is_output
) { // prolog
1335 /* generate reg save code */
1337 gen_le32(0xe92d0000 | saved_regset
); // push {...}
1339 /* generate load code */
1340 for(i
= 0; i
< nb_operands
; i
++) {
1343 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&&
1345 /* memory reference case (for both input and
1349 sv
.r
= (sv
.r
& ~VT_VALMASK
) | VT_LOCAL
| VT_LVAL
;
1352 } else if (i
>= nb_outputs
|| op
->is_rw
) { // not write-only
1353 /* load value in register */
1354 load(op
->reg
, op
->vt
);
1356 tcc_error("long long not implemented");
1361 /* generate save code */
1362 for(i
= 0 ; i
< nb_outputs
; i
++) {
1365 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
) {
1366 if (!op
->is_memory
) {
1369 sv
.r
= (sv
.r
& ~VT_VALMASK
) | VT_LOCAL
;
1374 sv
.r
= (sv
.r
& ~VT_VALMASK
) | out_reg
;
1375 store(op
->reg
, &sv
);
1378 store(op
->reg
, op
->vt
);
1380 tcc_error("long long not implemented");
1385 /* generate reg restore code */
1387 gen_le32(0xe8bd0000 | saved_regset
); // pop {...}
1391 /* return the constraint priority (we allocate first the lowest
1392 numbered constraints) */
1393 static inline int constraint_priority(const char *str
)
1395 int priority
, c
, pr
;
1397 /* we take the lowest priority */
1405 case 'l': // in ARM mode, that's an alias for 'r' [ARM].
1406 case 'r': // register [general]
1407 case 'p': // valid memory address for load,store [general]
1410 case 'M': // integer constant for shifts [ARM]
1411 case 'I': // integer valid for data processing instruction immediate
1412 case 'J': // integer in range -4095...4095
1414 case 'i': // immediate integer operand, including symbolic constants [general]
1415 case 'm': // memory operand [general]
1416 case 'g': // general-purpose-register, memory, immediate integer [general]
1420 tcc_error("unknown constraint '%c'", c
);
1429 static const char *skip_constraint_modifiers(const char *p
)
1431 /* Constraint modifier:
1432 = Operand is written to by this instruction
1433 + Operand is both read and written to by this instruction
1434 % Instruction is commutative for this operand and the following operand.
1436 Per-alternative constraint modifier:
1437 & Operand is clobbered before the instruction is done using the input operands
1439 while (*p
== '=' || *p
== '&' || *p
== '+' || *p
== '%')
1444 #define REG_OUT_MASK 0x01
1445 #define REG_IN_MASK 0x02
1447 #define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
1449 ST_FUNC
void asm_compute_constraints(ASMOperand
*operands
,
1450 int nb_operands
, int nb_outputs
,
1451 const uint8_t *clobber_regs
,
1454 /* overall format: modifier, then ,-seperated list of alternatives; all operands for a single instruction must have the same number of alternatives */
1455 /* TODO: Simple constraints
1457 o memory operand that is offsetable
1458 V memory but not offsetable
1459 < memory operand with autodecrement addressing is allowed. Restrictions apply.
1460 > memory operand with autoincrement addressing is allowed. Restrictions apply.
1461 n immediate integer operand with a known numeric value
1462 E immediate floating operand (const_double) is allowed, but only if target=host
1463 F immediate floating operand (const_double or const_vector) is allowed
1464 s immediate integer operand whose value is not an explicit integer
1465 X any operand whatsoever
1466 0...9 (postfix); (can also be more than 1 digit number); an operand that matches the specified operand number is allowed
1469 /* TODO: ARM constraints:
1470 k the stack pointer register
1471 G the floating-point constant 0.0
1472 Q memory reference where the exact address is in a single register ("m" is preferable for asm statements)
1473 R an item in the constant pool
1474 S symbol in the text segment of the current file
1475 [ Uv memory reference suitable for VFP load/store insns (reg+constant offset)]
1476 [ Uy memory reference suitable for iWMMXt load/store instructions]
1477 Uq memory reference suitable for the ARMv4 ldrsb instruction
1480 int sorted_op
[MAX_ASM_OPERANDS
];
1481 int i
, j
, k
, p1
, p2
, tmp
, reg
, c
, reg_mask
;
1483 uint8_t regs_allocated
[NB_ASM_REGS
];
1486 for (i
= 0; i
< nb_operands
; i
++) {
1488 op
->input_index
= -1;
1494 /* compute constraint priority and evaluate references to output
1495 constraints if input constraints */
1496 for (i
= 0; i
< nb_operands
; i
++) {
1498 str
= op
->constraint
;
1499 str
= skip_constraint_modifiers(str
);
1500 if (isnum(*str
) || *str
== '[') {
1501 /* this is a reference to another constraint */
1502 k
= find_constraint(operands
, nb_operands
, str
, NULL
);
1503 if ((unsigned) k
>= i
|| i
< nb_outputs
)
1504 tcc_error("invalid reference in constraint %d ('%s')",
1507 if (operands
[k
].input_index
>= 0)
1508 tcc_error("cannot reference twice the same operand");
1509 operands
[k
].input_index
= i
;
1511 } else if ((op
->vt
->r
& VT_VALMASK
) == VT_LOCAL
1513 && (reg
= op
->vt
->sym
->r
& VT_VALMASK
) < VT_CONST
) {
1517 op
->priority
= constraint_priority(str
);
1521 /* sort operands according to their priority */
1522 for (i
= 0; i
< nb_operands
; i
++)
1524 for (i
= 0; i
< nb_operands
- 1; i
++) {
1525 for (j
= i
+ 1; j
< nb_operands
; j
++) {
1526 p1
= operands
[sorted_op
[i
]].priority
;
1527 p2
= operands
[sorted_op
[j
]].priority
;
1530 sorted_op
[i
] = sorted_op
[j
];
1536 for (i
= 0; i
< NB_ASM_REGS
; i
++) {
1537 if (clobber_regs
[i
])
1538 regs_allocated
[i
] = REG_IN_MASK
| REG_OUT_MASK
;
1540 regs_allocated
[i
] = 0;
1542 /* sp cannot be used */
1543 regs_allocated
[13] = REG_IN_MASK
| REG_OUT_MASK
;
1544 /* fp cannot be used yet */
1545 regs_allocated
[11] = REG_IN_MASK
| REG_OUT_MASK
;
1547 /* allocate registers and generate corresponding asm moves */
1548 for (i
= 0; i
< nb_operands
; i
++) {
1551 str
= op
->constraint
;
1552 /* no need to allocate references */
1553 if (op
->ref_index
>= 0)
1555 /* select if register is used for output, input or both */
1556 if (op
->input_index
>= 0) {
1557 reg_mask
= REG_IN_MASK
| REG_OUT_MASK
;
1558 } else if (j
< nb_outputs
) {
1559 reg_mask
= REG_OUT_MASK
;
1561 reg_mask
= REG_IN_MASK
;
1564 if (is_reg_allocated(op
->reg
))
1566 ("asm regvar requests register that's taken already");
1573 case '=': // Operand is written-to
1575 case '+': // Operand is both READ and written-to
1578 case '&': // Operand is clobbered before the instruction is done using the input operands
1579 if (j
>= nb_outputs
)
1580 tcc_error("'%c' modifier can only be applied to outputs",
1582 reg_mask
= REG_IN_MASK
| REG_OUT_MASK
;
1584 case 'l': // In non-thumb mode, alias for 'r'--otherwise r0-r7 [ARM]
1585 case 'r': // general-purpose register
1586 case 'p': // loadable/storable address
1587 /* any general register */
1588 for (reg
= 0; reg
<= 8; reg
++) {
1589 if (!is_reg_allocated(reg
))
1594 /* now we can reload in the register */
1597 regs_allocated
[reg
] |= reg_mask
;
1599 case 'I': // integer that is valid as an data processing instruction immediate (0...255, rotated by a multiple of two)
1600 case 'J': // integer in the range -4095 to 4095 [ARM]
1601 case 'K': // integer that satisfies constraint I when inverted (one's complement)
1602 case 'L': // integer that satisfies constraint I when inverted (two's complement)
1603 case 'i': // immediate integer operand, including symbolic constants
1604 if (!((op
->vt
->r
& (VT_VALMASK
| VT_LVAL
)) == VT_CONST
))
1607 case 'M': // integer in the range 0 to 32
1609 ((op
->vt
->r
& (VT_VALMASK
| VT_LVAL
| VT_SYM
)) ==
1613 case 'm': // memory operand
1615 /* nothing special to do because the operand is already in
1616 memory, except if the pointer itself is stored in a
1617 memory variable (VT_LLOCAL case) */
1618 /* XXX: fix constant case */
1619 /* if it is a reference to a memory zone, it must lie
1620 in a register, so we reserve the register in the
1621 input registers and a load will be generated
1623 if (j
< nb_outputs
|| c
== 'm') {
1624 if ((op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
) {
1625 /* any general register */
1626 for (reg
= 0; reg
<= 8; reg
++) {
1627 if (!(regs_allocated
[reg
] & REG_IN_MASK
))
1632 /* now we can reload in the register */
1633 regs_allocated
[reg
] |= REG_IN_MASK
;
1640 tcc_error("asm constraint %d ('%s') could not be satisfied",
1644 /* if a reference is present for that operand, we assign it too */
1645 if (op
->input_index
>= 0) {
1646 operands
[op
->input_index
].reg
= op
->reg
;
1647 operands
[op
->input_index
].is_llong
= op
->is_llong
;
1651 /* compute out_reg. It is used to store outputs registers to memory
1652 locations references by pointers (VT_LLOCAL case) */
1654 for (i
= 0; i
< nb_operands
; i
++) {
1657 (op
->vt
->r
& VT_VALMASK
) == VT_LLOCAL
&& !op
->is_memory
) {
1658 for (reg
= 0; reg
<= 8; reg
++) {
1659 if (!(regs_allocated
[reg
] & REG_OUT_MASK
))
1662 tcc_error("could not find free output register for reloading");
1669 /* print sorted constraints */
1671 for (i
= 0; i
< nb_operands
; i
++) {
1674 printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
1676 op
->id
? get_tok_str(op
->id
, NULL
) : "",
1677 op
->constraint
, op
->vt
->r
, op
->reg
);
1680 printf("out_reg=%d\n", *pout_reg
);
1684 ST_FUNC
void asm_clobber(uint8_t *clobber_regs
, const char *str
)
1689 if (!strcmp(str
, "memory") ||
1690 !strcmp(str
, "cc") ||
1691 !strcmp(str
, "flags"))
1693 ts
= tok_alloc(str
, strlen(str
));
1694 reg
= asm_parse_regvar(ts
->tok
);
1696 tcc_error("invalid clobber register '%s'", str
);
1698 clobber_regs
[reg
] = 1;
1701 /* If T refers to a register then return the register number and type.
1702 Otherwise return -1. */
1703 ST_FUNC
int asm_parse_regvar (int t
)
1705 if (t
>= TOK_ASM_r0
&& t
<= TOK_ASM_pc
) { /* register name */
1708 return TOK_ASM_r11
- TOK_ASM_r0
;
1710 return TOK_ASM_r12
- TOK_ASM_r0
;
1712 return TOK_ASM_r13
- TOK_ASM_r0
;
1714 return TOK_ASM_r14
- TOK_ASM_r0
;
1716 return TOK_ASM_r15
- TOK_ASM_r0
;
1718 return t
- TOK_ASM_r0
;
1724 /*************************************************************/
1725 #endif /* ndef TARGET_DEFS_ONLY */