+ /* Now get the actual function definition */
+ compound_statement(state, end);
+
+ /* Finish anything unfinished with branches */
+ resolve_branches(state, first);
+
+ /* Remove the parameter scope */
+ end_scope(state);
+
+
+ /* 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) {
+ FILE *fp = state->dbgout;
+ fprintf(fp, "\n");
+ loc(fp, state, 0);
+ fprintf(fp, "\n__________ %s _________\n", __FUNCTION__);
+ display_func(state, fp, def);
+ fprintf(fp, "__________ %s _________ done\n\n", __FUNCTION__);
+ }
+
+ return def;
+}
+
+static struct triple *do_decl(struct compile_state *state,
+ struct type *type, struct hash_entry *ident)
+{
+ struct triple *def;
+ def = 0;
+ /* Clean up the storage types used */
+ switch (type->type & STOR_MASK) {
+ case STOR_AUTO:
+ case STOR_STATIC:
+ /* These are the good types I am aiming for */
+ break;
+ case STOR_REGISTER:
+ type->type &= ~STOR_MASK;
+ type->type |= STOR_AUTO;
+ break;
+ case STOR_LOCAL:
+ case STOR_EXTERN:
+ type->type &= ~STOR_MASK;
+ type->type |= STOR_STATIC;
+ break;
+ case STOR_TYPEDEF:
+ if (!ident) {
+ error(state, 0, "typedef without name");
+ }
+ symbol(state, ident, &ident->sym_ident, 0, type);
+ ident->tok = TOK_TYPE_NAME;
+ return 0;
+ break;
+ default:
+ internal_error(state, 0, "Undefined storage class");
+ }
+ if ((type->type & TYPE_MASK) == TYPE_FUNCTION) {
+ error(state, 0, "Function prototypes not supported");
+ }
+ if (ident &&
+ ((type->type & TYPE_MASK) == TYPE_ARRAY) &&
+ ((type->type & STOR_MASK) != STOR_STATIC))
+ error(state, 0, "non static arrays not supported");
+ if (ident &&
+ ((type->type & STOR_MASK) == STOR_STATIC) &&
+ ((type->type & QUAL_CONST) == 0)) {
+ error(state, 0, "non const static variables not supported");
+ }
+ if (ident) {
+ def = variable(state, type);
+ var_symbol(state, ident, def);
+ }
+ return def;
+}
+
+static void decl(struct compile_state *state, struct triple *first)
+{
+ struct type *base_type, *type;
+ struct hash_entry *ident;
+ struct triple *def;
+ int global;
+ global = (state->scope_depth <= GLOBAL_SCOPE_DEPTH);
+ base_type = decl_specifiers(state);
+ ident = 0;
+ type = declarator(state, base_type, &ident, 0);
+ type->type = attributes_opt(state, type->type);
+ 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);
+ state->function = 0;
+ }
+ else {
+ int done;
+ flatten(state, first, do_decl(state, type, ident));
+ /* type or variable definition */
+ do {
+ done = 1;
+ if (peek(state) == TOK_EQ) {
+ if (!ident) {
+ error(state, 0, "cannot assign to a type");
+ }
+ eat(state, TOK_EQ);
+ flatten(state, first,
+ init_expr(state,
+ ident->sym_ident->def,
+ initializer(state, type)));
+ }
+ arrays_complete(state, type);
+ if (peek(state) == TOK_COMMA) {
+ eat(state, TOK_COMMA);
+ ident = 0;
+ type = declarator(state, base_type, &ident, 0);
+ flatten(state, first, do_decl(state, type, ident));
+ done = 0;
+ }
+ } while(!done);
+ eat(state, TOK_SEMI);
+ }
+}
+
+static void decls(struct compile_state *state)
+{
+ struct triple *list;
+ int tok;
+ list = label(state);
+ while(1) {
+ tok = peek(state);
+ if (tok == TOK_EOF) {
+ return;
+ }
+ if (tok == TOK_SPACE) {
+ eat(state, TOK_SPACE);
+ }
+ decl(state, list);
+ if (list->next != list) {
+ error(state, 0, "global variables not supported");
+ }
+ }
+}
+
+/*
+ * Function inlining
+ */
+struct triple_reg_set {
+ struct triple_reg_set *next;
+ struct triple *member;
+ struct triple *new;
+};
+struct reg_block {
+ struct block *block;
+ struct triple_reg_set *in;
+ struct triple_reg_set *out;
+ int vertex;
+};
+static void setup_basic_blocks(struct compile_state *, struct basic_blocks *bb);
+static void analyze_basic_blocks(struct compile_state *state, struct basic_blocks *bb);
+static void free_basic_blocks(struct compile_state *, struct basic_blocks *bb);
+static int tdominates(struct compile_state *state, struct triple *dom, struct triple *sub);
+static void walk_blocks(struct compile_state *state, struct basic_blocks *bb,
+ void (*cb)(struct compile_state *state, struct block *block, void *arg),
+ void *arg);
+static void print_block(
+ struct compile_state *state, struct block *block, void *arg);
+static int do_triple_set(struct triple_reg_set **head,
+ struct triple *member, struct triple *new_member);
+static void do_triple_unset(struct triple_reg_set **head, struct triple *member);
+static struct reg_block *compute_variable_lifetimes(
+ struct compile_state *state, struct basic_blocks *bb);
+static void free_variable_lifetimes(struct compile_state *state,
+ struct basic_blocks *bb, struct reg_block *blocks);
+#if DEBUG_EXPLICIT_CLOSURES
+static void print_live_variables(struct compile_state *state,
+ struct basic_blocks *bb, struct reg_block *rb, FILE *fp);
+#endif
+
+
+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 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 void reverse_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 {
+ func = func->prev;
+ cb(state, func, arg);
+ } while(func != first);
+}
+
+
+static void mark_live(struct compile_state *state, struct triple *func, void *arg)
+{
+ struct triple *ptr, *first;
+ if (func->u.cval == 0) {
+ return;
+ }
+ ptr = first = RHS(func, 0);
+ do {
+ if (ptr->op == OP_FCALL) {
+ struct triple *called_func;
+ called_func = MISC(ptr, 0);
+ /* Mark the called function as used */
+ if (!(func->id & TRIPLE_FLAG_FLATTENED)) {
+ called_func->u.cval++;
+ }
+ /* Remove the called function from the list */
+ called_func->prev->next = called_func->next;
+ called_func->next->prev = called_func->prev;
+
+ /* Place the called function before me on the list */
+ called_func->next = func;
+ called_func->prev = func->prev;
+ called_func->prev->next = called_func;
+ called_func->next->prev = called_func;
+ }
+ ptr = ptr->next;
+ } while(ptr != first);
+ func->id |= TRIPLE_FLAG_FLATTENED;
+}
+
+static void mark_live_functions(struct compile_state *state)
+{
+ /* Ensure state->main_function is the last function in
+ * the list of functions.
+ */
+ if ((state->main_function->next != state->functions) ||
+ (state->functions->prev != state->main_function)) {
+ internal_error(state, 0,
+ "state->main_function is not at the end of the function list ");
+ }
+ state->main_function->u.cval = 1;
+ reverse_walk_functions(state, mark_live, 0);
+}
+
+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) {
+ FILE *fp = state->errout;
+ fprintf(fp, "global: ");
+ display_triple(fp, 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) {
+ FILE *fp = state->dbgout;
+ fprintf(fp, "\n");
+ loc(fp, state, 0);
+ fprintf(fp, "\n__________ %s _________\n", __FUNCTION__);
+ display_func(state, fp, ofunc);
+ fprintf(fp, "__________ %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 = old->lhs;
+ old_rhs = old->rhs;
+ 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;
+ new->id |= old->id & TRIPLE_FLAG_COPY;
+
+ /* During the copy remember new as user of old */
+ use_triple(old, 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);
+ 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", 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 void expand_inline_call(
+ struct compile_state *state, struct triple *me, struct triple *fcall)
+{
+ /* Inline the function call */
+ struct type *ptype;
+ struct triple *ofunc, *nfunc, *nfirst, *result, *retvar, *ins;
+ struct triple *end, *nend;
+ int pvals, i;
+
+ /* Find the triples */
+ ofunc = MISC(fcall, 0);
+ if (ofunc->op != OP_LIST) {
+ internal_error(state, 0, "improper function");
+ }
+ nfunc = copy_func(state, ofunc, fcall->occurance);
+ /* Prepend the parameter reading into the new function list */
+ ptype = nfunc->type->right;
+ pvals = fcall->rhs;
+ for(i = 0; i < pvals; i++) {
+ struct type *atype;
+ struct triple *arg, *param;
+ atype = ptype;
+ if ((ptype->type & TYPE_MASK) == TYPE_PRODUCT) {
+ atype = ptype->left;
+ }
+ param = farg(state, nfunc, i);
+ if ((param->type->type & TYPE_MASK) != (atype->type & TYPE_MASK)) {
+ internal_error(state, fcall, "param %d type mismatch", i);
+ }
+ arg = RHS(fcall, i);
+ flatten(state, fcall, write_expr(state, param, arg));
+ ptype = ptype->right;
+ }
+ result = 0;
+ if ((nfunc->type->left->type & TYPE_MASK) != TYPE_VOID) {
+ result = read_expr(state,
+ deref_index(state, fresult(state, nfunc), 1));
+ }
+ if (state->compiler->debug & DEBUG_INLINE) {
+ FILE *fp = state->dbgout;
+ fprintf(fp, "\n");
+ loc(fp, state, 0);
+ fprintf(fp, "\n__________ %s _________\n", __FUNCTION__);
+ display_func(state, fp, nfunc);
+ fprintf(fp, "__________ %s _________ done\n\n", __FUNCTION__);
+ }
+
+ /*
+ * Get rid of the extra triples
+ */
+ /* Remove the read of the return address */
+ ins = RHS(nfunc, 0)->prev->prev;
+ if ((ins->op != OP_READ) || (RHS(ins, 0) != fretaddr(state, nfunc))) {
+ internal_error(state, ins, "Not return addres read?");
+ }
+ release_triple(state, ins);
+ /* Remove the return instruction */
+ ins = RHS(nfunc, 0)->prev;
+ if (ins->op != OP_RET) {
+ internal_error(state, ins, "Not return?");
+ }
+ release_triple(state, ins);
+ /* Remove the retaddres variable */
+ retvar = fretaddr(state, nfunc);
+ if ((retvar->lhs != 1) ||
+ (retvar->op != OP_ADECL) ||
+ (retvar->next->op != OP_PIECE) ||
+ (MISC(retvar->next, 0) != retvar)) {
+ internal_error(state, retvar, "Not the return address?");
+ }
+ release_triple(state, retvar->next);
+ release_triple(state, retvar);
+
+ /* Remove the label at the start of the function */
+ ins = RHS(nfunc, 0);
+ if (ins->op != OP_LABEL) {
+ internal_error(state, ins, "Not label?");
+ }
+ nfirst = ins->next;
+ free_triple(state, ins);
+ /* Release the new function header */
+ RHS(nfunc, 0) = 0;
+ free_triple(state, nfunc);
+
+ /* Append the new function list onto the return list */
+ end = fcall->prev;
+ nend = nfirst->prev;
+ end->next = nfirst;
+ nfirst->prev = end;
+ nend->next = fcall;
+ fcall->prev = nend;
+
+ /* Now the result reading code */
+ if (result) {
+ result = flatten(state, fcall, result);
+ propogate_use(state, fcall, result);
+ }
+
+ /* Release the original fcall instruction */
+ release_triple(state, fcall);
+
+ return;
+}
+
+/*
+ *
+ * Type of the result variable.
+ *
+ * result
+ * |
+ * +----------+------------+
+ * | |
+ * union of closures result_type
+ * |
+ * +------------------+---------------+
+ * | |
+ * closure1 ... closuerN
+ * | |
+ * +----+--+-+--------+-----+ +----+----+---+-----+
+ * | | | | | | | | |
+ * var1 var2 var3 ... varN result var1 var2 ... varN result
+ * |
+ * +--------+---------+
+ * | |
+ * union of closures result_type
+ * |
+ * +-----+-------------------+
+ * | |
+ * closure1 ... closureN
+ * | |
+ * +-----+---+----+----+ +----+---+----+-----+
+ * | | | | | | | |
+ * var1 var2 ... varN result var1 var2 ... varN result
+ */
+
+static int add_closure_type(struct compile_state *state,
+ struct triple *func, struct type *closure_type)
+{
+ struct type *type, *ctype, **next;
+ struct triple *var, *new_var;
+ int i;
+
+#if 0
+ FILE *fp = state->errout;
+ fprintf(fp, "original_type: ");
+ name_of(fp, fresult(state, func)->type);
+ fprintf(fp, "\n");
+#endif
+ /* find the original type */
+ var = fresult(state, func);
+ type = var->type;
+ if (type->elements != 2) {
+ internal_error(state, var, "bad return type");
+ }
+
+ /* Find the complete closure type and update it */
+ ctype = type->left->left;
+ next = &ctype->left;
+ while(((*next)->type & TYPE_MASK) == TYPE_OVERLAP) {
+ next = &(*next)->right;
+ }
+ *next = new_type(TYPE_OVERLAP, *next, dup_type(state, closure_type));
+ ctype->elements += 1;
+
+#if 0
+ fprintf(fp, "new_type: ");
+ name_of(fp, type);
+ fprintf(fp, "\n");
+ fprintf(fp, "ctype: %p %d bits: %d ",
+ ctype, ctype->elements, reg_size_of(state, ctype));
+ name_of(fp, ctype);
+ fprintf(fp, "\n");
+#endif
+
+ /* Regenerate the variable with the new type definition */
+ new_var = pre_triple(state, var, OP_ADECL, type, 0, 0);
+ new_var->id |= TRIPLE_FLAG_FLATTENED;
+ for(i = 0; i < new_var->lhs; i++) {
+ LHS(new_var, i)->id |= TRIPLE_FLAG_FLATTENED;
+ }
+
+ /* Point everyone at the new variable */
+ propogate_use(state, var, new_var);
+
+ /* Release the original variable */
+ for(i = 0; i < var->lhs; i++) {
+ release_triple(state, LHS(var, i));
+ }
+ release_triple(state, var);
+
+ /* Return the index of the added closure type */
+ return ctype->elements - 1;
+}
+
+static struct triple *closure_expr(struct compile_state *state,
+ struct triple *func, int closure_idx, int var_idx)
+{
+ return deref_index(state,
+ deref_index(state,
+ deref_index(state, fresult(state, func), 0),
+ closure_idx),
+ var_idx);
+}
+
+
+static void insert_triple_set(
+ struct triple_reg_set **head, struct triple *member)
+{
+ struct triple_reg_set *new;
+ new = xcmalloc(sizeof(*new), "triple_set");
+ new->member = member;
+ new->new = 0;
+ new->next = *head;
+ *head = new;
+}
+
+static int ordered_triple_set(
+ struct triple_reg_set **head, struct triple *member)
+{
+ struct triple_reg_set **ptr;
+ if (!member)
+ return 0;
+ ptr = head;
+ while(*ptr) {
+ if (member == (*ptr)->member) {
+ return 0;
+ }
+ /* keep the list ordered */
+ if (member->id < (*ptr)->member->id) {
+ break;
+ }
+ ptr = &(*ptr)->next;
+ }
+ insert_triple_set(ptr, member);
+ return 1;
+}
+
+
+static void free_closure_variables(struct compile_state *state,
+ struct triple_reg_set **enclose)
+{
+ struct triple_reg_set *entry, *next;
+ for(entry = *enclose; entry; entry = next) {
+ next = entry->next;
+ do_triple_unset(enclose, entry->member);
+ }
+}
+
+static int lookup_closure_index(struct compile_state *state,
+ struct triple *me, struct triple *val)
+{
+ struct triple *first, *ins, *next;
+ first = RHS(me, 0);
+ ins = next = first;
+ do {
+ struct triple *result;
+ struct triple *index0, *index1, *index2, *read, *write;
+ ins = next;
+ next = ins->next;
+ if (ins->op != OP_CALL) {
+ continue;
+ }
+ /* I am at a previous call point examine it closely */
+ if (ins->next->op != OP_LABEL) {
+ internal_error(state, ins, "call not followed by label");
+ }
+ /* Does this call does not enclose any variables? */
+ if ((ins->next->next->op != OP_INDEX) ||
+ (ins->next->next->u.cval != 0) ||
+ (result = MISC(ins->next->next, 0)) ||
+ (result->id & TRIPLE_FLAG_LOCAL)) {
+ continue;
+ }
+ index0 = ins->next->next;
+ /* The pattern is:
+ * 0 index result < 0 >
+ * 1 index 0 < ? >
+ * 2 index 1 < ? >
+ * 3 read 2
+ * 4 write 3 var
+ */
+ for(index0 = ins->next->next;
+ (index0->op == OP_INDEX) &&
+ (MISC(index0, 0) == result) &&
+ (index0->u.cval == 0) ;
+ index0 = write->next)
+ {
+ index1 = index0->next;
+ index2 = index1->next;
+ read = index2->next;
+ write = read->next;
+ if ((index0->op != OP_INDEX) ||
+ (index1->op != OP_INDEX) ||
+ (index2->op != OP_INDEX) ||
+ (read->op != OP_READ) ||
+ (write->op != OP_WRITE) ||
+ (MISC(index1, 0) != index0) ||
+ (MISC(index2, 0) != index1) ||
+ (RHS(read, 0) != index2) ||
+ (RHS(write, 0) != read)) {
+ internal_error(state, index0, "bad var read");
+ }
+ if (MISC(write, 0) == val) {
+ return index2->u.cval;
+ }
+ }
+ } while(next != first);
+ return -1;
+}
+
+static inline int enclose_triple(struct triple *ins)
+{
+ return (ins && ((ins->type->type & TYPE_MASK) != TYPE_VOID));
+}
+
+static void compute_closure_variables(struct compile_state *state,
+ struct triple *me, struct triple *fcall, struct triple_reg_set **enclose)
+{
+ struct triple_reg_set *set, *vars, **last_var;
+ struct basic_blocks bb;
+ struct reg_block *rb;
+ struct block *block;
+ struct triple *old_result, *first, *ins;
+ size_t count, idx;
+ unsigned long used_indicies;
+ int i, max_index;
+#define MAX_INDICIES (sizeof(used_indicies)*CHAR_BIT)
+#define ID_BITS(X) ((X) & (TRIPLE_FLAG_LOCAL -1))
+ struct {
+ unsigned id;
+ int index;
+ } *info;
+
+
+ /* Find the basic blocks of this function */
+ bb.func = me;
+ bb.first = RHS(me, 0);
+ old_result = 0;
+ if (!triple_is_ret(state, bb.first->prev)) {
+ bb.func = 0;
+ } else {
+ old_result = fresult(state, me);
+ }
+ analyze_basic_blocks(state, &bb);
+
+ /* Find which variables are currently alive in a given block */
+ rb = compute_variable_lifetimes(state, &bb);
+
+ /* Find the variables that are currently alive */
+ block = block_of_triple(state, fcall);
+ if (!block || (block->vertex <= 0) || (block->vertex > bb.last_vertex)) {
+ internal_error(state, fcall, "No reg block? block: %p", block);
+ }
+
+#if DEBUG_EXPLICIT_CLOSURES
+ print_live_variables(state, &bb, rb, state->dbgout);
+ fflush(state->dbgout);
+#endif
+
+ /* Count the number of triples in the function */
+ first = RHS(me, 0);
+ ins = first;
+ count = 0;
+ do {
+ count++;
+ ins = ins->next;
+ } while(ins != first);
+
+ /* Allocate some memory to temorary hold the id info */
+ info = xcmalloc(sizeof(*info) * (count +1), "info");
+
+ /* Mark the local function */
+ first = RHS(me, 0);
+ ins = first;
+ idx = 1;
+ do {
+ info[idx].id = ins->id;
+ ins->id = TRIPLE_FLAG_LOCAL | idx;
+ idx++;
+ ins = ins->next;
+ } while(ins != first);
+
+ /*
+ * Build the list of variables to enclose.
+ *
+ * A target it to put the same variable in the
+ * same slot for ever call of a given function.
+ * After coloring this removes all of the variable
+ * manipulation code.
+ *
+ * The list of variables to enclose is built ordered
+ * program order because except in corner cases this
+ * gives me the stability of assignment I need.
+ *
+ * To gurantee that stability I lookup the variables
+ * to see where they have been used before and
+ * I build my final list with the assigned indicies.
+ */
+ vars = 0;
+ if (enclose_triple(old_result)) {
+ ordered_triple_set(&vars, old_result);
+ }
+ for(set = rb[block->vertex].out; set; set = set->next) {
+ if (!enclose_triple(set->member)) {
+ continue;
+ }
+ if ((set->member == fcall) || (set->member == old_result)) {
+ continue;
+ }
+ if (!local_triple(state, me, set->member)) {
+ internal_error(state, set->member, "not local?");
+ }
+ ordered_triple_set(&vars, set->member);
+ }
+
+ /* Lookup the current indicies of the live varialbe */
+ used_indicies = 0;
+ max_index = -1;
+ for(set = vars; set ; set = set->next) {
+ struct triple *ins;
+ int index;
+ ins = set->member;
+ index = lookup_closure_index(state, me, ins);
+ info[ID_BITS(ins->id)].index = index;
+ if (index < 0) {
+ continue;
+ }
+ if (index >= MAX_INDICIES) {
+ internal_error(state, ins, "index unexpectedly large");
+ }
+ if (used_indicies & (1 << index)) {
+ internal_error(state, ins, "index previously used?");
+ }
+ /* Remember which indicies have been used */
+ used_indicies |= (1 << index);
+ if (index > max_index) {
+ max_index = index;
+ }
+ }
+
+ /* Walk through the live variables and make certain
+ * everything is assigned an index.
+ */
+ for(set = vars; set; set = set->next) {
+ struct triple *ins;
+ int index;
+ ins = set->member;
+ index = info[ID_BITS(ins->id)].index;
+ if (index >= 0) {
+ continue;
+ }
+ /* Find the lowest unused index value */
+ for(index = 0; index < MAX_INDICIES; index++) {
+ if (!(used_indicies & (1 << index))) {
+ break;
+ }
+ }
+ if (index == MAX_INDICIES) {
+ internal_error(state, ins, "no free indicies?");
+ }
+ info[ID_BITS(ins->id)].index = index;
+ /* Remember which indicies have been used */
+ used_indicies |= (1 << index);
+ if (index > max_index) {
+ max_index = index;
+ }
+ }
+
+ /* Build the return list of variables with positions matching
+ * their indicies.
+ */
+ *enclose = 0;
+ last_var = enclose;
+ for(i = 0; i <= max_index; i++) {
+ struct triple *var;
+ var = 0;
+ if (used_indicies & (1 << i)) {
+ for(set = vars; set; set = set->next) {
+ int index;
+ index = info[ID_BITS(set->member->id)].index;
+ if (index == i) {
+ var = set->member;
+ break;
+ }
+ }
+ if (!var) {
+ internal_error(state, me, "missing variable");
+ }
+ }
+ insert_triple_set(last_var, var);
+ last_var = &(*last_var)->next;
+ }
+
+#if DEBUG_EXPLICIT_CLOSURES
+ /* Print out the variables to be enclosed */
+ loc(state->dbgout, state, fcall);
+ fprintf(state->dbgout, "Alive: \n");
+ for(set = *enclose; set; set = set->next) {
+ display_triple(state->dbgout, set->member);
+ }
+ fflush(state->dbgout);
+#endif
+
+ /* Clear the marks */
+ ins = first;
+ do {
+ ins->id = info[ID_BITS(ins->id)].id;
+ ins = ins->next;
+ } while(ins != first);
+
+ /* Release the ordered list of live variables */
+ free_closure_variables(state, &vars);
+
+ /* Release the storage of the old ids */
+ xfree(info);
+
+ /* Release the variable lifetime information */
+ free_variable_lifetimes(state, &bb, rb);
+
+ /* Release the basic blocks of this function */
+ free_basic_blocks(state, &bb);
+}
+
+static void expand_function_call(
+ struct compile_state *state, struct triple *me, struct triple *fcall)
+{
+ /* Generate an ordinary function call */
+ struct type *closure_type, **closure_next;
+ struct triple *func, *func_first, *func_last, *retvar;
+ struct triple *first;
+ struct type *ptype, *rtype;
+ struct triple *ret_addr, *ret_loc;
+ struct triple_reg_set *enclose, *set;
+ int closure_idx, pvals, i;
+
+#if DEBUG_EXPLICIT_CLOSURES
+ FILE *fp = state->dbgout;
+ fprintf(fp, "\ndisplay_func(me) ptr: %p\n", fcall);
+ display_func(state, fp, MISC(fcall, 0));
+ display_func(state, fp, me);
+ fprintf(fp, "__________ %s _________ done\n\n", __FUNCTION__);
+#endif
+
+ /* Find the triples */
+ func = MISC(fcall, 0);
+ func_first = RHS(func, 0);
+ retvar = fretaddr(state, func);
+ func_last = func_first->prev;
+ first = fcall->next;
+
+ /* Find what I need to enclose */
+ compute_closure_variables(state, me, fcall, &enclose);
+
+ /* Compute the closure type */
+ closure_type = new_type(TYPE_TUPLE, 0, 0);
+ closure_type->elements = 0;
+ closure_next = &closure_type->left;
+ for(set = enclose; set ; set = set->next) {
+ struct type *type;
+ type = &void_type;
+ if (set->member) {
+ type = set->member->type;
+ }
+ if (!*closure_next) {
+ *closure_next = type;
+ } else {
+ *closure_next = new_type(TYPE_PRODUCT, *closure_next,
+ type);
+ closure_next = &(*closure_next)->right;
+ }
+ closure_type->elements += 1;
+ }
+ if (closure_type->elements == 0) {
+ closure_type->type = TYPE_VOID;
+ }
+
+
+#if DEBUG_EXPLICIT_CLOSURES
+ fprintf(state->dbgout, "closure type: ");
+ name_of(state->dbgout, closure_type);
+ fprintf(state->dbgout, "\n");
+#endif
+
+ /* Update the called functions closure variable */
+ closure_idx = add_closure_type(state, func, closure_type);
+
+ /* 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;
+ pvals = fcall->rhs;
+ for(i = 0; i < pvals; i++) {
+ struct type *atype;
+ struct triple *arg, *param;
+ atype = ptype;
+ if ((ptype->type & TYPE_MASK) == TYPE_PRODUCT) {
+ atype = ptype->left;
+ }
+ param = farg(state, func, i);
+ if ((param->type->type & TYPE_MASK) != (atype->type & TYPE_MASK)) {
+ internal_error(state, fcall, "param type mismatch");
+ }
+ arg = RHS(fcall, i);
+ flatten(state, first, write_expr(state, param, arg));
+ ptype = ptype->right;
+ }
+ rtype = func->type->left;
+
+ /* Thread the triples together */
+ ret_loc = flatten(state, first, ret_loc);
+
+ /* Save the active variables in the result variable */
+ for(i = 0, set = enclose; set ; set = set->next, i++) {
+ if (!set->member) {
+ continue;
+ }
+ flatten(state, ret_loc,
+ write_expr(state,
+ closure_expr(state, func, closure_idx, i),
+ read_expr(state, set->member)));
+ }
+
+ /* Initialize the return value */
+ if ((rtype->type & TYPE_MASK) != TYPE_VOID) {
+ flatten(state, ret_loc,
+ write_expr(state,
+ deref_index(state, fresult(state, func), 1),
+ new_triple(state, OP_UNKNOWNVAL, rtype, 0, 0)));
+ }
+
+ ret_addr = flatten(state, ret_loc, ret_addr);
+ flatten(state, ret_loc, write_expr(state, retvar, ret_addr));
+ flatten(state, ret_loc,
+ call(state, retvar, ret_addr, func_first, func_last));
+
+ /* Find the result */
+ if ((rtype->type & TYPE_MASK) != TYPE_VOID) {
+ struct triple * result;
+ result = flatten(state, first,
+ read_expr(state,
+ deref_index(state, fresult(state, func), 1)));
+
+ propogate_use(state, fcall, result);
+ }
+
+ /* Release the original fcall instruction */
+ release_triple(state, fcall);
+
+ /* Restore the active variables from the result variable */
+ for(i = 0, set = enclose; set ; set = set->next, i++) {
+ struct triple_set *use, *next;
+ struct triple *new;
+ struct basic_blocks bb;
+ if (!set->member || (set->member == fcall)) {
+ continue;
+ }
+ /* Generate an expression for the value */
+ new = flatten(state, first,
+ read_expr(state,
+ closure_expr(state, func, closure_idx, i)));
+
+
+ /* If the original is an lvalue restore the preserved value */
+ if (is_lvalue(state, set->member)) {
+ flatten(state, first,
+ write_expr(state, set->member, new));
+ continue;
+ }
+ /*
+ * If the original is a value update the dominated uses.
+ */
+
+ /* Analyze the basic blocks so I can see who dominates whom */
+ bb.func = me;
+ bb.first = RHS(me, 0);
+ if (!triple_is_ret(state, bb.first->prev)) {
+ bb.func = 0;
+ }
+ analyze_basic_blocks(state, &bb);
+
+
+#if DEBUG_EXPLICIT_CLOSURES
+ fprintf(state->errout, "Updating domindated uses: %p -> %p\n",
+ set->member, new);
+#endif
+ /* If fcall dominates the use update the expression */
+ for(use = set->member->use; use; use = next) {
+ /* Replace use modifies the use chain and
+ * removes use, so I must take a copy of the
+ * next entry early.
+ */
+ next = use->next;
+ if (!tdominates(state, fcall, use->member)) {
+ continue;
+ }
+ replace_use(state, set->member, new, use->member);
+ }
+
+ /* Release the basic blocks, the instructions will be
+ * different next time, and flatten/insert_triple does
+ * not update the block values so I can't cache the analysis.
+ */
+ free_basic_blocks(state, &bb);
+ }
+
+ /* Release the closure variable list */
+ free_closure_variables(state, &enclose);
+
+ if (state->compiler->debug & DEBUG_INLINE) {
+ FILE *fp = state->dbgout;
+ fprintf(fp, "\n");
+ loc(fp, state, 0);
+ fprintf(fp, "\n__________ %s _________\n", __FUNCTION__);
+ display_func(state, fp, func);
+ display_func(state, fp, me);
+ fprintf(fp, "__________ %s _________ done\n\n", __FUNCTION__);
+ }
+
+ return;
+}
+
+static int do_inline(struct compile_state *state, struct triple *func)
+{
+ int do_inline;
+ int policy;
+
+ policy = state->compiler->flags & COMPILER_INLINE_MASK;
+ switch(policy) {
+ case COMPILER_INLINE_ALWAYS:
+ do_inline = 1;
+ if (func->type->type & ATTRIB_NOINLINE) {
+ error(state, func, "noinline with always_inline compiler option");
+ }
+ break;
+ case COMPILER_INLINE_NEVER:
+ do_inline = 0;
+ if (func->type->type & ATTRIB_ALWAYS_INLINE) {
+ error(state, func, "always_inline with noinline compiler option");
+ }
+ break;
+ case COMPILER_INLINE_DEFAULTON:
+ 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 = 1;
+ break;
+ }
+ break;
+ case COMPILER_INLINE_DEFAULTOFF:
+ 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 = 0;
+ break;
+ }
+ break;
+ case COMPILER_INLINE_NOPENALTY:
+ 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;
+ }
+ break;
+ default:
+ do_inline = 0;
+ internal_error(state, 0, "Unimplemented inline policy");
+ break;
+ }
+ /* Force inlining */
+ if (func->type->type & ATTRIB_NOINLINE) {
+ do_inline = 0;
+ }
+ if (func->type->type & ATTRIB_ALWAYS_INLINE) {
+ do_inline = 1;
+ }
+ return do_inline;
+}
+
+static void inline_function(struct compile_state *state, struct triple *me, void *arg)
+{
+ struct triple *first, *ptr, *next;
+ /* If the function is not used don't bother */
+ if (me->u.cval <= 0) {
+ return;
+ }
+ if (state->compiler->debug & DEBUG_CALLS2) {
+ FILE *fp = state->dbgout;
+ fprintf(fp, "in: %s\n",
+ me->type->type_ident->name);
+ }
+
+ first = RHS(me, 0);
+ ptr = next = first;
+ do {
+ struct triple *func, *prev;
+ 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 */
+ if (!do_inline(state, func)) {
+ /* Put a label after the fcall */
+ post_triple(state, ptr, OP_LABEL, &void_type, 0, 0);
+ continue;
+ }
+ if (state->compiler->debug & DEBUG_CALLS) {
+ FILE *fp = state->dbgout;
+ if (state->compiler->debug & DEBUG_CALLS2) {
+ loc(fp, state, ptr);
+ }
+ fprintf(fp, "inlining %s\n",
+ func->type->type_ident->name);
+ fflush(fp);
+ }
+
+ /* Update the function use counts */
+ func->u.cval -= 1;
+
+ /* Replace the fcall with the called function */
+ expand_inline_call(state, me, ptr);
+
+ next = prev->next;
+ } while (next != first);
+
+ ptr = next = first;
+ do {
+ struct triple *prev, *func;
+ ptr = next;
+ prev = ptr->prev;
+ next = ptr->next;
+ if (ptr->op != OP_FCALL) {
+ continue;
+ }
+ func = MISC(ptr, 0);
+ if (state->compiler->debug & DEBUG_CALLS) {
+ FILE *fp = state->dbgout;
+ if (state->compiler->debug & DEBUG_CALLS2) {
+ loc(fp, state, ptr);
+ }
+ fprintf(fp, "calling %s\n",
+ func->type->type_ident->name);
+ fflush(fp);
+ }
+ /* Replace the fcall with the instruction sequence
+ * needed to make the call.
+ */
+ expand_function_call(state, me, ptr);
+ next = prev->next;
+ } while(next != first);
+}
+
+static void inline_functions(struct compile_state *state, struct triple *func)
+{
+ inline_function(state, func, 0);
+ reverse_walk_functions(state, inline_function, 0);
+}
+
+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) {
+ FILE *fp = state->errout;
+ fprintf(fp, "%s func count: %d\n",
+ func->type->type_ident->name, func->u.cval);
+ }
+ if (func->u.cval == 0) {
+ return;
+ }
+
+ /* 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;
+}
+
+struct triple *input_asm(struct compile_state *state)
+{
+ struct asm_info *info;
+ struct triple *def;
+ int i, out;
+
+ info = xcmalloc(sizeof(*info), "asm_info");
+ info->str = "";
+
+ out = sizeof(arch_input_regs)/sizeof(arch_input_regs[0]);
+ memcpy(&info->tmpl.lhs, arch_input_regs, sizeof(arch_input_regs));