| #undef VERSION_MAJOR |
| #undef VERSION_MINOR |
| #undef RELEASE_DATE |
| #undef VERSION |
| #define VERSION_MAJOR "0" |
| #define VERSION_MINOR "72" |
| #define RELEASE_DATE "10 February 2010" |
| #define VERSION VERSION_MAJOR "." VERSION_MINOR |
| |
| #include <stdarg.h> |
| #include <errno.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <limits.h> |
| #include <locale.h> |
| #include <time.h> |
| |
| #define MAX_CWD_SIZE 4096 |
| #define MAX_ALLOCATION_PASSES 100 |
| |
| /* NOTE: Before you even start thinking to touch anything |
| * in this code, set DEBUG_ROMCC_WARNINGS to 1 to get an |
| * insight on the original author's thoughts. We introduced |
| * this switch as romcc was about the only thing producing |
| * massive warnings in our code.. |
| */ |
| #define DEBUG_ROMCC_WARNINGS 0 |
| |
| #define DEBUG_CONSISTENCY 1 |
| #define DEBUG_SDP_BLOCKS 0 |
| #define DEBUG_TRIPLE_COLOR 0 |
| |
| #define DEBUG_DISPLAY_USES 1 |
| #define DEBUG_DISPLAY_TYPES 1 |
| #define DEBUG_REPLACE_CLOSURE_TYPE_HIRES 0 |
| #define DEBUG_DECOMPOSE_PRINT_TUPLES 0 |
| #define DEBUG_DECOMPOSE_HIRES 0 |
| #define DEBUG_INITIALIZER 0 |
| #define DEBUG_UPDATE_CLOSURE_TYPE 0 |
| #define DEBUG_LOCAL_TRIPLE 0 |
| #define DEBUG_BASIC_BLOCKS_VERBOSE 0 |
| #define DEBUG_CPS_RENAME_VARIABLES_HIRES 0 |
| #define DEBUG_SIMPLIFY_HIRES 0 |
| #define DEBUG_SHRINKING 0 |
| #define DEBUG_COALESCE_HITCHES 0 |
| #define DEBUG_CODE_ELIMINATION 0 |
| |
| #define DEBUG_EXPLICIT_CLOSURES 0 |
| |
| #if DEBUG_ROMCC_WARNINGS |
| #warning "FIXME give clear error messages about unused variables" |
| #warning "FIXME properly handle multi dimensional arrays" |
| #warning "FIXME handle multiple register sizes" |
| #endif |
| |
| /* Control flow graph of a loop without goto. |
| * |
| * AAA |
| * +---/ |
| * / |
| * / +--->CCC |
| * | | / \ |
| * | | DDD EEE break; |
| * | | \ \ |
| * | | FFF \ |
| * \| / \ \ |
| * |\ GGG HHH | continue; |
| * | \ \ | | |
| * | \ III | / |
| * | \ | / / |
| * | vvv / |
| * +----BBB / |
| * | / |
| * vv |
| * JJJ |
| * |
| * |
| * AAA |
| * +-----+ | +----+ |
| * | \ | / | |
| * | BBB +-+ | |
| * | / \ / | | |
| * | CCC JJJ / / |
| * | / \ / / |
| * | DDD EEE / / |
| * | | +-/ / |
| * | FFF / |
| * | / \ / |
| * | GGG HHH / |
| * | | +-/ |
| * | III |
| * +--+ |
| * |
| * |
| * DFlocal(X) = { Y <- Succ(X) | idom(Y) != X } |
| * DFup(Z) = { Y <- DF(Z) | idom(Y) != X } |
| * |
| * |
| * [] == DFlocal(X) U DF(X) |
| * () == DFup(X) |
| * |
| * Dominator graph of the same nodes. |
| * |
| * AAA AAA: [ ] () |
| * / \ |
| * BBB JJJ BBB: [ JJJ ] ( JJJ ) JJJ: [ ] () |
| * | |
| * CCC CCC: [ ] ( BBB, JJJ ) |
| * / \ |
| * DDD EEE DDD: [ ] ( BBB ) EEE: [ JJJ ] () |
| * | |
| * FFF FFF: [ ] ( BBB ) |
| * / \ |
| * GGG HHH GGG: [ ] ( BBB ) HHH: [ BBB ] () |
| * | |
| * III III: [ BBB ] () |
| * |
| * |
| * BBB and JJJ are definitely the dominance frontier. |
| * Where do I place phi functions and how do I make that decision. |
| * |
| */ |
| |
| struct filelist { |
| const char *filename; |
| struct filelist *next; |
| }; |
| |
| struct filelist *include_filelist = NULL; |
| |
| static void __attribute__((noreturn)) die(char *fmt, ...) |
| { |
| va_list args; |
| |
| va_start(args, fmt); |
| vfprintf(stderr, fmt, args); |
| va_end(args); |
| fflush(stdout); |
| fflush(stderr); |
| exit(1); |
| } |
| |
| static void *xmalloc(size_t size, const char *name) |
| { |
| void *buf; |
| buf = malloc(size); |
| if (!buf) { |
| die("Cannot malloc %ld bytes to hold %s: %s\n", |
| size + 0UL, name, strerror(errno)); |
| } |
| return buf; |
| } |
| |
| static void *xcmalloc(size_t size, const char *name) |
| { |
| void *buf; |
| buf = xmalloc(size, name); |
| memset(buf, 0, size); |
| return buf; |
| } |
| |
| static void *xrealloc(void *ptr, size_t size, const char *name) |
| { |
| void *buf; |
| buf = realloc(ptr, size); |
| if (!buf) { |
| die("Cannot realloc %ld bytes to hold %s: %s\n", |
| size + 0UL, name, strerror(errno)); |
| } |
| return buf; |
| } |
| |
| static void xfree(const void *ptr) |
| { |
| free((void *)ptr); |
| } |
| |
| static char *xstrdup(const char *str) |
| { |
| char *new; |
| int len; |
| len = strlen(str); |
| new = xmalloc(len + 1, "xstrdup string"); |
| memcpy(new, str, len); |
| new[len] = '\0'; |
| return new; |
| } |
| |
| static void xchdir(const char *path) |
| { |
| if (chdir(path) != 0) { |
| die("chdir to `%s' failed: %s\n", |
| path, strerror(errno)); |
| } |
| } |
| |
| static int exists(const char *dirname, const char *filename) |
| { |
| char cwd[MAX_CWD_SIZE]; |
| int does_exist; |
| |
| if (getcwd(cwd, sizeof(cwd)) == 0) { |
| die("cwd buffer to small"); |
| } |
| |
| does_exist = 1; |
| if (chdir(dirname) != 0) { |
| does_exist = 0; |
| } |
| if (does_exist && (access(filename, O_RDONLY) < 0)) { |
| if ((errno != EACCES) && (errno != EROFS)) { |
| does_exist = 0; |
| } |
| } |
| xchdir(cwd); |
| return does_exist; |
| } |
| |
| |
| static char *slurp_file(const char *dirname, const char *filename, off_t *r_size) |
| { |
| char cwd[MAX_CWD_SIZE]; |
| char *buf; |
| off_t size, progress; |
| ssize_t result; |
| FILE* file; |
| |
| if (!filename) { |
| *r_size = 0; |
| return 0; |
| } |
| if (getcwd(cwd, sizeof(cwd)) == 0) { |
| die("cwd buffer to small"); |
| } |
| xchdir(dirname); |
| file = fopen(filename, "rb"); |
| xchdir(cwd); |
| if (file == NULL) { |
| die("Cannot open '%s' : %s\n", |
| filename, strerror(errno)); |
| } |
| fseek(file, 0, SEEK_END); |
| size = ftell(file); |
| fseek(file, 0, SEEK_SET); |
| *r_size = size +1; |
| buf = xmalloc(size +2, filename); |
| buf[size] = '\n'; /* Make certain the file is newline terminated */ |
| buf[size+1] = '\0'; /* Null terminate the file for good measure */ |
| progress = 0; |
| while(progress < size) { |
| result = fread(buf + progress, 1, size - progress, file); |
| if (result < 0) { |
| if ((errno == EINTR) || (errno == EAGAIN)) |
| continue; |
| die("read on %s of %ld bytes failed: %s\n", |
| filename, (size - progress)+ 0UL, strerror(errno)); |
| } |
| progress += result; |
| } |
| fclose(file); |
| return buf; |
| } |
| |
| /* Types on the destination platform */ |
| #if DEBUG_ROMCC_WARNINGS |
| #warning "FIXME this assumes 32bit x86 is the destination" |
| #endif |
| typedef int8_t schar_t; |
| typedef uint8_t uchar_t; |
| typedef int8_t char_t; |
| typedef int16_t short_t; |
| typedef uint16_t ushort_t; |
| typedef int32_t int_t; |
| typedef uint32_t uint_t; |
| typedef int32_t long_t; |
| #define ulong_t uint32_t |
| |
| #define SCHAR_T_MIN (-128) |
| #define SCHAR_T_MAX 127 |
| #define UCHAR_T_MAX 255 |
| #define CHAR_T_MIN SCHAR_T_MIN |
| #define CHAR_T_MAX SCHAR_T_MAX |
| #define SHRT_T_MIN (-32768) |
| #define SHRT_T_MAX 32767 |
| #define USHRT_T_MAX 65535 |
| #define INT_T_MIN (-LONG_T_MAX - 1) |
| #define INT_T_MAX 2147483647 |
| #define UINT_T_MAX 4294967295U |
| #define LONG_T_MIN (-LONG_T_MAX - 1) |
| #define LONG_T_MAX 2147483647 |
| #define ULONG_T_MAX 4294967295U |
| |
| #define SIZEOF_I8 8 |
| #define SIZEOF_I16 16 |
| #define SIZEOF_I32 32 |
| #define SIZEOF_I64 64 |
| |
| #define SIZEOF_CHAR 8 |
| #define SIZEOF_SHORT 16 |
| #define SIZEOF_INT 32 |
| #define SIZEOF_LONG (sizeof(long_t)*SIZEOF_CHAR) |
| |
| |
| #define ALIGNOF_CHAR 8 |
| #define ALIGNOF_SHORT 16 |
| #define ALIGNOF_INT 32 |
| #define ALIGNOF_LONG (sizeof(long_t)*SIZEOF_CHAR) |
| |
| #define REG_SIZEOF_REG 32 |
| #define REG_SIZEOF_CHAR REG_SIZEOF_REG |
| #define REG_SIZEOF_SHORT REG_SIZEOF_REG |
| #define REG_SIZEOF_INT REG_SIZEOF_REG |
| #define REG_SIZEOF_LONG REG_SIZEOF_REG |
| |
| #define REG_ALIGNOF_REG REG_SIZEOF_REG |
| #define REG_ALIGNOF_CHAR REG_SIZEOF_REG |
| #define REG_ALIGNOF_SHORT REG_SIZEOF_REG |
| #define REG_ALIGNOF_INT REG_SIZEOF_REG |
| #define REG_ALIGNOF_LONG REG_SIZEOF_REG |
| |
| /* Additional definitions for clarity. |
| * I currently assume a long is the largest native |
| * machine word and that a pointer fits into it. |
| */ |
| #define SIZEOF_WORD SIZEOF_LONG |
| #define SIZEOF_POINTER SIZEOF_LONG |
| #define ALIGNOF_WORD ALIGNOF_LONG |
| #define ALIGNOF_POINTER ALIGNOF_LONG |
| #define REG_SIZEOF_POINTER REG_SIZEOF_LONG |
| #define REG_ALIGNOF_POINTER REG_ALIGNOF_LONG |
| |
| struct file_state { |
| struct file_state *prev; |
| const char *basename; |
| char *dirname; |
| const char *buf; |
| off_t size; |
| const char *pos; |
| int line; |
| const char *line_start; |
| int report_line; |
| const char *report_name; |
| const char *report_dir; |
| int macro : 1; |
| int trigraphs : 1; |
| int join_lines : 1; |
| }; |
| struct hash_entry; |
| struct token { |
| int tok; |
| struct hash_entry *ident; |
| const char *pos; |
| int str_len; |
| union { |
| ulong_t integer; |
| const char *str; |
| int notmacro; |
| } val; |
| }; |
| |
| /* I have two classes of types: |
| * Operational types. |
| * Logical types. (The type the C standard says the operation is of) |
| * |
| * The operational types are: |
| * chars |
| * shorts |
| * ints |
| * longs |
| * |
| * floats |
| * doubles |
| * long doubles |
| * |
| * pointer |
| */ |
| |
| |
| /* Machine model. |
| * No memory is useable by the compiler. |
| * There is no floating point support. |
| * All operations take place in general purpose registers. |
| * There is one type of general purpose register. |
| * Unsigned longs are stored in that general purpose register. |
| */ |
| |
| /* Operations on general purpose registers. |
| */ |
| |
| #define OP_SDIVT 0 |
| #define OP_UDIVT 1 |
| #define OP_SMUL 2 |
| #define OP_UMUL 3 |
| #define OP_SDIV 4 |
| #define OP_UDIV 5 |
| #define OP_SMOD 6 |
| #define OP_UMOD 7 |
| #define OP_ADD 8 |
| #define OP_SUB 9 |
| #define OP_SL 10 |
| #define OP_USR 11 |
| #define OP_SSR 12 |
| #define OP_AND 13 |
| #define OP_XOR 14 |
| #define OP_OR 15 |
| #define OP_POS 16 /* Dummy positive operator don't use it */ |
| #define OP_NEG 17 |
| #define OP_INVERT 18 |
| |
| #define OP_EQ 20 |
| #define OP_NOTEQ 21 |
| #define OP_SLESS 22 |
| #define OP_ULESS 23 |
| #define OP_SMORE 24 |
| #define OP_UMORE 25 |
| #define OP_SLESSEQ 26 |
| #define OP_ULESSEQ 27 |
| #define OP_SMOREEQ 28 |
| #define OP_UMOREEQ 29 |
| |
| #define OP_LFALSE 30 /* Test if the expression is logically false */ |
| #define OP_LTRUE 31 /* Test if the expression is logcially true */ |
| |
| #define OP_LOAD 32 |
| #define OP_STORE 33 |
| /* For OP_STORE ->type holds the type |
| * RHS(0) holds the destination address |
| * RHS(1) holds the value to store. |
| */ |
| |
| #define OP_UEXTRACT 34 |
| /* OP_UEXTRACT extracts an unsigned bitfield from a pseudo register |
| * RHS(0) holds the psuedo register to extract from |
| * ->type holds the size of the bitfield. |
| * ->u.bitfield.size holds the size of the bitfield. |
| * ->u.bitfield.offset holds the offset to extract from |
| */ |
| #define OP_SEXTRACT 35 |
| /* OP_SEXTRACT extracts a signed bitfield from a pseudo register |
| * RHS(0) holds the psuedo register to extract from |
| * ->type holds the size of the bitfield. |
| * ->u.bitfield.size holds the size of the bitfield. |
| * ->u.bitfield.offset holds the offset to extract from |
| */ |
| #define OP_DEPOSIT 36 |
| /* OP_DEPOSIT replaces a bitfield with a new value. |
| * RHS(0) holds the value to replace a bitifield in. |
| * RHS(1) holds the replacement value |
| * ->u.bitfield.size holds the size of the bitfield. |
| * ->u.bitfield.offset holds the deposit into |
| */ |
| |
| #define OP_NOOP 37 |
| |
| #define OP_MIN_CONST 50 |
| #define OP_MAX_CONST 58 |
| #define IS_CONST_OP(X) (((X) >= OP_MIN_CONST) && ((X) <= OP_MAX_CONST)) |
| #define OP_INTCONST 50 |
| /* For OP_INTCONST ->type holds the type. |
| * ->u.cval holds the constant value. |
| */ |
| #define OP_BLOBCONST 51 |
| /* For OP_BLOBCONST ->type holds the layout and size |
| * information. u.blob holds a pointer to the raw binary |
| * data for the constant initializer. |
| */ |
| #define OP_ADDRCONST 52 |
| /* For OP_ADDRCONST ->type holds the type. |
| * MISC(0) holds the reference to the static variable. |
| * ->u.cval holds an offset from that value. |
| */ |
| #define OP_UNKNOWNVAL 59 |
| /* For OP_UNKNOWNAL ->type holds the type. |
| * For some reason we don't know what value this type has. |
| * This allows for variables that have don't have values |
| * assigned yet, or variables whose value we simply do not know. |
| */ |
| |
| #define OP_WRITE 60 |
| /* OP_WRITE moves one pseudo register to another. |
| * MISC(0) holds the destination pseudo register, which must be an OP_DECL. |
| * RHS(0) holds the psuedo to move. |
| */ |
| |
| #define OP_READ 61 |
| /* OP_READ reads the value of a variable and makes |
| * it available for the pseudo operation. |
| * Useful for things like def-use chains. |
| * RHS(0) holds points to the triple to read from. |
| */ |
| #define OP_COPY 62 |
| /* OP_COPY makes a copy of the pseudo register or constant in RHS(0). |
| */ |
| #define OP_CONVERT 63 |
| /* OP_CONVERT makes a copy of the pseudo register or constant in RHS(0). |
| * And then the type is converted appropriately. |
| */ |
| #define OP_PIECE 64 |
| /* OP_PIECE returns one piece of a instruction that returns a structure. |
| * MISC(0) is the instruction |
| * u.cval is the LHS piece of the instruction to return. |
| */ |
| #define OP_ASM 65 |
| /* OP_ASM holds a sequence of assembly instructions, the result |
| * of a C asm directive. |
| * RHS(x) holds input value x to the assembly sequence. |
| * LHS(x) holds the output value x from the assembly sequence. |
| * u.blob holds the string of assembly instructions. |
| */ |
| |
| #define OP_DEREF 66 |
| /* OP_DEREF generates an lvalue from a pointer. |
| * RHS(0) holds the pointer value. |
| * OP_DEREF serves as a place holder to indicate all necessary |
| * checks have been done to indicate a value is an lvalue. |
| */ |
| #define OP_DOT 67 |
| /* OP_DOT references a submember of a structure lvalue. |
| * MISC(0) holds the lvalue. |
| * ->u.field holds the name of the field we want. |
| * |
| * Not seen after structures are flattened. |
| */ |
| #define OP_INDEX 68 |
| /* OP_INDEX references a submember of a tuple or array lvalue. |
| * MISC(0) holds the lvalue. |
| * ->u.cval holds the index into the lvalue. |
| * |
| * Not seen after structures are flattened. |
| */ |
| #define OP_VAL 69 |
| /* OP_VAL returns the value of a subexpression of the current expression. |
| * Useful for operators that have side effects. |
| * RHS(0) holds the expression. |
| * MISC(0) holds the subexpression of RHS(0) that is the |
| * value of the expression. |
| * |
| * Not seen outside of expressions. |
| */ |
| |
| #define OP_TUPLE 70 |
| /* OP_TUPLE is an array of triples that are either variable |
| * or values for a structure or an array. It is used as |
| * a place holder when flattening compound types. |
| * The value represented by an OP_TUPLE is held in N registers. |
| * LHS(0..N-1) refer to those registers. |
| * ->use is a list of statements that use the value. |
| * |
| * Although OP_TUPLE always has register sized pieces they are not |
| * used until structures are flattened/decomposed into their register |
| * components. |
| * ???? registers ???? |
| */ |
| |
| #define OP_BITREF 71 |
| /* OP_BITREF describes a bitfield as an lvalue. |
| * RHS(0) holds the register value. |
| * ->type holds the type of the bitfield. |
| * ->u.bitfield.size holds the size of the bitfield. |
| * ->u.bitfield.offset holds the offset of the bitfield in the register |
| */ |
| |
| |
| #define OP_FCALL 72 |
| /* OP_FCALL performs a procedure call. |
| * MISC(0) holds a pointer to the OP_LIST of a function |
| * RHS(x) holds argument x of a function |
| * |
| * Currently not seen outside of expressions. |
| */ |
| #define OP_PROG 73 |
| /* OP_PROG is an expression that holds a list of statements, or |
| * expressions. The final expression is the value of the expression. |
| * RHS(0) holds the start of the list. |
| */ |
| |
| /* statements */ |
| #define OP_LIST 80 |
| /* OP_LIST Holds a list of statements that compose a function, and a result value. |
| * RHS(0) holds the list of statements. |
| * A list of all functions is maintained. |
| */ |
| |
| #define OP_BRANCH 81 /* an unconditional branch */ |
| /* For branch instructions |
| * TARG(0) holds the branch target. |
| * ->next holds where to branch to if the branch is not taken. |
| * The branch target can only be a label |
| */ |
| |
| #define OP_CBRANCH 82 /* a conditional branch */ |
| /* For conditional branch instructions |
| * RHS(0) holds the branch condition. |
| * TARG(0) holds the branch target. |
| * ->next holds where to branch to if the branch is not taken. |
| * The branch target can only be a label |
| */ |
| |
| #define OP_CALL 83 /* an uncontional branch that will return */ |
| /* For call instructions |
| * MISC(0) holds the OP_RET that returns from the branch |
| * TARG(0) holds the branch target. |
| * ->next holds where to branch to if the branch is not taken. |
| * The branch target can only be a label |
| */ |
| |
| #define OP_RET 84 /* an uncontinonal branch through a variable back to an OP_CALL */ |
| /* For call instructions |
| * RHS(0) holds the variable with the return address |
| * The branch target can only be a label |
| */ |
| |
| #define OP_LABEL 86 |
| /* OP_LABEL is a triple that establishes an target for branches. |
| * ->use is the list of all branches that use this label. |
| */ |
| |
| #define OP_ADECL 87 |
| /* OP_ADECL is a triple that establishes an lvalue for assignments. |
| * A variable takes N registers to contain. |
| * LHS(0..N-1) refer to an OP_PIECE triple that represents |
| * the Xth register that the variable is stored in. |
| * ->use is a list of statements that use the variable. |
| * |
| * Although OP_ADECL always has register sized pieces they are not |
| * used until structures are flattened/decomposed into their register |
| * components. |
| */ |
| |
| #define OP_SDECL 88 |
| /* OP_SDECL is a triple that establishes a variable of static |
| * storage duration. |
| * ->use is a list of statements that use the variable. |
| * MISC(0) holds the initializer expression. |
| */ |
| |
| |
| #define OP_PHI 89 |
| /* OP_PHI is a triple used in SSA form code. |
| * It is used when multiple code paths merge and a variable needs |
| * a single assignment from any of those code paths. |
| * The operation is a cross between OP_DECL and OP_WRITE, which |
| * is what OP_PHI is generated from. |
| * |
| * RHS(x) points to the value from code path x |
| * The number of RHS entries is the number of control paths into the block |
| * in which OP_PHI resides. The elements of the array point to point |
| * to the variables OP_PHI is derived from. |
| * |
| * MISC(0) holds a pointer to the orginal OP_DECL node. |
| */ |
| |
| #if 0 |
| /* continuation helpers |
| */ |
| #define OP_CPS_BRANCH 90 /* an unconditional branch */ |
| /* OP_CPS_BRANCH calls a continuation |
| * RHS(x) holds argument x of the function |
| * TARG(0) holds OP_CPS_START target |
| */ |
| #define OP_CPS_CBRANCH 91 /* a conditional branch */ |
| /* OP_CPS_CBRANCH conditionally calls one of two continuations |
| * RHS(0) holds the branch condition |
| * RHS(x + 1) holds argument x of the function |
| * TARG(0) holds the OP_CPS_START to jump to when true |
| * ->next holds the OP_CPS_START to jump to when false |
| */ |
| #define OP_CPS_CALL 92 /* an uncontional branch that will return */ |
| /* For OP_CPS_CALL instructions |
| * RHS(x) holds argument x of the function |
| * MISC(0) holds the OP_CPS_RET that returns from the branch |
| * TARG(0) holds the branch target. |
| * ->next holds where the OP_CPS_RET will return to. |
| */ |
| #define OP_CPS_RET 93 |
| /* OP_CPS_RET conditionally calls one of two continuations |
| * RHS(0) holds the variable with the return function address |
| * RHS(x + 1) holds argument x of the function |
| * The branch target may be any OP_CPS_START |
| */ |
| #define OP_CPS_END 94 |
| /* OP_CPS_END is the triple at the end of the program. |
| * For most practical purposes it is a branch. |
| */ |
| #define OP_CPS_START 95 |
| /* OP_CPS_START is a triple at the start of a continuation |
| * The arguments variables takes N registers to contain. |
| * LHS(0..N-1) refer to an OP_PIECE triple that represents |
| * the Xth register that the arguments are stored in. |
| */ |
| #endif |
| |
| /* Architecture specific instructions */ |
| #define OP_CMP 100 |
| #define OP_TEST 101 |
| #define OP_SET_EQ 102 |
| #define OP_SET_NOTEQ 103 |
| #define OP_SET_SLESS 104 |
| #define OP_SET_ULESS 105 |
| #define OP_SET_SMORE 106 |
| #define OP_SET_UMORE 107 |
| #define OP_SET_SLESSEQ 108 |
| #define OP_SET_ULESSEQ 109 |
| #define OP_SET_SMOREEQ 110 |
| #define OP_SET_UMOREEQ 111 |
| |
| #define OP_JMP 112 |
| #define OP_JMP_EQ 113 |
| #define OP_JMP_NOTEQ 114 |
| #define OP_JMP_SLESS 115 |
| #define OP_JMP_ULESS 116 |
| #define OP_JMP_SMORE 117 |
| #define OP_JMP_UMORE 118 |
| #define OP_JMP_SLESSEQ 119 |
| #define OP_JMP_ULESSEQ 120 |
| #define OP_JMP_SMOREEQ 121 |
| #define OP_JMP_UMOREEQ 122 |
| |
| /* Builtin operators that it is just simpler to use the compiler for */ |
| #define OP_INB 130 |
| #define OP_INW 131 |
| #define OP_INL 132 |
| #define OP_OUTB 133 |
| #define OP_OUTW 134 |
| #define OP_OUTL 135 |
| #define OP_BSF 136 |
| #define OP_BSR 137 |
| #define OP_RDMSR 138 |
| #define OP_WRMSR 139 |
| #define OP_HLT 140 |
| |
| struct op_info { |
| const char *name; |
| unsigned flags; |
| #define PURE 0x001 /* Triple has no side effects */ |
| #define IMPURE 0x002 /* Triple has side effects */ |
| #define PURE_BITS(FLAGS) ((FLAGS) & 0x3) |
| #define DEF 0x004 /* Triple is a variable definition */ |
| #define BLOCK 0x008 /* Triple stores the current block */ |
| #define STRUCTURAL 0x010 /* Triple does not generate a machine instruction */ |
| #define BRANCH_BITS(FLAGS) ((FLAGS) & 0xe0 ) |
| #define UBRANCH 0x020 /* Triple is an unconditional branch instruction */ |
| #define CBRANCH 0x040 /* Triple is a conditional branch instruction */ |
| #define RETBRANCH 0x060 /* Triple is a return instruction */ |
| #define CALLBRANCH 0x080 /* Triple is a call instruction */ |
| #define ENDBRANCH 0x0a0 /* Triple is an end instruction */ |
| #define PART 0x100 /* Triple is really part of another triple */ |
| #define BITFIELD 0x200 /* Triple manipulates a bitfield */ |
| signed char lhs, rhs, misc, targ; |
| }; |
| |
| #define OP(LHS, RHS, MISC, TARG, FLAGS, NAME) { \ |
| .name = (NAME), \ |
| .flags = (FLAGS), \ |
| .lhs = (LHS), \ |
| .rhs = (RHS), \ |
| .misc = (MISC), \ |
| .targ = (TARG), \ |
| } |
| static const struct op_info table_ops[] = { |
| [OP_SDIVT ] = OP( 2, 2, 0, 0, PURE | BLOCK , "sdivt"), |
| [OP_UDIVT ] = OP( 2, 2, 0, 0, PURE | BLOCK , "udivt"), |
| [OP_SMUL ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "smul"), |
| [OP_UMUL ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "umul"), |
| [OP_SDIV ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "sdiv"), |
| [OP_UDIV ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "udiv"), |
| [OP_SMOD ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "smod"), |
| [OP_UMOD ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "umod"), |
| [OP_ADD ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "add"), |
| [OP_SUB ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "sub"), |
| [OP_SL ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "sl"), |
| [OP_USR ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "usr"), |
| [OP_SSR ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "ssr"), |
| [OP_AND ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "and"), |
| [OP_XOR ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "xor"), |
| [OP_OR ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "or"), |
| [OP_POS ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK , "pos"), |
| [OP_NEG ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK , "neg"), |
| [OP_INVERT ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK , "invert"), |
| |
| [OP_EQ ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "eq"), |
| [OP_NOTEQ ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "noteq"), |
| [OP_SLESS ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "sless"), |
| [OP_ULESS ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "uless"), |
| [OP_SMORE ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "smore"), |
| [OP_UMORE ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "umore"), |
| [OP_SLESSEQ ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "slesseq"), |
| [OP_ULESSEQ ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "ulesseq"), |
| [OP_SMOREEQ ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "smoreeq"), |
| [OP_UMOREEQ ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK , "umoreeq"), |
| [OP_LFALSE ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK , "lfalse"), |
| [OP_LTRUE ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK , "ltrue"), |
| |
| [OP_LOAD ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "load"), |
| [OP_STORE ] = OP( 0, 2, 0, 0, PURE | BLOCK , "store"), |
| |
| [OP_UEXTRACT ] = OP( 0, 1, 0, 0, PURE | DEF | BITFIELD, "uextract"), |
| [OP_SEXTRACT ] = OP( 0, 1, 0, 0, PURE | DEF | BITFIELD, "sextract"), |
| [OP_DEPOSIT ] = OP( 0, 2, 0, 0, PURE | DEF | BITFIELD, "deposit"), |
| |
| [OP_NOOP ] = OP( 0, 0, 0, 0, PURE | BLOCK | STRUCTURAL, "noop"), |
| |
| [OP_INTCONST ] = OP( 0, 0, 0, 0, PURE | DEF, "intconst"), |
| [OP_BLOBCONST ] = OP( 0, 0, 0, 0, PURE , "blobconst"), |
| [OP_ADDRCONST ] = OP( 0, 0, 1, 0, PURE | DEF, "addrconst"), |
| [OP_UNKNOWNVAL ] = OP( 0, 0, 0, 0, PURE | DEF, "unknown"), |
| |
| #if DEBUG_ROMCC_WARNINGS |
| #warning "FIXME is it correct for OP_WRITE to be a def? I currently use it as one..." |
| #endif |
| [OP_WRITE ] = OP( 0, 1, 1, 0, PURE | DEF | BLOCK, "write"), |
| [OP_READ ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "read"), |
| [OP_COPY ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "copy"), |
| [OP_CONVERT ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "convert"), |
| [OP_PIECE ] = OP( 0, 0, 1, 0, PURE | DEF | STRUCTURAL | PART, "piece"), |
| [OP_ASM ] = OP(-1, -1, 0, 0, PURE, "asm"), |
| [OP_DEREF ] = OP( 0, 1, 0, 0, 0 | DEF | BLOCK, "deref"), |
| [OP_DOT ] = OP( 0, 0, 1, 0, PURE | DEF | PART, "dot"), |
| [OP_INDEX ] = OP( 0, 0, 1, 0, PURE | DEF | PART, "index"), |
| |
| [OP_VAL ] = OP( 0, 1, 1, 0, 0 | DEF | BLOCK, "val"), |
| [OP_TUPLE ] = OP(-1, 0, 0, 0, 0 | PURE | BLOCK | STRUCTURAL, "tuple"), |
| [OP_BITREF ] = OP( 0, 1, 0, 0, 0 | DEF | PURE | STRUCTURAL | BITFIELD, "bitref"), |
| /* Call is special most it can stand in for anything so it depends on context */ |
| [OP_FCALL ] = OP( 0, -1, 1, 0, 0 | BLOCK | CALLBRANCH, "fcall"), |
| [OP_PROG ] = OP( 0, 1, 0, 0, 0 | IMPURE | BLOCK | STRUCTURAL, "prog"), |
| /* The sizes of OP_FCALL depends upon context */ |
| |
| [OP_LIST ] = OP( 0, 1, 1, 0, 0 | DEF | STRUCTURAL, "list"), |
| [OP_BRANCH ] = OP( 0, 0, 0, 1, PURE | BLOCK | UBRANCH, "branch"), |
| [OP_CBRANCH ] = OP( 0, 1, 0, 1, PURE | BLOCK | CBRANCH, "cbranch"), |
| [OP_CALL ] = OP( 0, 0, 1, 1, PURE | BLOCK | CALLBRANCH, "call"), |
| [OP_RET ] = OP( 0, 1, 0, 0, PURE | BLOCK | RETBRANCH, "ret"), |
| [OP_LABEL ] = OP( 0, 0, 0, 0, PURE | BLOCK | STRUCTURAL, "label"), |
| [OP_ADECL ] = OP( 0, 0, 0, 0, PURE | BLOCK | STRUCTURAL, "adecl"), |
| [OP_SDECL ] = OP( 0, 0, 1, 0, PURE | BLOCK | STRUCTURAL, "sdecl"), |
| /* The number of RHS elements of OP_PHI depend upon context */ |
| [OP_PHI ] = OP( 0, -1, 1, 0, PURE | DEF | BLOCK, "phi"), |
| |
| #if 0 |
| [OP_CPS_BRANCH ] = OP( 0, -1, 0, 1, PURE | BLOCK | UBRANCH, "cps_branch"), |
| [OP_CPS_CBRANCH] = OP( 0, -1, 0, 1, PURE | BLOCK | CBRANCH, "cps_cbranch"), |
| [OP_CPS_CALL ] = OP( 0, -1, 1, 1, PURE | BLOCK | CALLBRANCH, "cps_call"), |
| [OP_CPS_RET ] = OP( 0, -1, 0, 0, PURE | BLOCK | RETBRANCH, "cps_ret"), |
| [OP_CPS_END ] = OP( 0, -1, 0, 0, IMPURE | BLOCK | ENDBRANCH, "cps_end"), |
| [OP_CPS_START ] = OP( -1, 0, 0, 0, PURE | BLOCK | STRUCTURAL, "cps_start"), |
| #endif |
| |
| [OP_CMP ] = OP( 0, 2, 0, 0, PURE | DEF | BLOCK, "cmp"), |
| [OP_TEST ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "test"), |
| [OP_SET_EQ ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "set_eq"), |
| [OP_SET_NOTEQ ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "set_noteq"), |
| [OP_SET_SLESS ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "set_sless"), |
| [OP_SET_ULESS ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "set_uless"), |
| [OP_SET_SMORE ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "set_smore"), |
| [OP_SET_UMORE ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "set_umore"), |
| [OP_SET_SLESSEQ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "set_slesseq"), |
| [OP_SET_ULESSEQ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "set_ulesseq"), |
| [OP_SET_SMOREEQ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "set_smoreq"), |
| [OP_SET_UMOREEQ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "set_umoreq"), |
| [OP_JMP ] = OP( 0, 0, 0, 1, PURE | BLOCK | UBRANCH, "jmp"), |
| [OP_JMP_EQ ] = OP( 0, 1, 0, 1, PURE | BLOCK | CBRANCH, "jmp_eq"), |
| [OP_JMP_NOTEQ ] = OP( 0, 1, 0, 1, PURE | BLOCK | CBRANCH, "jmp_noteq"), |
| [OP_JMP_SLESS ] = OP( 0, 1, 0, 1, PURE | BLOCK | CBRANCH, "jmp_sless"), |
| [OP_JMP_ULESS ] = OP( 0, 1, 0, 1, PURE | BLOCK | CBRANCH, "jmp_uless"), |
| [OP_JMP_SMORE ] = OP( 0, 1, 0, 1, PURE | BLOCK | CBRANCH, "jmp_smore"), |
| [OP_JMP_UMORE ] = OP( 0, 1, 0, 1, PURE | BLOCK | CBRANCH, "jmp_umore"), |
| [OP_JMP_SLESSEQ] = OP( 0, 1, 0, 1, PURE | BLOCK | CBRANCH, "jmp_slesseq"), |
| [OP_JMP_ULESSEQ] = OP( 0, 1, 0, 1, PURE | BLOCK | CBRANCH, "jmp_ulesseq"), |
| [OP_JMP_SMOREEQ] = OP( 0, 1, 0, 1, PURE | BLOCK | CBRANCH, "jmp_smoreq"), |
| [OP_JMP_UMOREEQ] = OP( 0, 1, 0, 1, PURE | BLOCK | CBRANCH, "jmp_umoreq"), |
| |
| [OP_INB ] = OP( 0, 1, 0, 0, IMPURE | DEF | BLOCK, "__inb"), |
| [OP_INW ] = OP( 0, 1, 0, 0, IMPURE | DEF | BLOCK, "__inw"), |
| [OP_INL ] = OP( 0, 1, 0, 0, IMPURE | DEF | BLOCK, "__inl"), |
| [OP_OUTB ] = OP( 0, 2, 0, 0, IMPURE| BLOCK, "__outb"), |
| [OP_OUTW ] = OP( 0, 2, 0, 0, IMPURE| BLOCK, "__outw"), |
| [OP_OUTL ] = OP( 0, 2, 0, 0, IMPURE| BLOCK, "__outl"), |
| [OP_BSF ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "__bsf"), |
| [OP_BSR ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK, "__bsr"), |
| [OP_RDMSR ] = OP( 2, 1, 0, 0, IMPURE | BLOCK, "__rdmsr"), |
| [OP_WRMSR ] = OP( 0, 3, 0, 0, IMPURE | BLOCK, "__wrmsr"), |
| [OP_HLT ] = OP( 0, 0, 0, 0, IMPURE | BLOCK, "__hlt"), |
| }; |
| #undef OP |
| #define OP_MAX (sizeof(table_ops)/sizeof(table_ops[0])) |
| |
| static const char *tops(int index) |
| { |
| static const char unknown[] = "unknown op"; |
| if (index < 0) { |
| return unknown; |
| } |
| if (index > OP_MAX) { |
| return unknown; |
| } |
| return table_ops[index].name; |
| } |
| |
| struct asm_info; |
| struct triple; |
| struct block; |
| struct triple_set { |
| struct triple_set *next; |
| struct triple *member; |
| }; |
| |
| #define MAX_LHS 63 |
| #define MAX_RHS 127 |
| #define MAX_MISC 3 |
| #define MAX_TARG 1 |
| |
| struct occurance { |
| int count; |
| const char *filename; |
| const char *function; |
| int line; |
| int col; |
| struct occurance *parent; |
| }; |
| struct bitfield { |
| ulong_t size : 8; |
| ulong_t offset : 24; |
| }; |
| struct triple { |
| struct triple *next, *prev; |
| struct triple_set *use; |
| struct type *type; |
| unsigned int op : 8; |
| unsigned int template_id : 7; |
| unsigned int lhs : 6; |
| unsigned int rhs : 7; |
| unsigned int misc : 2; |
| unsigned int targ : 1; |
| #define TRIPLE_SIZE(TRIPLE) \ |
| ((TRIPLE)->lhs + (TRIPLE)->rhs + (TRIPLE)->misc + (TRIPLE)->targ) |
| #define TRIPLE_LHS_OFF(PTR) (0) |
| #define TRIPLE_RHS_OFF(PTR) (TRIPLE_LHS_OFF(PTR) + (PTR)->lhs) |
| #define TRIPLE_MISC_OFF(PTR) (TRIPLE_RHS_OFF(PTR) + (PTR)->rhs) |
| #define TRIPLE_TARG_OFF(PTR) (TRIPLE_MISC_OFF(PTR) + (PTR)->misc) |
| #define LHS(PTR,INDEX) ((PTR)->param[TRIPLE_LHS_OFF(PTR) + (INDEX)]) |
| #define RHS(PTR,INDEX) ((PTR)->param[TRIPLE_RHS_OFF(PTR) + (INDEX)]) |
| #define TARG(PTR,INDEX) ((PTR)->param[TRIPLE_TARG_OFF(PTR) + (INDEX)]) |
| #define MISC(PTR,INDEX) ((PTR)->param[TRIPLE_MISC_OFF(PTR) + (INDEX)]) |
| unsigned id; /* A scratch value and finally the register */ |
| #define TRIPLE_FLAG_FLATTENED (1 << 31) |
| #define TRIPLE_FLAG_PRE_SPLIT (1 << 30) |
| #define TRIPLE_FLAG_POST_SPLIT (1 << 29) |
| #define TRIPLE_FLAG_VOLATILE (1 << 28) |
| #define TRIPLE_FLAG_INLINE (1 << 27) /* ???? */ |
| #define TRIPLE_FLAG_LOCAL (1 << 26) |
| |
| #define TRIPLE_FLAG_COPY TRIPLE_FLAG_VOLATILE |
| struct occurance *occurance; |
| union { |
| ulong_t cval; |
| struct bitfield bitfield; |
| struct block *block; |
| void *blob; |
| struct hash_entry *field; |
| struct asm_info *ainfo; |
| struct triple *func; |
| struct symbol *symbol; |
| } u; |
| struct triple *param[2]; |
| }; |
| |
| struct reg_info { |
| unsigned reg; |
| unsigned regcm; |
| }; |
| struct ins_template { |
| struct reg_info lhs[MAX_LHS + 1], rhs[MAX_RHS + 1]; |
| }; |
| |
| struct asm_info { |
| struct ins_template tmpl; |
| char *str; |
| }; |
| |
| struct block_set { |
| struct block_set *next; |
| struct block *member; |
| }; |
| struct block { |
| struct block *work_next; |
| struct triple *first, *last; |
| int edge_count; |
| struct block_set *edges; |
| int users; |
| struct block_set *use; |
| struct block_set *idominates; |
| struct block_set *domfrontier; |
| struct block *idom; |
| struct block_set *ipdominates; |
| struct block_set *ipdomfrontier; |
| struct block *ipdom; |
| int vertex; |
| |
| }; |
| |
| struct symbol { |
| struct symbol *next; |
| struct hash_entry *ident; |
| struct triple *def; |
| struct type *type; |
| int scope_depth; |
| }; |
| |
| struct macro_arg { |
| struct macro_arg *next; |
| struct hash_entry *ident; |
| }; |
| struct macro { |
| struct hash_entry *ident; |
| const char *buf; |
| int buf_len; |
| struct macro_arg *args; |
| int argc; |
| }; |
| |
| struct hash_entry { |
| struct hash_entry *next; |
| const char *name; |
| int name_len; |
| int tok; |
| struct macro *sym_define; |
| struct symbol *sym_label; |
| struct symbol *sym_tag; |
| struct symbol *sym_ident; |
| }; |
| |
| #define HASH_TABLE_SIZE 2048 |
| |
| struct compiler_state { |
| const char *label_prefix; |
| const char *ofilename; |
| unsigned long flags; |
| unsigned long debug; |
| unsigned long max_allocation_passes; |
| |
| size_t include_path_count; |
| const char **include_paths; |
| |
| size_t define_count; |
| const char **defines; |
| |
| size_t undef_count; |
| const char **undefs; |
| }; |
| struct arch_state { |
| unsigned long features; |
| }; |
| struct basic_blocks { |
| struct triple *func; |
| struct triple *first; |
| struct block *first_block, *last_block; |
| int last_vertex; |
| }; |
| #define MAX_PP_IF_DEPTH 63 |
| struct compile_state { |
| struct compiler_state *compiler; |
| struct arch_state *arch; |
| FILE *output; |
| FILE *errout; |
| FILE *dbgout; |
| struct file_state *file; |
| struct occurance *last_occurance; |
| const char *function; |
| int token_base; |
| struct token token[6]; |
| struct hash_entry *hash_table[HASH_TABLE_SIZE]; |
| struct hash_entry *i_switch; |
| struct hash_entry *i_case; |
| struct hash_entry *i_continue; |
| struct hash_entry *i_break; |
| struct hash_entry *i_default; |
| struct hash_entry *i_return; |
| struct hash_entry *i_noreturn; |
| struct hash_entry *i_unused; |
| /* Additional hash entries for predefined macros */ |
| struct hash_entry *i_defined; |
| struct hash_entry *i___VA_ARGS__; |
| struct hash_entry *i___FILE__; |
| struct hash_entry *i___LINE__; |
| /* Additional hash entries for predefined identifiers */ |
| struct hash_entry *i___func__; |
| /* Additional hash entries for attributes */ |
| struct hash_entry *i_noinline; |
| struct hash_entry *i_always_inline; |
| int scope_depth; |
| unsigned char if_bytes[(MAX_PP_IF_DEPTH + CHAR_BIT -1)/CHAR_BIT]; |
| int if_depth; |
| int eat_depth, eat_targ; |
| struct file_state *macro_file; |
| struct triple *functions; |
| struct triple *main_function; |
| struct triple *first; |
| struct triple *global_pool; |
| struct basic_blocks bb; |
| int functions_joined; |
| }; |
| |
| /* visibility global/local */ |
| /* static/auto duration */ |
| /* typedef, register, inline */ |
| #define STOR_SHIFT 0 |
| #define STOR_MASK 0x001f |
| /* Visibility */ |
| #define STOR_GLOBAL 0x0001 |
| /* Duration */ |
| #define STOR_PERM 0x0002 |
| /* Definition locality */ |
| #define STOR_NONLOCAL 0x0004 /* The definition is not in this translation unit */ |
| /* Storage specifiers */ |
| #define STOR_AUTO 0x0000 |
| #define STOR_STATIC 0x0002 |
| #define STOR_LOCAL 0x0003 |
| #define STOR_EXTERN 0x0007 |
| #define STOR_INLINE 0x0008 |
| #define STOR_REGISTER 0x0010 |
| #define STOR_TYPEDEF 0x0018 |
| |
| #define QUAL_SHIFT 5 |
| #define QUAL_MASK 0x00e0 |
| #define QUAL_NONE 0x0000 |
| #define QUAL_CONST 0x0020 |
| #define QUAL_VOLATILE 0x0040 |
| #define QUAL_RESTRICT 0x0080 |
| |
| #define TYPE_SHIFT 8 |
| #define TYPE_MASK 0x1f00 |
| #define TYPE_INTEGER(TYPE) ((((TYPE) >= TYPE_CHAR) && ((TYPE) <= TYPE_ULLONG)) || ((TYPE) == TYPE_ENUM) || ((TYPE) == TYPE_BITFIELD)) |
| #define TYPE_ARITHMETIC(TYPE) ((((TYPE) >= TYPE_CHAR) && ((TYPE) <= TYPE_LDOUBLE)) || ((TYPE) == TYPE_ENUM) || ((TYPE) == TYPE_BITFIELD)) |
| #define TYPE_UNSIGNED(TYPE) ((TYPE) & 0x0100) |
| #define TYPE_SIGNED(TYPE) (!TYPE_UNSIGNED(TYPE)) |
| #define TYPE_MKUNSIGNED(TYPE) (((TYPE) & ~0xF000) | 0x0100) |
| #define TYPE_RANK(TYPE) ((TYPE) & ~0xF1FF) |
| #define TYPE_PTR(TYPE) (((TYPE) & TYPE_MASK) == TYPE_POINTER) |
| #define TYPE_DEFAULT 0x0000 |
| #define TYPE_VOID 0x0100 |
| #define TYPE_CHAR 0x0200 |
| #define TYPE_UCHAR 0x0300 |
| #define TYPE_SHORT 0x0400 |
| #define TYPE_USHORT 0x0500 |
| #define TYPE_INT 0x0600 |
| #define TYPE_UINT 0x0700 |
| #define TYPE_LONG 0x0800 |
| #define TYPE_ULONG 0x0900 |
| #define TYPE_LLONG 0x0a00 /* long long */ |
| #define TYPE_ULLONG 0x0b00 |
| #define TYPE_FLOAT 0x0c00 |
| #define TYPE_DOUBLE 0x0d00 |
| #define TYPE_LDOUBLE 0x0e00 /* long double */ |
| |
| /* Note: TYPE_ENUM is chosen very carefully so TYPE_RANK works */ |
| #define TYPE_ENUM 0x1600 |
| #define TYPE_LIST 0x1700 |
| /* TYPE_LIST is a basic building block when defining enumerations |
| * type->field_ident holds the name of this enumeration entry. |
| * type->right holds the entry in the list. |
| */ |
| |
| #define TYPE_STRUCT 0x1000 |
| /* For TYPE_STRUCT |
| * type->left holds the link list of TYPE_PRODUCT entries that |
| * make up the structure. |
| * type->elements hold the length of the linked list |
| */ |
| #define TYPE_UNION 0x1100 |
| /* For TYPE_UNION |
| * type->left holds the link list of TYPE_OVERLAP entries that |
| * make up the union. |
| * type->elements hold the length of the linked list |
| */ |
| #define TYPE_POINTER 0x1200 |
| /* For TYPE_POINTER: |
| * type->left holds the type pointed to. |
| */ |
| #define TYPE_FUNCTION 0x1300 |
| /* For TYPE_FUNCTION: |
| * type->left holds the return type. |
| * type->right holds the type of the arguments |
| * type->elements holds the count of the arguments |
| */ |
| #define TYPE_PRODUCT 0x1400 |
| /* TYPE_PRODUCT is a basic building block when defining structures |
| * type->left holds the type that appears first in memory. |
| * type->right holds the type that appears next in memory. |
| */ |
| #define TYPE_OVERLAP 0x1500 |
| /* TYPE_OVERLAP is a basic building block when defining unions |
| * type->left and type->right holds to types that overlap |
| * each other in memory. |
| */ |
| #define TYPE_ARRAY 0x1800 |
| /* TYPE_ARRAY is a basic building block when definitng arrays. |
| * type->left holds the type we are an array of. |
| * type->elements holds the number of elements. |
| */ |
| #define TYPE_TUPLE 0x1900 |
| /* TYPE_TUPLE is a basic building block when defining |
| * positionally reference type conglomerations. (i.e. closures) |
| * In essence it is a wrapper for TYPE_PRODUCT, like TYPE_STRUCT |
| * except it has no field names. |
| * type->left holds the liked list of TYPE_PRODUCT entries that |
| * make up the closure type. |
| * type->elements hold the number of elements in the closure. |
| */ |
| #define TYPE_JOIN 0x1a00 |
| /* TYPE_JOIN is a basic building block when defining |
| * positionally reference type conglomerations. (i.e. closures) |
| * In essence it is a wrapper for TYPE_OVERLAP, like TYPE_UNION |
| * except it has no field names. |
| * type->left holds the liked list of TYPE_OVERLAP entries that |
| * make up the closure type. |
| * type->elements hold the number of elements in the closure. |
| */ |
| #define TYPE_BITFIELD 0x1b00 |
| /* TYPE_BITFIED is the type of a bitfield. |
| * type->left holds the type basic type TYPE_BITFIELD is derived from. |
| * type->elements holds the number of bits in the bitfield. |
| */ |
| #define TYPE_UNKNOWN 0x1c00 |
| /* TYPE_UNKNOWN is the type of an unknown value. |
| * Used on unknown consts and other places where I don't know the type. |
| */ |
| |
| #define ATTRIB_SHIFT 16 |
| #define ATTRIB_MASK 0xffff0000 |
| #define ATTRIB_NOINLINE 0x00010000 |
| #define ATTRIB_ALWAYS_INLINE 0x00020000 |
| |
| #define ELEMENT_COUNT_UNSPECIFIED ULONG_T_MAX |
| |
| struct type { |
| unsigned int type; |
| struct type *left, *right; |
| ulong_t elements; |
| struct hash_entry *field_ident; |
| struct hash_entry *type_ident; |
| }; |
| |
| #define TEMPLATE_BITS 7 |
| #define MAX_TEMPLATES (1<<TEMPLATE_BITS) |
| #define MAX_REG_EQUIVS 16 |
| #define MAX_REGC 14 |
| #define MAX_REGISTERS 75 |
| #define REGISTER_BITS 7 |
| #define MAX_VIRT_REGISTERS (1<<REGISTER_BITS) |
| #define REG_ERROR 0 |
| #define REG_UNSET 1 |
| #define REG_UNNEEDED 2 |
| #define REG_VIRT0 (MAX_REGISTERS + 0) |
| #define REG_VIRT1 (MAX_REGISTERS + 1) |
| #define REG_VIRT2 (MAX_REGISTERS + 2) |
| #define REG_VIRT3 (MAX_REGISTERS + 3) |
| #define REG_VIRT4 (MAX_REGISTERS + 4) |
| #define REG_VIRT5 (MAX_REGISTERS + 5) |
| #define REG_VIRT6 (MAX_REGISTERS + 6) |
| #define REG_VIRT7 (MAX_REGISTERS + 7) |
| #define REG_VIRT8 (MAX_REGISTERS + 8) |
| #define REG_VIRT9 (MAX_REGISTERS + 9) |
| |
| #if (MAX_REGISTERS + 9) > MAX_VIRT_REGISTERS |
| #error "MAX_VIRT_REGISTERS to small" |
| #endif |
| #if (MAX_REGC + REGISTER_BITS) >= 26 |
| #error "Too many id bits used" |
| #endif |
| |
| /* Provision for 8 register classes */ |
| #define REG_SHIFT 0 |
| #define REGC_SHIFT REGISTER_BITS |
| #define REGC_MASK (((1 << MAX_REGC) - 1) << REGISTER_BITS) |
| #define REG_MASK (MAX_VIRT_REGISTERS -1) |
| #define ID_REG(ID) ((ID) & REG_MASK) |
| #define SET_REG(ID, REG) ((ID) = (((ID) & ~REG_MASK) | ((REG) & REG_MASK))) |
| #define ID_REGCM(ID) (((ID) & REGC_MASK) >> REGC_SHIFT) |
| #define SET_REGCM(ID, REGCM) ((ID) = (((ID) & ~REGC_MASK) | (((REGCM) << REGC_SHIFT) & REGC_MASK))) |
| #define SET_INFO(ID, INFO) ((ID) = (((ID) & ~(REG_MASK | REGC_MASK)) | \ |
| (((INFO).reg) & REG_MASK) | ((((INFO).regcm) << REGC_SHIFT) & REGC_MASK))) |
| |
| #define ARCH_INPUT_REGS 4 |
| #define ARCH_OUTPUT_REGS 4 |
| |
| static const struct reg_info arch_input_regs[ARCH_INPUT_REGS]; |
| static const struct reg_info arch_output_regs[ARCH_OUTPUT_REGS]; |
| static unsigned arch_reg_regcm(struct compile_state *state, int reg); |
| static unsigned arch_regcm_normalize(struct compile_state *state, unsigned regcm); |
| static unsigned arch_regcm_reg_normalize(struct compile_state *state, unsigned regcm); |
| static void arch_reg_equivs( |
| struct compile_state *state, unsigned *equiv, int reg); |
| static int arch_select_free_register( |
| struct compile_state *state, char *used, int classes); |
| static unsigned arch_regc_size(struct compile_state *state, int class); |
| static int arch_regcm_intersect(unsigned regcm1, unsigned regcm2); |
| static unsigned arch_type_to_regcm(struct compile_state *state, struct type *type); |
| static const char *arch_reg_str(int reg); |
| static struct reg_info arch_reg_constraint( |
| struct compile_state *state, struct type *type, const char *constraint); |
| static struct reg_info arch_reg_clobber( |
| struct compile_state *state, const char *clobber); |
| static struct reg_info arch_reg_lhs(struct compile_state *state, |
| struct triple *ins, int index); |
| static struct reg_info arch_reg_rhs(struct compile_state *state, |
| struct triple *ins, int index); |
| static int arch_reg_size(int reg); |
| static struct triple *transform_to_arch_instruction( |
| struct compile_state *state, struct triple *ins); |
| static struct triple *flatten( |
| struct compile_state *state, struct triple *first, struct triple *ptr); |
| static void print_dominators(struct compile_state *state, |
| FILE *fp, struct basic_blocks *bb); |
| static void print_dominance_frontiers(struct compile_state *state, |
| FILE *fp, struct basic_blocks *bb); |
| |
| |
| |
| #define DEBUG_ABORT_ON_ERROR 0x00000001 |
| #define DEBUG_BASIC_BLOCKS 0x00000002 |
| #define DEBUG_FDOMINATORS 0x00000004 |
| #define DEBUG_RDOMINATORS 0x00000008 |
| #define DEBUG_TRIPLES 0x00000010 |
| #define DEBUG_INTERFERENCE 0x00000020 |
| #define DEBUG_SCC_TRANSFORM 0x00000040 |
| #define DEBUG_SCC_TRANSFORM2 0x00000080 |
| #define DEBUG_REBUILD_SSA_FORM 0x00000100 |
| #define DEBUG_INLINE 0x00000200 |
| #define DEBUG_RANGE_CONFLICTS 0x00000400 |
| #define DEBUG_RANGE_CONFLICTS2 0x00000800 |
| #define DEBUG_COLOR_GRAPH 0x00001000 |
| #define DEBUG_COLOR_GRAPH2 0x00002000 |
| #define DEBUG_COALESCING 0x00004000 |
| #define DEBUG_COALESCING2 0x00008000 |
| #define DEBUG_VERIFICATION 0x00010000 |
| #define DEBUG_CALLS 0x00020000 |
| #define DEBUG_CALLS2 0x00040000 |
| #define DEBUG_TOKENS 0x80000000 |
| |
| #define DEBUG_DEFAULT ( \ |
| DEBUG_ABORT_ON_ERROR | \ |
| DEBUG_BASIC_BLOCKS | \ |
| DEBUG_FDOMINATORS | \ |
| DEBUG_RDOMINATORS | \ |
| DEBUG_TRIPLES | \ |
| 0 ) |
| |
| #define DEBUG_ALL ( \ |
| DEBUG_ABORT_ON_ERROR | \ |
| DEBUG_BASIC_BLOCKS | \ |
| DEBUG_FDOMINATORS | \ |
| DEBUG_RDOMINATORS | \ |
| DEBUG_TRIPLES | \ |
| DEBUG_INTERFERENCE | \ |
| DEBUG_SCC_TRANSFORM | \ |
| DEBUG_SCC_TRANSFORM2 | \ |
| DEBUG_REBUILD_SSA_FORM | \ |
| DEBUG_INLINE | \ |
| DEBUG_RANGE_CONFLICTS | \ |
| DEBUG_RANGE_CONFLICTS2 | \ |
| DEBUG_COLOR_GRAPH | \ |
| DEBUG_COLOR_GRAPH2 | \ |
| DEBUG_COALESCING | \ |
| DEBUG_COALESCING2 | \ |
| DEBUG_VERIFICATION | \ |
| DEBUG_CALLS | \ |
| DEBUG_CALLS2 | \ |
| DEBUG_TOKENS | \ |
| 0 ) |
| |
| #define COMPILER_INLINE_MASK 0x00000007 |
| #define COMPILER_INLINE_ALWAYS 0x00000000 |
| #define COMPILER_INLINE_NEVER 0x00000001 |
| #define COMPILER_INLINE_DEFAULTON 0x00000002 |
| #define COMPILER_INLINE_DEFAULTOFF 0x00000003 |
| #define COMPILER_INLINE_NOPENALTY 0x00000004 |
| #define COMPILER_ELIMINATE_INEFECTUAL_CODE 0x00000008 |
| #define COMPILER_SIMPLIFY 0x00000010 |
| #define COMPILER_SCC_TRANSFORM 0x00000020 |
| #define COMPILER_SIMPLIFY_OP 0x00000040 |
| #define COMPILER_SIMPLIFY_PHI 0x00000080 |
| #define COMPILER_SIMPLIFY_LABEL 0x00000100 |
| #define COMPILER_SIMPLIFY_BRANCH 0x00000200 |
| #define COMPILER_SIMPLIFY_COPY 0x00000400 |
| #define COMPILER_SIMPLIFY_ARITH 0x00000800 |
| #define COMPILER_SIMPLIFY_SHIFT 0x00001000 |
| #define COMPILER_SIMPLIFY_BITWISE 0x00002000 |
| #define COMPILER_SIMPLIFY_LOGICAL 0x00004000 |
| #define COMPILER_SIMPLIFY_BITFIELD 0x00008000 |
| |
| #define COMPILER_TRIGRAPHS 0x40000000 |
| #define COMPILER_PP_ONLY 0x80000000 |
| |
| #define COMPILER_DEFAULT_FLAGS ( \ |
| COMPILER_TRIGRAPHS | \ |
| COMPILER_ELIMINATE_INEFECTUAL_CODE | \ |
| COMPILER_INLINE_DEFAULTON | \ |
| COMPILER_SIMPLIFY_OP | \ |
| COMPILER_SIMPLIFY_PHI | \ |
| COMPILER_SIMPLIFY_LABEL | \ |
| COMPILER_SIMPLIFY_BRANCH | \ |
| COMPILER_SIMPLIFY_COPY | \ |
| COMPILER_SIMPLIFY_ARITH | \ |
| COMPILER_SIMPLIFY_SHIFT | \ |
| COMPILER_SIMPLIFY_BITWISE | \ |
| COMPILER_SIMPLIFY_LOGICAL | \ |
| COMPILER_SIMPLIFY_BITFIELD | \ |
| 0 ) |
| |
| #define GLOBAL_SCOPE_DEPTH 1 |
| #define FUNCTION_SCOPE_DEPTH (GLOBAL_SCOPE_DEPTH + 1) |
| |
| static void compile_file(struct compile_state *old_state, const char *filename, int local); |
| |
| |
| |
| static void init_compiler_state(struct compiler_state *compiler) |
| { |
| memset(compiler, 0, sizeof(*compiler)); |
| compiler->label_prefix = ""; |
| compiler->ofilename = "auto.inc"; |
| compiler->flags = COMPILER_DEFAULT_FLAGS; |
| compiler->debug = 0; |
| compiler->max_allocation_passes = MAX_ALLOCATION_PASSES; |
| compiler->include_path_count = 1; |
| compiler->include_paths = xcmalloc(sizeof(char *), "include_paths"); |
| compiler->define_count = 1; |
| compiler->defines = xcmalloc(sizeof(char *), "defines"); |
| compiler->undef_count = 1; |
| compiler->undefs = xcmalloc(sizeof(char *), "undefs"); |
| } |
| |
| struct compiler_flag { |
| const char *name; |
| unsigned long flag; |
| }; |
| |
| struct compiler_arg { |
| const char *name; |
| unsigned long mask; |
| struct compiler_flag flags[16]; |
| }; |
| |
| static int set_flag( |
| const struct compiler_flag *ptr, unsigned long *flags, |
| int act, const char *flag) |
| { |
| int result = -1; |
| for(; ptr->name; ptr++) { |
| if (strcmp(ptr->name, flag) == 0) { |
| break; |
| } |
| } |
| if (ptr->name) { |
| result = 0; |
| *flags &= ~(ptr->flag); |
| if (act) { |
| *flags |= ptr->flag; |
| } |
| } |
| return result; |
| } |
| |
| static int set_arg( |
| const struct compiler_arg *ptr, unsigned long *flags, const char *arg) |
| { |
| const char *val; |
| int result = -1; |
| int len; |
| val = strchr(arg, '='); |
| if (val) { |
| len = val - arg; |
| val++; |
| for(; ptr->name; ptr++) { |
| if (strncmp(ptr->name, arg, len) == 0) { |
| break; |
| } |
| } |
| if (ptr->name) { |
| *flags &= ~ptr->mask; |
| result = set_flag(&ptr->flags[0], flags, 1, val); |
| } |
| } |
| return result; |
| } |
| |
| |
| static void flag_usage(FILE *fp, const struct compiler_flag *ptr, |
| const char *prefix, const char *invert_prefix) |
| { |
| for(;ptr->name; ptr++) { |
| fprintf(fp, "%s%s\n", prefix, ptr->name); |
| if (invert_prefix) { |
| fprintf(fp, "%s%s\n", invert_prefix, ptr->name); |
| } |
| } |
| } |
| |
| static void arg_usage(FILE *fp, const struct compiler_arg *ptr, |
| const char *prefix) |
| { |
| for(;ptr->name; ptr++) { |
| const struct compiler_flag *flag; |
| for(flag = &ptr->flags[0]; flag->name; flag++) { |
| fprintf(fp, "%s%s=%s\n", |
| prefix, ptr->name, flag->name); |
| } |
| } |
| } |
| |
| static int append_string(size_t *max, const char ***vec, const char *str, |
| const char *name) |
| { |
| size_t count; |
| count = ++(*max); |
| *vec = xrealloc(*vec, sizeof(char *)*count, "name"); |
| (*vec)[count -1] = 0; |
| (*vec)[count -2] = str; |
| return 0; |
| } |
| |
| static void arg_error(char *fmt, ...); |
| static const char *identifier(const char *str, const char *end); |
| |
| static int append_include_path(struct compiler_state *compiler, const char *str) |
| { |
| int result; |
| if (!exists(str, ".")) { |
| arg_error("Nonexistent include path: `%s'\n", |
| str); |
| } |
| result = append_string(&compiler->include_path_count, |
| &compiler->include_paths, str, "include_paths"); |
| return result; |
| } |
| |
| static int append_define(struct compiler_state *compiler, const char *str) |
| { |
| const char *end, *rest; |
| int result; |
| |
| end = strchr(str, '='); |
| if (!end) { |
| end = str + strlen(str); |
| } |
| rest = identifier(str, end); |
| if (rest != end) { |
| int len = end - str - 1; |
| arg_error("Invalid name cannot define macro: `%*.*s'\n", |
| len, len, str); |
| } |
| result = append_string(&compiler->define_count, |
| &compiler->defines, str, "defines"); |
| return result; |
| } |
| |
| static int append_undef(struct compiler_state *compiler, const char *str) |
| { |
| const char *end, *rest; |
| int result; |
| |
| end = str + strlen(str); |
| rest = identifier(str, end); |
| if (rest != end) { |
| int len = end - str - 1; |
| arg_error("Invalid name cannot undefine macro: `%*.*s'\n", |
| len, len, str); |
| } |
| result = append_string(&compiler->undef_count, |
| &compiler->undefs, str, "undefs"); |
| return result; |
| } |
| |
| static const struct compiler_flag romcc_flags[] = { |
| { "trigraphs", COMPILER_TRIGRAPHS }, |
| { "pp-only", COMPILER_PP_ONLY }, |
| { "eliminate-inefectual-code", COMPILER_ELIMINATE_INEFECTUAL_CODE }, |
| { "simplify", COMPILER_SIMPLIFY }, |
| { "scc-transform", COMPILER_SCC_TRANSFORM }, |
| { "simplify-op", COMPILER_SIMPLIFY_OP }, |
| { "simplify-phi", COMPILER_SIMPLIFY_PHI }, |
| { "simplify-label", COMPILER_SIMPLIFY_LABEL }, |
| { "simplify-branch", COMPILER_SIMPLIFY_BRANCH }, |
| { "simplify-copy", COMPILER_SIMPLIFY_COPY }, |
| { "simplify-arith", COMPILER_SIMPLIFY_ARITH }, |
| { "simplify-shift", COMPILER_SIMPLIFY_SHIFT }, |
| { "simplify-bitwise", COMPILER_SIMPLIFY_BITWISE }, |
| { "simplify-logical", COMPILER_SIMPLIFY_LOGICAL }, |
| { "simplify-bitfield", COMPILER_SIMPLIFY_BITFIELD }, |
| { 0, 0 }, |
| }; |
| static const struct compiler_arg romcc_args[] = { |
| { "inline-policy", COMPILER_INLINE_MASK, |
| { |
| { "always", COMPILER_INLINE_ALWAYS, }, |
| { "never", COMPILER_INLINE_NEVER, }, |
| { "defaulton", COMPILER_INLINE_DEFAULTON, }, |
| { "defaultoff", COMPILER_INLINE_DEFAULTOFF, }, |
| { "nopenalty", COMPILER_INLINE_NOPENALTY, }, |
| { 0, 0 }, |
| }, |
| }, |
| { 0, 0 }, |
| }; |
| static const struct compiler_flag romcc_opt_flags[] = { |
| { "-O", COMPILER_SIMPLIFY }, |
| { "-O2", COMPILER_SIMPLIFY | COMPILER_SCC_TRANSFORM }, |
| { "-E", COMPILER_PP_ONLY }, |
| { 0, 0, }, |
| }; |
| static const struct compiler_flag romcc_debug_flags[] = { |
| { "all", DEBUG_ALL }, |
| { "abort-on-error", DEBUG_ABORT_ON_ERROR }, |
| { "basic-blocks", DEBUG_BASIC_BLOCKS }, |
| { "fdominators", DEBUG_FDOMINATORS }, |
| { "rdominators", DEBUG_RDOMINATORS }, |
| { "triples", DEBUG_TRIPLES }, |
| { "interference", DEBUG_INTERFERENCE }, |
| { "scc-transform", DEBUG_SCC_TRANSFORM }, |
| { "scc-transform2", DEBUG_SCC_TRANSFORM2 }, |
| { "rebuild-ssa-form", DEBUG_REBUILD_SSA_FORM }, |
| { "inline", DEBUG_INLINE }, |
| { "live-range-conflicts", DEBUG_RANGE_CONFLICTS }, |
| { "live-range-conflicts2", DEBUG_RANGE_CONFLICTS2 }, |
| { "color-graph", DEBUG_COLOR_GRAPH }, |
| { "color-graph2", DEBUG_COLOR_GRAPH2 }, |
| { "coalescing", DEBUG_COALESCING }, |
| { "coalescing2", DEBUG_COALESCING2 }, |
| { "verification", DEBUG_VERIFICATION }, |
| { "calls", DEBUG_CALLS }, |
| { "calls2", DEBUG_CALLS2 }, |
| { "tokens", DEBUG_TOKENS }, |
| { 0, 0 }, |
| }; |
| |
| static int compiler_encode_flag( |
| struct compiler_state *compiler, const char *flag) |
| { |
| int act; |
| int result; |
| |
| act = 1; |
| result = -1; |
| if (strncmp(flag, "no-", 3) == 0) { |
| flag += 3; |
| act = 0; |
| } |
| if (strncmp(flag, "-O", 2) == 0) { |
| result = set_flag(romcc_opt_flags, &compiler->flags, act, flag); |
| } |
| else if (strncmp(flag, "-E", 2) == 0) { |
| result = set_flag(romcc_opt_flags, &compiler->flags, act, flag); |
| } |
| else if (strncmp(flag, "-I", 2) == 0) { |
| result = append_include_path(compiler, flag + 2); |
| } |
| else if (strncmp(flag, "-D", 2) == 0) { |
| result = append_define(compiler, flag + 2); |
| } |
| else if (strncmp(flag, "-U", 2) == 0) { |
| result = append_undef(compiler, flag + 2); |
| } |
| else if (act && strncmp(flag, "label-prefix=", 13) == 0) { |
| result = 0; |
| compiler->label_prefix = flag + 13; |
| } |
| else if (act && strncmp(flag, "max-allocation-passes=", 22) == 0) { |
| unsigned long max_passes; |
| char *end; |
| max_passes = strtoul(flag + 22, &end, 10); |
| if (end[0] == '\0') { |
| result = 0; |
| compiler->max_allocation_passes = max_passes; |
| } |
| } |
| else if (act && strcmp(flag, "debug") == 0) { |
| result = 0; |
| compiler->debug |= DEBUG_DEFAULT; |
| } |
| else if (strncmp(flag, "debug-", 6) == 0) { |
| flag += 6; |
| result = set_flag(romcc_debug_flags, &compiler->debug, act, flag); |
| } |
| else { |
| result = set_flag(romcc_flags, &compiler->flags, act, flag); |
| if (result < 0) { |
| result = set_arg(romcc_args, &compiler->flags, flag); |
| } |
| } |
| return result; |
| } |
| |
| static void compiler_usage(FILE *fp) |
| { |
| flag_usage(fp, romcc_opt_flags, "", 0); |
| flag_usage(fp, romcc_flags, "-f", "-fno-"); |
| arg_usage(fp, romcc_args, "-f"); |
| flag_usage(fp, romcc_debug_flags, "-fdebug-", "-fno-debug-"); |
| fprintf(fp, "-flabel-prefix=<prefix for assembly language labels>\n"); |
| fprintf(fp, "--label-prefix=<prefix for assembly language labels>\n"); |
| fprintf(fp, "-I<include path>\n"); |
| fprintf(fp, "-D<macro>[=defn]\n"); |
| fprintf(fp, "-U<macro>\n"); |
| } |
| |
| static void do_cleanup(struct compile_state *state) |
| { |
| if (state->output) { |
| fclose(state->output); |
| unlink(state->compiler->ofilename); |
| state->output = 0; |
| } |
| if (state->dbgout) { |
| fflush(state->dbgout); |
| } |
| if (state->errout) { |
| fflush(state->errout); |
| } |
| } |
| |
| static struct compile_state *exit_state; |
| static void exit_cleanup(void) |
| { |
| if (exit_state) { |
| do_cleanup(exit_state); |
| } |
| } |
| |
| static int get_col(struct file_state *file) |
| { |
| int col; |
| const char *ptr, *end; |
| ptr = file->line_start; |
| end = file->pos; |
| for(col = 0; ptr < end; ptr++) { |
| if (*ptr != '\t') { |
| col++; |
| } |
| else { |
| col = (col & ~7) + 8; |
| } |
| } |
| return col; |
| } |
| |
| static void loc(FILE *fp, struct compile_state *state, struct triple *triple) |
| { |
| int col; |
| if (triple && triple->occurance) { |
| struct occurance *spot; |
| for(spot = triple->occurance; spot; spot = spot->parent) { |
| fprintf(fp, "%s:%d.%d: ", |
| spot->filename, spot->line, spot->col); |
| } |
| return; |
| } |
| if (!state->file) { |
| return; |
| } |
| col = get_col(state->file); |
| fprintf(fp, "%s:%d.%d: ", |
| state->file->report_name, state->file->report_line, col); |
| } |
| |
| static void __attribute__ ((noreturn)) internal_error(struct compile_state *state, struct triple *ptr, |
| const char *fmt, ...) |
| { |
| FILE *fp = state->errout; |
| va_list args; |
| va_start(args, fmt); |
| loc(fp, state, ptr); |
| fputc('\n', fp); |
| if (ptr) { |
| fprintf(fp, "%p %-10s ", ptr, tops(ptr->op)); |
| } |
| fprintf(fp, "Internal compiler error: "); |
| vfprintf(fp, fmt, args); |
| fprintf(fp, "\n"); |
| va_end(args); |
| do_cleanup(state); |
| abort(); |
| } |
| |
| |
| static void internal_warning(struct compile_state *state, struct triple *ptr, |
| const char *fmt, ...) |
| { |
| FILE *fp = state->errout; |
| va_list args; |
| va_start(args, fmt); |
| loc(fp, state, ptr); |
| if (ptr) { |
| fprintf(fp, "%p %-10s ", ptr, tops(ptr->op)); |
| } |
| fprintf(fp, "Internal compiler warning: "); |
| vfprintf(fp, fmt, args); |
| fprintf(fp, "\n"); |
| va_end(args); |
| } |
| |
| |
| |
| static void __attribute__ ((noreturn)) error(struct compile_state *state, struct triple *ptr, |
| const char *fmt, ...) |
| { |
| FILE *fp = state->errout; |
| va_list args; |
| va_start(args, fmt); |
| loc(fp, state, ptr); |
| fputc('\n', fp); |
| if (ptr && (state->compiler->debug & DEBUG_ABORT_ON_ERROR)) { |
| fprintf(fp, "%p %-10s ", ptr, tops(ptr->op)); |
| } |
| vfprintf(fp, fmt, args); |
| va_end(args); |
| fprintf(fp, "\n"); |
| do_cleanup(state); |
| if (state->compiler->debug & DEBUG_ABORT_ON_ERROR) { |
| abort(); |
| } |
| exit(1); |
| } |
| |
| static void warning(struct compile_state *state, struct triple *ptr, |
| const char *fmt, ...) |
| { |
| FILE *fp = state->errout; |
| va_list args; |
| va_start(args, fmt); |
| loc(fp, state, ptr); |
| fprintf(fp, "warning: "); |
| if (ptr && (state->compiler->debug & DEBUG_ABORT_ON_ERROR)) { |
| fprintf(fp, "%p %-10s ", ptr, tops(ptr->op)); |
| } |
| vfprintf(fp, fmt, args); |
| fprintf(fp, "\n"); |
| va_end(args); |
| } |
| |
| #define FINISHME() warning(state, 0, "FINISHME @ %s.%s:%d", __FILE__, __func__, __LINE__) |
| |
| static void valid_op(struct compile_state *state, int op) |
| { |
| char *fmt = "invalid op: %d"; |
| if (op >= OP_MAX) { |
| internal_error(state, 0, fmt, op); |
| } |
| if (op < 0) { |
| internal_error(state, 0, fmt, op); |
| } |
| } |
| |
| static void valid_ins(struct compile_state *state, struct triple *ptr) |
| { |
| valid_op(state, ptr->op); |
| } |
| |
| #if DEBUG_ROMCC_WARNING |
| static void valid_param_count(struct compile_state *state, struct triple *ins) |
| { |
| int lhs, rhs, misc, targ; |
| valid_ins(state, ins); |
| lhs = table_ops[ins->op].lhs; |
| rhs = table_ops[ins->op].rhs; |
| misc = table_ops[ins->op].misc; |
| targ = table_ops[ins->op].targ; |
| |
| if ((lhs >= 0) && (ins->lhs != lhs)) { |
| internal_error(state, ins, "Bad lhs count"); |
| } |
| if ((rhs >= 0) && (ins->rhs != rhs)) { |
| internal_error(state, ins, "Bad rhs count"); |
| } |
| if ((misc >= 0) && (ins->misc != misc)) { |
| internal_error(state, ins, "Bad misc count"); |
| } |
| if ((targ >= 0) && (ins->targ != targ)) { |
| internal_error(state, ins, "Bad targ count"); |
| } |
| } |
| #endif |
| |
| static struct type void_type; |
| static struct type unknown_type; |
| static void use_triple(struct triple *used, struct triple *user) |
| { |
| struct triple_set **ptr, *new; |
| if (!used) |
| return; |
| if (!user) |
| return; |
| ptr = &used->use; |
| while(*ptr) { |
| if ((*ptr)->member == user) { |
| return; |
| } |
| ptr = &(*ptr)->next; |
| } |
| /* Append new to the head of the list, |
| * copy_func and rename_block_variables |
| * depends on this. |
| */ |
| new = xcmalloc(sizeof(*new), "triple_set"); |
| new->member = user; |
| new->next = used->use; |
| used->use = new; |
| } |
| |
| static void unuse_triple(struct triple *used, struct triple *unuser) |
| { |
| struct triple_set *use, **ptr; |
| if (!used) { |
| return; |
| } |
| ptr = &used->use; |
| while(*ptr) { |
| use = *ptr; |
| if (use->member == unuser) { |
| *ptr = use->next; |
| xfree(use); |
| } |
| else { |
| ptr = &use->next; |
| } |
| } |
| } |
| |
| static void put_occurance(struct occurance *occurance) |
| { |
| if (occurance) { |
| occurance->count -= 1; |
| if (occurance->count <= 0) { |
| if (occurance->parent) { |
| put_occurance(occurance->parent); |
| } |
| xfree(occurance); |
| } |
| } |
| } |
| |
| static void get_occurance(struct occurance *occurance) |
| { |
| if (occurance) { |
| occurance->count += 1; |
| } |
| } |
| |
| |
| static struct occurance *new_occurance(struct compile_state *state) |
| { |
| struct occurance *result, *last; |
| const char *filename; |
| const char *function; |
| int line, col; |
| |
| function = ""; |
| filename = 0; |
| line = 0; |
| col = 0; |
| if (state->file) { |
| filename = state->file->report_name; |
| line = state->file->report_line; |
| col = get_col(state->file); |
| } |
| if (state->function) { |
| function = state->function; |
| } |
| last = state->last_occurance; |
| if (last && |
| (last->col == col) && |
| (last->line == line) && |
| (last->function == function) && |
| ((last->filename == filename) || |
| (strcmp(last->filename, filename) == 0))) |
| { |
| get_occurance(last); |
| return last; |
| } |
| if (last) { |
| state->last_occurance = 0; |
| put_occurance(last); |
| } |
| result = xmalloc(sizeof(*result), "occurance"); |
| result->count = 2; |
| result->filename = filename; |
| result->function = function; |
| result->line = line; |
| result->col = col; |
| result->parent = 0; |
| state->last_occurance = result; |
| return result; |
| } |
| |
| static struct occurance *inline_occurance(struct compile_state *state, |
| struct occurance *base, struct occurance *top) |
| { |
| struct occurance *result, *last; |
| if (top->parent) { |
| internal_error(state, 0, "inlining an already inlined function?"); |
| } |
| /* If I have a null base treat it that way */ |
| if ((base->parent == 0) && |
| (base->col == 0) && |
| (base->line == 0) && |
| (base->function[0] == '\0') && |
| (base->filename[0] == '\0')) { |
| base = 0; |
| } |
| /* See if I can reuse the last occurance I had */ |
| last = state->last_occurance; |
| if (last && |
| (last->parent == base) && |
| (last->col == top->col) && |
| (last->line == top->line) && |
| (last->function == top->function) && |
| (last->filename == top->filename)) { |
| get_occurance(last); |
| return last; |
| } |
| /* I can't reuse the last occurance so free it */ |
| if (last) { |
| state->last_occurance = 0; |
| put_occurance(last); |
| } |
| /* Generate a new occurance structure */ |
| get_occurance(base); |
| result = xmalloc(sizeof(*result), "occurance"); |
| result->count = 2; |
| result->filename = top->filename; |
| result->function = top->function; |
| result->line = top->line; |
| result->col = top->col; |
| result->parent = base; |
| state->last_occurance = result; |
| return result; |
| } |
| |
| static struct occurance dummy_occurance = { |
| .count = 2, |
| .filename = __FILE__, |
| .function = "", |
| .line = __LINE__, |
| .col = 0, |
| .parent = 0, |
| }; |
| |
| /* The undef triple is used as a place holder when we are removing pointers |
| * from a triple. Having allows certain sanity checks to pass even |
| * when the original triple that was pointed to is gone. |
| */ |
| static struct triple unknown_triple = { |
| .next = &unknown_triple, |
| .prev = &unknown_triple, |
| .use = 0, |
| .op = OP_UNKNOWNVAL, |
| .lhs = 0, |
| .rhs = 0, |
| .misc = 0, |
| .targ = 0, |
| .type = &unknown_type, |
| .id = -1, /* An invalid id */ |
| .u = { .cval = 0, }, |
| .occurance = &dummy_occurance, |
| .param = { [0] = 0, [1] = 0, }, |
| }; |
| |
| |
| static size_t registers_of(struct compile_state *state, struct type *type); |
| |
| static struct triple *alloc_triple(struct compile_state *state, |
| int op, struct type *type, int lhs_wanted, int rhs_wanted, |
| struct occurance *occurance) |
| { |
| size_t size, extra_count, min_count; |
| int lhs, rhs, misc, targ; |
| struct triple *ret, dummy; |
| dummy.op = op; |
| dummy.occurance = occurance; |
| valid_op(state, op); |
| lhs = table_ops[op].lhs; |
| rhs = table_ops[op].rhs; |
| misc = table_ops[op].misc; |
| targ = table_ops[op].targ; |
| |
| switch(op) { |
| case OP_FCALL: |
| rhs = rhs_wanted; |
| break; |
| case OP_PHI: |
| rhs = rhs_wanted; |
| break; |
| case OP_ADECL: |
| lhs = registers_of(state, type); |
| break; |
| case OP_TUPLE: |
| lhs = registers_of(state, type); |
| break; |
| case OP_ASM: |
| rhs = rhs_wanted; |
| lhs = lhs_wanted; |
| break; |
| } |
| if ((rhs < 0) || (rhs > MAX_RHS)) { |
| internal_error(state, &dummy, "bad rhs count %d", rhs); |
| } |
| if ((lhs < 0) || (lhs > MAX_LHS)) { |
| internal_error(state, &dummy, "bad lhs count %d", lhs); |
| } |
| if ((misc < 0) || (misc > MAX_MISC)) { |
| internal_error(state, &dummy, "bad misc count %d", misc); |
| } |
| if ((targ < 0) || (targ > MAX_TARG)) { |
| internal_error(state, &dummy, "bad targs count %d", targ); |
| } |
| |
| min_count = sizeof(ret->param)/sizeof(ret->param[0]); |
| extra_count = lhs + rhs + misc + targ; |
| extra_count = (extra_count < min_count)? 0 : extra_count - min_count; |
| |
| size = sizeof(*ret) + sizeof(ret->param[0]) * extra_count; |
| ret = xcmalloc(size, "tripple"); |
| ret->op = op; |
| ret->lhs = lhs; |
| ret->rhs = rhs; |
| ret->misc = misc; |
| ret->targ = targ; |
| ret->type = type; |
| ret->next = ret; |
| ret->prev = ret; |
| ret->occurance = occurance; |
| /* A simple sanity check */ |
| if ((ret->op != op) || |
| (ret->lhs != lhs) || |
| (ret->rhs != rhs) || |
| (ret->misc != misc) || |
| (ret->targ != targ) || |
| (ret->type != type) || |
| (ret->next != ret) || |
| (ret->prev != ret) || |
| (ret->occurance != occurance)) { |
| internal_error(state, ret, "huh?"); |
| } |
| return ret; |
| } |
| |
| struct triple *dup_triple(struct compile_state *state, struct triple *src) |
| { |
| struct triple *dup; |
| int src_lhs, src_rhs, src_size; |
| src_lhs = src->lhs; |
| src_rhs = src->rhs; |
| src_size = TRIPLE_SIZE(src); |
| get_occurance(src->occurance); |
| dup = alloc_triple(state, src->op, src->type, src_lhs, src_rhs, |
| src->occurance); |
| memcpy(dup, src, sizeof(*src)); |
| memcpy(dup->param, src->param, src_size * sizeof(src->param[0])); |
| return dup; |
| } |
| |
| static struct triple *copy_triple(struct compile_state *state, struct triple *src) |
| { |
| struct triple *copy; |
| copy = dup_triple(state, src); |
| copy->use = 0; |
| copy->next = copy->prev = copy; |
| return copy; |
| } |
| |
| static struct triple *new_triple(struct compile_state *state, |
| int op, struct type *type, int lhs, int rhs) |
| { |
| struct triple *ret; |
| struct occurance *occurance; |
| occurance = new_occurance(state); |
| ret = alloc_triple(state, op, type, lhs, rhs, occurance); |
| return ret; |
| } |
| |
| static struct triple *build_triple(struct compile_state *state, |
| int op, struct type *type, struct triple *left, struct triple *right, |
| struct occurance *occurance) |
| { |
| struct triple *ret; |
| size_t count; |
| ret = alloc_triple(state, op, type, -1, -1, occurance); |
| count = TRIPLE_SIZE(ret); |
| if (count > 0) { |
| ret->param[0] = left; |
| } |
| if (count > 1) { |
| ret->param[1] = right; |
| } |
| return ret; |
| } |
| |
| static struct triple *triple(struct compile_state *state, |
| int op, struct type *type, struct triple *left, struct triple *right) |
| { |
| struct triple *ret; |
| size_t count; |
| ret = new_triple(state, op, type, -1, -1); |
| count = TRIPLE_SIZE(ret); |
| if (count >= 1) { |
| ret->param[0] = left; |
| } |
| if (count >= 2) { |
| ret->param[1] = right; |
| } |
| return ret; |
| } |
| |
| static struct triple *branch(struct compile_state *state, |
| struct triple *targ, struct triple *test) |
| { |
| struct triple *ret; |
| if (test) { |
| ret = new_triple(state, OP_CBRANCH, &void_type, -1, 1); |
| RHS(ret, 0) = test; |
| } else { |
| ret = new_triple(state, OP_BRANCH, &void_type, -1, 0); |
| } |
| TARG(ret, 0) = targ; |
| /* record the branch target was used */ |
| if (!targ || (targ->op != OP_LABEL)) { |
| internal_error(state, 0, "branch not to label"); |
| } |
| return ret; |
| } |
| |
| static int triple_is_label(struct compile_state *state, struct triple *ins); |
| static int triple_is_call(struct compile_state *state, struct triple *ins); |
| static int triple_is_cbranch(struct compile_state *state, struct triple *ins); |
| static void insert_triple(struct compile_state *state, |
| struct triple *first, struct triple *ptr) |
| { |
| if (ptr) { |
| if ((ptr->id & TRIPLE_FLAG_FLATTENED) || (ptr->next != ptr)) { |
| internal_error(state, ptr, "expression already used"); |
| } |
| ptr->next = first; |
| ptr->prev = first->prev; |
| ptr->prev->next = ptr; |
| ptr->next->prev = ptr; |
| |
| if (triple_is_cbranch(state, ptr->prev) || |
| triple_is_call(state, ptr->prev)) { |
| unuse_triple(first, ptr->prev); |
| use_triple(ptr, ptr->prev); |
| } |
| } |
| } |
| |
| static int triple_stores_block(struct compile_state *state, struct triple *ins) |
| { |
| /* This function is used to determine if u.block |
| * is utilized to store the current block number. |
| */ |
| int stores_block; |
| valid_ins(state, ins); |
| stores_block = (table_ops[ins->op].flags & BLOCK) == BLOCK; |
| return stores_block; |
| } |
| |
| static int triple_is_branch(struct compile_state *state, struct triple *ins); |
| static struct block *block_of_triple(struct compile_state *state, |
| struct triple *ins) |
| { |
| struct triple *first; |
| if (!ins || ins == &unknown_triple) { |
| return 0; |
| } |
| first = state->first; |
| while(ins != first && !triple_is_branch(state, ins->prev) && |
| !triple_stores_block(state, ins)) |
| { |
| if (ins == ins->prev) { |
| internal_error(state, ins, "ins == ins->prev?"); |
| } |
| ins = ins->prev; |
| } |
| return triple_stores_block(state, ins)? ins->u.block: 0; |
| } |
| |
| static void generate_lhs_pieces(struct compile_state *state, struct triple *ins); |
| static struct triple *pre_triple(struct compile_state *state, |
| struct triple *base, |
| int op, struct type *type, struct triple *left, struct triple *right) |
| { |
| struct block *block; |
| struct triple *ret; |
| int i; |
| /* If I am an OP_PIECE jump to the real instruction */ |
| if (base->op == OP_PIECE) { |
| base = MISC(base, 0); |
| } |
| block = block_of_triple(state, base); |
| get_occurance(base->occurance); |
| ret = build_triple(state, op, type, left, right, base->occurance); |
| generate_lhs_pieces(state, ret); |
| if (triple_stores_block(state, ret)) { |
| ret->u.block = block; |
| } |
| insert_triple(state, base, ret); |
| for(i = 0; i < ret->lhs; i++) { |
| struct triple *piece; |
| piece = LHS(ret, i); |
| insert_triple(state, base, piece); |
| use_triple(ret, piece); |
| use_triple(piece, ret); |
| } |
| if (block && (block->first == base)) { |
| block->first = ret; |
| } |
| return ret; |
| } |
| |
| static struct triple *post_triple(struct compile_state *state, |
| struct triple *base, |
| int op, struct type *type, struct triple *left, struct triple *right) |
| { |
| struct block *block; |
| struct triple *ret, *next; |
| int zlhs, i; |
| /* If I am an OP_PIECE jump to the real instruction */ |
| if (base->op == OP_PIECE) { |
| base = MISC(base, 0); |
| } |
| /* If I have a left hand side skip over it */ |
| zlhs = base->lhs; |
| if (zlhs) { |
| base = LHS(base, zlhs - 1); |
| } |
| |
| block = block_of_triple(state, base); |
| get_occurance(base->occurance); |
| ret = build_triple(state, op, type, left, right, base->occurance); |
| generate_lhs_pieces(state, ret); |
| if (triple_stores_block(state, ret)) { |
| ret->u.block = block; |
| } |
| next = base->next; |
| insert_triple(state, next, ret); |
| zlhs = ret->lhs; |
| for(i = 0; i < zlhs; i++) { |
| struct triple *piece; |
| piece = LHS(ret, i); |
| insert_triple(state, next, piece); |
| use_triple(ret, piece); |
| use_triple(piece, ret); |
| } |
| if (block && (block->last == base)) { |
| block->last = ret; |
| if (zlhs) { |
| block->last = LHS(ret, zlhs - 1); |
| } |
| } |
| return ret; |
| } |
| |
| static struct type *reg_type( |
| struct compile_state *state, struct type *type, int reg); |
| |
| static void generate_lhs_piece( |
| struct compile_state *state, struct triple *ins, int index) |
| { |
| struct type *piece_type; |
| struct triple *piece; |
| get_occurance(ins->occurance); |
| piece_type = reg_type(state, ins->type, index * REG_SIZEOF_REG); |
| |
| if ((piece_type->type & TYPE_MASK) == TYPE_BITFIELD) { |
| piece_type = piece_type->left; |
| } |
| #if 0 |
| { |
| static void name_of(FILE *fp, struct type *type); |
| FILE * fp = state->errout; |
| fprintf(fp, "piece_type(%d): ", index); |
| name_of(fp, piece_type); |
| fprintf(fp, "\n"); |
| } |
| #endif |
| piece = alloc_triple(state, OP_PIECE, piece_type, -1, -1, ins->occurance); |
| piece->u.cval = index; |
| LHS(ins, piece->u.cval) = piece; |
| MISC(piece, 0) = ins; |
| } |
| |
| static void generate_lhs_pieces(struct compile_state *state, struct triple *ins) |
| { |
| int i, zlhs; |
| zlhs = ins->lhs; |
| for(i = 0; i < zlhs; i++) { |
| generate_lhs_piece(state, ins, i); |
| } |
| } |
| |
| static struct triple *label(struct compile_state *state) |
| { |
| /* Labels don't get a type */ |
| struct triple *result; |
| result = triple(state, OP_LABEL, &void_type, 0, 0); |
| return result; |
| } |
| |
| static struct triple *mkprog(struct compile_state *state, ...) |
| { |
| struct triple *prog, *head, *arg; |
| va_list args; |
| int i; |
| |
| head = label(state); |
| prog = new_triple(state, OP_PROG, &void_type, -1, -1); |
| RHS(prog, 0) = head; |
| va_start(args, state); |
| i = 0; |
| while((arg = va_arg(args, struct triple *)) != 0) { |
| if (++i >= 100) { |
| internal_error(state, 0, "too many arguments to mkprog"); |
| } |
| flatten(state, head, arg); |
| } |
| va_end(args); |
| prog->type = head->prev->type; |
| return prog; |
| } |
| static void name_of(FILE *fp, struct type *type); |
| static void display_triple(FILE *fp, struct triple *ins) |
| { |
| struct occurance *ptr; |
| const char *reg; |
| char pre, post, vol; |
| pre = post = vol = ' '; |
| if (ins) { |
| if (ins->id & TRIPLE_FLAG_PRE_SPLIT) { |
| pre = '^'; |
| } |
| if (ins->id & TRIPLE_FLAG_POST_SPLIT) { |
| post = ','; |
| } |
| if (ins->id & TRIPLE_FLAG_VOLATILE) { |
| vol = 'v'; |
| } |
| reg = arch_reg_str(ID_REG(ins->id)); |
| } |
| if (ins == 0) { |
| fprintf(fp, "(%p) <nothing> ", ins); |
| } |
| else if (ins->op == OP_INTCONST) { |
| fprintf(fp, "(%p) %c%c%c %-7s %-2d %-10s <0x%08lx> ", |
| ins, pre, post, vol, reg, ins->template_id, tops(ins->op), |
| (unsigned long)(ins->u.cval)); |
| } |
| else if (ins->op == OP_ADDRCONST) { |
| fprintf(fp, "(%p) %c%c%c %-7s %-2d %-10s %-10p <0x%08lx>", |
| ins, pre, post, vol, reg, ins->template_id, tops(ins->op), |
| MISC(ins, 0), (unsigned long)(ins->u.cval)); |
| } |
| else if (ins->op == OP_INDEX) { |
| fprintf(fp, "(%p) %c%c%c %-7s %-2d %-10s %-10p <0x%08lx>", |
| ins, pre, post, vol, reg, ins->template_id, tops(ins->op), |
| RHS(ins, 0), (unsigned long)(ins->u.cval)); |
| } |
| else if (ins->op == OP_PIECE) { |
| fprintf(fp, "(%p) %c%c%c %-7s %-2d %-10s %-10p <0x%08lx>", |
| ins, pre, post, vol, reg, ins->template_id, tops(ins->op), |
| MISC(ins, 0), (unsigned long)(ins->u.cval)); |
| } |
| else { |
| int i, count; |
| fprintf(fp, "(%p) %c%c%c %-7s %-2d %-10s", |
| ins, pre, post, vol, reg, ins->template_id, tops(ins->op)); |
| if (table_ops[ins->op].flags & BITFIELD) { |
| fprintf(fp, " <%2d-%2d:%2d>", |
| ins->u.bitfield.offset, |
| ins->u.bitfield.offset + ins->u.bitfield.size, |
| ins->u.bitfield.size); |
| } |
| count = TRIPLE_SIZE(ins); |
| for(i = 0; i < count; i++) { |
| fprintf(fp, " %-10p", ins->param[i]); |
| } |
| for(; i < 2; i++) { |
| fprintf(fp, " "); |
| } |
| } |
| if (ins) { |
| struct triple_set *user; |
| #if DEBUG_DISPLAY_TYPES |
| fprintf(fp, " <"); |
| name_of(fp, ins->type); |
| fprintf(fp, "> "); |
| #endif |
| #if DEBUG_DISPLAY_USES |
| fprintf(fp, " ["); |
| for(user = ins->use; user; user = user->next) { |
| fprintf(fp, " %-10p", user->member); |
| } |
| fprintf(fp, " ]"); |
| #endif |
| fprintf(fp, " @"); |
| for(ptr = ins->occurance; ptr; ptr = ptr->parent) { |
| fprintf(fp, " %s,%s:%d.%d", |
| ptr->function, |
| ptr->filename, |
| ptr->line, |
| ptr->col); |
| } |
| if (ins->op == OP_ASM) { |
| fprintf(fp, "\n\t%s", ins->u.ainfo->str); |
| } |
| } |
| fprintf(fp, "\n"); |
| fflush(fp); |
| } |
| |
| static int equiv_types(struct type *left, struct type *right); |
| static void display_triple_changes( |
| FILE *fp, const struct triple *new, const struct triple *orig) |
| { |
| |
| int new_count, orig_count; |
| new_count = TRIPLE_SIZE(new); |
| orig_count = TRIPLE_SIZE(orig); |
| if ((new->op != orig->op) || |
| (new_count != orig_count) || |
| (memcmp(orig->param, new->param, |
| orig_count * sizeof(orig->param[0])) != 0) || |
| (memcmp(&orig->u, &new->u, sizeof(orig->u)) != 0)) |
| { |
| struct occurance *ptr; |
| int i, min_count, indent; |
| fprintf(fp, "(%p %p)", new, orig); |
| if (orig->op == new->op) { |
| fprintf(fp, " %-11s", tops(orig->op)); |
| } else { |
| fprintf(fp, " [%-10s %-10s]", |
| tops(new->op), tops(orig->op)); |
| } |
| min_count = new_count; |
| if (min_count > orig_count) { |
| min_count = orig_count; |
| } |
| for(indent = i = 0; i < min_count; i++) { |
| if (orig->param[i] == new->param[i]) { |
| fprintf(fp, " %-11p", |
| orig->param[i]); |
| indent += 12; |
| } else { |
| fprintf(fp, " [%-10p %-10p]", |
| new->param[i], |
| orig->param[i]); |
| indent += 24; |
| } |
| } |
| for(; i < orig_count; i++) { |
| fprintf(fp, " [%-9p]", orig->param[i]); |
| indent += 12; |
| } |
| for(; i < new_count; i++) { |
| fprintf(fp, " [%-9p]", new->param[i]); |
| indent += 12; |
| } |
| if ((new->op == OP_INTCONST)|| |
| (new->op == OP_ADDRCONST)) { |
| fprintf(fp, " <0x%08lx>", |
| (unsigned long)(new->u.cval)); |
| indent += 13; |
| } |
| for(;indent < 36; indent++) { |
| putc(' ', fp); |
| } |
| |
| #if DEBUG_DISPLAY_TYPES |
| fprintf(fp, " <"); |
| name_of(fp, new->type); |
| if (!equiv_types(new->type, orig->type)) { |
| fprintf(fp, " -- "); |
| name_of(fp, orig->type); |
| } |
| fprintf(fp, "> "); |
| #endif |
| |
| fprintf(fp, " @"); |
| for(ptr = orig->occurance; ptr; ptr = ptr->parent) { |
| fprintf(fp, " %s,%s:%d.%d", |
| ptr->function, |
| ptr->filename, |
| ptr->line, |
| ptr->col); |
| |
| } |
| fprintf(fp, "\n"); |
| fflush(fp); |
| } |
| } |
| |
| static int triple_is_pure(struct compile_state *state, struct triple *ins, unsigned id) |
| { |
| /* Does the triple have no side effects. |
| * I.e. Rexecuting the triple with the same arguments |
| * gives the same value. |
| */ |
| unsigned pure; |
| valid_ins(state, ins); |
| pure = PURE_BITS(table_ops[ins->op].flags); |
| if ((pure != PURE) && (pure != IMPURE)) { |
| internal_error(state, 0, "Purity of %s not known", |
| tops(ins->op)); |
| } |
| return (pure == PURE) && !(id & TRIPLE_FLAG_VOLATILE); |
| } |
| |
| static int triple_is_branch_type(struct compile_state *state, |
| struct triple *ins, unsigned type) |
| { |
| /* Is this one of the passed branch types? */ |
| valid_ins(state, ins); |
| return (BRANCH_BITS(table_ops[ins->op].flags) == type); |
| } |
| |
| static int triple_is_branch(struct compile_state *state, struct triple *ins) |
| { |
| /* Is this triple a branch instruction? */ |
| valid_ins(state, ins); |
| return (BRANCH_BITS(table_ops[ins->op].flags) != 0); |
| } |
| |
| static int triple_is_cbranch(struct compile_state *state, struct triple *ins) |
| { |
| /* Is this triple a conditional branch instruction? */ |
| return triple_is_branch_type(state, ins, CBRANCH); |
| } |
| |
| static int triple_is_ubranch(struct compile_state *state, struct triple *ins) |
| { |
| /* Is this triple a unconditional branch instruction? */ |
| unsigned type; |
| valid_ins(state, ins); |
| type = BRANCH_BITS(table_ops[ins->op].flags); |
| return (type != 0) && (type != CBRANCH); |
| } |
| |
| static int triple_is_call(struct compile_state *state, struct triple *ins) |
| { |
| /* Is this triple a call instruction? */ |
| return triple_is_branch_type(state, ins, CALLBRANCH); |
| } |
| |
| static int triple_is_ret(struct compile_state *state, struct triple *ins) |
| { |
| /* Is this triple a return instruction? */ |
| return triple_is_branch_type(state, ins, RETBRANCH); |
| } |
| |
| #if DEBUG_ROMCC_WARNING |
| static int triple_is_simple_ubranch(struct compile_state *state, struct triple *ins) |
| { |
| /* Is this triple an unconditional branch and not a call or a |
| * return? */ |
| return triple_is_branch_type(state, ins, UBRANCH); |
| } |
| #endif |
| |
| static int triple_is_end(struct compile_state *state, struct triple *ins) |
| { |
| return triple_is_branch_type(state, ins, ENDBRANCH); |
| } |
| |
| static int triple_is_label(struct compile_state *state, struct triple *ins) |
| { |
| valid_ins(state, ins); |
| return (ins->op == OP_LABEL); |
| } |
| |
| static struct triple *triple_to_block_start( |
| struct compile_state *state, struct triple *start) |
| { |
| while(!triple_is_branch(state, start->prev) && |
| (!triple_is_label(state, start) || !start->use)) { |
| start = start->prev; |
| } |
| return start; |
| } |
| |
| static int triple_is_def(struct compile_state *state, struct triple *ins) |
| { |
| /* This function is used to determine which triples need |
| * a register. |
| */ |
| int is_def; |
| valid_ins(state, ins); |
| is_def = (table_ops[ins->op].flags & DEF) == DEF; |
| if (ins->lhs >= 1) { |
| is_def = 0; |
| } |
| return is_def; |
| } |
| |
| static int triple_is_structural(struct compile_state *state, struct triple *ins) |
| { |
| int is_structural; |
| valid_ins(state, ins); |
| is_structural = (table_ops[ins->op].flags & STRUCTURAL) == STRUCTURAL; |
| return is_structural; |
| } |
| |
| static int triple_is_part(struct compile_state *state, struct triple *ins) |
| { |
| int is_part; |
| valid_ins(state, ins); |
| is_part = (table_ops[ins->op].flags & PART) == PART; |
| return is_part; |
| } |
| |
| static int triple_is_auto_var(struct compile_state *state, struct triple *ins) |
| { |
| return (ins->op == OP_PIECE) && (MISC(ins, 0)->op == OP_ADECL); |
| } |
| |
| static struct triple **triple_iter(struct compile_state *state, |
| size_t count, struct triple **vector, |
| struct triple *ins, struct triple **last) |
| { |
| struct triple **ret; |
| ret = 0; |
| if (count) { |
| if (!last) { |
| ret = vector; |
| } |
| else if ((last >= vector) && (last < (vector + count - 1))) { |
| ret = last + 1; |
| } |
| } |
| return ret; |
| |
| } |
| |
| static struct triple **triple_lhs(struct compile_state *state, |
| struct triple *ins, struct triple **last) |
| { |
| return triple_iter(state, ins->lhs, &LHS(ins,0), |
| ins, last); |
| } |
| |
| static struct triple **triple_rhs(struct compile_state *state, |
| struct triple *ins, struct triple **last) |
| { |
| return triple_iter(state, ins->rhs, &RHS(ins,0), |
| ins, last); |
| } |
| |
| static struct triple **triple_misc(struct compile_state *state, |
| struct triple *ins, struct triple **last) |
| { |
| return triple_iter(state, ins->misc, &MISC(ins,0), |
| ins, last); |
| } |
| |
| static struct triple **do_triple_targ(struct compile_state *state, |
| struct triple *ins, struct triple **last, int call_edges, int next_edges) |
| { |
| size_t count; |
| struct triple **ret, **vector; |
| int next_is_targ; |
| ret = 0; |
| count = ins->targ; |
| next_is_targ = 0; |
| if (triple_is_cbranch(state, ins)) { |
| next_is_targ = 1; |
| } |
| if (!call_edges && triple_is_call(state, ins)) { |
| count = 0; |
| } |
| if (next_edges && triple_is_call(state, ins)) { |
| next_is_targ = 1; |
| } |
| vector = &TARG(ins, 0); |
| if (!ret && next_is_targ) { |
| if (!last) { |
| ret = &ins->next; |
| } else if (last == &ins->next) { |
| last = 0; |
| } |
| } |
| if (!ret && count) { |
| if (!last) { |
| ret = vector; |
| } |
| else if ((last >= vector) && (last < (vector + count - 1))) { |
| ret = last + 1; |
| } |
| else if (last == vector + count - 1) { |
| last = 0; |
| } |
| } |
| if (!ret && triple_is_ret(state, ins) && call_edges) { |
| struct triple_set *use; |
| for(use = ins->use; use; use = use->next) { |
| if (!triple_is_call(state, use->member)) { |
| continue; |
| } |
| if (!last) { |
| ret = &use->member->next; |
| break; |
| } |
| else if (last == & |