1 /* neatcc code generation */
12 /* variable location */
17 #define LOC_LOCAL 0x10
19 #define MIN(a, b) ((a) < (b) ? (a) : (b))
20 #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
22 char cs
[SECLEN
]; /* code segment */
24 static struct mem ds
; /* data segment */
25 static long bsslen
; /* bss segment size */
27 static long sp
; /* stack pointer offset from R_RBP */
28 static long sp_max
; /* maximum stack pointer offset */
29 static long sp_tmp
; /* sp for the first tmp on the stack */
30 static int localoff
[NLOCALS
]; /* the offset of locals on the stack */
31 static int nlocals
; /* number of locals */
38 /* function statistics */
39 int pass1
; /* collect statistics; 1st pass */
40 static int stat_calls
; /* # of function calls */
41 static int stat_tmps
; /* # of stack temporaries */
42 static int stat_regs
; /* mask of used registers */
44 /* optimization info */
45 static int pass2
; /* use the collected statistics in the 1st pass */
46 static int tmp_mask
; /* registers that can be used for tmps */
48 /* register allocation for locals */
49 #define TMP_ISLREG(t) (!(t)->bt && (t)->loc == LOC_LOCAL && r_regmap((t)->id) >= 0)
50 #define TMP_LREG(t) (r_regmap((t)->id))
52 #define TMP(i) (((i) < ntmp) ? &tmps[ntmp - 1 - (i)] : NULL)
57 long off
; /* offset from a symbol or a local */
58 unsigned loc
; /* variable location */
59 unsigned bt
; /* type of address; zero when not a pointer */
60 int id
; /* local variable id */
64 static struct tmp
*regs
[N_REGS
];
67 static long labels
[NJMPS
];
69 static long jmp_loc
[NJMPS
];
70 static int jmp_goal
[NJMPS
];
71 static int jmp_len
[NJMPS
];
78 err("nomem: NJMPS reached!\n");
84 /* the number of bytes needed for holding jmp displacement */
85 static int jmp_sz(int id
)
87 long n
= jmp_len
[id
] > 0 ? jmp_len
[id
] : -jmp_len
[id
];
91 return n
== 0 ? 0 : 1;
92 return n
< 0x7000 ? 2 : 4;
95 static void jmp_add(int id
, int rn
, int z
)
99 err("nomem: NJMPS reached!\n");
100 i_jmp(rn
, z
, jmp_sz(njmps
));
101 jmp_loc
[njmps
] = cslen
;
102 jmp_goal
[njmps
] = id
;
106 static void jmp_fill(void)
109 for (i
= 0; i
< njmps
; i
++)
110 jmp_len
[i
] = i_fill(jmp_loc
[i
], labels
[jmp_goal
[i
]], jmp_sz(i
));
113 /* generating code */
115 void os(void *s
, int n
)
118 cs
[cslen
++] = *(char *) (s
++);
121 void oi(long n
, int l
)
129 static long sp_push(int sz
)
131 sp
-= ALIGN(sz
, LONGSZ
);
137 static void tmp_mem(struct tmp
*tmp
)
140 if (tmp
->loc
!= LOC_REG
|| (1 << src
) & r_lregs())
144 tmp
->addr
= sp_push(LONGSZ
);
145 i_save(src
, REG_FP
, tmp
->addr
, LONGSZ
);
151 static void num_cast(struct tmp
*t
, unsigned bt
)
153 if (!(bt
& BT_SIGNED
) && BT_SZ(bt
) != LONGSZ
)
154 t
->addr
&= ((1l << (long) (BT_SZ(bt
) * 8)) - 1);
155 if (bt
& BT_SIGNED
&& BT_SZ(bt
) != LONGSZ
&&
156 t
->addr
> (1l << (BT_SZ(bt
) * 8 - 1)))
157 t
->addr
= -((1l << (BT_SZ(bt
) * 8)) - t
->addr
);
160 static void tmp_reg(struct tmp
*tmp
, int dst
, int deref
)
167 if (tmp
->loc
== LOC_NUM
) {
168 i_num(dst
, tmp
->addr
);
173 if (tmp
->loc
== LOC_SYM
) {
174 i_sym(dst
, tmp
->sym
, tmp
->off
);
179 if (tmp
->loc
== LOC_REG
) {
181 i_load(dst
, tmp
->addr
, 0, bt
);
182 else if (dst
!= tmp
->addr
)
183 i_mov(dst
, tmp
->addr
);
184 regs
[tmp
->addr
] = NULL
;
186 if (tmp
->loc
== LOC_LOCAL
) {
192 i_load(dst
, REG_FP
, tmp
->addr
+ tmp
->off
, bt
);
194 i_op_imm(O_ADD
, dst
, REG_FP
, tmp
->addr
+ tmp
->off
);
196 if (tmp
->loc
== LOC_MEM
) {
197 i_load(dst
, REG_FP
, tmp
->addr
, LONGSZ
);
199 i_load(dst
, dst
, 0, bt
);
202 stat_regs
|= 1 << dst
;
207 /* empty the given register, but never touch the registers in rsrvd mask */
208 static void reg_free(int reg
, int rsrvd
)
214 for (i
= 0; i
< N_TMPS
; i
++)
215 if (!regs
[tmpregs
[i
]] && ~rsrvd
& (1 << tmpregs
[i
])) {
216 tmp_reg(regs
[reg
], tmpregs
[i
], 0);
222 static void reg_for(int reg
, struct tmp
*t
)
224 if (regs
[reg
] && regs
[reg
] != t
)
228 static void tmp_mv(struct tmp
*t
, int reg
)
234 static void tmp_to(struct tmp
*t
, int reg
)
237 if (t
->loc
== LOC_LOCAL
&& TMP_ISLREG(t
)) {
239 t
->addr
= TMP_LREG(t
);
245 static void tmp_drop(int n
)
248 for (i
= ntmp
- n
; i
< ntmp
; i
++)
249 if (tmps
[i
].loc
== LOC_REG
)
250 regs
[tmps
[i
].addr
] = NULL
;
254 static void tmp_pop(int reg
)
256 struct tmp
*t
= TMP(0);
261 static struct tmp
*tmp_new(void)
263 return &tmps
[ntmp
++];
266 static void tmp_push(int reg
)
268 struct tmp
*t
= tmp_new();
271 stat_regs
|= 1 << reg
;
276 void o_local(long addr
)
278 struct tmp
*t
= tmp_new();
279 t
->addr
= localoff
[addr
];
288 struct tmp
*t
= tmp_new();
294 void o_sym(char *name
)
296 struct tmp
*t
= tmp_new();
297 strcpy(t
->sym
, name
);
303 void o_tmpdrop(int n
)
305 if (n
== -1 || n
> ntmp
)
315 /* make sure tmps remain intact after a conditional expression */
319 for (i
= 0; i
< ntmp
- 1; i
++)
323 void o_forkpush(void)
328 void o_forkjoin(void)
335 struct tmp
*t1
= TMP(0);
336 struct tmp
*t2
= TMP(1);
338 memcpy(&t
, t1
, sizeof(t
));
339 memcpy(t1
, t2
, sizeof(t
));
340 memcpy(t2
, &t
, sizeof(t
));
341 if (t1
->loc
== LOC_REG
)
343 if (t2
->loc
== LOC_REG
)
347 static int reg_get(int mask
)
351 for (i
= 0; i
< N_TMPS
; i
++)
352 if ((1 << tmpregs
[i
]) & mask
&& !regs
[tmpregs
[i
]]) {
353 stat_regs
|= 1 << tmpregs
[i
];
356 for (i
= 0; i
< N_TMPS
; i
++)
357 if ((1 << tmpregs
[i
]) & mask
) {
358 reg_free(tmpregs
[i
], 0);
359 stat_regs
|= 1 << tmpregs
[i
];
362 die("reg_get: out of registers!\n");
366 static int reg_tmp(struct tmp
*t
, int mask
, int readonly
)
368 if (t
->loc
== LOC_REG
&& (mask
& (1 << t
->addr
)))
369 if (!(r_lregs() & (1 << t
->addr
)) || (readonly
&& !t
->bt
))
371 return reg_get(mask
);
374 static int reg_tmpn(struct tmp
*t
, int notmask
, int readonly
)
376 if (t
->loc
== LOC_REG
&& !(notmask
& (1 << t
->addr
)))
377 if (!(r_lregs() & (1 << t
->addr
)) || (readonly
&& !t
->bt
))
379 return reg_get(~notmask
);
382 static void tmp_copy(struct tmp
*t1
)
384 struct tmp
*t2
= tmp_new();
385 memcpy(t2
, t1
, sizeof(*t1
));
386 if (!(t1
->loc
& (LOC_REG
| LOC_MEM
)))
388 if (t1
->loc
== LOC_MEM
) {
389 tmp_mv(t2
, reg_get(R_TMPS
));
390 } else if (t1
->loc
== LOC_REG
) {
391 t2
->addr
= reg_tmpn(t2
, 1 << t1
->addr
, 0);
392 i_mov(t2
->addr
, t1
->addr
);
394 stat_regs
|= 1 << t2
->addr
;
403 void o_deref(unsigned bt
)
405 struct tmp
*t
= TMP(0);
408 t
->addr
= TMP_LREG(t
);
411 tmp_to(t
, reg_tmp(t
, R_TMPS
, 0));
418 struct tmp
*t
= TMP(0);
419 tmp_to(t
, reg_tmp(t
, R_TMPS
, 0));
422 #define TMP_NUM(t) ((t)->loc == LOC_NUM && !(t)->bt)
423 #define LOCAL_PTR(t) ((t)->loc == LOC_LOCAL && !(t)->bt)
424 #define SYM_PTR(t) ((t)->loc == LOC_SYM && !(t)->bt)
426 int o_popnum(long *c
)
428 struct tmp
*t
= TMP(0);
445 long o_mklocal(int sz
)
448 localoff
[nlocals
] = sp_push(ALIGN(sz
, LONGSZ
));
452 void o_rmlocal(long addr
, int sz
)
457 long o_arg2loc(int i
)
462 #define MOVXX(bt) ((BT_SZ(bt) == LONGSZ ? O_MOV : ((bt) & BT_SIGNED ? O_SX : O_ZX)))
464 void o_assign(unsigned bt
)
466 struct tmp
*t1
= TMP(0);
467 struct tmp
*t2
= TMP(1);
468 int r1
= reg_tmp(t1
, BT_SZ(bt
) > 1 ? R_TMPS
: R_BYTE
, 1);
469 int r2
= reg_tmpn(t2
, 1 << r1
, 1);
472 if (TMP_ISLREG(t2
)) {
473 i_op_imm(MOVXX(bt
), TMP_LREG(t2
), r1
, BT_SZ(bt
) * 8);
478 if (t2
->loc
== LOC_LOCAL
) {
480 off
= t2
->addr
+ t2
->off
;
485 i_save(r1
, r2
, off
, bt
);
491 static long cu(int op
, long i
)
504 static int c_uop(int op
)
506 struct tmp
*t1
= TMP(0);
510 o_num(cu(op
, t1
->addr
));
514 static long cb(int op
, long a
, long b
)
539 return (unsigned long) a
>> b
;
556 static int c_bop(int op
)
558 struct tmp
*t1
= TMP(0);
559 struct tmp
*t2
= TMP(1);
560 int locs
= LOCAL_PTR(t1
) + LOCAL_PTR(t2
);
561 int syms
= SYM_PTR(t1
) + SYM_PTR(t2
);
562 int nums
= TMP_NUM(t1
) + TMP_NUM(t2
);
563 if (syms
+ locs
== 2 || syms
+ nums
+ locs
!= 2)
566 if ((op
& 0xff) != O_ADD
&& ((op
& 0xff) != O_SUB
|| TMP_NUM(t2
)))
569 long o1
= TMP_NUM(t1
) ? t1
->addr
: t1
->off
;
570 long o2
= TMP_NUM(t2
) ? t2
->addr
: t2
->off
;
571 long ret
= cb(op
, o2
, o1
);
574 if (t2
->loc
== LOC_LOCAL
)
579 long ret
= cb(op
, t2
->addr
, t1
->addr
);
586 /* allocate registers for the given binary or unary instruction */
587 static void regs2(int op
, int *rd
, int *r1
, int *r2
)
592 i_reg(op
, &md
, &m1
, &m2
, &mt
);
594 struct tmp
*t2
= TMP(0);
595 *r2
= reg_tmp(t2
, m2
, 1);
600 struct tmp
*t1
= TMP(m2
? 1 : 0);
601 *r1
= reg_tmp(t1
, m1
& ~all
, md
? 1 : 0);
606 if (m2
&& md
& tmp_mask
& (1 << *r2
))
608 else if (m1
&& md
& tmp_mask
& (1 << *r1
))
611 *rd
= reg_get(md
& ~all
);
617 for (i
= 0; i
< N_TMPS
; i
++)
618 if (mt
& ~all
& (1 << tmpregs
[i
]))
619 reg_free(tmpregs
[i
], all
| mt
);
622 tmp_drop(m2
? 2 : 1);
625 /* allocate registers for a 3 operand instruction */
626 static void regs3(int op
, int *r0
, int *r1
, int *r2
)
629 struct tmp
*t0
= TMP(2);
630 struct tmp
*t1
= TMP(1);
631 struct tmp
*t2
= TMP(0);
634 i_reg(op
, &m0
, &m1
, &m2
, &mt
);
636 *r2
= reg_tmp(t2
, m2
, 1);
641 *r1
= reg_tmp(t1
, m1
& ~(1 << *r2
), 1);
646 *r0
= reg_tmp(t0
, m0
& ~((1 << *r2
) | (1 << *r1
)), 1);
651 for (i
= 0; i
< N_TMPS
; i
++)
652 if (mt
& ~all
& (1 << tmpregs
[i
]))
653 reg_free(tmpregs
[i
], all
| mt
);
659 static void op_imm(int op
, long n
)
662 regs2(op
| O_IMM
, &rd
, &r1
, &r2
);
663 i_op_imm(op
| O_IMM
, rd
, r1
, n
);
672 regs2(op
, &rd
, &r1
, &r2
);
673 i_op(op
, rd
, r1
, r2
);
677 static int bop_imm(int op
, long *n
, int swap
)
679 struct tmp
*t1
= TMP(0);
680 struct tmp
*t2
= TMP(1);
681 if (!TMP_NUM(t1
) && (!swap
|| !TMP_NUM(t2
)))
683 *n
= TMP_NUM(t1
) ? t1
->addr
: t2
->addr
;
692 static void bin_op(int op
, int swap
)
696 if (!bop_imm(op
, &n
, swap
)) {
697 regs2(op
| O_IMM
, &rd
, &r1
, &r2
);
698 i_op_imm(op
, rd
, r1
, n
);
700 regs2(op
, &rd
, &r1
, &r2
);
701 i_op(op
, rd
, r1
, r2
);
706 static int log2a(unsigned long n
)
709 for (i
= 0; i
< LONGSZ
* 8; i
++)
712 if (i
== LONGSZ
* 8 || !(n
>> (i
+ 1)))
717 /* optimized version of mul/div/mod for powers of two */
718 static int mul_2(int op
)
720 struct tmp
*t1
= TMP(0);
721 struct tmp
*t2
= TMP(1);
724 if ((op
& 0xff) == O_MUL
&& t2
->loc
== LOC_NUM
&& !t2
->bt
)
726 if (t1
->loc
!= LOC_NUM
|| t1
->bt
)
732 if ((op
& 0xff) == O_MUL
) {
748 op_imm((op
& O_SIGNED
) | O_SHR
, p
);
768 if ((op
& 0xf0) == 0x00) /* add */
769 bin_op(op
, (op
& 0xff) != O_SUB
);
770 if ((op
& 0xf0) == 0x10) /* shx */
772 if ((op
& 0xf0) == 0x20) { /* mul */
775 bin_op(op
, (op
& 0xff) == O_MUL
);
777 if ((op
& 0xf0) == 0x30)
778 bin_op(op
, (op
& 0xff) == O_EQ
|| (op
& 0xff) == O_NEQ
);
784 regs3(O_MCPY
, &r0
, &r1
, &r2
);
785 i_memcpy(r0
, r1
, r2
);
791 regs3(O_MSET
, &r0
, &r1
, &r2
);
792 i_memset(r0
, r1
, r2
);
795 void o_cast(unsigned bt
)
797 struct tmp
*t
= TMP(0);
798 if (!t
->bt
&& t
->loc
== LOC_NUM
) {
802 if (BT_SZ(bt
) != LONGSZ
)
803 op_imm(MOVXX(bt
), BT_SZ(bt
) * 8);
806 static void jxz(int id
, int z
)
808 int r
= reg_tmp(TMP(0), R_TMPS
, 1);
828 void o_call(int argc
, int rets
)
832 int aregs
= MIN(N_ARGS
, argc
);
833 for (i
= 0; i
< N_TMPS
; i
++)
834 if (regs
[tmpregs
[i
]] && regs
[tmpregs
[i
]] - tmps
< ntmp
- argc
)
835 tmp_mem(regs
[tmpregs
[i
]]);
837 sp_push(LONGSZ
* (argc
- aregs
));
838 for (i
= argc
- 1; i
>= aregs
; --i
) {
839 int reg
= reg_tmp(TMP(0), R_TMPS
, 1);
841 i_save(reg
, REG_SP
, (i
- aregs
) * LONGSZ
, LONGSZ
);
844 for (i
= aregs
- 1; i
>= 0; --i
)
845 tmp_to(TMP(aregs
- i
- 1), argregs
[i
]);
848 if (t
->loc
== LOC_SYM
&& !t
->bt
) {
849 i_call(t
->sym
, t
->off
);
852 int reg
= reg_tmp(t
, R_TMPS
, 1);
861 void o_bsnew(char *name
, int size
, int global
)
865 out_sym(name
, OUT_BSS
| (global
? OUT_GLOB
: 0), bsslen
, size
);
866 bsslen
+= ALIGN(size
, OUT_ALIGNMENT
);
869 static char dat_names
[NDATS
][NAMELEN
];
870 static int dat_offs
[NDATS
];
873 long o_dsnew(char *name
, int size
, int global
)
880 err("nomem: NDATS reached!\n");
881 strcpy(dat_names
[idx
], name
);
882 dat_offs
[idx
] = mem_len(&ds
);
883 out_sym(name
, OUT_DS
| (global
? OUT_GLOB
: 0), mem_len(&ds
), size
);
884 mem_putz(&ds
, ALIGN(size
, OUT_ALIGNMENT
));
885 return dat_offs
[idx
];
888 void o_dscpy(long addr
, void *buf
, int len
)
891 mem_cpy(&ds
, addr
, buf
, len
);
894 static int dat_off(char *name
)
897 for (i
= 0; i
< ndats
; i
++)
898 if (!strcmp(name
, dat_names
[i
]))
903 void o_dsset(char *name
, int off
, unsigned bt
)
905 struct tmp
*t
= TMP(0);
906 int sym_off
= dat_off(name
) + off
;
911 if (t
->loc
== LOC_NUM
&& !t
->bt
) {
913 mem_cpy(&ds
, sym_off
, &t
->addr
, BT_SZ(bt
));
915 if (t
->loc
== LOC_SYM
&& !t
->bt
) {
916 out_rel(t
->sym
, OUT_DS
, sym_off
);
917 mem_cpy(&ds
, sym_off
, &t
->off
, BT_SZ(bt
));
925 out_write(fd
, cs
, cslen
, mem_buf(&ds
), mem_len(&ds
));
928 static void func_reset(void)
932 memset(regs
, 0, sizeof(regs
));
942 stat_regs
= 1 << REG_RET
;
943 for (i
= 0; i
< func_argc
; i
++) {
944 localoff
[nlocals
++] = i_args() + argaddr
;
945 if (i
>= N_ARGS
|| r_sargs() & (1 << argregs
[i
]))
950 void o_func_beg(char *name
, int argc
, int global
, int varg
)
957 tmp_mask
= N_TMPS
> 6 ? R_TMPS
& ~R_SAVED
: R_TMPS
;
959 out_sym(name
, (global
? OUT_GLOB
: 0) | OUT_CS
, cslen
, 0);
960 i_prolog(argc
, varg
, r_sargs(), tmp_mask
& R_SAVED
, 1, 1);
972 int initfp
, subsp
, sregs
;
978 locregs
= r_alloc(leaf
, stat_regs
);
979 subsp
= nlocals
> locregs
|| !leaf
;
980 initfp
= subsp
|| stat_tmps
|| func_argc
> N_ARGS
;
981 sregs
= (r_lregs() | stat_regs
) & R_SAVED
;
982 tmp_mask
= stat_regs
;
985 i_prolog(func_argc
, func_varg
, r_sargs(), sregs
, initfp
, subsp
);
987 for (i
= 0; i
< MIN(func_argc
, N_ARGS
); i
++)
988 if (r_regmap(i
) >= 0 && r_regmap(i
) != argregs
[i
])
989 i_mov(r_regmap(i
), argregs
[i
]);
990 for (i
= N_ARGS
; i
< func_argc
; i
++)
991 if (r_regmap(i
) >= 0)
992 i_load(r_regmap(i
), REG_FP
, localoff
[i
], LONGSZ
);
995 void o_func_end(void)