Repository: aligrudi/neatcc Branch: master Commit: 3472e4b5aca9 Files: 18 Total size: 177.0 KB Directory structure: gitextract_0mes5txt/ ├── Makefile ├── README ├── arm.c ├── arm.h ├── cpp.c ├── div.s ├── gen.c ├── int.c ├── mem.c ├── ncc.c ├── ncc.h ├── out.c ├── reg.c ├── tok.c ├── x64.c ├── x64.h ├── x86.c └── x86.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: Makefile ================================================ # output architecture: x64, x86, arm OUT = x64 CC = cc CFLAGS = -Wall -O2 -DNEATCC_`echo $(OUT) | tr "[:lower:]" "[:upper:]"` LDFLAGS = OBJS = ncc.o tok.o out.o cpp.o gen.o int.o reg.o mem.o $(OUT).o all: ncc %.o: %.c ncc.h $(OUT).h $(CC) -c $(CFLAGS) $< ncc: $(OBJS) $(CC) -o $@ $(OBJS) $(LDFLAGS) clean: rm -f *.o ncc ================================================ FILE: README ================================================ NEATCC ====== Neatcc is an ARM/x86(-64) C compiler. It supports a large subset of ANSI C but lacks some of its features, the most important of which are struct bitfields, inline assembly, and floating point types. ================================================ FILE: arm.c ================================================ /* architecture-dependent code generation for ARM */ #include #include #include "ncc.h" #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) #define oi4(i) oi((i), 4) #define REG_DP 10 /* data pointer register */ #define REG_TMP 12 /* temporary register */ #define REG_LR 14 /* link register */ #define REG_PC 15 /* program counter */ #define REG_RET 0 /* returned value register */ #define I_AND 0x00 #define I_EOR 0x01 #define I_SUB 0x02 #define I_RSB 0x03 #define I_ADD 0x04 #define I_TST 0x08 #define I_CMP 0x0a #define I_ORR 0x0c #define I_MOV 0x0d #define I_MVN 0x0f int tmpregs[] = {4, 5, 6, 7, 8, 9, 3, 2, 1, 0}; int argregs[] = {0, 1, 2, 3}; static struct mem cs; /* generated code */ /* code generation functions */ static char *ointbuf(long n, int l) { static char buf[16]; int i; for (i = 0; i < l; i++) { buf[i] = n & 0xff; n >>= 8; } return buf; } static void oi(long n, int l) { mem_put(&cs, ointbuf(n, l), l); } static void oi_at(long pos, long n, int l) { mem_cpy(&cs, pos, ointbuf(n, l), l); } static long opos(void) { return mem_len(&cs); } /* compiled division functions; div.s contains the source */ static int udivdi3[] = { 0xe3a02000, 0xe3a03000, 0xe1110001, 0x0a00000a, 0xe1b0c211, 0xe2822001, 0x5afffffc, 0xe3a0c001, 0xe2522001, 0x4a000004, 0xe1500211, 0x3afffffb, 0xe0400211, 0xe083321c, 0xeafffff8, 0xe1a01000, 0xe1a00003, 0xe1a0f00e, }; static int umoddi3[] = { 0xe92d4000, 0xebffffeb, 0xe1a00001, 0xe8bd8000, }; static int divdi3[] = { 0xe92d4030, 0xe1a04000, 0xe1a05001, 0xe1100000, 0x42600000, 0xe1110001, 0x42611000, 0xebffffe1, 0xe1340005, 0x42600000, 0xe1140004, 0x42611000, 0xe8bd8030, }; static int moddi3[] = { 0xe92d4000, 0xebfffff0, 0xe1a00001, 0xe8bd8000, }; static long *rel_sym; /* relocation symbols */ static long *rel_flg; /* relocation flags */ static long *rel_off; /* relocation offsets */ static long rel_n, rel_sz; /* relocation count */ static long lab_sz; /* label count */ static long *lab_loc; /* label offsets in cs */ static long jmp_n, jmp_sz; /* jump count */ static long *jmp_off; /* jump offsets */ static long *jmp_dst; /* jump destinations */ static long jmp_ret; /* the position of the last return jmp */ static void lab_add(long id) { while (id >= lab_sz) { int lab_n = lab_sz; lab_sz = MAX(128, lab_sz * 2); lab_loc = mextend(lab_loc, lab_n, lab_sz, sizeof(*lab_loc)); } lab_loc[id] = opos(); } static void jmp_add(long off, long dst) { if (jmp_n == jmp_sz) { jmp_sz = MAX(128, jmp_sz * 2); jmp_off = mextend(jmp_off, jmp_n, jmp_sz, sizeof(*jmp_off)); jmp_dst = mextend(jmp_dst, jmp_n, jmp_sz, sizeof(*jmp_dst)); } jmp_off[jmp_n] = off; jmp_dst[jmp_n] = dst; jmp_n++; } void i_label(long id) { lab_add(id + 1); } static void rel_add(long sym, long flg, long off) { if (rel_n == rel_sz) { rel_sz = MAX(128, rel_sz * 2); rel_sym = mextend(rel_sym, rel_n, rel_sz, sizeof(*rel_sym)); rel_flg = mextend(rel_flg, rel_n, rel_sz, sizeof(*rel_flg)); rel_off = mextend(rel_off, rel_n, rel_sz, sizeof(*rel_off)); } rel_sym[rel_n] = sym; rel_flg[rel_n] = flg; rel_off[rel_n] = off; rel_n++; } static int putdiv = 0; /* output div/mod functions */ static int func_call; /* */ static void i_call(long sym, long off); static void i_div(char *func) { putdiv = 1; func_call = 1; i_call(out_sym(func), 0); } /* data pool */ static long *num_off; /* data immediate value */ static long *num_sym; /* relocation data symbol name */ static int num_n, num_sz; static int pool_find(long sym, long off) { int i; for (i = 0; i < num_n; i++) if (sym == num_sym[i] && off == num_off[i]) return i << 2; if (num_n == num_sz) { num_sz = MAX(128, num_sz * 2); num_off = mextend(num_off, num_n, num_sz, sizeof(*num_off)); num_sym = mextend(num_sym, num_n, num_sz, sizeof(*num_sym)); } num_off[i] = off; num_sym[i] = sym; return (num_n++) << 2; } static int pool_num(long num) { return pool_find(-1, num); } static int pool_reloc(long sym, long off) { return pool_find(sym, off); } static void pool_write(void) { int i; for (i = 0; i < num_n; i++) { if (num_sym[i] >= 0) rel_add(num_sym[i], OUT_CS, opos()); oi4(num_off[i]); } } /* * data processing: * +---------------------------------------+ * |COND|00|I| op |S| Rn | Rd | operand2 | * +---------------------------------------+ * * S: set condition code * Rn: first operand * Rd: destination operand * * I=0 operand2=| shift | Rm | * I=1 operand2=|rota| imm | */ #define ADD(op, rd, rn, s, i, cond) \ (((cond) << 28) | ((i) << 25) | ((s) << 20) | \ ((op) << 21) | ((rn) << 16) | ((rd) << 12)) static int add_encimm(unsigned n) { int i = 0; while (i < 12 && (n >> ((4 + i) << 1))) i++; return (n >> (i << 1)) | (((16 - i) & 0x0f) << 8); } static unsigned add_decimm(int n) { int rot = (16 - ((n >> 8) & 0x0f)) & 0x0f; return (n & 0xff) << (rot << 1); } static int add_rndimm(unsigned n) { int rot = (n >> 8) & 0x0f; int num = n & 0xff; if (rot == 0) return n; if (num == 0xff) { num = 0; rot = (rot + 12) & 0x0f; } return ((num + 1) & 0xff) | (rot << 8); } static int opcode_add(int op) { /* opcode for O_ADD, O_SUB, O_AND, O_OR, O_XOR */ static int rx[] = {I_ADD, I_SUB, I_AND, I_ORR, I_EOR}; return rx[op & 0x0f]; } static void i_add(int op, int rd, int rn, int rm) { oi4(ADD(opcode_add(op), rd, rn, 0, 0, 14) | rm); } static void i_add_imm(int op, int rd, int rn, long n) { oi4(ADD(opcode_add(op), rd, rn, 0, 1, 14) | add_encimm(n)); } static void i_ldr(int l, int rd, int rn, int off, int bt); static void i_num(int rd, long n) { int enc = add_encimm(n); if (n == add_decimm(enc)) { oi4(ADD(I_MOV, rd, 0, 0, 1, 14) | enc); return; } enc = add_encimm(-n - 1); if (~n == add_decimm(enc)) { oi4(ADD(I_MVN, rd, 0, 0, 1, 14) | enc); return; } i_ldr(1, rd, REG_DP, pool_num(n), LONGSZ); } static void i_add_anyimm(int rd, int rn, long n) { int neg = n < 0; int imm = add_encimm(neg ? -n : n); if (imm == add_decimm(neg ? -n : n)) { oi4(ADD(neg ? I_SUB : I_ADD, rd, rn, 0, 1, 14) | imm); } else { i_num(rd, n); i_add(O_ADD, rd, rd, rn); } } /* * multiply * +----------------------------------------+ * |COND|000000|A|S| Rd | Rn | Rs |1001| Rm | * +----------------------------------------+ * * Rd: destination * A: accumulate * C: set condition codes * * I=0 operand2=| shift | Rm | * I=1 operand2=|rota| imm | */ #define MUL(rd, rn, rs) \ ((14 << 28) | ((rd) << 16) | ((0) << 12) | ((rn) << 8) | ((9) << 4) | (rm)) static void i_mul(int rd, int rn, int rm) { oi4(MUL(rd, rn, rm)); } static int opcode_set(long op) { /* lt, ge, eq, ne, le, gt */ static int ucond[] = {3, 2, 0, 1, 9, 8}; static int scond[] = {11, 10, 0, 1, 13, 12}; long bt = O_T(op); return bt & T_MSIGN ? scond[op & 0x0f] : ucond[op & 0x0f]; } static void i_tst(int rn, int rm) { oi4(ADD(I_TST, 0, rn, 1, 0, 14) | rm); } static void i_cmp(int rn, int rm) { oi4(ADD(I_CMP, 0, rn, 1, 0, 14) | rm); } static void i_cmp_imm(int rn, long n) { oi4(ADD(I_CMP, 0, rn, 1, 1, 14) | add_encimm(n)); } static void i_set(int cond, int rd) { oi4(ADD(I_MOV, rd, 0, 0, 1, 14)); oi4(ADD(I_MOV, rd, 0, 0, 1, opcode_set(cond)) | 1); } #define SM_LSL 0 #define SM_LSR 1 #define SM_ASR 2 static int opcode_shl(long op) { if (op & 0x0f) return O_T(op) & T_MSIGN ? SM_ASR : SM_LSR; return SM_LSL; } static void i_shl(long op, int rd, int rm, int rs) { int sm = opcode_shl(op); oi4(ADD(I_MOV, rd, 0, 0, 0, 14) | (rs << 8) | (sm << 5) | (1 << 4) | rm); } static void i_shl_imm(long op, int rd, int rn, long n) { int sm = opcode_shl(op); oi4(ADD(I_MOV, rd, 0, 0, 0, 14) | (n << 7) | (sm << 5) | rn); } void i_mov(int rd, int rn) { oi4(ADD(I_MOV, rd, 0, 0, 0, 14) | rn); } /* * single data transfer: * +------------------------------------------+ * |COND|01|I|P|U|B|W|L| Rn | Rd | offset | * +------------------------------------------+ * * I: immediate/offset * P: post/pre indexing * U: down/up * B: byte/word * W: writeback * L: store/load * Rn: base register * Rd: source/destination register * * I=0 offset=| immediate | * I=1 offset=| shift | Rm | * * halfword and signed data transfer * +----------------------------------------------+ * |COND|000|P|U|0|W|L| Rn | Rd |0000|1|S|H|1| Rm | * +----------------------------------------------+ * * +----------------------------------------------+ * |COND|000|P|U|1|W|L| Rn | Rd |off1|1|S|H|1|off2| * +----------------------------------------------+ * * S: signed * H: halfword */ #define LDR(l, rd, rn, b, u, p, w) \ ((14 << 28) | (1 << 26) | ((p) << 24) | ((b) << 22) | ((u) << 23) | \ ((w) << 21) | ((l) << 20) | ((rn) << 16) | ((rd) << 12)) #define LDRH(l, rd, rn, s, h, u, i) \ ((14 << 28) | (1 << 24) | ((u) << 23) | ((i) << 22) | ((l) << 20) | \ ((rn) << 16) | ((rd) << 12) | ((s) << 6) | ((h) << 5) | (9 << 4)) static void i_ldr(int l, int rd, int rn, int off, int bt) { int b = T_SZ(bt) == 1; int h = T_SZ(bt) == 2; int s = l && (bt & T_MSIGN); int half = h || (b && s); int maximm = half ? 0x100 : 0x1000; int neg = off < 0; if (neg) off = -off; while (off >= maximm) { int imm = add_encimm(off); oi4(ADD(neg ? I_SUB : I_ADD, REG_TMP, rn, 0, 1, 14) | imm); rn = REG_TMP; off -= add_decimm(imm); } if (!half) oi4(LDR(l, rd, rn, b, !neg, 1, 0) | off); else oi4(LDRH(l, rd, rn, s, h, !neg, 1) | ((off & 0xf0) << 4) | (off & 0x0f)); } static void i_sym(int rd, long sym, long off) { int doff = pool_reloc(sym, off); i_ldr(1, rd, REG_DP, doff, LONGSZ); } static void i_neg(int rd, int r1) { oi4(ADD(I_RSB, rd, r1, 0, 1, 14)); } static void i_not(int rd, int r1) { oi4(ADD(I_MVN, rd, 0, 0, 0, 14) | r1); } static void i_lnot(int rd, int r1) { i_tst(r1, r1); i_set(O_EQ, rd); } /* rd = rd & ((1 << bits) - 1) */ static void i_zx(int rd, int r1, int bits) { if (bits <= 8) { oi4(ADD(I_AND, rd, r1, 0, 1, 14) | add_encimm((1 << bits) - 1)); } else { i_shl_imm(O_SHL, rd, r1, 32 - bits); i_shl_imm(O_SHR, rd, rd, 32 - bits); } } static void i_sx(int rd, int r1, int bits) { i_shl_imm(O_SHL, rd, r1, 32 - bits); i_shl_imm(O_MK(O_SHR, SLNG), rd, rd, 32 - bits); } /* * branch: * +-----------------------------------+ * |COND|101|L| offset | * +-----------------------------------+ * * L: link */ #define BL(cond, l, o) (((cond) << 28) | (5 << 25) | ((l) << 24) | \ ((((o) - 8) >> 2) & 0x00ffffff)) static long i_jmp(long op, long rn, long rm) { long pos; if (O_C(op) == O_JMP) { pos = opos(); oi4(BL(14, 0, 0)); return pos; } if (O_C(op) & O_JZ) { i_tst(rn, rn); pos = opos(); oi4(BL(O_C(op) == O_JZ ? 0 : 1, 0, 0)); return pos; } if (O_C(op) & O_JCC) { if (op & O_NUM) i_cmp_imm(rn, rm); else i_cmp(rn, rm); pos = opos(); oi4(BL(opcode_set(op), 0, 0)); return pos; } return -1; } static void i_memcpy(int rd, int rs, int rn) { oi4(ADD(I_SUB, rn, rn, 1, 1, 14) | 1); oi4(BL(4, 0, 16)); oi4(LDR(1, REG_TMP, rs, 1, 1, 0, 0) | 1); oi4(LDR(0, REG_TMP, rd, 1, 1, 0, 0) | 1); oi4(BL(14, 0, -16)); } static void i_memset(int rd, int rs, int rn) { oi4(ADD(I_SUB, rn, rn, 1, 1, 14) | 1); oi4(BL(4, 0, 12)); oi4(LDR(0, rs, rd, 1, 1, 0, 0) | 1); oi4(BL(14, 0, -12)); } static void i_call_reg(int rd) { i_mov(REG_LR, REG_PC); i_mov(REG_PC, rd); } static void i_call(long sym, long off) { rel_add(sym, OUT_CS | OUT_RLREL | OUT_RL24, opos()); oi4(BL(14, 1, off)); } int i_imm(long lim, long n) { return add_decimm(add_encimm(n)) == n; } long i_reg(long op, long *rd, long *r1, long *r2, long *r3, long *tmp) { long oc = O_C(op); *rd = 0; *r1 = 0; *r2 = 0; *r3 = 0; *tmp = 0; if (oc & O_MOV) { *rd = R_TMPS; *r1 = oc & (O_NUM | O_SYM) ? 32 : R_TMPS; return 0; } if (oc & O_MUL && oc & (O_NUM | O_SYM)) return 1; if (oc == O_DIV || oc == O_MOD) { *rd = 1 << REG_RET; *r1 = 1 << argregs[0]; *r2 = 1 << argregs[1]; *tmp = R_TMPS & ~R_PERM; return 0; } if (oc & O_BOP) { *rd = R_TMPS; *r1 = R_TMPS; *r2 = op & O_NUM ? 0 : R_TMPS; return 0; } if (oc & O_UOP) { *rd = R_TMPS; *r1 = op & O_NUM ? 0 : R_TMPS; return 0; } if (oc == O_MSET || oc == O_MCPY) { *r1 = 1 << 4; *r2 = 1 << 5; *r3 = 1 << 6; *tmp = (1 << 4) | (1 << 6) | (oc == O_MCPY ? (1 << 5) : 0); return 0; } if (oc == O_RET) { *r1 = (1 << REG_RET); return 0; } if (oc & O_CALL) { *rd = (1 << REG_RET); *r1 = oc & O_SYM ? 0 : R_TMPS; *tmp = R_TMPS & ~R_PERM; return 0; } if (oc & O_LD) { *rd = R_TMPS; *r1 = R_TMPS; *r2 = oc & O_NUM ? 0 : R_TMPS; return 0; } if (oc & O_ST) { *r1 = R_TMPS; *r2 = R_TMPS; *r3 = oc & O_NUM ? 0 : R_TMPS; return 0; } if (oc & O_JZ) { *r1 = R_TMPS; return 0; } if (oc & O_JCC) { *r1 = R_TMPS; *r2 = oc & O_NUM ? 0 : R_TMPS; return 0; } if (oc == O_JMP) return 0; return 1; } long i_ins(long op, long rd, long r1, long r2, long r3) { long oc = O_C(op); long bt = O_T(op); if (op & O_ADD) { if (op & O_NUM) { if (i_imm(0, r2)) i_add_imm(op, rd, r1, r2); else i_add_anyimm(rd, r1, r2); } else { i_add(op, rd, r1, r2); } } if (op & O_SHL) { if (op & O_NUM) i_shl_imm(op, rd, r1, r2); else i_shl(op, rd, r1, r2); } if (op & O_MUL) { if (oc == O_MUL) i_mul(rd, r1, r2); if (oc == O_DIV) i_div(O_T(op) & T_MSIGN ? "__divdi3" : "__udivdi3"); if (oc == O_MOD) i_div(O_T(op) & T_MSIGN ? "__moddi3" : "__umoddi3"); return 0; } if (oc & O_CMP) { if (op & O_NUM) i_cmp_imm(r1, r2); else i_cmp(r1, r2); i_set(op, rd); return 0; } if (oc & O_UOP) { if (oc == O_NEG) i_neg(rd, r1); if (oc == O_NOT) i_not(rd, r1); if (oc == O_LNOT) i_lnot(rd, r1); return 0; } if (oc == O_CALL) { func_call = 1; i_call_reg(r1); return 0; } if (oc == (O_CALL | O_SYM)) { func_call = 1; i_call(r1, r2); return 0; } if (oc == (O_MOV | O_SYM)) { i_sym(rd, r1, r2); return 0; } if (oc == (O_MOV | O_NUM)) { i_num(rd, r1); return 0; } if (oc == O_MSET) { i_memset(r1, r2, r3); return 0; } if (oc == O_MCPY) { i_memcpy(r1, r2, r3); return 0; } if (oc == O_RET) { jmp_ret = opos(); jmp_add(i_jmp(O_JMP, 0, 0), 0); return 0; } if (oc == (O_LD | O_NUM)) { i_ldr(1, rd, r1, r2, bt); return 0; } if (oc == (O_ST | O_NUM)) { i_ldr(0, r1, r2, r3, bt); return 0; } if (oc == O_MOV) { if (T_SZ(bt) == LONGSZ) i_mov(rd, r1); else { if (bt & T_MSIGN) i_sx(rd, r1, T_SZ(bt) * 8); else i_zx(rd, r1, T_SZ(bt) * 8); } return 0; } if (oc & O_JXX) { jmp_add(i_jmp(op, r1, r2), r3 + 1); return 0; } return 1; } void i_wrap(int argc, long sargs, long spsub, int initfp, long sregs, long sregs_pos) { long body_n; void *body; long diff; /* prologue length */ long dpadd; int nsargs = 0; /* number of saved arguments */ int initdp = num_n > 0; /* initialize data pointer */ long pregs = 1; /* registers saved in function prologue */ int i; if (func_call) initfp = 1; if (!initfp && !spsub && !initdp && !sargs && argc < N_ARGS) pregs = 0; initfp = initfp || pregs; /* removing the last jmp to the epilogue */ if (jmp_ret + 4 == opos()) { mem_cut(&cs, jmp_ret); jmp_n--; } lab_add(0); /* the return label */ body_n = mem_len(&cs); body = mem_get(&cs); /* generating function prologue */ for (i = 0; i < N_ARGS; i++) if ((1 << argregs[i]) & sargs) nsargs++; if (nsargs & 0x1) { /* keeping stack 8-aligned */ for (i = 0; i < N_ARGS; i++) if (!((1 << argregs[i]) & sargs)) break; sargs |= 1 << argregs[i]; } if (sargs) oi4(0xe92d0000 | sargs); /* stmfd sp!, {r0-r3} */ if (pregs) { oi4(0xe1a0c00d); /* mov r12, sp */ oi4(0xe92d5c00); /* stmfd sp!, {sl, fp, ip, lr} */ } if (initfp) oi4(0xe1a0b00d); /* mov fp, sp */ if (sregs) { /* sregs_pos should be encoded as immediate */ int npos = add_decimm(add_rndimm(add_encimm(-sregs_pos))); spsub += npos + sregs_pos; sregs_pos = -npos; } if (spsub) { /* sub sp, sp, xx */ spsub = ALIGN(spsub, 8); spsub = add_decimm(add_rndimm(add_encimm(spsub))); oi4(0xe24dd000 | add_encimm(spsub)); } if (initdp) { dpadd = opos(); oi4(0xe28fa000); /* add dp, pc, xx */ } if (sregs) { /* saving registers */ oi4(0xe24bc000 | add_encimm(-sregs_pos)); oi4(0xe88c0000 | sregs); /* stmea ip, {r4-r9} */ } diff = mem_len(&cs); mem_put(&cs, body, body_n); free(body); /* generating function epilogue */ if (sregs) { /* restoring saved registers */ oi4(0xe24bc000 | add_encimm(-sregs_pos)); oi4(0xe89c0000 | sregs); /* ldmfd ip, {r4-r9} */ } if (pregs) { oi4(0xe89bac00); /* ldmfd fp, {sl, fp, sp, pc} */ } else { oi4(0xe1a0f00e); /* mov pc, lr */ } /* adjusting code offsets */ for (i = 0; i < rel_n; i++) rel_off[i] += diff; for (i = 0; i < jmp_n; i++) jmp_off[i] += diff; for (i = 0; i < lab_sz; i++) lab_loc[i] += diff; /* writing the data pool */ if (initdp) { int dpoff = opos() - dpadd - 8; dpoff = add_decimm(add_rndimm(add_encimm(dpoff))); mem_putz(&cs, dpadd + dpoff + 8 - opos()); /* fill data ptr addition: dp = pc + xx */ oi_at(dpadd, 0xe28fa000 | add_encimm(dpoff), 4); } pool_write(); } static void i_fill(long src, long dst) { long *d = mem_buf(&cs) + src; long c = (*d & 0xff000000) | (((dst - src - 8) >> 2) & 0x00ffffff); oi_at(src, c, 4); } void i_code(char **c, long *c_len, long **rsym, long **rflg, long **roff, long *rcnt) { int i; for (i = 0; i < jmp_n; i++) /* filling jmp destinations */ i_fill(jmp_off[i], lab_loc[jmp_dst[i]]); *c_len = mem_len(&cs); *c = mem_get(&cs); *rsym = rel_sym; *rflg = rel_flg; *roff = rel_off; *rcnt = rel_n; rel_sym = NULL; rel_flg = NULL; rel_off = NULL; rel_n = 0; rel_sz = 0; jmp_n = 0; num_n = 0; func_call = 0; } void i_done(void) { if (putdiv) { o_code("__udivdi3", (void *) udivdi3, sizeof(udivdi3)); o_code("__umoddi3", (void *) umoddi3, sizeof(umoddi3)); o_code("__divdi3", (void *) divdi3, sizeof(divdi3)); o_code("__moddi3", (void *) moddi3, sizeof(moddi3)); } free(jmp_off); free(jmp_dst); free(lab_loc); free(num_sym); free(num_off); } ================================================ FILE: arm.h ================================================ /* architecture-dependent header for ARM */ #define LONGSZ 4 /* word size */ #define I_ARCH "__arm__" #define N_REGS 16 /* number of registers */ #define N_TMPS 10 /* number of tmp registers */ #define N_ARGS 4 /* number of arg registers */ #define R_TMPS 0x03ff /* mask of tmp registers */ #define R_ARGS 0x000f /* mask of arg registers */ #define R_PERM 0x0ff0 /* mask of callee-saved registers */ /* special registers */ #define REG_FP 11 /* frame pointer register */ #define REG_SP 13 /* stack pointer register */ /* stack positions */ #define I_ARG0 (-16) /* offset of the first argument from FP */ #define I_LOC0 0 /* offset of the first local from FP */ ================================================ FILE: cpp.c ================================================ /* neatcc preprocessor */ #include #include #include #include #include #include #include #include #include #include #include "ncc.h" static char *buf; static long len; static long cur; static struct macro { char name[NAMELEN]; /* macro name */ char def[MDEFLEN]; /* macro definition */ char args[NARGS][NAMELEN]; int nargs; /* number of arguments */ int isfunc; /* macro is a function */ int undef; /* macro is removed */ } macros[NDEFS]; static int mcount = 1; /* number of macros */ static int mhead[256]; /* macro hash table heads */ static int mnext[NDEFS]; /* macro hash table next entries */ #define BUF_FILE 0 #define BUF_MACRO 1 #define BUF_ARG 2 #define BUF_EVAL 3 #define BUF_TEMP 4 /* preprocessing input buffers for files, macros and macro arguments */ static struct buf { char *buf; long len; long cur; int type; /* for BUF_FILE */ char path[NAMELEN]; /* for BUF_MACRO */ struct macro *macro; char args[NARGS][MARGLEN]; /* arguments passed to a macro */ /* for BUF_ARG */ int arg_buf; /* the bufs index of the owning macro */ } bufs[NBUFS]; static int bufs_n; static int bufs_limit = 0; /* cpp_read() limit; useful in cpp_eval() */ void die(char *fmt, ...) { va_list ap; char msg[512]; va_start(ap, fmt); vsprintf(msg, fmt, ap); va_end(ap); write(2, msg, strlen(msg)); exit(1); } static void buf_new(int type, char *dat, long dlen) { if (bufs_n) { bufs[bufs_n - 1].buf = buf; bufs[bufs_n - 1].cur = cur; bufs[bufs_n - 1].len = len; } if (bufs_n >= NBUFS) die("nomem: NBUFS reached!\n"); bufs_n++; cur = 0; buf = dat; len = dlen; bufs[bufs_n - 1].type = type; } static void buf_file(char *path, char *dat, int dlen) { buf_new(BUF_FILE, dat, dlen); strcpy(bufs[bufs_n - 1].path, path ? path : ""); } static int macro_arg(struct macro *m, char *arg); static void buf_macro(struct macro *m) { struct mem mem; char *s = m->def; char arg[NAMELEN]; int len; int quote = 0; mem_init(&mem); while (*s) { int numsign = 0; if (quote && s[0] == quote) quote = 0; else if (!quote && s[0] == '"') quote = s[0]; else if (!quote && s[0] == '\'') quote = s[0]; if (!quote && s[0] == '#') numsign = s[1] == '#' ? 2 : 1; if (numsign && s[numsign]) { struct buf *mbuf = &bufs[bufs_n]; char *r = s + numsign; char *d = arg; while (*r && d - arg < sizeof(arg) - 1 && (isalnum((unsigned char) *r) || *r == '_')) *d++ = *r++; *d++ = '\0'; if (macro_arg(m, arg) >= 0) { char *def = mbuf->args[macro_arg(m, arg)]; if (def && numsign == 1) { mem_putc(&mem, '\"'); while (*def) { if (*def == '\"') mem_putc(&mem, '\\'); mem_putc(&mem, (unsigned char) *def++); } mem_putc(&mem, '\"'); s = r; continue; } if (def && numsign == 2) { while (*def) mem_putc(&mem, (unsigned char) *def++); s = r; continue; } } } if (quote && s[0] == '\\') mem_putc(&mem, (unsigned char) *s++); if (s[0]) mem_putc(&mem, (unsigned char) *s++); } len = mem_len(&mem); buf_new(BUF_MACRO, mem_get(&mem), len); mem_done(&mem); bufs[bufs_n - 1].macro = m; } static void buf_arg(char *arg, int mbuf) { buf_new(BUF_ARG, arg, strlen(arg)); bufs[bufs_n - 1].arg_buf = mbuf; } static void buf_pop(void) { bufs_n--; if (bufs[bufs_n].type == BUF_FILE || bufs[bufs_n].type == BUF_MACRO) free(buf); if (bufs_n) { cur = bufs[bufs_n - 1].cur; len = bufs[bufs_n - 1].len; buf = bufs[bufs_n - 1].buf; } } static int buf_iseval(void) { int i; for (i = bufs_n - 1; i >= 0; i--) if (bufs[i].type == BUF_EVAL) return 1; return 0; } static size_t file_size(int fd) { struct stat st; if (!fstat(fd, &st)) return st.st_size; return 0; } static int include_file(char *path) { int fd = open(path, O_RDONLY); int n = 0, nr = 0; char *dat; int size; if (fd == -1) return -1; size = file_size(fd) + 1; dat = malloc(size); while ((n = read(fd, dat + nr, size - nr)) > 0) nr += n; close(fd); dat[nr] = '\0'; buf_file(path, dat, nr); return 0; } int cpp_init(char *path) { return include_file(path); } static int jumpws(void) { int old = cur; while (cur < len && isspace(buf[cur])) cur++; return cur == old; } static void read_word(char *dst) { jumpws(); while (cur < len && (isalnum(buf[cur]) || buf[cur] == '_')) *dst++ = buf[cur++]; *dst = '\0'; } static int jumpcomment(void) { if (buf[cur] == '/' && buf[cur + 1] == '*') { while (++cur < len) { if (buf[cur] == '*' && buf[cur + 1] == '/') { cur += 2; return 0; } } } if (buf[cur] == '/' && buf[cur + 1] == '/') { while (++cur < len && buf[cur] != '\n') if (buf[cur] == '\\') cur++; return 0; } return 1; } static int jumpstr(void) { if (buf[cur] == '\'') { while (++cur < len && buf[cur] != '\'') if (buf[cur] == '\\') cur++; cur++; return 0; } if (buf[cur] == '"') { while (++cur < len && buf[cur] != '"') if (buf[cur] == '\\') cur++; cur++; return 0; } return 1; } static void read_tilleol(char *dst) { while (cur < len && isspace(buf[cur]) && buf[cur] != '\n') cur++; while (cur < len && buf[cur] != '\n') { int last = cur; if (buf[cur] == '\\' && buf[cur + 1] == '\n') { cur += 2; continue; } if (!jumpstr()) { memcpy(dst, buf + last, cur - last); dst += cur - last; continue; } if (!jumpcomment()) continue; *dst++ = buf[cur++]; } *dst = '\0'; } static char *locs[NLOCS] = {}; static int nlocs = 0; /* header directory */ void cpp_path(char *s) { locs[nlocs++] = s; } static int include_find(char *name, int std) { int i; for (i = std ? nlocs - 1 : nlocs; i >= 0; i--) { char path[1 << 10]; if (locs[i]) sprintf(path, "%s/%s", locs[i], name); else strcpy(path, name); if (!include_file(path)) return 0; } return -1; } static void readarg(char *s) { int depth = 0; int beg = cur; while (cur < len && (depth || (buf[cur] != ',' && buf[cur] != ')'))) { if (!jumpstr() || !jumpcomment()) continue; switch (buf[cur++]) { case '(': case '[': case '{': depth++; break; case ')': case ']': case '}': depth--; break; } } if (s) { memcpy(s, buf + beg, cur - beg); s[cur - beg] = '\0'; } } /* find a macro; if undef is nonzero, search #undef-ed macros too */ static int macro_find(char *name, int undef) { int i = mhead[(unsigned char) name[0]]; while (i > 0) { if (!strcmp(name, macros[i].name)) if (!macros[i].undef || undef) return i; i = mnext[i]; } return -1; } static void macro_undef(char *name) { int i = macro_find(name, 0); if (i >= 0) macros[i].undef = 1; } static int macro_new(char *name) { int i = macro_find(name, 1); if (i >= 0) return i; if (mcount >= NDEFS) die("nomem: NDEFS reached!\n"); i = mcount++; strcpy(macros[i].name, name); mnext[i] = mhead[(unsigned char) name[0]]; mhead[(unsigned char) name[0]] = i; return i; } static void macro_define(void) { char name[NAMELEN]; struct macro *d; read_word(name); d = ¯os[macro_new(name)]; d->isfunc = 0; d->nargs = 0; d->undef = 0; if (buf[cur] == '(') { cur++; jumpws(); while (cur < len && buf[cur] != ')') { readarg(d->args[d->nargs++]); jumpws(); if (buf[cur] != ',') break; cur++; jumpws(); } cur++; d->isfunc = 1; } read_tilleol(d->def); } static char ebuf[MARGLEN]; static int elen; static int ecur; static long evalexpr(void); static long cpp_eval(void) { char evalbuf[MARGLEN]; int old_limit; long ret, clen; char *cbuf; read_tilleol(evalbuf); buf_new(BUF_EVAL, evalbuf, strlen(evalbuf)); elen = 0; ecur = 0; old_limit = bufs_limit; bufs_limit = bufs_n; while (!cpp_read(&cbuf, &clen)) { memcpy(ebuf + elen, cbuf, clen); elen += clen; } bufs_limit = old_limit; ret = evalexpr(); buf_pop(); return ret; } static void jumpifs(int jumpelse) { int depth = 0; while (cur < len) { if (buf[cur] == '#') { char cmd[NAMELEN]; cur++; read_word(cmd); if (!strcmp("else", cmd)) if (!depth && !jumpelse) break; if (!strcmp("elif", cmd)) if (!depth && !jumpelse && cpp_eval()) break; if (!strcmp("endif", cmd)) { if (!depth) break; else depth--; } if (!strcmp("ifdef", cmd) || !strcmp("ifndef", cmd) || !strcmp("if", cmd)) depth++; continue; } if (!jumpcomment()) continue; if (!jumpstr()) continue; cur++; } } static int cpp_cmd(void) { char cmd[NAMELEN]; cur++; read_word(cmd); if (!strcmp("define", cmd)) { macro_define(); return 0; } if (!strcmp("undef", cmd)) { char name[NAMELEN]; read_word(name); macro_undef(name); return 0; } if (!strcmp("ifdef", cmd) || !strcmp("ifndef", cmd) || !strcmp("if", cmd)) { char name[NAMELEN]; int matched = 0; if (cmd[2]) { int not = cmd[2] == 'n'; read_word(name); matched = not ? macro_find(name, 0) < 0 : macro_find(name, 0) >= 0; } else { matched = cpp_eval(); } if (!matched) jumpifs(0); return 0; } if (!strcmp("else", cmd) || !strcmp("elif", cmd)) { jumpifs(1); return 0; } if (!strcmp("endif", cmd)) return 0; if (!strcmp("include", cmd)) { char file[NAMELEN]; char *s, *e; jumpws(); s = buf + cur + 1; e = strchr(buf + cur + 1, buf[cur] == '"' ? '"' : '>'); memcpy(file, s, e - s); file[e - s] = '\0'; cur += e - s + 2; if (include_find(file, *e == '>') == -1) err("cannot include <%s>\n", file); return 0; } err("unknown directive <%s>\n", cmd); return 1; } static int macro_arg(struct macro *m, char *arg) { int i; for (i = 0; i < m->nargs; i++) if (!strcmp(arg, m->args[i])) return i; return -1; } static int buf_arg_find(char *name) { int i; for (i = bufs_n - 1; i >= 0; i--) { struct buf *mbuf = &bufs[i]; struct macro *m = mbuf->macro; if (mbuf->type == BUF_MACRO && macro_arg(m, name) >= 0) return i; if (mbuf->type == BUF_ARG) i = mbuf->arg_buf; } return -1; } static void macro_expand(char *name) { struct macro *m; int mbuf; if ((mbuf = buf_arg_find(name)) >= 0) { int arg = macro_arg(bufs[mbuf].macro, name); char *dat = bufs[mbuf].args[arg]; buf_arg(dat, mbuf); return; } m = ¯os[macro_find(name, 0)]; if (!m->isfunc) { buf_macro(m); return; } jumpws(); if (buf[cur] == '(') { int i = 0; struct buf *mbuf = &bufs[bufs_n]; cur++; jumpws(); while (cur < len && buf[cur] != ')') { readarg(mbuf->args[i++]); jumpws(); if (buf[cur] != ',') break; cur++; jumpws(); } while (i < m->nargs) mbuf->args[i++][0] = '\0'; cur++; buf_macro(m); } } static int buf_expanding(char *macro) { int i; for (i = bufs_n - 1; i >= 0; i--) { if (bufs[i].type == BUF_ARG) return 0; if (bufs[i].type == BUF_MACRO && !strcmp(macro, bufs[i].macro->name)) return 1; } return 0; } /* return 1 for plain macros and arguments and 2 for function macros */ static int expandable(char *word) { int i; if (buf_arg_find(word) >= 0) return 1; if (buf_expanding(word)) return 0; i = macro_find(word, 0); return i >= 0 ? macros[i].isfunc + 1 : 0; } void cpp_define(char *name, char *def) { char tmp_buf[MDEFLEN]; sprintf(tmp_buf, "%s\t%s", name, def); buf_new(BUF_TEMP, tmp_buf, strlen(tmp_buf)); macro_define(); buf_pop(); } static int seen_macro; /* seen a macro; 2 if a function macro */ static char seen_name[NAMELEN]; /* the name of the last macro */ static int hunk_off; static int hunk_len; int cpp_read(char **obuf, long *olen) { int old, end; int jump_name = 0; *olen = 0; *obuf = ""; if (seen_macro == 1) { macro_expand(seen_name); seen_macro = 0; } if (cur == len) { if (bufs_n < bufs_limit + 1) return 1; buf_pop(); } old = cur; if (cur < len && buf[cur] == '#') if (!cpp_cmd()) return 0; while (cur < len) { if (!jumpws()) continue; if (buf[cur] == '#') break; if (!jumpcomment()) continue; if (seen_macro == 2) { if (buf[cur] == '(') macro_expand(seen_name); seen_macro = 0; old = cur; continue; } if (!jumpstr()) continue; if (isalnum(buf[cur]) || buf[cur] == '_') { char word[NAMELEN]; read_word(word); seen_macro = expandable(word); if (seen_macro) { strcpy(seen_name, word); jump_name = 1; break; } if (buf_iseval() && !strcmp("defined", word)) { int parens = 0; jumpws(); if (buf[cur] == '(') { parens = 1; cur++; } read_word(word); if (parens) { jumpws(); cur++; } } continue; } cur++; } /* macros are expanded later; ignoring their names */ end = jump_name ? cur - strlen(seen_name) : cur; if (!buf_iseval()) { hunk_off += hunk_len; hunk_len = end - old; } *obuf = buf + old; *olen = end - old; return 0; } /* preprocessor constant expression evaluation */ #define TOK2(a) ((a)[0] << 16 | (a)[1] << 8) #define TOK_NAME 256 #define TOK_NUM 257 #define TOK_EOF -1 static char etok[NAMELEN]; static int enext; static char *tok2[] = { "<<", ">>", "&&", "||", "==", "!=", "<=", ">=" }; static int eval_tok(void) { char *s = etok; int i; while (ecur < elen) { while (ecur < elen && isspace(ebuf[ecur])) ecur++; if (ebuf[ecur] == '/' && ebuf[ecur + 1] == '*') { while (ecur < elen && (ebuf[ecur - 2] != '*' || ebuf[ecur - 1] != '/')) ecur++; continue; } break; } if (ecur >= elen) return TOK_EOF; if (isalpha(ebuf[ecur]) || ebuf[ecur] == '_') { while (isalnum(ebuf[ecur]) || ebuf[ecur] == '_') *s++ = ebuf[ecur++]; *s = '\0'; return TOK_NAME; } if (isdigit(ebuf[ecur])) { while (isdigit(ebuf[ecur])) *s++ = ebuf[ecur++]; while (tolower(ebuf[ecur]) == 'u' || tolower(ebuf[ecur]) == 'l') ecur++; *s = '\0'; return TOK_NUM; } for (i = 0; i < LEN(tok2); i++) if (TOK2(tok2[i]) == TOK2(ebuf + ecur)) { int ret = TOK2(tok2[i]); ecur += 2; return ret; } return ebuf[ecur++]; } static int eval_see(void) { if (enext == -1) enext = eval_tok(); return enext; } static int eval_get(void) { if (enext != -1) { int ret = enext; enext = -1; return ret; } return eval_tok(); } static long eval_num(void) { return atol(etok); } static int eval_jmp(int tok) { if (eval_see() == tok) { eval_get(); return 0; } return 1; } static void eval_expect(int tok) { eval_jmp(tok); } static char *eval_id(void) { return etok; } static long evalcexpr(void); static long evalatom(void) { if (!eval_jmp(TOK_NUM)) return eval_num(); if (!eval_jmp(TOK_NAME)) { int parens = !eval_jmp('('); long ret; eval_expect(TOK_NAME); ret = macro_find(eval_id(), 0) >= 0; if (parens) eval_expect(')'); return ret; } if (!eval_jmp('(')) { long ret = evalcexpr(); eval_expect(')'); return ret; } return -1; } static long evalpre(void) { if (!eval_jmp('!')) return !evalpre(); if (!eval_jmp('-')) return -evalpre(); if (!eval_jmp('~')) return ~evalpre(); return evalatom(); } static long evalmul(void) { long ret = evalpre(); while (1) { if (!eval_jmp('*')) { ret *= evalpre(); continue; } if (!eval_jmp('/')) { ret /= evalpre(); continue; } if (!eval_jmp('%')) { ret %= evalpre(); continue; } break; } return ret; } static long evaladd(void) { long ret = evalmul(); while (1) { if (!eval_jmp('+')) { ret += evalmul(); continue; } if (!eval_jmp('-')) { ret -= evalmul(); continue; } break; } return ret; } static long evalshift(void) { long ret = evaladd(); while (1) { if (!eval_jmp(TOK2("<<"))) { ret <<= evaladd(); continue; } if (!eval_jmp(TOK2(">>"))) { ret >>= evaladd(); continue; } break; } return ret; } static long evalcmp(void) { long ret = evalshift(); while (1) { if (!eval_jmp('<')) { ret = ret < evalshift(); continue; } if (!eval_jmp('>')) { ret = ret > evalshift(); continue; } if (!eval_jmp(TOK2("<="))) { ret = ret <= evalshift(); continue; } if (!eval_jmp(TOK2(">="))) { ret = ret >= evalshift(); continue; } break; } return ret; } static long evaleq(void) { long ret = evalcmp(); while (1) { if (!eval_jmp(TOK2("=="))) { ret = ret == evalcmp(); continue; } if (!eval_jmp(TOK2("!="))) { ret = ret != evalcmp(); continue; } break; } return ret; } static long evalbitand(void) { long ret = evaleq(); while (!eval_jmp('&')) ret &= evaleq(); return ret; } static long evalxor(void) { long ret = evalbitand(); while (!eval_jmp('^')) ret ^= evalbitand(); return ret; } static long evalbitor(void) { long ret = evalxor(); while (!eval_jmp('|')) ret |= evalxor(); return ret; } static long evaland(void) { long ret = evalbitor(); while (!eval_jmp(TOK2("&&"))) ret = ret && evalbitor(); return ret; } static long evalor(void) { long ret = evaland(); while (!eval_jmp(TOK2("||"))) ret = ret || evaland(); return ret; } static long evalcexpr(void) { long ret = evalor(); if (eval_jmp('?')) return ret; if (ret) return evalor(); while (eval_get() != ':') ; return evalor(); } static long evalexpr(void) { enext = -1; return evalcexpr(); } static int buf_loc(char *s, int off) { char *e = s + off; int n = 1; while ((s = strchr(s, '\n')) && s < e) { n++; s++; } return n; } char *cpp_loc(long addr) { static char loc[256]; int line = -1; int i; for (i = bufs_n - 1; i > 0; i--) if (bufs[i].type == BUF_FILE) break; if (addr >= hunk_off && i == bufs_n - 1) line = buf_loc(buf, (cur - hunk_len) + (addr - hunk_off)); else line = buf_loc(bufs[i].buf, bufs[i].cur); sprintf(loc, "%s:%d", bufs[i].path, line); return loc; } ================================================ FILE: div.s ================================================ @ ARM software division implementation @ These functions are assembled using neatas and are included in gen.c .global __udivdi3 __udivdi3: mov r2, #0 mov r3, #0 @ zero divider tst r1, r1 beq .end @ shift the operand .shl: movs r12, r1, LSL r2 add r2, r2, #1 bpl .shl mov r12, #1 @ the main division algorithm .shr: subs r2, r2, #1 bmi .end cmps r0, r1, LSL r2 bcc .shr sub r0, r0, r1, LSL r2 add r3, r3, r12, LSL r2 b .shr .end: mov r1, r0 mov r0, r3 mov pc, lr .global __umoddi3 __umoddi3: stmfd sp!, {lr} bl __udivdi3 mov r0, r1 ldmfd sp!, {pc} .global __divdi3 __divdi3: stmfd sp!, {r4, r5, lr} mov r4, r0 mov r5, r1 @ handle negative operands tst r0, r0 rsbmi r0, r0, #0 tst r1, r1 rsbmi r1, r1, #0 bl __udivdi3 @ result is negative teq r4, r5 rsbmi r0, r0, #0 tst r4, r4 rsbmi r1, r1, #0 ldmfd sp!, {r4, r5, pc} .global __moddi3 __moddi3: stmfd sp!, {lr} bl __divdi3 mov r0, r1 ldmfd sp!, {pc} ================================================ FILE: gen.c ================================================ /* neatcc code generation */ #include #include #include #include "ncc.h" static struct mem ds; /* data segment */ static struct mem cs; /* code segment */ static long bsslen; /* bss segment size */ static struct ic *ic; /* current instruction stream */ static long ic_n; /* number of instructions in ic[] */ static long ic_i; /* current instruction */ static long *ic_luse; /* last instruction in which values are used */ static long *loc_off; /* offset of locals on the stack */ static long loc_n, loc_sz; /* number of locals */ static long loc_pos; /* current stack position */ static int *loc_mem; /* local was accessed on the stack */ static char (*ds_name)[NAMELEN];/* data section symbols */ static long *ds_off; /* data section offsets */ static long ds_n, ds_sz; /* number of data section symbols */ static int func_argc; /* number of arguments */ static int func_varg; /* varargs */ static int func_regs; /* used registers */ static int func_maxargs; /* the maximum number of arguments on the stack */ static long *ic_bbeg; /* whether each instruction begins a basic block */ static long ra_vmap[N_REGS]; /* register to intermediate value assignments */ static long ra_lmap[N_REGS]; /* register to local assignments */ static long *ra_gmask; /* the mask of good registers for each value */ static long ra_live[NTMPS]; /* live values */ static int ra_vmax; /* the number of values stored on the stack */ static long loc_add(long pos) { if (loc_n >= loc_sz) { loc_sz = MAX(128, loc_sz * 2); loc_off = mextend(loc_off, loc_n, loc_sz, sizeof(loc_off[0])); } loc_off[loc_n] = pos; return loc_n++; } long o_mklocal(long sz) { loc_pos += ALIGN(sz, ULNG); return loc_add(loc_pos); } void o_rmlocal(long addr, long sz) { } long o_arg2loc(int i) { return i; } void o_bsnew(char *name, long size, int global) { out_def(name, OUT_BSS | (global ? OUT_GLOB : 0), bsslen, size); bsslen += ALIGN(size, OUT_ALIGNMENT); } long o_dsnew(char *name, long size, int global) { int idx; if (ds_n >= ds_sz) { ds_sz = MAX(128, ds_sz * 2); ds_name = mextend(ds_name, ds_n, ds_sz, sizeof(ds_name[0])); ds_off = mextend(ds_off, ds_n, ds_sz, sizeof(ds_off[0])); } idx = ds_n++; strcpy(ds_name[idx], name); ds_off[idx] = mem_len(&ds); out_def(name, OUT_DS | (global ? OUT_GLOB : 0), mem_len(&ds), size); mem_putz(&ds, ALIGN(size, OUT_ALIGNMENT)); return ds_off[idx]; } void o_dscpy(long addr, void *buf, long len) { mem_cpy(&ds, addr, buf, len); } static int dat_off(char *name) { int i; for (i = 0; i < ds_n; i++) if (!strcmp(name, ds_name[i])) return ds_off[i]; return 0; } void o_dsset(char *name, long off, long bt) { long sym_off = dat_off(name) + off; long num, roff, rsym; if (!o_popnum(&num)) { mem_cpy(&ds, sym_off, &num, T_SZ(bt)); } else if (!o_popsym(&rsym, &roff)) { out_rel(rsym, OUT_DS, sym_off); mem_cpy(&ds, sym_off, &roff, T_SZ(bt)); } else { err("illegal assignment to static variables\n"); } } static int ra_vreg(int val) { int i; for (i = 0; i < LEN(ra_vmap); i++) if (ra_vmap[i] == val) return i; return -1; } static int ra_lreg(int loc) { int i; for (i = 0; i < LEN(ra_lmap); i++) if (ra_lmap[i] == loc) return i; return -1; } /* mask of registers assigned to locals */ static long ra_lmask(void) { long m = 0; int i; for (i = 0; i < LEN(ra_lmap); i++) if (ra_lmap[i] >= 0) m |= (1 << i); return m; } /* mask of registers assigned to values */ static long ra_vmask(void) { long m = 0; int i; for (i = 0; i < LEN(ra_vmap); i++) if (ra_vmap[i] >= 0) m |= (1 << i); return m; } /* find a temporary register specified in the given mask */ static long ra_regscn(long mask) { int i; for (i = 0; i < N_TMPS; i++) if ((1 << tmpregs[i]) & mask) return tmpregs[i]; return -1; } /* find a register, with the given good, acceptable, and bad register masks */ static long ra_regget(long iv, long gmask, long amask, long bmask) { long lmask, vmask; gmask &= ~bmask & amask; amask &= ~bmask; if (ra_vreg(iv) >= 0 && (1 << ra_vreg(iv)) & (gmask | amask)) return ra_vreg(iv); vmask = ra_vmask(); lmask = ra_lmask(); if (ra_regscn(gmask & ~vmask & ~lmask) >= 0) return ra_regscn(gmask & ~vmask & ~lmask); if (ra_regscn(amask & ~vmask & ~lmask) >= 0) return ra_regscn(amask & ~vmask & ~lmask); if (ra_regscn(gmask) >= 0) return ra_regscn(gmask); if (ra_regscn(amask) >= 0) return ra_regscn(amask); die("neatcc: cannot allocate an acceptable register\n"); return 0; } /* find a free and cheap register */ static long ra_regcheap(long mask) { return ra_regscn(mask & (func_regs | (R_TMPS & ~R_PERM)) & ~ra_lmask() & ~ra_vmask()); } /* allocate registers for the current instruction */ static void ra_map(int *rd, int *r1, int *r2, int *r3, long *mt) { long md, m1, m2, m3; struct ic *c = &ic[ic_i]; long all = 0; int n = ic_regcnt(c); int oc = O_C(c->op); int i; *rd = -1; *r1 = -1; *r2 = -1; *r3 = -1; *mt = 0; /* optimizing loading locals: point to local's register */ if (oc == (O_LD | O_LOC) && ra_lreg(c->a1) >= 0 && ra_vmap[ra_lreg(c->a1)] < 0) { *rd = ra_lreg(c->a1); func_regs |= 1 << *rd; return; } /* do not use argument registers to hold call destination */ if (oc & O_CALL) for (i = 0; i < MIN(c->a3, N_ARGS); i++) all |= (1 << argregs[i]); /* instructions on locals can be simplified */ if (oc & O_LOC) { if (oc & O_MOV) oc = O_ADD | O_NUM; if (oc & (O_ST | O_LD)) oc = (oc & ~O_LOC) & O_NUM; } if (i_reg(c->op, &md, &m1, &m2, &m3, mt)) die("neatcc: instruction %08lx not supported\n", c->op); /* * the registers used in global register allocation should not * be used in the last instruction of a basic block. */ if (c->op & (O_JZ | O_JCC)) for (i = 0; i < LEN(ra_lmap); i++) if (reg_rmap(ic_i, i) >= 0 && ra_lmap[i] != reg_rmap(ic_i, i)) all |= (1 << i); /* allocating registers for the operands */ if (n >= 2) { *r2 = ra_regget(c->a2, m2, m2, all); all |= (1 << *r2); } if (n >= 1) { *r1 = ra_regget(c->a1, m1, m1, all); all |= (1 << *r1); } if (n >= 3) { *r3 = ra_regget(c->a3, m3, m3, all); all |= (1 << *r3); } if (c->op & O_OUT) { long m4 = (md ? md : m1) & ~all; long a4 = md ? ic_i : c->a1; if (n >= 1 && !md) *rd = *r1; else if (ra_gmask[ic_i] & md & ~all) *rd = ra_regget(ic_i, ra_gmask[ic_i], md, 0); else if (n >= 2 && md & (1 << *r2) && ic_luse[*r2] <= ic_i) *rd = *r2; else if (n >= 1 && md & (1 << *r1) && ic_luse[*r1] <= ic_i) *rd = *r1; else *rd = ra_regget(ic_i, ra_gmask[ic_i], md, 0); /* if overwriting a local, use another register */ if (ra_lmap[*rd] >= 0) if (m4 & ~(1 << *rd)) *rd = ra_regget(a4, ra_gmask[ic_i], m4 & ~(1 << *rd), 0); if (n >= 1 && !md) *r1 = *rd; all |= (1 << *rd); } func_regs |= all | *mt; } static long iv_rank(long iv) { int i; for (i = 0; i < LEN(ra_live); i++) if (ra_live[i] == iv) return i; die("neatcc: the specified value is not live\n"); return 0; } static long iv_addr(long rank) { return loc_pos + rank * ULNG + ULNG; } static void loc_toreg(long loc, long off, int reg, int bt) { loc_mem[loc]++; i_ins(O_MK(O_LD | O_NUM, bt), reg, REG_FP, -loc_off[loc] + off, 0); } static void loc_tomem(long loc, long off, int reg, int bt) { loc_mem[loc]++; i_ins(O_MK(O_ST | O_NUM, bt), 0, reg, REG_FP, -loc_off[loc] + off); } static void loc_toadd(long loc, long off, int reg) { loc_mem[loc]++; i_ins(O_ADD | O_NUM, reg, REG_FP, -loc_off[loc] + off, 0); } /* return nonzero if the local is read at least once in this basic block */ static int loc_isread(long loc) { int i; for (i = ic_i + 1; i < ic_n && !ic_bbeg[i]; i++) if (ic[i].op & O_LOC) return ic[i].op & O_LD; return 0; } static void val_toreg(long val, int reg) { i_ins(O_MK(O_LD | O_NUM, ULNG), reg, REG_FP, -iv_addr(iv_rank(val)), 0); } static void val_tomem(long val, int reg) { long rank = iv_rank(val); ra_vmax = MAX(ra_vmax, rank + 1); i_ins(O_MK(O_ST | O_NUM, ULNG), 0, reg, REG_FP, -iv_addr(rank)); } /* move the value to the stack */ static void ra_spill(int reg) { if (ra_vmap[reg] >= 0) { val_tomem(ra_vmap[reg], reg); ra_vmap[reg] = -1; } if (ra_lmap[reg] >= 0) { if (ra_lmap[reg] == reg_rmap(ic_i, reg)) loc_tomem(ra_lmap[reg], 0, reg, ULNG); ra_lmap[reg] = -1; } } /* set the value to the given register */ static void ra_vsave(long iv, int reg) { int i; ra_vmap[reg] = iv; for (i = 0; i < LEN(ra_live); i++) if (ra_live[i] < 0) break; if (i == LEN(ra_live)) die("neatcc: too many live values\n"); ra_live[i] = iv; } /* load the value into a register */ static void ra_vload(long iv, int reg) { if (ra_vmap[reg] == iv) return; if (ra_vmap[reg] >= 0 || ra_lmap[reg] >= 0) ra_spill(reg); if (ra_vreg(iv) >= 0) { i_ins(O_MK(O_MOV, ULNG), reg, ra_vreg(iv), 0, 0); ra_vmap[ra_vreg(iv)] = -1; } else { val_toreg(iv, reg); } ra_vmap[reg] = iv; } /* the value is no longer needed */ static void ra_vdrop(long iv) { int i; for (i = 0; i < LEN(ra_live); i++) if (ra_live[i] == iv) ra_live[i] = -1; if (ra_vreg(iv) >= 0) ra_vmap[ra_vreg(iv)] = -1; } /* move the given value to memory or a free register */ static void ra_vmove(long iv, long mask) { int src = ra_vreg(iv); int dst = ra_regcheap(mask); if (dst >= 0 && ra_vmap[dst] < 0 && ra_lmap[dst] < 0) { i_ins(O_MK(O_MOV, ULNG), dst, src, 0, 0); ra_vmap[dst] = iv; } else { val_tomem(iv, src); } ra_vmap[src] = -1; } /* load the value of local loc into register reg */ static void ra_lload(long loc, long off, int reg, int bt) { int greg = reg_lmap(ic_i, loc); if (greg >= 0) { int lreg = ra_lreg(loc); /* values using the same register */ if (ra_vmap[greg] >= 0) ra_vmove(ra_vmap[greg], ra_gmask[ra_vmap[greg]] & ~(1 << reg)); if (lreg >= 0) { ra_lmap[lreg] = -1; if (lreg != greg) i_ins(O_MK(O_MOV, bt), greg, lreg, 0, 0); } else { loc_toreg(loc, off, greg, bt); } ra_lmap[greg] = loc; } if (ra_lreg(loc) < 0 && reg_safe(loc) && loc_isread(loc)) { ra_lmap[reg] = loc; loc_toreg(loc, off, reg, bt); } if (ra_lreg(loc) >= 0) { if (ra_lreg(loc) != reg) i_ins(O_MK(O_MOV, bt), reg, ra_lreg(loc), 0, 0); } else { loc_toreg(loc, off, reg, bt); } } /* register reg contains the value of local loc */ static void ra_lsave(long loc, long off, int reg, int bt) { int lreg = ra_lreg(loc); int greg = reg_lmap(ic_i, loc); if (greg >= 0) { if (lreg >= 0 && lreg != greg) ra_lmap[lreg] = -1; if (ra_vmap[greg] >= 0) /* values using the same register */ ra_vmove(ra_vmap[greg], ra_gmask[ra_vmap[greg]] & ~(1 << reg)); i_ins(O_MK(O_MOV, bt), greg, reg, 0, 0); ra_lmap[greg] = loc; } else { if (lreg >= 0) ra_lmap[lreg] = -1; loc_tomem(loc, off, reg, bt); if (ra_lmap[reg] < 0 && reg_safe(loc) && loc_isread(loc)) ra_lmap[reg] = loc; } } /* end of a basic block */ static void ra_bbend(void) { int i; /* save values to memory */ for (i = 0; i < LEN(ra_vmap); i++) if (ra_vmap[i] >= 0) ra_spill(i); /* dropping local caches */ for (i = 0; i < LEN(ra_lmap); i++) if (ra_lmap[i] != reg_rmap(ic_i, i) && ra_lmap[i] >= 0) ra_spill(i); /* load global register allocations from memory */ for (i = 0; i < LEN(ra_lmap); i++) { if (ra_lmap[i] != reg_rmap(ic_i, i)) { ra_lmap[i] = reg_rmap(ic_i, i); loc_toreg(ra_lmap[i], 0, i, ULNG); } } } static void ra_init(struct ic *ic, long ic_n) { long md, m1, m2, m3, mt; int i, j; ic_bbeg = calloc(ic_n, sizeof(ic_bbeg[0])); ra_gmask = calloc(ic_n, sizeof(ra_gmask[0])); loc_mem = calloc(loc_n, sizeof(loc_mem[0])); /* ic_bbeg */ for (i = 0; i < ic_n; i++) { if (i + 1 < ic_n && ic[i].op & (O_JXX | O_RET)) ic_bbeg[i + 1] = 1; if (ic[i].op & O_JXX && ic[i].a3 < ic_n) ic_bbeg[ic[i].a3] = 1; } /* ra_gmask */ for (i = 0; i < ic_n; i++) { int n = ic_regcnt(ic + i); int op = ic[i].op; i_reg(op, &md, &m1, &m2, &m3, &mt); if (n >= 1) ra_gmask[ic[i].a1] = m1; if (n >= 2) ra_gmask[ic[i].a2] = m2; if (n >= 3) ra_gmask[ic[i].a3] = m3; if (O_C(op) == (O_ST | O_LOC) && reg_lmap(i, ic[i].a2)) ra_gmask[ic[i].a1] = 1 << reg_lmap(i, ic[i].a2); if (op & O_CALL) for (j = 0; j < MIN(N_ARGS, ic[i].a3); j++) ra_gmask[ic[i].args[j]] = 1 << argregs[j]; } /* ra_vmap */ for (i = 0; i < LEN(ra_vmap); i++) ra_vmap[i] = -1; /* ra_lmap */ for (i = 0; i < LEN(ra_lmap); i++) ra_lmap[i] = reg_rmap(0, i); func_regs |= reg_mask(); /* ra_live */ for (i = 0; i < LEN(ra_live); i++) ra_live[i] = -1; ra_vmax = 0; func_maxargs = 0; } static void ra_done(void) { free(ic_bbeg); free(ra_gmask); free(loc_mem); } static void ic_gencode(struct ic *ic, long ic_n) { int rd, r1, r2, r3; long mt; int i, j; /* loading arguments in their allocated registers */ for (i = 0; i < LEN(ra_lmap); i++) { int loc = ra_lmap[i]; if (loc >= 0 && loc < func_argc) if (loc >= N_ARGS || i != argregs[loc]) loc_toreg(loc, 0, i, ULNG); } /* generating code */ for (i = 0; i < ic_n; i++) { long op = ic[i].op; long oc = O_C(op); int n = ic_regcnt(ic + i); ic_i = i; i_label(i); ra_map(&rd, &r1, &r2, &r3, &mt); if (oc & O_CALL) { int argc = ic[i].a3; int aregs = MIN(N_ARGS, argc); /* arguments passed via stack */ for (j = argc - 1; j >= aregs; --j) { int v = ic[i].args[j]; int rx = ra_vreg(v) >= 0 ? ra_vreg(v) : rd; ra_vload(v, rx); i_ins(O_MK(O_ST | O_NUM, ULNG), 0, rx, REG_SP, (j - aregs) * ULNG); ra_vdrop(v); } func_maxargs = MAX(func_maxargs, argc - aregs); /* arguments passed via registers */ for (j = aregs - 1; j >= 0; --j) ra_vload(ic[i].args[j], argregs[j]); } /* loading the operands */ if (n >= 1) ra_vload(ic[i].a1, r1); if (n >= 2) ra_vload(ic[i].a2, r2); if (n >= 3) ra_vload(ic[i].a3, r3); /* dropping values that are no longer used */ for (j = 0; j < LEN(ra_live); j++) if (ra_live[j] >= 0 && ic_luse[ra_live[j]] <= i) ra_vdrop(ra_live[j]); /* saving values stored in registers that may change */ for (j = 0; j < N_REGS; j++) if (mt & (1 << j)) ra_spill(j); /* overwriting a value that is needed later (unless loading a local to its register) */ if (oc & O_OUT) if (oc != (O_LD | O_LOC) || ra_lmap[rd] != ic[i].a1 || ra_vmap[rd] >= 0) ra_spill(rd); /* before the last instruction of a basic block; for jumps */ if (i + 1 < ic_n && ic_bbeg[i + 1] && oc & O_JXX) ra_bbend(); /* performing the instruction */ if (oc & O_BOP) i_ins(op, rd, r1, oc & O_NUM ? ic[i].a2 : r2, 0); if (oc & O_UOP) i_ins(op, rd, r1, r2, 0); if (oc == (O_LD | O_NUM)) i_ins(op, rd, r1, ic[i].a2, 0); if (oc == (O_LD | O_LOC)) ra_lload(ic[i].a1, ic[i].a2, rd, O_T(op)); if (oc == (O_ST | O_NUM)) i_ins(op, 0, r1, r2, ic[i].a3); if (oc == (O_ST | O_LOC)) ra_lsave(ic[i].a2, ic[i].a3, r1, O_T(op)); if (oc == O_RET) i_ins(op, 0, r1, 0, 0); if (oc == O_MOV) i_ins(op, rd, r1, 0, 0); if (oc == (O_MOV | O_NUM)) i_ins(op, rd, ic[i].a1, 0, 0); if (oc == (O_MOV | O_LOC)) loc_toadd(ic[i].a1, ic[i].a2, rd); if (oc == (O_MOV | O_SYM)) i_ins(op, rd, ic[i].a1, ic[i].a2, 0); if (oc == O_CALL) i_ins(op, rd, r1, 0, 0); if (oc == (O_CALL | O_SYM)) i_ins(op, rd, ic[i].a1, ic[i].a2, 0); if (oc == O_JMP) i_ins(op, 0, 0, 0, ic[i].a3); if (oc & O_JZ) i_ins(op, 0, r1, 0, ic[i].a3); if (oc & O_JCC) i_ins(op, 0, r1, oc & O_NUM ? ic[i].a2 : r2, ic[i].a3); if (oc == O_MSET) i_ins(op, 0, r1, r2, r3); if (oc == O_MCPY) i_ins(op, 0, r1, r2, r3); /* saving back the output register */ if (oc & O_OUT && ic_luse[i] > i) ra_vsave(ic_i, rd); /* after the last instruction of a basic block */ if (i + 1 < ic_n && ic_bbeg[i + 1] && !(oc & O_JXX)) ra_bbend(); } i_label(ic_n); } static void ic_reset(void) { o_tmpdrop(-1); o_back(0); free(loc_off); loc_off = NULL; loc_n = 0; loc_sz = 0; loc_pos = I_LOC0; } void o_func_beg(char *name, int argc, int global, int varg) { int i; func_argc = argc; func_varg = varg; func_regs = 0; ic_reset(); for (i = 0; i < argc; i++) loc_add(I_ARG0 + -i * ULNG); out_def(name, (global ? OUT_GLOB : 0) | OUT_CS, mem_len(&cs), 0); } void o_code(char *name, char *c, long c_len) { out_def(name, OUT_CS, mem_len(&cs), 0); mem_put(&cs, c, c_len); } void o_func_end(void) { long spsub; long sargs = 0; long sargs_last = -1; long sregs_pos; char *c; long c_len, *rsym, *rflg, *roff, rcnt; int leaf = 1; int locs = 0; /* accessing locals on the stack */ int i; ic_get(&ic, &ic_n); /* the intermediate code */ reg_init(ic, ic_n); /* global register allocation */ ra_init(ic, ic_n); /* initialize register allocation */ ic_luse = ic_lastuse(ic, ic_n); ic_gencode(ic, ic_n); /* generating machine code */ free(ic_luse); /* deciding which arguments to save */ for (i = 0; i < func_argc; i++) if (loc_mem[i]) sargs_last = i + 1; for (i = 0; i < N_ARGS && (func_varg || i < sargs_last); i++) sargs |= 1 << argregs[i]; /* computing the amount of stack subtraction */ for (i = 0; i < loc_n; i++) if (loc_mem[i]) locs = 1; spsub = (locs || ra_vmax) ? loc_pos + ra_vmax * ULNG : 0; for (i = 0; i < N_TMPS; i++) if (((1 << tmpregs[i]) & func_regs & R_PERM) != 0) spsub += ULNG; sregs_pos = spsub; spsub += func_maxargs * ULNG; /* leaf functions */ for (i = 0; i < ic_n; i++) if (ic[i].op & O_CALL) leaf = 0; /* adding function prologue and epilogue */ i_wrap(func_argc, sargs, spsub, spsub || locs || !leaf, func_regs & R_PERM, -sregs_pos); ra_done(); i_code(&c, &c_len, &rsym, &rflg, &roff, &rcnt); for (i = 0; i < rcnt; i++) /* adding the relocations */ out_rel(rsym[i], rflg[i], roff[i] + mem_len(&cs)); mem_put(&cs, c, c_len); /* appending function code */ free(c); free(rsym); free(rflg); free(roff); for (i = 0; i < ic_n; i++) ic_free(&ic[i]); free(ic); reg_done(); ic_reset(); } void o_write(int fd) { i_done(); out_write(fd, mem_buf(&cs), mem_len(&cs), mem_buf(&ds), mem_len(&ds)); free(loc_off); free(ds_name); free(ds_off); mem_done(&cs); mem_done(&ds); } ================================================ FILE: int.c ================================================ /* neatcc intermediate code generation */ #include #include #include #include "ncc.h" static struct ic *ic; /* intermediate code */ static long ic_n, ic_sz; /* number of instructions in ic[] */ static long iv[NTMPS]; /* operand stack */ static long iv_n; /* number of values in iv[] */ static long *lab_loc; /* label locations */ static long lab_n, lab_sz; /* number of labels in lab_loc[] */ static long lab_last; /* the last label target */ static int io_num(void); static int io_mul2(void); static int io_cmp(void); static int io_jmp(void); static int io_addr(void); static int io_loc(void); static int io_imm(void); static int io_call(void); static void io_deadcode(void); static void iv_put(long n); static struct ic *ic_put(long op, long arg1, long arg2, long arg3) { struct ic *c; if (ic_n == ic_sz) { ic_sz = MAX(128, ic_sz * 2); ic = mextend(ic, ic_n, ic_sz, sizeof(*ic)); } c = &ic[ic_n++]; c->op = op; c->a1 = arg1; c->a2 = arg2; c->a3 = arg3; if (op & O_OUT) iv_put(ic_n - 1); return c; } static void ic_back(long pos) { int i; for (i = pos; i < ic_n; i++) if (ic[i].op & O_CALL) free(ic[i].args); ic_n = pos; } static long iv_pop(void) { return iv[--iv_n]; } static long iv_get(int n) { return iv[iv_n - n - 1]; } static void iv_put(long n) { iv[iv_n++] = n; } static void iv_drop(int n) { iv_n = MAX(0, iv_n - n); } static void iv_swap(int x, int y) { long v = iv[iv_n - x - 1]; iv[iv_n - x - 1] = iv[iv_n - y - 1]; iv[iv_n - y - 1] = v; } static void iv_dup(void) { iv[iv_n] = iv[iv_n - 1]; iv_n++; } void o_num(long n) { ic_put(O_MOV | O_NUM, n, 0, 0); } void o_local(long id) { ic_put(O_MOV | O_LOC, id, 0, 0); } void o_sym(char *sym) { ic_put(O_MOV | O_SYM, out_sym(sym), 0, 0); } void o_tmpdrop(int n) { iv_drop(n >= 0 ? n : iv_n); } void o_tmpswap(void) { iv_swap(0, 1); } void o_tmpcopy(void) { iv_dup(); } /* return one if the given value is constant */ static int ic_const(long iv) { long oc = O_C(ic[iv].op); return oc & O_MOV && oc & (O_NUM | O_SYM | O_LOC); } /* return one if the given value is a simple load */ static int ic_load(long iv) { long oc = O_C(ic[iv].op); int i; if (oc & O_LD && oc & (O_NUM | O_SYM | O_LOC)) { for (i = iv + 1; i < ic_n; i++) if (ic[i].op & O_ST) return 0; return 1; } return 0; } void o_bop(long op) { int r1 = iv_pop(); int r2 = iv_pop(); /* load constants as late as possible */ if (opt(1) && ic_const(r2) && !ic_const(r1)) { ic_put(ic[r2].op, ic[r2].a1, ic[r2].a2, ic[r2].a3); r2 = iv_pop(); } ic_put(op, r2, r1, 0); if (opt(1)) { io_num(); io_mul2(); io_addr(); io_imm(); } } void o_uop(long op) { int r1 = iv_pop(); ic_put(op, r1, 0, 0); if (opt(1)) { io_num(); io_cmp(); } } void o_assign(long bt) { int rv = iv_pop(); int lv = iv_pop(); /* load constants as late as possible */ if (opt(1) && (ic_const(lv) || ic_load(lv))) { ic_put(ic[lv].op, ic[lv].a1, ic[lv].a2, ic[lv].a3); lv = iv_pop(); } ic_put(O_MK(O_ST | O_NUM, bt), rv, lv, 0); iv_put(rv); if (opt(1)) io_loc(); } void o_deref(long bt) { int r1 = iv_pop(); ic_put(O_MK(O_LD | O_NUM, bt), r1, 0, 0); if (opt(1)) io_loc(); } void o_cast(long bt) { if (T_SZ(bt) != ULNG) { int r1 = iv_pop(); ic_put(O_MK(O_MOV, bt), r1, 0, 0); if (opt(1)) io_num(); } } void o_memcpy(void) { int r2 = iv_pop(); int r1 = iv_pop(); int r0 = iv_pop(); ic_put(O_MCPY, r0, r1, r2); } void o_memset(void) { int r2 = iv_pop(); int r1 = iv_pop(); int r0 = iv_pop(); ic_put(O_MSET, r0, r1, r2); } void o_call(int argc, int ret) { struct ic *c; long *args = malloc(argc * sizeof(c->args[0])); int r1, i; for (i = argc - 1; i >= 0; --i) args[i] = iv_pop(); for (i = argc - 1; i >= 0; --i) { int iv = args[i]; /* load constants as late as possible */ if (opt(1) && (ic_const(iv) || ic_load(iv))) { ic_put(ic[iv].op, ic[iv].a1, ic[iv].a2, ic[iv].a3); args[i] = iv_pop(); } } r1 = iv_pop(); c = ic_put(O_CALL, r1, 0, argc); c->args = args; iv_drop(ret == 0); if (opt(1)) io_call(); } void o_ret(int ret) { if (!ret) o_num(0); ic_put(O_RET, iv_pop(), 0, 0); } void o_label(long id) { while (id >= lab_sz) { lab_sz = MAX(128, lab_sz * 2); lab_loc = mextend(lab_loc, lab_n, lab_sz, sizeof(*lab_loc)); } while (lab_n <= id) lab_loc[lab_n++] = -1; lab_loc[id] = ic_n; lab_last = ic_n; } void o_jmp(long id) { ic_put(O_JMP, 0, 0, id); } void o_jz(long id) { ic_put(O_JZ, iv_pop(), 0, id); if (opt(1)) io_jmp(); } int o_popnum(long *n) { if (ic_num(ic, iv_get(0), n)) return 1; iv_drop(1); return 0; } int o_popsym(long *sym, long *off) { if (ic_sym(ic, iv_get(0), sym, off)) return 1; iv_drop(1); return 0; } long o_mark(void) { return ic_n; } void o_back(long mark) { ic_back(mark); } void ic_get(struct ic **c, long *n) { int i; if (!ic_n || ~ic[ic_n - 1].op & O_RET || lab_last == ic_n) o_ret(0); for (i = 0; i < ic_n; i++) /* filling branch targets */ if (ic[i].op & O_JXX) ic[i].a3 = lab_loc[ic[i].a3]; io_deadcode(); /* removing dead code */ *c = ic; *n = ic_n; ic = NULL; ic_n = 0; ic_sz = 0; iv_n = 0; free(lab_loc); lab_loc = NULL; lab_n = 0; lab_sz = 0; lab_last = 0; } void ic_free(struct ic *ic) { if (ic->op & O_CALL) free(ic->args); } /* intermediate code queries */ static long cb(long op, long *r, long a, long b) { switch (O_C(op)) { case O_ADD: *r = a + b; break; case O_SUB: *r = a - b; break; case O_AND: *r = a & b; break; case O_OR: *r = a | b; break; case O_XOR: *r = a ^ b; break; case O_MUL: *r = a * b; break; case O_DIV: if (!b) return 1; *r = a / b; break; case O_MOD: if (!b) return 1; *r = a % b; break; case O_SHL: *r = a << b; break; case O_SHR: *r = O_T(op) & T_MSIGN ? a >> b : (unsigned long) a >> b; break; case O_LT: *r = a < b; break; case O_GT: *r = a > b; break; case O_LE: *r = a <= b; break; case O_GE: *r = a >= b; break; case O_EQ: *r = a == b; break; case O_NE: *r = a != b; break; } return 0; } static long cu(int op, long i) { switch (O_C(op)) { case O_NEG: return -i; case O_NOT: return ~i; case O_LNOT: return !i; } return 0; } static long c_cast(long n, unsigned bt) { if (!(bt & T_MSIGN) && T_SZ(bt) != ULNG) n &= ((1l << (long) (T_SZ(bt) * 8)) - 1); if (bt & T_MSIGN && T_SZ(bt) != ULNG && n > (1l << (T_SZ(bt) * 8 - 1))) n = -((1l << (T_SZ(bt) * 8)) - n); return n; } int ic_num(struct ic *ic, long iv, long *n) { long n1, n2; long oc = O_C(ic[iv].op); long bt = O_T(ic[iv].op); if (oc & O_MOV && oc & O_NUM) { *n = ic[iv].a1; return 0; } if (oc & O_BOP) { if (ic_num(ic, ic[iv].a1, &n1)) return 1; if (ic_num(ic, ic[iv].a2, &n2)) return 1; return cb(ic[iv].op, n, n1, n2); } if (oc & O_UOP) { if (ic_num(ic, ic[iv].a1, &n1)) return 1; *n = cu(ic[iv].op, n1); return 0; } if (oc & O_MOV && !(oc & (O_NUM | O_LOC | O_SYM))) { if (ic_num(ic, ic[iv].a1, &n1)) return 1; *n = c_cast(n1, bt); return 0; } return 1; } int ic_sym(struct ic *ic, long iv, long *sym, long *off) { long n; long oc = O_C(ic[iv].op); if (oc & O_MOV && oc & O_SYM) { *sym = ic[iv].a1; *off = ic[iv].a2; return 0; } if (oc == O_ADD) { if ((ic_sym(ic, ic[iv].a1, sym, off) || ic_num(ic, ic[iv].a2, &n)) && (ic_sym(ic, ic[iv].a2, sym, off) || ic_num(ic, ic[iv].a1, &n))) return 1; *off += n; return 0; } if (oc == O_SUB) { if (ic_sym(ic, ic[iv].a1, sym, off) || ic_num(ic, ic[iv].a2, &n)) return 1; *off -= n; return 0; } return 1; } static int ic_off(struct ic *ic, long iv, long *base_iv, long *off) { long n; long oc = O_C(ic[iv].op); if (oc == (O_ADD | O_NUM)) { *base_iv = ic[iv].a1; *off = ic[iv].a2; return 0; } if (oc == (O_SUB | O_NUM)) { *base_iv = ic[iv].a1; *off = -ic[iv].a2; return 0; } if (oc == O_ADD) { if ((ic_off(ic, ic[iv].a1, base_iv, off) || ic_num(ic, ic[iv].a2, &n)) && (ic_off(ic, ic[iv].a2, base_iv, off) || ic_num(ic, ic[iv].a1, &n))) return 1; *off += n; return 0; } if (oc == O_SUB) { if (ic_off(ic, ic[iv].a1, base_iv, off) || ic_num(ic, ic[iv].a2, &n)) return 1; *off -= n; return 0; } *base_iv = iv; *off = 0; return 0; } /* number of register arguments */ int ic_regcnt(struct ic *ic) { long o = ic->op; if (o & O_BOP) return o & (O_NUM | O_SYM | O_LOC) ? 1 : 2; if (o & O_UOP) return o & (O_NUM | O_SYM | O_LOC) ? 0 : 1; if (o & O_CALL) return o & (O_NUM | O_SYM | O_LOC) ? 0 : 1; if (o & O_MOV) return o & (O_NUM | O_SYM | O_LOC) ? 0 : 1; if (o & O_MEM) return 3; if (o & O_JMP) return 0; if (o & O_JZ) return 1; if (o & O_JCC) return o & (O_NUM | O_SYM | O_LOC) ? 1 : 2; if (o & O_RET) return 1; if (o & O_LD) return ((o & O_NUM) != 0); if (o & O_ST) return 1 + ((o & O_NUM) != 0); return 0; } /* * The returned array indicates the last instruction in * which the value produced by each instruction is used. */ long *ic_lastuse(struct ic *ic, long ic_n) { long *luse = calloc(ic_n, sizeof(luse[0])); int i, j; for (i = ic_n - 1; i >= 0; --i) { int n = ic_regcnt(ic + i); if (!luse[i]) if (!(ic[i].op & O_OUT) || ic[i].op & O_CALL) luse[i] = -1; if (!luse[i]) continue; if (n >= 1 && !luse[ic[i].a1]) luse[ic[i].a1] = i; if (n >= 2 && !luse[ic[i].a2]) luse[ic[i].a2] = i; if (n >= 3 && !luse[ic[i].a3]) luse[ic[i].a3] = i; if (ic[i].op & O_CALL) for (j = 0; j < ic[i].a3; j++) if (!luse[ic[i].args[j]]) luse[ic[i].args[j]] = i; } return luse; } /* intermediate code optimisations */ /* constant folding */ static int io_num(void) { long n; if (!ic_num(ic, iv_get(0), &n)) { iv_drop(1); o_num(n); return 0; } return 1; } static int log2a(unsigned long n) { int i = 0; for (i = 0; i < LONGSZ * 8; i++) if (n & (1u << i)) break; if (i == LONGSZ * 8 || !(n >> (i + 1))) return i; return -1; } static long iv_num(long n) { o_num(n); return iv_pop(); } /* optimised multiplication operations for powers of two */ static int io_mul2(void) { long iv = iv_get(0); long n, p; long r1, r2; long oc = O_C(ic[iv].op); long bt = O_T(ic[iv].op); if (!(oc & O_MUL)) return 1; if (oc == O_MUL && !ic_num(ic, ic[iv].a1, &n)) { long t = ic[iv].a1; ic[iv].a1 = ic[iv].a2; ic[iv].a2 = t; } if (ic_num(ic, ic[iv].a2, &n)) return 1; p = log2a(n); if (n && p < 0) return 1; if (oc == O_MUL) { iv_drop(1); if (n == 1) { iv_put(ic[iv].a1); return 0; } if (n == 0) { o_num(0); return 0; } r2 = iv_num(p); ic_put(O_MK(O_SHL, ULNG), ic[iv].a1, r2, 0); return 0; } if (oc == O_DIV && ~bt & T_MSIGN) { iv_drop(1); if (n == 1) { iv_put(ic[iv].a1); return 0; } r2 = iv_num(p); ic_put(O_MK(O_SHR, ULNG), ic[iv].a1, r2, 0); return 0; } if (oc == O_MOD && ~bt & T_MSIGN) { iv_drop(1); if (n == 1) { o_num(0); return 0; } r2 = iv_num(LONGSZ * 8 - p); ic_put(O_MK(O_SHL, ULNG), ic[iv].a1, r2, 0); r1 = iv_pop(); r2 = iv_num(LONGSZ * 8 - p); ic_put(O_MK(O_SHR, ULNG), r1, r2, 0); return 0; } return 1; } /* optimise comparison */ static int io_cmp(void) { long iv = iv_get(0); long cmp = ic[iv].a1; if (O_C(ic[iv].op) == O_LNOT && ic[cmp].op & O_CMP) { iv_drop(1); ic[cmp].op ^= 1; iv_put(cmp); return 0; } return 1; } /* optimise branch instructions after comparison */ static int io_jmp(void) { struct ic *c = &ic[ic_n - 1]; long oc = O_C(c->op); if (oc & O_JZ && O_C(ic[c->a1].op) == O_LNOT) { c->a1 = ic[c->a1].a1; c->op ^= 1; return 0; } if (oc & O_JZ && O_C(ic[c->a1].op) & O_CMP) { long cop = (ic[c->a1].op & ~O_CMP) | O_JCC; c->op = O_C(c->op) == O_JZ ? cop ^ 1 : cop; c->a2 = ic[c->a1].a2; c->a1 = ic[c->a1].a1; return 0; } return 1; } /* optimise accessing locals or symbols with an offset */ static int io_addr(void) { long iv, off; if (ic_off(ic, iv_get(0), &iv, &off) || iv == iv_get(0)) return 1; if (ic[iv].op & O_MOV && ic[iv].op & O_LOC) { iv_drop(1); ic_put(O_MOV | O_LOC, ic[iv].a1, ic[iv].a2 + off, 0); return 0; } if (ic[iv].op & O_MOV && ic[iv].op & O_SYM) { iv_drop(1); ic_put(O_MOV | O_SYM, ic[iv].a1, ic[iv].a2 + off, 0); return 0; } return 1; } static int imm_ok(long op, long n, int arg) { long m[5]; if (i_reg(op | O_NUM, m + 0, m + 1, m + 2, m + 3, m + 4)) return 0; return i_imm(m[arg], n); } /* optimise loading and storing locals */ static int io_loc(void) { struct ic *c = &ic[ic_n - 1]; long iv, off; if (c->op & O_LD && c->op & O_NUM) { if (ic_off(ic, c->a1, &iv, &off)) return 1; if (ic[iv].op & O_MOV && ic[iv].op & O_LOC) { c->op = (c->op & ~O_NUM) | O_LOC; c->a1 = ic[iv].a1; c->a2 += ic[iv].a2 + off; return 0; } if (imm_ok(c->op, off, 2)) { c->a1 = iv; c->a2 += off; } return 0; } if (c->op & O_ST && c->op & O_NUM) { if (ic_off(ic, c->a2, &iv, &off)) return 1; if (ic[iv].op & O_MOV && ic[iv].op & O_LOC) { c->op = (c->op & ~O_NUM) | O_LOC; c->a2 = ic[iv].a1; c->a3 += ic[iv].a2 + off; return 0; } if (imm_ok(c->op, off, 3)) { c->a2 = iv; c->a3 += off; } return 0; } return 1; } /* reverse the order of comparison operands (e.g., <= to >=) */ static int flip_cond(int op) { /* lt -> gt, ge -> le, eq -> eq, ne -> ne, le -> ge, gt -> lt */ static int cond[] = {5, 4, 2, 3, 1, 0}; return (op & ~0x0f) | cond[op & 0x0f]; } /* use instruction immediates */ static int io_imm(void) { struct ic *c = &ic[ic_n - 1]; long oc = O_C(c->op); long n; if (oc & (O_NUM | O_LOC | O_SYM)) return 1; if (oc == O_ADD || oc == O_MUL || oc == O_AND || oc == O_OR || oc == O_XOR || oc == O_EQ || oc == O_NE) { if (!ic_num(ic, c->a1, &n)) { long t = c->a1; c->a1 = c->a2; c->a2 = t; } } if (oc == O_LT || oc == O_GE || oc == O_LE || oc == O_GT) { if (!ic_num(ic, c->a1, &n)) { int t = c->a1; c->a1 = c->a2; c->a2 = t; c->op = flip_cond(c->op); } } if (oc & O_JCC && !ic_num(ic, c->a1, &n)) { int t = c->a1; c->a1 = c->a2; c->a2 = t; c->op = flip_cond(c->op); } if (oc & O_JCC && !ic_num(ic, c->a2, &n) && imm_ok(c->op, n, 2)) { c->op |= O_NUM; c->a2 = n; return 0; } if (!(oc & O_BOP) || ic_num(ic, c->a2, &n)) return 1; if ((oc == O_ADD || oc == O_SUB || oc & O_SHL) && n == 0) { iv_drop(1); iv_put(c->a1); return 0; } if (imm_ok(c->op, n, 2)) { c->op |= O_NUM; c->a2 = n; return 0; } return 1; } /* calling symbols */ static int io_call(void) { struct ic *c = &ic[ic_n - 1]; long sym, off; if (c->op & O_CALL && !ic_sym(ic, c->a1, &sym, &off) && !off) { c->op |= O_SYM; c->a1 = sym; return 0; } return 1; } /* remove dead code */ static void io_deadcode(void) { char *live; long *nidx; long src = 0, dst = 0; int i, j; /* liveness analysis */ live = calloc(ic_n, sizeof(live[0])); for (i = ic_n - 1; i >= 0; i--) { int n = ic_regcnt(ic + i); if (!(ic[i].op & O_OUT) || ic[i].op & O_CALL) live[i] = 1; if (!live[i]) continue; if (n >= 1) live[ic[i].a1] = 1; if (n >= 2) live[ic[i].a2] = 1; if (n >= 3) live[ic[i].a3] = 1; if (ic[i].op & O_CALL) for (j = 0; j < ic[i].a3; j++) live[ic[i].args[j]] = 1; } /* the new indices of intermediate instructions */ nidx = calloc(ic_n, sizeof(nidx[0])); while (src < ic_n) { while (src < ic_n && !live[src]) { nidx[src] = dst; ic_free(&ic[src++]); } if (src < ic_n) { nidx[src] = dst; if (src != dst) memcpy(ic + dst, ic + src, sizeof(ic[src])); src++; dst++; } } ic_n = dst; /* adjusting arguments and branch targets */ for (i = 0; i < ic_n; i++) { int n = ic_regcnt(ic + i); if (n >= 1) ic[i].a1 = nidx[ic[i].a1]; if (n >= 2) ic[i].a2 = nidx[ic[i].a2]; if (n >= 3) ic[i].a3 = nidx[ic[i].a3]; if (ic[i].op & O_JXX) ic[i].a3 = nidx[ic[i].a3]; if (ic[i].op & O_CALL) for (j = 0; j < ic[i].a3; j++) ic[i].args[j] = nidx[ic[i].args[j]]; } free(live); free(nidx); } ================================================ FILE: mem.c ================================================ #include #include #include #include #include "ncc.h" #define MEMSZ 512 static void mem_extend(struct mem *mem) { char *s = mem->s; mem->sz = mem->sz ? mem->sz + mem->sz : MEMSZ; mem->s = malloc(mem->sz); if (mem->n) memcpy(mem->s, s, mem->n); free(s); } void mem_init(struct mem *mem) { memset(mem, 0, sizeof(*mem)); } void mem_done(struct mem *mem) { free(mem->s); memset(mem, 0, sizeof(*mem)); } void mem_cut(struct mem *mem, long pos) { mem->n = pos < mem->n ? pos : mem->n; } void mem_cpy(struct mem *mem, long off, void *buf, long len) { while (mem->n + off + len + 1 >= mem->sz) mem_extend(mem); memcpy(mem->s + off, buf, len); } void mem_put(struct mem *mem, void *buf, long len) { mem_cpy(mem, mem->n, buf, len); mem->n += len; } void mem_putc(struct mem *mem, int c) { if (mem->n + 2 >= mem->sz) mem_extend(mem); mem->s[mem->n++] = c; } void mem_putz(struct mem *mem, long sz) { while (mem->n + sz + 1 >= mem->sz) mem_extend(mem); memset(mem->s + mem->n, 0, sz); mem->n += sz; } /* return a pointer to mem's buffer; valid as long as mem is not modified */ void *mem_buf(struct mem *mem) { if (!mem->s) return ""; mem->s[mem->n] = '\0'; return mem->s; } long mem_len(struct mem *mem) { return mem->n; } void *mem_get(struct mem *mem) { void *ret; if (!mem->s) mem_extend(mem); ret = mem->s; mem_init(mem); return ret; } ================================================ FILE: ncc.c ================================================ /* * THE NEATCC C COMPILER * * Copyright (C) 2010-2016 Ali Gholami Rudi * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * neatcc parser * * The parser reads tokens from the tokenizer (tok_*) and calls the * appropriate code generation functions (o_*). The generator * maintains a stack of values pushed via, for instance, o_num() * and generates the necessary code for the accesses to the items * in this stack, like o_bop() for performing a binary operations * on the top two items of the stack. The parser maintains the * types of values pushed to the generator stack in its type stack * (ts_*). For instance, for binary operations two types are * popped first and the resulting type is pushed to the type stack * (ts_binop()). * */ #include #include #include #include #include #include #include #include #include #include "ncc.h" #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) < (b) ? (b) : (a)) #define TYPE_BT(t) ((t)->ptr ? ULNG : (t)->bt) #define TYPE_SZ(t) ((t)->ptr ? ULNG : (t)->bt & T_MSIZE) #define TYPE_VOID(t) (!(t)->bt && !(t)->flags && !(t)->ptr) /* type->flag values */ #define T_ARRAY 0x01 #define T_STRUCT 0x02 #define T_FUNC 0x04 /* variable definition flags */ #define F_STATIC 0x01 #define F_EXTERN 0x02 struct type { unsigned bt; unsigned flags; int ptr; int id; /* for structs, functions and arrays */ int addr; /* the address is passed to gen.c; deref for value */ }; /* type stack */ static struct type ts[NTMPS]; static int nts; static void ts_push_bt(unsigned bt) { ts[nts].ptr = 0; ts[nts].flags = 0; ts[nts].addr = 0; ts[nts++].bt = bt; #ifdef NCCWORDCAST o_cast(bt); /* casting to architecture word */ #endif } static void ts_push(struct type *t) { struct type *d = &ts[nts++]; memcpy(d, t, sizeof(*t)); } static void ts_push_addr(struct type *t) { ts_push(t); ts[nts - 1].addr = 1; } static void ts_pop(struct type *type) { nts--; if (type) *type = ts[nts]; } void err(char *fmt, ...) { va_list ap; char msg[512]; va_start(ap, fmt); vsprintf(msg, fmt, ap); va_end(ap); die("%s: %s", cpp_loc(tok_addr()), msg); } void *mextend(void *old, long oldsz, long newsz, long memsz) { void *new = malloc(newsz * memsz); memcpy(new, old, oldsz * memsz); memset(new + oldsz * memsz, 0, (newsz - oldsz) * memsz); free(old); return new; } struct name { char name[NAMELEN]; char elfname[NAMELEN]; /* local elf name for function static variables */ struct type type; long addr; /* local stack offset, global data addr, struct offset */ }; static struct name *locals; static int locals_n, locals_sz; static struct name *globals; static int globals_n, globals_sz; static void local_add(struct name *name) { if (locals_n >= locals_sz) { locals_sz = MAX(128, locals_sz * 2); locals = mextend(locals, locals_n, locals_sz, sizeof(locals[0])); } memcpy(&locals[locals_n++], name, sizeof(*name)); } static int local_find(char *name) { int i; for (i = locals_n - 1; i >= 0; --i) if (!strcmp(locals[i].name, name)) return i; return -1; } static int global_find(char *name) { int i; for (i = globals_n - 1; i >= 0; i--) if (!strcmp(name, globals[i].name)) return i; return -1; } static void global_add(struct name *name) { if (globals_n >= globals_sz) { globals_sz = MAX(128, globals_sz * 2); globals = mextend(globals, globals_n, globals_sz, sizeof(globals[0])); } memcpy(&globals[globals_n++], name, sizeof(*name)); } #define LABEL() (++label) static int label; /* last used label id */ static int l_break; /* current break label */ static int l_cont; /* current continue label */ static struct enumval { char name[NAMELEN]; int n; } *enums; static int enums_n, enums_sz; static void enum_add(char *name, int val) { struct enumval *ev; if (enums_n >= enums_sz) { enums_sz = MAX(128, enums_sz * 2); enums = mextend(enums, enums_n, enums_sz, sizeof(enums[0])); } ev = &enums[enums_n++]; strcpy(ev->name, name); ev->n = val; } static int enum_find(int *val, char *name) { int i; for (i = enums_n - 1; i >= 0; --i) if (!strcmp(name, enums[i].name)) { *val = enums[i].n; return 0; } return 1; } static struct typdefinfo { char name[NAMELEN]; struct type type; } *typedefs; static int typedefs_n, typedefs_sz; static void typedef_add(char *name, struct type *type) { struct typdefinfo *ti; if (typedefs_n >= typedefs_sz) { typedefs_sz = MAX(128, typedefs_sz * 2); typedefs = mextend(typedefs, typedefs_n, typedefs_sz, sizeof(typedefs[0])); } ti = &typedefs[typedefs_n++]; strcpy(ti->name, name); memcpy(&ti->type, type, sizeof(*type)); } static int typedef_find(char *name) { int i; for (i = typedefs_n - 1; i >= 0; --i) if (!strcmp(name, typedefs[i].name)) return i; return -1; } static struct array { struct type type; int n; } *arrays; static int arrays_n, arrays_sz; static int array_add(struct type *type, int n) { struct array *a; if (arrays_n >= arrays_sz) { arrays_sz = MAX(128, arrays_sz * 2); arrays = mextend(arrays, arrays_n, arrays_sz, sizeof(arrays[0])); } a = &arrays[arrays_n++]; memcpy(&a->type, type, sizeof(*type)); a->n = n; return a - arrays; } static void array2ptr(struct type *t) { if (t->flags & T_ARRAY && !t->ptr) { memcpy(t, &arrays[t->id].type, sizeof(*t)); t->ptr++; } } static struct structinfo { char name[NAMELEN]; struct name fields[NFIELDS]; int nfields; int isunion; int size; } *structs; static int structs_n, structs_sz; static int struct_find(char *name, int isunion) { int i; for (i = structs_n - 1; i >= 0; --i) if (*structs[i].name && !strcmp(name, structs[i].name) && structs[i].isunion == isunion) return i; if (structs_n >= structs_sz) { structs_sz = MAX(128, structs_sz * 2); structs = mextend(structs, structs_n, structs_sz, sizeof(structs[0])); } i = structs_n++; memset(&structs[i], 0, sizeof(structs[i])); strcpy(structs[i].name, name); structs[i].isunion = isunion; return i; } static struct name *struct_field(int id, char *name) { struct structinfo *si = &structs[id]; int i; for (i = 0; i < si->nfields; i++) if (!strcmp(name, si->fields[i].name)) return &si->fields[i]; err("unknown field <%s>\n", name); return NULL; } /* return t's size */ static int type_totsz(struct type *t) { if (t->ptr) return ULNG; if (t->flags & T_ARRAY) return arrays[t->id].n * type_totsz(&arrays[t->id].type); return t->flags & T_STRUCT ? structs[t->id].size : T_SZ(t->bt); } /* return t's dereferenced size */ static unsigned type_szde(struct type *t) { struct type de = *t; array2ptr(&de); de.ptr--; return type_totsz(&de); } /* dereference stack top if t->addr (ie. address is pushed to gen.c) */ static void ts_de(int deref) { struct type *t = &ts[nts - 1]; array2ptr(t); if (deref && t->addr && (t->ptr || !(t->flags & T_FUNC))) o_deref(TYPE_BT(t)); t->addr = 0; } /* pop stack pop to *t and dereference if t->addr */ static void ts_pop_de(struct type *t) { ts_de(1); ts_pop(t); } /* pop the top 2 stack values and dereference them if t->addr */ static void ts_pop_de2(struct type *t1, struct type *t2) { ts_pop_de(t1); o_tmpswap(); ts_pop_de(t2); o_tmpswap(); } /* the previous identifier; to handle labels */ static char tok_previden[NAMELEN]; static char *tok_iden(void) { snprintf(tok_previden, sizeof(tok_previden), "%s", tok_get()); return tok_previden; } static int tok_jmp(char *tok) { if (strcmp(tok, tok_see())) return 1; tok_get(); return 0; } static int tok_comes(char *tok) { return !strcmp(tok, tok_see()); } static void tok_req(char *tok) { char *got = tok_get(); if (strcmp(tok, got)) err("syntax error (expected <%s> but got <%s>)\n", tok, got); } static int tok_grp(void) { int c = (unsigned char) tok_see()[0]; if (c == '"') return '"'; if (c == '\'' || isdigit(c)) return '0'; if (c == '_' || isalpha(c)) return 'a'; return 0; } /* the result of a binary operation on variables of type bt1 and bt2 */ static unsigned bt_op(unsigned bt1, unsigned bt2) { /* Type conversion according to ISO9899, sec. 6.3.1.8. * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf * * Summary: * Size: Always convert to largest size. * Sign: * - Same sign: Keep sign. * - Same size or unsigned larger: Convert to unsigned. * - Signed larger: Convert to signed. */ int sz = MAX(T_SZ(bt1), T_SZ(bt2)); /* the unsigned operand is at least as large as the signed one */ if ((!T_SG(bt1) && T_SG(bt2) && T_SZ(bt1) >= T_SZ(bt2)) || (T_SG(bt1) && !T_SG(bt2) && T_SZ(bt1) <= T_SZ(bt2))) return MAX(sz, UINT); return T_SG(bt1) | T_SG(bt2) | MAX(sz, UINT); } /* the result of a unary operation on variables of bt */ static unsigned bt_uop(unsigned bt) { return bt_op(bt, SINT); } /* push the result of a binary operation on the type stack */ static void ts_binop(int op) { struct type t1, t2; unsigned bt1, bt2, bt; ts_pop_de2(&t1, &t2); bt1 = TYPE_BT(&t1); bt2 = TYPE_BT(&t2); bt = bt_op(bt1, bt2); if (op == O_DIV || op == O_MOD) bt = T_MK(bt2 & T_MSIGN, bt); o_bop(O_MK(op, bt)); ts_push_bt(bt); } /* push the result of an additive binary operation on the type stack */ static void ts_addop(int op) { struct type t1, t2; ts_pop_de2(&t1, &t2); if (!t1.ptr && !t2.ptr) { o_bop(op); ts_push_bt(bt_op(TYPE_BT(&t1), TYPE_BT(&t2))); return; } if (t1.ptr && !t2.ptr) o_tmpswap(); if (!t1.ptr && t2.ptr) if (type_szde(&t2) > 1) { o_num(type_szde(&t2)); o_bop(O_MUL); } if (t1.ptr && !t2.ptr) o_tmpswap(); o_bop(op); if (t1.ptr && t2.ptr) { int sz = type_szde(&t1); if (sz > 1) { o_num(sz); o_bop(O_DIV); } ts_push_bt(SLNG); } else { ts_push(t1.ptr ? &t1 : &t2); } } /* function prototypes for parsing function and variable declarations */ static int readname(struct type *main, char *name, struct type *base); static int readtype(struct type *type); static int readdefs(void (*def)(long data, struct name *name, unsigned flags), long data); static int readdefs_int(void (*def)(long data, struct name *name, unsigned flags), long data); /* function prototypes for parsing initializer expressions */ static int initsize(void); static void initexpr(struct type *t, int off, void *obj, void (*set)(void *obj, int off, struct type *t)); static int type_alignment(struct type *t) { if (t->flags & T_ARRAY && !t->ptr) return type_alignment(&arrays[t->id].type); if (t->flags & T_STRUCT && !t->ptr) return type_alignment(&structs[t->id].fields[0].type); return MIN(ULNG, type_totsz(t)); } static void structdef(long data, struct name *name, unsigned flags) { struct structinfo *si = &structs[data]; if (si->isunion) { name->addr = 0; if (si->size < type_totsz(&name->type)) si->size = type_totsz(&name->type); } else { struct type *t = &name->type; int alignment = type_alignment(t); if (t->flags & T_ARRAY && !t->ptr) alignment = MIN(ULNG, type_totsz(&arrays[t->id].type)); si->size = ALIGN(si->size, alignment); name->addr = si->size; si->size += type_totsz(&name->type); } memcpy(&si->fields[si->nfields++], name, sizeof(*name)); } static int struct_create(char *name, int isunion) { int id = struct_find(name, isunion); tok_req("{"); while (tok_jmp("}")) { readdefs(structdef, id); tok_req(";"); } return id; } static void readexpr(void); static void enum_create(void) { long n = 0; tok_req("{"); while (tok_jmp("}")) { char name[NAMELEN]; strcpy(name, tok_get()); if (!tok_jmp("=")) { readexpr(); ts_pop_de(NULL); if (o_popnum(&n)) err("const expr expected!\n"); } enum_add(name, n++); tok_jmp(","); } } /* used to differentiate labels from case and cond exprs */ static int ncexpr; static int caseexpr; static void readpre(void); static char *tmp_str(char *buf, int len) { static char name[NAMELEN]; static int id; sprintf(name, "__neatcc.s%d", id++); buf[len] = '\0'; o_dscpy(o_dsnew(name, len + 1, 0), buf, len + 1); return name; } static void readprimary(void) { if (tok_grp() == '0') { long n; int bt = tok_num(tok_get(), &n); o_num(n); ts_push_bt(bt); return; } if (tok_grp() == '"') { struct type t = {}; /* char type inside the arrays */ struct type a = {}; /* the char array type */ char *buf = tok_get() + 1; int len = tok_len() - 2; t.bt = 1 | T_MSIGN; a.id = array_add(&t, len + 1); a.flags = T_ARRAY; o_sym(tmp_str(buf, len)); ts_push(&a); return; } if (tok_grp() == 'a') { struct name unkn = {""}; char *name = unkn.name; int n; strcpy(name, tok_iden()); /* don't search for labels here */ if (!ncexpr && !caseexpr && tok_comes(":")) return; if ((n = local_find(name)) != -1) { struct name *l = &locals[n]; o_local(l->addr); ts_push_addr(&l->type); return; } if ((n = global_find(name)) != -1) { struct name *g = &globals[n]; o_sym(*g->elfname ? g->elfname : g->name); ts_push_addr(&g->type); return; } if (!enum_find(&n, name)) { o_num(n); ts_push_bt(SINT); return; } if (!tok_comes("(")) err("unknown symbol <%s>\n", name); global_add(&unkn); o_sym(unkn.name); ts_push_bt(ULNG); return; } if (!tok_jmp("(")) { struct type t; if (!readtype(&t)) { struct type o; tok_req(")"); readpre(); ts_pop_de(&o); ts_push(&t); if (!t.ptr || !o.ptr) o_cast(TYPE_BT(&t)); } else { readexpr(); while (tok_jmp(")")) { tok_req(","); ts_pop(NULL); o_tmpdrop(1); readexpr(); } } return; } } static void arrayderef(void) { struct type t; int sz; ts_pop_de(NULL); ts_pop(&t); if (!(t.flags & T_ARRAY && !t.ptr) && t.addr) { o_tmpswap(); o_deref(TYPE_BT(&t)); o_tmpswap(); } array2ptr(&t); t.ptr--; sz = type_totsz(&t); t.addr = 1; if (sz > 1) { o_num(sz); o_bop(O_MUL); } o_bop(O_ADD); ts_push(&t); } static void inc_post(int op) { struct type t = ts[nts - 1]; /* pushing the value before inc */ o_tmpcopy(); ts_de(1); o_tmpswap(); /* increment by 1 or pointer size */ o_tmpcopy(); ts_push(&t); ts_pop_de(&t); o_num(t.ptr > 0 ? type_szde(&t) : 1); o_bop(op); /* assign back */ o_assign(TYPE_BT(&t)); o_tmpdrop(1); } static void readfield(void) { struct name *field; struct type t; ts_pop(&t); array2ptr(&t); field = struct_field(t.id, tok_get()); if (field->addr) { o_num(field->addr); o_bop(O_ADD); } ts_push_addr(&field->type); } static struct funcinfo { struct type args[NARGS]; struct type ret; int nargs; int varg; /* function and argument names; useful only when defining */ char argnames[NARGS][NAMELEN]; char name[NAMELEN]; } *funcs; static int funcs_n, funcs_sz; static int func_create(struct type *ret, char *name, char argnames[][NAMELEN], struct type *args, int nargs, int varg) { struct funcinfo *fi; int i; if (funcs_n >= funcs_sz) { funcs_sz = MAX(128, funcs_sz * 2); funcs = mextend(funcs, funcs_n, funcs_sz, sizeof(funcs[0])); } fi = &funcs[funcs_n++]; memcpy(&fi->ret, ret, sizeof(*ret)); for (i = 0; i < nargs; i++) memcpy(&fi->args[i], &args[i], sizeof(*ret)); fi->nargs = nargs; fi->varg = varg; strcpy(fi->name, name ? name : ""); for (i = 0; i < nargs; i++) strcpy(fi->argnames[i], argnames[i]); return fi - funcs; } static void readcall(void) { struct type t; struct funcinfo *fi; int argc = 0; ts_pop(&t); if (t.flags & T_FUNC && t.ptr > 0) o_deref(ULNG); if (!tok_comes(")")) { do { readexpr(); ts_pop_de(NULL); argc++; } while (!tok_jmp(",")); } tok_req(")"); fi = t.flags & T_FUNC ? &funcs[t.id] : NULL; o_call(argc, fi ? TYPE_BT(&fi->ret) : SINT); if (fi) { if (TYPE_BT(&fi->ret)) o_cast(TYPE_BT(&fi->ret)); ts_push(&fi->ret); } else { ts_push_bt(SINT); } } static void readpost(void) { readprimary(); while (1) { if (!tok_jmp("[")) { readexpr(); tok_req("]"); arrayderef(); continue; } if (!tok_jmp("(")) { readcall(); continue; } if (!tok_jmp("++")) { inc_post(O_ADD); continue; } if (!tok_jmp("--")) { inc_post(O_SUB); continue; } if (!tok_jmp(".")) { readfield(); continue; } if (!tok_jmp("->")) { ts_de(1); readfield(); continue; } break; } } static void inc_pre(int op) { struct type t; readpre(); /* copy the destination */ o_tmpcopy(); ts_push(&ts[nts - 1]); /* increment by 1 or pointer size */ ts_pop_de(&t); o_num(t.ptr > 0 ? type_szde(&t) : 1); o_bop(op); /* assign the result */ o_assign(TYPE_BT(&t)); ts_de(0); } static void readpre(void) { struct type t; if (!tok_jmp("&")) { readpre(); ts_pop(&t); if (!t.addr) err("cannot use the address\n"); t.ptr++; t.addr = 0; ts_push(&t); return; } if (!tok_jmp("*")) { readpre(); ts_pop(&t); array2ptr(&t); if (!t.ptr) err("dereferencing non-pointer\n"); if (t.addr) o_deref(TYPE_BT(&t)); t.ptr--; t.addr = 1; ts_push(&t); return; } if (!tok_jmp("!")) { readpre(); ts_pop_de(NULL); o_uop(O_LNOT); ts_push_bt(SINT); return; } if (!tok_jmp("+")) { readpre(); ts_de(1); ts_pop(&t); ts_push_bt(bt_uop(TYPE_BT(&t))); return; } if (!tok_jmp("-")) { readpre(); ts_de(1); ts_pop(&t); o_uop(O_NEG); ts_push_bt(bt_uop(TYPE_BT(&t))); return; } if (!tok_jmp("~")) { readpre(); ts_de(1); ts_pop(&t); o_uop(O_NOT); ts_push_bt(bt_uop(TYPE_BT(&t))); return; } if (!tok_jmp("++")) { inc_pre(O_ADD); return; } if (!tok_jmp("--")) { inc_pre(O_SUB); return; } if (!tok_jmp("sizeof")) { struct type t; int op = !tok_jmp("("); if (readtype(&t)) { long m = o_mark(); if (op) readexpr(); else readpre(); o_back(m); ts_pop(&t); o_tmpdrop(1); } o_num(type_totsz(&t)); ts_push_bt(ULNG); if (op) tok_req(")"); return; } readpost(); } static void readmul(void) { readpre(); while (1) { if (!tok_jmp("*")) { readpre(); ts_binop(O_MUL); continue; } if (!tok_jmp("/")) { readpre(); ts_binop(O_DIV); continue; } if (!tok_jmp("%")) { readpre(); ts_binop(O_MOD); continue; } break; } } static void readadd(void) { readmul(); while (1) { if (!tok_jmp("+")) { readmul(); ts_addop(O_ADD); continue; } if (!tok_jmp("-")) { readmul(); ts_addop(O_SUB); continue; } break; } } static void shift(int op) { struct type t; readadd(); ts_pop_de2(NULL, &t); o_bop(O_MK(op, TYPE_BT(&t))); ts_push_bt(bt_uop(TYPE_BT(&t))); } static void readshift(void) { readadd(); while (1) { if (!tok_jmp("<<")) { shift(O_SHL); continue; } if (!tok_jmp(">>")) { shift(O_SHR); continue; } break; } } static void cmp(int op) { struct type t1, t2; int bt; readshift(); ts_pop_de2(&t1, &t2); bt = bt_op(TYPE_BT(&t1), TYPE_BT(&t2)); o_bop(O_MK(op, bt)); ts_push_bt(SINT); } static void readcmp(void) { readshift(); while (1) { if (!tok_jmp("<")) { cmp(O_LT); continue; } if (!tok_jmp(">")) { cmp(O_GT); continue; } if (!tok_jmp("<=")) { cmp(O_LE); continue; } if (!tok_jmp(">=")) { cmp(O_GE); continue; } break; } } static void eq(int op) { readcmp(); ts_pop_de2(NULL, NULL); o_bop(op); ts_push_bt(SINT); } static void readeq(void) { readcmp(); while (1) { if (!tok_jmp("==")) { eq(O_EQ); continue; } if (!tok_jmp("!=")) { eq(O_NE); continue; } break; } } static void readbitand(void) { readeq(); while (!tok_jmp("&")) { readeq(); ts_binop(O_AND); } } static void readxor(void) { readbitand(); while (!tok_jmp("^")) { readbitand(); ts_binop(O_XOR); } } static void readbitor(void) { readxor(); while (!tok_jmp("|")) { readxor(); ts_binop(O_OR); } } static void savelocal(long val, int bt) { o_local(val); o_tmpswap(); o_assign(bt); o_tmpdrop(1); } static void loadlocal(long val, int bt) { o_local(val); o_deref(bt); o_rmlocal(val, bt); } static void readand(void) { int l_out, l_fail; long val; readbitor(); if (!tok_comes("&&")) return; val = o_mklocal(UINT); l_out = LABEL(); l_fail = LABEL(); ts_pop_de(NULL); o_jz(l_fail); while (!tok_jmp("&&")) { readbitor(); ts_pop_de(NULL); o_jz(l_fail); } o_num(1); savelocal(val, UINT); o_jmp(l_out); o_label(l_fail); o_num(0); savelocal(val, UINT); o_label(l_out); loadlocal(val, SINT); ts_push_bt(SINT); } static void reador(void) { int l_pass, l_end; long val; readand(); if (!tok_comes("||")) return; val = o_mklocal(UINT); l_pass = LABEL(); l_end = LABEL(); ts_pop_de(NULL); o_uop(O_LNOT); o_jz(l_pass); while (!tok_jmp("||")) { readand(); ts_pop_de(NULL); o_uop(O_LNOT); o_jz(l_pass); } o_num(0); savelocal(val, SINT); o_jmp(l_end); o_label(l_pass); o_num(1); savelocal(val, SINT); o_label(l_end); loadlocal(val, SINT); ts_push_bt(SINT); } static void readcexpr(void); static int readcexpr_const(void) { long c, m = 0; if (o_popnum(&c)) return -1; if (!c) m = o_mark(); readcexpr(); /* both branches yield the same type; so ignore the first */ ts_pop_de(NULL); tok_req(":"); if (!c) { o_back(m); o_tmpdrop(1); } if (c) m = o_mark(); readcexpr(); /* making sure t->addr == 0 on both branches */ ts_de(1); if (c) { o_back(m); o_tmpdrop(1); } return 0; } static void readcexpr(void) { reador(); if (tok_jmp("?")) return; ncexpr++; ts_pop_de(NULL); if (readcexpr_const()) { long val = 0; int l_fail = LABEL(); int l_end = LABEL(); struct type ret; o_jz(l_fail); readcexpr(); /* both branches yield the same type; so ignore the first */ ts_pop_de(&ret); if (!TYPE_VOID(&ret)) { val = o_mklocal(ULNG); savelocal(val, ULNG); } o_jmp(l_end); tok_req(":"); o_label(l_fail); readcexpr(); /* making sure t->addr == 0 on both branches */ ts_de(1); if (!TYPE_VOID(&ret)) { savelocal(val, ULNG); } o_label(l_end); if (!TYPE_VOID(&ret)) { loadlocal(val, ULNG); } } ncexpr--; } static void opassign(int op, int ptrop) { struct type t = ts[nts - 1]; o_tmpcopy(); ts_push(&t); readexpr(); if (op == O_ADD || op == O_SUB) ts_addop(op); else ts_binop(op); o_assign(TYPE_BT(&ts[nts - 2])); ts_pop(NULL); ts_de(0); } static void doassign(void) { struct type t = ts[nts - 1]; if (!t.ptr && t.flags & T_STRUCT) { ts_pop(NULL); o_num(type_totsz(&t)); o_memcpy(); } else { ts_pop_de(NULL); o_assign(TYPE_BT(&ts[nts - 1])); ts_de(0); } } static void readexpr(void) { readcexpr(); if (!tok_jmp("=")) { readexpr(); doassign(); return; } if (!tok_jmp("+=")) { opassign(O_ADD, 1); return; } if (!tok_jmp("-=")) { opassign(O_SUB, 1); return; } if (!tok_jmp("*=")) { opassign(O_MUL, 0); return; } if (!tok_jmp("/=")) { opassign(O_DIV, 0); return; } if (!tok_jmp("%=")) { opassign(O_MOD, 0); return; } if (!tok_jmp("<<=")) { opassign(O_SHL, 0); return; } if (!tok_jmp(">>=")) { opassign(O_SHR, 0); return; } if (!tok_jmp("&=")) { opassign(O_AND, 0); return; } if (!tok_jmp("|=")) { opassign(O_OR, 0); return; } if (!tok_jmp("^=")) { opassign(O_XOR, 0); return; } } static void readestmt(void) { do { o_tmpdrop(-1); nts = 0; readexpr(); } while (!tok_jmp(",")); } #define F_GLOBAL(flags) (!((flags) & F_STATIC)) static void globalinit(void *obj, int off, struct type *t) { struct name *name = obj; char *elfname = *name->elfname ? name->elfname : name->name; if (t->flags & T_ARRAY && tok_grp() == '"') { struct type *t_de = &arrays[t->id].type; if (!t_de->ptr && !t_de->flags && TYPE_SZ(t_de) == 1) { char *buf = tok_get() + 1; int len = tok_len() - 2; buf[len] = '\0'; o_dscpy(name->addr + off, buf, len + 1); return; } } readexpr(); ts_de(1); o_dsset(elfname, off, TYPE_BT(t)); ts_pop(NULL); } static void readfunc(struct name *name, int flags); static void globaldef(long data, struct name *name, unsigned flags) { struct type *t = &name->type; char *elfname = *name->elfname ? name->elfname : name->name; int sz; if (t->flags & T_ARRAY && !t->ptr && !arrays[t->id].n) if (~flags & F_EXTERN) arrays[t->id].n = initsize(); sz = type_totsz(t); if (!(flags & F_EXTERN) && (!(t->flags & T_FUNC) || t->ptr)) { if (tok_comes("=")) name->addr = o_dsnew(elfname, sz, F_GLOBAL(flags)); else o_bsnew(elfname, sz, F_GLOBAL(flags)); } global_add(name); if (!tok_jmp("=")) initexpr(t, 0, name, globalinit); if (tok_comes("{") && name->type.flags & T_FUNC) readfunc(name, flags); } /* generate the address of local + off */ static void o_localoff(long addr, int off) { o_local(addr); if (off) { o_num(off); o_bop(O_ADD); } } static void localinit(void *obj, int off, struct type *t) { long addr = *(long *) obj; if (t->flags & T_ARRAY && tok_grp() == '"') { struct type *t_de = &arrays[t->id].type; if (!t_de->ptr && !t_de->flags && TYPE_SZ(t_de) == 1) { char *buf = tok_get() + 1; int len = tok_len() - 2; o_localoff(addr, off); o_sym(tmp_str(buf, len)); o_num(len + 1); o_memcpy(); o_tmpdrop(1); return; } } o_localoff(addr, off); ts_push(t); readexpr(); doassign(); ts_pop(NULL); o_tmpdrop(1); } /* current function name */ static char func_name[NAMELEN]; static void localdef(long data, struct name *name, unsigned flags) { struct type *t = &name->type; if ((flags & F_EXTERN) || ((t->flags & T_FUNC) && !t->ptr)) { global_add(name); return; } if (flags & F_STATIC) { sprintf(name->elfname, "__neatcc.%s.%s", func_name, name->name); globaldef(data, name, flags); return; } if (t->flags & T_ARRAY && !t->ptr && !arrays[t->id].n) arrays[t->id].n = initsize(); name->addr = o_mklocal(type_totsz(&name->type)); local_add(name); if (!tok_jmp("=")) { /* this is not necessary for "struct x = y" */ if (t->flags & (T_ARRAY | T_STRUCT) && !t->ptr) { o_local(name->addr); o_num(0); o_num(type_totsz(t)); o_memset(); o_tmpdrop(1); } initexpr(t, 0, &name->addr, localinit); } } static void typedefdef(long data, struct name *name, unsigned flags) { typedef_add(name->name, &name->type); } static void readstmt(void); static void readswitch(void) { int o_break = l_break; long val_addr = o_mklocal(ULNG); struct type t; int ncases = 0; /* number of case labels */ int l_failed = LABEL(); /* address of last failed jmp */ int l_matched = LABEL(); /* address of last walk through jmp */ int l_default = 0; /* default case label */ l_break = LABEL(); tok_req("("); readexpr(); ts_pop_de(&t); o_local(val_addr); o_tmpswap(); o_assign(TYPE_BT(&t)); o_tmpdrop(1); tok_req(")"); tok_req("{"); while (tok_jmp("}")) { if (!tok_comes("case") && !tok_comes("default")) { readstmt(); continue; } if (ncases) o_jmp(l_matched); if (!strcmp("case", tok_get())) { o_label(l_failed); l_failed = LABEL(); caseexpr = 1; readexpr(); ts_pop_de(NULL); caseexpr = 0; o_local(val_addr); o_deref(TYPE_BT(&t)); o_bop(O_EQ); o_jz(l_failed); o_tmpdrop(1); } else { if (!ncases) o_jmp(l_failed); l_default = LABEL(); o_label(l_default); } tok_req(":"); o_label(l_matched); l_matched = LABEL(); ncases++; } o_rmlocal(val_addr, ULNG); o_jmp(l_break); o_label(l_failed); if (l_default) o_jmp(l_default); o_label(l_break); l_break = o_break; } static char (*label_name)[NAMELEN]; static int *label_ids; static int label_n, label_sz; static int label_id(char *name) { int i; if (label_n >= label_sz) { label_sz = MAX(128, label_sz * 2); label_name = mextend(label_name, label_n, label_sz, sizeof(label_name[0])); label_ids = mextend(label_ids, label_n, label_sz, sizeof(label_ids[0])); } for (i = label_n - 1; i >= 0; --i) if (!strcmp(label_name[i], name)) return label_ids[i]; strcpy(label_name[label_n], name); label_ids[label_n] = LABEL(); return label_ids[label_n++]; } static void readstmt(void) { o_tmpdrop(-1); nts = 0; if (!tok_jmp("{")) { int _nlocals = locals_n; int _nglobals = globals_n; int _nenums = enums_n; int _ntypedefs = typedefs_n; int _nstructs = structs_n; int _nfuncs = funcs_n; int _narrays = arrays_n; while (tok_jmp("}")) readstmt(); locals_n = _nlocals; enums_n = _nenums; typedefs_n = _ntypedefs; structs_n = _nstructs; funcs_n = _nfuncs; arrays_n = _narrays; globals_n = _nglobals; return; } if (!readdefs(localdef, 0)) { tok_req(";"); return; } if (!tok_jmp("typedef")) { readdefs(typedefdef, 0); tok_req(";"); return; } if (!tok_jmp("if")) { int l_fail = LABEL(); int l_end = LABEL(); tok_req("("); readestmt(); tok_req(")"); ts_pop_de(NULL); o_jz(l_fail); readstmt(); if (!tok_jmp("else")) { o_jmp(l_end); o_label(l_fail); readstmt(); o_label(l_end); } else { o_label(l_fail); } return; } if (!tok_jmp("while")) { int o_break = l_break; int o_cont = l_cont; l_break = LABEL(); l_cont = LABEL(); o_label(l_cont); tok_req("("); readestmt(); tok_req(")"); ts_pop_de(NULL); o_jz(l_break); readstmt(); o_jmp(l_cont); o_label(l_break); l_break = o_break; l_cont = o_cont; return; } if (!tok_jmp("do")) { int o_break = l_break; int o_cont = l_cont; int l_beg = LABEL(); l_break = LABEL(); l_cont = LABEL(); o_label(l_beg); readstmt(); tok_req("while"); tok_req("("); o_label(l_cont); readexpr(); ts_pop_de(NULL); o_uop(O_LNOT); o_jz(l_beg); tok_req(")"); o_label(l_break); tok_req(";"); l_break = o_break; l_cont = o_cont; return; } if (!tok_jmp("for")) { int o_break = l_break; int o_cont = l_cont; int l_check = LABEL(); /* for condition label */ int l_body = LABEL(); /* for block label */ l_cont = LABEL(); l_break = LABEL(); tok_req("("); if (!tok_comes(";")) readestmt(); tok_req(";"); o_label(l_check); if (!tok_comes(";")) { readestmt(); ts_pop_de(NULL); o_jz(l_break); } tok_req(";"); o_jmp(l_body); o_label(l_cont); if (!tok_comes(")")) readestmt(); tok_req(")"); o_jmp(l_check); o_label(l_body); readstmt(); o_jmp(l_cont); o_label(l_break); l_break = o_break; l_cont = o_cont; return; } if (!tok_jmp("switch")) { readswitch(); return; } if (!tok_jmp("return")) { int ret = !tok_comes(";"); if (ret) { readexpr(); ts_pop_de(NULL); } tok_req(";"); o_ret(ret); return; } if (!tok_jmp("break")) { tok_req(";"); o_jmp(l_break); return; } if (!tok_jmp("continue")) { tok_req(";"); o_jmp(l_cont); return; } if (!tok_jmp("goto")) { o_jmp(label_id(tok_get())); tok_req(";"); return; } readestmt(); /* labels */ if (!tok_jmp(":")) { o_label(label_id(tok_previden)); return; } tok_req(";"); } static void readfunc(struct name *name, int flags) { struct funcinfo *fi = &funcs[name->type.id]; int i; strcpy(func_name, fi->name); o_func_beg(func_name, fi->nargs, F_GLOBAL(flags), fi->varg); for (i = 0; i < fi->nargs; i++) { struct name arg = {"", "", fi->args[i], o_arg2loc(i)}; strcpy(arg.name, fi->argnames[i]); local_add(&arg); } label = 0; label_n = 0; readstmt(); o_func_end(); func_name[0] = '\0'; locals_n = 0; } static void readdecl(void) { if (!tok_jmp("typedef")) { readdefs(typedefdef, 0); tok_req(";"); return; } readdefs_int(globaldef, 0); tok_jmp(";"); } static void parse(void) { while (tok_jmp("")) readdecl(); } static void compat_macros(void) { cpp_define("__STDC__", ""); cpp_define("__linux__", ""); cpp_define(I_ARCH, ""); cpp_define("__neatcc__", ""); /* ignored keywords */ cpp_define("const", ""); cpp_define("register", ""); cpp_define("volatile", ""); cpp_define("inline", ""); cpp_define("restrict", ""); cpp_define("__inline__", ""); cpp_define("__restrict__", ""); cpp_define("__attribute__(x)", ""); cpp_define("__builtin_va_list__", "long"); } static int ncc_opt = 2; /* return one if the given optimization level is enabled */ int opt(int level) { return level <= ncc_opt; } int main(int argc, char *argv[]) { char obj[128] = ""; int ofd = 1; int cpp = 0; int i; compat_macros(); for (i = 1; i < argc && argv[i][0] == '-'; i++) { if (argv[i][1] == 'I') cpp_path(argv[i][2] ? argv[i] + 2 : argv[++i]); if (argv[i][1] == 'O') ncc_opt = argv[i][2] ? atoi(argv[i] + 2) : 2; if (argv[i][1] == 'E') cpp = 1; if (argv[i][1] == 'D') { char *name = argv[i] + 2; char *def = ""; char *eq = strchr(name, '='); if (eq) { *eq = '\0'; def = eq + 1; } cpp_define(name, def); } if (argv[i][1] == 'o') strcpy(obj, argv[i][2] ? argv[i] + 2 : argv[++i]); if (argv[i][1] == 'h') { printf("Usage: %s [options] source\n", argv[0]); printf("\n"); printf("Options:\n"); printf(" -I dir \tspecify a header directory\n"); printf(" -o out \tspecify output file name\n"); printf(" -E \tpreprocess only\n"); printf(" -Dname=val \tdefine a macro\n"); printf(" -On \toptimize (-O0 to disable)\n"); return 0; } } if (i == argc) die("neatcc: no file given\n"); if (cpp_init(argv[i])) die("neatcc: cannot open <%s>\n", argv[i]); if (cpp) { long clen; char *cbuf; if (*obj) ofd = open(obj, O_WRONLY | O_TRUNC | O_CREAT, 0600); while (!cpp_read(&cbuf, &clen)) write(ofd, cbuf, clen); if (*obj) close(ofd); return 0; } out_init(0); parse(); if (!*obj) { char *cp = strrchr(argv[i], '/'); strcpy(obj, cp ? cp + 1 : argv[i]); obj[strlen(obj) - 1] = 'o'; } free(locals); free(globals); free(label_name); free(label_ids); free(funcs); free(typedefs); free(structs); free(arrays); tok_done(); ofd = open(obj, O_WRONLY | O_TRUNC | O_CREAT, 0600); o_write(ofd); close(ofd); return 0; } /* parsing function and variable declarations */ /* read the base type of a variable */ static int basetype(struct type *type, unsigned *flags) { int sign = 1; int size = UINT; int done = 0; int i = 0; int isunion; char name[NAMELEN] = ""; *flags = 0; type->flags = 0; type->ptr = 0; type->addr = 0; while (!done) { if (!tok_jmp("static")) { *flags |= F_STATIC; } else if (!tok_jmp("extern")) { *flags |= F_EXTERN; } else if (!tok_jmp("void")) { sign = 0; size = 0; done = 1; } else if (!tok_jmp("int")) { done = 1; } else if (!tok_jmp("char")) { size = UCHR; done = 1; } else if (!tok_jmp("short")) { size = USHT; } else if (!tok_jmp("long")) { size = ULNG; } else if (!tok_jmp("signed")) { sign = 1; } else if (!tok_jmp("unsigned")) { sign = 0; } else if (tok_comes("union") || tok_comes("struct")) { isunion = !strcmp("union", tok_get()); if (tok_grp() == 'a') strcpy(name, tok_get()); if (tok_comes("{")) type->id = struct_create(name, isunion); else type->id = struct_find(name, isunion); type->flags |= T_STRUCT; type->bt = ULNG; return 0; } else if (!tok_jmp("enum")) { if (tok_grp() == 'a') tok_get(); if (tok_comes("{")) enum_create(); type->bt = SINT; return 0; } else { if (tok_grp() == 'a') { int id = typedef_find(tok_see()); if (id != -1) { tok_get(); memcpy(type, &typedefs[id].type, sizeof(*type)); return 0; } } if (!i) return 1; done = 1; continue; } i++; } type->bt = size | (sign ? T_MSIGN : 0); return 0; } static void readptrs(struct type *type) { while (!tok_jmp("*")) { type->ptr++; if (!type->bt) type->bt = 1; } } /* read function arguments */ static int readargs(struct type *args, char argnames[][NAMELEN], int *varg) { int nargs = 0; tok_req("("); *varg = 0; while (!tok_comes(")")) { if (!tok_jmp("...")) { *varg = 1; break; } if (readname(&args[nargs], argnames[nargs], NULL)) { /* argument has no type, assume int */ memset(&args[nargs], 0, sizeof(struct type)); args[nargs].bt = SINT; strcpy(argnames[nargs], tok_get()); } /* argument arrays are pointers */ array2ptr(&args[nargs]); nargs++; if (tok_jmp(",")) break; } tok_req(")"); /* void argument */ if (nargs == 1 && !TYPE_BT(&args[0])) return 0; return nargs; } /* read K&R function arguments */ static void krdef(long data, struct name *name, unsigned flags) { struct funcinfo *fi = &funcs[data]; int i; for (i = 0; i < fi->nargs; i++) if (!strcmp(fi->argnames[i], name->name)) memcpy(&fi->args[i], &name->type, sizeof(name->type)); } /* * readarrays() parses array specifiers when reading a definition in * readname(). The "type" parameter contains the type contained in the * inner array; for instance, type in "int *a[10][20]" would be an int * pointer. When returning, the "type" parameter is changed to point * to the final array. The function returns a pointer to the type in * the inner array; this is useful when the type is not complete yet, * like when creating an array of function pointers as in * "int (*f[10])(int)". If there is no array brackets, NULL is returned. */ static struct type *readarrays(struct type *type) { long arsz[16]; struct type *inner = NULL; int nar = 0; int i; while (!tok_jmp("[")) { long n = 0; if (tok_jmp("]")) { readexpr(); ts_pop_de(NULL); if (o_popnum(&n)) err("const expr expected\n"); tok_req("]"); } arsz[nar++] = n; } for (i = nar - 1; i >= 0; i--) { type->id = array_add(type, arsz[i]); if (!inner) inner = &arrays[type->id].type; type->flags = T_ARRAY; type->bt = ULNG; type->ptr = 0; } return inner; } static struct type *innertype(struct type *t) { while (t->flags & T_ARRAY && !t->ptr) t = &arrays[t->id].type; return t; } static void innertype_modify(struct type *t, struct type *s) { struct type *inner = innertype(t); int ptr = inner->ptr; memcpy(inner, s, sizeof(*inner)); inner->ptr = ptr; } /* * readname() reads a variable definition; the name is copied into * "name" and the type is copied into "main" argument. The "base" * argument, if not NULL, indicates the base type of the variable. * For instance, the base type of "a" and "b" in "int *a, b[10]" is * "int". If NULL, basetype() is called directly to read the base * type of the variable. readname() returns zero, only if the * variable can be read. */ static int readname(struct type *main, char *name, struct type *base) { struct type type; /* the main type */ struct type btype; /* type before parenthesis; e.g. "int *" in "int *(*p)[10] */ int paren; unsigned flags; if (name) *name = '\0'; if (!base) { if (basetype(&type, &flags)) return 1; } else { type = *base; } readptrs(&type); paren = !tok_jmp("("); if (paren) { btype = type; readptrs(&type); } if (tok_grp() == 'a' && name) strcpy(name, tok_get()); readarrays(&type); if (paren) tok_req(")"); if (tok_comes("(")) { struct type args[NARGS]; char argnames[NARGS][NAMELEN]; int varg = 0; int nargs = readargs(args, argnames, &varg); struct type rtype = type; /* return type */ struct type ftype = {0}; /* function type */ if (paren) rtype = btype; ftype.flags = T_FUNC; ftype.bt = ULNG; ftype.id = func_create(&rtype, name, argnames, args, nargs, varg); if (paren) innertype_modify(&type, &ftype); else type = ftype; if (!tok_comes(";")) while (!tok_comes("{") && !readdefs(krdef, type.id)) tok_req(";"); } else { if (paren && readarrays(&btype)) innertype_modify(&type, &btype); } *main = type; return 0; } static int readtype(struct type *type) { return readname(type, NULL, NULL); } /* * readdefs() reads a variable definition statement. The definition * can appear in different contexts: global variables, function * local variables, struct fields, and typedefs. For each defined * variable, def() callback is called with the appropriate name * struct and flags; the callback should finish parsing the definition * by possibly reading the initializer expression and saving the name * struct. */ static int readdefs(void (*def)(long data, struct name *name, unsigned flags), long data) { struct type base; unsigned base_flags; if (basetype(&base, &base_flags)) return 1; if (tok_comes(";") || tok_comes("{")) return 0; do { struct name name = {{""}}; if (readname(&name.type, name.name, &base)) break; def(data, &name, base_flags); } while (!tok_jmp(",")); return 0; } /* just like readdefs, but default to int type; for handling K&R functions */ static int readdefs_int(void (*def)(long data, struct name *name, unsigned flags), long data) { struct type base; unsigned flags = 0; int nobase = 0; if (basetype(&base, &flags)) { nobase = 1; if (tok_grp() != 'a') return 1; memset(&base, 0, sizeof(base)); base.bt = SINT; } if (!tok_comes(";")) { do { struct name name = {{""}}; if (readname(&name.type, name.name, &base)) break; if (nobase && tok_grp() == 'a') err("type missing!\n"); def(data, &name, flags); } while (!tok_jmp(",")); } return 0; } /* parsing initializer expressions */ static void jumpbrace(void) { int depth = 0; while (!tok_comes("}") || depth--) if (!strcmp("{", tok_get())) depth++; tok_req("}"); } /* compute the size of the initializer expression */ static int initsize(void) { long addr = tok_addr(); int n = 0; if (tok_jmp("=")) return 0; if (tok_grp() == '"') { tok_get(); n = tok_len() - 2 + 1; tok_jump(addr); return n; } tok_req("{"); while (tok_jmp("}")) { long idx = n; if (!tok_jmp("[")) { readexpr(); ts_pop_de(NULL); o_popnum(&idx); tok_req("]"); tok_req("="); } if (n < idx + 1) n = idx + 1; while (!tok_comes("}") && !tok_comes(",")) if (!strcmp("{", tok_get())) jumpbrace(); tok_jmp(","); } tok_jump(addr); return n; } /* read the initializer expression and initialize basic types using set() cb */ static void initexpr(struct type *t, int off, void *obj, void (*set)(void *obj, int off, struct type *t)) { if (tok_jmp("{")) { set(obj, off, t); return; } if (!t->ptr && t->flags & T_STRUCT) { struct structinfo *si = &structs[t->id]; int i; for (i = 0; i < si->nfields && !tok_comes("}"); i++) { struct name *field = &si->fields[i]; if (!tok_jmp(".")) { field = struct_field(t->id, tok_get()); tok_req("="); } initexpr(&field->type, off + field->addr, obj, set); if (tok_jmp(",")) break; } } else if (t->flags & T_ARRAY) { struct type t_de = arrays[t->id].type; int i; /* handling extra braces as in: char s[] = {"sth"} */ if (TYPE_SZ(&t_de) == 1 && tok_grp() == '"') { set(obj, off, t); tok_req("}"); return; } for (i = 0; !tok_comes("}"); i++) { long idx = i; struct type it = t_de; if (!tok_jmp("[")) { readexpr(); ts_pop_de(NULL); o_popnum(&idx); tok_req("]"); tok_req("="); } if (!tok_comes("{") && (tok_grp() != '"' || !(it.flags & T_ARRAY))) it = *innertype(&t_de); initexpr(&it, off + type_totsz(&it) * idx, obj, set); if (tok_jmp(",")) break; } } tok_req("}"); } ================================================ FILE: ncc.h ================================================ /* * THE NEATCC C COMPILER * * This header file is organized as follows: * * 0. helper functions and data structures * 1. ncc.c -> tok.c: the interface for reading tokens * 2. ncc.c -> int.c: the interface for generating the intermediate code * 3. int.c -> gen.c: the intermediate code * 4. gen.c -> x64.c: the interface for generating the final code * 5. gen.c -> out.c: the interface for generating object files */ /* SECTION ZERO: Helper Functions */ /* predefined array limits; (p.f. means per function) */ #define NARGS 32 /* number of function/macro arguments */ #define NTMPS 64 /* number of expression temporaries */ #define NFIELDS 128 /* number of fields in structs */ #define NAMELEN 128 /* size of identifiers */ #define NDEFS 4096 /* number of macros */ #define MARGLEN 1024 /* size of macro arguments */ #define MDEFLEN 2048 /* size of macro definitions */ #define NBUFS 32 /* macro expansion stack depth */ #define NLOCS 1024 /* number of header search paths */ #define LEN(a) (sizeof(a) / sizeof((a)[0])) #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) < (b) ? (b) : (a)) void *mextend(void *old, long oldsz, long newsz, long memsz); void die(char *msg, ...); void err(char *fmt, ...); int opt(int level); /* variable length buffer */ struct mem { char *s; /* allocated buffer */ long sz; /* buffer size */ long n; /* length of data stored in s */ }; void mem_init(struct mem *mem); void mem_done(struct mem *mem); void mem_cut(struct mem *mem, long pos); void *mem_buf(struct mem *mem); void mem_put(struct mem *mem, void *buf, long len); void mem_putc(struct mem *mem, int c); void mem_putz(struct mem *mem, long sz); void mem_cpy(struct mem *mem, long off, void *buf, long len); long mem_len(struct mem *mem); void *mem_get(struct mem *mem); /* SECTION ONE: Tokenisation */ void tok_init(char *path); void tok_done(void); char *tok_see(void); /* return the current token; a static buffer */ char *tok_get(void); /* return and consume the current token */ long tok_len(void); /* the length of the last token */ long tok_num(char *tok, long *n); long tok_addr(void); void tok_jump(long addr); int cpp_init(char *path); void cpp_path(char *s); void cpp_define(char *name, char *def); char *cpp_loc(long addr); int cpp_read(char **buf, long *len); /* SECTION TWO: Intermediate Code Generation */ /* basic type meaning */ #define T_MSIZE 0x000f #define T_MSIGN 0x0010 #define T_SZ(bt) ((bt) & T_MSIZE) #define T_SG(bt) ((bt) & T_MSIGN) #define T_MK(sign, size) (((sign) & T_MSIGN) | ((size) & T_MSIZE)) /* number of bytes in basic types */ #define ULNG (LONGSZ) #define UINT (4) #define USHT (2) #define UCHR (1) /* basic types */ #define SLNG (ULNG | T_MSIGN) #define SINT (UINT | T_MSIGN) #define SSHT (USHT | T_MSIGN) #define SCHR (UCHR | T_MSIGN) /* * Intermediate instruction operands * R: register, N: immediate, S: symbol, L: local, * D: displacement, G: label, C: arguments */ /* Instruction rd r1 r2 r3 */ #define O_ADD 0x000010 /* R R RN - */ #define O_SHL 0x000020 /* R R RN - */ #define O_MUL 0x000040 /* R R RN - */ #define O_CMP 0x000080 /* R R RN - */ #define O_UOP 0x000100 /* R R - - */ #define O_CALL 0x000200 /* R RS D C */ #define O_MOV 0x000400 /* R RNSL D - */ #define O_MEM 0x000800 /* - R R R */ #define O_JMP 0x001000 /* - - - G */ #define O_JZ 0x002000 /* - R - G */ #define O_JCC 0x004000 /* - R RN G */ #define O_RET 0x008000 /* - R - - */ #define O_LD 0x010000 /* R RSL D - */ #define O_ST 0x020000 /* - R RSL D */ /* opcode flags: num, loc, sym */ #define O_NUM 0x100000 /* instruction immediate */ #define O_LOC 0x200000 /* local (frame pointer displacement) */ #define O_SYM 0x400000 /* symbols (relocations and offset) */ /* other members of instruction groups */ #define O_SUB (1 | O_ADD) #define O_AND (2 | O_ADD) #define O_OR (3 | O_ADD) #define O_XOR (4 | O_ADD) #define O_SHR (1 | O_SHL) #define O_DIV (1 | O_MUL) #define O_MOD (2 | O_MUL) #define O_LT (0 | O_CMP) #define O_GE (1 | O_CMP) #define O_EQ (2 | O_CMP) #define O_NE (3 | O_CMP) #define O_LE (4 | O_CMP) #define O_GT (5 | O_CMP) #define O_NEG (0 | O_UOP) #define O_NOT (1 | O_UOP) #define O_LNOT (2 | O_UOP) #define O_MSET (0 | O_MEM) #define O_MCPY (1 | O_MEM) #define O_JNZ (1 | O_JZ) /* instruction masks */ #define O_BOP (O_ADD | O_MUL | O_CMP | O_SHL) #define O_OUT (O_BOP | O_UOP | O_CALL | O_MOV | O_LD) #define O_JXX (O_JMP | O_JZ | O_JCC) /* instruction operand type */ #define O_C(op) ((op) & 0xffffff) /* operation code */ #define O_T(op) ((op) >> 24) /* instruction operand type */ #define O_MK(op, bt) ((op) | ((bt) << 24)) /* operations on the stack */ void o_bop(long op); /* binary operation */ void o_uop(long op); /* unary operation */ void o_cast(long bt); void o_memcpy(void); void o_memset(void); void o_call(int argc, int ret); void o_ret(int ret); void o_assign(long bt); void o_deref(long bt); void o_load(void); int o_popnum(long *num); int o_popsym(long *sym, long *off); /* pushing values to the stack */ void o_num(long n); void o_local(long addr); void o_sym(char *sym); void o_tmpdrop(int n); void o_tmpswap(void); void o_tmpcopy(void); /* handling locals */ long o_mklocal(long size); void o_rmlocal(long addr, long sz); long o_arg2loc(int i); /* branches */ void o_label(long id); void o_jmp(long id); void o_jz(long id); long o_mark(void); void o_back(long mark); /* data/bss sections */ long o_dsnew(char *name, long size, int global); void o_dscpy(long addr, void *buf, long len); void o_dsset(char *name, long off, long bt); void o_bsnew(char *name, long size, int global); /* functions */ void o_func_beg(char *name, int argc, int global, int vararg); void o_func_end(void); void o_code(char *name, char *c, long c_len); /* output */ void o_write(int fd); /* SECTION THREE: The Intermediate Code */ /* intermediate code instructions */ struct ic { long op; /* instruction opcode */ long a1; /* first argument */ long a2; /* second argument */ long a3; /* more information, like jump target */ long *args; /* call arguments */ }; /* get the generated intermediate code */ void ic_get(struct ic **c, long *n); int ic_num(struct ic *ic, long iv, long *num); int ic_sym(struct ic *ic, long iv, long *sym, long *off); long *ic_lastuse(struct ic *ic, long ic_n); void ic_free(struct ic *ic); int ic_regcnt(struct ic *ic); /* global register allocation */ void reg_init(struct ic *ic, long ic_n); long reg_mask(void); int reg_lmap(long ic, long loc); int reg_rmap(long ic, long reg); int reg_safe(long loc); void reg_done(void); /* SECTION FOUR: Final Code Generation */ /* * To make maintaining different architectures easier and to unify the * optimizations, I have merged the code generation for different * architectures. The i_*() functions are now the low level * architecture-specific code generation entry points. The * differences between RISC and CISC architectures, actually the * annoying asymmetry in CISC architecture, has made this interface * more complex than it could have ideally been. Nevertheless, * the benefits of extracting gen.c and the cleaner design, * especially with the presence of the optimizations, outweighs the * added complexity. Overall, there were many challenges for * extracting gen.c including: * + Different register sets; caller/callee saved and argument registers * + CISC-style instructions that work on limited registers and parameters * + Different instruction formats and immediate value limitations * + Generating epilog, prolog, and local variable addresses when optimizing * * I tried to make this interface as small as possible. The key * functions and macros described next. * * i_reg() returns the mask of allowed registers for each * operand of an instruction. The first argument op, specifies * the instruction (O_* macros); i_reg() sets the value r0, r1, * and r2 to indicate the mask of acceptable registers for the * first, second, and third operands of the instruction. * The value of these masks may be changed to zero to indicate * fewer than three operands. If md is zero while m1 is not, * the destination register should be equal to the first register, * as in CISC architectures. mt denotes the mask of registers * that may lose their contents after the instruction. * * i_ins() generates code for the given instruction. The arguments * indicate the instruction and its operands. The code is generated * by calling os() and oi() functions and the current position in * the code segment is obtained by calling opos(). For branch * instructions, i_ins() returns the position of branch offset in * code segment, to be filled later with i_fill(). * * Some macros should be defined in architecture-dependent headers * and a few variables should be defined for each architecture, * such as tmpregs, which is an array of register numbers that * can be used for holding temporaries and argregs, which is an * array of register numbers for holding the first N_ARGS arguments. * Consult x64.h as an example, for the macros defined for each * architecture. * */ #ifdef NEATCC_ARM #include "arm.h" #endif #ifdef NEATCC_X64 #include "x64.h" #endif #ifdef NEATCC_X86 #include "x86.h" #endif /* architecture-specific operations */ long i_reg(long op, long *rd, long *r1, long *r2, long *r3, long *mt); long i_ins(long op, long rd, long r1, long r2, long r3); int i_imm(long lim, long n); void i_label(long id); void i_wrap(int argc, long sargs, long spsub, int initfp, long sregs, long sregs_pos); void i_code(char **c, long *c_len, long **rsym, long **rflg, long **roff, long *rcnt); void i_done(void); extern int tmpregs[]; extern int argregs[]; /* SECTION FIVE: Object File Generation */ #define OUT_CS 0x0001 /* code segment symbol */ #define OUT_DS 0x0002 /* data segment symbol */ #define OUT_BSS 0x0004 /* bss segment symbol */ #define OUT_GLOB 0x0010 /* global symbol */ #define OUT_RLREL 0x0020 /* relative relocation */ #define OUT_RLSX 0x0040 /* sign extend relocation */ #define OUT_RL24 0x0400 /* 3-byte relocation */ #define OUT_RL32 0x0800 /* 4-byte relocation */ #define OUT_ALIGNMENT 16 /* section alignment */ void out_init(long flags); long out_sym(char *name); void out_def(char *name, long flags, long off, long len); void out_rel(long id, long flags, long off); void out_write(int fd, char *cs, long cslen, char *ds, long dslen); ================================================ FILE: out.c ================================================ /* neatcc ELF object generation */ #include #include #include #include #include #include "ncc.h" #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) #define SEC_TEXT 1 #define SEC_REL 2 #define SEC_SYMS 3 #define SEC_SYMSTR 4 #define SEC_DAT 5 #define SEC_DATREL 6 #define SEC_BSS 7 #define NSECS 8 /* simplified elf struct and macro names */ #if LONGSZ == 8 # define USERELA 1 # define Elf_Ehdr Elf64_Ehdr # define Elf_Shdr Elf64_Shdr # define Elf_Sym Elf64_Sym # define Elf_Rel Elf64_Rela # define ELF_ST_INFO ELF64_ST_INFO # define ELF_ST_BIND ELF64_ST_BIND # define ELF_R_SYM ELF64_R_SYM # define ELF_R_TYPE ELF64_R_TYPE # define ELF_R_INFO ELF64_R_INFO #else # define USERELA 0 # define Elf_Ehdr Elf32_Ehdr # define Elf_Shdr Elf32_Shdr # define Elf_Sym Elf32_Sym # define Elf_Rel Elf32_Rel # define ELF_ST_INFO ELF32_ST_INFO # define ELF_ST_BIND ELF32_ST_BIND # define ELF_R_SYM ELF32_R_SYM # define ELF_R_TYPE ELF32_R_TYPE # define ELF_R_INFO ELF32_R_INFO #endif static Elf_Ehdr ehdr; static Elf_Shdr shdr[NSECS]; static Elf_Sym *syms; static long syms_n, syms_sz; static char *symstr; static long symstr_n, symstr_sz; static Elf_Rel *dsrel; static long dsrel_n, dsrel_sz; static Elf_Rel *csrel; static long csrel_n, csrel_sz; void err(char *msg, ...); static int rel_type(int flags); static void ehdr_init(Elf_Ehdr *ehdr); static long symstr_add(char *name) { long len = strlen(name) + 1; if (symstr_n + len >= symstr_sz) { symstr_sz = MAX(512, symstr_sz * 2); symstr = mextend(symstr, symstr_n, symstr_sz, sizeof(symstr[0])); } strcpy(symstr + symstr_n, name); symstr_n += len; return symstr_n - len; } static long sym_find(char *name) { int i; for (i = 0; i < syms_n; i++) if (!strcmp(name, symstr + syms[i].st_name)) return i; return -1; } static Elf_Sym *put_sym(char *name) { long found = sym_find(name); Elf_Sym *sym; if (found >= 0) return &syms[found]; if (syms_n >= syms_sz) { syms_sz = MAX(128, syms_sz * 2); syms = mextend(syms, syms_n, syms_sz, sizeof(syms[0])); } sym = &syms[syms_n++]; sym->st_name = symstr_add(name); sym->st_shndx = SHN_UNDEF; sym->st_info = ELF_ST_INFO(STB_GLOBAL, STT_FUNC); return sym; } #define SYMLOCAL(i) (ELF_ST_BIND(syms[i].st_info) == STB_LOCAL) static void mvrela(long *mv, Elf_Rel *rels, long n) { long i; for (i = 0; i < n; i++) { int sym = ELF_R_SYM(rels[i].r_info); int type = ELF_R_TYPE(rels[i].r_info); rels[i].r_info = ELF_R_INFO(mv[sym], type); } } static int syms_sort(void) { long *mv; int i, j; int glob_beg = 1; mv = malloc(syms_n * sizeof(mv[0])); for (i = 0; i < syms_n; i++) mv[i] = i; i = 1; j = syms_n - 1; while (1) { Elf_Sym t; while (i < j && SYMLOCAL(i)) i++; while (j >= i && !SYMLOCAL(j)) j--; if (i >= j) break; t = syms[j]; syms[j] = syms[i]; syms[i] = t; mv[i] = j; mv[j] = i; } glob_beg = j + 1; mvrela(mv, csrel, csrel_n); mvrela(mv, dsrel, dsrel_n); free(mv); return glob_beg; } void out_init(long flags) { put_sym(""); } /* return a symbol identifier */ void out_def(char *name, long flags, long off, long len) { Elf_Sym *sym = put_sym(name); int type = (flags & OUT_CS) ? STT_FUNC : STT_OBJECT; int bind = (flags & OUT_GLOB) ? STB_GLOBAL : STB_LOCAL; if (flags & OUT_CS) sym->st_shndx = SEC_TEXT; if (flags & OUT_DS) sym->st_shndx = SEC_DAT; if (flags & OUT_BSS) sym->st_shndx = SEC_BSS; sym->st_info = ELF_ST_INFO(bind, type); sym->st_value = off; sym->st_size = len; } long out_sym(char *name) { return put_sym(name) - syms; } static void out_csrel(long idx, long off, int flags) { Elf_Rel *r; if (csrel_n >= csrel_sz) { csrel_sz = MAX(128, csrel_sz * 2); csrel = mextend(csrel, csrel_n, csrel_sz, sizeof(csrel[0])); } r = &csrel[csrel_n++]; r->r_offset = off; r->r_info = ELF_R_INFO(idx, rel_type(flags)); } static void out_dsrel(long idx, long off, int flags) { Elf_Rel *r; if (dsrel_n >= dsrel_sz) { dsrel_sz = MAX(128, dsrel_sz * 2); dsrel = mextend(dsrel, dsrel_n, dsrel_sz, sizeof(dsrel[0])); } r = &dsrel[dsrel_n++]; r->r_offset = off; r->r_info = ELF_R_INFO(idx, rel_type(flags)); } void out_rel(long idx, long flags, long off) { if (flags & OUT_DS) out_dsrel(idx, off, flags); else out_csrel(idx, off, flags); } static long bss_len(void) { long len = 0; int i; for (i = 0; i < syms_n; i++) { long end = syms[i].st_value + syms[i].st_size; if (syms[i].st_shndx == SEC_BSS) if (len < end) len = end; } return len; } void out_write(int fd, char *cs, long cslen, char *ds, long dslen) { Elf_Shdr *text_shdr = &shdr[SEC_TEXT]; Elf_Shdr *rela_shdr = &shdr[SEC_REL]; Elf_Shdr *symstr_shdr = &shdr[SEC_SYMSTR]; Elf_Shdr *syms_shdr = &shdr[SEC_SYMS]; Elf_Shdr *dat_shdr = &shdr[SEC_DAT]; Elf_Shdr *datrel_shdr = &shdr[SEC_DATREL]; Elf_Shdr *bss_shdr = &shdr[SEC_BSS]; unsigned long offset = sizeof(ehdr); /* workaround for the idiotic gnuld; use neatld instead! */ text_shdr->sh_name = symstr_add(".cs"); rela_shdr->sh_name = symstr_add(USERELA ? ".rela.cs" : ".rels.cs"); dat_shdr->sh_name = symstr_add(".ds"); datrel_shdr->sh_name = symstr_add(USERELA ? ".rela.ds" : ".rels.ds"); ehdr.e_ident[0] = 0x7f; ehdr.e_ident[1] = 'E'; ehdr.e_ident[2] = 'L'; ehdr.e_ident[3] = 'F'; ehdr.e_ident[4] = LONGSZ == 8 ? ELFCLASS64 : ELFCLASS32; ehdr.e_ident[5] = ELFDATA2LSB; ehdr.e_ident[6] = EV_CURRENT; ehdr.e_type = ET_REL; ehdr_init(&ehdr); ehdr.e_version = EV_CURRENT; ehdr.e_ehsize = sizeof(ehdr); ehdr.e_shentsize = sizeof(shdr[0]); ehdr.e_shoff = offset; ehdr.e_shnum = NSECS; ehdr.e_shstrndx = SEC_SYMSTR; offset += sizeof(shdr[0]) * NSECS; text_shdr->sh_type = SHT_PROGBITS; text_shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC; text_shdr->sh_offset = offset; text_shdr->sh_size = cslen; text_shdr->sh_entsize = 1; text_shdr->sh_addralign = OUT_ALIGNMENT; offset += text_shdr->sh_size; rela_shdr->sh_type = USERELA ? SHT_RELA : SHT_REL; rela_shdr->sh_link = SEC_SYMS; rela_shdr->sh_info = SEC_TEXT; rela_shdr->sh_offset = offset; rela_shdr->sh_size = csrel_n * sizeof(csrel[0]); rela_shdr->sh_entsize = sizeof(csrel[0]); offset += rela_shdr->sh_size; syms_shdr->sh_type = SHT_SYMTAB; syms_shdr->sh_offset = offset; syms_shdr->sh_size = syms_n * sizeof(syms[0]); syms_shdr->sh_entsize = sizeof(syms[0]); syms_shdr->sh_link = SEC_SYMSTR; syms_shdr->sh_info = syms_sort(); offset += syms_shdr->sh_size; dat_shdr->sh_type = SHT_PROGBITS; dat_shdr->sh_flags = SHF_ALLOC | SHF_WRITE; dat_shdr->sh_offset = offset; dat_shdr->sh_size = dslen; dat_shdr->sh_entsize = 1; dat_shdr->sh_addralign = OUT_ALIGNMENT; offset += dat_shdr->sh_size; datrel_shdr->sh_type = USERELA ? SHT_RELA : SHT_REL; datrel_shdr->sh_offset = offset; datrel_shdr->sh_size = dsrel_n * sizeof(dsrel[0]); datrel_shdr->sh_entsize = sizeof(dsrel[0]); datrel_shdr->sh_link = SEC_SYMS; datrel_shdr->sh_info = SEC_DAT; offset += datrel_shdr->sh_size; bss_shdr->sh_type = SHT_NOBITS; bss_shdr->sh_flags = SHF_ALLOC | SHF_WRITE; bss_shdr->sh_offset = offset; bss_shdr->sh_size = bss_len(); bss_shdr->sh_entsize = 1; bss_shdr->sh_addralign = OUT_ALIGNMENT; symstr_shdr->sh_type = SHT_STRTAB; symstr_shdr->sh_offset = offset; symstr_shdr->sh_size = symstr_n; symstr_shdr->sh_entsize = 1; offset += symstr_shdr->sh_size; write(fd, &ehdr, sizeof(ehdr)); write(fd, shdr, NSECS * sizeof(shdr[0])); write(fd, cs, cslen); write(fd, csrel, csrel_n * sizeof(csrel[0])); write(fd, syms, syms_n * sizeof(syms[0])); write(fd, ds, dslen); write(fd, dsrel, dsrel_n * sizeof(dsrel[0])); write(fd, symstr, symstr_n); free(syms); free(symstr); free(csrel); free(dsrel); } /* architecture dependent functions */ #ifdef NEATCC_ARM static void ehdr_init(Elf_Ehdr *ehdr) { ehdr->e_machine = EM_ARM; ehdr->e_flags = EF_ARM_EABI_VER4; } static int rel_type(int flags) { if (flags & OUT_RL24) return R_ARM_PC24; return flags & OUT_RLREL ? R_ARM_REL32 : R_ARM_ABS32; } #endif #ifdef NEATCC_X64 static void ehdr_init(Elf_Ehdr *ehdr) { ehdr->e_machine = EM_X86_64; } static int rel_type(int flags) { if (flags & OUT_RLREL) return R_X86_64_PC32; if (flags & OUT_RL32) return flags & OUT_RLSX ? R_X86_64_32S : R_X86_64_32; return R_X86_64_64; } #endif #ifdef NEATCC_X86 static void ehdr_init(Elf_Ehdr *ehdr) { ehdr->e_machine = EM_386; } static int rel_type(int flags) { return flags & OUT_RLREL ? R_386_PC32 : R_386_32; } #endif ================================================ FILE: reg.c ================================================ /* neatcc global register allocation */ #include #include #include "ncc.h" #define IC_LLD(ic, i) (O_C((ic)[i].op) == (O_LD | O_LOC) ? (ic)[i].a1 : -1) #define IC_LST(ic, i) (O_C((ic)[i].op) == (O_ST | O_LOC) ? (ic)[i].a2 : -1) static int ic_loc(struct ic *ic, long iv, long *loc, long *off) { long oc = O_C(ic[iv].op); if (oc == (O_LD | O_LOC) || oc == (O_MOV | O_LOC)) { *loc = ic[iv].a1; *off = ic[iv].a2; return 0; } if (oc == (O_ST | O_LOC)) { *loc = ic[iv].a2; *off = ic[iv].a3; return 0; } return 1; } /* local live region */ struct rgn { long loc; /* local number */ long beg; /* region start (instruction number) */ long end; /* region end */ long cnt; /* number of accesses */ int reg; /* register allocated to this region */ }; static struct rgn *rgn; /* live regions */ static int rgn_n; /* number of entries in rgn[] */ static int rgn_sz; /* size of rgn[] */ static int *loc_ptr; /* if the address of locals is accessed */ static int loc_n; /* number of locals */ static long *dst_head; /* lists of jumps to each instruction */ static long *dst_next; /* next entries in dst_head[] lists */ static void rgn_add(long loc, long beg, long end, long cnt) { int i; for (i = 0; i < rgn_n; i++) { if (rgn[i].loc == loc && rgn[i].beg < end && rgn[i].end > beg) { if (beg > rgn[i].beg) beg = rgn[i].beg; if (end < rgn[i].end) end = rgn[i].end; cnt += rgn[i].cnt; rgn[i].loc = -1; } } for (i = 0; i < rgn_n; i++) if (rgn[i].loc < 0) break; if (i == rgn_n) { if (rgn_n >= rgn_sz) { rgn_sz = MAX(16, rgn_sz * 2); rgn = mextend(rgn, rgn_n, rgn_sz, sizeof(rgn[0])); } rgn_n++; } rgn[i].loc = loc; rgn[i].beg = beg; rgn[i].end = end; rgn[i].cnt = cnt; rgn[i].reg = -1; } /* return nonzero if register reg is free from beg till end */ static int rgn_available(long beg, long end, int reg) { int i; for (i = 0; i < rgn_n; i++) if (rgn[i].reg == reg) if (rgn[i].beg < end && rgn[i].end > beg) return 0; return 1; } static long reg_region(struct ic *ic, long ic_n, long loc, long pos, long *beg, long *end, char *mark) { long cnt = 0; long dst; for (; pos >= 0; pos--) { if (pos < *beg) *beg = pos; if (pos + 1 > *end) *end = pos + 1; if (mark[pos]) break; mark[pos] = 1; if (IC_LST(ic, pos) == loc) break; if (IC_LLD(ic, pos) == loc) cnt++; dst = dst_head[pos]; while (dst >= 0) { cnt += reg_region(ic, ic_n, loc, dst, beg, end, mark); dst = dst_next[dst]; } if (pos > 0 && ic[pos - 1].op & O_JMP) break; } return cnt; } /* compute local's live regions */ static void reg_regions(struct ic *ic, long ic_n, long loc) { char *mark; long beg, end; long cnt; long i; mark = calloc(ic_n, sizeof(mark[0])); for (i = 0; i < ic_n; i++) { if (IC_LLD(ic, i) == loc && !mark[i]) { beg = i; end = i + 1; cnt = reg_region(ic, ic_n, loc, i, &beg, &end, mark); rgn_add(loc, beg, end, cnt); } } for (i = 0; i < ic_n; i++) if (IC_LST(ic, i) == loc && !mark[i]) rgn_add(loc, i, i + 1, 1); free(mark); } /* number of times a local is accessed */ static long reg_loccnt(struct ic *ic, long ic_n, long loc) { long cnt = 0; long i; for (i = 0; i < ic_n; i++) if (IC_LLD(ic, i) == loc || IC_LST(ic, i) == loc) cnt++; return cnt; } /* perform global register allocation */ static void reg_glob(int leaf) { int *srt; int regs[N_REGS]; int i, j; int regs_max = MIN(N_TMPS >> 1, 4); long regs_mask = leaf ? R_TMPS : R_PERM; int regs_n = 0; for (i = leaf ? 1 : 3; i < N_TMPS && regs_n < regs_max; i++) if ((1 << i) & regs_mask) regs[regs_n++] = i; srt = malloc(rgn_n * sizeof(srt[0])); /* sorting locals */ for (i = 0; i < rgn_n; i++) { for (j = i - 1; j >= 0 && rgn[i].cnt > rgn[srt[j]].cnt; j--) srt[j + 1] = srt[j]; srt[j + 1] = i; } /* allocating registers */ for (i = 0; i < rgn_n; i++) { int r = srt[i]; long loc = rgn[r].loc; long beg = rgn[r].beg; long end = rgn[r].end; if (loc < 0 || loc_ptr[loc]) continue; if (leaf && loc < N_ARGS && beg == 0 && rgn_available(beg, end, argregs[loc])) { rgn[r].reg = argregs[loc]; continue; } for (j = 0; j < regs_n; j++) if (rgn_available(beg, end, regs[j])) break; if (j < regs_n) rgn[r].reg = regs[j]; } free(srt); } void reg_init(struct ic *ic, long ic_n) { long loc, off; int *loc_sz; int leaf = 1; long i; for (i = 0; i < ic_n; i++) if (ic[i].op & O_LOC && !ic_loc(ic, i, &loc, &off)) if (loc + 1 >= loc_n) loc_n = loc + 1; loc_ptr = calloc(loc_n, sizeof(loc_ptr[0])); loc_sz = calloc(loc_n, sizeof(loc_sz[0])); for (i = 0; i < loc_n; i++) loc_ptr[i] = !opt(1); for (i = 0; i < ic_n; i++) { long oc = O_C(ic[i].op); if (ic_loc(ic, i, &loc, &off)) continue; if (oc == (O_LD | O_LOC) || oc == (O_ST | O_LOC)) { int sz = T_SZ(O_T(ic[i].op)); if (!loc_sz[loc]) loc_sz[loc] = sz; if (off || sz < 2 || sz != loc_sz[loc]) loc_ptr[loc]++; } if (oc == (O_MOV | O_LOC)) loc_ptr[loc]++; } free(loc_sz); for (i = 0; i < ic_n; i++) if (ic[i].op & O_CALL) leaf = 0; dst_head = malloc(ic_n * sizeof(dst_head[0])); dst_next = malloc(ic_n * sizeof(dst_next[0])); for (i = 0; i < ic_n; i++) dst_head[i] = -1; for (i = 0; i < ic_n; i++) dst_next[i] = -1; for (i = 0; i < ic_n; i++) { if (ic[i].op & O_JXX) { dst_next[i] = dst_head[ic[i].a3]; dst_head[ic[i].a3] = i; } } for (i = 0; i < loc_n; i++) { if (!loc_ptr[i] && opt(2)) reg_regions(ic, ic_n, i); if (!loc_ptr[i] && !opt(2)) rgn_add(i, 0, ic_n, reg_loccnt(ic, ic_n, i)); } reg_glob(leaf); } long reg_mask(void) { long ret = 0; int i; for (i = 0; i < rgn_n; i++) if (rgn[i].reg >= 0) ret |= 1 << rgn[i].reg; return ret; } /* return the allocated register of local loc */ int reg_lmap(long c, long loc) { int i; for (i = 0; i < rgn_n; i++) if (rgn[i].loc == loc) if (rgn[i].beg <= c && rgn[i].end > c) return rgn[i].reg; return -1; } /* return the local to which register reg is allocated */ int reg_rmap(long c, long reg) { int i; for (i = 0; i < rgn_n; i++) if (rgn[i].reg == reg) if (rgn[i].beg <= c && rgn[i].end > c) return rgn[i].loc; return -1; } void reg_done(void) { free(dst_head); free(dst_next); free(loc_ptr); free(rgn); loc_ptr = NULL; rgn = NULL; rgn_sz = 0; rgn_n = 0; loc_n = 0; } int reg_safe(long loc) { return loc < loc_n && !loc_ptr[loc]; } ================================================ FILE: tok.c ================================================ /* neatcc tokenizer */ #include #include #include #include #include #include "ncc.h" static struct mem tok_mem; /* the data read via cpp_read() so far */ static struct mem tok; /* the previous token */ static char *buf; static long off, off_pre; /* current and previous positions */ static long len; static int tok_set; /* the current token was read */ static char *tok3[] = { "<<=", ">>=", "...", "<<", ">>", "++", "--", "+=", "-=", "*=", "/=", "%=", "|=", "&=", "^=", "&&", "||", "==", "!=", "<=", ">=", "->" }; static char *find_tok3(char *r) { int i; for (i = 0; i < LEN(tok3); i++) { char *s = tok3[i]; if (s[0] == r[0] && s[1] == r[1] && (!s[2] || s[2] == r[2])) return s; } return NULL; } static char *esc_code = "abefnrtv"; static char *esc = "\a\b\e\f\n\r\t\v"; static char *digs = "0123456789abcdef"; static int esc_char(int *c, char *s) { if (*s != '\\') { *c = (unsigned char) *s; return 1; } if (strchr(esc_code, (unsigned char) s[1])) { *c = esc[strchr(esc_code, (unsigned char) s[1]) - esc_code]; return 2; } if (isdigit(s[1]) || s[1] == 'x') { int ret = 0; int base = 8; int i = 1; char *d; if (s[1] == 'x') { base = 16; i++; } while ((d = memchr(digs, tolower(s[i]), base))) { ret *= base; ret += d - digs; i++; } *c = ret; return i; } *c = (unsigned char) s[1]; return 2; } long tok_num(char *tok, long *num) { int base = 10; long num_bt = 4 | T_MSIGN; if (tok[0] == '0' && tolower(tok[1]) == 'x') { num_bt &= ~T_MSIGN; base = 16; tok += 2; } if (strchr(digs, tolower((unsigned char) tok[0]))) { long result = 0; char *c; if (base == 10 && tok[0] == '0') base = 8; while (tok[0] && (c = strchr(digs, tolower((unsigned char) tok[0])))) { result *= base; result += c - digs; tok++; } *num = result; while (tok[0]) { int c = tolower((unsigned char) tok[0]); if (c != 'u' && c != 'l') break; if (c == 'u') num_bt &= ~T_MSIGN; if (c == 'l') num_bt = (num_bt & T_MSIGN) | LONGSZ; tok++; } return num_bt; } if (tok[0] == '\'') { int ret; esc_char(&ret, tok + 1); *num = ret; return num_bt; } return 0; } static int id_char(int c) { return isalnum(c) || c == '_'; } static int skipws(void) { long clen; char *cbuf; while (1) { if (off == len) { clen = 0; while (!clen) if (cpp_read(&cbuf, &clen)) return 1; mem_put(&tok_mem, cbuf, clen); buf = mem_buf(&tok_mem); len = mem_len(&tok_mem); } while (off < len && isspace(buf[off])) off++; if (off == len) continue; if (buf[off] == '\\' && buf[off + 1] == '\n') { off += 2; continue; } if (buf[off] == '/' && buf[off + 1] == '/') { while (++off < len && buf[off] != '\n') if (buf[off] == '\\') off++; continue; } if (buf[off] == '/' && buf[off + 1] == '*') { while (++off < len) { if (buf[off] == '*' && buf[off + 1] == '/') { off += 2; break; } } continue; } break; } return 0; } static int tok_read(void) { char *t3; int c; off_pre = off; mem_cut(&tok, 0); if (skipws()) return 1; if (buf[off] == '"') { mem_putc(&tok, '"'); while (buf[off] == '"') { off++; while (off < len && buf[off] != '"') { if (buf[off] == '\\') { off += esc_char(&c, buf + off); mem_putc(&tok, c); } else { mem_putc(&tok, (unsigned char) buf[off++]); } } if (off >= len || buf[off++] != '"') return 1; if (skipws()) return 1; } mem_putc(&tok, '"'); return 0; } if (isdigit((unsigned char) buf[off])) { if (buf[off] == '0' && (buf[off + 1] == 'x' || buf[off + 1] == 'X')) { mem_putc(&tok, (unsigned char) buf[off++]); mem_putc(&tok, (unsigned char) buf[off++]); } while (off < len && strchr(digs, tolower((unsigned char) buf[off]))) mem_putc(&tok, (unsigned char) buf[off++]); while (off < len && strchr("uUlL", (unsigned char) buf[off])) mem_putc(&tok, (unsigned char) buf[off++]); return 0; } if (buf[off] == '\'') { int c, i; int n = esc_char(&c, buf + off + 1) + 1 + 1; for (i = 0; i < n; i++) mem_putc(&tok, (unsigned char) buf[off++]); return 0; } if (id_char((unsigned char) buf[off])) { while (off < len && id_char((unsigned char) buf[off])) mem_putc(&tok, (unsigned char) buf[off++]); return 0; } if (off + 2 <= len && (t3 = find_tok3(buf + off))) { off += strlen(t3); mem_put(&tok, t3, strlen(t3)); return 0; } if (strchr(";,{}()[]<>*&!=+-/%?:|^~.", (unsigned char) buf[off])) { mem_putc(&tok, (unsigned char) buf[off++]); return 0; } return 1; } char *tok_get(void) { if (!tok_set) if (tok_read()) return ""; tok_set = 0; return mem_buf(&tok); } char *tok_see(void) { if (!tok_set) if (tok_read()) return ""; tok_set = 1; return mem_buf(&tok); } long tok_len(void) { return mem_len(&tok); } long tok_addr(void) { return tok_set ? off_pre : off; } void tok_jump(long addr) { off = addr; off_pre = -1; tok_set = 0; } void tok_done(void) { mem_done(&tok); mem_done(&tok_mem); } ================================================ FILE: x64.c ================================================ /* architecture-dependent code generation for x86_64 */ #include #include "ncc.h" /* x86-64 registers, without r8-r15 */ #define R_RAX 0x00 #define R_RCX 0x01 #define R_RDX 0x02 #define R_RBX 0x03 #define R_RSP 0x04 #define R_RBP 0x05 #define R_RSI 0x06 #define R_RDI 0x07 #define REG_RET R_RAX /* x86 opcodes */ #define I_MOV 0x89 #define I_MOVI 0xc7 #define I_MOVIR 0xb8 #define I_MOVR 0x8b #define I_MOVSXD 0x63 #define I_SHX 0xd3 #define I_CMP 0x3b #define I_TST 0x85 #define I_LEA 0x8d #define I_NOT 0xf7 #define I_CALL 0xff #define I_MUL 0xf7 #define I_XOR 0x33 #define I_CQO 0x99 #define I_PUSH 0x50 #define I_POP 0x58 #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) int tmpregs[] = {0, 7, 6, 2, 1, 8, 9, 10, 11, 3, 12, 13, 14, 15}; int argregs[] = {7, 6, 2, 1, 8, 9}; #define OP2(o2, o1) (0x010000 | ((o2) << 8) | (o1)) #define O2(op) (((op) >> 8) & 0xff) #define O1(op) ((op) & 0xff) #define MODRM(m, r1, r2) ((m) << 6 | (r1) << 3 | (r2)) #define REX(r1, r2) (0x48 | (((r1) & 8) >> 1) | (((r2) & 8) >> 3)) static struct mem cs; /* generated code */ /* code generation functions */ static void os(void *s, int n) { mem_put(&cs, s, n); } static char *ointbuf(long n, int l) { static char buf[16]; int i; for (i = 0; i < l; i++) { buf[i] = n & 0xff; n >>= 8; } return buf; } static void oi(long n, int l) { mem_put(&cs, ointbuf(n, l), l); } static void oi_at(long pos, long n, int l) { mem_cpy(&cs, pos, ointbuf(n, l), l); } static long opos(void) { return mem_len(&cs); } static void op_x(int op, int r1, int r2, int bt) { int sz = T_SZ(bt); int rex = 0; if (sz == 8) rex |= 8; if (sz == 1) rex |= 0x40; if (r1 & 0x8) rex |= 4; if (r2 & 0x8) rex |= 1; if (sz == 2) oi(0x66, 1); if (rex) oi(rex | 0x40, 1); if (op & 0x10000) oi(O2(op), 1); oi(sz == 1 ? O1(op) & ~0x1 : O1(op), 1); } #define op_mr op_rm /* op_*(): r=reg, m=mem, i=imm, s=sym */ static void op_rm(int op, int src, int base, int off, int bt) { int dis = off == (char) off ? 1 : 4; int mod = dis == 4 ? 2 : 1; if (!off && (base & 7) != R_RBP) mod = 0; op_x(op, src, base, bt); oi(MODRM(mod, src & 0x07, base & 0x07), 1); if ((base & 7) == R_RSP) oi(0x24, 1); if (mod) oi(off, dis); } static void op_rr(int op, int src, int dst, int bt) { op_x(op, src, dst, bt); oi(MODRM(3, src & 0x07, dst & 0x07), 1); } #define movrx_bt(bt) (((bt) == 4) ? 4 : LONGSZ) static int movrx_op(int bt, int mov) { int sz = T_SZ(bt); if (sz == 4) return bt & T_MSIGN ? I_MOVSXD : mov; if (sz == 2) return OP2(0x0f, bt & T_MSIGN ? 0xbf : 0xb7); if (sz == 1) return OP2(0x0f, bt & T_MSIGN ? 0xbe : 0xb6); return mov; } static void mov_r2r(int rd, int r1, unsigned bt) { if (rd != r1 || T_SZ(bt) != LONGSZ) op_rr(movrx_op(bt, I_MOVR), rd, r1, movrx_bt(bt)); } static void i_push(int reg) { op_x(I_PUSH | (reg & 0x7), 0, reg, LONGSZ); } static void i_pop(int reg) { op_x(I_POP | (reg & 0x7), 0, reg, LONGSZ); } void i_mov(int rd, int rn) { op_rr(movrx_op(LONGSZ, I_MOVR), rd, rn, movrx_bt(LONGSZ)); } static void i_add(int op, int rd, int r1, int r2) { /* opcode for O_ADD, O_SUB, O_AND, O_OR, O_XOR */ static int rx[] = {0003, 0053, 0043, 0013, 0063}; op_rr(rx[op & 0x0f], rd, r2, LONGSZ); } static void i_add_imm(int op, int rd, int rn, long n) { /* opcode for O_ADD, O_SUB, O_AND, O_OR, O_XOR */ static int rx[] = {0xc0, 0xe8, 0xe0, 0xc8, 0xf0}; unsigned char s[4] = {REX(0, rd), 0x83, rx[op & 0x0f] | (rd & 7), n & 0xff}; os((void *) s, 4); } static void i_num(int rd, long n) { if (!n) { op_rr(I_XOR, rd, rd, 4); return; } if (n < 0 && -n <= 0xffffffff) { op_rr(I_MOVI, 0, rd, LONGSZ); oi(n, 4); } else { int len = 8; if (n > 0 && n <= 0xffffffff) len = 4; op_x(I_MOVIR + (rd & 7), 0, rd, len); oi(n, len); } } static void i_mul(int rd, int r1, int r2) { if (r2 != R_RDX) i_num(R_RDX, 0); op_rr(I_MUL, 4, r2, LONGSZ); } static void i_div(int op, int rd, int r1, int r2) { long bt = O_T(op); if (r2 != R_RDX) { if (bt & T_MSIGN) op_x(I_CQO, R_RAX, R_RDX, LONGSZ); else i_num(R_RDX, 0); } op_rr(I_MUL, bt & T_MSIGN ? 7 : 6, r2, LONGSZ); } static void i_tst(int rn, int rm) { op_rr(I_TST, rn, rm, LONGSZ); } static void i_cmp(int rn, int rm) { op_rr(I_CMP, rn, rm, LONGSZ); } static void i_cmp_imm(int rn, long n) { unsigned char s[4] = {REX(0, rn), 0x83, 0xf8 | rn, n & 0xff}; os(s, 4); } static void i_shl(int op, int rd, int r1, int rs) { long bt = O_T(op); int sm = 4; if ((op & 0x0f) == 1) sm = bt & T_MSIGN ? 7 : 5; op_rr(I_SHX, sm, rd, LONGSZ); } static void i_shl_imm(int op, int rd, int rn, long n) { long bt = O_T(op); int sm = (op & 0x1) ? (bt & T_MSIGN ? 0xf8 : 0xe8) : 0xe0; char s[4] = {REX(0, rn), 0xc1, sm | (rn & 7), n & 0xff}; os(s, 4); } static void i_neg(int rd) { op_rr(I_NOT, 3, rd, LONGSZ); } static void i_not(int rd) { op_rr(I_NOT, 2, rd, LONGSZ); } static int i_cond(long op) { /* lt, ge, eq, ne, le, gt */ static int ucond[] = {0x92, 0x93, 0x94, 0x95, 0x96, 0x97}; static int scond[] = {0x9c, 0x9d, 0x94, 0x95, 0x9e, 0x9f}; long bt = O_T(op); return bt & T_MSIGN ? scond[op & 0x0f] : ucond[op & 0x0f]; } static void i_set(long op, int rd) { char set[] = "\x0f\x00\xc0"; set[1] = i_cond(op); os(set, 3); /* setl al */ os("\x48\x0f\xb6\xc0", 4); /* movzx rax, al */ } static void i_lnot(int rd) { char cmp[] = "\x00\x83\xf8\x00"; cmp[0] = REX(0, rd); cmp[2] |= rd & 7; os(cmp, 4); /* cmp rax, 0 */ i_set(O_EQ, rd); } static void jx(int x, int nbytes) { char op[2] = {0x0f}; if (nbytes == 1) { op[0] = 0x70 | (x & 0x0f); os(op, 1); /* jx $addr */ } else { op[1] = x; os(op, 2); /* jx $addr */ } } /* generate cmp or tst before a conditional jump */ static void i_jcmp(long op, long rn, long rm) { if (op & O_JZ) i_tst(rn, rn); if (op & O_JCC) { if (op & O_NUM) i_cmp_imm(rn, rm); else i_cmp(rn, rm); } } /* generate a jump instruction and return the of its displacement */ static long i_jmp(long op, int nb) { if (op & O_JZ) jx(O_C(op) == O_JZ ? 0x84 : 0x85, nb); else if (op & O_JCC) jx(i_cond(op) & ~0x10, nb); else os(nb == 1 ? "\xeb" : "\xe9", 1); oi(0, nb); return opos() - nb; } /* the length of a jump instruction opcode */ static int i_jlen(long op, int nb) { if (op & (O_JZ | O_JCC)) return nb ? 2 : 1; return 1; } /* zero extend */ static void i_zx(int rd, int r1, int bits) { if (bits & 0x07) { i_shl_imm(O_SHL, rd, rd, LONGSZ * 8 - bits); i_shl_imm(O_SHR, rd, rd, LONGSZ * 8 - bits); } else { mov_r2r(rd, r1, bits >> 3); } } /* sign extend */ static void i_sx(int rd, int r1, int bits) { mov_r2r(rd, r1, T_MSIGN | (bits >> 3)); } static void i_cast(int rd, int rn, int bt) { if (T_SZ(bt) == 8) { if (rd != rn) i_mov(rd, rn); } else { if (bt & T_MSIGN) i_sx(rd, rn, T_SZ(bt) * 8); else i_zx(rd, rn, T_SZ(bt) * 8); } } static void i_add_anyimm(int rd, int rn, long n) { op_rm(I_LEA, rd, rn, n, LONGSZ); } static long *rel_sym; /* relocation symbols */ static long *rel_flg; /* relocation flags */ static long *rel_off; /* relocation offsets */ static long rel_n, rel_sz; /* relocation count */ static long lab_sz; /* label count */ static long *lab_loc; /* label offsets in cs */ static long jmp_n, jmp_sz; /* jump count */ static long *jmp_off; /* jump offsets */ static long *jmp_dst; /* jump destinations */ static long *jmp_op; /* jump opcode */ static long jmp_ret; /* the position of the last return jmp */ static void lab_add(long id) { while (id >= lab_sz) { int lab_n = lab_sz; lab_sz = MAX(128, lab_sz * 2); lab_loc = mextend(lab_loc, lab_n, lab_sz, sizeof(*lab_loc)); } lab_loc[id] = opos(); } static void jmp_add(long op, long off, long dst) { if (jmp_n == jmp_sz) { jmp_sz = MAX(128, jmp_sz * 2); jmp_off = mextend(jmp_off, jmp_n, jmp_sz, sizeof(*jmp_off)); jmp_dst = mextend(jmp_dst, jmp_n, jmp_sz, sizeof(*jmp_dst)); jmp_op = mextend(jmp_op, jmp_n, jmp_sz, sizeof(*jmp_op)); } jmp_off[jmp_n] = off; jmp_dst[jmp_n] = dst; jmp_op[jmp_n] = op; jmp_n++; } void i_label(long id) { lab_add(id + 1); } static void i_rel(long sym, long flg, long off) { if (rel_n == rel_sz) { rel_sz = MAX(128, rel_sz * 2); rel_sym = mextend(rel_sym, rel_n, rel_sz, sizeof(*rel_sym)); rel_flg = mextend(rel_flg, rel_n, rel_sz, sizeof(*rel_flg)); rel_off = mextend(rel_off, rel_n, rel_sz, sizeof(*rel_off)); } rel_sym[rel_n] = sym; rel_flg[rel_n] = flg; rel_off[rel_n] = off; rel_n++; } static void i_sym(int rd, int sym, int off) { int sz = X64_ABS_RL & OUT_RL32 ? 4 : LONGSZ; if (X64_ABS_RL & OUT_RLSX) op_rr(I_MOVI, 0, rd, sz); else op_x(I_MOVIR + (rd & 7), 0, rd, sz); i_rel(sym, OUT_CS | X64_ABS_RL, opos()); oi(off, sz); } static void i_saveargs(long sargs) { int i; os("\x58", 1); /* pop rax */ for (i = N_ARGS - 1; i >= 0; i--) if ((1 << argregs[i]) & sargs) i_push(argregs[i]); os("\x50", 1); /* push rax */ } static void i_subsp(long val) { if (!val) return; if (val <= 127 && val >= -128) { os("\x48\x83\xec", 3); oi(val, 1); } else { os("\x48\x81\xec", 3); oi(val, 4); } } static int regs_count(long regs) { int cnt = 0; int i; for (i = 0; i < N_REGS; i++) if (((1 << i) & R_TMPS) & regs) cnt++; return cnt; } static void regs_save(long sregs, long dis) { int i; for (i = 0; i < N_REGS; i++) if (((1 << i) & R_TMPS) & sregs) i_push(i); if (dis) i_subsp(dis); } static void regs_load(long sregs, long dis) { int i; if (dis) i_subsp(-dis); for (i = N_REGS - 1; i >= 0; --i) if (((1 << i) & R_TMPS) & sregs) i_pop(i); } void i_wrap(int argc, long sargs, long spsub, int initfp, long sregs, long sregs_pos) { long body_n; void *body; long diff; /* prologue length */ int nsargs = 0; /* number of saved arguments */ int mod16; /* 16-byte alignment */ int i; /* removing the last jmp to the epilogue */ if (jmp_ret + i_jlen(O_JMP, 4) + 4 == opos()) { mem_cut(&cs, jmp_ret); jmp_n--; } lab_add(0); /* the return label */ body_n = mem_len(&cs); body = mem_get(&cs); /* generating function prologue */ if (sargs) i_saveargs(sargs); if (initfp) { os("\x55", 1); /* push rbp */ os("\x48\x89\xe5", 3); /* mov rbp, rsp */ } for (i = 0; i < N_ARGS; i++) if ((1 << argregs[i]) & sargs) nsargs++; mod16 = (spsub + nsargs * LONGSZ) % 16; /* forcing 16-byte alignment */ if (spsub) { spsub = spsub + (16 - mod16); i_subsp(sregs ? -sregs_pos - regs_count(sregs) * ULNG : spsub); } if (sregs) /* saving registers */ regs_save(sregs, spsub + sregs_pos); diff = mem_len(&cs); mem_put(&cs, body, body_n); free(body); /* generating function epilogue */ if (sregs) /* restoring saved registers */ regs_load(sregs, spsub + sregs_pos); if (initfp) os("\xc9", 1); /* leave */ if (sargs) { os("\xc2", 1); /* ret n */ oi(nsargs * LONGSZ, 2); } else { os("\xc3", 1); /* ret */ } /* adjusting code offsets */ for (i = 0; i < rel_n; i++) rel_off[i] += diff; for (i = 0; i < jmp_n; i++) jmp_off[i] += diff; for (i = 0; i < lab_sz; i++) lab_loc[i] += diff; } /* introduce shorter jumps, if possible */ static void i_shortjumps(int *nb) { long off = 0; /* current code offset */ long dif = 0; /* the difference after changing jump instructions */ int rel = 0; /* current relocation */ int lab = 1; /* current label */ long c_len = mem_len(&cs); char *c = mem_get(&cs); int i; for (i = 0; i < jmp_n; i++) nb[i] = abs(lab_loc[jmp_dst[i]] - jmp_off[i]) < 0x70 ? 1 : 4; for (i = 0; i < jmp_n; i++) { long cur = jmp_off[i] - i_jlen(jmp_op[i], 4); while (rel < rel_n && rel_off[rel] <= cur) rel_off[rel++] += dif; while (lab < lab_sz && lab_loc[lab] <= cur) lab_loc[lab++] += dif; mem_put(&cs, c + off, cur - off); jmp_off[i] = i_jmp(jmp_op[i], nb[i]); off = cur + i_jlen(jmp_op[i], 4) + 4; dif = mem_len(&cs) - off; } while (rel < rel_n) rel_off[rel++] += dif; while (lab < lab_sz) lab_loc[lab++] += dif; lab_loc[0] += dif; mem_put(&cs, c + off, c_len - off); free(c); } void i_code(char **c, long *c_len, long **rsym, long **rflg, long **roff, long *rcnt) { int *nb; /* number of bytes necessary for jump displacements */ int i; /* more compact jmp instructions */ nb = malloc(jmp_n * sizeof(nb[0])); for (i = 0; i < jmp_n; i++) nb[i] = 4; i_shortjumps(nb); for (i = 0; i < jmp_n; i++) /* filling jmp destinations */ oi_at(jmp_off[i], lab_loc[jmp_dst[i]] - jmp_off[i] - nb[i], nb[i]); free(nb); *c_len = mem_len(&cs); *c = mem_get(&cs); *rsym = rel_sym; *rflg = rel_flg; *roff = rel_off; *rcnt = rel_n; rel_sym = NULL; rel_flg = NULL; rel_off = NULL; rel_n = 0; rel_sz = 0; jmp_n = 0; } void i_done(void) { free(jmp_off); free(jmp_dst); free(jmp_op); free(lab_loc); } long i_reg(long op, long *rd, long *r1, long *r2, long *r3, long *tmp) { int oc = O_C(op); *rd = 0; *r1 = 0; *r2 = 0; *r3 = 0; *tmp = 0; if (oc & O_MOV) { *rd = R_TMPS; *r1 = oc & (O_NUM | O_SYM) ? 32 : R_TMPS; return 0; } if (oc & O_ADD) { *r1 = R_TMPS; *r2 = oc & O_NUM ? (oc == O_ADD ? 32 : 8) : R_TMPS; return 0; } if (oc & O_SHL) { if (oc & O_NUM) { *r1 = R_TMPS; *r2 = 8; } else { *r2 = 1 << R_RCX; *r1 = R_TMPS & ~*r2; } return 0; } if (oc & O_MUL) { if (oc & O_NUM) return 1; *rd = oc == O_MOD ? (1 << R_RDX) : (1 << R_RAX); *r1 = (1 << R_RAX); *r2 = R_TMPS & ~*rd & ~*r1; if (oc == O_DIV) *r2 &= ~(1 << R_RDX); *tmp = (1 << R_RDX) | (1 << R_RAX); return 0; } if (oc & O_CMP) { *rd = 1 << R_RAX; *r1 = R_TMPS; *r2 = oc & O_NUM ? 8 : R_TMPS; return 0; } if (oc & O_UOP) { if (oc == O_LNOT) *r1 = 1 << R_RAX; else *r1 = R_TMPS; return 0; } if (oc == O_MSET) { *r1 = 1 << R_RDI; *r2 = 1 << R_RAX; *r3 = 1 << R_RCX; *tmp = (1 << R_RDI) | (1 << R_RCX); return 0; } if (oc == O_MCPY) { *r1 = 1 << R_RDI; *r2 = 1 << R_RSI; *r3 = 1 << R_RCX; *tmp = (1 << R_RDI) | (1 << R_RSI) | (1 << R_RCX); return 0; } if (oc == O_RET) { *r1 = (1 << REG_RET); return 0; } if (oc & O_CALL) { *rd = (1 << REG_RET); *r1 = oc & O_SYM ? 0 : R_TMPS; *tmp = R_TMPS & ~R_PERM; return 0; } if (oc & O_LD) { *rd = R_TMPS; *r1 = R_TMPS; *r2 = oc & O_NUM ? 32 : R_TMPS; return 0; } if (oc & O_ST) { *r1 = R_TMPS; *r2 = R_TMPS; *r3 = oc & O_NUM ? 32 : R_TMPS; return 0; } if (oc & O_JZ) { *r1 = R_TMPS; return 0; } if (oc & O_JCC) { *r1 = R_TMPS; *r2 = oc & O_NUM ? 8 : R_TMPS; return 0; } if (oc == O_JMP) return 0; return 1; } int i_imm(long lim, long n) { long max = (1 << (lim - 1)) - 1; return n <= max && n + 1 >= -max; } long i_ins(long op, long rd, long r1, long r2, long r3) { long oc = O_C(op); long bt = O_T(op); if (oc & O_ADD) { if (oc & O_NUM) { if (rd == r1 && r2 <= 127 && r2 >= -128) i_add_imm(op, r1, r1, r2); else i_add_anyimm(rd, r1, r2); } else { i_add(op, r1, r1, r2); } } if (oc & O_SHL) { if (oc & O_NUM) i_shl_imm(op, r1, r1, r2); else i_shl(op, r1, r1, r2); } if (oc & O_MUL) { if (oc == O_MUL) i_mul(R_RAX, r1, r2); if (oc == O_DIV) i_div(op, R_RAX, r1, r2); if (oc == O_MOD) i_div(op, R_RDX, r1, r2); return 0; } if (oc & O_CMP) { if (oc & O_NUM) i_cmp_imm(r1, r2); else i_cmp(r1, r2); i_set(op, rd); return 0; } if (oc & O_UOP) { /* uop */ if (oc == O_NEG) i_neg(r1); if (oc == O_NOT) i_not(r1); if (oc == O_LNOT) i_lnot(r1); return 0; } if (oc == O_CALL) { op_rr(I_CALL, 2, r1, LONGSZ); return 0; } if (oc == (O_CALL | O_SYM)) { os("\xe8", 1); /* call $x */ i_rel(r1, OUT_CS | OUT_RLREL, opos()); oi(-4 + r2, 4); return 0; } if (oc == (O_MOV | O_SYM)) { i_sym(rd, r1, r2); return 0; } if (oc == (O_MOV | O_NUM)) { i_num(rd, r1); return 0; } if (oc == O_MSET) { os("\xfc\xf3\xaa", 3); /* cld; rep stosb */ return 0; } if (oc == O_MCPY) { os("\xfc\xf3\xa4", 3); /* cld; rep movs */ return 0; } if (oc == O_RET) { jmp_ret = opos(); jmp_add(O_JMP, i_jmp(op, 4), 0); return 0; } if (oc == (O_LD | O_NUM)) { op_rm(movrx_op(bt, I_MOVR), rd, r1, r2, movrx_bt(bt)); return 0; } if (oc == (O_ST | O_NUM)) { op_rm(I_MOV, r1, r2, r3, bt); return 0; } if (oc == O_MOV) { i_cast(rd, r1, bt); return 0; } if (oc & O_JXX) { i_jcmp(op, r1, r2); jmp_add(op, i_jmp(op, 4), r3 + 1); return 0; } return 1; } ================================================ FILE: x64.h ================================================ /* architecture-dependent header for x86_64 */ #define LONGSZ 8 /* word size */ #define I_ARCH "__x86_64__" #define N_REGS 16 /* number of registers */ #define N_TMPS 14 /* number of tmp registers */ #define N_ARGS 6 /* number of arg registers */ #define R_TMPS 0xffcf /* mask of tmp registers */ #define R_ARGS 0x03c6 /* mask of arg registers */ #define R_PERM 0xf008 /* mask of callee-saved registers */ #define REG_FP 5 /* frame pointer register */ #define REG_SP 4 /* stack pointer register */ #define I_ARG0 (-16) /* offset of the first argument from FP */ #define I_LOC0 0 /* offset of the first local from FP */ #define X64_ABS_RL (OUT_RL32) /* x86_64 memory model */ ================================================ FILE: x86.c ================================================ /* architecture-dependent code generation for x86 */ #include #include "ncc.h" /* x86-64 registers, without r8-r15 */ #define R_RAX 0x00 #define R_RCX 0x01 #define R_RDX 0x02 #define R_RBX 0x03 #define R_RSP 0x04 #define R_RBP 0x05 #define R_RSI 0x06 #define R_RDI 0x07 #define REG_RET R_RAX #define R_BYTE 0x0007 /* x86 opcodes */ #define I_MOV 0x89 #define I_MOVI 0xc7 #define I_MOVIR 0xb8 #define I_MOVR 0x8b #define I_MOVSXD 0x63 #define I_SHX 0xd3 #define I_CMP 0x3b #define I_TST 0x85 #define I_LEA 0x8d #define I_NOT 0xf7 #define I_CALL 0xff #define I_MUL 0xf7 #define I_XOR 0x33 #define I_CQO 0x99 #define I_PUSH 0x50 #define I_POP 0x58 #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) int tmpregs[] = {0, 1, 2, 6, 7, 3}; int argregs[] = {0}; #define OP2(o2, o1) (0x010000 | ((o2) << 8) | (o1)) #define O2(op) (((op) >> 8) & 0xff) #define O1(op) ((op) & 0xff) #define MODRM(m, r1, r2) ((m) << 6 | (r1) << 3 | (r2)) static struct mem cs; /* generated code */ /* code generation functions */ static void os(void *s, int n) { mem_put(&cs, s, n); } static char *ointbuf(long n, int l) { static char buf[16]; int i; for (i = 0; i < l; i++) { buf[i] = n & 0xff; n >>= 8; } return buf; } static void oi(long n, int l) { mem_put(&cs, ointbuf(n, l), l); } static void oi_at(long pos, long n, int l) { mem_cpy(&cs, pos, ointbuf(n, l), l); } static long opos(void) { return mem_len(&cs); } static void op_x(int op, int r1, int r2, int bt) { int sz = T_SZ(bt); if (sz == 2) oi(0x66, 1); if (op & 0x10000) oi(O2(op), 1); oi(sz == 1 ? O1(op) & ~0x1 : O1(op), 1); } #define op_mr op_rm /* op_*(): r=reg, m=mem, i=imm, s=sym */ static void op_rm(int op, int src, int base, int off, int bt) { int dis = off == (char) off ? 1 : 4; int mod = dis == 4 ? 2 : 1; if (!off && (base & 7) != R_RBP) mod = 0; op_x(op, src, base, bt); oi(MODRM(mod, src & 0x07, base & 0x07), 1); if ((base & 7) == R_RSP) oi(0x24, 1); if (mod) oi(off, dis); } static void op_rr(int op, int src, int dst, int bt) { op_x(op, src, dst, bt); oi(MODRM(3, src & 0x07, dst & 0x07), 1); } #define movrx_bt(bt) (LONGSZ) static int movrx_op(int bt, int mov) { int sz = T_SZ(bt); if (sz == 2) return OP2(0x0f, bt & T_MSIGN ? 0xbf : 0xb7); if (sz == 1) return OP2(0x0f, bt & T_MSIGN ? 0xbe : 0xb6); return mov; } static void mov_r2r(int rd, int r1, unsigned bt) { if (rd != r1 || T_SZ(bt) != LONGSZ) op_rr(movrx_op(bt, I_MOVR), rd, r1, movrx_bt(bt)); } static void i_push(int reg) { op_x(I_PUSH | (reg & 0x7), 0, reg, LONGSZ); } static void i_pop(int reg) { op_x(I_POP | (reg & 0x7), 0, reg, LONGSZ); } static void i_mov(int rd, int rn) { op_rr(movrx_op(LONGSZ, I_MOVR), rd, rn, movrx_bt(LONGSZ)); } static void i_add(int op, int rd, int r1, int r2) { /* opcode for O_ADD, O_SUB, O_AND, O_OR, O_XOR */ static int rx[] = {0003, 0053, 0043, 0013, 0063}; op_rr(rx[op & 0x0f], rd, r2, LONGSZ); } static void i_add_imm(int op, int rd, int rn, long n) { /* opcode for O_ADD, O_SUB, O_AND, O_OR, O_XOR */ static int rx[] = {0xc0, 0xe8, 0xe0, 0xc8, 0xf0}; unsigned char s[4] = {0x83, rx[op & 0x0f] | rd, n & 0xff}; os((void *) s, 3); } static void i_num(int rd, long n) { if (!n) { op_rr(I_XOR, rd, rd, 4); return; } else { op_x(I_MOVIR + (rd & 7), 0, rd, LONGSZ); oi(n, LONGSZ); } } static void i_mul(int rd, int r1, int r2) { if (r2 != R_RDX) i_num(R_RDX, 0); op_rr(I_MUL, 4, r2, LONGSZ); } static void i_div(int op, int rd, int r1, int r2) { long bt = O_T(op); if (r2 != R_RDX) { if (bt & T_MSIGN) op_x(I_CQO, R_RAX, R_RDX, LONGSZ); else i_num(R_RDX, 0); } op_rr(I_MUL, bt & T_MSIGN ? 7 : 6, r2, LONGSZ); } static void i_tst(int rn, int rm) { op_rr(I_TST, rn, rm, LONGSZ); } static void i_cmp(int rn, int rm) { op_rr(I_CMP, rn, rm, LONGSZ); } static void i_cmp_imm(int rn, long n) { unsigned char s[4] = {0x83, 0xf8 | rn, n & 0xff}; os(s, 3); } static void i_shl(int op, int rd, int r1, int rs) { long bt = O_T(op); int sm = 4; if ((op & 0x0f) == 1) sm = bt & T_MSIGN ? 7 : 5; op_rr(I_SHX, sm, rd, LONGSZ); } static void i_shl_imm(int op, int rd, int rn, long n) { long bt = O_T(op); int sm = (op & 0x1) ? (bt & T_MSIGN ? 0xf8 : 0xe8) : 0xe0; char s[4] = {0xc1, sm | rn, n & 0xff}; os(s, 3); } static void i_neg(int rd) { op_rr(I_NOT, 3, rd, LONGSZ); } static void i_not(int rd) { op_rr(I_NOT, 2, rd, LONGSZ); } static int i_cond(long op) { /* lt, ge, eq, ne, le, gt */ static int ucond[] = {0x92, 0x93, 0x94, 0x95, 0x96, 0x97}; static int scond[] = {0x9c, 0x9d, 0x94, 0x95, 0x9e, 0x9f}; long bt = O_T(op); return bt & T_MSIGN ? scond[op & 0x0f] : ucond[op & 0x0f]; } static void i_set(long op, int rd) { char set[] = "\x0f\x00\xc0"; set[1] = i_cond(op); os(set, 3); /* setl al */ os("\x0f\xb6\xc0", 3); /* movzx rax, al */ } static void i_lnot(int rd) { char cmp[] = "\x83\xf8\x00"; cmp[1] |= rd; os(cmp, 3); /* cmp rax, 0 */ i_set(O_EQ, rd); } static void jx(int x, int nbytes) { char op[2] = {0x0f}; if (nbytes == 1) { oi(0x70 | (x & 0x0f), 1); /* jx $addr */ } else { op[1] = x; os(op, 2); /* jx $addr */ } } /* generate cmp or tst before a conditional jump */ static void i_jcmp(long op, long rn, long rm) { if (op & O_JZ) i_tst(rn, rn); if (op & O_JCC) { if (op & O_NUM) i_cmp_imm(rn, rm); else i_cmp(rn, rm); } } /* generate a jump instruction and return the of its displacement */ static long i_jmp(long op, int nb) { if (op & O_JZ) jx(O_C(op) == O_JZ ? 0x84 : 0x85, nb); else if (op & O_JCC) jx(i_cond(op) & ~0x10, nb); else os(nb == 1 ? "\xeb" : "\xe9", 1); oi(0, nb); return opos() - nb; } /* the length of a jump instruction opcode */ static int i_jlen(long op, int nb) { if (op & (O_JZ | O_JCC)) return nb ? 2 : 1; return 1; } /* zero extend */ static void i_zx(int rd, int r1, int bits) { if (bits & 0x07) { i_shl_imm(O_SHL, rd, rd, LONGSZ * 8 - bits); i_shl_imm(O_SHR, rd, rd, LONGSZ * 8 - bits); } else { mov_r2r(rd, r1, bits >> 3); } } /* sign extend */ static void i_sx(int rd, int r1, int bits) { mov_r2r(rd, r1, T_MSIGN | (bits >> 3)); } static void i_cast(int rd, int rn, int bt) { if (T_SZ(bt) == 8) { if (rd != rn) i_mov(rd, rn); } else { if (bt & T_MSIGN) i_sx(rd, rn, T_SZ(bt) * 8); else i_zx(rd, rn, T_SZ(bt) * 8); } } static void i_add_anyimm(int rd, int rn, long n) { op_rm(I_LEA, rd, rn, n, LONGSZ); } static long *rel_sym; /* relocation symbols */ static long *rel_flg; /* relocation flags */ static long *rel_off; /* relocation offsets */ static long rel_n, rel_sz; /* relocation count */ static long lab_sz; /* label count */ static long *lab_loc; /* label offsets in cs */ static long jmp_n, jmp_sz; /* jump count */ static long *jmp_off; /* jump offsets */ static long *jmp_dst; /* jump destinations */ static long *jmp_op; /* jump opcode */ static long jmp_ret; /* the position of the last return jmp */ static void lab_add(long id) { while (id >= lab_sz) { int lab_n = lab_sz; lab_sz = MAX(128, lab_sz * 2); lab_loc = mextend(lab_loc, lab_n, lab_sz, sizeof(*lab_loc)); } lab_loc[id] = opos(); } static void jmp_add(long op, long off, long dst) { if (jmp_n == jmp_sz) { jmp_sz = MAX(128, jmp_sz * 2); jmp_off = mextend(jmp_off, jmp_n, jmp_sz, sizeof(*jmp_off)); jmp_dst = mextend(jmp_dst, jmp_n, jmp_sz, sizeof(*jmp_dst)); jmp_op = mextend(jmp_op, jmp_n, jmp_sz, sizeof(*jmp_op)); } jmp_off[jmp_n] = off; jmp_dst[jmp_n] = dst; jmp_op[jmp_n] = op; jmp_n++; } void i_label(long id) { lab_add(id + 1); } static void i_rel(long sym, long flg, long off) { if (rel_n == rel_sz) { rel_sz = MAX(128, rel_sz * 2); rel_sym = mextend(rel_sym, rel_n, rel_sz, sizeof(*rel_sym)); rel_flg = mextend(rel_flg, rel_n, rel_sz, sizeof(*rel_flg)); rel_off = mextend(rel_off, rel_n, rel_sz, sizeof(*rel_off)); } rel_sym[rel_n] = sym; rel_flg[rel_n] = flg; rel_off[rel_n] = off; rel_n++; } static void i_sym(int rd, int sym, int off) { op_x(I_MOVIR + (rd & 7), 0, rd, LONGSZ); i_rel(sym, OUT_CS, opos()); oi(off, LONGSZ); } static void i_subsp(long val) { if (!val) return; if (val <= 127 && val >= -128) { os("\x83\xec", 2); oi(val, 1); } else { os("\x81\xec", 2); oi(val, 4); } } static int regs_count(long regs) { int cnt = 0; int i; for (i = 0; i < N_REGS; i++) if (((1 << i) & R_TMPS) & regs) cnt++; return cnt; } static void regs_save(long sregs, long dis) { int i; for (i = 0; i < N_REGS; i++) if (((1 << i) & R_TMPS) & sregs) i_push(i); if (dis) i_subsp(dis); } static void regs_load(long sregs, long dis) { int i; if (dis) i_subsp(-dis); for (i = N_REGS - 1; i >= 0; --i) if (((1 << i) & R_TMPS) & sregs) i_pop(i); } void i_wrap(int argc, long sargs, long spsub, int initfp, long sregs, long sregs_pos) { long body_n; void *body; long diff; /* prologue length */ int i; /* removing the last jmp to the epilogue */ if (jmp_ret + i_jlen(O_JMP, 4) + 4 == opos()) { mem_cut(&cs, jmp_ret); jmp_n--; } lab_add(0); /* the return label */ body_n = mem_len(&cs); body = mem_get(&cs); /* generating function prologue */ if (initfp) { os("\x55", 1); /* push rbp */ os("\x89\xe5", 2); /* mov rbp, rsp */ } if (spsub) { spsub = ALIGN(spsub, 8); i_subsp(sregs ? -sregs_pos - regs_count(sregs) * ULNG : spsub); } if (sregs) /* saving registers */ regs_save(sregs, spsub + sregs_pos); diff = mem_len(&cs); mem_put(&cs, body, body_n); free(body); /* generating function epilogue */ if (sregs) /* restoring saved registers */ regs_load(sregs, spsub + sregs_pos); if (initfp) os("\xc9", 1); /* leave */ os("\xc3", 1); /* ret */ /* adjusting code offsets */ for (i = 0; i < rel_n; i++) rel_off[i] += diff; for (i = 0; i < jmp_n; i++) jmp_off[i] += diff; for (i = 0; i < lab_sz; i++) lab_loc[i] += diff; } /* introduce shorter jumps, if possible */ static void i_shortjumps(int *nb) { long off = 0; /* current code offset */ long dif = 0; /* the difference after changing jump instructions */ int rel = 0; /* current relocation */ int lab = 1; /* current label */ long c_len = mem_len(&cs); char *c = mem_get(&cs); int i; for (i = 0; i < jmp_n; i++) nb[i] = abs(lab_loc[jmp_dst[i]] - jmp_off[i]) < 0x70 ? 1 : 4; for (i = 0; i < jmp_n; i++) { long cur = jmp_off[i] - i_jlen(jmp_op[i], 4); while (rel < rel_n && rel_off[rel] <= cur) rel_off[rel++] += dif; while (lab < lab_sz && lab_loc[lab] <= cur) lab_loc[lab++] += dif; mem_put(&cs, c + off, cur - off); jmp_off[i] = i_jmp(jmp_op[i], nb[i]); off = cur + i_jlen(jmp_op[i], 4) + 4; dif = mem_len(&cs) - off; } while (rel < rel_n) rel_off[rel++] += dif; while (lab < lab_sz) lab_loc[lab++] += dif; lab_loc[0] += dif; mem_put(&cs, c + off, c_len - off); free(c); } void i_code(char **c, long *c_len, long **rsym, long **rflg, long **roff, long *rcnt) { int *nb; /* number of bytes necessary for jump displacements */ int i; /* more compact jmp instructions */ nb = malloc(jmp_n * sizeof(nb[0])); for (i = 0; i < jmp_n; i++) nb[i] = 4; i_shortjumps(nb); for (i = 0; i < jmp_n; i++) /* filling jmp destinations */ oi_at(jmp_off[i], lab_loc[jmp_dst[i]] - jmp_off[i] - nb[i], nb[i]); free(nb); *c_len = mem_len(&cs); *c = mem_get(&cs); *rsym = rel_sym; *rflg = rel_flg; *roff = rel_off; *rcnt = rel_n; rel_sym = NULL; rel_flg = NULL; rel_off = NULL; rel_n = 0; rel_sz = 0; jmp_n = 0; } void i_done(void) { free(jmp_off); free(jmp_dst); free(jmp_op); free(lab_loc); } long i_reg(long op, long *rd, long *r1, long *r2, long *r3, long *tmp) { long oc = O_C(op); long bt = O_T(op); *rd = 0; *r1 = 0; *r2 = 0; *r3 = 0; *tmp = 0; if (oc & O_MOV) { *rd = R_TMPS; if (oc & (O_NUM | O_SYM)) *r1 = 32; else *r1 = T_SZ(bt) == 1 ? R_BYTE : R_TMPS; return 0; } if (oc & O_ADD) { *r1 = R_TMPS; *r2 = oc & O_NUM ? (oc == O_ADD ? 32 : 8) : R_TMPS; return 0; } if (oc & O_SHL) { if (oc & O_NUM) { *r1 = R_TMPS; *r2 = 8; } else { *r2 = 1 << R_RCX; *r1 = R_TMPS & ~*r2; } return 0; } if (oc & O_MUL) { if (oc & O_NUM) return 1; *rd = oc == O_MOD ? (1 << R_RDX) : (1 << R_RAX); *r1 = (1 << R_RAX); *r2 = R_TMPS & ~*rd & ~*r1; if (oc == O_DIV) *r2 &= ~(1 << R_RDX); *tmp = (1 << R_RDX) | (1 << R_RAX); return 0; } if (oc & O_CMP) { *rd = 1 << R_RAX; *r1 = R_TMPS; *r2 = oc & O_NUM ? 8 : R_TMPS; return 0; } if (oc & O_UOP) { if (oc == O_LNOT) *r1 = 1 << R_RAX; else *r1 = R_TMPS; return 0; } if (oc == O_MSET) { *r1 = 1 << R_RDI; *r2 = 1 << R_RAX; *r3 = 1 << R_RCX; *tmp = (1 << R_RDI) | (1 << R_RCX); return 0; } if (oc == O_MCPY) { *r1 = 1 << R_RDI; *r2 = 1 << R_RSI; *r3 = 1 << R_RCX; *tmp = (1 << R_RDI) | (1 << R_RSI) | (1 << R_RCX); return 0; } if (oc == O_RET) { *r1 = (1 << REG_RET); return 0; } if (oc & O_CALL) { *rd = (1 << REG_RET); *r1 = oc & O_SYM ? 0 : R_TMPS; *tmp = R_TMPS & ~R_PERM; return 0; } if (oc & O_LD) { *rd = T_SZ(bt) == 1 ? R_BYTE : R_TMPS; *r1 = R_TMPS; *r2 = oc & O_NUM ? 32 : R_TMPS; return 0; } if (oc & O_ST) { *r1 = T_SZ(bt) == 1 ? R_BYTE : R_TMPS; *r2 = R_TMPS; *r3 = oc & O_NUM ? 32 : R_TMPS; return 0; } if (oc & O_JZ) { *r1 = R_TMPS; return 0; } if (oc & O_JCC) { *r1 = R_TMPS; *r2 = oc & O_NUM ? 8 : R_TMPS; return 0; } if (oc == O_JMP) return 0; return 1; } int i_imm(long lim, long n) { long max = (1 << (lim - 1)) - 1; return n <= max && n + 1 >= -max; } long i_ins(long op, long rd, long r1, long r2, long r3) { long oc = O_C(op); long bt = O_T(op); if (oc & O_ADD) { if (oc & O_NUM) { if (rd == r1 && r2 <= 127 && r2 >= -128) i_add_imm(op, r1, r1, r2); else i_add_anyimm(rd, r1, r2); } else { i_add(op, r1, r1, r2); } } if (oc & O_SHL) { if (oc & O_NUM) i_shl_imm(op, r1, r1, r2); else i_shl(op, r1, r1, r2); } if (oc & O_MUL) { if (oc == O_MUL) i_mul(R_RAX, r1, r2); if (oc == O_DIV) i_div(op, R_RAX, r1, r2); if (oc == O_MOD) i_div(op, R_RDX, r1, r2); return 0; } if (oc & O_CMP) { if (oc & O_NUM) i_cmp_imm(r1, r2); else i_cmp(r1, r2); i_set(op, rd); return 0; } if (oc & O_UOP) { /* uop */ if (oc == O_NEG) i_neg(r1); if (oc == O_NOT) i_not(r1); if (oc == O_LNOT) i_lnot(r1); return 0; } if (oc == O_CALL) { op_rr(I_CALL, 2, r1, LONGSZ); return 0; } if (oc == (O_CALL | O_SYM)) { os("\xe8", 1); /* call $x */ i_rel(r1, OUT_CS | OUT_RLREL, opos()); oi(-4, 4); return 0; } if (oc == (O_MOV | O_SYM)) { i_sym(rd, r1, r2); return 0; } if (oc == (O_MOV | O_NUM)) { i_num(rd, r1); return 0; } if (oc == O_MSET) { os("\xfc\xf3\xaa", 3); /* cld; rep stosb */ return 0; } if (oc == O_MCPY) { os("\xfc\xf3\xa4", 3); /* cld; rep movs */ return 0; } if (oc == O_RET) { jmp_ret = opos(); jmp_add(O_JMP, i_jmp(op, 4), 0); return 0; } if (oc == (O_LD | O_NUM)) { op_rm(movrx_op(bt, I_MOVR), rd, r1, r2, movrx_bt(bt)); return 0; } if (oc == (O_ST | O_NUM)) { op_rm(I_MOV, r1, r2, r3, bt); return 0; } if (oc == O_MOV) { i_cast(rd, r1, bt); return 0; } if (oc & O_JXX) { i_jcmp(op, r1, r2); jmp_add(op, i_jmp(op, 4), r3 + 1); return 0; } return 1; } ================================================ FILE: x86.h ================================================ /* architecture-dependent header for x86 */ #define LONGSZ 4 /* word size */ #define I_ARCH "__i386__" #define N_REGS 8 /* number of registers */ #define N_TMPS 6 /* number of tmp registers */ #define N_ARGS 0 /* number of arg registers */ #define R_TMPS 0x00cf /* mask of tmp registers */ #define R_ARGS 0x0000 /* mask of arg registers */ #define R_PERM 0x00c8 /* mask of callee-saved registers */ #define REG_FP 5 /* frame pointer register */ #define REG_SP 4 /* stack pointer register */ #define I_ARG0 (-8) /* offset of the first argument from FP */ #define I_LOC0 0 /* offset of the first local from FP */