1 /* architecture-dependent code generation for ARM */
7 #define MIN(a, b) ((a) < (b) ? (a) : (b))
8 #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
9 #define oi4(i) oi((i), 4)
11 #define REG_DP 10 /* data pointer register */
12 #define REG_TMP 12 /* temporary register */
13 #define REG_LR 14 /* link register */
14 #define REG_PC 15 /* program counter */
27 int tmpregs
[] = {4, 5, 6, 7, 8, 9, 3, 2, 1, 0};
28 int argregs
[] = {0, 1, 2, 3};
30 /* compiled division functions; div.s contains the source */
31 static int udivdi3
[] = {
32 0xe3a02000, 0xe3a03000, 0xe1110001, 0x0a00000a,
33 0xe1b0c211, 0xe2822001, 0x5afffffc, 0xe3a0c001,
34 0xe2522001, 0x4a000004, 0xe1500211, 0x3afffffb,
35 0xe0400211, 0xe083321c, 0xeafffff8, 0xe1a01000,
36 0xe1a00003, 0xe1a0f00e,
38 static int umoddi3
[] = {
39 0xe92d4000, 0xebffffeb, 0xe1a00001, 0xe8bd8000,
41 static int divdi3
[] = {
42 0xe92d4030, 0xe1a04000, 0xe1a05001, 0xe1100000,
43 0x42600000, 0xe1110001, 0x42611000, 0xebffffe1,
44 0xe1340005, 0x42600000, 0xe1140004, 0x42611000,
47 static int moddi3
[] = {
48 0xe92d4000, 0xebfffff0, 0xe1a00001, 0xe8bd8000,
51 /* output div/mod functions */
52 static int putdiv
= 0;
54 static void insert_spsub(void);
56 static void i_div(char *func
)
66 out_sym("__udivdi3", OUT_CS
, cslen
, 0);
67 os(udivdi3
, sizeof(udivdi3
));
68 out_sym("__umoddi3", OUT_CS
, cslen
, 0);
69 os(umoddi3
, sizeof(umoddi3
));
70 out_sym("__divdi3", OUT_CS
, cslen
, 0);
71 os(divdi3
, sizeof(divdi3
));
72 out_sym("__moddi3", OUT_CS
, cslen
, 0);
73 os(moddi3
, sizeof(moddi3
));
77 /* for optimizing cmp + bcc */
78 #define OPT_ISCMP() (last_cmp + 12 == cslen && last_set + 4 == cslen)
79 #define OPT_CCOND() (*(unsigned int *) ((void *) cs + last_set) >> 28)
81 static long last_cmp
= -1;
82 static long last_set
= -1;
85 static long num_offs
[NNUMS
]; /* data immediate value */
86 static char num_names
[NNUMS
][NAMELEN
]; /* relocation data symbol name */
89 static int pool_find(char *name
, int off
)
92 for (i
= 0; i
< nums
; i
++)
93 if (!strcmp(name
, num_names
[i
]) && off
== num_offs
[i
])
98 static int pool_num(long num
)
100 int idx
= pool_find("", num
);
104 num_names
[idx
][0] = '\0';
109 static int pool_reloc(char *name
, long off
)
111 int idx
= pool_find(name
, off
);
115 strcpy(num_names
[idx
], name
);
120 static void pool_write(void)
123 for (i
= 0; i
< nums
; i
++) {
124 if (num_names
[i
] && !pass1
)
125 out_rel(num_names
[i
], OUT_CS
, cslen
);
132 * +---------------------------------------+
133 * |COND|00|I| op |S| Rn | Rd | operand2 |
134 * +---------------------------------------+
136 * S: set condition code
138 * Rd: destination operand
140 * I=0 operand2=| shift | Rm |
141 * I=1 operand2=|rota| imm |
143 #define ADD(op, rd, rn, s, i, cond) \
144 (((cond) << 28) | ((i) << 25) | ((s) << 20) | \
145 ((op) << 21) | ((rn) << 16) | ((rd) << 12))
147 static int add_encimm(unsigned n
)
150 while (i
< 12 && (n
>> ((4 + i
) << 1)))
152 return (n
>> (i
<< 1)) | (((16 - i
) & 0x0f) << 8);
155 static unsigned add_decimm(int n
)
157 int rot
= (16 - ((n
>> 8) & 0x0f)) & 0x0f;
158 return (n
& 0xff) << (rot
<< 1);
161 static int add_rndimm(unsigned n
)
163 int rot
= (n
>> 8) & 0x0f;
169 rot
= (rot
+ 12) & 0x0f;
171 return ((num
+ 1) & 0xff) | (rot
<< 8);
174 static int opcode_add(int op
)
176 /* opcode for O_ADD, O_SUB, O_AND, O_OR, O_XOR */
177 static int rx
[] = {I_ADD
, I_SUB
, I_AND
, I_ORR
, I_EOR
};
178 return rx
[op
& 0x0f];
181 static void i_add(int op
, int rd
, int rn
, int rm
)
183 oi4(ADD(opcode_add(op
), rd
, rn
, 0, 0, 14) | rm
);
186 int i_imm(int op
, long imm
)
188 return (op
& 0xf0) != 0x20 && add_decimm(add_encimm(imm
)) == imm
;
191 static void i_add_imm(int op
, int rd
, int rn
, long n
)
193 oi4(ADD(opcode_add(op
), rd
, rn
, 0, 1, 14) | add_encimm(n
));
196 static void i_ldr(int l
, int rd
, int rn
, int off
, int bt
);
198 void i_num(int rd
, long n
)
200 int enc
= add_encimm(n
);
201 if (n
== add_decimm(enc
)) {
202 oi4(ADD(I_MOV
, rd
, 0, 0, 1, 14) | enc
);
205 enc
= add_encimm(-n
- 1);
206 if (~n
== add_decimm(enc
)) {
207 oi4(ADD(I_MVN
, rd
, 0, 0, 1, 14) | enc
);
210 i_ldr(1, rd
, REG_DP
, pool_num(n
), LONGSZ
);
213 static void i_add_anyimm(int rd
, int rn
, long n
)
216 int imm
= add_encimm(neg
? -n
: n
);
217 if (imm
== add_decimm(neg
? -n
: n
)) {
218 oi4(ADD(neg
? I_SUB
: I_ADD
, rd
, rn
, 0, 1, 14) | imm
);
221 i_add(O_ADD
, rd
, rd
, rn
);
227 * +----------------------------------------+
228 * |COND|000000|A|S| Rd | Rn | Rs |1001| Rm |
229 * +----------------------------------------+
233 * C: set condition codes
235 * I=0 operand2=| shift | Rm |
236 * I=1 operand2=|rota| imm |
238 #define MUL(rd, rn, rs) \
239 ((14 << 28) | ((rd) << 16) | ((0) << 12) | ((rn) << 8) | ((9) << 4) | (rm))
241 static void i_mul(int rd
, int rn
, int rm
)
243 oi4(MUL(rd
, rn
, rm
));
246 static int opcode_set(int op
)
248 /* lt, gt, le, ge, eq, neq */
249 static int ucond
[] = {3, 8, 9, 2, 0, 1};
250 static int scond
[] = {11, 12, 13, 10, 0, 1};
251 return op
& O_SIGNED
? scond
[op
& 0x0f] : ucond
[op
& 0x0f];
254 static void i_tst(int rn
, int rm
)
256 oi4(ADD(I_TST
, 0, rn
, 1, 0, 14) | rm
);
259 static void i_cmp(int rn
, int rm
)
262 oi4(ADD(I_CMP
, 0, rn
, 1, 0, 14) | rm
);
265 static void i_cmp_imm(int rn
, long n
)
268 oi4(ADD(I_CMP
, 0, rn
, 1, 1, 14) | add_encimm(n
));
271 static void i_set(int cond
, int rd
)
273 oi4(ADD(I_MOV
, rd
, 0, 0, 1, 14));
275 oi4(ADD(I_MOV
, rd
, 0, 0, 1, opcode_set(cond
)) | 1);
282 static int opcode_shl(int op
)
285 return op
& O_SIGNED
? SM_ASR
: SM_LSR
;
289 static void i_shl(int op
, int rd
, int rm
, int rs
)
291 int sm
= opcode_shl(op
);
292 oi4(ADD(I_MOV
, rd
, 0, 0, 0, 14) | (rs
<< 8) | (sm
<< 5) | (1 << 4) | rm
);
295 static void i_shl_imm(int op
, int rd
, int rn
, long n
)
297 int sm
= opcode_shl(op
);
298 oi4(ADD(I_MOV
, rd
, 0, 0, 0, 14) | (n
<< 7) | (sm
<< 5) | rn
);
301 void i_mov(int rd
, int rn
)
303 oi4(ADD(I_MOV
, rd
, 0, 0, 0, 14) | rn
);
307 * single data transfer:
308 * +------------------------------------------+
309 * |COND|01|I|P|U|B|W|L| Rn | Rd | offset |
310 * +------------------------------------------+
312 * I: immediate/offset
313 * P: post/pre indexing
319 * Rd: source/destination register
321 * I=0 offset=| immediate |
322 * I=1 offset=| shift | Rm |
324 * halfword and signed data transfer
325 * +----------------------------------------------+
326 * |COND|000|P|U|0|W|L| Rn | Rd |0000|1|S|H|1| Rm |
327 * +----------------------------------------------+
329 * +----------------------------------------------+
330 * |COND|000|P|U|1|W|L| Rn | Rd |off1|1|S|H|1|off2|
331 * +----------------------------------------------+
336 #define LDR(l, rd, rn, b, u, p, w) \
337 ((14 << 28) | (1 << 26) | ((p) << 24) | ((b) << 22) | ((u) << 23) | \
338 ((w) << 21) | ((l) << 20) | ((rn) << 16) | ((rd) << 12))
339 #define LDRH(l, rd, rn, s, h, u, i) \
340 ((14 << 28) | (1 << 24) | ((u) << 23) | ((i) << 22) | ((l) << 20) | \
341 ((rn) << 16) | ((rd) << 12) | ((s) << 6) | ((h) << 5) | (9 << 4))
343 static void i_ldr(int l
, int rd
, int rn
, int off
, int bt
)
345 int b
= BT_SZ(bt
) == 1;
346 int h
= BT_SZ(bt
) == 2;
347 int s
= l
&& (bt
& BT_SIGNED
);
348 int half
= h
|| (b
&& s
);
349 int maximm
= half
? 0x100 : 0x1000;
353 while (off
>= maximm
) {
354 int imm
= add_encimm(off
);
355 oi4(ADD(neg
? I_SUB
: I_ADD
, REG_TMP
, rn
, 0, 1, 14) | imm
);
357 off
-= add_decimm(imm
);
360 oi4(LDR(l
, rd
, rn
, b
, !neg
, 1, 0) | off
);
362 oi4(LDRH(l
, rd
, rn
, s
, h
, !neg
, 1) |
363 ((off
& 0xf0) << 4) | (off
& 0x0f));
366 void i_load(int rd
, int rn
, int off
, int bt
)
368 i_ldr(1, rd
, rn
, off
, bt
);
371 void i_save(int rd
, int rn
, int off
, int bt
)
373 i_ldr(0, rd
, rn
, off
, bt
);
376 void i_sym(int rd
, char *sym
, int off
)
378 int doff
= pool_reloc(sym
, off
);
379 i_ldr(1, rd
, REG_DP
, doff
, LONGSZ
);
382 static void i_neg(int rd
, int r1
)
384 oi4(ADD(I_RSB
, rd
, r1
, 0, 1, 14));
387 static void i_not(int rd
, int r1
)
389 oi4(ADD(I_MVN
, rd
, 0, 0, 0, 14) | r1
);
392 static int cond_nots
[] = {1, 0, 3, 2, -1, -1, -1, -1, 9, 8, 11, 10, 13, 12, -1};
394 static void i_lnot(int rd
, int r1
)
397 unsigned int *lset
= (void *) cs
+ last_set
;
398 int cond
= cond_nots
[OPT_CCOND()];
399 *lset
= (*lset
& 0x0fffffff) | (cond
<< 28);
406 /* rd = rd & ((1 << bits) - 1) */
407 static void i_zx(int rd
, int r1
, int bits
)
410 oi4(ADD(I_AND
, rd
, r1
, 0, 1, 14) | add_encimm((1 << bits
) - 1));
412 i_shl_imm(O_SHL
, rd
, r1
, 32 - bits
);
413 i_shl_imm(O_SHR
, rd
, rd
, 32 - bits
);
417 static void i_sx(int rd
, int r1
, int bits
)
419 i_shl_imm(O_SHL
, rd
, r1
, 32 - bits
);
420 i_shl_imm(O_SIGNED
| O_SHR
, rd
, rd
, 32 - bits
);
425 * +-----------------------------------+
426 * |COND|101|L| offset |
427 * +-----------------------------------+
431 #define BL(cond, l, o) (((cond) << 28) | (5 << 25) | ((l) << 24) | \
432 ((((o) - 8) >> 2) & 0x00ffffff))
433 void i_jmp(int rn
, int jc
, int nbytes
)
442 int cond
= OPT_CCOND();
443 cslen
= last_cmp
+ 4;
445 oi4(BL(jc
? cond_nots
[cond
] : cond
, 0, 0));
449 oi4(BL(jc
? 0 : 1, 0, 0));
452 long i_fill(long src
, long dst
, int nbytes
)
454 long *d
= (void *) cs
+ src
- 4;
457 *d
= (*d
& 0xff000000) | (((dst
- src
- 4) >> 2) & 0x00ffffff);
461 void i_memcpy(int rd
, int rs
, int rn
)
463 oi4(ADD(I_SUB
, rn
, rn
, 1, 1, 14) | 1);
465 oi4(LDR(1, REG_TMP
, rs
, 1, 1, 0, 0) | 1);
466 oi4(LDR(0, REG_TMP
, rd
, 1, 1, 0, 0) | 1);
470 void i_memset(int rd
, int rs
, int rn
)
472 oi4(ADD(I_SUB
, rn
, rn
, 1, 1, 14) | 1);
474 oi4(LDR(0, rs
, rd
, 1, 1, 0, 0) | 1);
478 void i_call_reg(int rd
)
480 i_mov(REG_LR
, REG_PC
);
484 void i_call(char *sym
, int off
)
487 out_rel(sym
, OUT_CS
| OUT_RLREL
| OUT_RL24
, cslen
);
491 void i_reg(int op
, int *rd
, int *r1
, int *r2
, int *tmp
)
495 *r2
= (op
& O_IMM
|| (op
& 0xf0) == 0x40) ? 0 : R_TMPS
;
497 if ((op
& 0xff) == O_DIV
|| (op
& 0xff) == O_MOD
) {
499 *r1
= 1 << argregs
[0];
500 *r2
= 1 << argregs
[1];
501 *tmp
= R_TMPS
& ~R_SAVED
;
505 void i_op(int op
, int rd
, int r1
, int r2
)
507 if ((op
& 0xf0) == 0x00)
508 i_add(op
, rd
, r1
, r2
);
509 if ((op
& 0xf0) == 0x10)
510 i_shl(op
, rd
, r1
, r2
);
511 if ((op
& 0xf0) == 0x20) {
512 if ((op
& 0xff) == O_MUL
)
514 if ((op
& 0xff) == O_DIV
)
515 i_div(op
& O_SIGNED
? "__divdi3" : "__udivdi3");
516 if ((op
& 0xff) == O_MOD
)
517 i_div(op
& O_SIGNED
? "__moddi3" : "__umoddi3");
520 if ((op
& 0xf0) == 0x30) {
525 if ((op
& 0xf0) == 0x40) { /* uop */
526 if ((op
& 0xff) == O_NEG
)
528 if ((op
& 0xff) == O_NOT
)
530 if ((op
& 0xff) == O_LNOT
)
536 void i_op_imm(int op
, int rd
, int r1
, long n
)
538 if ((op
& 0xf0) == 0x00) {
540 i_add_imm(op
, rd
, r1
, n
);
542 i_add_anyimm(rd
, r1
, n
);
544 if ((op
& 0xf0) == 0x10) /* shl */
545 i_shl_imm(op
, rd
, r1
, n
);
546 if ((op
& 0xf0) == 0x30) { /* imm */
550 if ((op
& 0xf0) == 0x50) { /* etc */
551 if ((op
& 0xff) == O_ZX
)
553 if ((op
& 0xff) == O_SX
)
555 if ((op
& 0xff) == O_MOV
)
560 static int func_argc
;
561 static int func_varg
;
562 static int func_spsub
;
563 static int func_sargs
;
564 static int func_sregs
;
565 static int func_initfp
;
566 static int func_initdp
= 1;
567 static int spsub_addr
;
568 static int dpadd_addr
;
570 static int saved_regs(int args
)
574 for (i
= 0; i
< N_REGS
; i
++) {
575 if ((1 << i
) & func_sregs
)
577 if (args
&& (1 << i
) & func_sargs
)
585 return saved_regs(0) * LONGSZ
;
593 static int plain_function(void)
595 return !func_initfp
&& !func_spsub
&& !func_initdp
&& !func_varg
&&
596 !func_sargs
&& !func_sregs
&& func_argc
<= N_ARGS
;
599 static void insert_spsub(void)
604 oi4(0xe24dd000); /* sub sp, sp, xx */
608 void i_prolog(int argc
, int varg
, int sargs
, int sregs
, int initfp
, int spsub
)
616 func_initfp
= initfp
;
618 if (plain_function())
621 func_sregs
|= 1 << REG_FP
;
623 func_sregs
|= 1 << REG_DP
;
624 /* stack should remain 8-aligned */
625 if (saved_regs(1) & 0x1)
627 oi4(0xe1a0c00d); /* mov r12, sp */
629 oi4(0xe92d0000 | func_sargs
); /* stmfd sp!, {r0-r3} */
630 oi4(0xe92d5000 | func_sregs
); /* stmfd sp!, {r0-r11, r12, lr} */
632 oi4(0xe1a0b00d); /* mov fp, sp */
637 oi4(0xe28fa000); /* add dp, pc, xx */
641 void i_epilog(int sp_max
)
644 if (plain_function()) {
645 oi4(0xe1a0f00e); /* mov pc, lr */
649 oi4(0xe89ba000 | func_sregs
);/* ldmfd fp, {r4-r11, sp, pc} */
651 oi4(0xe89da000 | func_sregs
);/* ldmfd sp, {r4-r11, sp, pc} */
653 int dpoff
= cslen
- dpadd_addr
- 8;
654 dpoff
= add_decimm(add_rndimm(add_encimm(dpoff
)));
655 cslen
= dpadd_addr
+ dpoff
+ 8;
656 /* fill data ptr addition: dp = pc + xx */
657 *(long *) (cs
+ dpadd_addr
) |= add_encimm(dpoff
);
659 if (func_initfp
&& func_spsub
) {
660 sp_max
= ALIGN(sp_max
, 8);
661 sp_max
= add_decimm(add_rndimm(add_encimm(sp_max
)));
662 /* fill stack sub: sp = sp - xx */
663 *(long *) (cs
+ spsub_addr
) |= add_encimm(sp_max
);