summaryrefslogtreecommitdiff
path: root/il-gen.c
diff options
context:
space:
mode:
authorThomas Preud'homme <thomas.preudhomme@celest.fr>2010-03-13 11:02:43 +0100
committerThomas Preud'homme <thomas.preudhomme@celest.fr>2010-03-13 11:02:43 +0100
commitfa3f0c7c00c4e5da92e4e4f1a06f193507d56261 (patch)
treeffa81e17bcc8c9ae4f70d0de727cbba6ed5f7306 /il-gen.c
Imported Upstream version 0.9.24
Diffstat (limited to 'il-gen.c')
-rw-r--r--il-gen.c667
1 files changed, 667 insertions, 0 deletions
diff --git a/il-gen.c b/il-gen.c
new file mode 100644
index 0000000..fef6194
--- /dev/null
+++ b/il-gen.c
@@ -0,0 +1,667 @@
+/*
+ * CIL code generator for TCC
+ *
+ * Copyright (c) 2002 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* number of available registers */
+#define NB_REGS 3
+
+/* a register can belong to several classes. The classes must be
+ sorted from more general to more precise (see gv2() code which does
+ assumptions on it). */
+#define RC_ST 0x0001 /* any stack entry */
+#define RC_ST0 0x0002 /* top of stack */
+#define RC_ST1 0x0004 /* top - 1 */
+
+#define RC_INT RC_ST
+#define RC_FLOAT RC_ST
+#define RC_IRET RC_ST0 /* function return: integer register */
+#define RC_LRET RC_ST0 /* function return: second integer register */
+#define RC_FRET RC_ST0 /* function return: float register */
+
+/* pretty names for the registers */
+enum {
+ REG_ST0 = 0,
+ REG_ST1,
+ REG_ST2,
+};
+
+int reg_classes[NB_REGS] = {
+ /* ST0 */ RC_ST | RC_ST0,
+ /* ST1 */ RC_ST | RC_ST1,
+ /* ST2 */ RC_ST,
+};
+
+/* return registers for function */
+#define REG_IRET REG_ST0 /* single word int return register */
+#define REG_LRET REG_ST0 /* second word return register (for long long) */
+#define REG_FRET REG_ST0 /* float return register */
+
+/* defined if function parameters must be evaluated in reverse order */
+//#define INVERT_FUNC_PARAMS
+
+/* defined if structures are passed as pointers. Otherwise structures
+ are directly pushed on stack. */
+//#define FUNC_STRUCT_PARAM_AS_PTR
+
+/* pointer size, in bytes */
+#define PTR_SIZE 4
+
+/* long double size and alignment, in bytes */
+#define LDOUBLE_SIZE 8
+#define LDOUBLE_ALIGN 8
+
+/* function call context */
+typedef struct GFuncContext {
+ int func_call; /* func call type (FUNC_STDCALL or FUNC_CDECL) */
+} GFuncContext;
+
+/******************************************************/
+/* opcode definitions */
+
+#define IL_OP_PREFIX 0xFE
+
+enum ILOPCodes {
+#define OP(name, str, n) IL_OP_ ## name = n,
+#include "il-opcodes.h"
+#undef OP
+};
+
+char *il_opcodes_str[] = {
+#define OP(name, str, n) [n] = str,
+#include "il-opcodes.h"
+#undef OP
+};
+
+/******************************************************/
+
+/* arguments variable numbers start from there */
+#define ARG_BASE 0x70000000
+
+static FILE *il_outfile;
+
+static void out_byte(int c)
+{
+ *(char *)ind++ = c;
+}
+
+static void out_le32(int c)
+{
+ out_byte(c);
+ out_byte(c >> 8);
+ out_byte(c >> 16);
+ out_byte(c >> 24);
+}
+
+static void init_outfile(void)
+{
+ if (!il_outfile) {
+ il_outfile = stdout;
+ fprintf(il_outfile,
+ ".assembly extern mscorlib\n"
+ "{\n"
+ ".ver 1:0:2411:0\n"
+ "}\n\n");
+ }
+}
+
+static void out_op1(int op)
+{
+ if (op & 0x100)
+ out_byte(IL_OP_PREFIX);
+ out_byte(op & 0xff);
+}
+
+/* output an opcode with prefix */
+static void out_op(int op)
+{
+ out_op1(op);
+ fprintf(il_outfile, " %s\n", il_opcodes_str[op]);
+}
+
+static void out_opb(int op, int c)
+{
+ out_op1(op);
+ out_byte(c);
+ fprintf(il_outfile, " %s %d\n", il_opcodes_str[op], c);
+}
+
+static void out_opi(int op, int c)
+{
+ out_op1(op);
+ out_le32(c);
+ fprintf(il_outfile, " %s 0x%x\n", il_opcodes_str[op], c);
+}
+
+/* XXX: not complete */
+static void il_type_to_str(char *buf, int buf_size,
+ int t, const char *varstr)
+{
+ int bt;
+ Sym *s, *sa;
+ char buf1[256];
+ const char *tstr;
+
+ t = t & VT_TYPE;
+ bt = t & VT_BTYPE;
+ buf[0] = '\0';
+ if (t & VT_UNSIGNED)
+ pstrcat(buf, buf_size, "unsigned ");
+ switch(bt) {
+ case VT_VOID:
+ tstr = "void";
+ goto add_tstr;
+ case VT_BOOL:
+ tstr = "bool";
+ goto add_tstr;
+ case VT_BYTE:
+ tstr = "int8";
+ goto add_tstr;
+ case VT_SHORT:
+ tstr = "int16";
+ goto add_tstr;
+ case VT_ENUM:
+ case VT_INT:
+ case VT_LONG:
+ tstr = "int32";
+ goto add_tstr;
+ case VT_LLONG:
+ tstr = "int64";
+ goto add_tstr;
+ case VT_FLOAT:
+ tstr = "float32";
+ goto add_tstr;
+ case VT_DOUBLE:
+ case VT_LDOUBLE:
+ tstr = "float64";
+ add_tstr:
+ pstrcat(buf, buf_size, tstr);
+ break;
+ case VT_STRUCT:
+ error("structures not handled yet");
+ break;
+ case VT_FUNC:
+ s = sym_find((unsigned)t >> VT_STRUCT_SHIFT);
+ il_type_to_str(buf, buf_size, s->t, varstr);
+ pstrcat(buf, buf_size, "(");
+ sa = s->next;
+ while (sa != NULL) {
+ il_type_to_str(buf1, sizeof(buf1), sa->t, NULL);
+ pstrcat(buf, buf_size, buf1);
+ sa = sa->next;
+ if (sa)
+ pstrcat(buf, buf_size, ", ");
+ }
+ pstrcat(buf, buf_size, ")");
+ goto no_var;
+ case VT_PTR:
+ s = sym_find((unsigned)t >> VT_STRUCT_SHIFT);
+ pstrcpy(buf1, sizeof(buf1), "*");
+ if (varstr)
+ pstrcat(buf1, sizeof(buf1), varstr);
+ il_type_to_str(buf, buf_size, s->t, buf1);
+ goto no_var;
+ }
+ if (varstr) {
+ pstrcat(buf, buf_size, " ");
+ pstrcat(buf, buf_size, varstr);
+ }
+ no_var: ;
+}
+
+
+/* patch relocation entry with value 'val' */
+void greloc_patch1(Reloc *p, int val)
+{
+}
+
+/* output a symbol and patch all calls to it */
+void gsym_addr(t, a)
+{
+}
+
+/* output jump and return symbol */
+static int out_opj(int op, int c)
+{
+ out_op1(op);
+ out_le32(0);
+ if (c == 0) {
+ c = ind - (int)cur_text_section->data;
+ }
+ fprintf(il_outfile, " %s L%d\n", il_opcodes_str[op], c);
+ return c;
+}
+
+void gsym(int t)
+{
+ fprintf(il_outfile, "L%d:\n", t);
+}
+
+/* load 'r' from value 'sv' */
+void load(int r, SValue *sv)
+{
+ int v, fc, ft;
+
+ v = sv->r & VT_VALMASK;
+ fc = sv->c.i;
+ ft = sv->t;
+
+ if (sv->r & VT_LVAL) {
+ if (v == VT_LOCAL) {
+ if (fc >= ARG_BASE) {
+ fc -= ARG_BASE;
+ if (fc >= 0 && fc <= 4) {
+ out_op(IL_OP_LDARG_0 + fc);
+ } else if (fc <= 0xff) {
+ out_opb(IL_OP_LDARG_S, fc);
+ } else {
+ out_opi(IL_OP_LDARG, fc);
+ }
+ } else {
+ if (fc >= 0 && fc <= 4) {
+ out_op(IL_OP_LDLOC_0 + fc);
+ } else if (fc <= 0xff) {
+ out_opb(IL_OP_LDLOC_S, fc);
+ } else {
+ out_opi(IL_OP_LDLOC, fc);
+ }
+ }
+ } else if (v == VT_CONST) {
+ /* XXX: handle globals */
+ out_opi(IL_OP_LDSFLD, 0);
+ } else {
+ if ((ft & VT_BTYPE) == VT_FLOAT) {
+ out_op(IL_OP_LDIND_R4);
+ } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
+ out_op(IL_OP_LDIND_R8);
+ } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
+ out_op(IL_OP_LDIND_R8);
+ } else if ((ft & VT_TYPE) == VT_BYTE)
+ out_op(IL_OP_LDIND_I1);
+ else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED))
+ out_op(IL_OP_LDIND_U1);
+ else if ((ft & VT_TYPE) == VT_SHORT)
+ out_op(IL_OP_LDIND_I2);
+ else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED))
+ out_op(IL_OP_LDIND_U2);
+ else
+ out_op(IL_OP_LDIND_I4);
+ }
+ } else {
+ if (v == VT_CONST) {
+ /* XXX: handle globals */
+ if (fc >= -1 && fc <= 8) {
+ out_op(IL_OP_LDC_I4_M1 + fc + 1);
+ } else {
+ out_opi(IL_OP_LDC_I4, fc);
+ }
+ } else if (v == VT_LOCAL) {
+ if (fc >= ARG_BASE) {
+ fc -= ARG_BASE;
+ if (fc <= 0xff) {
+ out_opb(IL_OP_LDARGA_S, fc);
+ } else {
+ out_opi(IL_OP_LDARGA, fc);
+ }
+ } else {
+ if (fc <= 0xff) {
+ out_opb(IL_OP_LDLOCA_S, fc);
+ } else {
+ out_opi(IL_OP_LDLOCA, fc);
+ }
+ }
+ } else {
+ /* XXX: do it */
+ }
+ }
+}
+
+/* store register 'r' in lvalue 'v' */
+void store(int r, SValue *sv)
+{
+ int v, fc, ft;
+
+ v = sv->r & VT_VALMASK;
+ fc = sv->c.i;
+ ft = sv->t;
+ if (v == VT_LOCAL) {
+ if (fc >= ARG_BASE) {
+ fc -= ARG_BASE;
+ /* XXX: check IL arg store semantics */
+ if (fc <= 0xff) {
+ out_opb(IL_OP_STARG_S, fc);
+ } else {
+ out_opi(IL_OP_STARG, fc);
+ }
+ } else {
+ if (fc >= 0 && fc <= 4) {
+ out_op(IL_OP_STLOC_0 + fc);
+ } else if (fc <= 0xff) {
+ out_opb(IL_OP_STLOC_S, fc);
+ } else {
+ out_opi(IL_OP_STLOC, fc);
+ }
+ }
+ } else if (v == VT_CONST) {
+ /* XXX: handle globals */
+ out_opi(IL_OP_STSFLD, 0);
+ } else {
+ if ((ft & VT_BTYPE) == VT_FLOAT)
+ out_op(IL_OP_STIND_R4);
+ else if ((ft & VT_BTYPE) == VT_DOUBLE)
+ out_op(IL_OP_STIND_R8);
+ else if ((ft & VT_BTYPE) == VT_LDOUBLE)
+ out_op(IL_OP_STIND_R8);
+ else if ((ft & VT_BTYPE) == VT_BYTE)
+ out_op(IL_OP_STIND_I1);
+ else if ((ft & VT_BTYPE) == VT_SHORT)
+ out_op(IL_OP_STIND_I2);
+ else
+ out_op(IL_OP_STIND_I4);
+ }
+}
+
+/* start function call and return function call context */
+void gfunc_start(GFuncContext *c, int func_call)
+{
+ c->func_call = func_call;
+}
+
+/* push function parameter which is in (vtop->t, vtop->c). Stack entry
+ is then popped. */
+void gfunc_param(GFuncContext *c)
+{
+ if ((vtop->t & VT_BTYPE) == VT_STRUCT) {
+ error("structures passed as value not handled yet");
+ } else {
+ /* simply push on stack */
+ gv(RC_ST0);
+ }
+ vtop--;
+}
+
+/* generate function call with address in (vtop->t, vtop->c) and free function
+ context. Stack entry is popped */
+void gfunc_call(GFuncContext *c)
+{
+ char buf[1024];
+
+ if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
+ /* XXX: more info needed from tcc */
+ il_type_to_str(buf, sizeof(buf), vtop->t, "xxx");
+ fprintf(il_outfile, " call %s\n", buf);
+ } else {
+ /* indirect call */
+ gv(RC_INT);
+ il_type_to_str(buf, sizeof(buf), vtop->t, NULL);
+ fprintf(il_outfile, " calli %s\n", buf);
+ }
+ vtop--;
+}
+
+/* generate function prolog of type 't' */
+void gfunc_prolog(int t)
+{
+ int addr, u, func_call;
+ Sym *sym;
+ char buf[1024];
+
+ init_outfile();
+
+ /* XXX: pass function name to gfunc_prolog */
+ il_type_to_str(buf, sizeof(buf), t, funcname);
+ fprintf(il_outfile, ".method static %s il managed\n", buf);
+ fprintf(il_outfile, "{\n");
+ /* XXX: cannot do better now */
+ fprintf(il_outfile, " .maxstack %d\n", NB_REGS);
+ fprintf(il_outfile, " .locals (int32, int32, int32, int32, int32, int32, int32, int32)\n");
+
+ if (!strcmp(funcname, "main"))
+ fprintf(il_outfile, " .entrypoint\n");
+
+ sym = sym_find((unsigned)t >> VT_STRUCT_SHIFT);
+ func_call = sym->r;
+
+ addr = ARG_BASE;
+ /* if the function returns a structure, then add an
+ implicit pointer parameter */
+ func_vt = sym->t;
+ if ((func_vt & VT_BTYPE) == VT_STRUCT) {
+ func_vc = addr;
+ addr++;
+ }
+ /* define parameters */
+ while ((sym = sym->next) != NULL) {
+ u = sym->t;
+ sym_push(sym->v & ~SYM_FIELD, u,
+ VT_LOCAL | VT_LVAL, addr);
+ addr++;
+ }
+}
+
+/* generate function epilog */
+void gfunc_epilog(void)
+{
+ out_op(IL_OP_RET);
+ fprintf(il_outfile, "}\n\n");
+}
+
+/* generate a jump to a label */
+int gjmp(int t)
+{
+ return out_opj(IL_OP_BR, t);
+}
+
+/* generate a jump to a fixed address */
+void gjmp_addr(int a)
+{
+ /* XXX: handle syms */
+ out_opi(IL_OP_BR, a);
+}
+
+/* generate a test. set 'inv' to invert test. Stack entry is popped */
+int gtst(int inv, int t)
+{
+ int v, *p, c;
+
+ v = vtop->r & VT_VALMASK;
+ if (v == VT_CMP) {
+ c = vtop->c.i ^ inv;
+ switch(c) {
+ case TOK_EQ:
+ c = IL_OP_BEQ;
+ break;
+ case TOK_NE:
+ c = IL_OP_BNE_UN;
+ break;
+ case TOK_LT:
+ c = IL_OP_BLT;
+ break;
+ case TOK_LE:
+ c = IL_OP_BLE;
+ break;
+ case TOK_GT:
+ c = IL_OP_BGT;
+ break;
+ case TOK_GE:
+ c = IL_OP_BGE;
+ break;
+ case TOK_ULT:
+ c = IL_OP_BLT_UN;
+ break;
+ case TOK_ULE:
+ c = IL_OP_BLE_UN;
+ break;
+ case TOK_UGT:
+ c = IL_OP_BGT_UN;
+ break;
+ case TOK_UGE:
+ c = IL_OP_BGE_UN;
+ break;
+ }
+ t = out_opj(c, t);
+ } else if (v == VT_JMP || v == VT_JMPI) {
+ /* && or || optimization */
+ if ((v & 1) == inv) {
+ /* insert vtop->c jump list in t */
+ p = &vtop->c.i;
+ while (*p != 0)
+ p = (int *)*p;
+ *p = t;
+ t = vtop->c.i;
+ } else {
+ t = gjmp(t);
+ gsym(vtop->c.i);
+ }
+ } else {
+ if (is_float(vtop->t)) {
+ vpushi(0);
+ gen_op(TOK_NE);
+ }
+ if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_FORWARD)) == VT_CONST) {
+ /* constant jmp optimization */
+ if ((vtop->c.i != 0) != inv)
+ t = gjmp(t);
+ } else {
+ v = gv(RC_INT);
+ t = out_opj(IL_OP_BRTRUE - inv, t);
+ }
+ }
+ vtop--;
+ return t;
+}
+
+/* generate an integer binary operation */
+void gen_opi(int op)
+{
+ gv2(RC_ST1, RC_ST0);
+ switch(op) {
+ case '+':
+ out_op(IL_OP_ADD);
+ goto std_op;
+ case '-':
+ out_op(IL_OP_SUB);
+ goto std_op;
+ case '&':
+ out_op(IL_OP_AND);
+ goto std_op;
+ case '^':
+ out_op(IL_OP_XOR);
+ goto std_op;
+ case '|':
+ out_op(IL_OP_OR);
+ goto std_op;
+ case '*':
+ out_op(IL_OP_MUL);
+ goto std_op;
+ case TOK_SHL:
+ out_op(IL_OP_SHL);
+ goto std_op;
+ case TOK_SHR:
+ out_op(IL_OP_SHR_UN);
+ goto std_op;
+ case TOK_SAR:
+ out_op(IL_OP_SHR);
+ goto std_op;
+ case '/':
+ case TOK_PDIV:
+ out_op(IL_OP_DIV);
+ goto std_op;
+ case TOK_UDIV:
+ out_op(IL_OP_DIV_UN);
+ goto std_op;
+ case '%':
+ out_op(IL_OP_REM);
+ goto std_op;
+ case TOK_UMOD:
+ out_op(IL_OP_REM_UN);
+ std_op:
+ vtop--;
+ vtop[0].r = REG_ST0;
+ break;
+ case TOK_EQ:
+ case TOK_NE:
+ case TOK_LT:
+ case TOK_LE:
+ case TOK_GT:
+ case TOK_GE:
+ case TOK_ULT:
+ case TOK_ULE:
+ case TOK_UGT:
+ case TOK_UGE:
+ vtop--;
+ vtop[0].r = VT_CMP;
+ vtop[0].c.i = op;
+ break;
+ }
+}
+
+/* generate a floating point operation 'v = t1 op t2' instruction. The
+ two operands are guaranted to have the same floating point type */
+void gen_opf(int op)
+{
+ /* same as integer */
+ gen_opi(op);
+}
+
+/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
+ and 'long long' cases. */
+void gen_cvt_itof(int t)
+{
+ gv(RC_ST0);
+ if (t == VT_FLOAT)
+ out_op(IL_OP_CONV_R4);
+ else
+ out_op(IL_OP_CONV_R8);
+}
+
+/* convert fp to int 't' type */
+/* XXX: handle long long case */
+void gen_cvt_ftoi(int t)
+{
+ gv(RC_ST0);
+ switch(t) {
+ case VT_INT | VT_UNSIGNED:
+ out_op(IL_OP_CONV_U4);
+ break;
+ case VT_LLONG:
+ out_op(IL_OP_CONV_I8);
+ break;
+ case VT_LLONG | VT_UNSIGNED:
+ out_op(IL_OP_CONV_U8);
+ break;
+ default:
+ out_op(IL_OP_CONV_I4);
+ break;
+ }
+}
+
+/* convert from one floating point type to another */
+void gen_cvt_ftof(int t)
+{
+ gv(RC_ST0);
+ if (t == VT_FLOAT) {
+ out_op(IL_OP_CONV_R4);
+ } else {
+ out_op(IL_OP_CONV_R8);
+ }
+}
+
+/* end of CIL code generator */
+/*************************************************************/
+