1 /* architecture-dependent code generation for ARM */
6 #define MIN(a, b) ((a) < (b) ? (a) : (b))
7 #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
8 #define oi4(i) oi((i), 4)
10 #define REG_DP 10 /* data pointer register */
11 #define REG_TMP 12 /* temporary register */
12 #define REG_LR 14 /* link register */
13 #define REG_PC 15 /* program counter */
14 #define REG_RET 0 /* returned value register */
27 int tmpregs
[] = {4, 5, 6, 7, 8, 9, 3, 2, 1, 0};
28 int argregs
[] = {0, 1, 2, 3};
30 static struct mem cs
; /* generated code */
32 /* code generation functions */
33 static char *ointbuf(long n
, int l
)
37 for (i
= 0; i
< l
; i
++) {
44 static void oi(long n
, int l
)
46 mem_put(&cs
, ointbuf(n
, l
), l
);
49 static void oi_at(long pos
, long n
, int l
)
51 mem_cpy(&cs
, pos
, ointbuf(n
, l
), l
);
54 static long opos(void)
59 /* compiled division functions; div.s contains the source */
60 static int udivdi3
[] = {
61 0xe3a02000, 0xe3a03000, 0xe1110001, 0x0a00000a,
62 0xe1b0c211, 0xe2822001, 0x5afffffc, 0xe3a0c001,
63 0xe2522001, 0x4a000004, 0xe1500211, 0x3afffffb,
64 0xe0400211, 0xe083321c, 0xeafffff8, 0xe1a01000,
65 0xe1a00003, 0xe1a0f00e,
67 static int umoddi3
[] = {
68 0xe92d4000, 0xebffffeb, 0xe1a00001, 0xe8bd8000,
70 static int divdi3
[] = {
71 0xe92d4030, 0xe1a04000, 0xe1a05001, 0xe1100000,
72 0x42600000, 0xe1110001, 0x42611000, 0xebffffe1,
73 0xe1340005, 0x42600000, 0xe1140004, 0x42611000,
76 static int moddi3
[] = {
77 0xe92d4000, 0xebfffff0, 0xe1a00001, 0xe8bd8000,
80 static long *rel_sym
; /* relocation symbols */
81 static long *rel_flg
; /* relocation flags */
82 static long *rel_off
; /* relocation offsets */
83 static long rel_n
, rel_sz
; /* relocation count */
85 static long lab_sz
; /* label count */
86 static long *lab_loc
; /* label offsets in cs */
87 static long jmp_n
, jmp_sz
; /* jump count */
88 static long *jmp_off
; /* jump offsets */
89 static long *jmp_dst
; /* jump destinations */
90 static long jmp_ret
; /* the position of the last return jmp */
92 static void lab_add(long id
)
94 while (id
>= lab_sz
) {
96 lab_sz
= MAX(128, lab_sz
* 2);
97 lab_loc
= mextend(lab_loc
, lab_n
, lab_sz
, sizeof(*lab_loc
));
102 static void jmp_add(long off
, long dst
)
104 if (jmp_n
== jmp_sz
) {
105 jmp_sz
= MAX(128, jmp_sz
* 2);
106 jmp_off
= mextend(jmp_off
, jmp_n
, jmp_sz
, sizeof(*jmp_off
));
107 jmp_dst
= mextend(jmp_dst
, jmp_n
, jmp_sz
, sizeof(*jmp_dst
));
109 jmp_off
[jmp_n
] = off
;
110 jmp_dst
[jmp_n
] = dst
;
114 void i_label(long id
)
119 static void rel_add(long sym
, long flg
, long off
)
121 if (rel_n
== rel_sz
) {
122 rel_sz
= MAX(128, rel_sz
* 2);
123 rel_sym
= mextend(rel_sym
, rel_n
, rel_sz
, sizeof(*rel_sym
));
124 rel_flg
= mextend(rel_flg
, rel_n
, rel_sz
, sizeof(*rel_flg
));
125 rel_off
= mextend(rel_off
, rel_n
, rel_sz
, sizeof(*rel_off
));
127 rel_sym
[rel_n
] = sym
;
128 rel_flg
[rel_n
] = flg
;
129 rel_off
[rel_n
] = off
;
133 static int putdiv
= 0; /* output div/mod functions */
134 static int func_call
; /* */
136 static void i_call(long sym
, long off
);
138 static void i_div(char *func
)
142 i_call(out_sym(func
), 0);
146 static long *num_off
; /* data immediate value */
147 static long *num_sym
; /* relocation data symbol name */
148 static int num_n
, num_sz
;
150 static int pool_find(long sym
, long off
)
153 for (i
= 0; i
< num_n
; i
++)
154 if (sym
== num_sym
[i
] && off
== num_off
[i
])
156 if (num_n
== num_sz
) {
157 num_sz
= MAX(128, num_sz
* 2);
158 num_off
= mextend(num_off
, num_n
, num_sz
, sizeof(*num_off
));
159 num_sym
= mextend(num_sym
, num_n
, num_sz
, sizeof(*num_sym
));
163 return (num_n
++) << 2;
166 static int pool_num(long num
)
168 return pool_find(-1, num
);
171 static int pool_reloc(long sym
, long off
)
173 return pool_find(sym
, off
);
176 static void pool_write(void)
179 for (i
= 0; i
< num_n
; i
++) {
181 rel_add(num_sym
[i
], OUT_CS
, opos());
188 * +---------------------------------------+
189 * |COND|00|I| op |S| Rn | Rd | operand2 |
190 * +---------------------------------------+
192 * S: set condition code
194 * Rd: destination operand
196 * I=0 operand2=| shift | Rm |
197 * I=1 operand2=|rota| imm |
199 #define ADD(op, rd, rn, s, i, cond) \
200 (((cond) << 28) | ((i) << 25) | ((s) << 20) | \
201 ((op) << 21) | ((rn) << 16) | ((rd) << 12))
203 static int add_encimm(unsigned n
)
206 while (i
< 12 && (n
>> ((4 + i
) << 1)))
208 return (n
>> (i
<< 1)) | (((16 - i
) & 0x0f) << 8);
211 static unsigned add_decimm(int n
)
213 int rot
= (16 - ((n
>> 8) & 0x0f)) & 0x0f;
214 return (n
& 0xff) << (rot
<< 1);
217 static int add_rndimm(unsigned n
)
219 int rot
= (n
>> 8) & 0x0f;
225 rot
= (rot
+ 12) & 0x0f;
227 return ((num
+ 1) & 0xff) | (rot
<< 8);
230 static int opcode_add(int op
)
232 /* opcode for O_ADD, O_SUB, O_AND, O_OR, O_XOR */
233 static int rx
[] = {I_ADD
, I_SUB
, I_AND
, I_ORR
, I_EOR
};
234 return rx
[op
& 0x0f];
237 static void i_add(int op
, int rd
, int rn
, int rm
)
239 oi4(ADD(opcode_add(op
), rd
, rn
, 0, 0, 14) | rm
);
242 static void i_add_imm(int op
, int rd
, int rn
, long n
)
244 oi4(ADD(opcode_add(op
), rd
, rn
, 0, 1, 14) | add_encimm(n
));
247 static void i_ldr(int l
, int rd
, int rn
, int off
, int bt
);
249 static void i_num(int rd
, long n
)
251 int enc
= add_encimm(n
);
252 if (n
== add_decimm(enc
)) {
253 oi4(ADD(I_MOV
, rd
, 0, 0, 1, 14) | enc
);
256 enc
= add_encimm(-n
- 1);
257 if (~n
== add_decimm(enc
)) {
258 oi4(ADD(I_MVN
, rd
, 0, 0, 1, 14) | enc
);
261 i_ldr(1, rd
, REG_DP
, pool_num(n
), LONGSZ
);
264 static void i_add_anyimm(int rd
, int rn
, long n
)
267 int imm
= add_encimm(neg
? -n
: n
);
268 if (imm
== add_decimm(neg
? -n
: n
)) {
269 oi4(ADD(neg
? I_SUB
: I_ADD
, rd
, rn
, 0, 1, 14) | imm
);
272 i_add(O_ADD
, rd
, rd
, rn
);
278 * +----------------------------------------+
279 * |COND|000000|A|S| Rd | Rn | Rs |1001| Rm |
280 * +----------------------------------------+
284 * C: set condition codes
286 * I=0 operand2=| shift | Rm |
287 * I=1 operand2=|rota| imm |
289 #define MUL(rd, rn, rs) \
290 ((14 << 28) | ((rd) << 16) | ((0) << 12) | ((rn) << 8) | ((9) << 4) | (rm))
292 static void i_mul(int rd
, int rn
, int rm
)
294 oi4(MUL(rd
, rn
, rm
));
297 static int opcode_set(long op
)
299 /* lt, ge, eq, ne, le, gt */
300 static int ucond
[] = {3, 2, 0, 1, 9, 8};
301 static int scond
[] = {11, 10, 0, 1, 13, 12};
303 return bt
& T_MSIGN
? scond
[op
& 0x0f] : ucond
[op
& 0x0f];
306 static void i_tst(int rn
, int rm
)
308 oi4(ADD(I_TST
, 0, rn
, 1, 0, 14) | rm
);
311 static void i_cmp(int rn
, int rm
)
313 oi4(ADD(I_CMP
, 0, rn
, 1, 0, 14) | rm
);
316 static void i_cmp_imm(int rn
, long n
)
318 oi4(ADD(I_CMP
, 0, rn
, 1, 1, 14) | add_encimm(n
));
321 static void i_set(int cond
, int rd
)
323 oi4(ADD(I_MOV
, rd
, 0, 0, 1, 14));
324 oi4(ADD(I_MOV
, rd
, 0, 0, 1, opcode_set(cond
)) | 1);
331 static int opcode_shl(long op
)
334 return O_T(op
) & T_MSIGN
? SM_ASR
: SM_LSR
;
338 static void i_shl(long op
, int rd
, int rm
, int rs
)
340 int sm
= opcode_shl(op
);
341 oi4(ADD(I_MOV
, rd
, 0, 0, 0, 14) | (rs
<< 8) | (sm
<< 5) | (1 << 4) | rm
);
344 static void i_shl_imm(long op
, int rd
, int rn
, long n
)
346 int sm
= opcode_shl(op
);
347 oi4(ADD(I_MOV
, rd
, 0, 0, 0, 14) | (n
<< 7) | (sm
<< 5) | rn
);
350 void i_mov(int rd
, int rn
)
352 oi4(ADD(I_MOV
, rd
, 0, 0, 0, 14) | rn
);
356 * single data transfer:
357 * +------------------------------------------+
358 * |COND|01|I|P|U|B|W|L| Rn | Rd | offset |
359 * +------------------------------------------+
361 * I: immediate/offset
362 * P: post/pre indexing
368 * Rd: source/destination register
370 * I=0 offset=| immediate |
371 * I=1 offset=| shift | Rm |
373 * halfword and signed data transfer
374 * +----------------------------------------------+
375 * |COND|000|P|U|0|W|L| Rn | Rd |0000|1|S|H|1| Rm |
376 * +----------------------------------------------+
378 * +----------------------------------------------+
379 * |COND|000|P|U|1|W|L| Rn | Rd |off1|1|S|H|1|off2|
380 * +----------------------------------------------+
385 #define LDR(l, rd, rn, b, u, p, w) \
386 ((14 << 28) | (1 << 26) | ((p) << 24) | ((b) << 22) | ((u) << 23) | \
387 ((w) << 21) | ((l) << 20) | ((rn) << 16) | ((rd) << 12))
388 #define LDRH(l, rd, rn, s, h, u, i) \
389 ((14 << 28) | (1 << 24) | ((u) << 23) | ((i) << 22) | ((l) << 20) | \
390 ((rn) << 16) | ((rd) << 12) | ((s) << 6) | ((h) << 5) | (9 << 4))
392 static void i_ldr(int l
, int rd
, int rn
, int off
, int bt
)
394 int b
= T_SZ(bt
) == 1;
395 int h
= T_SZ(bt
) == 2;
396 int s
= l
&& (bt
& T_MSIGN
);
397 int half
= h
|| (b
&& s
);
398 int maximm
= half
? 0x100 : 0x1000;
402 while (off
>= maximm
) {
403 int imm
= add_encimm(off
);
404 oi4(ADD(neg
? I_SUB
: I_ADD
, REG_TMP
, rn
, 0, 1, 14) | imm
);
406 off
-= add_decimm(imm
);
409 oi4(LDR(l
, rd
, rn
, b
, !neg
, 1, 0) | off
);
411 oi4(LDRH(l
, rd
, rn
, s
, h
, !neg
, 1) |
412 ((off
& 0xf0) << 4) | (off
& 0x0f));
415 static void i_sym(int rd
, long sym
, long off
)
417 int doff
= pool_reloc(sym
, off
);
418 i_ldr(1, rd
, REG_DP
, doff
, LONGSZ
);
421 static void i_neg(int rd
, int r1
)
423 oi4(ADD(I_RSB
, rd
, r1
, 0, 1, 14));
426 static void i_not(int rd
, int r1
)
428 oi4(ADD(I_MVN
, rd
, 0, 0, 0, 14) | r1
);
431 static void i_lnot(int rd
, int r1
)
437 /* rd = rd & ((1 << bits) - 1) */
438 static void i_zx(int rd
, int r1
, int bits
)
441 oi4(ADD(I_AND
, rd
, r1
, 0, 1, 14) | add_encimm((1 << bits
) - 1));
443 i_shl_imm(O_SHL
, rd
, r1
, 32 - bits
);
444 i_shl_imm(O_SHR
, rd
, rd
, 32 - bits
);
448 static void i_sx(int rd
, int r1
, int bits
)
450 i_shl_imm(O_SHL
, rd
, r1
, 32 - bits
);
451 i_shl_imm(O_MK(O_SHR
, SLNG
), rd
, rd
, 32 - bits
);
456 * +-----------------------------------+
457 * |COND|101|L| offset |
458 * +-----------------------------------+
462 #define BL(cond, l, o) (((cond) << 28) | (5 << 25) | ((l) << 24) | \
463 ((((o) - 8) >> 2) & 0x00ffffff))
464 static long i_jmp(long op
, long rn
, long rm
)
467 if (O_C(op
) == O_JMP
) {
472 if (O_C(op
) & O_JZ
) {
475 oi4(BL(O_C(op
) == O_JZ
? 0 : 1, 0, 0));
478 if (O_C(op
) & O_JCC
) {
484 oi4(BL(opcode_set(op
), 0, 0));
490 static void i_memcpy(int rd
, int rs
, int rn
)
492 oi4(ADD(I_SUB
, rn
, rn
, 1, 1, 14) | 1);
494 oi4(LDR(1, REG_TMP
, rs
, 1, 1, 0, 0) | 1);
495 oi4(LDR(0, REG_TMP
, rd
, 1, 1, 0, 0) | 1);
499 static void i_memset(int rd
, int rs
, int rn
)
501 oi4(ADD(I_SUB
, rn
, rn
, 1, 1, 14) | 1);
503 oi4(LDR(0, rs
, rd
, 1, 1, 0, 0) | 1);
507 static void i_call_reg(int rd
)
509 i_mov(REG_LR
, REG_PC
);
513 static void i_call(long sym
, long off
)
515 rel_add(sym
, OUT_CS
| OUT_RLREL
| OUT_RL24
, opos());
519 int i_imm(long lim
, long n
)
521 return add_decimm(add_encimm(n
)) == n
;
524 long i_reg(long op
, long *rd
, long *r1
, long *r2
, long *r3
, long *tmp
)
534 *r1
= oc
& (O_NUM
| O_SYM
) ? 32 : R_TMPS
;
537 if (oc
& O_MUL
&& oc
& (O_NUM
| O_SYM
))
539 if (oc
== O_DIV
|| oc
== O_MOD
) {
541 *r1
= 1 << argregs
[0];
542 *r2
= 1 << argregs
[1];
543 *tmp
= R_TMPS
& ~R_PERM
;
549 *r2
= op
& O_NUM
? 0 : R_TMPS
;
554 *r1
= op
& O_NUM
? 0 : R_TMPS
;
557 if (oc
== O_MSET
|| oc
== O_MCPY
) {
561 *tmp
= (1 << 4) | (1 << 6) | (oc
== O_MCPY
? (1 << 5) : 0);
565 *r1
= (1 << REG_RET
);
569 *rd
= (1 << REG_RET
);
570 *r1
= oc
& O_SYM
? 0 : R_TMPS
;
571 *tmp
= R_TMPS
& ~R_PERM
;
577 *r2
= oc
& O_NUM
? 0 : R_TMPS
;
583 *r3
= oc
& O_NUM
? 0 : R_TMPS
;
592 *r2
= oc
& O_NUM
? 0 : R_TMPS
;
600 long i_ins(long op
, long rd
, long r1
, long r2
, long r3
)
607 i_add_imm(op
, rd
, r1
, r2
);
609 i_add_anyimm(rd
, r1
, r2
);
611 i_add(op
, rd
, r1
, r2
);
616 i_shl_imm(op
, rd
, r1
, r2
);
618 i_shl(op
, rd
, r1
, r2
);
624 i_div(O_T(op
) & T_MSIGN
? "__divdi3" : "__udivdi3");
626 i_div(O_T(op
) & T_MSIGN
? "__moddi3" : "__umoddi3");
651 if (oc
== (O_CALL
| O_SYM
)) {
656 if (oc
== (O_MOV
| O_SYM
)) {
660 if (oc
== (O_MOV
| O_NUM
)) {
665 i_memset(r1
, r2
, r3
);
669 i_memcpy(r1
, r2
, r3
);
674 jmp_add(i_jmp(O_JMP
, 0, 0), 0);
677 if (oc
== (O_LD
| O_NUM
)) {
678 i_ldr(1, rd
, r1
, r2
, bt
);
681 if (oc
== (O_ST
| O_NUM
)) {
682 i_ldr(0, r1
, r2
, r3
, bt
);
686 if (T_SZ(bt
) == LONGSZ
)
690 i_sx(rd
, r1
, T_SZ(bt
) * 8);
692 i_zx(rd
, r1
, T_SZ(bt
) * 8);
697 jmp_add(i_jmp(op
, r1
, r2
), r3
+ 1);
703 void i_wrap(int argc
, long sargs
, long spsub
, int initfp
, long sregs
, long sregs_pos
)
707 long diff
; /* prologue length */
709 int nsargs
= 0; /* number of saved arguments */
710 int initdp
= num_n
> 0; /* initialize data pointer */
711 long pregs
= 1; /* registers saved in function prologue */
715 if (!initfp
&& !spsub
&& !initdp
&& !sargs
&& argc
< N_ARGS
)
717 initfp
= initfp
|| pregs
;
718 /* removing the last jmp to the epilogue */
719 if (jmp_ret
+ 4 == opos()) {
720 mem_cut(&cs
, jmp_ret
);
723 lab_add(0); /* the return label */
724 body_n
= mem_len(&cs
);
726 /* generating function prologue */
727 for (i
= 0; i
< N_ARGS
; i
++)
728 if ((1 << argregs
[i
]) & sargs
)
730 if (nsargs
& 0x1) { /* keeping stack 8-aligned */
731 for (i
= 0; i
< N_ARGS
; i
++)
732 if (!((1 << argregs
[i
]) & sargs
))
734 sargs
|= 1 << argregs
[i
];
737 oi4(0xe92d0000 | sargs
); /* stmfd sp!, {r0-r3} */
739 oi4(0xe1a0c00d); /* mov r12, sp */
740 oi4(0xe92d5c00); /* stmfd sp!, {sl, fp, ip, lr} */
743 oi4(0xe1a0b00d); /* mov fp, sp */
744 if (sregs
) { /* sregs_pos should be encoded as immediate */
745 int npos
= add_decimm(add_rndimm(add_encimm(-sregs_pos
)));
746 spsub
+= npos
+ sregs_pos
;
749 if (spsub
) { /* sub sp, sp, xx */
750 spsub
= ALIGN(spsub
, 8);
751 spsub
= add_decimm(add_rndimm(add_encimm(spsub
)));
752 oi4(0xe24dd000 | add_encimm(spsub
));
756 oi4(0xe28fa000); /* add dp, pc, xx */
758 if (sregs
) { /* saving registers */
759 oi4(0xe24bc000 | add_encimm(-sregs_pos
));
760 oi4(0xe88c0000 | sregs
); /* stmea ip, {r4-r9} */
763 mem_put(&cs
, body
, body_n
);
765 /* generating function epilogue */
766 if (sregs
) { /* restoring saved registers */
767 oi4(0xe24bc000 | add_encimm(-sregs_pos
));
768 oi4(0xe89c0000 | sregs
); /* ldmfd ip, {r4-r9} */
771 oi4(0xe89bac00); /* ldmfd fp, {sl, fp, sp, pc} */
773 oi4(0xe1a0f00e); /* mov pc, lr */
775 /* adjusting code offsets */
776 for (i
= 0; i
< rel_n
; i
++)
778 for (i
= 0; i
< jmp_n
; i
++)
780 for (i
= 0; i
< lab_sz
; i
++)
782 /* writing the data pool */
784 int dpoff
= opos() - dpadd
- 8;
785 dpoff
= add_decimm(add_rndimm(add_encimm(dpoff
)));
786 mem_putz(&cs
, dpadd
+ dpoff
+ 8 - opos());
787 /* fill data ptr addition: dp = pc + xx */
788 oi_at(dpadd
, 0xe28fa000 | add_encimm(dpoff
), 4);
793 static void i_fill(long src
, long dst
)
795 long *d
= mem_buf(&cs
) + src
;
796 long c
= (*d
& 0xff000000) | (((dst
- src
- 8) >> 2) & 0x00ffffff);
800 void i_code(char **c
, long *c_len
, long **rsym
, long **rflg
, long **roff
, long *rcnt
)
803 for (i
= 0; i
< jmp_n
; i
++) /* filling jmp destinations */
804 i_fill(jmp_off
[i
], lab_loc
[jmp_dst
[i
]]);
805 *c_len
= mem_len(&cs
);
824 o_code("__udivdi3", (void *) udivdi3
, sizeof(udivdi3
));
825 o_code("__umoddi3", (void *) umoddi3
, sizeof(umoddi3
));
826 o_code("__divdi3", (void *) divdi3
, sizeof(divdi3
));
827 o_code("__moddi3", (void *) moddi3
, sizeof(moddi3
));