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 <stdlib.h>
#include <string.h>
#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 <ctype.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#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 <elf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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 <stdio.h>
#include <stdlib.h>
#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 <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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 <stdlib.h>
#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 <stdlib.h>
#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 */
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
SYMBOL INDEX (502 symbols across 12 files)
FILE: arm.c
type mem (line 30) | struct mem
function oi (line 44) | static void oi(long n, int l)
function oi_at (line 49) | static void oi_at(long pos, long n, int l)
function opos (line 54) | static long opos(void)
function lab_add (line 92) | static void lab_add(long id)
function jmp_add (line 102) | static void jmp_add(long off, long dst)
function i_label (line 114) | void i_label(long id)
function rel_add (line 119) | static void rel_add(long sym, long flg, long off)
function i_div (line 138) | static void i_div(char *func)
function pool_find (line 150) | static int pool_find(long sym, long off)
function pool_num (line 166) | static int pool_num(long num)
function pool_reloc (line 171) | static int pool_reloc(long sym, long off)
function pool_write (line 176) | static void pool_write(void)
function add_encimm (line 203) | static int add_encimm(unsigned n)
function add_decimm (line 211) | static unsigned add_decimm(int n)
function add_rndimm (line 217) | static int add_rndimm(unsigned n)
function opcode_add (line 230) | static int opcode_add(int op)
function i_add (line 237) | static void i_add(int op, int rd, int rn, int rm)
function i_add_imm (line 242) | static void i_add_imm(int op, int rd, int rn, long n)
function i_num (line 249) | static void i_num(int rd, long n)
function i_add_anyimm (line 264) | static void i_add_anyimm(int rd, int rn, long n)
function i_mul (line 292) | static void i_mul(int rd, int rn, int rm)
function opcode_set (line 297) | static int opcode_set(long op)
function i_tst (line 306) | static void i_tst(int rn, int rm)
function i_cmp (line 311) | static void i_cmp(int rn, int rm)
function i_cmp_imm (line 316) | static void i_cmp_imm(int rn, long n)
function i_set (line 321) | static void i_set(int cond, int rd)
function opcode_shl (line 331) | static int opcode_shl(long op)
function i_shl (line 338) | static void i_shl(long op, int rd, int rm, int rs)
function i_shl_imm (line 344) | static void i_shl_imm(long op, int rd, int rn, long n)
function i_mov (line 350) | void i_mov(int rd, int rn)
function i_ldr (line 392) | static void i_ldr(int l, int rd, int rn, int off, int bt)
function i_sym (line 415) | static void i_sym(int rd, long sym, long off)
function i_neg (line 421) | static void i_neg(int rd, int r1)
function i_not (line 426) | static void i_not(int rd, int r1)
function i_lnot (line 431) | static void i_lnot(int rd, int r1)
function i_zx (line 438) | static void i_zx(int rd, int r1, int bits)
function i_sx (line 448) | static void i_sx(int rd, int r1, int bits)
function i_jmp (line 464) | static long i_jmp(long op, long rn, long rm)
function i_memcpy (line 490) | static void i_memcpy(int rd, int rs, int rn)
function i_memset (line 499) | static void i_memset(int rd, int rs, int rn)
function i_call_reg (line 507) | static void i_call_reg(int rd)
function i_call (line 513) | static void i_call(long sym, long off)
function i_imm (line 519) | int i_imm(long lim, long n)
function i_reg (line 524) | long i_reg(long op, long *rd, long *r1, long *r2, long *r3, long *tmp)
function i_ins (line 600) | long i_ins(long op, long rd, long r1, long r2, long r3)
function i_wrap (line 703) | void i_wrap(int argc, long sargs, long spsub, int initfp, long sregs, lo...
function i_fill (line 793) | static void i_fill(long src, long dst)
function i_code (line 800) | void i_code(char **c, long *c_len, long **rsym, long **rflg, long **roff...
function i_done (line 821) | void i_done(void)
FILE: cpp.c
type macro (line 18) | struct macro {
type buf (line 37) | struct buf {
function die (line 53) | void die(char *fmt, ...)
function buf_new (line 64) | static void buf_new(int type, char *dat, long dlen)
function buf_file (line 80) | static void buf_file(char *path, char *dat, int dlen)
type macro (line 86) | struct macro
function buf_macro (line 88) | static void buf_macro(struct macro *m)
function buf_arg (line 146) | static void buf_arg(char *arg, int mbuf)
function buf_pop (line 152) | static void buf_pop(void)
function buf_iseval (line 164) | static int buf_iseval(void)
function file_size (line 173) | static size_t file_size(int fd)
function include_file (line 181) | static int include_file(char *path)
function cpp_init (line 199) | int cpp_init(char *path)
function jumpws (line 204) | static int jumpws(void)
function read_word (line 212) | static void read_word(char *dst)
function jumpcomment (line 220) | static int jumpcomment(void)
function jumpstr (line 239) | static int jumpstr(void)
function read_tilleol (line 258) | static void read_tilleol(char *dst)
function cpp_path (line 284) | void cpp_path(char *s)
function include_find (line 289) | static int include_find(char *name, int std)
function readarg (line 304) | static void readarg(char *s)
function macro_find (line 331) | static int macro_find(char *name, int undef)
function macro_undef (line 343) | static void macro_undef(char *name)
function macro_new (line 350) | static int macro_new(char *name)
function macro_define (line 364) | static void macro_define(void)
function cpp_eval (line 396) | static long cpp_eval(void)
function jumpifs (line 418) | static void jumpifs(int jumpelse)
function cpp_cmd (line 451) | static int cpp_cmd(void)
function macro_arg (line 505) | static int macro_arg(struct macro *m, char *arg)
function buf_arg_find (line 514) | static int buf_arg_find(char *name)
function macro_expand (line 528) | static void macro_expand(char *name)
function buf_expanding (line 564) | static int buf_expanding(char *macro)
function expandable (line 578) | static int expandable(char *word)
function cpp_define (line 589) | void cpp_define(char *name, char *def)
function cpp_read (line 604) | int cpp_read(char **obuf, long *olen)
function eval_tok (line 690) | static int eval_tok(void)
function eval_see (line 730) | static int eval_see(void)
function eval_get (line 737) | static int eval_get(void)
function eval_num (line 747) | static long eval_num(void)
function eval_jmp (line 752) | static int eval_jmp(int tok)
function eval_expect (line 761) | static void eval_expect(int tok)
function evalatom (line 773) | static long evalatom(void)
function evalpre (line 794) | static long evalpre(void)
function evalmul (line 805) | static long evalmul(void)
function evaladd (line 826) | static long evaladd(void)
function evalshift (line 843) | static long evalshift(void)
function evalcmp (line 860) | static long evalcmp(void)
function evaleq (line 885) | static long evaleq(void)
function evalbitand (line 902) | static long evalbitand(void)
function evalxor (line 910) | static long evalxor(void)
function evalbitor (line 918) | static long evalbitor(void)
function evaland (line 926) | static long evaland(void)
function evalor (line 934) | static long evalor(void)
function evalcexpr (line 942) | static long evalcexpr(void)
function evalexpr (line 954) | static long evalexpr(void)
function buf_loc (line 960) | static int buf_loc(char *s, int off)
FILE: gen.c
type mem (line 7) | struct mem
type mem (line 8) | struct mem
type ic (line 10) | struct ic
function loc_add (line 36) | static long loc_add(long pos)
function o_mklocal (line 46) | long o_mklocal(long sz)
function o_rmlocal (line 52) | void o_rmlocal(long addr, long sz)
function o_arg2loc (line 56) | long o_arg2loc(int i)
function o_bsnew (line 61) | void o_bsnew(char *name, long size, int global)
function o_dsnew (line 67) | long o_dsnew(char *name, long size, int global)
function o_dscpy (line 83) | void o_dscpy(long addr, void *buf, long len)
function dat_off (line 88) | static int dat_off(char *name)
function o_dsset (line 97) | void o_dsset(char *name, long off, long bt)
function ra_vreg (line 111) | static int ra_vreg(int val)
function ra_lreg (line 120) | static int ra_lreg(int loc)
function ra_lmask (line 130) | static long ra_lmask(void)
function ra_vmask (line 141) | static long ra_vmask(void)
function ra_regscn (line 152) | static long ra_regscn(long mask)
function ra_regget (line 162) | static long ra_regget(long iv, long gmask, long amask, long bmask)
function ra_regcheap (line 184) | static long ra_regcheap(long mask)
function ra_map (line 191) | static void ra_map(int *rd, int *r1, int *r2, int *r3, long *mt)
function iv_rank (line 269) | static long iv_rank(long iv)
function iv_addr (line 279) | static long iv_addr(long rank)
function loc_toreg (line 284) | static void loc_toreg(long loc, long off, int reg, int bt)
function loc_tomem (line 290) | static void loc_tomem(long loc, long off, int reg, int bt)
function loc_toadd (line 296) | static void loc_toadd(long loc, long off, int reg)
function loc_isread (line 303) | static int loc_isread(long loc)
function val_toreg (line 312) | static void val_toreg(long val, int reg)
function val_tomem (line 317) | static void val_tomem(long val, int reg)
function ra_spill (line 325) | static void ra_spill(int reg)
function ra_vsave (line 339) | static void ra_vsave(long iv, int reg)
function ra_vload (line 352) | static void ra_vload(long iv, int reg)
function ra_vdrop (line 368) | static void ra_vdrop(long iv)
function ra_vmove (line 379) | static void ra_vmove(long iv, long mask)
function ra_lload (line 393) | static void ra_lload(long loc, long off, int reg, int bt)
function ra_lsave (line 424) | static void ra_lsave(long loc, long off, int reg, int bt)
function ra_bbend (line 446) | static void ra_bbend(void)
function ra_init (line 466) | static void ra_init(struct ic *ic, long ic_n)
function ra_done (line 511) | static void ra_done(void)
function ic_gencode (line 518) | static void ic_gencode(struct ic *ic, long ic_n)
function ic_reset (line 625) | static void ic_reset(void)
function o_func_beg (line 636) | void o_func_beg(char *name, int argc, int global, int varg)
function o_code (line 648) | void o_code(char *name, char *c, long c_len)
function o_func_end (line 654) | void o_func_end(void)
function o_write (line 710) | void o_write(int fd)
FILE: int.c
type ic (line 7) | struct ic
type ic (line 27) | struct ic
type ic (line 29) | struct ic
function ic_back (line 44) | static void ic_back(long pos)
function iv_pop (line 53) | static long iv_pop(void)
function iv_get (line 58) | static long iv_get(int n)
function iv_put (line 63) | static void iv_put(long n)
function iv_drop (line 68) | static void iv_drop(int n)
function iv_swap (line 73) | static void iv_swap(int x, int y)
function iv_dup (line 80) | static void iv_dup(void)
function o_num (line 86) | void o_num(long n)
function o_local (line 91) | void o_local(long id)
function o_sym (line 96) | void o_sym(char *sym)
function o_tmpdrop (line 101) | void o_tmpdrop(int n)
function o_tmpswap (line 106) | void o_tmpswap(void)
function o_tmpcopy (line 111) | void o_tmpcopy(void)
function ic_const (line 117) | static int ic_const(long iv)
function ic_load (line 124) | static int ic_load(long iv)
function o_bop (line 137) | void o_bop(long op)
function o_uop (line 155) | void o_uop(long op)
function o_assign (line 165) | void o_assign(long bt)
function o_deref (line 180) | void o_deref(long bt)
function o_cast (line 188) | void o_cast(long bt)
function o_memcpy (line 198) | void o_memcpy(void)
function o_memset (line 206) | void o_memset(void)
function o_call (line 214) | void o_call(int argc, int ret)
function o_ret (line 237) | void o_ret(int ret)
function o_label (line 244) | void o_label(long id)
function o_jmp (line 256) | void o_jmp(long id)
function o_jz (line 261) | void o_jz(long id)
function o_popnum (line 268) | int o_popnum(long *n)
function o_popsym (line 276) | int o_popsym(long *sym, long *off)
function o_mark (line 284) | long o_mark(void)
function o_back (line 289) | void o_back(long mark)
function ic_get (line 294) | void ic_get(struct ic **c, long *n)
function ic_free (line 316) | void ic_free(struct ic *ic)
function cb (line 324) | static long cb(long op, long *r, long a, long b)
function cu (line 383) | static long cu(int op, long i)
function c_cast (line 396) | static long c_cast(long n, unsigned bt)
function ic_num (line 406) | int ic_num(struct ic *ic, long iv, long *n)
function ic_sym (line 437) | int ic_sym(struct ic *ic, long iv, long *sym, long *off)
function ic_off (line 465) | static int ic_off(struct ic *ic, long iv, long *base_iv, long *off)
function ic_regcnt (line 501) | int ic_regcnt(struct ic *ic)
type ic (line 533) | struct ic
function io_num (line 561) | static int io_num(void)
function log2a (line 572) | static int log2a(unsigned long n)
function iv_num (line 583) | static long iv_num(long n)
function io_mul2 (line 590) | static int io_mul2(void)
function io_cmp (line 650) | static int io_cmp(void)
function io_jmp (line 664) | static int io_jmp(void)
function io_addr (line 684) | static int io_addr(void)
function imm_ok (line 702) | static int imm_ok(long op, long n, int arg)
function io_loc (line 711) | static int io_loc(void)
function flip_cond (line 749) | static int flip_cond(int op) {
function io_imm (line 756) | static int io_imm(void)
function io_call (line 806) | static int io_call(void)
function io_deadcode (line 819) | static void io_deadcode(void)
FILE: mem.c
function mem_extend (line 9) | static void mem_extend(struct mem *mem)
function mem_init (line 19) | void mem_init(struct mem *mem)
function mem_done (line 24) | void mem_done(struct mem *mem)
function mem_cut (line 30) | void mem_cut(struct mem *mem, long pos)
function mem_cpy (line 35) | void mem_cpy(struct mem *mem, long off, void *buf, long len)
function mem_put (line 42) | void mem_put(struct mem *mem, void *buf, long len)
function mem_putc (line 48) | void mem_putc(struct mem *mem, int c)
function mem_putz (line 55) | void mem_putz(struct mem *mem, long sz)
type mem (line 64) | struct mem
function mem_len (line 72) | long mem_len(struct mem *mem)
type mem (line 77) | struct mem
FILE: ncc.c
type type (line 61) | struct type {
type type (line 70) | struct type
function ts_push_bt (line 73) | static void ts_push_bt(unsigned bt)
function ts_push (line 84) | static void ts_push(struct type *t)
function ts_push_addr (line 90) | static void ts_push_addr(struct type *t)
function ts_pop (line 96) | static void ts_pop(struct type *type)
function err (line 103) | void err(char *fmt, ...)
type name (line 122) | struct name {
type name (line 129) | struct name
type name (line 131) | struct name
function local_add (line 134) | static void local_add(struct name *name)
function local_find (line 143) | static int local_find(char *name)
function global_find (line 152) | static int global_find(char *name)
function global_add (line 161) | static void global_add(struct name *name)
type enumval (line 176) | struct enumval {
function enum_add (line 182) | static void enum_add(char *name, int val)
function enum_find (line 194) | static int enum_find(int *val, char *name)
type typdefinfo (line 205) | struct typdefinfo {
function typedef_add (line 211) | static void typedef_add(char *name, struct type *type)
function typedef_find (line 224) | static int typedef_find(char *name)
type array (line 233) | struct array {
function array_add (line 239) | static int array_add(struct type *type, int n)
function array2ptr (line 252) | static void array2ptr(struct type *t)
type structinfo (line 260) | struct structinfo {
function struct_find (line 269) | static int struct_find(char *name, int isunion)
type name (line 287) | struct name
type structinfo (line 289) | struct structinfo
function type_totsz (line 299) | static int type_totsz(struct type *t)
function type_szde (line 309) | static unsigned type_szde(struct type *t)
function ts_de (line 318) | static void ts_de(int deref)
function ts_pop_de (line 328) | static void ts_pop_de(struct type *t)
function ts_pop_de2 (line 335) | static void ts_pop_de2(struct type *t1, struct type *t2)
function tok_jmp (line 352) | static int tok_jmp(char *tok)
function tok_comes (line 360) | static int tok_comes(char *tok)
function tok_req (line 365) | static void tok_req(char *tok)
function tok_grp (line 372) | static int tok_grp(void)
function bt_op (line 385) | static unsigned bt_op(unsigned bt1, unsigned bt2)
function bt_uop (line 406) | static unsigned bt_uop(unsigned bt)
function ts_binop (line 412) | static void ts_binop(int op)
function ts_addop (line 427) | static void ts_addop(int op)
type type (line 459) | struct type
type type (line 459) | struct type
type type (line 460) | struct type
type name (line 461) | struct name
type name (line 463) | struct name
type type (line 468) | struct type
type type (line 469) | struct type
function type_alignment (line 471) | static int type_alignment(struct type *t)
function structdef (line 480) | static void structdef(long data, struct name *name, unsigned flags)
function struct_create (line 499) | static int struct_create(char *name, int isunion)
function enum_create (line 512) | static void enum_create(void)
function readprimary (line 546) | static void readprimary(void)
function arrayderef (line 622) | static void arrayderef(void)
function inc_post (line 645) | static void inc_post(int op)
function readfield (line 665) | static void readfield(void)
type funcinfo (line 679) | struct funcinfo {
function func_create (line 690) | static int func_create(struct type *ret, char *name, char argnames[][NAM...
function readcall (line 711) | static void readcall(void)
function readpost (line 738) | static void readpost(void)
function inc_pre (line 773) | static void inc_pre(int op)
function readpre (line 789) | static void readpre(void)
function readmul (line 875) | static void readmul(void)
function readadd (line 898) | static void readadd(void)
function shift (line 916) | static void shift(int op)
function readshift (line 925) | static void readshift(void)
function cmp (line 941) | static void cmp(int op)
function readcmp (line 952) | static void readcmp(void)
function eq (line 976) | static void eq(int op)
function readeq (line 984) | static void readeq(void)
function readbitand (line 1000) | static void readbitand(void)
function readxor (line 1009) | static void readxor(void)
function readbitor (line 1018) | static void readbitor(void)
function savelocal (line 1027) | static void savelocal(long val, int bt)
function loadlocal (line 1035) | static void loadlocal(long val, int bt)
function readand (line 1042) | static void readand(void)
function reador (line 1070) | static void reador(void)
function readcexpr_const (line 1102) | static int readcexpr_const(void)
function readcexpr (line 1129) | static void readcexpr(void)
function opassign (line 1167) | static void opassign(int op, int ptrop)
function doassign (line 1182) | static void doassign(void)
function readexpr (line 1196) | static void readexpr(void)
function readestmt (line 1246) | static void readestmt(void)
function globalinit (line 1257) | static void globalinit(void *obj, int off, struct type *t)
type name (line 1277) | struct name
function globaldef (line 1279) | static void globaldef(long data, struct name *name, unsigned flags)
function o_localoff (line 1302) | static void o_localoff(long addr, int off)
function localinit (line 1311) | static void localinit(void *obj, int off, struct type *t)
function localdef (line 1338) | static void localdef(long data, struct name *name, unsigned flags)
function typedefdef (line 1367) | static void typedefdef(long data, struct name *name, unsigned flags)
function readswitch (line 1374) | static void readswitch(void)
function label_id (line 1436) | static int label_id(char *name)
function readstmt (line 1454) | static void readstmt(void)
function readfunc (line 1615) | static void readfunc(struct name *name, int flags)
function readdecl (line 1634) | static void readdecl(void)
function parse (line 1645) | static void parse(void)
function compat_macros (line 1651) | static void compat_macros(void)
function opt (line 1673) | int opt(int level)
function main (line 1678) | int main(int argc, char *argv[])
function basetype (line 1757) | static int basetype(struct type *type, unsigned *flags)
function readptrs (line 1830) | static void readptrs(struct type *type)
function readargs (line 1840) | static int readargs(struct type *args, char argnames[][NAMELEN], int *varg)
function krdef (line 1870) | static void krdef(long data, struct name *name, unsigned flags)
type type (line 1889) | struct type
type type (line 1889) | struct type
type type (line 1892) | struct type
type type (line 1917) | struct type
type type (line 1917) | struct type
function innertype_modify (line 1924) | static void innertype_modify(struct type *t, struct type *s)
function readname (line 1941) | static int readname(struct type *main, char *name, struct type *base)
function readtype (line 1993) | static int readtype(struct type *type)
function readdefs (line 2007) | static int readdefs(void (*def)(long data, struct name *name, unsigned f...
function readdefs_int (line 2026) | static int readdefs_int(void (*def)(long data, struct name *name, unsign...
function jumpbrace (line 2055) | static void jumpbrace(void)
function initsize (line 2065) | static int initsize(void)
function initexpr (line 2099) | static void initexpr(struct type *t, int off, void *obj,
FILE: ncc.h
type mem (line 37) | struct mem {
type mem (line 43) | struct mem
type mem (line 44) | struct mem
type mem (line 45) | struct mem
type mem (line 46) | struct mem
type mem (line 47) | struct mem
type mem (line 48) | struct mem
type mem (line 49) | struct mem
type mem (line 50) | struct mem
type mem (line 51) | struct mem
type mem (line 52) | struct mem
type ic (line 186) | struct ic {
type ic (line 195) | struct ic
type ic (line 196) | struct ic
type ic (line 197) | struct ic
type ic (line 198) | struct ic
type ic (line 199) | struct ic
type ic (line 200) | struct ic
type ic (line 203) | struct ic
FILE: out.c
function symstr_add (line 61) | static long symstr_add(char *name)
function sym_find (line 73) | static long sym_find(char *name)
function Elf_Sym (line 82) | static Elf_Sym *put_sym(char *name)
function mvrela (line 101) | static void mvrela(long *mv, Elf_Rel *rels, long n)
function syms_sort (line 111) | static int syms_sort(void)
function out_init (line 142) | void out_init(long flags)
function out_def (line 148) | void out_def(char *name, long flags, long off, long len)
function out_sym (line 164) | long out_sym(char *name)
function out_csrel (line 169) | static void out_csrel(long idx, long off, int flags)
function out_dsrel (line 181) | static void out_dsrel(long idx, long off, int flags)
function out_rel (line 193) | void out_rel(long idx, long flags, long off)
function bss_len (line 201) | static long bss_len(void)
function out_write (line 214) | void out_write(int fd, char *cs, long cslen, char *ds, long dslen)
function ehdr_init (line 319) | static void ehdr_init(Elf_Ehdr *ehdr)
function rel_type (line 325) | static int rel_type(int flags)
function ehdr_init (line 335) | static void ehdr_init(Elf_Ehdr *ehdr)
function rel_type (line 340) | static int rel_type(int flags)
function ehdr_init (line 351) | static void ehdr_init(Elf_Ehdr *ehdr)
function rel_type (line 356) | static int rel_type(int flags)
FILE: reg.c
function ic_loc (line 9) | static int ic_loc(struct ic *ic, long iv, long *loc, long *off)
type rgn (line 26) | struct rgn {
type rgn (line 34) | struct rgn
function rgn_add (line 44) | static void rgn_add(long loc, long beg, long end, long cnt)
function rgn_available (line 75) | static int rgn_available(long beg, long end, int reg)
function reg_region (line 85) | static long reg_region(struct ic *ic, long ic_n, long loc, long pos,
function reg_regions (line 114) | static void reg_regions(struct ic *ic, long ic_n, long loc)
function reg_loccnt (line 136) | static long reg_loccnt(struct ic *ic, long ic_n, long loc)
function reg_glob (line 147) | static void reg_glob(int leaf)
function reg_init (line 187) | void reg_init(struct ic *ic, long ic_n)
function reg_mask (line 240) | long reg_mask(void)
function reg_lmap (line 251) | int reg_lmap(long c, long loc)
function reg_rmap (line 262) | int reg_rmap(long c, long reg)
function reg_done (line 272) | void reg_done(void)
function reg_safe (line 285) | int reg_safe(long loc)
FILE: tok.c
type mem (line 9) | struct mem
type mem (line 10) | struct mem
function esc_char (line 36) | static int esc_char(int *c, char *s)
function tok_num (line 67) | long tok_num(char *tok, long *num)
function id_char (line 108) | static int id_char(int c)
function skipws (line 113) | static int skipws(void)
function tok_read (line 155) | static int tok_read(void)
function tok_len (line 236) | long tok_len(void)
function tok_addr (line 241) | long tok_addr(void)
function tok_jump (line 246) | void tok_jump(long addr)
function tok_done (line 253) | void tok_done(void)
FILE: x64.c
type mem (line 47) | struct mem
function os (line 50) | static void os(void *s, int n)
function oi (line 66) | static void oi(long n, int l)
function oi_at (line 71) | static void oi_at(long pos, long n, int l)
function opos (line 76) | static long opos(void)
function op_x (line 81) | static void op_x(int op, int r1, int r2, int bt)
function op_rm (line 105) | static void op_rm(int op, int src, int base, int off, int bt)
function op_rr (line 119) | static void op_rr(int op, int src, int dst, int bt)
function movrx_op (line 127) | static int movrx_op(int bt, int mov)
function mov_r2r (line 139) | static void mov_r2r(int rd, int r1, unsigned bt)
function i_push (line 145) | static void i_push(int reg)
function i_pop (line 150) | static void i_pop(int reg)
function i_mov (line 155) | void i_mov(int rd, int rn)
function i_add (line 160) | static void i_add(int op, int rd, int r1, int r2)
function i_add_imm (line 167) | static void i_add_imm(int op, int rd, int rn, long n)
function i_num (line 175) | static void i_num(int rd, long n)
function i_mul (line 193) | static void i_mul(int rd, int r1, int r2)
function i_div (line 200) | static void i_div(int op, int rd, int r1, int r2)
function i_tst (line 212) | static void i_tst(int rn, int rm)
function i_cmp (line 217) | static void i_cmp(int rn, int rm)
function i_cmp_imm (line 222) | static void i_cmp_imm(int rn, long n)
function i_shl (line 228) | static void i_shl(int op, int rd, int r1, int rs)
function i_shl_imm (line 237) | static void i_shl_imm(int op, int rd, int rn, long n)
function i_neg (line 245) | static void i_neg(int rd)
function i_not (line 250) | static void i_not(int rd)
function i_cond (line 255) | static int i_cond(long op)
function i_set (line 264) | static void i_set(long op, int rd)
function i_lnot (line 272) | static void i_lnot(int rd)
function jx (line 281) | static void jx(int x, int nbytes)
function i_jcmp (line 294) | static void i_jcmp(long op, long rn, long rm)
function i_jmp (line 307) | static long i_jmp(long op, int nb)
function i_jlen (line 320) | static int i_jlen(long op, int nb)
function i_zx (line 328) | static void i_zx(int rd, int r1, int bits)
function i_sx (line 339) | static void i_sx(int rd, int r1, int bits)
function i_cast (line 344) | static void i_cast(int rd, int rn, int bt)
function i_add_anyimm (line 357) | static void i_add_anyimm(int rd, int rn, long n)
function lab_add (line 375) | static void lab_add(long id)
function jmp_add (line 385) | static void jmp_add(long op, long off, long dst)
function i_label (line 399) | void i_label(long id)
function i_rel (line 404) | static void i_rel(long sym, long flg, long off)
function i_sym (line 418) | static void i_sym(int rd, int sym, int off)
function i_saveargs (line 429) | static void i_saveargs(long sargs)
function i_subsp (line 439) | static void i_subsp(long val)
function regs_count (line 452) | static int regs_count(long regs)
function regs_save (line 462) | static void regs_save(long sregs, long dis)
function regs_load (line 472) | static void regs_load(long sregs, long dis)
function i_wrap (line 482) | void i_wrap(int argc, long sargs, long spsub, int initfp, long sregs, lo...
function i_shortjumps (line 539) | static void i_shortjumps(int *nb)
function i_code (line 570) | void i_code(char **c, long *c_len, long **rsym, long **rflg, long **roff...
function i_done (line 597) | void i_done(void)
function i_reg (line 605) | long i_reg(long op, long *rd, long *r1, long *r2, long *r3, long *tmp)
function i_imm (line 707) | int i_imm(long lim, long n)
function i_ins (line 713) | long i_ins(long op, long rd, long r1, long r2, long r3)
FILE: x86.c
type mem (line 47) | struct mem
function os (line 50) | static void os(void *s, int n)
function oi (line 66) | static void oi(long n, int l)
function oi_at (line 71) | static void oi_at(long pos, long n, int l)
function opos (line 76) | static long opos(void)
function op_x (line 81) | static void op_x(int op, int r1, int r2, int bt)
function op_rm (line 94) | static void op_rm(int op, int src, int base, int off, int bt)
function op_rr (line 108) | static void op_rr(int op, int src, int dst, int bt)
function movrx_op (line 116) | static int movrx_op(int bt, int mov)
function mov_r2r (line 126) | static void mov_r2r(int rd, int r1, unsigned bt)
function i_push (line 132) | static void i_push(int reg)
function i_pop (line 137) | static void i_pop(int reg)
function i_mov (line 142) | static void i_mov(int rd, int rn)
function i_add (line 147) | static void i_add(int op, int rd, int r1, int r2)
function i_add_imm (line 154) | static void i_add_imm(int op, int rd, int rn, long n)
function i_num (line 162) | static void i_num(int rd, long n)
function i_mul (line 173) | static void i_mul(int rd, int r1, int r2)
function i_div (line 180) | static void i_div(int op, int rd, int r1, int r2)
function i_tst (line 192) | static void i_tst(int rn, int rm)
function i_cmp (line 197) | static void i_cmp(int rn, int rm)
function i_cmp_imm (line 202) | static void i_cmp_imm(int rn, long n)
function i_shl (line 208) | static void i_shl(int op, int rd, int r1, int rs)
function i_shl_imm (line 217) | static void i_shl_imm(int op, int rd, int rn, long n)
function i_neg (line 225) | static void i_neg(int rd)
function i_not (line 230) | static void i_not(int rd)
function i_cond (line 235) | static int i_cond(long op)
function i_set (line 244) | static void i_set(long op, int rd)
function i_lnot (line 252) | static void i_lnot(int rd)
function jx (line 260) | static void jx(int x, int nbytes)
function i_jcmp (line 272) | static void i_jcmp(long op, long rn, long rm)
function i_jmp (line 285) | static long i_jmp(long op, int nb)
function i_jlen (line 298) | static int i_jlen(long op, int nb)
function i_zx (line 306) | static void i_zx(int rd, int r1, int bits)
function i_sx (line 317) | static void i_sx(int rd, int r1, int bits)
function i_cast (line 322) | static void i_cast(int rd, int rn, int bt)
function i_add_anyimm (line 335) | static void i_add_anyimm(int rd, int rn, long n)
function lab_add (line 353) | static void lab_add(long id)
function jmp_add (line 363) | static void jmp_add(long op, long off, long dst)
function i_label (line 377) | void i_label(long id)
function i_rel (line 382) | static void i_rel(long sym, long flg, long off)
function i_sym (line 396) | static void i_sym(int rd, int sym, int off)
function i_subsp (line 403) | static void i_subsp(long val)
function regs_count (line 416) | static int regs_count(long regs)
function regs_save (line 426) | static void regs_save(long sregs, long dis)
function regs_load (line 436) | static void regs_load(long sregs, long dis)
function i_wrap (line 446) | void i_wrap(int argc, long sargs, long spsub, int initfp, long sregs, lo...
function i_shortjumps (line 490) | static void i_shortjumps(int *nb)
function i_code (line 521) | void i_code(char **c, long *c_len, long **rsym, long **rflg, long **roff...
function i_done (line 548) | void i_done(void)
function i_reg (line 556) | long i_reg(long op, long *rd, long *r1, long *r2, long *r3, long *tmp)
function i_imm (line 662) | int i_imm(long lim, long n)
function i_ins (line 668) | long i_ins(long op, long rd, long r1, long r2, long r3)
Condensed preview — 18 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (202K chars).
[
{
"path": "Makefile",
"chars": 326,
"preview": "# output architecture: x64, x86, arm\nOUT = x64\n\nCC = cc\nCFLAGS = -Wall -O2 -DNEATCC_`echo $(OUT) | tr \"[:lower:]\" \"[:upp"
},
{
"path": "README",
"chars": 216,
"preview": "NEATCC\n======\n\nNeatcc is an ARM/x86(-64) C compiler. It supports a large subset of\nANSI C but lacks some of its feature"
},
{
"path": "arm.c",
"chars": 18219,
"preview": "/* architecture-dependent code generation for ARM */\n#include <stdlib.h>\n#include <string.h>\n#include \"ncc.h\"\n\n#define M"
},
{
"path": "arm.h",
"chars": 676,
"preview": "/* architecture-dependent header for ARM */\n#define LONGSZ\t\t4\t/* word size */\n#define I_ARCH\t\t\"__arm__\"\n\n#define N_REGS\t"
},
{
"path": "cpp.c",
"chars": 17721,
"preview": "/* neatcc preprocessor */\n#include <ctype.h>\n#include <fcntl.h>\n#include <stdarg.h>\n#include <stddef.h>\n#include <stdio."
},
{
"path": "div.s",
"chars": 955,
"preview": "@ ARM software division implementation\n@ These functions are assembled using neatas and are included in gen.c\n.global __"
},
{
"path": "gen.c",
"chars": 18155,
"preview": "/* neatcc code generation */\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include \"ncc.h\"\n\nstatic struct "
},
{
"path": "int.c",
"chars": 16052,
"preview": "/* neatcc intermediate code generation */\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include \"ncc.h\"\n\ns"
},
{
"path": "mem.c",
"chars": 1423,
"preview": "#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"ncc.h\"\n\n#define MEMSZ\t\t512\n\nsta"
},
{
"path": "ncc.c",
"chars": 43784,
"preview": "/*\n * THE NEATCC C COMPILER\n *\n * Copyright (C) 2010-2016 Ali Gholami Rudi\n *\n * Permission to use, copy, modify, and/or"
},
{
"path": "ncc.h",
"chars": 10513,
"preview": "/*\n * THE NEATCC C COMPILER\n *\n * This header file is organized as follows:\n *\n * 0. helper functions and data structure"
},
{
"path": "out.c",
"chars": 8503,
"preview": "/* neatcc ELF object generation */\n#include <elf.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include "
},
{
"path": "reg.c",
"chars": 6443,
"preview": "/* neatcc global register allocation */\n#include <stdio.h>\n#include <stdlib.h>\n#include \"ncc.h\"\n\n#define IC_LLD(ic, i)\t\t"
},
{
"path": "tok.c",
"chars": 5090,
"preview": "/* neatcc tokenizer */\n#include <ctype.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>"
},
{
"path": "x64.c",
"chars": 16488,
"preview": "/* architecture-dependent code generation for x86_64 */\n#include <stdlib.h>\n#include \"ncc.h\"\n\n/* x86-64 registers, witho"
},
{
"path": "x64.h",
"chars": 691,
"preview": "/* architecture-dependent header for x86_64 */\n#define LONGSZ\t\t8\t/* word size */\n#define I_ARCH\t\t\"__x86_64__\"\n\n#define N"
},
{
"path": "x86.c",
"chars": 15373,
"preview": "/* architecture-dependent code generation for x86 */\n#include <stdlib.h>\n#include \"ncc.h\"\n\n/* x86-64 registers, without "
},
{
"path": "x86.h",
"chars": 626,
"preview": "/* architecture-dependent header for x86 */\n#define LONGSZ\t\t4\t/* word size */\n#define I_ARCH\t\t\"__i386__\"\n\n#define N_REGS"
}
]
About this extraction
This page contains the full source code of the aligrudi/neatcc GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 18 files (177.0 KB), approximately 67.5k tokens, and a symbol index with 502 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.