#include <string.h>
#include <limits.h>
-#define DEBUG_ERROR_MESSAGES 0
-#define DEBUG_COLOR_GRAPH 0
-#define DEBUG_SCC 0
+#define MAX_ALLOCATION_PASSES 100
+
#define DEBUG_CONSISTENCY 2
-#define DEBUG_RANGE_CONFLICTS 0
-#define DEBUG_COALESCING 0
+#define DEBUG_SDP_BLOCKS 0
+#define DEBUG_TRIPLE_COLOR 0
#warning "FIXME boundary cases with small types in larger registers"
#warning "FIXME give clear error messages about unused variables"
+#warning "FIXME properly handle multi dimensional arrays"
/* Control flow graph of a loop without goto.
*
return buf;
}
-/* Long on the destination platform */
-typedef unsigned long ulong_t;
-typedef long long_t;
+/* Types on the destination platform */
+#warning "FIXME this assumes 32bit x86 is the destination"
+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;
+typedef uint32_t ulong_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
struct file_state {
struct file_state *prev;
/* Operations on general purpose registers.
*/
-#define OP_SMUL 0
-#define OP_UMUL 1
-#define OP_SDIV 2
-#define OP_UDIV 3
-#define OP_SMOD 4
-#define OP_UMOD 5
-#define OP_ADD 6
-#define OP_SUB 7
-#define OP_SL 8
-#define OP_USR 9
-#define OP_SSR 10
-#define OP_AND 11
-#define OP_XOR 12
-#define OP_OR 13
-#define OP_POS 14 /* Dummy positive operator don't use it */
-#define OP_NEG 15
-#define OP_INVERT 16
+#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_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_NOOP 34
#define OP_WRITE 60
/* OP_WRITE moves one pseudo register to another.
- * LHS(0) holds the destination pseudo register, which must be an OP_DECL.
- * RHS(0) holds the psuedo to move.
+ * RHS(0) holds the destination pseudo register, which must be an OP_DECL.
+ * RHS(1) holds the psuedo to move.
*/
#define OP_READ 61
* Not seen outside of expressions.
*/
-#define OP_CALL 72
-/* OP_CALL performs a procedure call.
+#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
*
/* statements */
#define OP_LIST 80
-/* OP_LIST Holds a list of statements, and a result value.
+/* OP_LIST Holds a list of statements that compose a function, and a result value.
* RHS(0) holds the list of statements.
* MISC(0) holds the value of the statements.
+ * A list of all functions is maintained.
*/
-#define OP_BRANCH 81 /* branch */
+#define OP_BRANCH 81 /* an unconditional branch */
/* For branch instructions
* TARG(0) holds the branch target.
- * RHS(0) if present holds the branch condition.
* ->next holds where to branch to if the branch is not taken.
- * The branch target can only be a decl...
+ * 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(1) 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 83
+#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 84
-/* OP_DECL is a triple that establishes an lvalue for assignments.
+#define OP_ADECL 87
+/* OP_ADECL is a triple that establishes an lvalue for assignments.
* ->use is a list of statements that use the variable.
*/
-#define OP_SDECL 85
+#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.
*/
-#define OP_PHI 86
+#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 geneared from.
+ * 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
struct op_info {
const char *name;
unsigned flags;
-#define PURE 1
-#define IMPURE 2
+#define PURE 1 /* Triple has no side effects */
+#define IMPURE 2 /* Triple has side effects */
#define PURE_BITS(FLAGS) ((FLAGS) & 0x3)
-#define DEF 4
+#define DEF 4 /* Triple is a variable definition */
#define BLOCK 8 /* Triple stores the current block */
+#define STRUCTURAL 16 /* Triple does not generate a machine instruction */
+#define BRANCH 32 /* Triple is a branch instruction */
+#define CBRANCH 64 /* Triple is a conditional branch instruction */
unsigned char lhs, rhs, misc, targ;
};
.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_LTRUE ] = OP( 0, 1, 0, 0, PURE | DEF | BLOCK , "ltrue"),
[OP_LOAD ] = OP( 0, 1, 0, 0, IMPURE | DEF | BLOCK, "load"),
-[OP_STORE ] = OP( 1, 1, 0, 0, IMPURE | BLOCK , "store"),
+[OP_STORE ] = OP( 0, 2, 0, 0, IMPURE | BLOCK , "store"),
-[OP_NOOP ] = OP( 0, 0, 0, 0, PURE | BLOCK, "noop"),
+[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_BLOBCONST ] = OP( 0, 0, 0, 0, PURE , "blobconst"),
[OP_ADDRCONST ] = OP( 0, 0, 1, 0, PURE | DEF, "addrconst"),
-[OP_WRITE ] = OP( 1, 1, 0, 0, PURE | BLOCK, "write"),
+[OP_WRITE ] = OP( 0, 2, 0, 0, PURE | 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_PIECE ] = OP( 0, 0, 1, 0, PURE | DEF, "piece"),
+[OP_PIECE ] = OP( 0, 0, 1, 0, PURE | DEF | STRUCTURAL, "piece"),
[OP_ASM ] = OP(-1, -1, 0, 0, IMPURE, "asm"),
[OP_DEREF ] = OP( 0, 1, 0, 0, 0 | DEF | BLOCK, "deref"),
[OP_DOT ] = OP( 0, 1, 0, 0, 0 | DEF | BLOCK, "dot"),
[OP_COND ] = OP( 0, 3, 0, 0, 0 | DEF | BLOCK, "cond"),
[OP_COMMA ] = OP( 0, 2, 0, 0, 0 | DEF | BLOCK, "comma"),
/* Call is special most it can stand in for anything so it depends on context */
-[OP_CALL ] = OP(-1, -1, 1, 0, 0 | BLOCK, "call"),
-/* The sizes of OP_CALL and OP_VAL_VEC depend upon context */
-[OP_VAL_VEC ] = OP( 0, -1, 0, 0, 0 | BLOCK, "valvec"),
-
-[OP_LIST ] = OP( 0, 1, 1, 0, 0 | DEF, "list"),
-/* The number of targets for OP_BRANCH depends on context */
-[OP_BRANCH ] = OP( 0, -1, 0, 1, PURE | BLOCK, "branch"),
-[OP_LABEL ] = OP( 0, 0, 0, 0, PURE | BLOCK, "label"),
-[OP_ADECL ] = OP( 0, 0, 0, 0, PURE | BLOCK, "adecl"),
-[OP_SDECL ] = OP( 0, 0, 1, 0, PURE | BLOCK, "sdecl"),
+[OP_FCALL ] = OP(-1, -1, 1, 0, 0 | BLOCK, "fcall"),
+/* The sizes of OP_FCALL and OP_VAL_VEC depend upon context */
+[OP_VAL_VEC ] = OP( 0, -1, 0, 0, 0 | BLOCK | STRUCTURAL, "valvec"),
+
+[OP_LIST ] = OP( 0, 1, 1, 0, 0 | DEF | STRUCTURAL, "list"),
+[OP_BRANCH ] = OP( 0, 0, 0, 1, PURE | BLOCK | BRANCH, "branch"),
+[OP_CBRANCH ] = OP( 0, 1, 0, 1, PURE | BLOCK | BRANCH | CBRANCH, "cbranch"),
+[OP_CALL ] = OP( 0, 0, 1, 1, PURE | BLOCK | BRANCH, "call"),
+[OP_RET ] = OP( 0, 1, 0, 0, PURE | BLOCK | BRANCH, "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"),
[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, "jmp"),
-[OP_JMP_EQ ] = OP( 0, 1, 0, 1, PURE | BLOCK, "jmp_eq"),
-[OP_JMP_NOTEQ ] = OP( 0, 1, 0, 1, PURE | BLOCK, "jmp_noteq"),
-[OP_JMP_SLESS ] = OP( 0, 1, 0, 1, PURE | BLOCK, "jmp_sless"),
-[OP_JMP_ULESS ] = OP( 0, 1, 0, 1, PURE | BLOCK, "jmp_uless"),
-[OP_JMP_SMORE ] = OP( 0, 1, 0, 1, PURE | BLOCK, "jmp_smore"),
-[OP_JMP_UMORE ] = OP( 0, 1, 0, 1, PURE | BLOCK, "jmp_umore"),
-[OP_JMP_SLESSEQ] = OP( 0, 1, 0, 1, PURE | BLOCK, "jmp_slesseq"),
-[OP_JMP_ULESSEQ] = OP( 0, 1, 0, 1, PURE | BLOCK, "jmp_ulesseq"),
-[OP_JMP_SMOREEQ] = OP( 0, 1, 0, 1, PURE | BLOCK, "jmp_smoreq"),
-[OP_JMP_UMOREEQ] = OP( 0, 1, 0, 1, PURE | BLOCK, "jmp_umoreq"),
+[OP_JMP ] = OP( 0, 0, 0, 1, PURE | BLOCK | BRANCH, "jmp"),
+[OP_JMP_EQ ] = OP( 0, 1, 0, 1, PURE | BLOCK | BRANCH | CBRANCH, "jmp_eq"),
+[OP_JMP_NOTEQ ] = OP( 0, 1, 0, 1, PURE | BLOCK | BRANCH | CBRANCH, "jmp_noteq"),
+[OP_JMP_SLESS ] = OP( 0, 1, 0, 1, PURE | BLOCK | BRANCH | CBRANCH, "jmp_sless"),
+[OP_JMP_ULESS ] = OP( 0, 1, 0, 1, PURE | BLOCK | BRANCH | CBRANCH, "jmp_uless"),
+[OP_JMP_SMORE ] = OP( 0, 1, 0, 1, PURE | BLOCK | BRANCH | CBRANCH, "jmp_smore"),
+[OP_JMP_UMORE ] = OP( 0, 1, 0, 1, PURE | BLOCK | BRANCH | CBRANCH, "jmp_umore"),
+[OP_JMP_SLESSEQ] = OP( 0, 1, 0, 1, PURE | BLOCK | BRANCH | CBRANCH, "jmp_slesseq"),
+[OP_JMP_ULESSEQ] = OP( 0, 1, 0, 1, PURE | BLOCK | BRANCH | CBRANCH, "jmp_ulesseq"),
+[OP_JMP_SMOREEQ] = OP( 0, 1, 0, 1, PURE | BLOCK | BRANCH | CBRANCH, "jmp_smoreq"),
+[OP_JMP_UMOREEQ] = OP( 0, 1, 0, 1, PURE | BLOCK | BRANCH | 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"),
};
#define MAX_LHS 15
-#define MAX_RHS 15
-#define MAX_MISC 15
-#define MAX_TARG 15
+#define MAX_RHS 250
+#define MAX_MISC 3
+#define MAX_TARG 3
struct occurance {
int count;
unsigned char template_id;
unsigned short sizes;
#define TRIPLE_LHS(SIZES) (((SIZES) >> 0) & 0x0f)
-#define TRIPLE_RHS(SIZES) (((SIZES) >> 4) & 0x0f)
-#define TRIPLE_MISC(SIZES) (((SIZES) >> 8) & 0x0f)
-#define TRIPLE_TARG(SIZES) (((SIZES) >> 12) & 0x0f)
+#define TRIPLE_RHS(SIZES) (((SIZES) >> 4) & 0xff)
+#define TRIPLE_MISC(SIZES) (((SIZES) >> 12) & 0x03)
+#define TRIPLE_TARG(SIZES) (((SIZES) >> 14) & 0x03)
#define TRIPLE_SIZE(SIZES) \
- ((((SIZES) >> 0) & 0x0f) + \
- (((SIZES) >> 4) & 0x0f) + \
- (((SIZES) >> 8) & 0x0f) + \
- (((SIZES) >> 12) & 0x0f))
+ (TRIPLE_LHS(SIZES) + \
+ TRIPLE_RHS(SIZES) + \
+ TRIPLE_MISC(SIZES) + \
+ TRIPLE_TARG(SIZES))
#define TRIPLE_SIZES(LHS, RHS, MISC, TARG) \
((((LHS) & 0x0f) << 0) | \
- (((RHS) & 0x0f) << 4) | \
- (((MISC) & 0x0f) << 8) | \
- (((TARG) & 0x0f) << 12))
+ (((RHS) & 0xff) << 4) | \
+ (((MISC) & 0x03) << 12) | \
+ (((TARG) & 0x03) << 14))
#define TRIPLE_LHS_OFF(SIZES) (0)
#define TRIPLE_RHS_OFF(SIZES) (TRIPLE_LHS_OFF(SIZES) + TRIPLE_LHS(SIZES))
#define TRIPLE_MISC_OFF(SIZES) (TRIPLE_RHS_OFF(SIZES) + TRIPLE_RHS(SIZES))
#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_LOCAL (1 << 27)
struct occurance *occurance;
union {
ulong_t cval;
};
struct block {
struct block *work_next;
- struct block *left, *right;
struct triple *first, *last;
+ int edge_count;
+ struct block_set *edges;
int users;
struct block_set *use;
struct block_set *idominates;
int tok;
struct macro *sym_define;
struct symbol *sym_label;
- struct symbol *sym_struct;
+ struct symbol *sym_tag;
struct symbol *sym_ident;
};
#define HASH_TABLE_SIZE 2048
-struct compile_state {
+struct compiler_state {
const char *label_prefix;
const char *ofilename;
+ unsigned long flags;
+ unsigned long debug;
+ unsigned long max_allocation_passes;
+};
+struct arch_state {
+ unsigned long features;
+};
+struct compile_state {
+ struct compiler_state *compiler;
+ struct arch_state *arch;
FILE *output;
struct file_state *file;
struct occurance *last_occurance;
const char *function;
struct token token[4];
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;
int scope_depth;
int if_depth, if_value;
int macro_line;
struct file_state *macro_file;
+ struct triple *functions;
struct triple *main_function;
+ struct triple *first;
+ struct triple *global_pool;
struct block *first_block, *last_block;
int last_vertex;
- int cpu;
- int debug;
- int optimize;
};
/* visibility global/local */
/* static/auto duration */
/* typedef, register, inline */
#define STOR_SHIFT 0
-#define STOR_MASK 0x000f
+#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_EXTERN 0x0003
-#define STOR_REGISTER 0x0004
-#define STOR_TYPEDEF 0x0008
-#define STOR_INLINE 0x000c
-
-#define QUAL_SHIFT 4
-#define QUAL_MASK 0x0070
+#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 0x0010
-#define QUAL_VOLATILE 0x0020
-#define QUAL_RESTRICT 0x0040
+#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))
-#define TYPE_ARITHMETIC(TYPE) (((TYPE) >= TYPE_CHAR) && ((TYPE) <= TYPE_LDOUBLE))
+#define TYPE_INTEGER(TYPE) ((((TYPE) >= TYPE_CHAR) && ((TYPE) <= TYPE_ULLONG)) || ((TYPE) == TYPE_ENUM))
+#define TYPE_ARITHMETIC(TYPE) ((((TYPE) >= TYPE_CHAR) && ((TYPE) <= TYPE_LDOUBLE)) || ((TYPE) == TYPE_ENUM))
#define TYPE_UNSIGNED(TYPE) ((TYPE) & 0x0100)
#define TYPE_SIGNED(TYPE) (!TYPE_UNSIGNED(TYPE))
-#define TYPE_MKUNSIGNED(TYPE) ((TYPE) | 0x0100)
-#define TYPE_RANK(TYPE) ((TYPE) & ~0x0100)
+#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_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
-#define TYPE_ENUM 0x1100
+#define TYPE_UNION 0x1100
#define TYPE_POINTER 0x1200
/* For TYPE_POINTER:
* type->left holds the type pointed to.
* type->left and type->right holds to types that overlap
* each other in memory.
*/
-#define TYPE_ARRAY 0x1600
+#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-> holds the number of elements.
*/
-#define ELEMENT_COUNT_UNSPECIFIED (~0UL)
+#define ELEMENT_COUNT_UNSPECIFIED ULONG_T_MAX
struct type {
unsigned int type;
struct hash_entry *type_ident;
};
-#define MAX_REGISTERS 75
+#define TEMPLATE_BITS 7
+#define MAX_TEMPLATES (1<<TEMPLATE_BITS)
#define MAX_REG_EQUIVS 16
-#define REGISTER_BITS 16
+#define MAX_REGC 14
+#define MAX_REGISTERS 75
+#define REGISTER_BITS 7
#define MAX_VIRT_REGISTERS (1<<REGISTER_BITS)
-#define TEMPLATE_BITS 6
-#define MAX_TEMPLATES (1<<TEMPLATE_BITS)
-#define MAX_REGC 12
#define REG_UNSET 0
#define REG_UNNEEDED 1
#define REG_VIRT0 (MAX_REGISTERS + 0)
#define REG_VIRT3 (MAX_REGISTERS + 3)
#define REG_VIRT4 (MAX_REGISTERS + 4)
#define REG_VIRT5 (MAX_REGISTERS + 5)
-#define REG_VIRT6 (MAX_REGISTERS + 5)
-#define REG_VIRT7 (MAX_REGISTERS + 5)
-#define REG_VIRT8 (MAX_REGISTERS + 5)
-#define REG_VIRT9 (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) > 27
+#error "Too many id bits used"
+#endif
/* Provision for 8 register classes */
#define REG_SHIFT 0
-#define DEBUG_ABORT_ON_ERROR 0x0001
-#define DEBUG_INTERMEDIATE_CODE 0x0002
-#define DEBUG_CONTROL_FLOW 0x0004
-#define DEBUG_BASIC_BLOCKS 0x0008
-#define DEBUG_FDOMINATORS 0x0010
-#define DEBUG_RDOMINATORS 0x0020
-#define DEBUG_TRIPLES 0x0040
-#define DEBUG_INTERFERENCE 0x0080
-#define DEBUG_ARCH_CODE 0x0100
-#define DEBUG_CODE_ELIMINATION 0x0200
-#define DEBUG_INSERTED_COPIES 0x0400
+#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_DEFAULT ( \
+ DEBUG_ABORT_ON_ERROR | \
+ DEBUG_BASIC_BLOCKS | \
+ DEBUG_FDOMINATORS | \
+ DEBUG_RDOMINATORS | \
+ DEBUG_TRIPLES | \
+ 0 )
+
+#define COMPILER_ELIMINATE_INEFECTUAL_CODE 0x00000001
+#define COMPILER_SIMPLIFY 0x00000002
+#define COMPILER_SCC_TRANSFORM 0x00000004
+#define COMPILER_INLINE 0x00000008
+#define COMPILER_ALWAYS_INLINE 0x00000010
+#define COMPILER_SIMPLIFY_OP 0x00000020
+#define COMPILER_SIMPLIFY_PHI 0x00000040
+#define COMPILER_SIMPLIFY_LABEL 0x00000080
+#define COMPILER_SIMPLIFY_BRANCH 0x00000100
+#define COMPILER_SIMPLIFY_COPY 0x00000200
+#define COMPILER_SIMPLIFY_ARITH 0x00000400
+#define COMPILER_SIMPLIFY_SHIFT 0x00000800
+#define COMPILER_SIMPLIFY_BITWISE 0x00001000
+#define COMPILER_SIMPLIFY_LOGICAL 0x00002000
+
+#define COMPILER_DEFAULT_FLAGS ( \
+ COMPILER_ELIMINATE_INEFECTUAL_CODE | \
+ COMPILER_INLINE | \
+ COMPILER_ALWAYS_INLINE | \
+ 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 | \
+ 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;
+
+}
+
+struct compiler_flag {
+ const char *name;
+ unsigned long flag;
+};
+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 compiler_encode_flag(
+ struct compiler_state *compiler, const char *flag)
+{
+ static const struct compiler_flag flags[] = {
+ { "eliminate-inefectual-code", COMPILER_ELIMINATE_INEFECTUAL_CODE },
+ { "simplify", COMPILER_SIMPLIFY },
+ { "scc-transform", COMPILER_SCC_TRANSFORM },
+ { "inline", COMPILER_INLINE },
+ { "always-inline", COMPILER_ALWAYS_INLINE },
+ { "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 },
+ { 0, 0 },
+ };
+ static const struct compiler_flag opt_flags[] = {
+ { "-O", COMPILER_SIMPLIFY },
+ { "-O2", COMPILER_SIMPLIFY | COMPILER_SCC_TRANSFORM },
+ { 0, 0, },
+ };
+ static const struct compiler_flag debug_flags[] = {
+ { "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 },
+ { 0, 0 },
+ };
+ 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(opt_flags, &compiler->flags, act, flag);
+ }
+ 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(debug_flags, &compiler->debug, act, flag);
+ }
+ else {
+ result = set_flag(flags, &compiler->flags, act, flag);
+ }
+ return result;
+}
+
static void do_cleanup(struct compile_state *state)
{
if (state->output) {
fclose(state->output);
- unlink(state->ofilename);
+ unlink(state->compiler->ofilename);
}
}
static void loc(FILE *fp, struct compile_state *state, struct triple *triple)
{
int col;
- if (triple) {
+ if (triple && triple->occurance) {
struct occurance *spot;
- spot = triple->occurance;
- while(spot->parent) {
- spot = spot->parent;
+ for(spot = triple->occurance; spot; spot = spot->parent) {
+ fprintf(fp, "%s:%d.%d: ",
+ spot->filename, spot->line, spot->col);
}
- fprintf(fp, "%s:%d.%d: ",
- spot->filename, spot->line, spot->col);
return;
}
if (!state->file) {
state->file->report_name, state->file->report_line, col);
}
-static void __internal_error(struct compile_state *state, struct triple *ptr,
+static void internal_error(struct compile_state *state, struct triple *ptr,
char *fmt, ...)
{
va_list args;
va_start(args, fmt);
loc(stderr, state, ptr);
+ fputc('\n', stderr);
if (ptr) {
fprintf(stderr, "%p %s ", ptr, tops(ptr->op));
}
}
-static void __internal_warning(struct compile_state *state, struct triple *ptr,
+static void internal_warning(struct compile_state *state, struct triple *ptr,
char *fmt, ...)
{
va_list args;
va_start(args, fmt);
loc(stderr, state, ptr);
+ if (ptr) {
+ fprintf(stderr, "%p %s ", ptr, tops(ptr->op));
+ }
fprintf(stderr, "Internal compiler warning: ");
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
-static void __error(struct compile_state *state, struct triple *ptr,
+static void error(struct compile_state *state, struct triple *ptr,
char *fmt, ...)
{
va_list args;
va_start(args, fmt);
loc(stderr, state, ptr);
+ fputc('\n', stderr);
+ if (ptr && (state->compiler->debug & DEBUG_ABORT_ON_ERROR)) {
+ fprintf(stderr, "%p %s ", ptr, tops(ptr->op));
+ }
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
do_cleanup(state);
- if (state->debug & DEBUG_ABORT_ON_ERROR) {
+ if (state->compiler->debug & DEBUG_ABORT_ON_ERROR) {
abort();
}
exit(1);
}
-static void __warning(struct compile_state *state, struct triple *ptr,
+static void warning(struct compile_state *state, struct triple *ptr,
char *fmt, ...)
{
va_list args;
va_end(args);
}
-#if DEBUG_ERROR_MESSAGES
-# define internal_error fprintf(stderr, "@ %s.%s:%d \t", __FILE__, __func__, __LINE__),__internal_error
-# define internal_warning fprintf(stderr, "@ %s.%s:%d \t", __FILE__, __func__, __LINE__),__internal_warning
-# define error fprintf(stderr, "@ %s.%s:%d \t", __FILE__, __func__, __LINE__),__error
-# define warning fprintf(stderr, "@ %s.%s:%d \t", __FILE__, __func__, __LINE__),__warning
-#else
-# define internal_error __internal_error
-# define internal_warning __internal_warning
-# define error __error
-# define warning __warning
-#endif
#define FINISHME() warning(state, 0, "FINISHME @ %s.%s:%d", __FILE__, __func__, __LINE__)
static void valid_op(struct compile_state *state, int op)
}
}
-static void push_triple(struct triple *used, struct triple *user)
-{
- struct triple_set *new;
- if (!used)
- return;
- if (!user)
- return;
- /* Append new to the head of the list,
- * it's the only sensible behavoir for a stack.
- */
- new = xcmalloc(sizeof(*new), "triple_set");
- new->member = user;
- new->next = used->use;
- used->use = new;
-}
-
-static void pop_triple(struct triple *used, struct triple *unuser)
-{
- struct triple_set *use, **ptr;
- ptr = &used->use;
- while(*ptr) {
- use = *ptr;
- if (use->member == unuser) {
- *ptr = use->next;
- xfree(use);
- /* Only free one occurance from the stack */
- return;
- }
- else {
- ptr = &use->next;
- }
- }
-}
-
static void put_occurance(struct occurance *occurance)
{
- occurance->count -= 1;
- if (occurance->count <= 0) {
- if (occurance->parent) {
- put_occurance(occurance->parent);
+ if (occurance) {
+ occurance->count -= 1;
+ if (occurance->count <= 0) {
+ if (occurance->parent) {
+ put_occurance(occurance->parent);
+ }
+ xfree(occurance);
}
- xfree(occurance);
}
}
static void get_occurance(struct occurance *occurance)
{
- occurance->count += 1;
+ if (occurance) {
+ occurance->count += 1;
+ }
}
(last->col == col) &&
(last->line == line) &&
(last->function == function) &&
- (strcmp(last->filename, filename) == 0)) {
+ ((last->filename == filename) ||
+ (strcmp(last->filename, filename) == 0)))
+ {
get_occurance(last);
return last;
}
}
static struct occurance *inline_occurance(struct compile_state *state,
- struct occurance *new, struct occurance *orig)
+ 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 == orig) &&
- (last->col == new->col) &&
- (last->line == new->line) &&
- (last->function == new->function) &&
- (last->filename == new->filename)) {
+ (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);
}
- get_occurance(orig);
+ /* Generate a new occurance structure */
+ get_occurance(base);
result = xmalloc(sizeof(*result), "occurance");
result->count = 2;
- result->filename = new->filename;
- result->function = new->function;
- result->line = new->line;
- result->col = new->col;
- result->parent = orig;
+ 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,
.op = OP_INTCONST,
.sizes = TRIPLE_SIZES(0, 0, 0, 0),
.id = -1, /* An invalid id */
- .u = { .cval = 0, },
+ .u = { .cval = 0, },
.occurance = &dummy_occurance,
- .param { [0] = 0, [1] = 0, },
+ .param = { [0] = 0, [1] = 0, },
};
static unsigned short triple_sizes(struct compile_state *state,
- int op, struct type *type, int lhs_wanted, int rhs_wanted)
+ int op, struct type *type, int lhs_wanted, int rhs_wanted,
+ struct occurance *occurance)
{
int lhs, rhs, misc, targ;
+ struct triple dummy;
+ dummy.op = op;
+ dummy.occurance = occurance;
valid_op(state, op);
lhs = table_ops[op].lhs;
rhs = table_ops[op].rhs;
targ = table_ops[op].targ;
- if (op == OP_CALL) {
- struct type *param;
- rhs = 0;
- param = type->right;
- while((param->type & TYPE_MASK) == TYPE_PRODUCT) {
- rhs++;
- param = param->right;
- }
- if ((param->type & TYPE_MASK) != TYPE_VOID) {
- rhs++;
- }
+ if (op == OP_FCALL) {
+ rhs = rhs_wanted;
lhs = 0;
- if ((type->left->type & TYPE_MASK) == TYPE_STRUCT) {
+ if ((type->type & TYPE_MASK) == TYPE_STRUCT) {
lhs = type->left->elements;
}
}
else if (op == OP_VAL_VEC) {
rhs = type->elements;
}
- else if ((op == OP_BRANCH) || (op == OP_PHI)) {
+ else if (op == OP_PHI) {
rhs = rhs_wanted;
}
else if (op == OP_ASM) {
lhs = lhs_wanted;
}
if ((rhs < 0) || (rhs > MAX_RHS)) {
- internal_error(state, 0, "bad rhs");
+ internal_error(state, &dummy, "bad rhs %d", rhs);
}
if ((lhs < 0) || (lhs > MAX_LHS)) {
- internal_error(state, 0, "bad lhs");
+ internal_error(state, &dummy, "bad lhs");
}
if ((misc < 0) || (misc > MAX_MISC)) {
- internal_error(state, 0, "bad misc");
+ internal_error(state, &dummy, "bad misc");
}
if ((targ < 0) || (targ > MAX_TARG)) {
- internal_error(state, 0, "bad targs");
+ internal_error(state, &dummy, "bad targs");
}
return TRIPLE_SIZES(lhs, rhs, misc, targ);
}
{
size_t size, sizes, extra_count, min_count;
struct triple *ret;
- sizes = triple_sizes(state, op, type, lhs, rhs);
+ sizes = triple_sizes(state, op, type, lhs, rhs, occurance);
min_count = sizeof(ret->param)/sizeof(ret->param[0]);
extra_count = TRIPLE_SIZE(sizes);
struct triple *targ, struct triple *test)
{
struct triple *ret;
- ret = new_triple(state, OP_BRANCH, &void_type, -1, test?1:0);
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");
- use_triple(targ, ret);
}
return ret;
}
-
static void insert_triple(struct compile_state *state,
struct triple *first, struct triple *ptr)
{
ptr->prev = first->prev;
ptr->prev->next = ptr;
ptr->next->prev = ptr;
- if ((ptr->prev->op == OP_BRANCH) &&
- TRIPLE_RHS(ptr->prev->sizes)) {
+
+ if ((ptr->prev->op == OP_CBRANCH) || (ptr->prev->op == OP_CALL)) {
unuse_triple(first, ptr->prev);
use_triple(ptr, ptr->prev);
}
struct triple *ins)
{
struct triple *first;
- first = RHS(state->main_function, 0);
+ if (!ins || ins == &zero_triple) {
+ return 0;
+ }
+ first = state->first;
while(ins != first && !triple_stores_block(state, ins)) {
if (ins == ins->prev) {
- internal_error(state, 0, "ins == ins->prev?");
+ internal_error(state, ins, "ins == ins->prev?");
}
ins = ins->prev;
}
}
/* If I have a left hand side skip over it */
zlhs = TRIPLE_LHS(base->sizes);
- if (zlhs && (base->op != OP_WRITE) && (base->op != OP_STORE)) {
+ if (zlhs) {
base = LHS(base, zlhs - 1);
}
if (ins->op == OP_INTCONST) {
fprintf(fp, "(%p) %c%c %-7s %-2d %-10s <0x%08lx> ",
ins, pre, post, reg, ins->template_id, tops(ins->op),
- ins->u.cval);
+ (unsigned long)(ins->u.cval));
}
else if (ins->op == OP_ADDRCONST) {
fprintf(fp, "(%p) %c%c %-7s %-2d %-10s %-10p <0x%08lx>",
ins, pre, post, reg, ins->template_id, tops(ins->op),
- MISC(ins, 0), ins->u.cval);
+ MISC(ins, 0), (unsigned long)(ins->u.cval));
}
else {
int i, count;
ptr->col);
}
fprintf(fp, "\n");
+#if 0
+ {
+ struct triple_set *user;
+ for(user = ptr->use; user; user = user->next) {
+ fprintf(fp, "use: %p\n", user->member);
+ }
+ }
+#endif
fflush(fp);
}
-static int triple_is_pure(struct compile_state *state, struct triple *ins)
+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->sizes);
+ orig_count = TRIPLE_SIZE(orig->sizes);
+ 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)", 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);
+ }
+ 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 void display_func(FILE *fp, struct triple *func)
+{
+ struct triple *first, *ins;
+ fprintf(fp, "display_func %s\n", func->type->type_ident->name);
+ first = ins = RHS(func, 0);
+ do {
+ display_triple(fp, ins);
+ ins = ins->next;
+ } while(ins != first);
+}
+
+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
internal_error(state, 0, "Purity of %s not known\n",
tops(ins->op));
}
- return pure == PURE;
+ return (pure == PURE) && !(id & TRIPLE_FLAG_VOLATILE);
}
static int triple_is_branch(struct compile_state *state, struct triple *ins)
{
- /* This function is used to determine which triples need
- * a register.
- */
- int is_branch;
+ /* Is this triple a branch instruction? */
+ valid_ins(state, ins);
+ return (table_ops[ins->op].flags & BRANCH) != 0;
+}
+
+static int triple_is_cond_branch(struct compile_state *state, struct triple *ins)
+{
+ /* Is this triple a conditional branch instruction? */
+ valid_ins(state, ins);
+ return (table_ops[ins->op].flags & CBRANCH) != 0;
+}
+
+static int triple_is_uncond_branch(struct compile_state *state, struct triple *ins)
+{
+ /* Is this triple a unconditional branch instruction? */
valid_ins(state, ins);
- is_branch = (table_ops[ins->op].targ != 0);
- return is_branch;
+ return (table_ops[ins->op].flags & CBRANCH) == 0;
}
static int triple_is_def(struct compile_state *state, struct triple *ins)
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 struct triple **triple_iter(struct compile_state *state,
size_t count, struct triple **vector,
struct triple *ins, struct triple **last)
ret = 0;
count = TRIPLE_TARG(ins->sizes);
vector = &TARG(ins, 0);
- if (count) {
+ if (!ret &&
+ ((ins->op == OP_CALL) || (table_ops[ins->op].flags & CBRANCH))) {
+ 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)) &&
- TRIPLE_RHS(ins->sizes)) {
- ret = &ins->next;
+ else if (last == vector + count - 1) {
+ last = 0;
+ }
+ }
+ if (!ret && (ins->op == OP_RET)) {
+ struct triple_set *use;
+ for(use = ins->use; use; use = use->next) {
+ if (use->member->op != OP_CALL) {
+ continue;
+ }
+ if (!last) {
+ ret = &use->member->next;
+ break;
+ }
+ else if (last == &use->member->next) {
+ last = 0;
+ }
}
}
return ret;
}
-
static void verify_use(struct compile_state *state,
struct triple *user, struct triple *used)
{
{
struct triple_set *set, *next;
struct triple **expr;
+ struct block *block;
+ valid_ins(state, ptr);
+ /* Make certain the we are not the first or last element of a block */
+ block = block_of_triple(state, ptr);
+ if (block) {
+ if ((block->last == ptr) && (block->first == ptr)) {
+ block->last = block->first = 0;
+ }
+ else if (block->last == ptr) {
+ block->last = ptr->prev;
+ }
+ else if (block->first == ptr) {
+ block->first = ptr->next;
+ }
+ }
/* Remove ptr from use chains where it is the user */
expr = triple_rhs(state, ptr, 0);
for(; expr; expr = triple_rhs(state, ptr, expr)) {
}
expr = triple_targ(state, ptr, 0);
for(; expr; expr = triple_targ(state, ptr, expr)) {
- if (*expr) {
+ if (*expr){
unuse_triple(*expr, ptr);
}
}
/* Reomve ptr from use chains where it is used */
for(set = ptr->use; set; set = next) {
next = set->next;
+ valid_ins(state, set->member);
expr = triple_rhs(state, set->member, 0);
for(; expr; expr = triple_rhs(state, set->member, expr)) {
if (*expr == ptr) {
free_triple(state, ptr);
}
-static void print_triple(struct compile_state *state, struct triple *ptr);
+static void print_triples(struct compile_state *state);
+static void print_blocks(struct compile_state *state, const char *func, FILE *fp);
#define TOK_UNKNOWN 0
#define TOK_SPACE 1
struct hash_entry *entry;
entry = state->hash_table[i];
while(entry) {
- end_scope_syms(&entry->sym_label, depth);
- end_scope_syms(&entry->sym_struct, depth);
- end_scope_syms(&entry->sym_ident, depth);
+ end_scope_syms(&entry->sym_label, depth);
+ end_scope_syms(&entry->sym_tag, depth);
+ end_scope_syms(&entry->sym_ident, depth);
entry = entry->next;
}
}
break;
case TOK_LIT_INT:
{
+ long lval;
char *end;
meat(state, index, TOK_LIT_INT);
errno = 0;
- val = strtol(state->token[index].val.str, &end, 0);
- if (((val == LONG_MIN) || (val == LONG_MAX)) &&
- (errno == ERANGE)) {
+ lval = strtol(state->token[index].val.str, &end, 0);
+ if ((lval > LONG_T_MAX) || (lval < LONG_T_MIN) ||
+ (((lval == LONG_MIN) || (lval == LONG_MAX)) &&
+ (errno == ERANGE))) {
error(state, 0, "Integer constant to large");
}
+ val = lval;
break;
}
default:
meat(state, index, TOK_LIT_STRING);
name = xmalloc(tk->str_len, "report_name");
token = tk->val.str + 1;
+ base = strrchr(token, '/');
name_len = tk->str_len - 2;
if (base != 0) {
dir_len = base - token;
static struct type long_type = { .type = TYPE_LONG };
static struct type ulong_type = { .type = TYPE_ULONG };
+static struct type void_ptr_type = {
+ .type = TYPE_POINTER,
+ .left = &void_type,
+};
+
+static struct type void_func_type = {
+ .type = TYPE_FUNCTION,
+ .left = &void_type,
+ .right = &void_type,
+};
+
static struct triple *variable(struct compile_state *state, struct type *type)
{
struct triple *result;
case STOR_STATIC:
fprintf(fp, "static ");
break;
+ case STOR_LOCAL:
+ fprintf(fp, "local ");
+ break;
case STOR_EXTERN:
fprintf(fp, "extern ");
break;
case STOR_TYPEDEF:
fprintf(fp, "typedef ");
break;
- case STOR_INLINE:
+ case STOR_INLINE | STOR_LOCAL:
fprintf(fp, "inline ");
break;
+ case STOR_INLINE | STOR_STATIC:
+ fprintf(fp, "static inline");
+ break;
+ case STOR_INLINE | STOR_EXTERN:
+ fprintf(fp, "extern inline");
+ break;
+ default:
+ fprintf(fp, "stor:%x", type->type & STOR_MASK);
+ break;
}
}
static void qual_of(FILE *fp, struct type *type)
}
case TYPE_ARRAY:
name_of(fp, type->left);
- fprintf(fp, " [%ld]", type->elements);
+ fprintf(fp, " [%ld]", (long)(type->elements));
break;
default:
fprintf(fp, "????: %x", type->type & TYPE_MASK);
}
align = align_of(state, type);
pad = needed_padding(size, align);
- size = size + pad + sizeof(type);
+ size = size + pad + size_of(state, type);
break;
}
case TYPE_OVERLAP:
}
break;
case TYPE_STRUCT:
+ {
+ size_t align, pad;
size = size_of(state, type->left);
+ /* Pad structures so their size is a multiples of their alignment */
+ align = align_of(state, type);
+ pad = needed_padding(size, align);
+ size = size + pad;
break;
+ }
default:
internal_error(state, 0, "sizeof not yet defined for type\n");
break;
static unsigned int do_integral_promotion(unsigned int type)
{
type &= TYPE_MASK;
- if (TYPE_INTEGER(type) &&
- TYPE_RANK(type) < TYPE_RANK(TYPE_INT)) {
+ if (type == TYPE_ENUM) type = TYPE_INT;
+ if (TYPE_INTEGER(type) && (TYPE_RANK(type) < TYPE_RANK(TYPE_INT))) {
type = TYPE_INT;
}
return type;
{
left &= TYPE_MASK;
right &= TYPE_MASK;
+ /* Convert enums to ints */
+ if (left == TYPE_ENUM) left = TYPE_INT;
+ if (right == TYPE_ENUM) right = TYPE_INT;
if ((left == TYPE_LDOUBLE) || (right == TYPE_LDOUBLE)) {
return TYPE_LDOUBLE;
}
return 0;
}
type = left->type & TYPE_MASK;
+ /* If the basic types match and it is a void type we are done */
+ if (type == TYPE_VOID) {
+ return 1;
+ }
/* if the basic types match and it is an arithmetic type we are done */
if (TYPE_ARITHMETIC(type)) {
return 1;
int_type = type->type & ~TYPE_MASK;
int_type |= do_integral_promotion(type->type);
if (int_type != type->type) {
- def->type = new_type(int_type, 0, 0);
+ if (def->op != OP_LOAD) {
+ def->type = new_type(int_type, 0, 0);
+ }
+ else {
+#warning "FIXME can I just cast all operands like this?"
+ def = triple(state, OP_COPY,
+ new_type(int_type, 0, 0), def, 0);
+ }
}
}
return def;
}
+static struct triple *read_expr(struct compile_state *state, struct triple *def);
+
static struct triple *do_mk_addr_expr(struct compile_state *state,
struct triple *expr, struct type *type, ulong_t offset)
{
RHS(expr, 0),
int_const(state, &ulong_type, offset));
}
+ if (!result) {
+ internal_error(state, expr, "cannot take address of expression");
+ }
return result;
}
{
if ((def->type->type & TYPE_MASK) == TYPE_ARRAY) {
struct type *type;
- struct triple *addrconst;
type = new_type(
TYPE_POINTER | (def->type->type & QUAL_MASK),
def->type->left, 0);
- addrconst = triple(state, OP_ADDRCONST, type, 0, 0);
- MISC(addrconst, 0) = def;
- def = addrconst;
+ if ((def->op == OP_SDECL) || IS_CONST_OP(def->op)) {
+ struct triple *addrconst;
+ if ((def->op != OP_SDECL) && (def->op != OP_BLOBCONST)) {
+ internal_error(state, def, "bad array constant");
+ }
+ addrconst = triple(state, OP_ADDRCONST, type, 0, 0);
+ MISC(addrconst, 0) = def;
+ def = addrconst;
+ }
+ else {
+ def = triple(state, OP_COPY, type, def, 0);
+ }
}
return def;
}
if (is_in_reg(state, def)) {
op = OP_READ;
} else {
+ if (def->op == OP_SDECL) {
+ def = mk_addr_expr(state, def, 0);
+ def = mk_deref_expr(state, def);
+ }
op = OP_LOAD;
}
return triple(state, op, def->type, def, 0);
}
-static void write_compatible(struct compile_state *state,
+int is_write_compatible(struct compile_state *state,
struct type *dest, struct type *rval)
{
int compatible = 0;
(dest->type_ident == rval->type_ident)) {
compatible = 1;
}
- if (!compatible) {
+ return compatible;
+}
+
+
+static void write_compatible(struct compile_state *state,
+ struct type *dest, struct type *rval)
+{
+ if (!is_write_compatible(state, dest, rval)) {
error(state, 0, "Incompatible types in assignment");
}
}
+static int is_init_compatible(struct compile_state *state,
+ struct type *dest, struct type *rval)
+{
+ int compatible = 0;
+ if (is_write_compatible(state, dest, rval)) {
+ compatible = 1;
+ }
+ else if (equiv_types(dest, rval)) {
+ compatible = 1;
+ }
+ return compatible;
+}
+
static struct triple *write_expr(
struct compile_state *state, struct triple *dest, struct triple *rval)
{
rdepth = expr_depth(state, RHS(ins, 1));
count = (ldepth >= rdepth)? ldepth : rdepth;
}
- else if (ins->op == OP_CALL) {
+ else if (ins->op == OP_FCALL) {
/* Don't figure the depth of a call just guess it is huge */
count = 1000;
}
struct compile_state *state, struct triple *first, struct triple *ptr);
static struct triple *flatten_generic(
- struct compile_state *state, struct triple *first, struct triple *ptr)
+ struct compile_state *state, struct triple *first, struct triple *ptr,
+ int ignored)
{
struct rhs_vector {
int depth;
struct triple **ins;
} vector[MAX_RHS];
int i, rhs, lhs;
- /* Only operations with just a rhs should come here */
+ /* Only operations with just a rhs and a lhs should come here */
rhs = TRIPLE_RHS(ptr->sizes);
lhs = TRIPLE_LHS(ptr->sizes);
- if (TRIPLE_SIZE(ptr->sizes) != lhs + rhs) {
+ if (TRIPLE_SIZE(ptr->sizes) != lhs + rhs + ignored) {
internal_error(state, ptr, "unexpected args for: %d %s",
ptr->op, tops(ptr->op));
}
return read_expr(state, val);
}
-struct triple *copy_func(struct compile_state *state, struct triple *ofunc,
- struct occurance *base_occurance)
+static struct triple *flatten_fcall(
+ struct compile_state *state, struct triple *first, struct triple *ptr)
{
- struct triple *nfunc;
- struct triple *nfirst, *ofirst;
- struct triple *new, *old;
-
-#if 0
- fprintf(stdout, "\n");
- loc(stdout, state, 0);
- fprintf(stdout, "\n__________ copy_func _________\n");
- print_triple(state, ofunc);
- fprintf(stdout, "__________ copy_func _________ done\n\n");
-#endif
+ return flatten_generic(state, first, ptr, 1);
+}
- /* Make a new copy of the old function */
- nfunc = triple(state, OP_LIST, ofunc->type, 0, 0);
- nfirst = 0;
- ofirst = old = RHS(ofunc, 0);
+static struct triple *flatten(
+ struct compile_state *state, struct triple *first, struct triple *ptr)
+{
+ struct triple *orig_ptr;
+ if (!ptr)
+ return 0;
do {
- struct triple *new;
- struct occurance *occurance;
- int old_lhs, old_rhs;
- old_lhs = TRIPLE_LHS(old->sizes);
- old_rhs = TRIPLE_RHS(old->sizes);
- occurance = inline_occurance(state, base_occurance, old->occurance);
- new = alloc_triple(state, old->op, old->type, old_lhs, old_rhs,
- occurance);
- if (!triple_stores_block(state, new)) {
- memcpy(&new->u, &old->u, sizeof(new->u));
- }
- if (!nfirst) {
- RHS(nfunc, 0) = nfirst = new;
- }
- else {
- insert_triple(state, nfirst, new);
- }
- new->id |= TRIPLE_FLAG_FLATTENED;
-
- /* During the copy remember new as user of old */
- use_triple(old, new);
-
- /* Populate the return type if present */
- if (old == MISC(ofunc, 0)) {
- MISC(nfunc, 0) = new;
- }
- old = old->next;
- } while(old != ofirst);
-
- /* Make a second pass to fix up any unresolved references */
- old = ofirst;
- new = nfirst;
- do {
- struct triple **oexpr, **nexpr;
- int count, i;
- /* Lookup where the copy is, to join pointers */
- count = TRIPLE_SIZE(old->sizes);
- for(i = 0; i < count; i++) {
- oexpr = &old->param[i];
- nexpr = &new->param[i];
- if (!*nexpr && *oexpr && (*oexpr)->use) {
- *nexpr = (*oexpr)->use->member;
- if (*nexpr == old) {
- internal_error(state, 0, "new == old?");
- }
- use_triple(*nexpr, new);
- }
- if (!*nexpr && *oexpr) {
- internal_error(state, 0, "Could not copy %d\n", i);
- }
- }
- old = old->next;
- new = new->next;
- } while((old != ofirst) && (new != nfirst));
-
- /* Make a third pass to cleanup the extra useses */
- old = ofirst;
- new = nfirst;
- do {
- unuse_triple(old, new);
- old = old->next;
- new = new->next;
- } while ((old != ofirst) && (new != nfirst));
- return nfunc;
-}
-
-static struct triple *flatten_call(
- struct compile_state *state, struct triple *first, struct triple *ptr)
-{
- /* Inline the function call */
- struct type *ptype;
- struct triple *ofunc, *nfunc, *nfirst, *param, *result;
- struct triple *end, *nend;
- int pvals, i;
-
- /* Find the triples */
- ofunc = MISC(ptr, 0);
- if (ofunc->op != OP_LIST) {
- internal_error(state, 0, "improper function");
- }
- nfunc = copy_func(state, ofunc, ptr->occurance);
- nfirst = RHS(nfunc, 0)->next;
- /* Prepend the parameter reading into the new function list */
- ptype = nfunc->type->right;
- param = RHS(nfunc, 0)->next;
- pvals = TRIPLE_RHS(ptr->sizes);
- for(i = 0; i < pvals; i++) {
- struct type *atype;
- struct triple *arg;
- atype = ptype;
- if ((ptype->type & TYPE_MASK) == TYPE_PRODUCT) {
- atype = ptype->left;
- }
- while((param->type->type & TYPE_MASK) != (atype->type & TYPE_MASK)) {
- param = param->next;
- }
- arg = RHS(ptr, i);
- flatten(state, nfirst, write_expr(state, param, arg));
- ptype = ptype->right;
- param = param->next;
- }
- result = 0;
- if ((nfunc->type->left->type & TYPE_MASK) != TYPE_VOID) {
- result = read_expr(state, MISC(nfunc,0));
- }
-#if 0
- fprintf(stdout, "\n");
- loc(stdout, state, 0);
- fprintf(stdout, "\n__________ flatten_call _________\n");
- print_triple(state, nfunc);
- fprintf(stdout, "__________ flatten_call _________ done\n\n");
-#endif
-
- /* Get rid of the extra triples */
- nfirst = RHS(nfunc, 0)->next;
- free_triple(state, RHS(nfunc, 0));
- RHS(nfunc, 0) = 0;
- free_triple(state, nfunc);
-
- /* Append the new function list onto the return list */
- end = first->prev;
- nend = nfirst->prev;
- end->next = nfirst;
- nfirst->prev = end;
- nend->next = first;
- first->prev = nend;
-
- return result;
-}
-
-static struct triple *flatten(
- struct compile_state *state, struct triple *first, struct triple *ptr)
-{
- struct triple *orig_ptr;
- if (!ptr)
- return 0;
- do {
- orig_ptr = ptr;
- /* Only flatten triples once */
- if (ptr->id & TRIPLE_FLAG_FLATTENED) {
- return ptr;
+ orig_ptr = ptr;
+ /* Only flatten triples once */
+ if (ptr->id & TRIPLE_FLAG_FLATTENED) {
+ return ptr;
}
switch(ptr->op) {
- case OP_WRITE:
- case OP_STORE:
- RHS(ptr, 0) = flatten(state, first, RHS(ptr, 0));
- LHS(ptr, 0) = flatten(state, first, LHS(ptr, 0));
- use_triple(LHS(ptr, 0), ptr);
- use_triple(RHS(ptr, 0), ptr);
- break;
case OP_COMMA:
RHS(ptr, 0) = flatten(state, first, RHS(ptr, 0));
ptr = RHS(ptr, 1);
case OP_COND:
ptr = flatten_cond(state, first, ptr);
break;
- case OP_CALL:
- ptr = flatten_call(state, first, ptr);
+ case OP_FCALL:
+ ptr = flatten_fcall(state, first, ptr);
break;
case OP_READ:
case OP_LOAD:
break;
case OP_BRANCH:
use_triple(TARG(ptr, 0), ptr);
- if (TRIPLE_RHS(ptr->sizes)) {
- use_triple(RHS(ptr, 0), ptr);
- if (ptr->next != ptr) {
- use_triple(ptr->next, ptr);
- }
+ break;
+ case OP_CBRANCH:
+ RHS(ptr, 0) = flatten(state, first, RHS(ptr, 0));
+ use_triple(RHS(ptr, 0), ptr);
+ use_triple(TARG(ptr, 0), ptr);
+ if (ptr->next != ptr) {
+ use_triple(ptr->next, ptr);
+ }
+ break;
+ case OP_CALL:
+ MISC(ptr, 0) = flatten(state, first, MISC(ptr, 0));
+ use_triple(MISC(ptr, 0), ptr);
+ use_triple(TARG(ptr, 0), ptr);
+ if (ptr->next != ptr) {
+ use_triple(ptr->next, ptr);
}
break;
+ case OP_RET:
+ RHS(ptr, 0) = flatten(state, first, RHS(ptr, 0));
+ use_triple(RHS(ptr, 0), ptr);
+ break;
case OP_BLOBCONST:
- insert_triple(state, first, ptr);
+ insert_triple(state, state->global_pool, ptr);
ptr->id |= TRIPLE_FLAG_FLATTENED;
+ ptr->id &= ~TRIPLE_FLAG_LOCAL;
ptr = triple(state, OP_SDECL, ptr->type, ptr, 0);
use_triple(MISC(ptr, 0), ptr);
break;
use_triple(ptr, MISC(ptr, 0));
break;
case OP_ADDRCONST:
- case OP_SDECL:
MISC(ptr, 0) = flatten(state, first, MISC(ptr, 0));
use_triple(MISC(ptr, 0), ptr);
break;
+ case OP_SDECL:
+ first = state->global_pool;
+ MISC(ptr, 0) = flatten(state, first, MISC(ptr, 0));
+ use_triple(MISC(ptr, 0), ptr);
+ insert_triple(state, first, ptr);
+ ptr->id |= TRIPLE_FLAG_FLATTENED;
+ ptr->id &= ~TRIPLE_FLAG_LOCAL;
+ return ptr;
case OP_ADECL:
break;
default:
/* Flatten the easy cases we don't override */
- ptr = flatten_generic(state, first, ptr);
+ ptr = flatten_generic(state, first, ptr, 0);
break;
}
} while(ptr && (ptr != orig_ptr));
if (ptr) {
insert_triple(state, first, ptr);
ptr->id |= TRIPLE_FLAG_FLATTENED;
+ ptr->id &= ~TRIPLE_FLAG_LOCAL;
}
return ptr;
}
return mk_deref_expr(state, mk_add_expr(state, left, right));
}
+static struct triple *mk_cast_expr(
+ struct compile_state *state, struct type *type, struct triple *expr)
+{
+ struct triple *def;
+ def = read_expr(state, expr);
+ def = triple(state, OP_COPY, type, def, 0);
+ return def;
+}
+
/*
* Compile time evaluation
* ===========================
return IS_CONST_OP(ins->op);
}
+static int is_simple_const(struct triple *ins)
+{
+ return IS_CONST_OP(ins->op) && (ins->op != OP_ADDRCONST);
+}
+
static int constants_equal(struct compile_state *state,
struct triple *left, struct triple *right)
{
static int is_zero(struct triple *ins)
{
- return is_const(ins) && (ins->u.cval == 0);
+ return is_simple_const(ins) && (ins->u.cval == 0);
}
static int is_one(struct triple *ins)
{
- return is_const(ins) && (ins->u.cval == 1);
+ return is_simple_const(ins) && (ins->u.cval == 1);
}
+static long_t bit_count(ulong_t value)
+{
+ int count;
+ int i;
+ count = 0;
+ for(i = (sizeof(ulong_t)*8) -1; i >= 0; i--) {
+ ulong_t mask;
+ mask = 1;
+ mask <<= i;
+ if (value & mask) {
+ count++;
+ }
+ }
+ return count;
+
+}
static long_t bsr(ulong_t value)
{
int i;
}
static ulong_t read_const(struct compile_state *state,
- struct triple *ins, struct triple **expr)
+ struct triple *ins, struct triple *rhs)
{
- struct triple *rhs;
- rhs = *expr;
switch(rhs->type->type &TYPE_MASK) {
case TYPE_CHAR:
case TYPE_SHORT:
internal_error(state, rhs, "bad type to read_const\n");
break;
}
+ if (!is_simple_const(rhs)) {
+ internal_error(state, rhs, "bad op to read_const\n");
+ }
return rhs->u.cval;
}
-static long_t read_sconst(struct triple *ins, struct triple **expr)
+static long_t read_sconst(struct compile_state *state,
+ struct triple *ins, struct triple *rhs)
{
- struct triple *rhs;
- rhs = *expr;
return (long_t)(rhs->u.cval);
}
+int const_ltrue(struct compile_state *state, struct triple *ins, struct triple *rhs)
+{
+ if (!is_const(rhs)) {
+ internal_error(state, 0, "non const passed to const_true\n");
+ }
+ return !is_zero(rhs);
+}
+
+int const_eq(struct compile_state *state, struct triple *ins,
+ struct triple *left, struct triple *right)
+{
+ int result;
+ if (!is_const(left) || !is_const(right)) {
+ internal_error(state, ins, "non const passed to const_eq\n");
+ result = 0;
+ }
+ else if (left == right) {
+ result = 1;
+ }
+ else if (is_simple_const(left) && is_simple_const(right)) {
+ ulong_t lval, rval;
+ lval = read_const(state, ins, left);
+ rval = read_const(state, ins, right);
+ result = (lval == rval);
+ }
+ else if ((left->op == OP_ADDRCONST) &&
+ (right->op == OP_ADDRCONST)) {
+ result = (MISC(left, 0) == MISC(right, 0)) &&
+ (left->u.cval == right->u.cval);
+ }
+ else {
+ internal_error(state, ins, "incomparable constants passed to const_eq\n");
+ result = 0;
+ }
+ return result;
+
+}
+
+int const_ucmp(struct compile_state *state, struct triple *ins,
+ struct triple *left, struct triple *right)
+{
+ int result;
+ if (!is_const(left) || !is_const(right)) {
+ internal_error(state, ins, "non const past to ucmp_const\n");
+ result = -2;
+ }
+ else if (left == right) {
+ result = 0;
+ }
+ else if (is_simple_const(left) && is_simple_const(right)) {
+ ulong_t lval, rval;
+ lval = read_const(state, ins, left);
+ rval = read_const(state, ins, right);
+ result = 0;
+ if (lval > rval) {
+ result = 1;
+ } else if (rval > lval) {
+ result = -1;
+ }
+ }
+ else if ((left->op == OP_ADDRCONST) &&
+ (right->op == OP_ADDRCONST) &&
+ (MISC(left, 0) == MISC(right, 0))) {
+ result = 0;
+ if (left->u.cval > right->u.cval) {
+ result = 1;
+ } else if (left->u.cval < right->u.cval) {
+ result = -1;
+ }
+ }
+ else {
+ internal_error(state, ins, "incomparable constants passed to const_ucmp\n");
+ result = -2;
+ }
+ return result;
+}
+
+int const_scmp(struct compile_state *state, struct triple *ins,
+ struct triple *left, struct triple *right)
+{
+ int result;
+ if (!is_const(left) || !is_const(right)) {
+ internal_error(state, ins, "non const past to ucmp_const\n");
+ result = -2;
+ }
+ else if (left == right) {
+ result = 0;
+ }
+ else if (is_simple_const(left) && is_simple_const(right)) {
+ long_t lval, rval;
+ lval = read_sconst(state, ins, left);
+ rval = read_sconst(state, ins, right);
+ result = 0;
+ if (lval > rval) {
+ result = 1;
+ } else if (rval > lval) {
+ result = -1;
+ }
+ }
+ else {
+ internal_error(state, ins, "incomparable constants passed to const_scmp\n");
+ result = -2;
+ }
+ return result;
+}
+
static void unuse_rhs(struct compile_state *state, struct triple *ins)
{
struct triple **expr;
static void mkcopy(struct compile_state *state,
struct triple *ins, struct triple *rhs)
{
+ struct block *block;
+ block = block_of_triple(state, ins);
wipe_ins(state, ins);
ins->op = OP_COPY;
ins->sizes = TRIPLE_SIZES(0, 1, 0, 0);
+ ins->u.block = block;
RHS(ins, 0) = rhs;
use_triple(RHS(ins, 0), ins);
}
static void mkaddr_const(struct compile_state *state,
struct triple *ins, struct triple *sdecl, ulong_t value)
{
+ if (sdecl->op != OP_SDECL) {
+ internal_error(state, ins, "bad base for addrconst");
+ }
wipe_ins(state, ins);
ins->op = OP_ADDRCONST;
ins->sizes = TRIPLE_SIZES(0, 0, 1, 0);
static void flatten_structures(struct compile_state *state)
{
struct triple *ins, *first;
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
/* Pass one expand structure values into valvecs.
*/
do {
struct triple *next;
next = ins->next;
+ valid_ins(state, ins);
if ((ins->type->type & TYPE_MASK) == TYPE_STRUCT) {
if (ins->op == OP_VAL_VEC) {
/* Do nothing */
}
propogate_use(state, ins, next);
flatten(state, ins, next);
- free_triple(state, ins);
+ release_triple(state, ins);
}
else if ((ins->op == OP_STORE) || (ins->op == OP_WRITE)) {
struct triple *src, *dst, **vector;
ulong_t i;
op = ins->op;
- src = RHS(ins, 0);
- dst = LHS(ins, 0);
+ src = RHS(ins, 1);
+ dst = RHS(ins, 0);
get_occurance(ins->occurance);
next = alloc_triple(state, OP_VAL_VEC, ins->type, -1, -1,
ins->occurance);
}
propogate_use(state, ins, next);
flatten(state, ins, next);
- free_triple(state, ins);
+ release_triple(state, ins);
}
}
ins = next;
struct triple *next;
next = ins->next;
if (ins->op == OP_VAL_VEC) {
+ if (ins->use) {
+ internal_error(state, ins, "valvec used\n");
+ }
release_triple(state, ins);
}
ins = next;
ins = first;
do {
ins->id &= ~TRIPLE_FLAG_FLATTENED;
- if ((ins->type->type & TYPE_MASK) == TYPE_STRUCT) {
+ if ((ins->op != OP_BLOBCONST) && (ins->op != OP_SDECL) &&
+ ((ins->type->type & TYPE_MASK) == TYPE_STRUCT)) {
internal_error(state, ins, "STRUCT_TYPE remains?");
}
if (ins->op == OP_DOT) {
}
if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
long_t left, right;
- left = read_sconst(ins, &RHS(ins, 0));
- right = read_sconst(ins, &RHS(ins, 1));
+ left = read_sconst(state, ins, RHS(ins, 0));
+ right = read_sconst(state, ins, RHS(ins, 1));
mkconst(state, ins, left * right);
}
else if (is_zero(RHS(ins, 1))) {
struct triple *val;
val = int_const(state, ins->type, tlog2(RHS(ins, 1)));
ins->op = OP_SL;
- insert_triple(state, ins, val);
+ insert_triple(state, state->global_pool, val);
unuse_triple(RHS(ins, 1), ins);
use_triple(val, ins);
RHS(ins, 1) = val;
}
if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
+ left = read_const(state, ins, RHS(ins, 0));
+ right = read_const(state, ins, RHS(ins, 1));
mkconst(state, ins, left * right);
}
else if (is_zero(RHS(ins, 1))) {
struct triple *val;
val = int_const(state, ins->type, tlog2(RHS(ins, 1)));
ins->op = OP_SL;
- insert_triple(state, ins, val);
+ insert_triple(state, state->global_pool, val);
unuse_triple(RHS(ins, 1), ins);
use_triple(val, ins);
RHS(ins, 1) = val;
{
if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
long_t left, right;
- left = read_sconst(ins, &RHS(ins, 0));
- right = read_sconst(ins, &RHS(ins, 1));
+ left = read_sconst(state, ins, RHS(ins, 0));
+ right = read_sconst(state, ins, RHS(ins, 1));
mkconst(state, ins, left / right);
}
else if (is_zero(RHS(ins, 0))) {
struct triple *val;
val = int_const(state, ins->type, tlog2(RHS(ins, 1)));
ins->op = OP_SSR;
- insert_triple(state, ins, val);
+ insert_triple(state, state->global_pool, val);
unuse_triple(RHS(ins, 1), ins);
use_triple(val, ins);
RHS(ins, 1) = val;
{
if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
+ left = read_const(state, ins, RHS(ins, 0));
+ right = read_const(state, ins, RHS(ins, 1));
mkconst(state, ins, left / right);
}
else if (is_zero(RHS(ins, 0))) {
struct triple *val;
val = int_const(state, ins->type, tlog2(RHS(ins, 1)));
ins->op = OP_USR;
- insert_triple(state, ins, val);
+ insert_triple(state, state->global_pool, val);
unuse_triple(RHS(ins, 1), ins);
use_triple(val, ins);
RHS(ins, 1) = val;
{
if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
long_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
+ left = read_const(state, ins, RHS(ins, 0));
+ right = read_const(state, ins, RHS(ins, 1));
mkconst(state, ins, left % right);
}
else if (is_zero(RHS(ins, 0))) {
struct triple *val;
val = int_const(state, ins->type, RHS(ins, 1)->u.cval - 1);
ins->op = OP_AND;
- insert_triple(state, ins, val);
+ insert_triple(state, state->global_pool, val);
unuse_triple(RHS(ins, 1), ins);
use_triple(val, ins);
RHS(ins, 1) = val;
}
}
+
static void simplify_umod(struct compile_state *state, struct triple *ins)
{
if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
+ left = read_const(state, ins, RHS(ins, 0));
+ right = read_const(state, ins, RHS(ins, 1));
mkconst(state, ins, left % right);
}
else if (is_zero(RHS(ins, 0))) {
struct triple *val;
val = int_const(state, ins->type, RHS(ins, 1)->u.cval - 1);
ins->op = OP_AND;
- insert_triple(state, ins, val);
+ insert_triple(state, state->global_pool, val);
unuse_triple(RHS(ins, 1), ins);
use_triple(val, ins);
RHS(ins, 1) = val;
RHS(ins, 1) = tmp;
}
if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
- if (!is_pointer(RHS(ins, 0))) {
+ if (RHS(ins, 0)->op == OP_INTCONST) {
ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
+ left = read_const(state, ins, RHS(ins, 0));
+ right = read_const(state, ins, RHS(ins, 1));
mkconst(state, ins, left + right);
}
- else /* op == OP_ADDRCONST */ {
+ else if (RHS(ins, 0)->op == OP_ADDRCONST) {
struct triple *sdecl;
ulong_t left, right;
sdecl = MISC(RHS(ins, 0), 0);
right = RHS(ins, 1)->u.cval;
mkaddr_const(state, ins, sdecl, left + right);
}
+ else {
+ internal_warning(state, ins, "Optimize me!");
+ }
}
else if (is_const(RHS(ins, 0)) && !is_const(RHS(ins, 1))) {
struct triple *tmp;
static void simplify_sub(struct compile_state *state, struct triple *ins)
{
if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
- if (!is_pointer(RHS(ins, 0))) {
+ if (RHS(ins, 0)->op == OP_INTCONST) {
ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
+ left = read_const(state, ins, RHS(ins, 0));
+ right = read_const(state, ins, RHS(ins, 1));
mkconst(state, ins, left - right);
}
- else /* op == OP_ADDRCONST */ {
+ else if (RHS(ins, 0)->op == OP_ADDRCONST) {
struct triple *sdecl;
ulong_t left, right;
sdecl = MISC(RHS(ins, 0), 0);
right = RHS(ins, 1)->u.cval;
mkaddr_const(state, ins, sdecl, left - right);
}
+ else {
+ internal_warning(state, ins, "Optimize me!");
+ }
}
}
{
if (is_const(RHS(ins, 1))) {
ulong_t right;
- right = read_const(state, ins, &RHS(ins, 1));
+ right = read_const(state, ins, RHS(ins, 1));
if (right >= (size_of(state, ins->type)*8)) {
warning(state, ins, "left shift count >= width of type");
}
}
if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
+ left = read_const(state, ins, RHS(ins, 0));
+ right = read_const(state, ins, RHS(ins, 1));
mkconst(state, ins, left << right);
}
}
{
if (is_const(RHS(ins, 1))) {
ulong_t right;
- right = read_const(state, ins, &RHS(ins, 1));
+ right = read_const(state, ins, RHS(ins, 1));
if (right >= (size_of(state, ins->type)*8)) {
warning(state, ins, "right shift count >= width of type");
}
}
if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
+ left = read_const(state, ins, RHS(ins, 0));
+ right = read_const(state, ins, RHS(ins, 1));
mkconst(state, ins, left >> right);
}
}
{
if (is_const(RHS(ins, 1))) {
ulong_t right;
- right = read_const(state, ins, &RHS(ins, 1));
+ right = read_const(state, ins, RHS(ins, 1));
if (right >= (size_of(state, ins->type)*8)) {
warning(state, ins, "right shift count >= width of type");
}
}
if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
long_t left, right;
- left = read_sconst(ins, &RHS(ins, 0));
- right = read_sconst(ins, &RHS(ins, 1));
+ left = read_sconst(state, ins, RHS(ins, 0));
+ right = read_sconst(state, ins, RHS(ins, 1));
mkconst(state, ins, left >> right);
}
}
{
if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
+ left = read_const(state, ins, RHS(ins, 0));
+ right = read_const(state, ins, RHS(ins, 1));
mkconst(state, ins, left & right);
}
}
{
if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
+ left = read_const(state, ins, RHS(ins, 0));
+ right = read_const(state, ins, RHS(ins, 1));
mkconst(state, ins, left | right);
}
}
{
if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
+ left = read_const(state, ins, RHS(ins, 0));
+ right = read_const(state, ins, RHS(ins, 1));
mkconst(state, ins, left ^ right);
}
}
{
if (is_const(RHS(ins, 0))) {
ulong_t left;
- left = read_const(state, ins, &RHS(ins, 0));
+ left = read_const(state, ins, RHS(ins, 0));
mkconst(state, ins, -left);
}
else if (RHS(ins, 0)->op == OP_NEG) {
{
if (is_const(RHS(ins, 0))) {
ulong_t left;
- left = read_const(state, ins, &RHS(ins, 0));
+ left = read_const(state, ins, RHS(ins, 0));
mkconst(state, ins, ~left);
}
}
static void simplify_eq(struct compile_state *state, struct triple *ins)
{
- if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
- ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
- mkconst(state, ins, left == right);
+ struct triple *left, *right;
+ left = RHS(ins, 0);
+ right = RHS(ins, 1);
+
+ if (is_const(left) && is_const(right)) {
+ mkconst(state, ins, const_eq(state, ins, left, right) == 1);
}
- else if (RHS(ins, 0) == RHS(ins, 1)) {
+ else if (left == right) {
mkconst(state, ins, 1);
}
}
static void simplify_noteq(struct compile_state *state, struct triple *ins)
{
- if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
- ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
- mkconst(state, ins, left != right);
+ struct triple *left, *right;
+ left = RHS(ins, 0);
+ right = RHS(ins, 1);
+
+ if (is_const(left) && is_const(right)) {
+ mkconst(state, ins, const_eq(state, ins, left, right) != 1);
}
- else if (RHS(ins, 0) == RHS(ins, 1)) {
+ if (left == right) {
mkconst(state, ins, 0);
}
}
static void simplify_sless(struct compile_state *state, struct triple *ins)
{
- if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
- long_t left, right;
- left = read_sconst(ins, &RHS(ins, 0));
- right = read_sconst(ins, &RHS(ins, 1));
- mkconst(state, ins, left < right);
+ struct triple *left, *right;
+ left = RHS(ins, 0);
+ right = RHS(ins, 1);
+
+ if (is_const(left) && is_const(right)) {
+ mkconst(state, ins, const_scmp(state, ins, left, right) < 0);
}
- else if (RHS(ins, 0) == RHS(ins, 1)) {
+ else if (left == right) {
mkconst(state, ins, 0);
}
}
static void simplify_uless(struct compile_state *state, struct triple *ins)
{
- if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
- ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
- mkconst(state, ins, left < right);
+ struct triple *left, *right;
+ left = RHS(ins, 0);
+ right = RHS(ins, 1);
+
+ if (is_const(left) && is_const(right)) {
+ mkconst(state, ins, const_ucmp(state, ins, left, right) < 0);
}
- else if (is_zero(RHS(ins, 0))) {
- mkconst(state, ins, 1);
+ else if (is_zero(right)) {
+ mkconst(state, ins, 0);
}
- else if (RHS(ins, 0) == RHS(ins, 1)) {
+ else if (left == right) {
mkconst(state, ins, 0);
}
}
static void simplify_smore(struct compile_state *state, struct triple *ins)
{
- if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
- long_t left, right;
- left = read_sconst(ins, &RHS(ins, 0));
- right = read_sconst(ins, &RHS(ins, 1));
- mkconst(state, ins, left > right);
+ struct triple *left, *right;
+ left = RHS(ins, 0);
+ right = RHS(ins, 1);
+
+ if (is_const(left) && is_const(right)) {
+ mkconst(state, ins, const_scmp(state, ins, left, right) > 0);
}
- else if (RHS(ins, 0) == RHS(ins, 1)) {
+ else if (left == right) {
mkconst(state, ins, 0);
}
}
static void simplify_umore(struct compile_state *state, struct triple *ins)
{
- if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
- ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
- mkconst(state, ins, left > right);
+ struct triple *left, *right;
+ left = RHS(ins, 0);
+ right = RHS(ins, 1);
+
+ if (is_const(left) && is_const(right)) {
+ mkconst(state, ins, const_ucmp(state, ins, left, right) > 0);
}
- else if (is_zero(RHS(ins, 1))) {
- mkconst(state, ins, 1);
+ else if (is_zero(left)) {
+ mkconst(state, ins, 0);
}
- else if (RHS(ins, 0) == RHS(ins, 1)) {
+ else if (left == right) {
mkconst(state, ins, 0);
}
}
static void simplify_slesseq(struct compile_state *state, struct triple *ins)
{
- if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
- long_t left, right;
- left = read_sconst(ins, &RHS(ins, 0));
- right = read_sconst(ins, &RHS(ins, 1));
- mkconst(state, ins, left <= right);
+ struct triple *left, *right;
+ left = RHS(ins, 0);
+ right = RHS(ins, 1);
+
+ if (is_const(left) && is_const(right)) {
+ mkconst(state, ins, const_scmp(state, ins, left, right) <= 0);
}
- else if (RHS(ins, 0) == RHS(ins, 1)) {
+ else if (left == right) {
mkconst(state, ins, 1);
}
}
static void simplify_ulesseq(struct compile_state *state, struct triple *ins)
{
- if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
- ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
- mkconst(state, ins, left <= right);
+ struct triple *left, *right;
+ left = RHS(ins, 0);
+ right = RHS(ins, 1);
+
+ if (is_const(left) && is_const(right)) {
+ mkconst(state, ins, const_ucmp(state, ins, left, right) <= 0);
}
- else if (is_zero(RHS(ins, 0))) {
+ else if (is_zero(left)) {
mkconst(state, ins, 1);
}
- else if (RHS(ins, 0) == RHS(ins, 1)) {
+ else if (left == right) {
mkconst(state, ins, 1);
}
}
static void simplify_smoreeq(struct compile_state *state, struct triple *ins)
{
- if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 0))) {
- long_t left, right;
- left = read_sconst(ins, &RHS(ins, 0));
- right = read_sconst(ins, &RHS(ins, 1));
- mkconst(state, ins, left >= right);
+ struct triple *left, *right;
+ left = RHS(ins, 0);
+ right = RHS(ins, 1);
+
+ if (is_const(left) && is_const(right)) {
+ mkconst(state, ins, const_scmp(state, ins, left, right) >= 0);
}
- else if (RHS(ins, 0) == RHS(ins, 1)) {
+ else if (left == right) {
mkconst(state, ins, 1);
}
}
static void simplify_umoreeq(struct compile_state *state, struct triple *ins)
{
- if (is_const(RHS(ins, 0)) && is_const(RHS(ins, 1))) {
- ulong_t left, right;
- left = read_const(state, ins, &RHS(ins, 0));
- right = read_const(state, ins, &RHS(ins, 1));
- mkconst(state, ins, left >= right);
+ struct triple *left, *right;
+ left = RHS(ins, 0);
+ right = RHS(ins, 1);
+
+ if (is_const(left) && is_const(right)) {
+ mkconst(state, ins, const_ucmp(state, ins, left, right) >= 0);
}
- else if (is_zero(RHS(ins, 1))) {
+ else if (is_zero(right)) {
mkconst(state, ins, 1);
}
- else if (RHS(ins, 0) == RHS(ins, 1)) {
+ else if (left == right) {
mkconst(state, ins, 1);
}
}
static void simplify_lfalse(struct compile_state *state, struct triple *ins)
{
- if (is_const(RHS(ins, 0))) {
- ulong_t left;
- left = read_const(state, ins, &RHS(ins, 0));
- mkconst(state, ins, left == 0);
+ struct triple *rhs;
+ rhs = RHS(ins, 0);
+
+ if (is_const(rhs)) {
+ mkconst(state, ins, !const_ltrue(state, ins, rhs));
}
/* Otherwise if I am the only user... */
- else if ((RHS(ins, 0)->use->member == ins) && (RHS(ins, 0)->use->next == 0)) {
+ else if ((rhs->use) &&
+ (rhs->use->member == ins) && (rhs->use->next == 0)) {
int need_copy = 1;
/* Invert a boolean operation */
- switch(RHS(ins, 0)->op) {
- case OP_LTRUE: RHS(ins, 0)->op = OP_LFALSE; break;
- case OP_LFALSE: RHS(ins, 0)->op = OP_LTRUE; break;
- case OP_EQ: RHS(ins, 0)->op = OP_NOTEQ; break;
- case OP_NOTEQ: RHS(ins, 0)->op = OP_EQ; break;
- case OP_SLESS: RHS(ins, 0)->op = OP_SMOREEQ; break;
- case OP_ULESS: RHS(ins, 0)->op = OP_UMOREEQ; break;
- case OP_SMORE: RHS(ins, 0)->op = OP_SLESSEQ; break;
- case OP_UMORE: RHS(ins, 0)->op = OP_ULESSEQ; break;
- case OP_SLESSEQ: RHS(ins, 0)->op = OP_SMORE; break;
- case OP_ULESSEQ: RHS(ins, 0)->op = OP_UMORE; break;
- case OP_SMOREEQ: RHS(ins, 0)->op = OP_SLESS; break;
- case OP_UMOREEQ: RHS(ins, 0)->op = OP_ULESS; break;
+ switch(rhs->op) {
+ case OP_LTRUE: rhs->op = OP_LFALSE; break;
+ case OP_LFALSE: rhs->op = OP_LTRUE; break;
+ case OP_EQ: rhs->op = OP_NOTEQ; break;
+ case OP_NOTEQ: rhs->op = OP_EQ; break;
+ case OP_SLESS: rhs->op = OP_SMOREEQ; break;
+ case OP_ULESS: rhs->op = OP_UMOREEQ; break;
+ case OP_SMORE: rhs->op = OP_SLESSEQ; break;
+ case OP_UMORE: rhs->op = OP_ULESSEQ; break;
+ case OP_SLESSEQ: rhs->op = OP_SMORE; break;
+ case OP_ULESSEQ: rhs->op = OP_UMORE; break;
+ case OP_SMOREEQ: rhs->op = OP_SLESS; break;
+ case OP_UMOREEQ: rhs->op = OP_ULESS; break;
default:
need_copy = 0;
break;
}
if (need_copy) {
- mkcopy(state, ins, RHS(ins, 0));
+ mkcopy(state, ins, rhs);
}
}
}
static void simplify_ltrue (struct compile_state *state, struct triple *ins)
{
- if (is_const(RHS(ins, 0))) {
- ulong_t left;
- left = read_const(state, ins, &RHS(ins, 0));
- mkconst(state, ins, left != 0);
+ struct triple *rhs;
+ rhs = RHS(ins, 0);
+
+ if (is_const(rhs)) {
+ mkconst(state, ins, const_ltrue(state, ins, rhs));
}
- else switch(RHS(ins, 0)->op) {
+ else switch(rhs->op) {
case OP_LTRUE: case OP_LFALSE: case OP_EQ: case OP_NOTEQ:
case OP_SLESS: case OP_ULESS: case OP_SMORE: case OP_UMORE:
case OP_SLESSEQ: case OP_ULESSEQ: case OP_SMOREEQ: case OP_UMOREEQ:
- mkcopy(state, ins, RHS(ins, 0));
+ mkcopy(state, ins, rhs);
}
}
case OP_INTCONST:
{
ulong_t left;
- left = read_const(state, ins, &RHS(ins, 0));
+ left = read_const(state, ins, RHS(ins, 0));
mkconst(state, ins, left);
break;
}
}
}
+static int phi_present(struct block *block)
+{
+ struct triple *ptr;
+ if (!block) {
+ return 0;
+ }
+ ptr = block->first;
+ do {
+ if (ptr->op == OP_PHI) {
+ return 1;
+ }
+ ptr = ptr->next;
+ } while(ptr != block->last);
+ return 0;
+}
+
+static int phi_dependency(struct block *block)
+{
+ /* A block has a phi dependency if a phi function
+ * depends on that block to exist, and makes a block
+ * that is otherwise useless unsafe to remove.
+ */
+ if (block) {
+ struct block_set *edge;
+ for(edge = block->edges; edge; edge = edge->next) {
+ if (phi_present(edge->member)) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static struct triple *branch_target(struct compile_state *state, struct triple *ins)
+{
+ struct triple *targ;
+ targ = TARG(ins, 0);
+ /* During scc_transform temporary triples are allocated that
+ * loop back onto themselves. If I see one don't advance the
+ * target.
+ */
+ while(triple_is_structural(state, targ) &&
+ (targ->next != targ) && (targ->next != state->first)) {
+ targ = targ->next;
+ }
+ return targ;
+}
+
+
static void simplify_branch(struct compile_state *state, struct triple *ins)
{
- struct block *block;
- if (ins->op != OP_BRANCH) {
+ int simplified;
+ if ((ins->op != OP_BRANCH) && (ins->op != OP_CBRANCH)) {
internal_error(state, ins, "not branch");
}
if (ins->use != 0) {
internal_error(state, ins, "branch use");
}
-#warning "FIXME implement simplify branch."
/* The challenge here with simplify branch is that I need to
* make modifications to the control flow graph as well
- * as to the branch instruction itself.
+ * as to the branch instruction itself. That is handled
+ * by rebuilding the basic blocks after simplify all is called.
*/
- block = ins->u.block;
-
- if (TRIPLE_RHS(ins->sizes) && is_const(RHS(ins, 0))) {
+
+ /* If we have a branch to an unconditional branch update
+ * our target. But watch out for dependencies from phi
+ * functions.
+ */
+ do {
+ struct triple *targ;
+ simplified = 0;
+ targ = branch_target(state, ins);
+ if ((targ != ins) && (targ->op == OP_BRANCH) &&
+ !phi_dependency(targ->u.block))
+ {
+ unuse_triple(TARG(ins, 0), ins);
+ TARG(ins, 0) = TARG(targ, 0);
+ use_triple(TARG(ins, 0), ins);
+ simplified = 1;
+ }
+ } while(simplified);
+
+ /* If we have a conditional branch with a constant condition
+ * make it an unconditional branch.
+ */
+ if ((ins->op == OP_CBRANCH) && is_const(RHS(ins, 0))) {
struct triple *targ;
ulong_t value;
- value = read_const(state, ins, &RHS(ins, 0));
+ value = read_const(state, ins, RHS(ins, 0));
unuse_triple(RHS(ins, 0), ins);
targ = TARG(ins, 0);
ins->sizes = TRIPLE_SIZES(0, 0, 0, 1);
+ ins->op = OP_BRANCH;
if (value) {
unuse_triple(ins->next, ins);
TARG(ins, 0) = targ;
unuse_triple(targ, ins);
TARG(ins, 0) = ins->next;
}
-#warning "FIXME handle the case of making a branch unconditional"
}
+
+ /* If we have a branch to the next instruction
+ * make it a noop.
+ */
if (TARG(ins, 0) == ins->next) {
unuse_triple(ins->next, ins);
- if (TRIPLE_RHS(ins->sizes)) {
+ if (ins->op == OP_CBRANCH) {
unuse_triple(RHS(ins, 0), ins);
unuse_triple(ins->next, ins);
}
if (ins->use) {
internal_error(state, ins, "noop use != 0");
}
-#warning "FIXME handle the case of killing a branch"
+ }
+}
+
+static void simplify_label(struct compile_state *state, struct triple *ins)
+{
+ /* Ignore volatile labels */
+ if (!triple_is_pure(state, ins, ins->id)) {
+ return;
+ }
+ if (ins->use == 0) {
+ ins->op = OP_NOOP;
+ }
+ else if (ins->prev->op == OP_LABEL) {
+ /* In general it is not safe to merge one label that
+ * imediately follows another. The problem is that the empty
+ * looking block may have phi functions that depend on it.
+ */
+ if (!phi_dependency(ins->prev->u.block)) {
+ struct triple_set *user, *next;
+ ins->op = OP_NOOP;
+ for(user = ins->use; user; user = next) {
+ struct triple *use, **expr;
+ next = user->next;
+ use = user->member;
+ expr = triple_targ(state, use, 0);
+ for(;expr; expr = triple_targ(state, use, expr)) {
+ if (*expr == ins) {
+ *expr = ins->prev;
+ unuse_triple(ins, use);
+ use_triple(ins->prev, use);
+ }
+
+ }
+ }
+ if (ins->use) {
+ internal_error(state, ins, "noop use != 0");
+ }
+ }
}
}
static void simplify_phi(struct compile_state *state, struct triple *ins)
{
- struct triple **expr;
- ulong_t value;
- expr = triple_rhs(state, ins, 0);
- if (!*expr || !is_const(*expr)) {
+ struct triple **slot;
+ struct triple *value;
+ int zrhs, i;
+ ulong_t cvalue;
+ slot = &RHS(ins, 0);
+ zrhs = TRIPLE_RHS(ins->sizes);
+ if (zrhs == 0) {
return;
}
- value = read_const(state, ins, expr);
- for(;expr;expr = triple_rhs(state, ins, expr)) {
- if (!*expr || !is_const(*expr)) {
- return;
+ /* See if all of the rhs members of a phi have the same value */
+ if (slot[0] && is_simple_const(slot[0])) {
+ cvalue = read_const(state, ins, slot[0]);
+ for(i = 1; i < zrhs; i++) {
+ if ( !slot[i] ||
+ !is_simple_const(slot[i]) ||
+ (cvalue != read_const(state, ins, slot[i]))) {
+ break;
+ }
}
- if (value != read_const(state, ins, expr)) {
+ if (i == zrhs) {
+ mkconst(state, ins, cvalue);
return;
}
}
- mkconst(state, ins, value);
+
+ /* See if all of rhs members of a phi are the same */
+ value = slot[0];
+ for(i = 1; i < zrhs; i++) {
+ if (slot[i] != value) {
+ break;
+ }
+ }
+ if (i == zrhs) {
+ /* If the phi has a single value just copy it */
+ mkcopy(state, ins, value);
+ return;
+ }
}
{
if (is_const(RHS(ins, 0))) {
ulong_t left;
- left = read_const(state, ins, &RHS(ins, 0));
+ left = read_const(state, ins, RHS(ins, 0));
mkconst(state, ins, bsf(left));
}
}
{
if (is_const(RHS(ins, 0))) {
ulong_t left;
- left = read_const(state, ins, &RHS(ins, 0));
+ left = read_const(state, ins, RHS(ins, 0));
mkconst(state, ins, bsr(left));
}
}
typedef void (*simplify_t)(struct compile_state *state, struct triple *ins);
-static const simplify_t table_simplify[] = {
-#if 0
-#define simplify_smul simplify_noop
-#define simplify_umul simplify_noop
-#define simplify_sdiv simplify_noop
-#define simplify_udiv simplify_noop
-#define simplify_smod simplify_noop
-#define simplify_umod simplify_noop
-#endif
-#if 0
-#define simplify_add simplify_noop
-#define simplify_sub simplify_noop
-#endif
-#if 0
-#define simplify_sl simplify_noop
-#define simplify_usr simplify_noop
-#define simplify_ssr simplify_noop
-#endif
-#if 0
-#define simplify_and simplify_noop
-#define simplify_xor simplify_noop
-#define simplify_or simplify_noop
-#endif
-#if 0
-#define simplify_pos simplify_noop
-#define simplify_neg simplify_noop
-#define simplify_invert simplify_noop
-#endif
-
-#if 0
-#define simplify_eq simplify_noop
-#define simplify_noteq simplify_noop
-#endif
-#if 0
-#define simplify_sless simplify_noop
-#define simplify_uless simplify_noop
-#define simplify_smore simplify_noop
-#define simplify_umore simplify_noop
-#endif
-#if 0
-#define simplify_slesseq simplify_noop
-#define simplify_ulesseq simplify_noop
-#define simplify_smoreeq simplify_noop
-#define simplify_umoreeq simplify_noop
-#endif
-#if 0
-#define simplify_lfalse simplify_noop
-#endif
-#if 0
-#define simplify_ltrue simplify_noop
-#endif
-
-#if 0
-#define simplify_copy simplify_noop
-#endif
-
-#if 0
-#define simplify_branch simplify_noop
-#endif
-
-#if 0
-#define simplify_phi simplify_noop
-#endif
-
-#if 0
-#define simplify_bsf simplify_noop
-#define simplify_bsr simplify_noop
-#endif
-
-[OP_SMUL ] = simplify_smul,
-[OP_UMUL ] = simplify_umul,
-[OP_SDIV ] = simplify_sdiv,
-[OP_UDIV ] = simplify_udiv,
-[OP_SMOD ] = simplify_smod,
-[OP_UMOD ] = simplify_umod,
-[OP_ADD ] = simplify_add,
-[OP_SUB ] = simplify_sub,
-[OP_SL ] = simplify_sl,
-[OP_USR ] = simplify_usr,
-[OP_SSR ] = simplify_ssr,
-[OP_AND ] = simplify_and,
-[OP_XOR ] = simplify_xor,
-[OP_OR ] = simplify_or,
-[OP_POS ] = simplify_pos,
-[OP_NEG ] = simplify_neg,
-[OP_INVERT ] = simplify_invert,
-
-[OP_EQ ] = simplify_eq,
-[OP_NOTEQ ] = simplify_noteq,
-[OP_SLESS ] = simplify_sless,
-[OP_ULESS ] = simplify_uless,
-[OP_SMORE ] = simplify_smore,
-[OP_UMORE ] = simplify_umore,
-[OP_SLESSEQ ] = simplify_slesseq,
-[OP_ULESSEQ ] = simplify_ulesseq,
-[OP_SMOREEQ ] = simplify_smoreeq,
-[OP_UMOREEQ ] = simplify_umoreeq,
-[OP_LFALSE ] = simplify_lfalse,
-[OP_LTRUE ] = simplify_ltrue,
-
-[OP_LOAD ] = simplify_noop,
-[OP_STORE ] = simplify_noop,
-
-[OP_NOOP ] = simplify_noop,
-
-[OP_INTCONST ] = simplify_noop,
-[OP_BLOBCONST ] = simplify_noop,
-[OP_ADDRCONST ] = simplify_noop,
-
-[OP_WRITE ] = simplify_noop,
-[OP_READ ] = simplify_noop,
-[OP_COPY ] = simplify_copy,
-[OP_PIECE ] = simplify_noop,
-[OP_ASM ] = simplify_noop,
-
-[OP_DOT ] = simplify_noop,
-[OP_VAL_VEC ] = simplify_noop,
-
-[OP_LIST ] = simplify_noop,
-[OP_BRANCH ] = simplify_branch,
-[OP_LABEL ] = simplify_noop,
-[OP_ADECL ] = simplify_noop,
-[OP_SDECL ] = simplify_noop,
-[OP_PHI ] = simplify_phi,
-
-[OP_INB ] = simplify_noop,
-[OP_INW ] = simplify_noop,
-[OP_INL ] = simplify_noop,
-[OP_OUTB ] = simplify_noop,
-[OP_OUTW ] = simplify_noop,
-[OP_OUTL ] = simplify_noop,
-[OP_BSF ] = simplify_bsf,
-[OP_BSR ] = simplify_bsr,
-[OP_RDMSR ] = simplify_noop,
-[OP_WRMSR ] = simplify_noop,
-[OP_HLT ] = simplify_noop,
+static const struct simplify_table {
+ simplify_t func;
+ unsigned long flag;
+} table_simplify[] = {
+#define simplify_sdivt simplify_noop
+#define simplify_udivt simplify_noop
+#define simplify_piece simplify_noop
+
+[OP_SDIVT ] = { simplify_sdivt, COMPILER_SIMPLIFY_ARITH },
+[OP_UDIVT ] = { simplify_udivt, COMPILER_SIMPLIFY_ARITH },
+[OP_SMUL ] = { simplify_smul, COMPILER_SIMPLIFY_ARITH },
+[OP_UMUL ] = { simplify_umul, COMPILER_SIMPLIFY_ARITH },
+[OP_SDIV ] = { simplify_sdiv, COMPILER_SIMPLIFY_ARITH },
+[OP_UDIV ] = { simplify_udiv, COMPILER_SIMPLIFY_ARITH },
+[OP_SMOD ] = { simplify_smod, COMPILER_SIMPLIFY_ARITH },
+[OP_UMOD ] = { simplify_umod, COMPILER_SIMPLIFY_ARITH },
+[OP_ADD ] = { simplify_add, COMPILER_SIMPLIFY_ARITH },
+[OP_SUB ] = { simplify_sub, COMPILER_SIMPLIFY_ARITH },
+[OP_SL ] = { simplify_sl, COMPILER_SIMPLIFY_SHIFT },
+[OP_USR ] = { simplify_usr, COMPILER_SIMPLIFY_SHIFT },
+[OP_SSR ] = { simplify_ssr, COMPILER_SIMPLIFY_SHIFT },
+[OP_AND ] = { simplify_and, COMPILER_SIMPLIFY_BITWISE },
+[OP_XOR ] = { simplify_xor, COMPILER_SIMPLIFY_BITWISE },
+[OP_OR ] = { simplify_or, COMPILER_SIMPLIFY_BITWISE },
+[OP_POS ] = { simplify_pos, COMPILER_SIMPLIFY_ARITH },
+[OP_NEG ] = { simplify_neg, COMPILER_SIMPLIFY_ARITH },
+[OP_INVERT ] = { simplify_invert, COMPILER_SIMPLIFY_BITWISE },
+
+[OP_EQ ] = { simplify_eq, COMPILER_SIMPLIFY_LOGICAL },
+[OP_NOTEQ ] = { simplify_noteq, COMPILER_SIMPLIFY_LOGICAL },
+[OP_SLESS ] = { simplify_sless, COMPILER_SIMPLIFY_LOGICAL },
+[OP_ULESS ] = { simplify_uless, COMPILER_SIMPLIFY_LOGICAL },
+[OP_SMORE ] = { simplify_smore, COMPILER_SIMPLIFY_LOGICAL },
+[OP_UMORE ] = { simplify_umore, COMPILER_SIMPLIFY_LOGICAL },
+[OP_SLESSEQ ] = { simplify_slesseq, COMPILER_SIMPLIFY_LOGICAL },
+[OP_ULESSEQ ] = { simplify_ulesseq, COMPILER_SIMPLIFY_LOGICAL },
+[OP_SMOREEQ ] = { simplify_smoreeq, COMPILER_SIMPLIFY_LOGICAL },
+[OP_UMOREEQ ] = { simplify_umoreeq, COMPILER_SIMPLIFY_LOGICAL },
+[OP_LFALSE ] = { simplify_lfalse, COMPILER_SIMPLIFY_LOGICAL },
+[OP_LTRUE ] = { simplify_ltrue, COMPILER_SIMPLIFY_LOGICAL },
+
+[OP_LOAD ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_STORE ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+
+[OP_NOOP ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+
+[OP_INTCONST ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_BLOBCONST ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_ADDRCONST ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+
+[OP_WRITE ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_READ ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_COPY ] = { simplify_copy, COMPILER_SIMPLIFY_COPY },
+[OP_PIECE ] = { simplify_piece, COMPILER_SIMPLIFY_OP },
+[OP_ASM ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+
+[OP_DOT ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_VAL_VEC ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+
+[OP_LIST ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_BRANCH ] = { simplify_branch, COMPILER_SIMPLIFY_BRANCH },
+[OP_CBRANCH ] = { simplify_branch, COMPILER_SIMPLIFY_BRANCH },
+[OP_CALL ] = { simplify_noop, COMPILER_SIMPLIFY_BRANCH },
+[OP_RET ] = { simplify_noop, COMPILER_SIMPLIFY_BRANCH },
+[OP_LABEL ] = { simplify_label, COMPILER_SIMPLIFY_LABEL },
+[OP_ADECL ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_SDECL ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_PHI ] = { simplify_phi, COMPILER_SIMPLIFY_PHI },
+
+[OP_INB ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_INW ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_INL ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_OUTB ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_OUTW ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_OUTL ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_BSF ] = { simplify_bsf, COMPILER_SIMPLIFY_OP },
+[OP_BSR ] = { simplify_bsr, COMPILER_SIMPLIFY_OP },
+[OP_RDMSR ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_WRMSR ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
+[OP_HLT ] = { simplify_noop, COMPILER_SIMPLIFY_OP },
};
static void simplify(struct compile_state *state, struct triple *ins)
if ((op < 0) || (op > sizeof(table_simplify)/sizeof(table_simplify[0]))) {
do_simplify = 0;
}
+ else if (state->compiler->flags & table_simplify[op].flag) {
+ do_simplify = table_simplify[op].func;
+ }
else {
- do_simplify = table_simplify[op];
+ do_simplify = simplify_noop;
}
+
if (!do_simplify) {
internal_error(state, ins, "cannot simplify op: %d %s\n",
op, tops(op));
} while(ins->op != op);
}
+static void rebuild_ssa_form(struct compile_state *state);
+
static void simplify_all(struct compile_state *state)
{
struct triple *ins, *first;
- first = RHS(state->main_function, 0);
+ if (!(state->compiler->flags & COMPILER_SIMPLIFY)) {
+ return;
+ }
+ first = state->first;
+ ins = first->prev;
+ do {
+ simplify(state, ins);
+ ins = ins->prev;
+ } while(ins != first->prev);
ins = first;
do {
simplify(state, ins);
ins = ins->next;
- } while(ins != first);
+ }while(ins != first);
+ rebuild_ssa_form(state);
+
+ print_blocks(state, __func__, stdout);
}
/*
const char *name, int op, struct type *rtype, ...)
{
struct type *ftype, *atype, *param, **next;
- struct triple *def, *arg, *result, *work, *last, *first;
+ struct triple *def, *arg, *result, *work, *last, *first, *retvar, *ret;
struct hash_entry *ident;
struct file_state file;
int parameters;
}
/* Find the function type */
- ftype = new_type(TYPE_FUNCTION, rtype, 0);
+ ftype = new_type(TYPE_FUNCTION | STOR_INLINE | STOR_STATIC, rtype, 0);
next = &ftype->right;
va_start(args, rtype);
for(i = 0; i < parameters; i++) {
def = triple(state, OP_LIST, ftype, 0, 0);
first = label(state);
RHS(def, 0) = first;
+ retvar = variable(state, &void_ptr_type);
+ retvar = flatten(state, first, retvar);
+ ret = triple(state, OP_RET, &void_type, read_expr(state, retvar), 0);
/* Now string them together */
param = ftype->right;
}
MISC(def, 0) = result;
work = new_triple(state, op, rtype, -1, parameters);
- for(i = 0, arg = first->next; i < parameters; i++, arg = arg->next) {
+ for(i = 0, arg = first->next->next; i < parameters; i++, arg = arg->next) {
RHS(work, i) = read_expr(state, arg);
}
if (result && ((rtype->type & TYPE_MASK) == TYPE_STRUCT)) {
}
work = flatten(state, first, work);
last = flatten(state, first, label(state));
+ ret = flatten(state, first, ret);
name_len = strlen(name);
ident = lookup(state, name, name_len);
+ ftype->type_ident = ident;
symbol(state, ident, &ident->sym_ident, def, ftype);
state->file = file.prev;
state->function = 0;
-#if 0
- fprintf(stdout, "\n");
- loc(stdout, state, 0);
- fprintf(stdout, "\n__________ builtin_function _________\n");
- print_triple(state, def);
- fprintf(stdout, "__________ builtin_function _________ done\n\n");
-#endif
+
+ if (!state->functions) {
+ state->functions = def;
+ } else {
+ insert_triple(state, state->functions, def);
+ }
+ if (state->compiler->debug & DEBUG_INLINE) {
+ fprintf(stdout, "\n");
+ loc(stdout, state, 0);
+ fprintf(stdout, "\n__________ %s _________\n", __FUNCTION__);
+ display_func(stdout, def);
+ fprintf(stdout, "__________ %s _________ done\n\n", __FUNCTION__);
+ }
}
static struct type *partial_struct(struct compile_state *state,
field = field->right;
}
elements++;
- symbol(state, ident, &ident->sym_struct, 0, type);
+ symbol(state, ident, &ident->sym_tag, 0, type);
type->type_ident = ident;
type->elements = elements;
}
static void register_builtins(struct compile_state *state)
{
+ struct type *div_type, *ldiv_type;
+ struct type *udiv_type, *uldiv_type;
struct type *msr_type;
+ div_type = register_builtin_type(state, "__builtin_div_t",
+ partial_struct(state, "quot", &int_type,
+ partial_struct(state, "rem", &int_type, 0)));
+ ldiv_type = register_builtin_type(state, "__builtin_ldiv_t",
+ partial_struct(state, "quot", &long_type,
+ partial_struct(state, "rem", &long_type, 0)));
+ udiv_type = register_builtin_type(state, "__builtin_udiv_t",
+ partial_struct(state, "quot", &uint_type,
+ partial_struct(state, "rem", &uint_type, 0)));
+ uldiv_type = register_builtin_type(state, "__builtin_uldiv_t",
+ partial_struct(state, "quot", &ulong_type,
+ partial_struct(state, "rem", &ulong_type, 0)));
+
+ register_builtin_function(state, "__builtin_div", OP_SDIVT, div_type,
+ &int_type, &int_type);
+ register_builtin_function(state, "__builtin_ldiv", OP_SDIVT, ldiv_type,
+ &long_type, &long_type);
+ register_builtin_function(state, "__builtin_udiv", OP_UDIVT, udiv_type,
+ &uint_type, &uint_type);
+ register_builtin_function(state, "__builtin_uldiv", OP_UDIVT, uldiv_type,
+ &ulong_type, &ulong_type);
+
register_builtin_function(state, "__builtin_inb", OP_INB, &uchar_type,
&ushort_type);
register_builtin_function(state, "__builtin_inw", OP_INW, &ushort_type,
eat(state, TOK_LPAREN);
/* Find the return type without any specifiers */
type = clone_type(0, func->type->left);
- def = new_triple(state, OP_CALL, func->type, -1, -1);
- def->type = type;
-
- pvals = TRIPLE_RHS(def->sizes);
+ /* Count the number of rhs entries for OP_FCALL */
+ param = func->type->right;
+ pvals = 0;
+ while((param->type & TYPE_MASK) == TYPE_PRODUCT) {
+ pvals++;
+ param = param->right;
+ }
+ if ((param->type & TYPE_MASK) != TYPE_VOID) {
+ pvals++;
+ }
+ def = new_triple(state, OP_FCALL, type, -1, pvals);
MISC(def, 0) = func;
param = func->type->right;
errno = 0;
decimal = (tk->val.str[0] != '0');
val = strtoul(tk->val.str, &end, 0);
- if ((val == ULONG_MAX) && (errno == ERANGE)) {
+ if ((val > ULONG_T_MAX) || ((val == ULONG_MAX) && (errno == ERANGE))) {
error(state, 0, "Integer constant to large");
}
u = l = 0;
}
else if (l) {
type = &long_type;
- if (!decimal && (val > LONG_MAX)) {
+ if (!decimal && (val > LONG_T_MAX)) {
type = &ulong_type;
}
}
else if (u) {
type = &uint_type;
- if (val > UINT_MAX) {
+ if (val > UINT_T_MAX) {
type = &ulong_type;
}
}
else {
type = &int_type;
- if (!decimal && (val > INT_MAX) && (val <= UINT_MAX)) {
+ if (!decimal && (val > INT_T_MAX) && (val <= UINT_T_MAX)) {
type = &uint_type;
}
- else if (!decimal && (val > LONG_MAX)) {
+ else if (!decimal && (val > LONG_T_MAX)) {
type = &ulong_type;
}
- else if (val > INT_MAX) {
+ else if (val > INT_T_MAX) {
type = &long_type;
}
}
/* Here ident is either:
* a varable name
* a function name
- * an enumeration constant.
*/
eat(state, TOK_IDENT);
ident = state->token[0].ident;
break;
}
case TOK_ENUM_CONST:
+ {
+ struct hash_entry *ident;
/* Here ident is an enumeration constant */
eat(state, TOK_ENUM_CONST);
- def = 0;
- FINISHME();
+ ident = state->token[0].ident;
+ if (!ident->sym_ident) {
+ error(state, 0, "%s undeclared", ident->name);
+ }
+ def = ident->sym_ident->def;
break;
+ }
case TOK_LPAREN:
eat(state, TOK_LPAREN);
def = expr(state);
eat(state, TOK_LPAREN);
type = type_name(state);
eat(state, TOK_RPAREN);
- def = read_expr(state, cast_expr(state));
- def = triple(state, OP_COPY, type, def, 0);
+ def = mk_cast_expr(state, type, cast_expr(state));
}
else {
def = unary_expr(state);
/* Find the return variable */
var = MISC(state->main_function, 0);
/* Find the return destination */
- dest = RHS(state->main_function, 0)->prev;
+ dest = state->i_return->sym_ident->def;
mv = jmp = 0;
/* If needed generate a jump instruction */
if (!last) {
static void switch_statement(struct compile_state *state, struct triple *first)
{
- FINISHME();
+ struct triple *value, *top, *end, *dbranch;
+ struct hash_entry *ident;
+
+ /* See if we have a valid switch statement */
eat(state, TOK_SWITCH);
eat(state, TOK_LPAREN);
- expr(state);
+ value = expr(state);
+ integral(state, value);
+ value = read_expr(state, value);
eat(state, TOK_RPAREN);
+ /* Generate the needed pieces */
+ top = label(state);
+ end = label(state);
+ dbranch = branch(state, end, 0);
+ /* Remember where case branches and break goes */
+ start_scope(state);
+ ident = state->i_switch;
+ symbol(state, ident, &ident->sym_ident, value, value->type);
+ ident = state->i_case;
+ symbol(state, ident, &ident->sym_ident, top, top->type);
+ ident = state->i_break;
+ symbol(state, ident, &ident->sym_ident, end, end->type);
+ ident = state->i_default;
+ symbol(state, ident, &ident->sym_ident, dbranch, dbranch->type);
+ /* Thread them together */
+ flatten(state, first, value);
+ flatten(state, first, top);
+ flatten(state, first, dbranch);
statement(state, first);
- error(state, 0, "switch statements are not implemented");
- FINISHME();
+ flatten(state, first, end);
+ /* Cleanup the switch scope */
+ end_scope(state);
}
static void case_statement(struct compile_state *state, struct triple *first)
{
- FINISHME();
+ struct triple *cvalue, *dest, *test, *jmp;
+ struct triple *ptr, *value, *top, *dbranch;
+
+ /* See if w have a valid case statement */
eat(state, TOK_CASE);
- constant_expr(state);
+ cvalue = constant_expr(state);
+ integral(state, cvalue);
+ if (cvalue->op != OP_INTCONST) {
+ error(state, 0, "integer constant expected");
+ }
eat(state, TOK_COLON);
+ if (!state->i_case->sym_ident) {
+ error(state, 0, "case statement not within a switch");
+ }
+
+ /* Lookup the interesting pieces */
+ top = state->i_case->sym_ident->def;
+ value = state->i_switch->sym_ident->def;
+ dbranch = state->i_default->sym_ident->def;
+
+ /* See if this case label has already been used */
+ for(ptr = top; ptr != dbranch; ptr = ptr->next) {
+ if (ptr->op != OP_EQ) {
+ continue;
+ }
+ if (RHS(ptr, 1)->u.cval == cvalue->u.cval) {
+ error(state, 0, "duplicate case %d statement",
+ cvalue->u.cval);
+ }
+ }
+ /* Generate the needed pieces */
+ dest = label(state);
+ test = triple(state, OP_EQ, &int_type, value, cvalue);
+ jmp = branch(state, dest, test);
+ /* Thread the pieces together */
+ flatten(state, dbranch, test);
+ flatten(state, dbranch, jmp);
+ flatten(state, dbranch, label(state));
+ flatten(state, first, dest);
statement(state, first);
- error(state, 0, "case statements are not implemented");
- FINISHME();
}
static void default_statement(struct compile_state *state, struct triple *first)
{
- FINISHME();
+ struct triple *dest;
+ struct triple *dbranch, *end;
+
+ /* See if we have a valid default statement */
eat(state, TOK_DEFAULT);
eat(state, TOK_COLON);
+
+ if (!state->i_case->sym_ident) {
+ error(state, 0, "default statement not within a switch");
+ }
+
+ /* Lookup the interesting pieces */
+ dbranch = state->i_default->sym_ident->def;
+ end = state->i_break->sym_ident->def;
+
+ /* See if a default statement has already happened */
+ if (TARG(dbranch, 0) != end) {
+ error(state, 0, "duplicate default statement");
+ }
+
+ /* Generate the needed pieces */
+ dest = label(state);
+
+ /* Thread the pieces together */
+ TARG(dbranch, 0) = dest;
+ flatten(state, first, dest);
statement(state, first);
- error(state, 0, "default statements are not implemented");
- FINISHME();
}
static void asm_statement(struct compile_state *state, struct triple *first)
error(state, 0, "Maximum clobber limit exceeded.");
}
clobber = string_constant(state);
- eat(state, TOK_RPAREN);
clob_param[clobbers].constraint = clobber;
if (peek(state) == TOK_COMMA) {
RHS(def, i) = read_expr(state,in_param[i].expr);
}
flatten(state, first, def);
- for(i = 0; i < out; i++) {
+ for(i = 0; i < (out + clobbers); i++) {
+ struct type *type;
struct triple *piece;
- piece = triple(state, OP_PIECE, out_param[i].expr->type, def, 0);
+ type = (i < out)? out_param[i].expr->type : &void_type;
+ piece = triple(state, OP_PIECE, type, def, 0);
piece->u.cval = i;
LHS(def, i) = piece;
- flatten(state, first,
- write_expr(state, out_param[i].expr, piece));
+ flatten(state, first, piece);
}
- for(; i - out < clobbers; i++) {
+ /* And write the helpers to their destinations */
+ for(i = 0; i < out; i++) {
struct triple *piece;
- piece = triple(state, OP_PIECE, &void_type, def, 0);
- piece->u.cval = i;
- LHS(def, i) = piece;
- flatten(state, first, piece);
+ piece = LHS(def, i);
+ flatten(state, first,
+ write_expr(state, out_param[i].expr, piece));
}
}
static struct type *param_type_list(struct compile_state *state, struct type *type)
{
struct type *ftype, **next;
- ftype = new_type(TYPE_FUNCTION, type, param_decl(state));
+ ftype = new_type(TYPE_FUNCTION | (type->type & STOR_MASK), type, param_decl(state));
next = &ftype->right;
while(peek(state) == TOK_COMMA) {
eat(state, TOK_COMMA);
}
static struct type *enum_specifier(
- struct compile_state *state, unsigned int specifiers)
+ struct compile_state *state, unsigned int spec)
{
+ struct hash_entry *ident;
+ ulong_t base;
int tok;
- struct type *type;
- type = 0;
- FINISHME();
+ struct type *enum_type;
+ enum_type = 0;
+ ident = 0;
eat(state, TOK_ENUM);
tok = peek(state);
- if (tok == TOK_IDENT) {
- eat(state, TOK_IDENT);
+ if ((tok == TOK_IDENT) || (tok == TOK_ENUM_CONST) || (tok == TOK_TYPE_NAME)) {
+ eat(state, tok);
+ ident = state->token[0].ident;
+
}
- if ((tok != TOK_IDENT) || (peek(state) == TOK_LBRACE)) {
+ base = 0;
+ if (!ident || (peek(state) == TOK_LBRACE)) {
+ struct type **next;
eat(state, TOK_LBRACE);
+ enum_type = new_type(TYPE_ENUM | spec, 0, 0);
+ enum_type->type_ident = ident;
+ next = &enum_type->right;
do {
+ struct hash_entry *eident;
+ struct triple *value;
+ struct type *entry;
eat(state, TOK_IDENT);
+ eident = state->token[0].ident;
+ if (eident->sym_ident) {
+ error(state, 0, "%s already declared",
+ eident->name);
+ }
+ eident->tok = TOK_ENUM_CONST;
if (peek(state) == TOK_EQ) {
+ struct triple *val;
eat(state, TOK_EQ);
- constant_expr(state);
- }
+ val = constant_expr(state);
+ integral(state, val);
+ base = val->u.cval;
+ }
+ value = int_const(state, &int_type, base);
+ symbol(state, eident, &eident->sym_ident, value, &int_type);
+ entry = new_type(TYPE_LIST, 0, 0);
+ entry->field_ident = eident;
+ *next = entry;
+ next = &entry->right;
+ base += 1;
if (peek(state) == TOK_COMMA) {
eat(state, TOK_COMMA);
}
} while(peek(state) != TOK_RBRACE);
eat(state, TOK_RBRACE);
+ if (ident) {
+ symbol(state, ident, &ident->sym_tag, 0, enum_type);
+ }
}
- FINISHME();
- return type;
+ if (ident && ident->sym_tag &&
+ ident->sym_tag->type &&
+ ((ident->sym_tag->type->type & TYPE_MASK) == TYPE_ENUM)) {
+ enum_type = clone_type(spec, ident->sym_tag->type);
+ }
+ else if (ident && !enum_type) {
+ error(state, 0, "enum %s undeclared", ident->name);
+ }
+ return enum_type;
}
-#if 0
static struct type *struct_declarator(
struct compile_state *state, struct type *type, struct hash_entry **ident)
{
int tok;
-#warning "struct_declarator is complicated because of bitfields, kill them?"
tok = peek(state);
if (tok != TOK_COLON) {
type = declarator(state, type, ident, 1);
}
if ((tok == TOK_COLON) || (peek(state) == TOK_COLON)) {
+ struct triple *value;
eat(state, TOK_COLON);
- constant_expr(state);
+ value = constant_expr(state);
+#warning "FIXME implement bitfields to reduce register usage"
+ error(state, 0, "bitfields not yet implemented");
}
- FINISHME();
return type;
}
-#endif
static struct type *struct_or_union_specifier(
struct compile_state *state, unsigned int spec)
break;
}
tok = peek(state);
- if ((tok == TOK_IDENT) || (tok == TOK_TYPE_NAME)) {
+ if ((tok == TOK_IDENT) || (tok == TOK_ENUM_CONST) || (tok == TOK_TYPE_NAME)) {
eat(state, tok);
ident = state->token[0].ident;
}
struct type *type;
struct hash_entry *fident;
done = 1;
- type = declarator(state, base_type, &fident, 1);
+ type = struct_declarator(state, base_type, &fident);
elements++;
if (peek(state) == TOK_COMMA) {
done = 0;
struct_type = new_type(TYPE_STRUCT | spec, struct_type, 0);
struct_type->type_ident = ident;
struct_type->elements = elements;
- symbol(state, ident, &ident->sym_struct, 0, struct_type);
+ if (ident) {
+ symbol(state, ident, &ident->sym_tag, 0, struct_type);
+ }
}
- if (ident && ident->sym_struct) {
- struct_type = clone_type(spec, ident->sym_struct->type);
+ if (ident && ident->sym_tag &&
+ ident->sym_tag->type &&
+ ((ident->sym_tag->type->type & TYPE_MASK) == TYPE_STRUCT)) {
+ struct_type = clone_type(spec, ident->sym_tag->type);
}
- else if (ident && !ident->sym_struct) {
+ else if (ident && !struct_type) {
error(state, 0, "struct %s undeclared", ident->name);
}
return struct_type;
break;
default:
if (state->scope_depth <= GLOBAL_SCOPE_DEPTH) {
- specifiers = STOR_STATIC;
+ specifiers = STOR_LOCAL;
}
else {
specifiers = STOR_AUTO;
struct compile_state *state, struct type *type)
{
struct triple *result;
+#warning "FIXME more consistent initializer handling (where should eval_const_expr go?"
if (peek(state) != TOK_LBRACE) {
result = assignment_expr(state);
+ if (((type->type & TYPE_MASK) == TYPE_ARRAY) &&
+ (type->elements == ELEMENT_COUNT_UNSPECIFIED) &&
+ ((result->type->type & TYPE_MASK) == TYPE_ARRAY) &&
+ (result->type->elements != ELEMENT_COUNT_UNSPECIFIED) &&
+ (equiv_types(type->left, result->type->left))) {
+ type->elements = result->type->elements;
+ }
+ if (is_stable(state, result) &&
+ ((result->type->type & TYPE_MASK) == TYPE_ARRAY) &&
+ (type->type & TYPE_MASK) != TYPE_ARRAY)
+ {
+ result = array_to_pointer(state, result);
+ }
+ if (!is_init_compatible(state, type, result->type)) {
+ error(state, 0, "Incompatible types in initializer");
+ }
+ if (!equiv_types(type, result->type)) {
+ result = mk_cast_expr(state, type, result);
+ }
}
else {
int comma;
static struct triple *function_definition(
struct compile_state *state, struct type *type)
{
- struct triple *def, *tmp, *first, *end;
+ struct triple *def, *tmp, *first, *end, *retvar, *ret;
struct hash_entry *ident;
struct type *param;
int i;
/* Put a label at the very end of a function */
end = label(state);
flatten(state, first, end);
+ /* Remember where return goes */
+ ident = state->i_return;
+ symbol(state, ident, &ident->sym_ident, end, end->type);
+
+ /* Allocate a variable for the return address */
+ retvar = variable(state, &void_ptr_type);
+ retvar = flatten(state, end, retvar);
+
+ /* Add in the return instruction */
+ ret = triple(state, OP_RET, &void_type, read_expr(state, retvar), 0);
+ ret = flatten(state, first, ret);
/* Walk through the parameters and create symbol table entries
* for them.
/* Remove the parameter scope */
end_scope(state);
-#if 0
- fprintf(stdout, "\n");
- loc(stdout, state, 0);
- fprintf(stdout, "\n__________ function_definition _________\n");
- print_triple(state, def);
- fprintf(stdout, "__________ function_definition _________ done\n\n");
-#endif
- return def;
-}
+ /* Remember I have defined a function */
+ if (!state->functions) {
+ state->functions = def;
+ } else {
+ insert_triple(state, state->functions, def);
+ }
+ if (state->compiler->debug & DEBUG_INLINE) {
+ fprintf(stdout, "\n");
+ loc(stdout, state, 0);
+ fprintf(stdout, "\n__________ %s _________\n", __FUNCTION__);
+ display_func(stdout, def);
+ fprintf(stdout, "__________ %s _________ done\n\n", __FUNCTION__);
+ }
+
+ return def;
+}
static struct triple *do_decl(struct compile_state *state,
struct type *type, struct hash_entry *ident)
type->type &= ~STOR_MASK;
type->type |= STOR_AUTO;
break;
+ case STOR_LOCAL:
case STOR_EXTERN:
type->type &= ~STOR_MASK;
type->type |= STOR_STATIC;
type = declarator(state, base_type, &ident, 0);
if (global && ident && (peek(state) == TOK_LBRACE)) {
/* function */
+ type->type_ident = ident;
state->function = ident->name;
def = function_definition(state, type);
symbol(state, ident, &ident->sym_ident, def, type);
}
}
+/*
+ * Function inlining
+ */
+
+static struct triple *call(struct compile_state *state,
+ struct triple *retvar, struct triple *ret_addr,
+ struct triple *targ, struct triple *ret)
+{
+ struct triple *call;
+
+ if (!retvar || !is_lvalue(state, retvar)) {
+ internal_error(state, 0, "writing to a non lvalue?");
+ }
+ write_compatible(state, retvar->type, &void_ptr_type);
+
+ call = new_triple(state, OP_CALL, &void_type, 1, 0);
+ TARG(call, 0) = targ;
+ MISC(call, 0) = ret;
+ if (!targ || (targ->op != OP_LABEL)) {
+ internal_error(state, 0, "call not to a label");
+ }
+ if (!ret || (ret->op != OP_RET)) {
+ internal_error(state, 0, "call not matched with return");
+ }
+ return call;
+}
+
+static void mark_live_functions(struct compile_state *state, struct triple *first)
+{
+ struct triple *ptr;
+ ptr = first;
+ do {
+ if (ptr->op == OP_FCALL) {
+ struct triple *func;
+ func = MISC(ptr, 0);
+ if (func->u.cval++ == 0) {
+ mark_live_functions(state, RHS(func, 0));
+ }
+ }
+ ptr = ptr->next;
+ } while(ptr != first);
+}
+
+static void walk_functions(struct compile_state *state,
+ void (*cb)(struct compile_state *state, struct triple *func, void *arg),
+ void *arg)
+{
+ struct triple *func, *first;
+ func = first = state->functions;
+ do {
+ cb(state, func, arg);
+ func = func->next;
+ } while(func != first);
+}
+
+
+static int local_triple(struct compile_state *state,
+ struct triple *func, struct triple *ins)
+{
+ int local = (ins->id & TRIPLE_FLAG_LOCAL);
+#if 0
+ if (!local) {
+ fprintf(stderr, "global: ");
+ display_triple(stderr, ins);
+ }
+#endif
+ return local;
+}
+
+struct triple *copy_func(struct compile_state *state, struct triple *ofunc,
+ struct occurance *base_occurance)
+{
+ struct triple *nfunc;
+ struct triple *nfirst, *ofirst;
+ struct triple *new, *old;
+
+ if (state->compiler->debug & DEBUG_INLINE) {
+ fprintf(stdout, "\n");
+ loc(stdout, state, 0);
+ fprintf(stdout, "\n__________ %s _________\n", __FUNCTION__);
+ display_func(stdout, ofunc);
+ fprintf(stdout, "__________ %s _________ done\n\n", __FUNCTION__);
+ }
+
+ /* Make a new copy of the old function */
+ nfunc = triple(state, OP_LIST, ofunc->type, 0, 0);
+ nfirst = 0;
+ ofirst = old = RHS(ofunc, 0);
+ do {
+ struct triple *new;
+ struct occurance *occurance;
+ int old_lhs, old_rhs;
+ old_lhs = TRIPLE_LHS(old->sizes);
+ old_rhs = TRIPLE_RHS(old->sizes);
+ occurance = inline_occurance(state, base_occurance, old->occurance);
+ if (ofunc->u.cval && (old->op == OP_FCALL)) {
+ MISC(old, 0)->u.cval += 1;
+ }
+ new = alloc_triple(state, old->op, old->type, old_lhs, old_rhs,
+ occurance);
+ if (!triple_stores_block(state, new)) {
+ memcpy(&new->u, &old->u, sizeof(new->u));
+ }
+ if (!nfirst) {
+ RHS(nfunc, 0) = nfirst = new;
+ }
+ else {
+ insert_triple(state, nfirst, new);
+ }
+ new->id |= TRIPLE_FLAG_FLATTENED;
+
+ /* During the copy remember new as user of old */
+ use_triple(old, new);
+
+ /* Populate the return type if present */
+ if (old == MISC(ofunc, 0)) {
+ MISC(nfunc, 0) = new;
+ }
+ /* Remember which instructions are local */
+ old->id |= TRIPLE_FLAG_LOCAL;
+ old = old->next;
+ } while(old != ofirst);
+
+ /* Make a second pass to fix up any unresolved references */
+ old = ofirst;
+ new = nfirst;
+ do {
+ struct triple **oexpr, **nexpr;
+ int count, i;
+ /* Lookup where the copy is, to join pointers */
+ count = TRIPLE_SIZE(old->sizes);
+ for(i = 0; i < count; i++) {
+ oexpr = &old->param[i];
+ nexpr = &new->param[i];
+ if (*oexpr && !*nexpr) {
+ if (!local_triple(state, ofunc, *oexpr)) {
+ *nexpr = *oexpr;
+ }
+ else if ((*oexpr)->use) {
+ *nexpr = (*oexpr)->use->member;
+ }
+ if (*nexpr == old) {
+ internal_error(state, 0, "new == old?");
+ }
+ use_triple(*nexpr, new);
+ }
+ if (!*nexpr && *oexpr) {
+ internal_error(state, 0, "Could not copy %d\n", i);
+ }
+ }
+ old = old->next;
+ new = new->next;
+ } while((old != ofirst) && (new != nfirst));
+
+ /* Make a third pass to cleanup the extra useses */
+ old = ofirst;
+ new = nfirst;
+ do {
+ unuse_triple(old, new);
+ /* Forget which instructions are local */
+ old->id &= ~TRIPLE_FLAG_LOCAL;
+ old = old->next;
+ new = new->next;
+ } while ((old != ofirst) && (new != nfirst));
+ return nfunc;
+}
+
+static struct triple *flatten_inline_call(
+ struct compile_state *state, struct triple *first, struct triple *ptr)
+{
+ /* Inline the function call */
+ struct type *ptype;
+ struct triple *ofunc, *nfunc, *nfirst, *param, *result;
+ struct triple *end, *nend;
+ int pvals, i;
+
+ /* Find the triples */
+ ofunc = MISC(ptr, 0);
+ if (ofunc->op != OP_LIST) {
+ internal_error(state, 0, "improper function");
+ }
+ nfunc = copy_func(state, ofunc, ptr->occurance);
+ nfirst = RHS(nfunc, 0)->next->next;
+ /* Prepend the parameter reading into the new function list */
+ ptype = nfunc->type->right;
+ param = RHS(nfunc, 0)->next->next;
+ pvals = TRIPLE_RHS(ptr->sizes);
+ for(i = 0; i < pvals; i++) {
+ struct type *atype;
+ struct triple *arg;
+ atype = ptype;
+ if ((ptype->type & TYPE_MASK) == TYPE_PRODUCT) {
+ atype = ptype->left;
+ }
+ while((param->type->type & TYPE_MASK) != (atype->type & TYPE_MASK)) {
+ param = param->next;
+ }
+ arg = RHS(ptr, i);
+ flatten(state, nfirst, write_expr(state, param, arg));
+ ptype = ptype->right;
+ param = param->next;
+ }
+ result = 0;
+ if ((nfunc->type->left->type & TYPE_MASK) != TYPE_VOID) {
+ result = read_expr(state, MISC(nfunc,0));
+ }
+ if (state->compiler->debug & DEBUG_INLINE) {
+ fprintf(stdout, "\n");
+ loc(stdout, state, 0);
+ fprintf(stdout, "\n__________ %s _________\n", __FUNCTION__);
+ display_func(stdout, nfunc);
+ fprintf(stdout, "__________ %s _________ done\n\n", __FUNCTION__);
+ }
+
+ /* Get rid of the extra triples */
+ nfirst = RHS(nfunc, 0)->next->next;
+ release_triple(state, RHS(nfunc, 0)->prev->prev);
+ release_triple(state, RHS(nfunc, 0)->prev);
+ release_triple(state, RHS(nfunc, 0)->next);
+ free_triple(state, RHS(nfunc, 0));
+ RHS(nfunc, 0) = 0;
+ free_triple(state, nfunc);
+
+ /* Append the new function list onto the return list */
+ end = first->prev;
+ nend = nfirst->prev;
+ end->next = nfirst;
+ nfirst->prev = end;
+ nend->next = first;
+ first->prev = nend;
+
+ return result;
+}
+
+static struct triple *flatten_function_call(
+ struct compile_state *state, struct triple *first, struct triple *ptr)
+{
+ /* Generate an ordinary function call */
+ struct triple *func, *func_first, *func_last, *retvar;
+ struct type *ptype;
+ struct triple *param;
+ struct triple *jmp;
+ struct triple *ret_addr, *ret_loc, *ret_set;
+ struct triple *result;
+ int pvals, i;
+
+ FINISHME();
+ /* Find the triples */
+ func = MISC(ptr, 0);
+ func_first = RHS(func, 0);
+ retvar = func_first->next;
+ func_last = func_first->prev;
+
+ /* Generate some needed triples */
+ ret_loc = label(state);
+ ret_addr = triple(state, OP_ADDRCONST, &void_ptr_type, ret_loc, 0);
+
+ /* Pass the parameters to the new function */
+ ptype = func->type->right;
+ param = func_first->next->next;
+ pvals = TRIPLE_RHS(ptr->sizes);
+ for(i = 0; i < pvals; i++) {
+ struct type *atype;
+ struct triple *arg;
+ atype = ptype;
+ if ((ptype->type & TYPE_MASK) == TYPE_PRODUCT) {
+ atype = ptype->left;
+ }
+ while((param->type->type & TYPE_MASK) != (atype->type & TYPE_MASK)) {
+ param = param->next;
+ }
+ arg = RHS(ptr, i);
+ flatten(state, first, write_expr(state, param, arg));
+ ptype = ptype->right;
+ param = param->next;
+ }
+
+ /* Thread the triples together */
+ ret_loc = flatten(state, first, ret_loc);
+ ret_addr = flatten(state, ret_loc, ret_addr);
+ ret_set = flatten(state, ret_loc, write_expr(state, retvar, ret_addr));
+ jmp = flatten(state, ret_loc,
+ call(state, retvar, ret_addr, func_first, func_last));
+
+ /* Find the result */
+ result = 0;
+ if ((func->type->left->type & TYPE_MASK) != TYPE_VOID) {
+ result = read_expr(state, MISC(func, 0));
+ }
+
+ if (state->compiler->debug & DEBUG_INLINE) {
+ fprintf(stdout, "\n");
+ loc(stdout, state, 0);
+ fprintf(stdout, "\n__________ %s _________\n", __FUNCTION__);
+ display_func(stdout, func);
+ fprintf(stdout, "__________ %s _________ done\n\n", __FUNCTION__);
+ }
+
+ return result;
+}
+
+static void inline_functions(struct compile_state *state, struct triple *first)
+{
+ struct triple *ptr, *next;
+ ptr = next = first;
+ do {
+ int do_inline;
+ struct triple *func, *prev, *new;
+ ptr = next;
+ prev = ptr->prev;
+ next = ptr->next;
+ if (ptr->op != OP_FCALL) {
+ continue;
+ }
+ func = MISC(ptr, 0);
+ /* See if the function should be inlined */
+ switch(func->type->type & STOR_MASK) {
+ case STOR_STATIC | STOR_INLINE:
+ case STOR_LOCAL | STOR_INLINE:
+ case STOR_EXTERN | STOR_INLINE:
+ do_inline = 1;
+ break;
+ default:
+ do_inline = (func->u.cval == 1);
+ break;
+ }
+ if (state->compiler->flags & COMPILER_ALWAYS_INLINE) {
+ do_inline = 1;
+ }
+ if (!(state->compiler->flags & COMPILER_INLINE)) {
+ do_inline = 0;
+ }
+ if (!do_inline) {
+ continue;
+ }
+ if (state->compiler->debug & DEBUG_INLINE) {
+ fprintf(stderr, "inlining %s\n",
+ func->type->type_ident->name);
+ }
+
+ /* Update the function use counts */
+ func->u.cval -= 1;
+ /* Unhook the call and really inline it */
+ next->prev = prev;
+ prev->next = next;
+ ptr->next = ptr->prev = ptr;
+
+ new = flatten(state, next,
+ flatten_inline_call(state, next, ptr));
+ if (new) {
+ propogate_use(state, ptr, new);
+ }
+ release_triple(state, ptr);
+ next = prev->next;
+ } while (next != first);
+ ptr = next = first;
+ do {
+ struct triple *func, *prev, *new;
+ ptr = next;
+ prev = ptr->prev;
+ next = ptr->next;
+ if (ptr->op != OP_FCALL) {
+ continue;
+ }
+ func = MISC(ptr, 0);
+ inline_functions(state, RHS(func, 0));
+ /* Unhook the call and really flatten it */
+ next->prev = prev;
+ prev->next = next;
+ ptr->next = ptr->prev = ptr;
+ new = flatten(state, next,
+ flatten_function_call(state, next, ptr));
+ if (new) {
+ propogate_use(state, ptr, new);
+ }
+ release_triple(state, ptr);
+ next = prev->next;
+ } while(next != first);
+}
+
+static void insert_function(struct compile_state *state,
+ struct triple *func, void *arg)
+{
+ struct triple *first, *end, *ffirst, *fend;
+
+ if (state->compiler->debug & DEBUG_INLINE) {
+ fprintf(stderr, "%s func count: %d\n",
+ func->type->type_ident->name, func->u.cval);
+ }
+ if (func->u.cval == 0) {
+ return;
+ }
+ if (state->compiler->flags & COMPILER_ALWAYS_INLINE) {
+ internal_error(state, func, "always inline failed\n");
+ }
+
+ /* Find the end points of the lists */
+ first = arg;
+ end = first->prev;
+ ffirst = RHS(func, 0);
+ fend = ffirst->prev;
+
+ /* splice the lists together */
+ end->next = ffirst;
+ ffirst->prev = end;
+ fend->next = first;
+ first->prev = fend;
+}
+
+static void join_functions(struct compile_state *state)
+{
+ struct triple *jmp, *start, *end, *call;
+ struct file_state file;
+
+ /* Dummy file state to get debug handing right */
+ memset(&file, 0, sizeof(file));
+ file.basename = "";
+ file.line = 0;
+ file.report_line = 0;
+ file.report_name = file.basename;
+ file.prev = state->file;
+ state->file = &file;
+ state->function = "";
+
+ /* Lay down the basic program structure */
+ end = label(state);
+ start = label(state);
+ start = flatten(state, state->first, start);
+ end = flatten(state, state->first, end);
+ call = new_triple(state, OP_FCALL, &void_type, -1, 0);
+ MISC(call, 0) = state->main_function;
+ flatten(state, state->first, call);
+
+ /* See which functions are called, and how often */
+ mark_live_functions(state, state->first);
+ inline_functions(state, state->first);
+ walk_functions(state, insert_function, end);
+
+ if (start->next != end) {
+ jmp = flatten(state, start, branch(state, end, 0));
+ }
+
+ /* Done now cleanup */
+ state->file = file.prev;
+ state->function = 0;
+}
+
/*
* Data structurs for optimation.
*/
-static void do_use_block(
+
+static int do_use_block(
struct block *used, struct block_set **head, struct block *user,
int front)
{
struct block_set **ptr, *new;
if (!used)
- return;
+ return 0;
if (!user)
- return;
+ return 0;
ptr = head;
while(*ptr) {
if ((*ptr)->member == user) {
- return;
+ return 0;
}
ptr = &(*ptr)->next;
}
new->next = 0;
*ptr = new;
}
+ return 1;
}
-static void do_unuse_block(
+static int do_unuse_block(
struct block *used, struct block_set **head, struct block *unuser)
{
struct block_set *use, **ptr;
+ int count;
+ count = 0;
ptr = head;
while(*ptr) {
use = *ptr;
*ptr = use->next;
memset(use, -1, sizeof(*use));
xfree(use);
+ count += 1;
}
else {
ptr = &use->next;
}
}
+ return count;
}
static void use_block(struct block *used, struct block *user)
{
+ int count;
/* Append new to the head of the list, print_block
* depends on this.
*/
- do_use_block(used, &used->use, user, 1);
- used->users++;
+ count = do_use_block(used, &used->use, user, 1);
+ used->users += count;
}
static void unuse_block(struct block *used, struct block *unuser)
{
- do_unuse_block(used, &used->use, unuser);
- used->users--;
+ int count;
+ count = do_unuse_block(used, &used->use, unuser);
+ used->users -= count;
+}
+
+static void add_block_edge(struct block *block, struct block *edge, int front)
+{
+ int count;
+ count = do_use_block(block, &block->edges, edge, front);
+ block->edge_count += count;
+}
+
+static void remove_block_edge(struct block *block, struct block *edge)
+{
+ int count;
+ count = do_unuse_block(block, &block->edges, edge);
+ block->edge_count -= count;
}
static void idom_block(struct block *idom, struct block *user)
do_unuse_block(block, &block->ipdomfrontier, unipdomf);
}
-
-
-static int do_walk_triple(struct compile_state *state,
- struct triple *ptr, int depth,
- int (*cb)(struct compile_state *state, struct triple *ptr, int depth))
+static int walk_triples(
+ struct compile_state *state,
+ int (*cb)(struct compile_state *state, struct triple *ptr))
{
+ struct triple *ptr;
int result;
- result = cb(state, ptr, depth);
- if ((result == 0) && (ptr->op == OP_LIST)) {
- struct triple *list;
- list = ptr;
- ptr = RHS(list, 0);
- do {
- result = do_walk_triple(state, ptr, depth + 1, cb);
- if (ptr->next->prev != ptr) {
- internal_error(state, ptr->next, "bad prev");
- }
- ptr = ptr->next;
-
- } while((result == 0) && (ptr != RHS(list, 0)));
- }
+ ptr = state->first;
+ do {
+ result = cb(state, ptr);
+ if (ptr->next->prev != ptr) {
+ internal_error(state, ptr->next, "bad prev");
+ }
+ ptr = ptr->next;
+ } while((result == 0) && (ptr != state->first));
return result;
}
-static int walk_triple(
- struct compile_state *state,
- struct triple *ptr,
- int (*cb)(struct compile_state *state, struct triple *ptr, int depth))
-{
- return do_walk_triple(state, ptr, 0, cb);
-}
-
-static void do_print_prefix(int depth)
-{
- int i;
- for(i = 0; i < depth; i++) {
- printf(" ");
- }
-}
-
#define PRINT_LIST 1
-static int do_print_triple(struct compile_state *state, struct triple *ins, int depth)
+static int do_print_triple(struct compile_state *state, struct triple *ins)
{
int op;
op = ins->op;
if ((op == OP_LABEL) && (ins->use)) {
printf("\n%p:\n", ins);
}
- do_print_prefix(depth);
display_triple(stdout, ins);
- if ((ins->op == OP_BRANCH) && ins->use) {
+ if (triple_is_branch(state, ins) && ins->use && (ins->op != OP_RET)) {
internal_error(state, ins, "branch used?");
}
-#if 0
- {
- struct triple_set *user;
- for(user = ins->use; user; user = user->next) {
- printf("use: %p\n", user->member);
- }
- }
-#endif
if (triple_is_branch(state, ins)) {
printf("\n");
}
return 0;
}
-static void print_triple(struct compile_state *state, struct triple *ins)
-{
- walk_triple(state, ins, do_print_triple);
-}
-
static void print_triples(struct compile_state *state)
{
- print_triple(state, state->main_function);
+ if (state->compiler->debug & DEBUG_TRIPLES) {
+ walk_triples(state, do_print_triple);
+ }
}
struct cf_block {
};
static void find_cf_blocks(struct cf_block *cf, struct block *block)
{
+ struct block_set *edge;
if (!block || (cf[block->vertex].block == block)) {
return;
}
cf[block->vertex].block = block;
- find_cf_blocks(cf, block->left);
- find_cf_blocks(cf, block->right);
+ for(edge = block->edges; edge; edge = edge->next) {
+ find_cf_blocks(cf, edge->member);
+ }
}
static void print_control_flow(struct compile_state *state)
for(i = 1; i <= state->last_vertex; i++) {
struct block *block;
+ struct block_set *edge;
block = cf[i].block;
if (!block)
continue;
printf("(%p) %d:", block, block->vertex);
- if (block->left) {
- printf(" %d", block->left->vertex);
- }
- if (block->right && (block->right != block->left)) {
- printf(" %d", block->right->vertex);
+ for(edge = block->edges; edge; edge = edge->next) {
+ printf(" %d", edge->member->vertex);
}
printf("\n");
}
}
-static struct block *basic_block(struct compile_state *state,
- struct triple *first)
+static struct block *basic_block(struct compile_state *state, struct triple *first)
{
struct block *block;
struct triple *ptr;
- int op;
if (first->op != OP_LABEL) {
internal_error(state, 0, "block does not start with a label");
}
block->vertex = state->last_vertex;
ptr = first;
do {
- if ((ptr != first) && (ptr->op == OP_LABEL) && ptr->use) {
+ if ((ptr != first) && (ptr->op == OP_LABEL) && (ptr->use)) {
break;
}
block->last = ptr;
if (triple_stores_block(state, ptr)) {
ptr->u.block = block;
}
- if (ptr->op == OP_BRANCH) {
+ if (triple_is_branch(state, ptr)) {
break;
}
ptr = ptr->next;
- } while (ptr != RHS(state->main_function, 0));
- if (ptr == RHS(state->main_function, 0))
- return block;
- op = ptr->op;
- if (op == OP_LABEL) {
- block->left = basic_block(state, ptr);
- block->right = 0;
- use_block(block->left, block);
- }
- else if (op == OP_BRANCH) {
- block->left = 0;
- /* Trace the branch target */
- block->right = basic_block(state, TARG(ptr, 0));
- use_block(block->right, block);
- /* If there is a test trace the branch as well */
- if (TRIPLE_RHS(ptr->sizes)) {
- block->left = basic_block(state, ptr->next);
- use_block(block->left, block);
+ } while (ptr != state->first);
+ if (ptr == state->first) {
+ /* The block has no outflowing edges */
+ }
+ else if (ptr->op == OP_LABEL) {
+ struct block *next;
+ next = basic_block(state, ptr);
+ add_block_edge(block, next, 0);
+ use_block(next, block);
+ }
+ else if (triple_is_branch(state, ptr)) {
+ struct triple **expr, *first;
+ struct block *child;
+ /* Find the branch targets.
+ * I special case the first branch as that magically
+ * avoids some difficult cases for the register allocator.
+ */
+ expr = triple_targ(state, ptr, 0);
+ if (!expr) {
+ internal_error(state, ptr, "branch without targets");
+ }
+ first = *expr;
+ expr = triple_targ(state, ptr, expr);
+ for(; expr; expr = triple_targ(state, ptr, expr)) {
+ if (!*expr) continue;
+ child = basic_block(state, *expr);
+ use_block(child, block);
+ add_block_edge(block, child, 0);
+ }
+ if (first) {
+ child = basic_block(state, first);
+ use_block(child, block);
+ add_block_edge(block, child, 1);
}
}
else {
internal_error(state, 0, "Bad basic block split");
}
+#if 0
+{
+ struct block_set *edge;
+ fprintf(stderr, "basic_block: %10p [%2d] ( %10p - %10p )",
+ block, block->vertex,
+ block->first, block->last);
+ for(edge = block->edges; edge; edge = edge->next) {
+ fprintf(stderr, " %10p [%2d]",
+ edge->member ? edge->member->first : 0,
+ edge->member ? edge->member->vertex : -1);
+ }
+ fprintf(stderr, "\n");
+}
+#endif
return block;
}
struct triple *ptr, *first;
struct block *last_block;
last_block = 0;
- first = RHS(state->main_function, 0);
+ first = state->first;
ptr = first;
do {
- struct block *block;
- if (ptr->op == OP_LABEL) {
+ if (triple_stores_block(state, ptr)) {
+ struct block *block;
block = ptr->u.block;
if (block && (block != last_block)) {
cb(state, block, arg);
static void print_block(
struct compile_state *state, struct block *block, void *arg)
{
+ struct block_set *user, *edge;
struct triple *ptr;
FILE *fp = arg;
- fprintf(fp, "\nblock: %p (%d), %p<-%p %p<-%p\n",
+ fprintf(fp, "\nblock: %p (%d) ",
block,
- block->vertex,
- block->left,
- block->left && block->left->use?block->left->use->member : 0,
- block->right,
- block->right && block->right->use?block->right->use->member : 0);
+ block->vertex);
+
+ for(edge = block->edges; edge; edge = edge->next) {
+ fprintf(fp, " %p<-%p",
+ edge->member,
+ (edge->member && edge->member->use)?
+ edge->member->use->member : 0);
+ }
+ fprintf(fp, "\n");
if (block->first->op == OP_LABEL) {
fprintf(fp, "%p:\n", block->first);
}
for(ptr = block->first; ; ptr = ptr->next) {
- struct triple_set *user;
- int op = ptr->op;
-
- if (triple_stores_block(state, ptr)) {
- if (ptr->u.block != block) {
- internal_error(state, ptr,
- "Wrong block pointer: %p\n",
- ptr->u.block);
- }
- }
- if (op == OP_ADECL) {
- for(user = ptr->use; user; user = user->next) {
- if (!user->member->u.block) {
- internal_error(state, user->member,
- "Use %p not in a block?\n",
- user->member);
- }
- }
- }
display_triple(fp, ptr);
-
-#if 0
- for(user = ptr->use; user; user = user->next) {
- fprintf(fp, "use: %p\n", user->member);
- }
-#endif
-
- /* Sanity checks... */
- valid_ins(state, ptr);
- for(user = ptr->use; user; user = user->next) {
- struct triple *use;
- use = user->member;
- valid_ins(state, use);
- if (triple_stores_block(state, user->member) &&
- !user->member->u.block) {
- internal_error(state, user->member,
- "Use %p not in a block?",
- user->member);
- }
- }
-
if (ptr == block->last)
break;
}
- fprintf(fp,"\n");
+ fprintf(fp, "users %d: ", block->users);
+ for(user = block->use; user; user = user->next) {
+ fprintf(fp, "%p (%d) ",
+ user->member,
+ user->member->vertex);
+ }
+ fprintf(fp,"\n\n");
}
-static void print_blocks(struct compile_state *state, FILE *fp)
+static void romcc_print_blocks(struct compile_state *state, FILE *fp)
{
fprintf(fp, "--------------- blocks ---------------\n");
walk_blocks(state, print_block, fp);
}
+static void print_blocks(struct compile_state *state, const char *func, FILE *fp)
+{
+ if (state->compiler->debug & DEBUG_BASIC_BLOCKS) {
+ fprintf(fp, "After %s\n", func);
+ romcc_print_blocks(state, fp);
+ print_control_flow(state);
+ }
+}
static void prune_nonblock_triples(struct compile_state *state)
{
struct block *block;
struct triple *first, *ins, *next;
/* Delete the triples not in a basic block */
- first = RHS(state->main_function, 0);
+ first = state->first;
block = 0;
ins = first;
do {
if (!block) {
release_triple(state, ins);
}
+ if (block && block->last == ins) {
+ block = 0;
+ }
ins = next;
} while(ins != first);
}
static void setup_basic_blocks(struct compile_state *state)
{
- if (!triple_stores_block(state, RHS(state->main_function, 0)) ||
- !triple_stores_block(state, RHS(state->main_function,0)->prev)) {
+ if (!triple_stores_block(state, state->first)) {
internal_error(state, 0, "ins will not store block?");
}
/* Find the basic blocks */
state->last_vertex = 0;
- state->first_block = basic_block(state, RHS(state->main_function,0));
+ state->first_block = basic_block(state, state->first);
/* Delete the triples not in a basic block */
prune_nonblock_triples(state);
- /* Find the last basic block */
- state->last_block = RHS(state->main_function, 0)->prev->u.block;
- if (!state->last_block) {
- internal_error(state, 0, "end not used?");
- }
- /* Insert an extra unused edge from start to the end
- * This helps with reverse control flow calculations.
+
+ /* Find the last basic block.
+ *
+ * For purposes of reverse flow computation it is
+ * important that the last basic block is empty.
+ * This allows the control flow graph to be modified to
+ * have one unique starting block and one unique final block.
+ * With the insertion of a few extra edges.
+ *
+ * If the final block contained instructions it could contain
+ * phi functions from edges that would never contribute a
+ * value. Which for now at least I consider a compile error.
*/
- use_block(state->first_block, state->last_block);
+ state->last_block = block_of_triple(state, state->first->prev);
+ if ((state->last_block->first != state->last_block->last) ||
+ (state->last_block->last->op != OP_LABEL))
+ {
+ struct block *block, *prev_block;
+ struct triple *final;
+
+ prev_block = state->last_block;
+
+ final = label(state);
+ flatten(state, state->first, final);
+ final->id |= TRIPLE_FLAG_VOLATILE;
+ use_triple(final, final);
+ block = basic_block(state, final);
+
+ state->last_block = block;
+
+ add_block_edge(prev_block, block, 0);
+ use_block(block, prev_block);
+ }
+
+#if 0
/* If we are debugging print what I have just done */
- if (state->debug & DEBUG_BASIC_BLOCKS) {
+ if (state->compiler->debug & DEBUG_BASIC_BLOCKS) {
print_blocks(state, stdout);
print_control_flow(state);
}
+#endif
}
static void free_basic_block(struct compile_state *state, struct block *block)
{
- struct block_set *entry, *next;
+ struct block_set *edge, *entry;
struct block *child;
if (!block) {
return;
return;
}
block->vertex = -1;
- if (block->left) {
- unuse_block(block->left, block);
- }
- if (block->right) {
- unuse_block(block->right, block);
+ for(edge = block->edges; edge; edge = edge->next) {
+ if (edge->member) {
+ unuse_block(edge->member, block);
+ }
}
if (block->idom) {
unidom_block(block->idom, block);
unipdom_block(block->ipdom, block);
}
block->ipdom = 0;
- for(entry = block->use; entry; entry = next) {
- next = entry->next;
+ while((entry = block->use)) {
child = entry->member;
unuse_block(block, child);
- if (child->left == block) {
- child->left = 0;
- }
- if (child->right == block) {
- child->right = 0;
+ if (child && (child->vertex != -1)) {
+ for(edge = child->edges; edge; edge = edge->next) {
+ edge->member = 0;
+ }
}
}
- for(entry = block->idominates; entry; entry = next) {
- next = entry->next;
+ while((entry = block->idominates)) {
child = entry->member;
unidom_block(block, child);
- child->idom = 0;
+ if (child && (child->vertex != -1)) {
+ child->idom = 0;
+ }
}
- for(entry = block->domfrontier; entry; entry = next) {
- next = entry->next;
+ while((entry = block->domfrontier)) {
child = entry->member;
undomf_block(block, child);
}
- for(entry = block->ipdominates; entry; entry = next) {
- next = entry->next;
+ while((entry = block->ipdominates)) {
child = entry->member;
unipdom_block(block, child);
- child->ipdom = 0;
+ if (child && (child->vertex != -1)) {
+ child->ipdom = 0;
+ }
}
- for(entry = block->ipdomfrontier; entry; entry = next) {
- next = entry->next;
+ while((entry = block->ipdomfrontier)) {
child = entry->member;
unipdomf_block(block, child);
}
if (block->users != 0) {
internal_error(state, 0, "block still has users");
}
- free_basic_block(state, block->left);
- block->left = 0;
- free_basic_block(state, block->right);
- block->right = 0;
+ while((edge = block->edges)) {
+ child = edge->member;
+ remove_block_edge(block, child);
+
+ if (child && (child->vertex != -1)) {
+ free_basic_block(state, child);
+ }
+ }
memset(block, -1, sizeof(*block));
xfree(block);
}
free_basic_block(state, state->first_block);
state->last_vertex = 0;
state->first_block = state->last_block = 0;
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
if (triple_stores_block(state, ins)) {
static int initialize_sdblock(struct sdom_block *sd,
struct block *parent, struct block *block, int vertex)
{
+ struct block_set *edge;
if (!block || (sd[block->vertex].block == block)) {
return vertex;
}
sd[vertex].parent = parent? &sd[parent->vertex] : 0;
sd[vertex].ancestor = 0;
sd[vertex].vertex = vertex;
- vertex = initialize_sdblock(sd, block, block->left, vertex);
- vertex = initialize_sdblock(sd, block, block->right, vertex);
+ for(edge = block->edges; edge; edge = edge->next) {
+ vertex = initialize_sdblock(sd, block, edge->member, vertex);
+ }
return vertex;
}
-static int initialize_sdpblock(struct sdom_block *sd,
+static int initialize_spdblock(
+ struct compile_state *state, struct sdom_block *sd,
struct block *parent, struct block *block, int vertex)
{
struct block_set *user;
sd[vertex].ancestor = 0;
sd[vertex].vertex = vertex;
for(user = block->use; user; user = user->next) {
- vertex = initialize_sdpblock(sd, block, user->member, vertex);
+ vertex = initialize_spdblock(state, sd, block, user->member, vertex);
+ }
+ return vertex;
+}
+
+static int setup_spdblocks(struct compile_state *state, struct sdom_block *sd)
+{
+ struct block *block;
+ int vertex;
+ /* Setup as many sdpblocks as possible without using fake edges */
+ vertex = initialize_spdblock(state, sd, 0, state->last_block, 0);
+
+ /* Walk through the graph and find unconnected blocks. Add a
+ * fake edge from the unconnected blocks to the end of the
+ * graph.
+ */
+ block = state->first_block->last->next->u.block;
+ for(; block && block != state->first_block; block = block->last->next->u.block) {
+ if (sd[block->vertex].block == block) {
+ continue;
+ }
+#if DEBUG_SDP_BLOCKS
+ fprintf(stderr, "Adding %d\n", vertex +1);
+#endif
+ add_block_edge(block, state->last_block, 0);
+ use_block(state->last_block, block);
+
+ vertex = initialize_spdblock(state, sd, state->last_block, block, vertex);
}
return vertex;
}
*/
for(i = state->last_vertex; i >= 2; i--) {
struct sdom_block *u, *v, *parent, *next;
+ struct block_set *edge;
struct block *block;
block = sd[i].block;
parent = sd[i].parent;
/* Step 2 */
- if (block->left) {
- v = &sd[block->left->vertex];
- u = !(v->ancestor)? v : (compress_ancestors(v), v->label);
- if (u->sdom->vertex < sd[i].sdom->vertex) {
- sd[i].sdom = u->sdom;
- }
- }
- if (block->right && (block->right != block->left)) {
- v = &sd[block->right->vertex];
+ for(edge = block->edges; edge; edge = edge->next) {
+ v = &sd[edge->member->vertex];
u = !(v->ancestor)? v : (compress_ancestors(v), v->label);
if (u->sdom->vertex < sd[i].sdom->vertex) {
sd[i].sdom = u->sdom;
static void find_post_dominators(struct compile_state *state)
{
struct sdom_block *sd;
+ int vertex;
/* Step 1 initialize the basic block information */
sd = xcmalloc(sizeof(*sd) * (state->last_vertex + 1), "sdom_state");
- initialize_sdpblock(sd, 0, state->last_block, 0);
+ vertex = setup_spdblocks(state, sd);
+ if (vertex != state->last_vertex) {
+ internal_error(state, 0, "missing %d blocks\n",
+ state->last_vertex - vertex);
+ }
/* Step 2 compute the semidominators */
/* Step 3 implicitly define the immediate dominator of each vertex */
static void find_block_domf(struct compile_state *state, struct block *block)
{
struct block *child;
- struct block_set *user;
+ struct block_set *user, *edge;
if (block->domfrontier != 0) {
internal_error(state, block->first, "domfrontier present?");
}
}
find_block_domf(state, child);
}
- if (block->left && block->left->idom != block) {
- domf_block(block, block->left);
- }
- if (block->right && block->right->idom != block) {
- domf_block(block, block->right);
+ for(edge = block->edges; edge; edge = edge->next) {
+ if (edge->member->idom != block) {
+ domf_block(block, edge->member);
+ }
}
for(user = block->idominates; user; user = user->next) {
struct block_set *frontier;
}
find_block_ipdomf(state, child);
}
- if (block->left && block->left->ipdom != block) {
- ipdomf_block(block, block->left);
- }
- if (block->right && block->right->ipdom != block) {
- ipdomf_block(block, block->right);
+ for(user = block->use; user; user = user->next) {
+ if (user->member->ipdom != block) {
+ ipdomf_block(block, user->member);
+ }
}
- for(user = block->idominates; user; user = user->next) {
+ for(user = block->ipdominates; user; user = user->next) {
struct block_set *frontier;
child = user->member;
for(frontier = child->ipdomfrontier; frontier; frontier = frontier->next) {
fprintf(fp,"\n");
}
+static void print_dominated2(
+ struct compile_state *state, FILE *fp, int depth, struct block *block)
+{
+ struct block_set *user;
+ struct triple *ins;
+ struct occurance *ptr, *ptr2;
+ const char *filename1, *filename2;
+ int equal_filenames;
+ int i;
+ for(i = 0; i < depth; i++) {
+ fprintf(fp, " ");
+ }
+ fprintf(fp, "%3d: %p (%p - %p) @",
+ block->vertex, block, block->first, block->last);
+ ins = block->first;
+ while(ins != block->last && (ins->occurance->line == 0)) {
+ ins = ins->next;
+ }
+ ptr = ins->occurance;
+ ptr2 = block->last->occurance;
+ filename1 = ptr->filename? ptr->filename : "";
+ filename2 = ptr2->filename? ptr2->filename : "";
+ equal_filenames = (strcmp(filename1, filename2) == 0);
+ if ((ptr == ptr2) || (equal_filenames && ptr->line == ptr2->line)) {
+ fprintf(fp, " %s:%d", ptr->filename, ptr->line);
+ } else if (equal_filenames) {
+ fprintf(fp, " %s:(%d - %d)",
+ ptr->filename, ptr->line, ptr2->line);
+ } else {
+ fprintf(fp, " (%s:%d - %s:%d)",
+ ptr->filename, ptr->line,
+ ptr2->filename, ptr2->line);
+ }
+ fprintf(fp, "\n");
+ for(user = block->idominates; user; user = user->next) {
+ print_dominated2(state, fp, depth + 1, user->member);
+ }
+}
+
static void print_dominators(struct compile_state *state, FILE *fp)
{
fprintf(fp, "\ndominates\n");
walk_blocks(state, print_dominated, fp);
+ fprintf(fp, "dominates\n");
+ print_dominated2(state, fp, 0, state->first_block);
}
static int print_frontiers(
struct compile_state *state, struct block *block, int vertex)
{
- struct block_set *user;
+ struct block_set *user, *edge;
if (!block || (block->vertex != vertex + 1)) {
return vertex;
printf(" %d", user->member->vertex);
}
printf("\n");
-
- vertex = print_frontiers(state, block->left, vertex);
- vertex = print_frontiers(state, block->right, vertex);
+
+ for(edge = block->edges; edge; edge = edge->next) {
+ vertex = print_frontiers(state, edge->member, vertex);
+ }
return vertex;
}
static void print_dominance_frontiers(struct compile_state *state)
/* Find the dominance frontiers */
find_block_domf(state, state->first_block);
/* If debuging print the print what I have just found */
- if (state->debug & DEBUG_FDOMINATORS) {
+ if (state->compiler->debug & DEBUG_FDOMINATORS) {
print_dominators(state, stdout);
print_dominance_frontiers(state);
print_control_flow(state);
/* Find the control dependencies (post dominance frontiers) */
find_block_ipdomf(state, state->last_block);
/* If debuging print the print what I have just found */
- if (state->debug & DEBUG_RDOMINATORS) {
+ if (state->compiler->debug & DEBUG_RDOMINATORS) {
print_ipdominators(state, stdout);
print_ipdominance_frontiers(state);
print_control_flow(state);
return result;
}
+static void analyze_basic_blocks(struct compile_state *state)
+{
+ setup_basic_blocks(state);
+ analyze_idominators(state);
+ analyze_ipdominators(state);
+}
+
static void insert_phi_operations(struct compile_state *state)
{
size_t size;
work = xcmalloc(size, "work");
iter = 0;
- first = RHS(state->main_function, 0);
+ first = state->first;
for(var = first->next; var != first ; var = vnext) {
struct block *block;
struct triple_set *user, *unext;
/* Count how many edges flow into this block */
in_edges = front->users;
/* Insert a phi function for this variable */
- get_occurance(front->first->occurance);
+ get_occurance(var->occurance);
phi = alloc_triple(
state, OP_PHI, var->type, -1, in_edges,
- front->first->occurance);
+ var->occurance);
phi->u.block = front;
MISC(phi, 0) = var;
use_triple(var, phi);
front->last = front->first->next;
}
has_already[front->vertex] = iter;
+ transform_to_arch_instruction(state, phi);
/* If necessary plan to visit the basic block */
if (work[front->vertex] >= iter) {
xfree(work);
}
+
+struct stack {
+ struct triple_set *top;
+ unsigned orig_id;
+};
+
+static int count_adecls(struct compile_state *state)
+{
+ struct triple *first, *ins;
+ int adecls = 0;
+ first = state->first;
+ ins = first;
+ do {
+ if (ins->op == OP_ADECL) {
+ adecls += 1;
+ }
+ ins = ins->next;
+ } while(ins != first);
+ return adecls;
+}
+
+static void number_adecls(struct compile_state *state, struct stack *stacks)
+{
+ struct triple *first, *ins;
+ int adecls = 0;
+ first = state->first;
+ ins = first;
+ do {
+ if (ins->op == OP_ADECL) {
+ adecls += 1;
+ stacks[adecls].orig_id = ins->id;
+ ins->id = adecls;
+ }
+ ins = ins->next;
+ } while(ins != first);
+}
+
+static void restore_adecls(struct compile_state *state, struct stack *stacks)
+{
+ struct triple *first, *ins;
+ first = state->first;
+ ins = first;
+ do {
+ if (ins->op == OP_ADECL) {
+ ins->id = stacks[ins->id].orig_id;
+ }
+ ins = ins->next;
+ } while(ins != first);
+}
+
+static struct triple *peek_triple(struct stack *stacks, struct triple *var)
+{
+ struct triple_set *head;
+ struct triple *top_val;
+ top_val = 0;
+ head = stacks[var->id].top;
+ if (head) {
+ top_val = head->member;
+ }
+ return top_val;
+}
+
+static void push_triple(struct stack *stacks, struct triple *var, struct triple *val)
+{
+ struct triple_set *new;
+ /* Append new to the head of the list,
+ * it's the only sensible behavoir for a stack.
+ */
+ new = xcmalloc(sizeof(*new), "triple_set");
+ new->member = val;
+ new->next = stacks[var->id].top;
+ stacks[var->id].top = new;
+}
+
+static void pop_triple(struct stack *stacks, struct triple *var, struct triple *oldval)
+{
+ struct triple_set *set, **ptr;
+ ptr = &stacks[var->id].top;
+ while(*ptr) {
+ set = *ptr;
+ if (set->member == oldval) {
+ *ptr = set->next;
+ xfree(set);
+ /* Only free one occurance from the stack */
+ return;
+ }
+ else {
+ ptr = &set->next;
+ }
+ }
+}
+
/*
* C(V)
* S(V)
*/
static void fixup_block_phi_variables(
- struct compile_state *state, struct block *parent, struct block *block)
+ struct compile_state *state, struct stack *stacks, struct block *parent, struct block *block)
{
struct block_set *set;
struct triple *ptr;
internal_error(state, ptr, "no var???");
}
/* Find the current value of the variable */
- val = var->use->member;
- if ((val->op == OP_WRITE) || (val->op == OP_READ)) {
+ val = peek_triple(stacks, var);
+ if (val && ((val->op == OP_WRITE) || (val->op == OP_READ))) {
internal_error(state, val, "bad value in phi");
}
if (edge >= TRIPLE_RHS(ptr->sizes)) {
static void rename_block_variables(
- struct compile_state *state, struct block *block)
+ struct compile_state *state, struct stack *stacks, struct block *block)
{
- struct block_set *user;
+ struct block_set *user, *edge;
struct triple *ptr, *next, *last;
int done;
if (!block)
struct triple *var, *val;
var = RHS(ptr, 0);
unuse_triple(var, ptr);
- if (!var->use) {
+ /* Find the current value of the variable */
+ val = peek_triple(stacks, var);
+ if (!val) {
error(state, ptr, "variable used without being set");
}
- /* Find the current value of the variable */
- val = var->use->member;
if ((val->op == OP_WRITE) || (val->op == OP_READ)) {
internal_error(state, val, "bad value in read");
}
/* LHS(A) */
if (ptr->op == OP_WRITE) {
struct triple *var, *val, *tval;
- var = LHS(ptr, 0);
- tval = val = RHS(ptr, 0);
+ var = RHS(ptr, 0);
+ tval = val = RHS(ptr, 1);
if ((val->op == OP_WRITE) || (val->op == OP_READ)) {
- internal_error(state, val, "bad value in write");
+ internal_error(state, ptr, "bad value in write");
}
/* Insert a copy if the types differ */
if (!equiv_types(ptr->type, val->type)) {
tval = pre_triple(state, ptr, OP_COPY, ptr->type, val, 0);
use_triple(val, tval);
}
+ transform_to_arch_instruction(state, tval);
unuse_triple(val, ptr);
- RHS(ptr, 0) = tval;
+ RHS(ptr, 1) = tval;
use_triple(tval, ptr);
}
propogate_use(state, ptr, tval);
unuse_triple(var, ptr);
/* Push OP_WRITE ptr->right onto a stack of variable uses */
- push_triple(var, tval);
+ push_triple(stacks, var, tval);
}
if (ptr->op == OP_PHI) {
struct triple *var;
var = MISC(ptr, 0);
/* Push OP_PHI onto a stack of variable uses */
- push_triple(var, ptr);
+ push_triple(stacks, var, ptr);
}
last = ptr;
}
block->last = last;
/* Fixup PHI functions in the cf successors */
- fixup_block_phi_variables(state, block, block->left);
- fixup_block_phi_variables(state, block, block->right);
+ for(edge = block->edges; edge; edge = edge->next) {
+ fixup_block_phi_variables(state, stacks, block, edge->member);
+ }
/* rename variables in the dominated nodes */
for(user = block->idominates; user; user = user->next) {
- rename_block_variables(state, user->member);
+ rename_block_variables(state, stacks, user->member);
}
/* pop the renamed variable stack */
last = block->first;
}
if (ptr->op == OP_WRITE) {
struct triple *var;
- var = LHS(ptr, 0);
+ var = RHS(ptr, 0);
/* Pop OP_WRITE ptr->right from the stack of variable uses */
- pop_triple(var, RHS(ptr, 0));
+ pop_triple(stacks, var, RHS(ptr, 1));
release_triple(state, ptr);
continue;
}
struct triple *var;
var = MISC(ptr, 0);
/* Pop OP_WRITE ptr->right from the stack of variable uses */
- pop_triple(var, ptr);
+ pop_triple(stacks, var, ptr);
}
last = ptr;
}
block->last = last;
}
+static void rename_variables(struct compile_state *state)
+{
+ struct stack *stacks;
+ int adecls;
+
+ /* Allocate stacks for the Variables */
+ adecls = count_adecls(state);
+ stacks = xcmalloc(sizeof(stacks[0])*(adecls + 1), "adecl stacks");
+
+ /* Give each adecl a stack */
+ number_adecls(state, stacks);
+
+ /* Rename the variables */
+ rename_block_variables(state, stacks, state->first_block);
+
+ /* Remove the stacks from the adecls */
+ restore_adecls(state, stacks);
+ xfree(stacks);
+}
+
static void prune_block_variables(struct compile_state *state,
struct block *block)
{
}
}
+struct phi_triple {
+ struct triple *phi;
+ unsigned orig_id;
+ int alive;
+};
+
+static void keep_phi(struct compile_state *state, struct phi_triple *live, struct triple *phi)
+{
+ struct triple **slot;
+ int zrhs, i;
+ if (live[phi->id].alive) {
+ return;
+ }
+ live[phi->id].alive = 1;
+ zrhs = TRIPLE_RHS(phi->sizes);
+ slot = &RHS(phi, 0);
+ for(i = 0; i < zrhs; i++) {
+ struct triple *used;
+ used = slot[i];
+ if (used && (used->op == OP_PHI)) {
+ keep_phi(state, live, used);
+ }
+ }
+}
+
+static void prune_unused_phis(struct compile_state *state)
+{
+ struct triple *first, *phi;
+ struct phi_triple *live;
+ int phis, i;
+
+ /* Find the first instruction */
+ first = state->first;
+
+ /* Count how many phi functions I need to process */
+ phis = 0;
+ for(phi = first->next; phi != first; phi = phi->next) {
+ if (phi->op == OP_PHI) {
+ phis += 1;
+ }
+ }
+
+ /* Mark them all dead */
+ live = xcmalloc(sizeof(*live) * (phis + 1), "phi_triple");
+ phis = 0;
+ for(phi = first->next; phi != first; phi = phi->next) {
+ if (phi->op != OP_PHI) {
+ continue;
+ }
+ live[phis].alive = 0;
+ live[phis].orig_id = phi->id;
+ live[phis].phi = phi;
+ phi->id = phis;
+ phis += 1;
+ }
+
+ /* Mark phis alive that are used by non phis */
+ for(i = 0; i < phis; i++) {
+ struct triple_set *set;
+ for(set = live[i].phi->use; !live[i].alive && set; set = set->next) {
+ if (set->member->op != OP_PHI) {
+ keep_phi(state, live, live[i].phi);
+ break;
+ }
+ }
+ }
+
+ /* Delete the extraneous phis */
+ for(i = 0; i < phis; i++) {
+ struct triple **slot;
+ int zrhs, j;
+ if (!live[i].alive) {
+ release_triple(state, live[i].phi);
+ continue;
+ }
+ phi = live[i].phi;
+ slot = &RHS(phi, 0);
+ zrhs = TRIPLE_RHS(phi->sizes);
+ for(j = 0; j < zrhs; j++) {
+ if(!slot[j]) {
+ error(state, phi, "variable not set on all paths to use");
+ }
+ }
+ }
+ xfree(live);
+}
+
static void transform_to_ssa_form(struct compile_state *state)
{
insert_phi_operations(state);
-#if 0
- printf("@%s:%d\n", __FILE__, __LINE__);
- print_blocks(state, stdout);
-#endif
- rename_block_variables(state, state->first_block);
+ rename_variables(state);
+
prune_block_variables(state, state->first_block);
+ prune_unused_phis(state);
+
+ print_blocks(state, __func__, stdout);
}
static void clear_vertex(
struct compile_state *state, struct block *block, void *arg)
{
+ /* Clear the current blocks vertex and the vertex of all
+ * of the current blocks neighbors in case there are malformed
+ * blocks with now instructions at this point.
+ */
+ struct block_set *user, *edge;
block->vertex = 0;
+ for(edge = block->edges; edge; edge = edge->next) {
+ edge->member->vertex = 0;
+ }
+ for(user = block->use; user; user = user->next) {
+ user->member->vertex = 0;
+ }
}
static void mark_live_block(
mark_live_block(state, (*targ)->u.block, next_vertex);
}
}
- else if (block->last->next != RHS(state->main_function, 0)) {
+ else if (block->last->next != state->first) {
struct triple *ins;
ins = block->last->next;
if (!triple_stores_block(state, ins)) {
* edges to blocks containting phi functions.
*/
struct triple *first;
- struct triple *phi, *next;
+ struct triple *phi, *var, *next;
int next_vertex;
/* Walk the control flow to see which blocks remain alive */
mark_live_block(state, state->first_block, &next_vertex);
/* Walk all of the operations to find the phi functions */
- first = RHS(state->main_function, 0);
+ first = state->first;
for(phi = first->next; phi != first ; phi = next) {
struct block_set *set;
struct block *block;
struct triple **slot;
- struct triple *var, *read;
+ struct triple *var;
struct triple_set *use, *use_next;
int edge, used;
next = phi->next;
if (phi->op != OP_PHI) {
continue;
}
+
block = phi->u.block;
slot = &RHS(phi, 0);
+ /* If this phi is in a dead block just forget it */
+ if (block->vertex == 0) {
+ release_triple(state, phi);
+ continue;
+ }
+
/* Forget uses from code in dead blocks */
for(use = phi->use; use; use = use_next) {
struct block *ublock;
}
unuse_triple(phi, use->member);
}
-
/* A variable to replace the phi function */
var = post_triple(state, phi, OP_ADECL, phi->type, 0,0);
- /* A read of the single value that is set into the variable */
- read = post_triple(state, var, OP_READ, phi->type, var, 0);
- use_triple(var, read);
- /* Replaces uses of the phi with variable reads */
- propogate_use(state, phi, read);
+ /* Replaces use of phi with var */
+ propogate_use(state, phi, var);
/* Walk all of the incoming edges/blocks and insert moves.
*/
+ used = 0;
for(edge = 0, set = block->use; set; set = set->next, edge++) {
- struct block *eblock;
+ struct block *eblock, *vblock;
struct triple *move;
- struct triple *val;
+ struct triple *val, *base;
eblock = set->member;
val = slot[edge];
slot[edge] = 0;
unuse_triple(val, phi);
+ vblock = block_of_triple(state, val);
- if (!val || (val == &zero_triple) ||
- (block->vertex == 0) || (eblock->vertex == 0) ||
- (val == phi) || (val == read)) {
+ /* If we don't have a value that belongs in an OP_WRITE
+ * continue on.
+ */
+ if (!val || (val == &zero_triple) || (val == phi) ||
+ (!vblock) || (vblock->vertex == 0)) {
+ continue;
+ }
+
+ /* If the value occurs in a dead block see if a replacement
+ * block can be found.
+ */
+ while(eblock && (eblock->vertex == 0)) {
+ eblock = eblock->idom;
+ }
+ /* If not continue on with the next value. */
+ if (!eblock || (eblock->vertex == 0)) {
continue;
}
+
+ /* If we have an empty incoming block ignore it. */
+ if (!eblock->first) {
+ internal_error(state, 0, "empty block?");
+ }
- move = post_triple(state,
- val, OP_WRITE, phi->type, var, val);
+ /* Make certain the write is placed in the edge block... */
+ base = eblock->first;
+ if (block_of_triple(state, val) == eblock) {
+ base = val;
+ }
+ move = post_triple(state, base, OP_WRITE, var->type, var, val);
use_triple(val, move);
use_triple(var, move);
+ used = 1;
}
- /* See if there are any writers of var */
- used = 0;
- for(use = var->use; use; use = use->next) {
- struct triple **expr;
- expr = triple_lhs(state, use->member, 0);
- for(; expr; expr = triple_lhs(state, use->member, expr)) {
- if (*expr == var) {
- used = 1;
- }
- }
- }
/* If var is not used free it */
if (!used) {
- unuse_triple(var, read);
- free_triple(state, read);
free_triple(state, var);
}
release_triple(state, phi);
}
+ /* Walk all of the operations to find the adecls */
+ for(var = first->next; var != first ; var = var->next) {
+ struct triple_set *use, *use_next;
+ if (var->op != OP_ADECL) {
+ continue;
+ }
+
+ /* Walk through all of the rhs uses of var and
+ * replace them with read of var.
+ */
+ for(use = var->use; use; use = use_next) {
+ struct triple *read, *user;
+ struct triple **slot;
+ int zrhs, i, used;
+ use_next = use->next;
+ user = use->member;
+
+ /* Generate a read of var */
+ read = pre_triple(state, user, OP_READ, var->type, var, 0);
+ use_triple(var, read);
+
+ /* Find the rhs uses and see if they need to be replaced */
+ used = 0;
+ zrhs = TRIPLE_RHS(user->sizes);
+ slot = &RHS(user, 0);
+ for(i = 0; i < zrhs; i++) {
+ if ((slot[i] == var) &&
+ ((i != 0) || (user->op != OP_WRITE)))
+ {
+ slot[i] = read;
+ used = 1;
+ }
+ }
+ /* If we did use it cleanup the uses */
+ if (used) {
+ unuse_triple(var, user);
+ use_triple(read, user);
+ }
+ /* If we didn't use it release the extra triple */
+ else {
+ release_triple(state, read);
+ }
+ }
+ }
}
+#define HI() if (state->compiler->debug & DEBUG_REBUILD_SSA_FORM) { \
+ fprintf(stderr, "@ %s:%d\n", __FILE__, __LINE__); romcc_print_blocks(state, stderr); \
+ }
+
+static void rebuild_ssa_form(struct compile_state *state)
+{
+HI();
+ transform_from_ssa_form(state);
+HI();
+ free_basic_blocks(state);
+ analyze_basic_blocks(state);
+HI();
+ insert_phi_operations(state);
+HI();
+ rename_variables(state);
+HI();
+
+ prune_block_variables(state, state->first_block);
+HI();
+ prune_unused_phis(state);
+HI();
+}
+#undef HI
/*
* Register conflict resolution
struct triple_set *set;
struct reg_info info;
struct triple *lhs;
-#if 0
+#if DEBUG_TRIPLE_COLOR
fprintf(stderr, "find_lhs_post_color(%p, %d)\n",
ins, index);
#endif
info.regcm &= rinfo.regcm;
}
}
-#if 0
+#if DEBUG_TRIPLE_COLOR
fprintf(stderr, "find_lhs_post_color(%p, %d) -> ( %d, %x)\n",
ins, index, info.reg, info.regcm);
#endif
{
struct reg_info info, rinfo;
int zlhs, i;
-#if 0
+#if DEBUG_TRIPLE_COLOR
fprintf(stderr, "find_rhs_post_color(%p, %d)\n",
ins, index);
#endif
if (tinfo.reg >= MAX_REGISTERS) {
tinfo.reg = REG_UNSET;
}
- info.regcm &= linfo.reg;
+ info.regcm &= linfo.regcm;
info.regcm &= tinfo.regcm;
if (info.reg != REG_UNSET) {
internal_error(state, ins, "register conflict");
info.reg = tinfo.reg;
}
}
-#if 0
+#if DEBUG_TRIPLE_COLOR
fprintf(stderr, "find_rhs_post_color(%p, %d) -> ( %d, %x)\n",
ins, index, info.reg, info.regcm);
#endif
struct compile_state *state, struct triple *ins, int index)
{
struct reg_info pre, post, info;
-#if 0
+#if DEBUG_TRIPLE_COLOR
fprintf(stderr, "find_lhs_color(%p, %d)\n",
ins, index);
#endif
if (info.reg == REG_UNSET) {
info.reg = post.reg;
}
-#if 0
- fprintf(stderr, "find_lhs_color(%p, %d) -> ( %d, %x)\n",
- ins, index, info.reg, info.regcm);
+#if DEBUG_TRIPLE_COLOR
+ fprintf(stderr, "find_lhs_color(%p, %d) -> ( %d, %x) ... (%d, %x) (%d, %x)\n",
+ ins, index, info.reg, info.regcm,
+ pre.reg, pre.regcm, post.reg, post.regcm);
#endif
return info;
}
struct triple *phi;
/* Walk all of the operations to find the phi functions */
- first = RHS(state->main_function, 0);
+ first = state->first;
for(phi = first->next; phi != first ; phi = phi->next) {
struct block_set *set;
struct block *block;
unuse_triple(val, phi);
use_triple(move, phi);
+ /* Walk up the dominator tree until I have found the appropriate block */
+ while(eblock && !tdominates(state, val, eblock->last)) {
+ eblock = eblock->idom;
+ }
+ if (!eblock) {
+ internal_error(state, phi, "Cannot find block dominated by %p",
+ val);
+ }
+
/* Walk through the block backwards to find
* an appropriate location for the OP_COPY.
*/
transform_to_arch_instruction(state, move);
}
}
+ print_blocks(state, __func__, stdout);
}
struct triple_reg_set {
int i;
change = 0;
for(i = 1; i <= state->last_vertex; i++) {
+ struct block_set *edge;
struct reg_block *rb;
rb = &blocks[i];
- /* Add the left successor's input set to in */
- if (rb->block->left) {
- change |= reg_in(state, blocks, rb, rb->block->left);
- }
- /* Add the right successor's input set to in */
- if ((rb->block->right) &&
- (rb->block->right != rb->block->left)) {
- change |= reg_in(state, blocks, rb, rb->block->right);
+ /* Add the all successor's input set to in */
+ for(edge = rb->block->edges; edge; edge = edge->next) {
+ change |= reg_in(state, blocks, rb, edge->member);
}
/* Add use to in... */
change |= use_in(state, rb);
{
struct triple *first, *ins;
int triples = 0;
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
triples++;
} while (ins != first);
return triples;
}
+
+
struct dead_triple {
struct triple *triple;
struct dead_triple *work_next;
struct block *block;
- int color;
+ int old_id;
int flags;
#define TRIPLE_FLAG_ALIVE 1
};
triple->id);
}
if (triple->op == OP_NOOP) {
- internal_warning(state, triple, "awakening noop?");
+ internal_error(state, triple, "awakening noop?");
return;
}
dt = &dtriple[triple->id];
struct block *block;
struct dead_triple *dtriple, *work_list, **work_list_tail, *dt;
int triples, i;
- struct triple *first, *ins;
+ struct triple *first, *final, *ins;
+
+ if (!(state->compiler->flags & COMPILER_ELIMINATE_INEFECTUAL_CODE)) {
+ return;
+ }
/* Setup the work list */
work_list = 0;
work_list_tail = &work_list;
- first = RHS(state->main_function, 0);
+ first = state->first;
+ final = state->first->prev;
/* Count how many triples I have */
triples = count_triples(state);
i = 1;
block = 0;
do {
- if (ins->op == OP_LABEL) {
- block = ins->u.block;
- }
dtriple[i].triple = ins;
- dtriple[i].block = block;
+ dtriple[i].block = block_of_triple(state, ins);
dtriple[i].flags = 0;
- dtriple[i].color = ins->id;
+ dtriple[i].old_id = ins->id;
ins->id = i;
/* See if it is an operation we always keep */
-#warning "FIXME handle the case of killing a branch instruction"
- if (!triple_is_pure(state, ins) || triple_is_branch(state, ins)) {
+ if (!triple_is_pure(state, ins, dtriple[i].old_id)) {
awaken(state, dtriple, &ins, &work_list_tail);
}
i++;
ins = ins->next;
} while(ins != first);
while(work_list) {
+ struct block *block;
struct dead_triple *dt;
struct block_set *user;
struct triple **expr;
if (!work_list) {
work_list_tail = &work_list;
}
+ /* Make certain the block the current instruction is in lives */
+ block = block_of_triple(state, dt->triple);
+ awaken(state, dtriple, &block->first, &work_list_tail);
+ if (triple_is_branch(state, block->last)) {
+ awaken(state, dtriple, &block->last, &work_list_tail);
+ }
+
/* Wake up the data depencencies of this triple */
expr = 0;
do {
} while(expr);
/* Wake up the reverse control dependencies of this triple */
for(user = dt->block->ipdomfrontier; user; user = user->next) {
- awaken(state, dtriple, &user->member->last, &work_list_tail);
+ struct triple *last;
+ last = user->member->last;
+ while((last->op == OP_NOOP) && (last != user->member->first)) {
+ internal_warning(state, last, "awakening noop?");
+ last = last->prev;
+ }
+ awaken(state, dtriple, &last, &work_list_tail);
}
}
for(dt = &dtriple[1]; dt <= &dtriple[triples]; dt++) {
(dt->flags & TRIPLE_FLAG_ALIVE)) {
internal_error(state, dt->triple, "noop effective?");
}
- dt->triple->id = dt->color; /* Restore the color */
+ dt->triple->id = dt->old_id; /* Restore the color */
if (!(dt->flags & TRIPLE_FLAG_ALIVE)) {
-#warning "FIXME handle the case of killing a basic block"
- if (dt->block->first == dt->triple) {
- continue;
- }
- if (dt->block->last == dt->triple) {
- dt->block->last = dt->triple->prev;
- }
release_triple(state, dt->triple);
}
}
xfree(dtriple);
+
+ rebuild_ssa_form(state);
+
+ print_blocks(state, __func__, stdout);
}
* are inserting copies before instructions but that
* case should be rare.
*/
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
struct triple_set *entry, *next;
next:
ins = ins->next;
} while(ins != first);
+
+ print_blocks(state, __func__, stdout);
}
unsigned defs;
unsigned ranges;
int passes, max_passes;
-#define MAX_ALLOCATION_PASSES 100
};
{
struct print_interference_block_info *info = arg;
struct reg_state *rstate = info->rstate;
+ struct block_set *edge;
FILE *fp = info->fp;
struct reg_block *rb;
struct triple *ptr;
int done;
rb = &rstate->blocks[block->vertex];
- fprintf(fp, "\nblock: %p (%d), %p<-%p %p<-%p\n",
- block,
- block->vertex,
- block->left,
- block->left && block->left->use?block->left->use->member : 0,
- block->right,
- block->right && block->right->use?block->right->use->member : 0);
+ fprintf(fp, "\nblock: %p (%d),",
+ block, block->vertex);
+ for(edge = block->edges; edge; edge = edge->next) {
+ fprintf(fp, " %p<-%p",
+ edge->member,
+ edge->member && edge->member->use?edge->member->use->member : 0);
+ }
+ fprintf(fp, "\n");
if (rb->in) {
struct triple_reg_set *in_set;
fprintf(fp, " in:");
fprintf(fp, "%p:\n", block->first);
}
for(done = 0, ptr = block->first; !done; ptr = ptr->next) {
- struct triple_set *user;
struct live_range *lr;
unsigned id;
int op;
done = (ptr == block->last);
lr = rstate->lrd[ptr->id].lr;
- if (triple_stores_block(state, ptr)) {
- if (ptr->u.block != block) {
- internal_error(state, ptr,
- "Wrong block pointer: %p",
- ptr->u.block);
- }
- }
- if (op == OP_ADECL) {
- for(user = ptr->use; user; user = user->next) {
- if (!user->member->u.block) {
- internal_error(state, user->member,
- "Use %p not in a block?",
- user->member);
- }
-
- }
- }
id = ptr->id;
ptr->id = rstate->lrd[id].orig_id;
SET_REG(ptr->id, lr->color);
internal_error(state, ptr, "Invalid triple id: %d",
ptr->id);
}
- for(user = ptr->use; user; user = user->next) {
- struct triple *use;
- struct live_range *ulr;
- use = user->member;
- valid_ins(state, use);
- if ((use->id < 0) || (use->id > rstate->defs)) {
- internal_error(state, use, "Invalid triple id: %d",
- use->id);
- }
- ulr = rstate->lrd[user->member->id].lr;
- if (triple_stores_block(state, user->member) &&
- !user->member->u.block) {
- internal_error(state, user->member,
- "Use %p not in a block?",
- user->member);
- }
- }
}
if (rb->out) {
struct triple_reg_set *out_set;
internal_error(state, lr1->defs->def,
"cannot coalesce live ranges with dissimilar register classes");
}
-#if DEBUG_COALESCING
- fprintf(stderr, "coalescing:");
- lrd = lr1->defs;
- do {
- fprintf(stderr, " %p", lrd->def);
- lrd = lrd->next;
- } while(lrd != lr1->defs);
- fprintf(stderr, " |");
- lrd = lr2->defs;
- do {
- fprintf(stderr, " %p", lrd->def);
- lrd = lrd->next;
- } while(lrd != lr2->defs);
- fprintf(stderr, "\n");
-#endif
+ if (state->compiler->debug & DEBUG_COALESCING) {
+ fprintf(stderr, "coalescing:");
+ lrd = lr1->defs;
+ do {
+ fprintf(stderr, " %p", lrd->def);
+ lrd = lrd->next;
+ } while(lrd != lr1->defs);
+ fprintf(stderr, " |");
+ lrd = lr2->defs;
+ do {
+ fprintf(stderr, " %p", lrd->def);
+ lrd = lrd->next;
+ } while(lrd != lr2->defs);
+ fprintf(stderr, "\n");
+ }
/* If there is a clear dominate live range put it in lr1,
* For purposes of this test phi functions are
* considered dominated by the definitions that feed into
size_t count, size;
int i, j;
- first = RHS(state->main_function, 0);
+ first = state->first;
/* First count how many instructions I have.
*/
count = count_triples(state);
}
zrhs = TRIPLE_RHS(ins->sizes);
-#if DEBUG_COALESCING > 1
- fprintf(stderr, "mandatory coalesce: %p %d %d\n",
- ins, zlhs, zrhs);
-
-#endif
+ if (state->compiler->debug & DEBUG_COALESCING2) {
+ fprintf(stderr, "mandatory coalesce: %p %d %d\n",
+ ins, zlhs, zrhs);
+ }
+
for(i = 0; i < zlhs; i++) {
struct reg_info linfo;
struct live_range_def *lhs;
} else {
lhs = &rstate->lrd[LHS(ins, i)->id];
}
-#if DEBUG_COALESCING > 1
- fprintf(stderr, "coalesce lhs(%d): %p %d\n",
- i, lhs, linfo.reg);
-
-#endif
+
+ if (state->compiler->debug & DEBUG_COALESCING2) {
+ fprintf(stderr, "coalesce lhs(%d): %p %d\n",
+ i, lhs, linfo.reg);
+ }
+
for(j = 0; j < zrhs; j++) {
struct reg_info rinfo;
struct live_range_def *rhs;
continue;
}
rhs = &rstate->lrd[RHS(ins, j)->id];
-#if DEBUG_COALESCING > 1
- fprintf(stderr, "coalesce rhs(%d): %p %d\n",
- j, rhs, rinfo.reg);
-
-#endif
+
+ if (state->compiler->debug & DEBUG_COALESCING2) {
+ fprintf(stderr, "coalesce rhs(%d): %p %d\n",
+ j, rhs, rinfo.reg);
+ }
+
if (rinfo.reg == linfo.reg) {
coalesce_ranges(state, rstate,
lhs->lr, rhs->lr);
static void color_instructions(struct compile_state *state)
{
struct triple *ins, *first;
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
if (triple_is_def(state, ins)) {
struct triple *find_constrained_def(
struct compile_state *state, struct live_range *range, struct triple *constrained)
{
- struct live_range_def *lrd;
- lrd = range->defs;
+ struct live_range_def *lrd, *lrd_next;
+ lrd_next = range->defs;
do {
struct reg_info info;
unsigned regcm;
- int is_constrained;
+
+ lrd = lrd_next;
+ lrd_next = lrd->next;
+
regcm = arch_type_to_regcm(state, lrd->def->type);
info = find_lhs_color(state, lrd->def, 0);
regcm = arch_regcm_reg_normalize(state, regcm);
info.regcm = arch_regcm_reg_normalize(state, info.regcm);
- /* If the 2 register class masks are not equal the
- * the current register class is constrained.
+ /* If the 2 register class masks are equal then
+ * the current register class is not constrained.
+ */
+ if (regcm == info.regcm) {
+ continue;
+ }
+
+ /* If there is just one use.
+ * That use cannot accept a larger register class.
+ * There are no intervening definitions except
+ * definitions that feed into that use.
+ * Then a triple is not constrained.
+ * FIXME handle this case!
*/
- is_constrained = regcm != info.regcm;
+#warning "FIXME ignore cases that cannot be fixed (a definition followed by a use)"
+
/* Of the constrained live ranges deal with the
* least dominated one first.
*/
- if (is_constrained) {
- if (!constrained ||
- tdominates(state, lrd->def, constrained))
- {
- constrained = lrd->def;
- }
+ if (state->compiler->debug & DEBUG_RANGE_CONFLICTS) {
+ fprintf(stderr, "canidate: %p %-8s regcm: %x %x\n",
+ lrd->def, tops(lrd->def->op), regcm, info.regcm);
}
- lrd = lrd->next;
- } while(lrd != range->defs);
+ if (!constrained ||
+ tdominates(state, lrd->def, constrained))
+ {
+ constrained = lrd->def;
+ }
+ } while(lrd_next != range->defs);
return constrained;
}
for(edge = range->edges; edge; edge = edge->next) {
constrained = find_constrained_def(state, edge->node, constrained);
}
+#warning "FIXME should I call find_constrained_def here only if no previous constrained def was found?"
if (!constrained) {
constrained = find_constrained_def(state, range, constrained);
}
-#if DEBUG_RANGE_CONFLICTS
- fprintf(stderr, "constrained: %s %p\n",
- tops(constrained->op), constrained);
-#endif
+
+ if (state->compiler->debug & DEBUG_RANGE_CONFLICTS) {
+ fprintf(stderr, "constrained: %p %-8s\n",
+ constrained, tops(constrained->op));
+ }
if (constrained) {
ids_from_rstate(state, rstate);
cleanup_rstate(state, rstate);
char *used, struct live_range *range)
{
int split;
-#if DEBUG_RANGE_CONFLICTS
- fprintf(stderr, "split_ranges %d %s %p\n",
- rstate->passes, tops(range->defs->def->op), range->defs->def);
-#endif
+ if (state->compiler->debug & DEBUG_RANGE_CONFLICTS) {
+ fprintf(stderr, "split_ranges %d %s %p\n",
+ rstate->passes, tops(range->defs->def->op), range->defs->def);
+ }
if ((range->color == REG_UNNEEDED) ||
(rstate->passes >= rstate->max_passes)) {
return 0;
*
*/
#warning "WISHLIST implement live range splitting..."
- if ((DEBUG_RANGE_CONFLICTS > 1) &&
- (!split || (DEBUG_RANGE_CONFLICTS > 2))) {
+
+ if (!split && (state->compiler->debug & DEBUG_RANGE_CONFLICTS2)) {
print_interference_blocks(state, rstate, stderr, 0);
print_dominators(state, stderr);
}
return split;
}
+static FILE *cgdebug_fp(struct compile_state *state)
+{
+ FILE *fp;
+ fp = 0;
+ if (!fp && (state->compiler->debug & DEBUG_COLOR_GRAPH2)) {
+ fp = stderr;
+ }
+ if (!fp && (state->compiler->debug & DEBUG_COLOR_GRAPH)) {
+ fp = stdout;
+ }
+ return fp;
+}
+
+static void cgdebug_printf(struct compile_state *state, const char *fmt, ...)
+{
+ FILE *fp;
+ fp = cgdebug_fp(state);
+ if (fp) {
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(fp, fmt, args);
+ va_end(args);
+ }
+}
-#if DEBUG_COLOR_GRAPH > 1
-#define cgdebug_printf(...) fprintf(stdout, __VA_ARGS__)
-#define cgdebug_flush() fflush(stdout)
-#define cgdebug_loc(STATE, TRIPLE) loc(stdout, STATE, TRIPLE)
-#elif DEBUG_COLOR_GRAPH == 1
-#define cgdebug_printf(...) fprintf(stderr, __VA_ARGS__)
-#define cgdebug_flush() fflush(stderr)
-#define cgdebug_loc(STATE, TRIPLE) loc(stderr, STATE, TRIPLE)
-#else
-#define cgdebug_printf(...)
-#define cgdebug_flush()
-#define cgdebug_loc(STATE, TRIPLE)
-#endif
+static void cgdebug_flush(struct compile_state *state)
+{
+ FILE *fp;
+ fp = cgdebug_fp(state);
+ if (fp) {
+ fflush(fp);
+ }
+}
+
+static void cgdebug_loc(struct compile_state *state, struct triple *ins)
+{
+ FILE *fp;
+ fp = cgdebug_fp(state);
+ if (fp) {
+ loc(fp, state, ins);
+ }
+}
-
static int select_free_color(struct compile_state *state,
struct reg_state *rstate, struct live_range *range)
{
}
reg_fill_used(state, used, edge->node->color);
}
-#if DEBUG_COLOR_GRAPH > 1
- {
+
+ if (state->compiler->debug & DEBUG_COLOR_GRAPH2) {
int i;
i = 0;
for(edge = range->edges; edge; edge = edge->next) {
i++;
}
- cgdebug_printf("\n%s edges: %d @%s:%d.%d\n",
- tops(range->def->op), i,
- range->def->filename, range->def->line, range->def->col);
+ cgdebug_printf(state, "\n%s edges: %d",
+ tops(range->defs->def->op), i);
+ cgdebug_loc(state, range->defs->def);
+ cgdebug_printf(state, "\n");
for(i = 0; i < MAX_REGISTERS; i++) {
if (used[i]) {
- cgdebug_printf("used: %s\n",
+ cgdebug_printf(state, "used: %s\n",
arch_reg_str(i));
}
}
}
-#endif
-
-#warning "FIXME detect conflicts caused by the source and destination being the same register"
/* If a color is already assigned see if it will work */
if (range->color != REG_UNSET) {
entry = lrd->def->use;
for(;(range->color == REG_UNSET) && entry; entry = entry->next) {
struct live_range_def *insd;
+ unsigned regcm;
insd = &rstate->lrd[entry->member->id];
if (insd->lr->defs == 0) {
continue;
!interfere(rstate, range, insd->lr)) {
phi = insd;
}
- if ((insd->lr->color == REG_UNSET) ||
- ((insd->lr->classes & range->classes) == 0) ||
+ if (insd->lr->color == REG_UNSET) {
+ continue;
+ }
+ regcm = insd->lr->classes;
+ if (((regcm & range->classes) == 0) ||
(used[insd->lr->color])) {
continue;
}
expr = triple_rhs(state, phi->def, 0);
for(; expr; expr = triple_rhs(state, phi->def, expr)) {
struct live_range *lr;
+ unsigned regcm;
if (!*expr) {
continue;
}
lr = rstate->lrd[(*expr)->id].lr;
- if ((lr->color == REG_UNSET) ||
- ((lr->classes & range->classes) == 0) ||
+ if (lr->color == REG_UNSET) {
+ continue;
+ }
+ regcm = lr->classes;
+ if (((regcm & range->classes) == 0) ||
(used[lr->color])) {
continue;
}
expr = triple_rhs(state, lrd->def, 0);
for(; expr; expr = triple_rhs(state, lrd->def, expr)) {
struct live_range *lr;
+ unsigned regcm;
if (!*expr) {
continue;
}
lr = rstate->lrd[(*expr)->id].lr;
- if ((lr->color == -1) ||
- ((lr->classes & range->classes) == 0) ||
+ if (lr->color == REG_UNSET) {
+ continue;
+ }
+ regcm = lr->classes;
+ if (((regcm & range->classes) == 0) ||
(used[lr->color])) {
continue;
}
arch_reg_str(i));
}
}
-#if DEBUG_COLOR_GRAPH < 2
error(state, range->defs->def, "too few registers");
-#else
- internal_error(state, range->defs->def, "too few registers");
-#endif
}
- range->classes = arch_reg_regcm(state, range->color);
- if (range->color == -1) {
+ range->classes &= arch_reg_regcm(state, range->color);
+ if ((range->color == REG_UNSET) || (range->classes == 0)) {
internal_error(state, range->defs->def, "select_free_color did not?");
}
return 1;
struct live_range_edge *edge;
struct live_range *range;
if (rstate->low) {
- cgdebug_printf("Lo: ");
+ cgdebug_printf(state, "Lo: ");
range = rstate->low;
if (*range->group_prev != range) {
internal_error(state, 0, "lo: *prev != range?");
}
}
else if (rstate->high) {
- cgdebug_printf("Hi: ");
+ cgdebug_printf(state, "Hi: ");
range = rstate->high;
if (*range->group_prev != range) {
internal_error(state, 0, "hi: *prev != range?");
else {
return 1;
}
- cgdebug_printf(" %d\n", range - rstate->lr);
+ cgdebug_printf(state, " %d\n", range - rstate->lr);
range->group_prev = 0;
for(edge = range->edges; edge; edge = edge->next) {
struct live_range *node;
if (&node->group_next == rstate->high_tail) {
rstate->high_tail = node->group_prev;
}
- cgdebug_printf("Moving...%d to low\n", node - rstate->lr);
+ cgdebug_printf(state, "Moving...%d to low\n", node - rstate->lr);
node->group_prev = rstate->low_tail;
node->group_next = 0;
*rstate->low_tail = node;
}
colored = color_graph(state, rstate);
if (colored) {
- cgdebug_printf("Coloring %d @", range - rstate->lr);
+ cgdebug_printf(state, "Coloring %d @", range - rstate->lr);
cgdebug_loc(state, range->defs->def);
- cgdebug_flush();
+ cgdebug_flush(state);
colored = select_free_color(state, rstate, range);
- cgdebug_printf(" %s\n", arch_reg_str(range->color));
+ cgdebug_printf(state, " %s\n", arch_reg_str(range->color));
}
return colored;
}
struct live_range_edge *edge;
struct triple *ins, *first;
char used[MAX_REGISTERS];
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
if (triple_is_def(state, ins)) {
{
struct live_range *lr;
struct triple *first, *ins;
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
if ((ins->id < 0) || (ins->id > rstate->defs)) {
return;
}
/* Display the graph if desired */
- if (state->debug & DEBUG_INTERFERENCE) {
- print_blocks(state, stdout);
+ if (state->compiler->debug & DEBUG_INTERFERENCE) {
+ print_interference_blocks(state, rstate, stdout, 0);
print_control_flow(state);
+ fflush(stdout);
}
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
if (ins->id) {
/* Clear out the reg_state */
memset(&rstate, 0, sizeof(rstate));
- rstate.max_passes = MAX_ALLOCATION_PASSES;
+ rstate.max_passes = state->compiler->max_allocation_passes;
do {
struct live_range **point, **next;
int tangles;
int coalesced;
-#if DEBUG_RANGE_CONFLICTS
- fprintf(stderr, "pass: %d\n", rstate.passes);
-#endif
+ if (state->compiler->debug & DEBUG_RANGE_CONFLICTS) {
+ fprintf(stderr, "pass: %d\n", rstate.passes);
+ fflush(stderr);
+ }
/* Restore ids */
ids_from_rstate(state, &rstate);
tangles = correct_tangles(state, rstate.blocks);
} while(tangles);
- if (state->debug & DEBUG_INSERTED_COPIES) {
- printf("After resolve_tangles\n");
- print_blocks(state, stdout);
- print_control_flow(state);
- }
+
+ print_blocks(state, "resolve_tangles", stdout);
verify_consistency(state);
/* Allocate and initialize the live ranges */
* yields some benefit.
*/
do {
-#if DEBUG_COALESCING
- fprintf(stderr, "coalescing\n");
-#endif
+ if (state->compiler->debug & DEBUG_COALESCING) {
+ fprintf(stderr, "coalescing\n");
+ }
+
/* Remove any previous live edge calculations */
cleanup_live_edges(&rstate);
state, rstate.blocks, graph_ins, &rstate);
/* Display the interference graph if desired */
- if (state->debug & DEBUG_INTERFERENCE) {
+ if (state->compiler->debug & DEBUG_INTERFERENCE) {
print_interference_blocks(state, &rstate, stdout, 1);
printf("\nlive variables by instruction\n");
walk_variable_lifetimes(
coalesced = coalesce_live_ranges(state, &rstate);
-#if DEBUG_COALESCING
- fprintf(stderr, "coalesced: %d\n", coalesced);
-#endif
+ if (state->compiler->debug & DEBUG_COALESCING) {
+ fprintf(stderr, "coalesced: %d\n", coalesced);
+ }
} while(coalesced);
#if DEBUG_CONSISTENCY > 1
*/
if ((range->degree < regc_max_size(state, range->classes)) ||
(range->color != REG_UNSET)) {
- cgdebug_printf("Lo: %5d degree %5d%s\n",
+ cgdebug_printf(state, "Lo: %5d degree %5d%s\n",
range - rstate.lr, range->degree,
(range->color != REG_UNSET) ? " (colored)": "");
*range->group_prev = range->group_next;
next = point;
}
else {
- cgdebug_printf("hi: %5d degree %5d%s\n",
+ cgdebug_printf(state, "hi: %5d degree %5d%s\n",
range - rstate.lr, range->degree,
(range->color != REG_UNSET) ? " (colored)": "");
}
/* Cleanup the temporary data structures */
cleanup_rstate(state, &rstate);
+
+ /* Display the new graph */
+ print_blocks(state, __func__, stdout);
}
/* Sparce Conditional Constant Propogation
struct flow_edge *out_next;
int executable;
};
+#define MAX_FLOW_BLOCK_EDGES 3
struct flow_block {
struct block *block;
struct flow_edge *in;
struct flow_edge *out;
- struct flow_edge left, right;
+ struct flow_edge *edges;
};
struct scc_state {
static void scc_add_fedge(struct compile_state *state, struct scc_state *scc,
struct flow_edge *fedge)
{
+ if (state->compiler->debug & DEBUG_SCC_TRANSFORM2) {
+ fprintf(stderr, "adding fedge: %p (%4d -> %5d)\n",
+ fedge,
+ fedge->src->block?fedge->src->block->last->id: 0,
+ fedge->dst->block?fedge->dst->block->first->id: 0);
+ }
+ if ((fedge == scc->flow_work_list) ||
+ (fedge->work_next != fedge) ||
+ (fedge->work_prev != fedge)) {
+
+ if (state->compiler->debug & DEBUG_SCC_TRANSFORM2) {
+ fprintf(stderr, "dupped fedge: %p\n",
+ fedge);
+ }
+ return;
+ }
if (!scc->flow_work_list) {
scc->flow_work_list = fedge;
fedge->work_next = fedge->work_prev = fedge;
} else {
scc->flow_work_list = 0;
}
+ fedge->work_next = fedge->work_prev = fedge;
}
return fedge;
}
static void scc_add_sedge(struct compile_state *state, struct scc_state *scc,
struct ssa_edge *sedge)
{
+ if (state->compiler->debug & DEBUG_SCC_TRANSFORM2) {
+ fprintf(stderr, "adding sedge: %5d (%4d -> %5d)\n",
+ sedge - scc->ssa_edges,
+ sedge->src->def->id,
+ sedge->dst->def->id);
+ }
+ if ((sedge == scc->ssa_work_list) ||
+ (sedge->work_next != sedge) ||
+ (sedge->work_prev != sedge)) {
+
+ if (state->compiler->debug & DEBUG_SCC_TRANSFORM2) {
+ fprintf(stderr, "dupped sedge: %5d\n",
+ sedge - scc->ssa_edges);
+ }
+ return;
+ }
if (!scc->ssa_work_list) {
scc->ssa_work_list = sedge;
sedge->work_next = sedge->work_prev = sedge;
} else {
scc->ssa_work_list = 0;
}
+ sedge->work_next = sedge->work_prev = sedge;
}
return sedge;
}
memset(scc, 0, sizeof(*scc));
/* Inialize pass zero find out how much memory we need */
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
ins_count = ssa_edge_count = 0;
do {
}
ins = ins->next;
} while(ins != first);
-#if DEBUG_SCC
- fprintf(stderr, "ins_count: %d ssa_edge_count: %d vertex_count: %d\n",
- ins_count, ssa_edge_count, state->last_vertex);
-#endif
+ if (state->compiler->debug & DEBUG_SCC_TRANSFORM) {
+ fprintf(stderr, "ins_count: %d ssa_edge_count: %d vertex_count: %d\n",
+ ins_count, ssa_edge_count, state->last_vertex);
+ }
scc->ins_count = ins_count;
scc->lattice =
xcmalloc(sizeof(*scc->lattice)*(ins_count + 1), "lattice");
block->vertex = fblock_index;
fblock = &scc->flow_blocks[fblock_index];
fblock->block = block;
+ fblock->edges = xcmalloc(sizeof(*fblock->edges)*block->edge_count,
+ "flow_edges");
}
{
struct lattice_node *lnode;
fblock = 0;
ins = first;
do {
+ {
+ struct triple_set *edge;
+ struct ssa_edge **stail;
+ struct lattice_node *lnode;
+ lnode = &scc->lattice[ins->id];
+ lnode->out = 0;
+ stail = &lnode->out;
+ for(edge = ins->use; edge; edge = edge->next) {
+ struct ssa_edge *sedge;
+ ssa_edge_index += 1;
+ sedge = &scc->ssa_edges[ssa_edge_index];
+ *stail = sedge;
+ stail = &sedge->out_next;
+ sedge->src = lnode;
+ sedge->dst = &scc->lattice[edge->member->id];
+ sedge->work_next = sedge->work_prev = sedge;
+ sedge->out_next = 0;
+ }
+ }
if ((ins->op == OP_LABEL) && (block != ins->u.block)) {
struct flow_edge *fedge, **ftail;
struct block_set *bedge;
fblock->in = 0;
fblock->out = 0;
ftail = &fblock->out;
- if (block->left) {
- fblock->left.dst = &scc->flow_blocks[block->left->vertex];
- if (fblock->left.dst->block != block->left) {
- internal_error(state, 0, "block mismatch");
- }
- fblock->left.out_next = 0;
- *ftail = &fblock->left;
- ftail = &fblock->left.out_next;
- }
- if (block->right) {
- fblock->right.dst = &scc->flow_blocks[block->right->vertex];
- if (fblock->right.dst->block != block->right) {
+
+ fedge = fblock->edges;
+ bedge = block->edges;
+ for(; bedge; bedge = bedge->next, fedge++) {
+ fedge->dst = &scc->flow_blocks[bedge->member->vertex];
+ if (fedge->dst->block != bedge->member) {
internal_error(state, 0, "block mismatch");
}
- fblock->right.out_next = 0;
- *ftail = &fblock->right;
- ftail = &fblock->right.out_next;
+ *ftail = fedge;
+ ftail = &fedge->out_next;
+ fedge->out_next = 0;
}
for(fedge = fblock->out; fedge; fedge = fedge->out_next) {
fedge->src = fblock;
fedge->work_next = fedge->work_prev = fedge;
fedge->executable = 0;
}
+ }
+ ins = ins->next;
+ } while (ins != first);
+ block = 0;
+ fblock = 0;
+ ins = first;
+ do {
+ if ((ins->op == OP_LABEL) && (block != ins->u.block)) {
+ struct flow_edge **ftail;
+ struct block_set *bedge;
+ block = ins->u.block;
+ fblock = &scc->flow_blocks[block->vertex];
ftail = &fblock->in;
for(bedge = block->use; bedge; bedge = bedge->next) {
struct block *src_block;
struct flow_edge *sfedge;
src_block = bedge->member;
sfblock = &scc->flow_blocks[src_block->vertex];
- sfedge = 0;
- if (src_block->left == block) {
- sfedge = &sfblock->left;
- } else {
- sfedge = &sfblock->right;
+ for(sfedge = sfblock->out; sfedge; sfedge = sfedge->out_next) {
+ if (sfedge->dst == fblock) {
+ break;
+ }
+ }
+ if (!sfedge) {
+ internal_error(state, 0, "edge mismatch");
}
*ftail = sfedge;
ftail = &sfedge->in_next;
sfedge->in_next = 0;
}
}
- {
- struct triple_set *edge;
- struct ssa_edge **stail;
- struct lattice_node *lnode;
- lnode = &scc->lattice[ins->id];
- lnode->out = 0;
- stail = &lnode->out;
- for(edge = ins->use; edge; edge = edge->next) {
- struct ssa_edge *sedge;
- ssa_edge_index += 1;
- sedge = &scc->ssa_edges[ssa_edge_index];
- *stail = sedge;
- stail = &sedge->out_next;
- sedge->src = lnode;
- sedge->dst = &scc->lattice[edge->member->id];
- sedge->work_next = sedge->work_prev = sedge;
- sedge->out_next = 0;
- }
- }
ins = ins->next;
} while(ins != first);
/* Setup a dummy block 0 as a node above the start node */
struct flow_edge *fedge;
fblock = &scc->flow_blocks[0];
fblock->block = 0;
+ fblock->edges = xcmalloc(sizeof(*fblock->edges)*1, "flow_edges");
fblock->in = 0;
- fblock->out = &fblock->left;
+ fblock->out = fblock->edges;
dst = &scc->flow_blocks[state->first_block->vertex];
- fedge = &fblock->left;
+ fedge = fblock->edges;
fedge->src = fblock;
fedge->dst = dst;
fedge->work_next = fedge;
scc->ssa_work_list = 0;
scc_add_fedge(state, scc, fedge);
}
-#if DEBUG_SCC
- fprintf(stderr, "ins_index: %d ssa_edge_index: %d fblock_index: %d\n",
- ins_index, ssa_edge_index, fblock_index);
-#endif
+ if (state->compiler->debug & DEBUG_SCC_TRANSFORM) {
+ fprintf(stderr, "ins_index: %d ssa_edge_index: %d fblock_index: %d\n",
+ ins_index, ssa_edge_index, fblock_index);
+ }
}
static void free_scc_state(
struct compile_state *state, struct scc_state *scc)
{
+ int i;
+ for(i = 0; i < state->last_vertex + 1; i++) {
+ struct flow_block *fblock;
+ fblock = &scc->flow_blocks[i];
+ if (fblock->edges) {
+ xfree(fblock->edges);
+ fblock->edges = 0;
+ }
+ }
xfree(scc->flow_blocks);
xfree(scc->ssa_edges);
xfree(scc->lattice);
}
+static void scc_debug_lnode(
+ struct compile_state *state, struct lattice_node *lnode, int changed)
+{
+ if (state->compiler->debug & DEBUG_SCC_TRANSFORM) {
+ FILE *fp = stderr;
+ struct triple *val, **expr;
+ val = lnode->val? lnode->val : lnode->def;
+ fprintf(fp, "%p %s %3d %10s (",
+ lnode->def,
+ ((lnode->def->op == OP_PHI)? "phi: ": "expr:"),
+ lnode->def->id,
+ tops(lnode->def->op));
+ expr = triple_rhs(state, lnode->def, 0);
+ for(;expr;expr = triple_rhs(state, lnode->def, expr)) {
+ if (*expr) {
+ fprintf(fp, " %d", (*expr)->id);
+ }
+ }
+ if (val->op == OP_INTCONST) {
+ fprintf(fp, " <0x%08lx>", (unsigned long)(val->u.cval));
+ }
+ fprintf(fp, " ) -> %s %s\n",
+ ((!lnode->val)? "lo": is_const(lnode->val)? "const": "hi"),
+ changed? "changed" : ""
+ );
+ }
+}
+
static void scc_visit_phi(struct compile_state *state, struct scc_state *scc,
struct lattice_node *lnode)
{
struct lattice_node *tmp;
struct triple **slot, *old;
struct flow_edge *fedge;
+ int changed;
int index;
if (lnode->def->op != OP_PHI) {
internal_error(state, lnode->def, "not phi");
slot = &RHS(lnode->def, 0);
index = 0;
for(fedge = lnode->fblock->in; fedge; index++, fedge = fedge->in_next) {
+ if (state->compiler->debug & DEBUG_SCC_TRANSFORM) {
+ fprintf(stderr, "Examining edge: %d vertex: %d executable: %d\n",
+ index,
+ fedge->dst->block->vertex,
+ fedge->executable
+ );
+ }
if (!fedge->executable) {
continue;
}
break;
}
}
-#if DEBUG_SCC
- fprintf(stderr, "phi: %d -> %s\n",
- lnode->def->id,
- (!lnode->val)? "lo": is_const(lnode->val)? "const": "hi");
-#endif
+ changed = lval_changed(state, old, lnode);
+ scc_debug_lnode(state, lnode, changed);
+
/* If the lattice value has changed update the work lists. */
- if (lval_changed(state, old, lnode)) {
+ if (changed) {
struct ssa_edge *sedge;
for(sedge = lnode->out; sedge; sedge = sedge->out_next) {
scc_add_sedge(state, scc, sedge);
*vexpr = (tmp->val)? tmp->val : tmp->def;
}
}
- if (scratch->op == OP_BRANCH) {
+ if (triple_is_branch(state, scratch)) {
scratch->next = lnode->def->next;
}
/* Recompute the value */
}
if ((scratch->prev != scratch) ||
((scratch->next != scratch) &&
- ((lnode->def->op != OP_BRANCH) ||
+ (!triple_is_branch(state, lnode->def) ||
(scratch->next != lnode->def->next)))) {
internal_error(state, lnode->def, "scratch in list?");
}
/* Find the cases that are always lattice lo */
if (lnode->val &&
triple_is_def(state, lnode->val) &&
- !triple_is_pure(state, lnode->val)) {
+ !triple_is_pure(state, lnode->val, lnode->old_id)) {
lnode->val = 0;
}
- if (lnode->val &&
- (lnode->val->op == OP_SDECL) &&
- (lnode->val != lnode->def)) {
- internal_error(state, lnode->def, "bad sdecl");
- }
/* See if the lattice value has changed */
changed = lval_changed(state, old, lnode);
+ /* See if this value should not change */
+ if (lnode->val &&
+ (( !triple_is_def(state, lnode->def) &&
+ !triple_is_cond_branch(state, lnode->def)) ||
+ (lnode->def->op == OP_PIECE))) {
+#warning "FIXME constant propogate through expressions with multiple left hand sides"
+ if (changed) {
+ internal_warning(state, lnode->def, "non def changes value?");
+ }
+ lnode->val = 0;
+ }
+ /* Report what has just happened */
+ if (state->compiler->debug & DEBUG_SCC_TRANSFORM2) {
+ display_triple_changes(stderr, scratch, lnode->def);
+ }
+
+ /* See if we need to free the scratch value */
if (lnode->val != scratch) {
xfree(scratch);
}
struct lattice_node *lnode)
{
struct lattice_node *cond;
-#if DEBUG_SCC
- {
+ struct flow_edge *left, *right;
+ if (state->compiler->debug & DEBUG_SCC_TRANSFORM) {
struct flow_edge *fedge;
- fprintf(stderr, "branch: %d (",
+ fprintf(stderr, "%s: %d (",
+ tops(lnode->def->op),
lnode->def->id);
for(fedge = lnode->fblock->out; fedge; fedge = fedge->out_next) {
}
fprintf(stderr, "\n");
}
-#endif
- if (lnode->def->op != OP_BRANCH) {
+ if (!triple_is_branch(state, lnode->def)) {
internal_error(state, lnode->def, "not branch");
}
/* This only applies to conditional branches */
- if (TRIPLE_RHS(lnode->def->sizes) == 0) {
+ if (!triple_is_cond_branch(state, lnode->def)) {
return;
}
cond = triple_to_lattice(state, scc, RHS(lnode->def,0));
+ for(left = cond->fblock->out; left; left = left->out_next) {
+ if (left->dst->block->first == lnode->def->next) {
+ break;
+ }
+ }
+ if (!left) {
+ internal_error(state, lnode->def, "Cannot find left branch edge");
+ }
+ for(right = cond->fblock->out; right; right = right->out_next) {
+ if (right->dst->block->first == TARG(lnode->def, 0)) {
+ break;
+ }
+ }
+ if (!right) {
+ internal_error(state, lnode->def, "Cannot find right branch edge");
+ }
if (cond->val && !is_const(cond->val)) {
#warning "FIXME do I need to do something here?"
warning(state, cond->def, "condition not constant?");
return;
}
if (cond->val == 0) {
- scc_add_fedge(state, scc, cond->fblock->out);
- scc_add_fedge(state, scc, cond->fblock->out->out_next);
+ scc_add_fedge(state, scc, left);
+ scc_add_fedge(state, scc, right);
}
else if (cond->val->u.cval) {
- scc_add_fedge(state, scc, cond->fblock->out->out_next);
-
+ scc_add_fedge(state, scc, right);
} else {
- scc_add_fedge(state, scc, cond->fblock->out);
+ scc_add_fedge(state, scc, left);
}
}
int changed;
changed = compute_lnode_val(state, scc, lnode);
-#if DEBUG_SCC
- {
- struct triple **expr;
- fprintf(stderr, "expr: %3d %10s (",
- lnode->def->id, tops(lnode->def->op));
- expr = triple_rhs(state, lnode->def, 0);
- for(;expr;expr = triple_rhs(state, lnode->def, expr)) {
- if (*expr) {
- fprintf(stderr, " %d", (*expr)->id);
- }
- }
- fprintf(stderr, " ) -> %s\n",
- (!lnode->val)? "lo": is_const(lnode->val)? "const": "hi");
- }
-#endif
- if (lnode->def->op == OP_BRANCH) {
- scc_visit_branch(state, scc, lnode);
+ scc_debug_lnode(state, lnode, changed);
+ if (triple_is_branch(state, lnode->def)) {
+ scc_visit_branch(state, scc, lnode);
}
else if (changed) {
struct ssa_edge *sedge;
struct compile_state *state, struct scc_state *scc)
{
struct triple *first, *ins;
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
struct lattice_node *lnode;
lnode = triple_to_lattice(state, scc, ins);
+
+ if (state->compiler->debug & DEBUG_SCC_TRANSFORM) {
+ if (lnode->val &&
+ !is_const(lnode->val) &&
+ !triple_is_uncond_branch(state, lnode->val) &&
+ (lnode->val->op != OP_NOOP))
+ {
+ struct flow_edge *fedge;
+ int executable;
+ executable = 0;
+ for(fedge = lnode->fblock->in;
+ !executable && fedge; fedge = fedge->in_next) {
+ executable |= fedge->executable;
+ }
+ if (executable) {
+ internal_warning(state, lnode->val,
+ "lattice node %d %s->%s still high?",
+ ins->id,
+ tops(lnode->def->op),
+ tops(lnode->val->op));
+ }
+ }
+ }
+
/* Restore id */
ins->id = lnode->old_id;
-#if DEBUG_SCC
- if (lnode->val && !is_const(lnode->val)) {
- warning(state, lnode->def,
- "lattice node still high?");
- }
-#endif
if (lnode->val && (lnode->val != ins)) {
/* See if it something I know how to write back */
switch(lnode->val->op) {
static void scc_transform(struct compile_state *state)
{
struct scc_state scc;
+ if (!(state->compiler->flags & COMPILER_SCC_TRANSFORM)) {
+ return;
+ }
initialize_scc_state(state, &scc);
struct block *block;
struct triple *ptr;
struct flow_block *fblock;
- int time;
+ int reps;
int done;
if (fedge->executable) {
continue;
fedge->executable = 1;
fblock = fedge->dst;
block = fblock->block;
- time = 0;
+ reps = 0;
for(fptr = fblock->in; fptr; fptr = fptr->in_next) {
if (fptr->executable) {
- time++;
+ reps++;
}
}
-#if DEBUG_SCC
- fprintf(stderr, "vertex: %d time: %d\n",
- block->vertex, time);
-#endif
+ if (state->compiler->debug & DEBUG_SCC_TRANSFORM) {
+ fprintf(stderr, "vertex: %d reps: %d\n",
+ block->vertex, reps);
+ }
+
done = 0;
for(ptr = block->first; !done; ptr = ptr->next) {
struct lattice_node *lnode;
if (ptr->op == OP_PHI) {
scc_visit_phi(state, &scc, lnode);
}
- else if (time == 1) {
+ else if (reps == 1) {
scc_visit_expr(state, &scc, lnode);
}
}
- if (fblock->out && !fblock->out->out_next) {
- scc_add_fedge(state, &scc, fblock->out);
+ /* Add unconditional branch edges */
+ if (!triple_is_cond_branch(state, fblock->block->last)) {
+ struct flow_edge *out;
+ for(out = fblock->out; out; out = out->out_next) {
+ scc_add_fedge(state, &scc, out);
+ }
}
}
while((sedge = scc_next_sedge(state, &scc))) {
struct flow_block *fblock;
lnode = sedge->dst;
fblock = lnode->fblock;
-#if DEBUG_SCC
- fprintf(stderr, "sedge: %5d (%5d -> %5d)\n",
- sedge - scc.ssa_edges,
- sedge->src->def->id,
- sedge->dst->def->id);
-#endif
+
+ if (state->compiler->debug & DEBUG_SCC_TRANSFORM) {
+ fprintf(stderr, "sedge: %5d (%5d -> %5d)\n",
+ sedge - scc.ssa_edges,
+ sedge->src->def->id,
+ sedge->dst->def->id);
+ }
+
if (lnode->def->op == OP_PHI) {
scc_visit_phi(state, &scc, lnode);
}
scc_writeback_values(state, &scc);
free_scc_state(state, &scc);
+ rebuild_ssa_form(state);
+
+ print_blocks(state, __func__, stdout);
}
static void transform_to_arch_instructions(struct compile_state *state)
{
struct triple *ins, *first;
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
ins = transform_to_arch_instruction(state, ins);
} while(ins != first);
+
+ print_blocks(state, __func__, stdout);
}
#if DEBUG_CONSISTENCY
{
struct triple *first, *ins;
struct triple_set *set;
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
struct triple **expr;
if (!state->first_block) {
return;
}
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
+ valid_ins(state, ins);
if (triple_stores_block(state, ins)) {
if (!ins->u.block) {
internal_error(state, ins,
}
+
+static int edge_present(struct compile_state *state, struct block *block, struct triple *edge)
+{
+ struct block_set *bedge;
+ struct block *targ;
+ targ = block_of_triple(state, edge);
+ for(bedge = block->edges; bedge; bedge = bedge->next) {
+ if (bedge->member == targ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
static void verify_blocks(struct compile_state *state)
{
struct triple *ins;
struct block *block;
+ int blocks;
block = state->first_block;
if (!block) {
return;
}
+ blocks = 0;
do {
+ int users;
+ struct block_set *user, *edge;
+ blocks++;
for(ins = block->first; ins != block->last->next; ins = ins->next) {
- if (!triple_stores_block(state, ins)) {
+ if (triple_stores_block(state, ins) && (ins->u.block != block)) {
+ internal_error(state, ins, "inconsitent block specified");
+ }
+ valid_ins(state, ins);
+ }
+ users = 0;
+ for(user = block->use; user; user = user->next) {
+ users++;
+ if (!user->member->first) {
+ internal_error(state, block->first, "user is empty");
+ }
+ if ((block == state->last_block) &&
+ (user->member == state->first_block)) {
continue;
}
- if (ins->u.block != block) {
- internal_error(state, ins, "inconsitent block specified");
+ for(edge = user->member->edges; edge; edge = edge->next) {
+ if (edge->member == block) {
+ break;
+ }
+ }
+ if (!edge) {
+ internal_error(state, user->member->first,
+ "user does not use block");
+ }
+ }
+ if (triple_is_branch(state, block->last)) {
+ struct triple **expr;
+ expr = triple_targ(state, block->last, 0);
+ for(;expr; expr = triple_targ(state, block->last, expr)) {
+ if (*expr && !edge_present(state, block, *expr)) {
+ internal_error(state, block->last, "no edge to targ");
+ }
+ }
+ }
+ if (!triple_is_uncond_branch(state, block->last) &&
+ (block != state->last_block) &&
+ !edge_present(state, block, block->last->next)) {
+ internal_error(state, block->last, "no edge to block->last->next");
+ }
+ for(edge = block->edges; edge; edge = edge->next) {
+ for(user = edge->member->use; user; user = user->next) {
+ if (user->member == block) {
+ break;
+ }
+ }
+ if (!user || user->member != block) {
+ internal_error(state, block->first,
+ "block does not use edge");
+ }
+ if (!edge->member->first) {
+ internal_error(state, block->first, "edge block is empty");
}
}
+ if (block->users != users) {
+ internal_error(state, block->first,
+ "computed users %d != stored users %d\n",
+ users, block->users);
+ }
if (!triple_stores_block(state, block->last->next)) {
internal_error(state, block->last->next,
"cannot find next block");
"bad next block");
}
} while(block != state->first_block);
+ if (blocks != state->last_vertex) {
+ internal_error(state, 0, "computed blocks != stored blocks %d\n",
+ blocks, state->last_vertex);
+ }
}
static void verify_domination(struct compile_state *state)
return;
}
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
for(set = ins->use; set; set = set->next) {
- struct triple **expr;
- if (set->member->op == OP_PHI) {
- continue;
- }
- /* See if the use is on the righ hand side */
- expr = triple_rhs(state, set->member, 0);
- for(; expr ; expr = triple_rhs(state, set->member, expr)) {
- if (*expr == ins) {
+ struct triple **slot;
+ struct triple *use_point;
+ int i, zrhs;
+ use_point = 0;
+ zrhs = TRIPLE_RHS(set->member->sizes);
+ slot = &RHS(set->member, 0);
+ /* See if the use is on the right hand side */
+ for(i = 0; i < zrhs; i++) {
+ if (slot[i] == ins) {
break;
}
}
- if (expr &&
- !tdominates(state, ins, set->member)) {
- internal_error(state, set->member,
- "non dominated rhs use?");
+ if (i < zrhs) {
+ use_point = set->member;
+ if (set->member->op == OP_PHI) {
+ struct block_set *bset;
+ int edge;
+ bset = set->member->u.block->use;
+ for(edge = 0; bset && (edge < i); edge++) {
+ bset = bset->next;
+ }
+ if (!bset) {
+ internal_error(state, set->member,
+ "no edge for phi rhs %d\n", i);
+ }
+ use_point = bset->member->last;
+ }
+ }
+ if (use_point &&
+ !tdominates(state, ins, use_point)) {
+ internal_error(state, use_point,
+ "non dominated rhs use point?");
+ }
+ }
+ ins = ins->next;
+ } while(ins != first);
+}
+
+static void verify_rhs(struct compile_state *state)
+{
+ struct triple *first, *ins;
+ first = state->first;
+ ins = first;
+ do {
+ struct triple **slot;
+ int zrhs, i;
+ zrhs = TRIPLE_RHS(ins->sizes);
+ slot = &RHS(ins, 0);
+ for(i = 0; i < zrhs; i++) {
+ if (slot[i] == 0) {
+ internal_error(state, ins,
+ "missing rhs %d on %s",
+ i, tops(ins->op));
+ }
+ if ((ins->op != OP_PHI) && (slot[i] == ins)) {
+ internal_error(state, ins,
+ "ins == rhs[%d] on %s",
+ i, tops(ins->op));
}
}
ins = ins->next;
static void verify_piece(struct compile_state *state)
{
struct triple *first, *ins;
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
struct triple *ptr;
int lhs, i;
lhs = TRIPLE_LHS(ins->sizes);
- if ((ins->op == OP_WRITE) || (ins->op == OP_STORE)) {
- lhs = 0;
- }
for(ptr = ins->next, i = 0; i < lhs; i++, ptr = ptr->next) {
if (ptr != LHS(ins, i)) {
internal_error(state, ins, "malformed lhs on %s",
ins = ins->next;
} while(ins != first);
}
+
static void verify_ins_colors(struct compile_state *state)
{
struct triple *first, *ins;
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
ins = ins->next;
verify_blocks_present(state);
verify_blocks(state);
verify_domination(state);
+ verify_rhs(state);
verify_piece(state);
verify_ins_colors(state);
}
#else
static void verify_consistency(struct compile_state *state) {}
-#endif /* DEBUG_USES */
+#endif /* DEBUG_CONSISTENCY */
static void optimize(struct compile_state *state)
{
- if (state->debug & DEBUG_TRIPLES) {
- print_triples(state);
- }
+ /* Dump what the instruction graph intially looks like */
+ print_triples(state);
+
/* Replace structures with simpler data types */
flatten_structures(state);
- if (state->debug & DEBUG_TRIPLES) {
- print_triples(state);
- }
+ print_triples(state);
+
verify_consistency(state);
/* Analize the intermediate code */
- setup_basic_blocks(state);
- analyze_idominators(state);
- analyze_ipdominators(state);
-
- /* Transform the code to ssa form */
+ analyze_basic_blocks(state);
+
+ /* Transform the code to ssa form. */
+ /*
+ * The transformation to ssa form puts a phi function
+ * on each of edge of a dominance frontier where that
+ * phi function might be needed. At -O2 if we don't
+ * eleminate the excess phi functions we can get an
+ * exponential code size growth. So I kill the extra
+ * phi functions early and I kill them often.
+ */
transform_to_ssa_form(state);
verify_consistency(state);
- if (state->debug & DEBUG_CODE_ELIMINATION) {
- fprintf(stdout, "After transform_to_ssa_form\n");
- print_blocks(state, stdout);
- }
+
+ /* Remove dead code */
+ eliminate_inefectual_code(state);
+ verify_consistency(state);
+
/* Do strength reduction and simple constant optimizations */
- if (state->optimize >= 1) {
- simplify_all(state);
- }
+ simplify_all(state);
verify_consistency(state);
/* Propogate constants throughout the code */
- if (state->optimize >= 2) {
-#warning "FIXME fix scc_transform"
- scc_transform(state);
- transform_from_ssa_form(state);
- free_basic_blocks(state);
- setup_basic_blocks(state);
- analyze_idominators(state);
- analyze_ipdominators(state);
- transform_to_ssa_form(state);
- }
+ scc_transform(state);
verify_consistency(state);
#warning "WISHLIST implement single use constants (least possible register pressure)"
#warning "WISHLIST implement induction variable elimination"
*/
transform_to_arch_instructions(state);
verify_consistency(state);
- if (state->debug & DEBUG_ARCH_CODE) {
- printf("After transform_to_arch_instructions\n");
- print_blocks(state, stdout);
- print_control_flow(state);
- }
+
+ /* Remove dead code */
eliminate_inefectual_code(state);
verify_consistency(state);
- if (state->debug & DEBUG_CODE_ELIMINATION) {
- printf("After eliminate_inefectual_code\n");
- print_blocks(state, stdout);
- print_control_flow(state);
- }
- verify_consistency(state);
+
/* Color all of the variables to see if they will fit in registers */
insert_copies_to_phi(state);
- if (state->debug & DEBUG_INSERTED_COPIES) {
- printf("After insert_copies_to_phi\n");
- print_blocks(state, stdout);
- print_control_flow(state);
- }
verify_consistency(state);
+
insert_mandatory_copies(state);
- if (state->debug & DEBUG_INSERTED_COPIES) {
- printf("After insert_mandatory_copies\n");
- print_blocks(state, stdout);
- print_control_flow(state);
- }
verify_consistency(state);
+
allocate_registers(state);
verify_consistency(state);
- if (state->debug & DEBUG_INTERMEDIATE_CODE) {
- print_blocks(state, stdout);
- }
- if (state->debug & DEBUG_CONTROL_FLOW) {
- print_control_flow(state);
- }
+
/* Remove the optimization information.
* This is more to check for memory consistency than to free memory.
*/
*/
#define X86_4_8BIT_GPRS 1
-/* Recognized x86 cpu variants */
-#define BAD_CPU 0
-#define CPU_I386 1
-#define CPU_P3 2
-#define CPU_P4 3
-#define CPU_K7 4
-#define CPU_K8 5
-
-#define CPU_DEFAULT CPU_I386
+/* x86 featrues */
+#define X86_MMX_REGS (1<<0)
+#define X86_XMM_REGS (1<<1)
/* The x86 register classes */
-#define REGC_FLAGS 0
-#define REGC_GPR8 1
-#define REGC_GPR16 2
-#define REGC_GPR32 3
-#define REGC_GPR64 4
-#define REGC_MMX 5
-#define REGC_XMM 6
-#define REGC_GPR32_8 7
-#define REGC_GPR16_8 8
-#define REGC_IMM32 9
-#define REGC_IMM16 10
-#define REGC_IMM8 11
+#define REGC_FLAGS 0
+#define REGC_GPR8 1
+#define REGC_GPR16 2
+#define REGC_GPR32 3
+#define REGC_DIVIDEND64 4
+#define REGC_DIVIDEND32 5
+#define REGC_MMX 6
+#define REGC_XMM 7
+#define REGC_GPR32_8 8
+#define REGC_GPR16_8 9
+#define REGC_GPR8_LO 10
+#define REGC_IMM32 11
+#define REGC_IMM16 12
+#define REGC_IMM8 13
#define LAST_REGC REGC_IMM8
#if LAST_REGC >= MAX_REGC
#error "MAX_REGC is to low"
#endif
/* Register class masks */
-#define REGCM_FLAGS (1 << REGC_FLAGS)
-#define REGCM_GPR8 (1 << REGC_GPR8)
-#define REGCM_GPR16 (1 << REGC_GPR16)
-#define REGCM_GPR32 (1 << REGC_GPR32)
-#define REGCM_GPR64 (1 << REGC_GPR64)
-#define REGCM_MMX (1 << REGC_MMX)
-#define REGCM_XMM (1 << REGC_XMM)
-#define REGCM_GPR32_8 (1 << REGC_GPR32_8)
-#define REGCM_GPR16_8 (1 << REGC_GPR16_8)
-#define REGCM_IMM32 (1 << REGC_IMM32)
-#define REGCM_IMM16 (1 << REGC_IMM16)
-#define REGCM_IMM8 (1 << REGC_IMM8)
-#define REGCM_ALL ((1 << (LAST_REGC + 1)) - 1)
+#define REGCM_FLAGS (1 << REGC_FLAGS)
+#define REGCM_GPR8 (1 << REGC_GPR8)
+#define REGCM_GPR16 (1 << REGC_GPR16)
+#define REGCM_GPR32 (1 << REGC_GPR32)
+#define REGCM_DIVIDEND64 (1 << REGC_DIVIDEND64)
+#define REGCM_DIVIDEND32 (1 << REGC_DIVIDEND32)
+#define REGCM_MMX (1 << REGC_MMX)
+#define REGCM_XMM (1 << REGC_XMM)
+#define REGCM_GPR32_8 (1 << REGC_GPR32_8)
+#define REGCM_GPR16_8 (1 << REGC_GPR16_8)
+#define REGCM_GPR8_LO (1 << REGC_GPR8_LO)
+#define REGCM_IMM32 (1 << REGC_IMM32)
+#define REGCM_IMM16 (1 << REGC_IMM16)
+#define REGCM_IMM8 (1 << REGC_IMM8)
+#define REGCM_ALL ((1 << (LAST_REGC + 1)) - 1)
/* The x86 registers */
#define REG_EFLAGS 2
#define REG_BH 8
#define REG_CH 9
#define REG_DH 10
+#define REGC_GPR8_LO_FIRST REG_AL
+#define REGC_GPR8_LO_LAST REG_DL
#define REGC_GPR8_FIRST REG_AL
-#if X86_4_8BIT_GPRS
-#define REGC_GPR8_LAST REG_DL
-#else
#define REGC_GPR8_LAST REG_DH
-#endif
#define REG_AX 11
#define REG_BX 12
#define REG_CX 13
#define REGC_GPR32_FIRST REG_EAX
#define REGC_GPR32_LAST REG_ESP
#define REG_EDXEAX 27
-#define REGC_GPR64_FIRST REG_EDXEAX
-#define REGC_GPR64_LAST REG_EDXEAX
-#define REG_MMX0 28
-#define REG_MMX1 29
-#define REG_MMX2 30
-#define REG_MMX3 31
-#define REG_MMX4 32
-#define REG_MMX5 33
-#define REG_MMX6 34
-#define REG_MMX7 35
+#define REGC_DIVIDEND64_FIRST REG_EDXEAX
+#define REGC_DIVIDEND64_LAST REG_EDXEAX
+#define REG_DXAX 28
+#define REGC_DIVIDEND32_FIRST REG_DXAX
+#define REGC_DIVIDEND32_LAST REG_DXAX
+#define REG_MMX0 29
+#define REG_MMX1 30
+#define REG_MMX2 31
+#define REG_MMX3 32
+#define REG_MMX4 33
+#define REG_MMX5 34
+#define REG_MMX6 35
+#define REG_MMX7 36
#define REGC_MMX_FIRST REG_MMX0
#define REGC_MMX_LAST REG_MMX7
-#define REG_XMM0 36
-#define REG_XMM1 37
-#define REG_XMM2 38
-#define REG_XMM3 39
-#define REG_XMM4 40
-#define REG_XMM5 41
-#define REG_XMM6 42
-#define REG_XMM7 43
+#define REG_XMM0 37
+#define REG_XMM1 38
+#define REG_XMM2 39
+#define REG_XMM3 40
+#define REG_XMM4 41
+#define REG_XMM5 42
+#define REG_XMM6 43
+#define REG_XMM7 44
#define REGC_XMM_FIRST REG_XMM0
#define REGC_XMM_LAST REG_XMM7
#warning "WISHLIST figure out how to use pinsrw and pextrw to better use extended regs"
static unsigned regc_size[LAST_REGC +1] = {
- [REGC_FLAGS] = REGC_FLAGS_LAST - REGC_FLAGS_FIRST + 1,
- [REGC_GPR8] = REGC_GPR8_LAST - REGC_GPR8_FIRST + 1,
- [REGC_GPR16] = REGC_GPR16_LAST - REGC_GPR16_FIRST + 1,
- [REGC_GPR32] = REGC_GPR32_LAST - REGC_GPR32_FIRST + 1,
- [REGC_GPR64] = REGC_GPR64_LAST - REGC_GPR64_FIRST + 1,
- [REGC_MMX] = REGC_MMX_LAST - REGC_MMX_FIRST + 1,
- [REGC_XMM] = REGC_XMM_LAST - REGC_XMM_FIRST + 1,
- [REGC_GPR32_8] = REGC_GPR32_8_LAST - REGC_GPR32_8_FIRST + 1,
- [REGC_GPR16_8] = REGC_GPR16_8_LAST - REGC_GPR16_8_FIRST + 1,
- [REGC_IMM32] = 0,
- [REGC_IMM16] = 0,
- [REGC_IMM8] = 0,
+ [REGC_FLAGS] = REGC_FLAGS_LAST - REGC_FLAGS_FIRST + 1,
+ [REGC_GPR8] = REGC_GPR8_LAST - REGC_GPR8_FIRST + 1,
+ [REGC_GPR16] = REGC_GPR16_LAST - REGC_GPR16_FIRST + 1,
+ [REGC_GPR32] = REGC_GPR32_LAST - REGC_GPR32_FIRST + 1,
+ [REGC_DIVIDEND64] = REGC_DIVIDEND64_LAST - REGC_DIVIDEND64_FIRST + 1,
+ [REGC_DIVIDEND32] = REGC_DIVIDEND32_LAST - REGC_DIVIDEND32_FIRST + 1,
+ [REGC_MMX] = REGC_MMX_LAST - REGC_MMX_FIRST + 1,
+ [REGC_XMM] = REGC_XMM_LAST - REGC_XMM_FIRST + 1,
+ [REGC_GPR32_8] = REGC_GPR32_8_LAST - REGC_GPR32_8_FIRST + 1,
+ [REGC_GPR16_8] = REGC_GPR16_8_LAST - REGC_GPR16_8_FIRST + 1,
+ [REGC_GPR8_LO] = REGC_GPR8_LO_LAST - REGC_GPR8_LO_FIRST + 1,
+ [REGC_IMM32] = 0,
+ [REGC_IMM16] = 0,
+ [REGC_IMM8] = 0,
};
static const struct {
int first, last;
} regcm_bound[LAST_REGC + 1] = {
- [REGC_FLAGS] = { REGC_FLAGS_FIRST, REGC_FLAGS_LAST },
- [REGC_GPR8] = { REGC_GPR8_FIRST, REGC_GPR8_LAST },
- [REGC_GPR16] = { REGC_GPR16_FIRST, REGC_GPR16_LAST },
- [REGC_GPR32] = { REGC_GPR32_FIRST, REGC_GPR32_LAST },
- [REGC_GPR64] = { REGC_GPR64_FIRST, REGC_GPR64_LAST },
- [REGC_MMX] = { REGC_MMX_FIRST, REGC_MMX_LAST },
- [REGC_XMM] = { REGC_XMM_FIRST, REGC_XMM_LAST },
- [REGC_GPR32_8] = { REGC_GPR32_8_FIRST, REGC_GPR32_8_LAST },
- [REGC_GPR16_8] = { REGC_GPR16_8_FIRST, REGC_GPR16_8_LAST },
- [REGC_IMM32] = { REGC_IMM32_FIRST, REGC_IMM32_LAST },
- [REGC_IMM16] = { REGC_IMM16_FIRST, REGC_IMM16_LAST },
- [REGC_IMM8] = { REGC_IMM8_FIRST, REGC_IMM8_LAST },
+ [REGC_FLAGS] = { REGC_FLAGS_FIRST, REGC_FLAGS_LAST },
+ [REGC_GPR8] = { REGC_GPR8_FIRST, REGC_GPR8_LAST },
+ [REGC_GPR16] = { REGC_GPR16_FIRST, REGC_GPR16_LAST },
+ [REGC_GPR32] = { REGC_GPR32_FIRST, REGC_GPR32_LAST },
+ [REGC_DIVIDEND64] = { REGC_DIVIDEND64_FIRST, REGC_DIVIDEND64_LAST },
+ [REGC_DIVIDEND32] = { REGC_DIVIDEND32_FIRST, REGC_DIVIDEND32_LAST },
+ [REGC_MMX] = { REGC_MMX_FIRST, REGC_MMX_LAST },
+ [REGC_XMM] = { REGC_XMM_FIRST, REGC_XMM_LAST },
+ [REGC_GPR32_8] = { REGC_GPR32_8_FIRST, REGC_GPR32_8_LAST },
+ [REGC_GPR16_8] = { REGC_GPR16_8_FIRST, REGC_GPR16_8_LAST },
+ [REGC_GPR8_LO] = { REGC_GPR8_LO_FIRST, REGC_GPR8_LO_LAST },
+ [REGC_IMM32] = { REGC_IMM32_FIRST, REGC_IMM32_LAST },
+ [REGC_IMM16] = { REGC_IMM16_FIRST, REGC_IMM16_LAST },
+ [REGC_IMM8] = { REGC_IMM8_FIRST, REGC_IMM8_LAST },
};
-static int arch_encode_cpu(const char *cpu)
-{
- struct cpu {
- const char *name;
- int cpu;
- } cpus[] = {
- { "i386", CPU_I386 },
- { "p3", CPU_P3 },
- { "p4", CPU_P4 },
- { "k7", CPU_K7 },
- { "k8", CPU_K8 },
- { 0, BAD_CPU }
+static void init_arch_state(struct arch_state *arch)
+{
+ memset(arch, 0, sizeof(*arch));
+ arch->features = 0;
+}
+
+static int arch_encode_flag(struct arch_state *arch, const char *flag)
+{
+ static const struct compiler_flag flags[] = {
+ { "mmx", X86_MMX_REGS },
+ { "sse", X86_XMM_REGS },
+ { 0, 0 },
};
- struct cpu *ptr;
- for(ptr = cpus; ptr->name; ptr++) {
- if (strcmp(ptr->name, cpu) == 0) {
- break;
- }
+ static const struct compiler_flag cpus[] = {
+ { "i386", 0 },
+ { "p2", X86_MMX_REGS },
+ { "p3", X86_MMX_REGS | X86_XMM_REGS },
+ { "p4", X86_MMX_REGS | X86_XMM_REGS },
+ { "k7", X86_MMX_REGS },
+ { "k8", X86_MMX_REGS | X86_XMM_REGS },
+ { "c3", X86_MMX_REGS },
+ { "c3-2", X86_MMX_REGS | X86_XMM_REGS }, /* Nehemiah */
+ { 0, 0 }
+ };
+ int result;
+ int act;
+
+ act = 1;
+ result = -1;
+ if (strncmp(flag, "no-", 3) == 0) {
+ flag += 3;
+ act = 0;
+ }
+ if (act && strncmp(flag, "cpu=", 4) == 0) {
+ flag += 4;
+ result = set_flag(cpus, &arch->features, 1, flag);
+ }
+ else {
+ result = set_flag(flags, &arch->features, act, flag);
}
- return ptr->cpu;
+ return result;
}
static unsigned arch_regc_size(struct compile_state *state, int class)
static int arch_regcm_intersect(unsigned regcm1, unsigned regcm2)
{
/* See if two register classes may have overlapping registers */
- unsigned gpr_mask = REGCM_GPR8 | REGCM_GPR16_8 | REGCM_GPR16 |
- REGCM_GPR32_8 | REGCM_GPR32 | REGCM_GPR64;
+ unsigned gpr_mask = REGCM_GPR8 | REGCM_GPR8_LO | REGCM_GPR16_8 | REGCM_GPR16 |
+ REGCM_GPR32_8 | REGCM_GPR32 |
+ REGCM_DIVIDEND32 | REGCM_DIVIDEND64;
/* Special case for the immediates */
if ((regcm1 & (REGCM_IMM32 | REGCM_IMM16 | REGCM_IMM8)) &&
#endif
*equiv++ = REG_AX;
*equiv++ = REG_EAX;
+ *equiv++ = REG_DXAX;
*equiv++ = REG_EDXEAX;
break;
case REG_AH:
#endif
*equiv++ = REG_AX;
*equiv++ = REG_EAX;
+ *equiv++ = REG_DXAX;
*equiv++ = REG_EDXEAX;
break;
case REG_BL:
#endif
*equiv++ = REG_DX;
*equiv++ = REG_EDX;
+ *equiv++ = REG_DXAX;
*equiv++ = REG_EDXEAX;
break;
case REG_DH:
#endif
*equiv++ = REG_DX;
*equiv++ = REG_EDX;
+ *equiv++ = REG_DXAX;
*equiv++ = REG_EDXEAX;
break;
case REG_AX:
*equiv++ = REG_AL;
*equiv++ = REG_AH;
*equiv++ = REG_EAX;
+ *equiv++ = REG_DXAX;
*equiv++ = REG_EDXEAX;
break;
case REG_BX:
*equiv++ = REG_DL;
*equiv++ = REG_DH;
*equiv++ = REG_EDX;
+ *equiv++ = REG_DXAX;
*equiv++ = REG_EDXEAX;
break;
case REG_SI:
*equiv++ = REG_AL;
*equiv++ = REG_AH;
*equiv++ = REG_AX;
+ *equiv++ = REG_DXAX;
*equiv++ = REG_EDXEAX;
break;
case REG_EBX:
*equiv++ = REG_DL;
*equiv++ = REG_DH;
*equiv++ = REG_DX;
+ *equiv++ = REG_DXAX;
*equiv++ = REG_EDXEAX;
break;
case REG_ESI:
case REG_ESP:
*equiv++ = REG_SP;
break;
+ case REG_DXAX:
+ *equiv++ = REG_AL;
+ *equiv++ = REG_AH;
+ *equiv++ = REG_DL;
+ *equiv++ = REG_DH;
+ *equiv++ = REG_AX;
+ *equiv++ = REG_DX;
+ *equiv++ = REG_EAX;
+ *equiv++ = REG_EDX;
+ *equiv++ = REG_EDXEAX;
+ break;
case REG_EDXEAX:
*equiv++ = REG_AL;
*equiv++ = REG_AH;
*equiv++ = REG_DX;
*equiv++ = REG_EAX;
*equiv++ = REG_EDX;
+ *equiv++ = REG_DXAX;
break;
}
*equiv++ = REG_UNSET;
static unsigned arch_avail_mask(struct compile_state *state)
{
unsigned avail_mask;
- avail_mask = REGCM_GPR8 | REGCM_GPR16_8 | REGCM_GPR16 |
- REGCM_GPR32 | REGCM_GPR32_8 | REGCM_GPR64 |
+ /* REGCM_GPR8 is not available */
+ avail_mask = REGCM_GPR8_LO | REGCM_GPR16_8 | REGCM_GPR16 |
+ REGCM_GPR32 | REGCM_GPR32_8 |
+ REGCM_DIVIDEND32 | REGCM_DIVIDEND64 |
REGCM_IMM32 | REGCM_IMM16 | REGCM_IMM8 | REGCM_FLAGS;
- switch(state->cpu) {
- case CPU_P3:
- case CPU_K7:
+ if (state->arch->features & X86_MMX_REGS) {
avail_mask |= REGCM_MMX;
- break;
- case CPU_P4:
- case CPU_K8:
- avail_mask |= REGCM_MMX | REGCM_XMM;
- break;
}
-#if 0
- /* Don't enable 8 bit values until I can force both operands
- * to be 8bits simultaneously.
- */
- avail_mask &= ~(REGCM_GPR8 | REGCM_GPR16_8 | REGCM_GPR16);
-#endif
+ if (state->arch->features & X86_XMM_REGS) {
+ avail_mask |= REGCM_XMM;
+ }
return avail_mask;
}
unsigned mask, result;
int class, class2;
result = regcm;
- result &= arch_avail_mask(state);
for(class = 0, mask = 1; mask; mask <<= 1, class++) {
if ((result & mask) == 0) {
}
}
}
+ result &= arch_avail_mask(state);
return result;
}
unsigned int mask;
unsigned int reg;
} constraints[] = {
- { 'r', REGCM_GPR32, REG_UNSET },
- { 'g', REGCM_GPR32, REG_UNSET },
- { 'p', REGCM_GPR32, REG_UNSET },
- { 'q', REGCM_GPR8, REG_UNSET },
+ { 'r', REGCM_GPR32, REG_UNSET },
+ { 'g', REGCM_GPR32, REG_UNSET },
+ { 'p', REGCM_GPR32, REG_UNSET },
+ { 'q', REGCM_GPR8_LO, REG_UNSET },
{ 'Q', REGCM_GPR32_8, REG_UNSET },
- { 'x', REGCM_XMM, REG_UNSET },
- { 'y', REGCM_MMX, REG_UNSET },
- { 'a', REGCM_GPR32, REG_EAX },
- { 'b', REGCM_GPR32, REG_EBX },
- { 'c', REGCM_GPR32, REG_ECX },
- { 'd', REGCM_GPR32, REG_EDX },
- { 'D', REGCM_GPR32, REG_EDI },
- { 'S', REGCM_GPR32, REG_ESI },
+ { 'x', REGCM_XMM, REG_UNSET },
+ { 'y', REGCM_MMX, REG_UNSET },
+ { 'a', REGCM_GPR32, REG_EAX },
+ { 'b', REGCM_GPR32, REG_EBX },
+ { 'c', REGCM_GPR32, REG_ECX },
+ { 'd', REGCM_GPR32, REG_EDX },
+ { 'D', REGCM_GPR32, REG_EDI },
+ { 'S', REGCM_GPR32, REG_ESI },
{ '\0', 0, REG_UNSET },
};
unsigned int regcm;
for(i = REGC_GPR8_FIRST; (reg == REG_UNSET) && (i <= REGC_GPR8_LAST); i++) {
reg = do_select_reg(state, used, i, classes);
}
- for(i = REGC_GPR64_FIRST; (reg == REG_UNSET) && (i <= REGC_GPR64_LAST); i++) {
+ for(i = REGC_GPR8_LO_FIRST; (reg == REG_UNSET) && (i <= REGC_GPR8_LO_LAST); i++) {
+ reg = do_select_reg(state, used, i, classes);
+ }
+ for(i = REGC_DIVIDEND32_FIRST; (reg == REG_UNSET) && (i <= REGC_DIVIDEND32_LAST); i++) {
+ reg = do_select_reg(state, used, i, classes);
+ }
+ for(i = REGC_DIVIDEND64_FIRST; (reg == REG_UNSET) && (i <= REGC_DIVIDEND64_LAST); i++) {
reg = do_select_reg(state, used, i, classes);
}
for(i = REGC_FLAGS_FIRST; (reg == REG_UNSET) && (i <= REGC_FLAGS_LAST); i++) {
break;
case TYPE_CHAR:
case TYPE_UCHAR:
- mask = REGCM_GPR8 |
+ mask = REGCM_GPR8 | REGCM_GPR8_LO |
REGCM_GPR16 | REGCM_GPR16_8 |
REGCM_GPR32 | REGCM_GPR32_8 |
- REGCM_GPR64 |
+ REGCM_DIVIDEND32 | REGCM_DIVIDEND64 |
REGCM_MMX | REGCM_XMM |
REGCM_IMM32 | REGCM_IMM16 | REGCM_IMM8;
break;
case TYPE_USHORT:
mask = REGCM_GPR16 | REGCM_GPR16_8 |
REGCM_GPR32 | REGCM_GPR32_8 |
- REGCM_GPR64 |
+ REGCM_DIVIDEND32 | REGCM_DIVIDEND64 |
REGCM_MMX | REGCM_XMM |
REGCM_IMM32 | REGCM_IMM16;
break;
case TYPE_ULONG:
case TYPE_POINTER:
mask = REGCM_GPR32 | REGCM_GPR32_8 |
- REGCM_GPR64 | REGCM_MMX | REGCM_XMM |
+ REGCM_DIVIDEND32 | REGCM_DIVIDEND64 |
+ REGCM_MMX | REGCM_XMM |
REGCM_IMM32;
break;
default:
return 1;
}
-#define TEMPLATE_NOP 0
-#define TEMPLATE_INTCONST8 1
-#define TEMPLATE_INTCONST32 2
-#define TEMPLATE_COPY8_REG 3
-#define TEMPLATE_COPY16_REG 4
-#define TEMPLATE_COPY32_REG 5
-#define TEMPLATE_COPY_IMM8 6
-#define TEMPLATE_COPY_IMM16 7
-#define TEMPLATE_COPY_IMM32 8
-#define TEMPLATE_PHI8 9
-#define TEMPLATE_PHI16 10
-#define TEMPLATE_PHI32 11
-#define TEMPLATE_STORE8 12
-#define TEMPLATE_STORE16 13
-#define TEMPLATE_STORE32 14
-#define TEMPLATE_LOAD8 15
-#define TEMPLATE_LOAD16 16
-#define TEMPLATE_LOAD32 17
-#define TEMPLATE_BINARY_REG 18
-#define TEMPLATE_BINARY_IMM 19
-#define TEMPLATE_SL_CL 20
-#define TEMPLATE_SL_IMM 21
-#define TEMPLATE_UNARY 22
-#define TEMPLATE_CMP_REG 23
-#define TEMPLATE_CMP_IMM 24
-#define TEMPLATE_TEST 25
-#define TEMPLATE_SET 26
-#define TEMPLATE_JMP 27
-#define TEMPLATE_INB_DX 28
-#define TEMPLATE_INB_IMM 29
-#define TEMPLATE_INW_DX 30
-#define TEMPLATE_INW_IMM 31
-#define TEMPLATE_INL_DX 32
-#define TEMPLATE_INL_IMM 33
-#define TEMPLATE_OUTB_DX 34
-#define TEMPLATE_OUTB_IMM 35
-#define TEMPLATE_OUTW_DX 36
-#define TEMPLATE_OUTW_IMM 37
-#define TEMPLATE_OUTL_DX 38
-#define TEMPLATE_OUTL_IMM 39
-#define TEMPLATE_BSF 40
-#define TEMPLATE_RDMSR 41
-#define TEMPLATE_WRMSR 42
-#define TEMPLATE_UMUL 43
-#define TEMPLATE_DIV 44
-#define TEMPLATE_MOD 45
-#define LAST_TEMPLATE TEMPLATE_MOD
+#define TEMPLATE_NOP 0
+#define TEMPLATE_INTCONST8 1
+#define TEMPLATE_INTCONST32 2
+#define TEMPLATE_COPY8_REG 3
+#define TEMPLATE_COPY16_REG 4
+#define TEMPLATE_COPY32_REG 5
+#define TEMPLATE_COPY_IMM8 6
+#define TEMPLATE_COPY_IMM16 7
+#define TEMPLATE_COPY_IMM32 8
+#define TEMPLATE_PHI8 9
+#define TEMPLATE_PHI16 10
+#define TEMPLATE_PHI32 11
+#define TEMPLATE_STORE8 12
+#define TEMPLATE_STORE16 13
+#define TEMPLATE_STORE32 14
+#define TEMPLATE_LOAD8 15
+#define TEMPLATE_LOAD16 16
+#define TEMPLATE_LOAD32 17
+#define TEMPLATE_BINARY8_REG 18
+#define TEMPLATE_BINARY16_REG 19
+#define TEMPLATE_BINARY32_REG 20
+#define TEMPLATE_BINARY8_IMM 21
+#define TEMPLATE_BINARY16_IMM 22
+#define TEMPLATE_BINARY32_IMM 23
+#define TEMPLATE_SL8_CL 24
+#define TEMPLATE_SL16_CL 25
+#define TEMPLATE_SL32_CL 26
+#define TEMPLATE_SL8_IMM 27
+#define TEMPLATE_SL16_IMM 28
+#define TEMPLATE_SL32_IMM 29
+#define TEMPLATE_UNARY8 30
+#define TEMPLATE_UNARY16 31
+#define TEMPLATE_UNARY32 32
+#define TEMPLATE_CMP8_REG 33
+#define TEMPLATE_CMP16_REG 34
+#define TEMPLATE_CMP32_REG 35
+#define TEMPLATE_CMP8_IMM 36
+#define TEMPLATE_CMP16_IMM 37
+#define TEMPLATE_CMP32_IMM 38
+#define TEMPLATE_TEST8 39
+#define TEMPLATE_TEST16 40
+#define TEMPLATE_TEST32 41
+#define TEMPLATE_SET 42
+#define TEMPLATE_JMP 43
+#define TEMPLATE_RET 44
+#define TEMPLATE_INB_DX 45
+#define TEMPLATE_INB_IMM 46
+#define TEMPLATE_INW_DX 47
+#define TEMPLATE_INW_IMM 48
+#define TEMPLATE_INL_DX 49
+#define TEMPLATE_INL_IMM 50
+#define TEMPLATE_OUTB_DX 51
+#define TEMPLATE_OUTB_IMM 52
+#define TEMPLATE_OUTW_DX 53
+#define TEMPLATE_OUTW_IMM 54
+#define TEMPLATE_OUTL_DX 55
+#define TEMPLATE_OUTL_IMM 56
+#define TEMPLATE_BSF 57
+#define TEMPLATE_RDMSR 58
+#define TEMPLATE_WRMSR 59
+#define TEMPLATE_UMUL8 60
+#define TEMPLATE_UMUL16 61
+#define TEMPLATE_UMUL32 62
+#define TEMPLATE_DIV8 63
+#define TEMPLATE_DIV16 64
+#define TEMPLATE_DIV32 65
+#define LAST_TEMPLATE TEMPLATE_DIV32
#if LAST_TEMPLATE >= MAX_TEMPLATES
#error "MAX_TEMPLATES to low"
#endif
-#define COPY8_REGCM (REGCM_GPR64 | REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8 | REGCM_MMX | REGCM_XMM)
-#define COPY16_REGCM (REGCM_GPR64 | REGCM_GPR32 | REGCM_GPR16 | REGCM_MMX | REGCM_XMM)
-#define COPY32_REGCM (REGCM_GPR64 | REGCM_GPR32 | REGCM_MMX | REGCM_XMM)
-#define COPYIMM8_REGCM (REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8)
-#define COPYIMM16_REGCM (REGCM_GPR32 | REGCM_GPR16)
-#define COPYIMM32_REGCM (REGCM_GPR32)
+#define COPY8_REGCM (REGCM_DIVIDEND64 | REGCM_DIVIDEND32 | REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8_LO | REGCM_MMX | REGCM_XMM)
+#define COPY16_REGCM (REGCM_DIVIDEND64 | REGCM_DIVIDEND32 | REGCM_GPR32 | REGCM_GPR16 | REGCM_MMX | REGCM_XMM)
+#define COPY32_REGCM (REGCM_DIVIDEND64 | REGCM_DIVIDEND32 | REGCM_GPR32 | REGCM_MMX | REGCM_XMM)
static struct ins_template templates[] = {
.rhs = { [0] = { REG_UNSET, COPY32_REGCM } },
},
[TEMPLATE_COPY_IMM8] = {
- .lhs = { [0] = { REG_UNSET, COPYIMM8_REGCM } },
+ .lhs = { [0] = { REG_UNSET, COPY8_REGCM } },
.rhs = { [0] = { REG_UNNEEDED, REGCM_IMM8 } },
},
[TEMPLATE_COPY_IMM16] = {
- .lhs = { [0] = { REG_UNSET, COPYIMM16_REGCM } },
+ .lhs = { [0] = { REG_UNSET, COPY16_REGCM } },
.rhs = { [0] = { REG_UNNEEDED, REGCM_IMM16 | REGCM_IMM8 } },
},
[TEMPLATE_COPY_IMM32] = {
- .lhs = { [0] = { REG_UNSET, COPYIMM32_REGCM } },
+ .lhs = { [0] = { REG_UNSET, COPY32_REGCM } },
.rhs = { [0] = { REG_UNNEEDED, REGCM_IMM32 | REGCM_IMM16 | REGCM_IMM8 } },
},
[TEMPLATE_PHI8] = {
[15] = { REG_VIRT0, COPY32_REGCM },
}, },
[TEMPLATE_STORE8] = {
- .lhs = { [0] = { REG_UNSET, REGCM_GPR32 } },
- .rhs = { [0] = { REG_UNSET, REGCM_GPR8 } },
+ .rhs = {
+ [0] = { REG_UNSET, REGCM_GPR32 },
+ [1] = { REG_UNSET, REGCM_GPR8_LO },
+ },
},
[TEMPLATE_STORE16] = {
- .lhs = { [0] = { REG_UNSET, REGCM_GPR32 } },
- .rhs = { [0] = { REG_UNSET, REGCM_GPR16 } },
+ .rhs = {
+ [0] = { REG_UNSET, REGCM_GPR32 },
+ [1] = { REG_UNSET, REGCM_GPR16 },
+ },
},
[TEMPLATE_STORE32] = {
- .lhs = { [0] = { REG_UNSET, REGCM_GPR32 } },
- .rhs = { [0] = { REG_UNSET, REGCM_GPR32 } },
+ .rhs = {
+ [0] = { REG_UNSET, REGCM_GPR32 },
+ [1] = { REG_UNSET, REGCM_GPR32 },
+ },
},
[TEMPLATE_LOAD8] = {
- .lhs = { [0] = { REG_UNSET, REGCM_GPR8 } },
+ .lhs = { [0] = { REG_UNSET, REGCM_GPR8_LO } },
.rhs = { [0] = { REG_UNSET, REGCM_GPR32 } },
},
[TEMPLATE_LOAD16] = {
.lhs = { [0] = { REG_UNSET, REGCM_GPR32 } },
.rhs = { [0] = { REG_UNSET, REGCM_GPR32 } },
},
- [TEMPLATE_BINARY_REG] = {
+ [TEMPLATE_BINARY8_REG] = {
+ .lhs = { [0] = { REG_VIRT0, REGCM_GPR8_LO } },
+ .rhs = {
+ [0] = { REG_VIRT0, REGCM_GPR8_LO },
+ [1] = { REG_UNSET, REGCM_GPR8_LO },
+ },
+ },
+ [TEMPLATE_BINARY16_REG] = {
+ .lhs = { [0] = { REG_VIRT0, REGCM_GPR16 } },
+ .rhs = {
+ [0] = { REG_VIRT0, REGCM_GPR16 },
+ [1] = { REG_UNSET, REGCM_GPR16 },
+ },
+ },
+ [TEMPLATE_BINARY32_REG] = {
.lhs = { [0] = { REG_VIRT0, REGCM_GPR32 } },
.rhs = {
[0] = { REG_VIRT0, REGCM_GPR32 },
[1] = { REG_UNSET, REGCM_GPR32 },
},
},
- [TEMPLATE_BINARY_IMM] = {
+ [TEMPLATE_BINARY8_IMM] = {
+ .lhs = { [0] = { REG_VIRT0, REGCM_GPR8_LO } },
+ .rhs = {
+ [0] = { REG_VIRT0, REGCM_GPR8_LO },
+ [1] = { REG_UNNEEDED, REGCM_IMM8 },
+ },
+ },
+ [TEMPLATE_BINARY16_IMM] = {
+ .lhs = { [0] = { REG_VIRT0, REGCM_GPR16 } },
+ .rhs = {
+ [0] = { REG_VIRT0, REGCM_GPR16 },
+ [1] = { REG_UNNEEDED, REGCM_IMM16 },
+ },
+ },
+ [TEMPLATE_BINARY32_IMM] = {
.lhs = { [0] = { REG_VIRT0, REGCM_GPR32 } },
.rhs = {
[0] = { REG_VIRT0, REGCM_GPR32 },
[1] = { REG_UNNEEDED, REGCM_IMM32 },
},
},
- [TEMPLATE_SL_CL] = {
+ [TEMPLATE_SL8_CL] = {
+ .lhs = { [0] = { REG_VIRT0, REGCM_GPR8_LO } },
+ .rhs = {
+ [0] = { REG_VIRT0, REGCM_GPR8_LO },
+ [1] = { REG_CL, REGCM_GPR8_LO },
+ },
+ },
+ [TEMPLATE_SL16_CL] = {
+ .lhs = { [0] = { REG_VIRT0, REGCM_GPR16 } },
+ .rhs = {
+ [0] = { REG_VIRT0, REGCM_GPR16 },
+ [1] = { REG_CL, REGCM_GPR8_LO },
+ },
+ },
+ [TEMPLATE_SL32_CL] = {
.lhs = { [0] = { REG_VIRT0, REGCM_GPR32 } },
.rhs = {
[0] = { REG_VIRT0, REGCM_GPR32 },
- [1] = { REG_CL, REGCM_GPR8 },
+ [1] = { REG_CL, REGCM_GPR8_LO },
+ },
+ },
+ [TEMPLATE_SL8_IMM] = {
+ .lhs = { [0] = { REG_VIRT0, REGCM_GPR8_LO } },
+ .rhs = {
+ [0] = { REG_VIRT0, REGCM_GPR8_LO },
+ [1] = { REG_UNNEEDED, REGCM_IMM8 },
+ },
+ },
+ [TEMPLATE_SL16_IMM] = {
+ .lhs = { [0] = { REG_VIRT0, REGCM_GPR16 } },
+ .rhs = {
+ [0] = { REG_VIRT0, REGCM_GPR16 },
+ [1] = { REG_UNNEEDED, REGCM_IMM8 },
+ },
+ },
+ [TEMPLATE_SL32_IMM] = {
+ .lhs = { [0] = { REG_VIRT0, REGCM_GPR32 } },
+ .rhs = {
+ [0] = { REG_VIRT0, REGCM_GPR32 },
+ [1] = { REG_UNNEEDED, REGCM_IMM8 },
+ },
+ },
+ [TEMPLATE_UNARY8] = {
+ .lhs = { [0] = { REG_VIRT0, REGCM_GPR8_LO } },
+ .rhs = { [0] = { REG_VIRT0, REGCM_GPR8_LO } },
+ },
+ [TEMPLATE_UNARY16] = {
+ .lhs = { [0] = { REG_VIRT0, REGCM_GPR16 } },
+ .rhs = { [0] = { REG_VIRT0, REGCM_GPR16 } },
+ },
+ [TEMPLATE_UNARY32] = {
+ .lhs = { [0] = { REG_VIRT0, REGCM_GPR32 } },
+ .rhs = { [0] = { REG_VIRT0, REGCM_GPR32 } },
+ },
+ [TEMPLATE_CMP8_REG] = {
+ .lhs = { [0] = { REG_EFLAGS, REGCM_FLAGS } },
+ .rhs = {
+ [0] = { REG_UNSET, REGCM_GPR8_LO },
+ [1] = { REG_UNSET, REGCM_GPR8_LO },
+ },
+ },
+ [TEMPLATE_CMP16_REG] = {
+ .lhs = { [0] = { REG_EFLAGS, REGCM_FLAGS } },
+ .rhs = {
+ [0] = { REG_UNSET, REGCM_GPR16 },
+ [1] = { REG_UNSET, REGCM_GPR16 },
+ },
+ },
+ [TEMPLATE_CMP32_REG] = {
+ .lhs = { [0] = { REG_EFLAGS, REGCM_FLAGS } },
+ .rhs = {
+ [0] = { REG_UNSET, REGCM_GPR32 },
+ [1] = { REG_UNSET, REGCM_GPR32 },
},
},
- [TEMPLATE_SL_IMM] = {
- .lhs = { [0] = { REG_VIRT0, REGCM_GPR32 } },
- .rhs = {
- [0] = { REG_VIRT0, REGCM_GPR32 },
+ [TEMPLATE_CMP8_IMM] = {
+ .lhs = { [0] = { REG_EFLAGS, REGCM_FLAGS } },
+ .rhs = {
+ [0] = { REG_UNSET, REGCM_GPR8_LO },
[1] = { REG_UNNEEDED, REGCM_IMM8 },
},
},
- [TEMPLATE_UNARY] = {
- .lhs = { [0] = { REG_VIRT0, REGCM_GPR32 } },
- .rhs = { [0] = { REG_VIRT0, REGCM_GPR32 } },
- },
- [TEMPLATE_CMP_REG] = {
+ [TEMPLATE_CMP16_IMM] = {
.lhs = { [0] = { REG_EFLAGS, REGCM_FLAGS } },
.rhs = {
- [0] = { REG_UNSET, REGCM_GPR32 },
- [1] = { REG_UNSET, REGCM_GPR32 },
+ [0] = { REG_UNSET, REGCM_GPR16 },
+ [1] = { REG_UNNEEDED, REGCM_IMM16 },
},
},
- [TEMPLATE_CMP_IMM] = {
+ [TEMPLATE_CMP32_IMM] = {
.lhs = { [0] = { REG_EFLAGS, REGCM_FLAGS } },
.rhs = {
[0] = { REG_UNSET, REGCM_GPR32 },
[1] = { REG_UNNEEDED, REGCM_IMM32 },
},
},
- [TEMPLATE_TEST] = {
+ [TEMPLATE_TEST8] = {
+ .lhs = { [0] = { REG_EFLAGS, REGCM_FLAGS } },
+ .rhs = { [0] = { REG_UNSET, REGCM_GPR8_LO } },
+ },
+ [TEMPLATE_TEST16] = {
+ .lhs = { [0] = { REG_EFLAGS, REGCM_FLAGS } },
+ .rhs = { [0] = { REG_UNSET, REGCM_GPR16 } },
+ },
+ [TEMPLATE_TEST32] = {
.lhs = { [0] = { REG_EFLAGS, REGCM_FLAGS } },
.rhs = { [0] = { REG_UNSET, REGCM_GPR32 } },
},
[TEMPLATE_SET] = {
- .lhs = { [0] = { REG_UNSET, REGCM_GPR8 } },
+ .lhs = { [0] = { REG_UNSET, REGCM_GPR8_LO } },
.rhs = { [0] = { REG_EFLAGS, REGCM_FLAGS } },
},
[TEMPLATE_JMP] = {
.rhs = { [0] = { REG_EFLAGS, REGCM_FLAGS } },
},
+ [TEMPLATE_RET] = {
+ .rhs = { [0] = { REG_UNSET, REGCM_GPR32 } },
+ },
[TEMPLATE_INB_DX] = {
- .lhs = { [0] = { REG_AL, REGCM_GPR8 } },
+ .lhs = { [0] = { REG_AL, REGCM_GPR8_LO } },
.rhs = { [0] = { REG_DX, REGCM_GPR16 } },
},
[TEMPLATE_INB_IMM] = {
- .lhs = { [0] = { REG_AL, REGCM_GPR8 } },
+ .lhs = { [0] = { REG_AL, REGCM_GPR8_LO } },
.rhs = { [0] = { REG_UNNEEDED, REGCM_IMM8 } },
},
[TEMPLATE_INW_DX] = {
},
[TEMPLATE_OUTB_DX] = {
.rhs = {
- [0] = { REG_AL, REGCM_GPR8 },
+ [0] = { REG_AL, REGCM_GPR8_LO },
[1] = { REG_DX, REGCM_GPR16 },
},
},
[TEMPLATE_OUTB_IMM] = {
.rhs = {
- [0] = { REG_AL, REGCM_GPR8 },
+ [0] = { REG_AL, REGCM_GPR8_LO },
[1] = { REG_UNNEEDED, REGCM_IMM8 },
},
},
[2] = { REG_EDX, REGCM_GPR32 },
},
},
- [TEMPLATE_UMUL] = {
- .lhs = { [0] = { REG_EDXEAX, REGCM_GPR64 } },
+ [TEMPLATE_UMUL8] = {
+ .lhs = { [0] = { REG_AX, REGCM_GPR16 } },
+ .rhs = {
+ [0] = { REG_AL, REGCM_GPR8_LO },
+ [1] = { REG_UNSET, REGCM_GPR8_LO },
+ },
+ },
+ [TEMPLATE_UMUL16] = {
+ .lhs = { [0] = { REG_DXAX, REGCM_DIVIDEND32 } },
+ .rhs = {
+ [0] = { REG_AX, REGCM_GPR16 },
+ [1] = { REG_UNSET, REGCM_GPR16 },
+ },
+ },
+ [TEMPLATE_UMUL32] = {
+ .lhs = { [0] = { REG_EDXEAX, REGCM_DIVIDEND64 } },
.rhs = {
[0] = { REG_EAX, REGCM_GPR32 },
[1] = { REG_UNSET, REGCM_GPR32 },
},
},
- [TEMPLATE_DIV] = {
+ [TEMPLATE_DIV8] = {
.lhs = {
- [0] = { REG_EAX, REGCM_GPR32 },
- [1] = { REG_EDX, REGCM_GPR32 },
+ [0] = { REG_AL, REGCM_GPR8_LO },
+ [1] = { REG_AH, REGCM_GPR8 },
},
.rhs = {
- [0] = { REG_EDXEAX, REGCM_GPR64 },
- [1] = { REG_UNSET, REGCM_GPR32 },
+ [0] = { REG_AX, REGCM_GPR16 },
+ [1] = { REG_UNSET, REGCM_GPR8_LO },
},
},
- [TEMPLATE_MOD] = {
+ [TEMPLATE_DIV16] = {
.lhs = {
- [0] = { REG_EDX, REGCM_GPR32 },
- [1] = { REG_EAX, REGCM_GPR32 },
+ [0] = { REG_AX, REGCM_GPR16 },
+ [1] = { REG_DX, REGCM_GPR16 },
+ },
+ .rhs = {
+ [0] = { REG_DXAX, REGCM_DIVIDEND32 },
+ [1] = { REG_UNSET, REGCM_GPR16 },
+ },
+ },
+ [TEMPLATE_DIV32] = {
+ .lhs = {
+ [0] = { REG_EAX, REGCM_GPR32 },
+ [1] = { REG_EDX, REGCM_GPR32 },
},
.rhs = {
- [0] = { REG_EDXEAX, REGCM_GPR64 },
+ [0] = { REG_EDXEAX, REGCM_DIVIDEND64 },
[1] = { REG_UNSET, REGCM_GPR32 },
},
},
};
+static void fixup_branch(struct compile_state *state,
+ struct triple *branch, int jmp_op, int cmp_op, struct type *cmp_type,
+ struct triple *left, struct triple *right)
+{
+ struct triple *test;
+ if (!left) {
+ internal_error(state, branch, "no branch test?");
+ }
+ test = pre_triple(state, branch,
+ cmp_op, cmp_type, left, right);
+ test->template_id = TEMPLATE_TEST32;
+ if (cmp_op == OP_CMP) {
+ test->template_id = TEMPLATE_CMP32_REG;
+ if (get_imm32(test, &RHS(test, 1))) {
+ test->template_id = TEMPLATE_CMP32_IMM;
+ }
+ }
+ use_triple(RHS(test, 0), test);
+ use_triple(RHS(test, 1), test);
+ unuse_triple(RHS(branch, 0), branch);
+ RHS(branch, 0) = test;
+ branch->op = jmp_op;
+ branch->template_id = TEMPLATE_JMP;
+ use_triple(RHS(branch, 0), branch);
+}
+
static void fixup_branches(struct compile_state *state,
struct triple *cmp, struct triple *use, int jmp_op)
{
if (entry->member->op == OP_COPY) {
fixup_branches(state, cmp, entry->member, jmp_op);
}
- else if (entry->member->op == OP_BRANCH) {
- struct triple *branch, *test;
+ else if (entry->member->op == OP_CBRANCH) {
+ struct triple *branch;
struct triple *left, *right;
left = right = 0;
left = RHS(cmp, 0);
right = RHS(cmp, 1);
}
branch = entry->member;
- test = pre_triple(state, branch,
+ fixup_branch(state, branch, jmp_op,
cmp->op, cmp->type, left, right);
- test->template_id = TEMPLATE_TEST;
- if (cmp->op == OP_CMP) {
- test->template_id = TEMPLATE_CMP_REG;
- if (get_imm32(test, &RHS(test, 1))) {
- test->template_id = TEMPLATE_CMP_IMM;
- }
- }
- use_triple(RHS(test, 0), test);
- use_triple(RHS(test, 1), test);
- unuse_triple(RHS(branch, 0), branch);
- RHS(branch, 0) = test;
- branch->op = jmp_op;
- branch->template_id = TEMPLATE_JMP;
- use_triple(RHS(branch, 0), branch);
}
}
}
/* Modify the comparison operator */
ins->op = cmp_op;
- ins->template_id = TEMPLATE_TEST;
+ ins->template_id = TEMPLATE_TEST32;
if (cmp_op == OP_CMP) {
- ins->template_id = TEMPLATE_CMP_REG;
+ ins->template_id = TEMPLATE_CMP32_REG;
if (get_imm32(ins, &RHS(ins, 1))) {
- ins->template_id = TEMPLATE_CMP_IMM;
+ ins->template_id = TEMPLATE_CMP32_IMM;
}
}
/* Generate the instruction sequence that will transform the
return result;
}
+static struct triple *mod_div(struct compile_state *state,
+ struct triple *ins, int div_op, int index)
+{
+ struct triple *div, *piece0, *piece1;
+
+ /* Generate a piece to hold the remainder */
+ piece1 = post_triple(state, ins, OP_PIECE, ins->type, 0, 0);
+ piece1->u.cval = 1;
+
+ /* Generate a piece to hold the quotient */
+ piece0 = post_triple(state, ins, OP_PIECE, ins->type, 0, 0);
+ piece0->u.cval = 0;
+
+ /* Generate the appropriate division instruction */
+ div = post_triple(state, ins, div_op, ins->type, 0, 0);
+ RHS(div, 0) = RHS(ins, 0);
+ RHS(div, 1) = RHS(ins, 1);
+ LHS(div, 0) = piece0;
+ LHS(div, 1) = piece1;
+ div->template_id = TEMPLATE_DIV32;
+ use_triple(RHS(div, 0), div);
+ use_triple(RHS(div, 1), div);
+ use_triple(LHS(div, 0), div);
+ use_triple(LHS(div, 1), div);
+
+ /* Hook on piece0 */
+ MISC(piece0, 0) = div;
+ use_triple(div, piece0);
+
+ /* Hook on piece1 */
+ MISC(piece1, 0) = div;
+ use_triple(div, piece1);
+
+ /* Replate uses of ins with the appropriate piece of the div */
+ propogate_use(state, ins, LHS(div, index));
+ release_triple(state, ins);
+
+ /* Return the address of the next instruction */
+ return piece1->next;
+}
+
static struct triple *transform_to_arch_instruction(
struct compile_state *state, struct triple *ins)
{
case OP_LOAD:
switch(ins->type->type & TYPE_MASK) {
case TYPE_CHAR: case TYPE_UCHAR:
- ins->template_id = TEMPLATE_LOAD8;
- break;
- case TYPE_SHORT:
- case TYPE_USHORT:
- ins->template_id = TEMPLATE_LOAD16;
- break;
- case TYPE_INT:
- case TYPE_UINT:
- case TYPE_LONG:
- case TYPE_ULONG:
+ case TYPE_SHORT: case TYPE_USHORT:
+ case TYPE_INT: case TYPE_UINT:
+ case TYPE_LONG: case TYPE_ULONG:
case TYPE_POINTER:
- ins->template_id = TEMPLATE_LOAD32;
break;
default:
internal_error(state, ins, "unknown type in load");
break;
}
+ ins->template_id = TEMPLATE_LOAD32;
break;
case OP_ADD:
case OP_SUB:
case OP_XOR:
case OP_OR:
case OP_SMUL:
- ins->template_id = TEMPLATE_BINARY_REG;
+ ins->template_id = TEMPLATE_BINARY32_REG;
if (get_imm32(ins, &RHS(ins, 1))) {
- ins->template_id = TEMPLATE_BINARY_IMM;
+ ins->template_id = TEMPLATE_BINARY32_IMM;
}
break;
-#if 0
- /* This code does not work yet */
+ case OP_SDIVT:
+ case OP_UDIVT:
+ ins->template_id = TEMPLATE_DIV32;
+ next = after_lhs(state, ins);
+ break;
+ /* FIXME UMUL does not work yet.. */
case OP_UMUL:
- ins->template_id = TEMPLATE_UMUL;
+ ins->template_id = TEMPLATE_UMUL32;
break;
case OP_UDIV:
+ next = mod_div(state, ins, OP_UDIVT, 0);
+ break;
case OP_SDIV:
- ins->template_id = TEMPLATE_DIV;
+ next = mod_div(state, ins, OP_SDIVT, 0);
break;
case OP_UMOD:
+ next = mod_div(state, ins, OP_UDIVT, 1);
+ break;
case OP_SMOD:
- ins->template_id = TEMPLATE_MOD;
+ next = mod_div(state, ins, OP_SDIVT, 1);
break;
-#endif
case OP_SL:
case OP_SSR:
case OP_USR:
- ins->template_id = TEMPLATE_SL_CL;
+ ins->template_id = TEMPLATE_SL32_CL;
if (get_imm8(ins, &RHS(ins, 1))) {
- ins->template_id = TEMPLATE_SL_IMM;
+ ins->template_id = TEMPLATE_SL32_IMM;
} else if (size_of(state, RHS(ins, 1)->type) > 1) {
typed_pre_copy(state, &char_type, ins, 1);
}
break;
case OP_INVERT:
case OP_NEG:
- ins->template_id = TEMPLATE_UNARY;
+ ins->template_id = TEMPLATE_UNARY32;
break;
case OP_EQ:
bool_cmp(state, ins, OP_CMP, OP_JMP_EQ, OP_SET_EQ);
bool_cmp(state, ins, OP_TEST, OP_JMP_EQ, OP_SET_EQ);
break;
case OP_BRANCH:
- if (TRIPLE_RHS(ins->sizes) > 0) {
- internal_error(state, ins, "bad branch test");
- }
ins->op = OP_JMP;
ins->template_id = TEMPLATE_NOP;
break;
+ case OP_CBRANCH:
+ fixup_branch(state, ins, OP_JMP_NOTEQ, OP_TEST,
+ RHS(ins, 0)->type, RHS(ins, 0), 0);
+ break;
+ case OP_CALL:
+ ins->template_id = TEMPLATE_NOP;
+ break;
+ case OP_RET:
+ ins->template_id = TEMPLATE_RET;
+ break;
case OP_INB:
case OP_INW:
case OP_INL:
break;
/* Already transformed instructions */
case OP_TEST:
- ins->template_id = TEMPLATE_TEST;
+ ins->template_id = TEMPLATE_TEST32;
break;
case OP_CMP:
- ins->template_id = TEMPLATE_CMP_REG;
+ ins->template_id = TEMPLATE_CMP32_REG;
if (get_imm32(ins, &RHS(ins, 1))) {
- ins->template_id = TEMPLATE_CMP_IMM;
+ ins->template_id = TEMPLATE_CMP32_IMM;
}
break;
+ case OP_JMP:
+ ins->template_id = TEMPLATE_NOP;
+ break;
case OP_JMP_EQ: case OP_JMP_NOTEQ:
case OP_JMP_SLESS: case OP_JMP_ULESS:
case OP_JMP_SMORE: case OP_JMP_UMORE:
return next;
}
+static long next_label(struct compile_state *state)
+{
+ static long label_counter = 0;
+ return ++label_counter;
+}
static void generate_local_labels(struct compile_state *state)
{
struct triple *first, *label;
- int label_counter;
- label_counter = 0;
- first = RHS(state->main_function, 0);
+ first = state->first;
label = first;
do {
if ((label->op == OP_LABEL) ||
(label->op == OP_SDECL)) {
if (label->use) {
- label->u.cval = ++label_counter;
+ label->u.cval = next_label(state);
} else {
label->u.cval = 0;
}
static const char *arch_reg_str(int reg)
{
+#if REG_XMM7 != 44
+#error "Registers have renumberd fix arch_reg_str"
+#endif
static const char *regs[] = {
"%unset",
"%unneeded",
"%ax", "%bx", "%cx", "%dx", "%si", "%di", "%bp", "%sp",
"%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "%ebp", "%esp",
"%edx:%eax",
+ "%dx:%ax",
"%mm0", "%mm1", "%mm2", "%mm3", "%mm4", "%mm5", "%mm6", "%mm7",
"%xmm0", "%xmm1", "%xmm2", "%xmm3",
"%xmm4", "%xmm5", "%xmm6", "%xmm7",
switch(ins->op) {
case OP_INTCONST:
fprintf(fp, " $%ld ",
- (long_t)(ins->u.cval));
+ (long)(ins->u.cval));
break;
case OP_ADDRCONST:
+ if ((MISC(ins, 0)->op != OP_SDECL) &&
+ (MISC(ins, 0)->op != OP_LABEL))
+ {
+ internal_error(state, ins, "bad base for addrconst");
+ }
+ if (MISC(ins, 0)->u.cval <= 0) {
+ internal_error(state, ins, "unlabeled constant");
+ }
fprintf(fp, " $L%s%lu+%lu ",
- state->label_prefix,
- MISC(ins, 0)->u.cval,
- ins->u.cval);
+ state->compiler->label_prefix,
+ (unsigned long)(MISC(ins, 0)->u.cval),
+ (unsigned long)(ins->u.cval));
break;
default:
internal_error(state, ins, "unknown constant type");
}
}
+static void print_const(struct compile_state *state,
+ struct triple *ins, FILE *fp)
+{
+ switch(ins->op) {
+ case OP_INTCONST:
+ switch(ins->type->type & TYPE_MASK) {
+ case TYPE_CHAR:
+ case TYPE_UCHAR:
+ fprintf(fp, ".byte 0x%02lx\n",
+ (unsigned long)(ins->u.cval));
+ break;
+ case TYPE_SHORT:
+ case TYPE_USHORT:
+ fprintf(fp, ".short 0x%04lx\n",
+ (unsigned long)(ins->u.cval));
+ break;
+ case TYPE_INT:
+ case TYPE_UINT:
+ case TYPE_LONG:
+ case TYPE_ULONG:
+ fprintf(fp, ".int %lu\n",
+ (unsigned long)(ins->u.cval));
+ break;
+ default:
+ internal_error(state, ins, "Unknown constant type");
+ }
+ break;
+ case OP_ADDRCONST:
+ if ((MISC(ins, 0)->op != OP_SDECL) &&
+ (MISC(ins, 0)->op != OP_LABEL)) {
+ internal_error(state, ins, "bad base for addrconst");
+ }
+ if (MISC(ins, 0)->u.cval <= 0) {
+ internal_error(state, ins, "unlabeled constant");
+ }
+ fprintf(fp, ".int L%s%lu+%lu\n",
+ state->compiler->label_prefix,
+ (unsigned long)(MISC(ins, 0)->u.cval),
+ (unsigned long)(ins->u.cval));
+ break;
+ case OP_BLOBCONST:
+ {
+ unsigned char *blob;
+ size_t size, i;
+ size = size_of(state, ins->type);
+ blob = ins->u.blob;
+ for(i = 0; i < size; i++) {
+ fprintf(fp, ".byte 0x%02x\n",
+ blob[i]);
+ }
+ break;
+ }
+ default:
+ internal_error(state, ins, "Unknown constant type");
+ break;
+ }
+}
+
+#define TEXT_SECTION ".rom.text"
+#define DATA_SECTION ".rom.data"
+
+static long get_const_pool_ref(
+ struct compile_state *state, struct triple *ins, FILE *fp)
+{
+ long ref;
+ ref = next_label(state);
+ fprintf(fp, ".section \"" DATA_SECTION "\"\n");
+ fprintf(fp, ".balign %d\n", align_of(state, ins->type));
+ fprintf(fp, "L%s%lu:\n", state->compiler->label_prefix, ref);
+ print_const(state, ins, fp);
+ fprintf(fp, ".section \"" TEXT_SECTION "\"\n");
+ return ref;
+}
+
static void print_binary_op(struct compile_state *state,
const char *op, struct triple *ins, FILE *fp)
{
unsigned mask;
- mask = REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8;
- if (RHS(ins, 0)->id != ins->id) {
+ mask = REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8_LO;
+ if (ID_REG(RHS(ins, 0)->id) != ID_REG(ins->id)) {
internal_error(state, ins, "invalid register assignment");
}
if (is_const(RHS(ins, 1))) {
const char *op, struct triple *ins, FILE *fp)
{
unsigned mask;
- mask = REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8;
+ mask = REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8_LO;
fprintf(fp, "\t%s %s\n",
op,
reg(state, RHS(ins, 0), mask));
const char *op, struct triple *ins, FILE *fp)
{
unsigned mask;
- mask = REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8;
- if (RHS(ins, 0)->id != ins->id) {
+ mask = REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8_LO;
+ if (ID_REG(RHS(ins, 0)->id) != ID_REG(ins->id)) {
internal_error(state, ins, "invalid register assignment");
}
if (is_const(RHS(ins, 1))) {
else {
fprintf(fp, "\t%s %s, %s\n",
op,
- reg(state, RHS(ins, 1), REGCM_GPR8),
+ reg(state, RHS(ins, 1), REGCM_GPR8_LO),
reg(state, RHS(ins, 0), mask));
}
}
int dreg;
mask = 0;
switch(ins->op) {
- case OP_INB: op = "inb", mask = REGCM_GPR8; break;
+ case OP_INB: op = "inb", mask = REGCM_GPR8_LO; break;
case OP_INW: op = "inw", mask = REGCM_GPR16; break;
case OP_INL: op = "inl", mask = REGCM_GPR32; break;
default:
int lreg;
mask = 0;
switch(ins->op) {
- case OP_OUTB: op = "outb", mask = REGCM_GPR8; break;
+ case OP_OUTB: op = "outb", mask = REGCM_GPR8_LO; break;
case OP_OUTW: op = "outw", mask = REGCM_GPR16; break;
case OP_OUTL: op = "outl", mask = REGCM_GPR32; break;
default:
src = RHS(ins, 0);
dst = ins;
}
- else if (ins->op == OP_WRITE) {
- dst = LHS(ins, 0);
- src = RHS(ins, 0);
- }
else {
internal_error(state, ins, "unknown move operation");
src = dst = 0;
if (!is_const(src)) {
int src_reg, dst_reg;
int src_regcm, dst_regcm;
- src_reg = ID_REG(src->id);
+ src_reg = ID_REG(src->id);
dst_reg = ID_REG(dst->id);
src_regcm = arch_reg_regcm(state, src_reg);
- dst_regcm = arch_reg_regcm(state, dst_reg);
+ dst_regcm = arch_reg_regcm(state, dst_reg);
/* If the class is the same just move the register */
if (src_regcm & dst_regcm &
- (REGCM_GPR8 | REGCM_GPR16 | REGCM_GPR32)) {
+ (REGCM_GPR8_LO | REGCM_GPR16 | REGCM_GPR32)) {
if ((src_reg != dst_reg) || !omit_copy) {
fprintf(fp, "\tmov %s, %s\n",
reg(state, src, src_regcm),
}
/* Move 32bit to 8bit */
else if ((src_regcm & REGCM_GPR32_8) &&
- (dst_regcm & REGCM_GPR8))
+ (dst_regcm & REGCM_GPR8_LO))
{
src_reg = (src_reg - REGC_GPR32_8_FIRST) + REGC_GPR8_FIRST;
if ((src_reg != dst_reg) || !omit_copy) {
}
/* Move 16bit to 8bit */
else if ((src_regcm & REGCM_GPR16_8) &&
- (dst_regcm & REGCM_GPR8))
+ (dst_regcm & REGCM_GPR8_LO))
{
src_reg = (src_reg - REGC_GPR16_8_FIRST) + REGC_GPR8_FIRST;
if ((src_reg != dst_reg) || !omit_copy) {
}
}
/* Move 8/16bit to 16/32bit */
- else if ((src_regcm & (REGCM_GPR8 | REGCM_GPR16)) &&
+ else if ((src_regcm & (REGCM_GPR8_LO | REGCM_GPR16)) &&
(dst_regcm & (REGCM_GPR16 | REGCM_GPR32))) {
const char *op;
op = is_signed(src->type)? "movsx": "movzx";
reg(state, dst, dst_regcm));
}
}
- /* Move between mmx registers or mmx & sse registers */
- else if ((src_regcm & (REGCM_MMX | REGCM_XMM)) &&
- (dst_regcm & (REGCM_MMX | REGCM_XMM))) {
+ /* Move between mmx registers */
+ else if ((src_regcm & dst_regcm & REGCM_MMX)) {
if ((src_reg != dst_reg) || !omit_copy) {
fprintf(fp, "\tmovq %s, %s\n",
reg(state, src, src_regcm),
reg(state, dst, dst_regcm));
}
}
+ /* Move from sse to mmx registers */
+ else if ((src_regcm & REGCM_XMM) && (dst_regcm & REGCM_MMX)) {
+ fprintf(fp, "\tmovdq2q %s, %s\n",
+ reg(state, src, src_regcm),
+ reg(state, dst, dst_regcm));
+ }
+ /* Move from mmx to sse registers */
+ else if ((src_regcm & REGCM_MMX) && (dst_regcm & REGCM_XMM)) {
+ fprintf(fp, "\tmovq2dq %s, %s\n",
+ reg(state, src, src_regcm),
+ reg(state, dst, dst_regcm));
+ }
/* Move between 32bit gprs & mmx/sse registers */
else if ((src_regcm & (REGCM_GPR32 | REGCM_MMX | REGCM_XMM)) &&
(dst_regcm & (REGCM_GPR32 | REGCM_MMX | REGCM_XMM))) {
(dst_regcm & (REGCM_MMX | REGCM_XMM))) {
const char *op;
int mid_reg;
- op = is_signed(src->type)? "movsx":"movxz";
+ op = is_signed(src->type)? "movsx":"movzx";
mid_reg = (src_reg - REGC_GPR16_FIRST) + REGC_GPR32_FIRST;
fprintf(fp, "\t%s %s, %s\n\tmovd %s, %s\n",
op,
arch_reg_str(mid_reg),
arch_reg_str(dst_reg));
}
-
/* Move from mmx/sse registers to 16bit gprs */
else if ((src_regcm & (REGCM_MMX | REGCM_XMM)) &&
(dst_regcm & REGCM_GPR16)) {
arch_reg_str(src_reg),
arch_reg_str(dst_reg));
}
-
+ /* Move from gpr to 64bit dividend */
+ else if ((src_regcm & (REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8_LO)) &&
+ (dst_regcm & REGCM_DIVIDEND64)) {
+ const char *extend;
+ extend = is_signed(src->type)? "cltd":"movl $0, %edx";
+ fprintf(fp, "\tmov %s, %%eax\n\t%s\n",
+ arch_reg_str(src_reg),
+ extend);
+ }
+ /* Move from 64bit gpr to gpr */
+ else if ((src_regcm & REGCM_DIVIDEND64) &&
+ (dst_regcm & (REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8_LO))) {
+ if (dst_regcm & REGCM_GPR32) {
+ src_reg = REG_EAX;
+ }
+ else if (dst_regcm & REGCM_GPR16) {
+ src_reg = REG_AX;
+ }
+ else if (dst_regcm & REGCM_GPR8_LO) {
+ src_reg = REG_AL;
+ }
+ fprintf(fp, "\tmov %s, %s\n",
+ arch_reg_str(src_reg),
+ arch_reg_str(dst_reg));
+ }
+ /* Move from mmx/sse registers to 64bit gpr */
+ else if ((src_regcm & (REGCM_MMX | REGCM_XMM)) &&
+ (dst_regcm & REGCM_DIVIDEND64)) {
+ const char *extend;
+ extend = is_signed(src->type)? "cltd": "movl $0, %edx";
+ fprintf(fp, "\tmovd %s, %%eax\n\t%s\n",
+ arch_reg_str(src_reg),
+ extend);
+ }
+ /* Move from 64bit gpr to mmx/sse register */
+ else if ((src_regcm & REGCM_DIVIDEND64) &&
+ (dst_regcm & (REGCM_XMM | REGCM_MMX))) {
+ fprintf(fp, "\tmovd %%eax, %s\n",
+ arch_reg_str(dst_reg));
+ }
#if X86_4_8BIT_GPRS
/* Move from 8bit gprs to mmx/sse registers */
- else if ((src_regcm & REGCM_GPR8) && (src_reg <= REG_DL) &&
+ else if ((src_regcm & REGCM_GPR8_LO) && (src_reg <= REG_DL) &&
(dst_regcm & (REGCM_MMX | REGCM_XMM))) {
const char *op;
int mid_reg;
}
/* Move from mmx/sse registers and 8bit gprs */
else if ((src_regcm & (REGCM_MMX | REGCM_XMM)) &&
- (dst_regcm & REGCM_GPR8) && (dst_reg <= REG_DL)) {
+ (dst_regcm & REGCM_GPR8_LO) && (dst_reg <= REG_DL)) {
int mid_reg;
mid_reg = (dst_reg - REGC_GPR8_FIRST) + REGC_GPR32_FIRST;
fprintf(fp, "\tmovd %s, %s\n",
}
/* Move from 32bit gprs to 8bit gprs */
else if ((src_regcm & REGCM_GPR32) &&
- (dst_regcm & REGCM_GPR8)) {
+ (dst_regcm & REGCM_GPR8_LO)) {
dst_reg = (dst_reg - REGC_GPR8_FIRST) + REGC_GPR32_FIRST;
if ((src_reg != dst_reg) || !omit_copy) {
fprintf(fp, "\tmov %s, %s\n",
}
/* Move from 16bit gprs to 8bit gprs */
else if ((src_regcm & REGCM_GPR16) &&
- (dst_regcm & REGCM_GPR8)) {
+ (dst_regcm & REGCM_GPR8_LO)) {
dst_reg = (dst_reg - REGC_GPR8_FIRST) + REGC_GPR16_FIRST;
if ((src_reg != dst_reg) || !omit_copy) {
fprintf(fp, "\tmov %s, %s\n",
}
}
else {
- fprintf(fp, "\tmov ");
- print_const_val(state, src, fp);
- fprintf(fp, ", %s\n",
- reg(state, dst, REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8));
+ int dst_reg;
+ int dst_regcm;
+ dst_reg = ID_REG(dst->id);
+ dst_regcm = arch_reg_regcm(state, dst_reg);
+ if (dst_regcm & (REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8_LO)) {
+ fprintf(fp, "\tmov ");
+ print_const_val(state, src, fp);
+ fprintf(fp, ", %s\n",
+ reg(state, dst, REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8_LO));
+ }
+ else if (dst_regcm & REGCM_DIVIDEND64) {
+ if (size_of(state, dst->type) > 4) {
+ internal_error(state, ins, "64bit constant...");
+ }
+ fprintf(fp, "\tmov $0, %%edx\n");
+ fprintf(fp, "\tmov ");
+ print_const_val(state, src, fp);
+ fprintf(fp, ", %%eax\n");
+ }
+ else if (dst_regcm & REGCM_DIVIDEND32) {
+ if (size_of(state, dst->type) > 2) {
+ internal_error(state, ins, "32bit constant...");
+ }
+ fprintf(fp, "\tmov $0, %%dx\n");
+ fprintf(fp, "\tmov ");
+ print_const_val(state, src, fp);
+ fprintf(fp, ", %%ax");
+ }
+ else if (dst_regcm & (REGCM_XMM | REGCM_MMX)) {
+ long ref;
+ ref = get_const_pool_ref(state, src, fp);
+ fprintf(fp, "\tmovd L%s%lu, %s\n",
+ state->compiler->label_prefix, ref,
+ reg(state, dst, (REGCM_XMM | REGCM_MMX)));
+ }
+ else {
+ internal_error(state, ins, "unknown copy immediate type");
+ }
}
}
struct triple *ins, FILE *fp)
{
struct triple *dst, *src;
+ const char *op;
dst = ins;
src = RHS(ins, 0);
if (is_const(src) || is_const(dst)) {
internal_error(state, ins, "unknown load operation");
}
- fprintf(fp, "\tmov (%s), %s\n",
+ switch(ins->type->type & TYPE_MASK) {
+ case TYPE_CHAR: op = "movsbl"; break;
+ case TYPE_UCHAR: op = "movzbl"; break;
+ case TYPE_SHORT: op = "movswl"; break;
+ case TYPE_USHORT: op = "movzwl"; break;
+ case TYPE_INT: case TYPE_UINT:
+ case TYPE_LONG: case TYPE_ULONG:
+ case TYPE_POINTER:
+ op = "movl";
+ break;
+ default:
+ internal_error(state, ins, "unknown type in load");
+ op = "<invalid opcode>";
+ break;
+ }
+ fprintf(fp, "\t%s (%s), %s\n",
+ op,
reg(state, src, REGCM_GPR32),
- reg(state, dst, REGCM_GPR8 | REGCM_GPR16 | REGCM_GPR32));
+ reg(state, dst, REGCM_GPR32));
}
struct triple *ins, FILE *fp)
{
struct triple *dst, *src;
- dst = LHS(ins, 0);
- src = RHS(ins, 0);
+ dst = RHS(ins, 0);
+ src = RHS(ins, 1);
if (is_const(src) && (src->op == OP_INTCONST)) {
long_t value;
value = (long_t)(src->u.cval);
fprintf(fp, "\tmov%s $%ld, (%s)\n",
type_suffix(state, src->type),
- value,
+ (long)(value),
reg(state, dst, REGCM_GPR32));
}
else if (is_const(dst) && (dst->op == OP_INTCONST)) {
fprintf(fp, "\tmov%s %s, 0x%08lx\n",
type_suffix(state, src->type),
- reg(state, src, REGCM_GPR8 | REGCM_GPR16 | REGCM_GPR32),
- dst->u.cval);
+ reg(state, src, REGCM_GPR8_LO | REGCM_GPR16 | REGCM_GPR32),
+ (unsigned long)(dst->u.cval));
}
else {
if (is_const(src) || is_const(dst)) {
}
fprintf(fp, "\tmov%s %s, (%s)\n",
type_suffix(state, src->type),
- reg(state, src, REGCM_GPR8 | REGCM_GPR16 | REGCM_GPR32),
+ reg(state, src, REGCM_GPR8_LO | REGCM_GPR16 | REGCM_GPR32),
reg(state, dst, REGCM_GPR32));
}
{
unsigned mask;
int dreg;
- mask = REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8;
+ mask = REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8_LO;
dreg = check_reg(state, ins, REGCM_FLAGS);
if (!reg_is_reg(state, dreg, REG_EFLAGS)) {
internal_error(state, ins, "bad dest register for cmp");
struct triple *ins, FILE *fp)
{
unsigned mask;
- mask = REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8;
+ mask = REGCM_GPR32 | REGCM_GPR16 | REGCM_GPR8_LO;
fprintf(fp, "\ttest %s, %s\n",
reg(state, RHS(ins, 0), mask),
reg(state, RHS(ins, 0), mask));
struct triple *branch, FILE *fp)
{
const char *bop = "j";
- if (branch->op == OP_JMP) {
+ if ((branch->op == OP_JMP) || (branch->op == OP_CALL)) {
if (TRIPLE_RHS(branch->sizes) != 0) {
internal_error(state, branch, "jmp with condition?");
}
}
fprintf(fp, "\t%s L%s%lu\n",
bop,
- state->label_prefix,
- TARG(branch, 0)->u.cval);
+ state->compiler->label_prefix,
+ (unsigned long)(TARG(branch, 0)->u.cval));
+}
+
+static void print_op_ret(struct compile_state *state,
+ struct triple *branch, FILE *fp)
+{
+ fprintf(fp, "\tjmp *%s\n",
+ reg(state, RHS(branch, 0), REGCM_GPR32));
}
static void print_op_set(struct compile_state *state,
break;
}
fprintf(fp, "\t%s %s\n",
- sop, reg(state, set, REGCM_GPR8));
+ sop, reg(state, set, REGCM_GPR8_LO));
}
static void print_op_bit_scan(struct compile_state *state,
reg(state, ins, REGCM_GPR32));
}
-static void print_const(struct compile_state *state,
- struct triple *ins, FILE *fp)
-{
- switch(ins->op) {
- case OP_INTCONST:
- switch(ins->type->type & TYPE_MASK) {
- case TYPE_CHAR:
- case TYPE_UCHAR:
- fprintf(fp, ".byte 0x%02lx\n", ins->u.cval);
- break;
- case TYPE_SHORT:
- case TYPE_USHORT:
- fprintf(fp, ".short 0x%04lx\n", ins->u.cval);
- break;
- case TYPE_INT:
- case TYPE_UINT:
- case TYPE_LONG:
- case TYPE_ULONG:
- fprintf(fp, ".int %lu\n", ins->u.cval);
- break;
- default:
- internal_error(state, ins, "Unknown constant type");
- }
- break;
- case OP_BLOBCONST:
- {
- unsigned char *blob;
- size_t size, i;
- size = size_of(state, ins->type);
- blob = ins->u.blob;
- for(i = 0; i < size; i++) {
- fprintf(fp, ".byte 0x%02x\n",
- blob[i]);
- }
- break;
- }
- default:
- internal_error(state, ins, "Unknown constant type");
- break;
- }
-}
-
-#define TEXT_SECTION ".rom.text"
-#define DATA_SECTION ".rom.data"
static void print_sdecl(struct compile_state *state,
struct triple *ins, FILE *fp)
{
fprintf(fp, ".section \"" DATA_SECTION "\"\n");
fprintf(fp, ".balign %d\n", align_of(state, ins->type));
- fprintf(fp, "L%s%lu:\n", state->label_prefix, ins->u.cval);
+ fprintf(fp, "L%s%lu:\n",
+ state->compiler->label_prefix, (unsigned long)(ins->u.cval));
print_const(state, MISC(ins, 0), fp);
fprintf(fp, ".section \"" TEXT_SECTION "\"\n");
case OP_SDECL:
print_sdecl(state, ins, fp);
break;
- case OP_WRITE:
case OP_COPY:
print_op_move(state, ins, fp);
break;
case OP_JMP_SMORE: case OP_JMP_UMORE:
case OP_JMP_SLESSEQ: case OP_JMP_ULESSEQ:
case OP_JMP_SMOREEQ: case OP_JMP_UMOREEQ:
+ case OP_CALL:
print_op_branch(state, ins, fp);
break;
+ case OP_RET:
+ print_op_ret(state, ins, fp);
+ break;
case OP_SET_EQ: case OP_SET_NOTEQ:
case OP_SET_SLESS: case OP_SET_ULESS:
case OP_SET_SMORE: case OP_SET_UMORE:
case OP_HLT:
fprintf(fp, "\thlt\n");
break;
+ case OP_SDIVT:
+ fprintf(fp, "\tidiv %s\n", reg(state, RHS(ins, 1), REGCM_GPR32));
+ break;
+ case OP_UDIVT:
+ fprintf(fp, "\tdiv %s\n", reg(state, RHS(ins, 1), REGCM_GPR32));
+ break;
+ case OP_UMUL:
+ fprintf(fp, "\tmul %s\n", reg(state, RHS(ins, 1), REGCM_GPR32));
+ break;
case OP_LABEL:
if (!ins->use) {
return;
}
- fprintf(fp, "L%s%lu:\n", state->label_prefix, ins->u.cval);
+ fprintf(fp, "L%s%lu:\n",
+ state->compiler->label_prefix, (unsigned long)(ins->u.cval));
break;
/* Ignore OP_PIECE */
case OP_PIECE:
break;
- /* Operations I am not yet certain how to handle */
- case OP_UMUL:
+ /* Operations that should never get here */
case OP_SDIV: case OP_UDIV:
case OP_SMOD: case OP_UMOD:
- /* Operations that should never get here */
case OP_LTRUE: case OP_LFALSE: case OP_EQ: case OP_NOTEQ:
case OP_SLESS: case OP_ULESS: case OP_SMORE: case OP_UMORE:
case OP_SLESSEQ: case OP_ULESSEQ: case OP_SMOREEQ: case OP_UMOREEQ:
int print_location;
struct occurance *last_occurance;
FILE *fp;
+ int max_inline_depth;
+ max_inline_depth = 0;
print_location = 1;
last_occurance = 0;
fp = state->output;
fprintf(fp, ".section \"" TEXT_SECTION "\"\n");
- first = RHS(state->main_function, 0);
+ first = state->first;
ins = first;
do {
if (print_location &&
}
else {
struct occurance *ptr;
+ int inline_depth;
fprintf(fp, "\t/*\n");
+ inline_depth = 0;
for(ptr = ins->occurance; ptr; ptr = ptr->parent) {
+ inline_depth++;
fprintf(fp, "\t * %s,%s:%d.%d\n",
ptr->function,
ptr->filename,
ptr->col);
}
fprintf(fp, "\t */\n");
-
+ if (inline_depth > max_inline_depth) {
+ max_inline_depth = inline_depth;
+ }
}
if (last_occurance) {
put_occurance(last_occurance);
print_instruction(state, ins, fp);
ins = ins->next;
} while(ins != first);
-
+ if (print_location) {
+ fprintf(fp, "/* max inline depth %d */\n",
+ max_inline_depth);
+ }
}
+
static void generate_code(struct compile_state *state)
{
generate_local_labels(state);
} while(tk->tok != TOK_EOF);
}
-static void compile(const char *filename, const char *ofilename,
- int cpu, int debug, int opt, const char *label_prefix)
+static void compile(const char *filename,
+ struct compiler_state *compiler, struct arch_state *arch)
{
int i;
struct compile_state state;
+ struct triple *ptr;
memset(&state, 0, sizeof(state));
+ state.compiler = compiler;
+ state.arch = arch;
state.file = 0;
for(i = 0; i < sizeof(state.token)/sizeof(state.token[0]); i++) {
memset(&state.token[i], 0, sizeof(state.token[i]));
state.token[i].tok = -1;
}
- /* Remember the debug settings */
- state.cpu = cpu;
- state.debug = debug;
- state.optimize = opt;
/* Remember the output filename */
- state.ofilename = ofilename;
- state.output = fopen(state.ofilename, "w");
+ state.output = fopen(state.compiler->ofilename, "w");
if (!state.output) {
error(&state, 0, "Cannot open output file %s\n",
- ofilename);
+ state.compiler->ofilename);
}
- /* Remember the label prefix */
- state.label_prefix = label_prefix;
/* Prep the preprocessor */
state.if_depth = 0;
state.if_value = 0;
/* register the keywords the macro preprocessor knows */
register_macro_keywords(&state);
/* Memorize where some special keywords are. */
+ state.i_switch = lookup(&state, "switch", 6);
+ state.i_case = lookup(&state, "case", 4);
state.i_continue = lookup(&state, "continue", 8);
state.i_break = lookup(&state, "break", 5);
+ state.i_default = lookup(&state, "default", 7);
+ state.i_return = lookup(&state, "return", 6);
+
+ /* Allocate beginning bounding labels for the function list */
+ state.first = label(&state);
+ state.first->id |= TRIPLE_FLAG_VOLATILE;
+ use_triple(state.first, state.first);
+ ptr = label(&state);
+ ptr->id |= TRIPLE_FLAG_VOLATILE;
+ use_triple(ptr, ptr);
+ flatten(&state, state.first, ptr);
+
+ /* Allocate a label for the pool of global variables */
+ state.global_pool = label(&state);
+ state.global_pool->id |= TRIPLE_FLAG_VOLATILE;
+ flatten(&state, state.first, state.global_pool);
+
+
/* Enter the globl definition scope */
start_scope(&state);
register_builtins(&state);
print_tokens(&state);
#endif
decls(&state);
+
/* Exit the global definition scope */
end_scope(&state);
+ /* Join all of the functions into one giant function */
+ join_functions(&state);
+
/* Now that basic compilation has happened
* optimize the intermediate code
*/
optimize(&state);
generate_code(&state);
- if (state.debug) {
+ if (state.compiler->debug) {
fprintf(stderr, "done\n");
}
}
int main(int argc, char **argv)
{
const char *filename;
- const char *ofilename;
- const char *label_prefix;
- int cpu;
- int last_argc;
- int debug;
- int optimize;
- cpu = CPU_DEFAULT;
- label_prefix = "";
- ofilename = "auto.inc";
- optimize = 0;
- debug = 0;
- last_argc = -1;
- while((argc > 1) && (argc != last_argc)) {
- last_argc = argc;
- if (strncmp(argv[1], "--debug=", 8) == 0) {
- debug = atoi(argv[1] + 8);
- argv++;
- argc--;
- }
- else if (strncmp(argv[1], "--label-prefix=", 15) == 0) {
- label_prefix= argv[1] + 15;
- argv++;
- argc--;
- }
- else if ((strcmp(argv[1],"-O") == 0) ||
- (strcmp(argv[1], "-O1") == 0)) {
- optimize = 1;
- argv++;
- argc--;
+ struct compiler_state compiler;
+ struct arch_state arch;
+ int all_opts;
+ init_compiler_state(&compiler);
+ init_arch_state(&arch);
+ filename = 0;
+ all_opts = 0;
+ while(argc > 1) {
+ if (!all_opts && (strcmp(argv[1], "-o") == 0) && (argc > 2)) {
+ compiler.ofilename = argv[2];
+ argv += 2;
+ argc -= 2;
}
- else if (strcmp(argv[1],"-O2") == 0) {
- optimize = 2;
+ else if (!all_opts && argv[1][0] == '-') {
+ int result;
+ result = -1;
+ if (strcmp(argv[1], "--") == 0) {
+ result = 0;
+ all_opts = 1;
+ }
+ else if (strncmp(argv[1],"-O", 2) == 0) {
+ result = compiler_encode_flag(&compiler, argv[1]);
+ }
+ else if (strncmp(argv[1], "--label-prefix=", 15) == 0) {
+ result = compiler_encode_flag(&compiler, argv[1]+2);
+ }
+ else if (strncmp(argv[1], "-f", 2) == 0) {
+ result = compiler_encode_flag(&compiler, argv[1]+2);
+ }
+ else if (strncmp(argv[1], "-m", 2) == 0) {
+ result = arch_encode_flag(&arch, argv[1]+2);
+ }
+ if (result < 0) {
+ arg_error("Invalid option specified: %s\n",
+ argv[1]);
+ }
argv++;
argc--;
}
- else if ((strcmp(argv[1], "-o") == 0) && (argc > 2)) {
- ofilename = argv[2];
- argv += 2;
- argc -= 2;
- }
- else if (strncmp(argv[1], "-mcpu=", 6) == 0) {
- cpu = arch_encode_cpu(argv[1] + 6);
- if (cpu == BAD_CPU) {
- arg_error("Invalid cpu specified: %s\n",
- argv[1] + 6);
+ else {
+ if (filename) {
+ arg_error("Only one filename may be specified\n");
}
+ filename = argv[1];
argv++;
argc--;
}
}
- if (argc != 2) {
- arg_error("Wrong argument count %d\n", argc);
+ if (!filename) {
+ arg_error("No filename specified\n");
}
- filename = argv[1];
- compile(filename, ofilename, cpu, debug, optimize, label_prefix);
+ compile(filename, &compiler, &arch);
return 0;
}