Signed-off-by: Jon Loeliger <[EMAIL PROTECTED]> --- ir_eval.c | 1504 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 1504 insertions(+), 0 deletions(-) create mode 100644 ir_eval.c
diff --git a/ir_eval.c b/ir_eval.c new file mode 100644 index 0000000..bd667cb --- /dev/null +++ b/ir_eval.c @@ -0,0 +1,1504 @@ +/* + * Copyright 2008 Jon Loeliger, Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include <stdio.h> + +#include "dtc.h" +#include "srcpos.h" +#include "ir.h" +#include "ir_scope.h" + + +/* + * Returns: + * 0 on success, with *val filled in + * -1 == bad characters in literal number + * -2 == literal out of range + * -3 == bad literal + */ + +int +ir_eval_literal_guessing(const char *s, int base, int bits, + unsigned long long *val) +{ + char *e; + + errno = 0; + *val = strtoull(s, &e, base); + + if (*e) + return -1; + else if ((errno == ERANGE) + || ((bits < 64) && (*val >= (1ULL << bits)))) + return -2; + else if (errno != 0) + return -3; + + return 0; +} + +unsigned long long +ir_eval_literal_str(const char *s, int base, int bits) +{ + unsigned long long val; + char *e; + + errno = 0; + val = strtoull(s, &e, base); + + if (*e) + die("bad characters in literal"); + else if ((errno == ERANGE) + || ((bits < 64) && (val >= (1ULL << bits)))) + die("literal out of range"); + else if (errno != 0) + die("bad literal"); + + return val; +} + + +struct ir * +ir_eval_cvt_to_string(struct ir *ir) +{ + char buf[30]; + unsigned long long lit1; + struct ir *ir_new; + + ir_new = ir_alloc(IR_LIT_STR, ir->ir_srcpos); + if (ir_is_constant(ir)) { + lit1 = ir_eval_for_addr(ir); + snprintf(buf, 30, "%llu", lit1); + ir_new->ir_lit_str = strdup(buf); + } else { + ir_error(ir, + "Can't convert %s to a string\n", + ir_type_string(ir->ir_type)); + } + + return ir_new; +} + + +/* + * FIXME: This should be named ir_is_constant_number() + */ +int +ir_is_constant(struct ir *ir) +{ + return ir && + (ir->ir_type == IR_LITERAL + || ir->ir_type == IR_LIT_BYTE + || ir->ir_type == IR_LIT_CELL + || ir->ir_type == IR_LIT_ADDR); +} + + +int +ir_is_string(struct ir *ir) +{ + return ir && ir->ir_type == IR_LIT_STR; +} + + +char * +ir_eval_for_label(struct ir *ir) +{ + char *str; + + if (ir == NULL) + return NULL; + + if (ir->ir_type == IR_LABEL + || ir->ir_type == IR_REF_PATH + || ir->ir_type == IR_REF_PHANDLE) { + str = strdup(ir->ir_label_name); + } else if (ir->ir_type == IR_LIT_STR) { + str = strdup(ir->ir_lit_str); + } else { + str = NULL; + } + + return str; +} + + +char * +ir_eval_for_name(struct ir *ir) +{ + struct ir *ir_val; + char *str; + + if (ir == NULL) + return NULL; + + ir_val = ir; + + if (ir_val == NULL) { + ir_error(ir, "Expected a name\n"); + return NULL; + } + + if (ir_val->ir_type != IR_PROPNODENAME + && ir_val->ir_type != IR_ID + && ir_val->ir_type != IR_LIT_STR) + return NULL; + + str = strdup(ir->ir_lit_str); + + return str; +} + + +/* + * FIXME: This is misnamed. Should be more like ir_eval_for_const() + */ +uint64_t +ir_eval_for_addr(struct ir *ir) +{ + unsigned long long a = 0; + + struct ir *ir_val; + + ir_val = ir_eval(ir); + if (ir_val == NULL) { + ir_error(ir, "Expected a const expression\n"); + return 0; + } + + /* + * FIXME: UH, ir_is_constant() check or something? + */ + + a = ir_val->ir_literal; + ir_free(ir_val); + debug("eval_for_addr() is 0x%08llx\n", a); + + return a; +} + + +char * +ir_eval_for_c_string(struct ir *ir) +{ + struct data dtmp; + char *p; + + if (ir == NULL) + return NULL; + + + if (!ir_is_string(ir)) + return NULL; + + p = ir->ir_lit_str; + dtmp = data_copy_escape_string(p, strlen(p)); + + return strdup(dtmp.val); +} + + +void +ir_eval_for_data(struct ir *ir, struct data *d) +{ + struct ir *ir_val; + struct ir *iri; + struct data dtmp; + char *lab; + cell_t c; + unsigned long long ulit64; + + if (ir == NULL) + return; + + ir_val = ir_eval(ir); + + switch (ir_val->ir_type) { + case IR_LIST: + for (iri = ir_val->ir_first; iri != NULL; iri = iri->ir_next) { + ir_eval_for_data(iri, d); + } + break; + + case IR_LIT_STR: + dtmp = data_copy_escape_string(ir_val->ir_lit_str, + strlen(ir_val->ir_lit_str)); + *d = data_merge(*d, dtmp); + break; + + case IR_LIT_BYTE: + *d = data_append_byte(*d, ir_val->ir_literal); + break; + + case IR_LIT_ADDR: + ulit64 = ir_val->ir_literal; + *d = data_append_addr(*d, ulit64); + break; + + case IR_LIT_CELL: + c = (cell_t) ir_val->ir_literal; + *d = data_append_cell(*d, c); + break; + + case IR_CELL: + ir_eval_for_data(ir_val->ir_expr1, d); + break; + + case IR_LABEL: + lab = ir_eval_for_label(ir); + *d = data_add_marker(*d, LABEL, lab); + break; + + case IR_REF_PATH: + lab = ir_eval_for_label(ir); + *d = data_add_marker(*d, REF_PATH, lab); + break; + + case IR_REF_PHANDLE: + lab = ir_eval_for_label(ir); + *d = data_add_marker(*d, REF_PHANDLE, lab); + *d = data_append_cell(*d, -1); + break; + + case IR_INCBIN: { + struct search_path path = { srcpos_file->dir, NULL, NULL }; + struct data dinc = empty_data; + char *file_name; + struct dtc_file *file; + unsigned long long start; + unsigned long long len; + struct ir *ir_pos; + + /* + * expr1 is file_name + * expr2 is start, NULL implies start of file + * expr3 is length, NULL implies whole file + */ + file_name = ir_eval_for_c_string(ir_val->ir_expr1); + file = dtc_open_file(file_name, &path); + + ir_pos = ir_val->ir_expr2; + start = ir_eval_for_addr(ir_val->ir_expr2); + if (ir_val->ir_expr3) + len = ir_eval_for_addr(ir_val->ir_expr3); + else + len = -1; + + if (start != 0) { + if (fseek(file->file, start, SEEK_SET) != 0) { + ir_error(ir_pos, + "Couldn't seek to offset %llu in \"%s\": %s", + (unsigned long long)start, + file_name, + strerror(errno)); + } + } + + dinc = data_copy_file(file->file, len); + *d = data_merge(*d, dinc); + dtc_close_file(file); + break; + } + + default: + ir_error(ir, + "Can't convert IR type %s to data\n", + ir_type_string(ir_val->ir_type)); + break; + } +} + + +struct ir_scope * +ir_eval_func_body(struct ir *ir_func) +{ + char *func_name; + struct ir *ir_func_def; + struct ir *ir_parameters; + struct ir *ir_statements; + struct ir_symbol *irsym; + struct ir *ir_p; + struct ir *ir_f; + char *param_name; + struct ir_scope *irs_scope; + struct ir *ir_next; + struct ir *ir_pos; + + if (ir_func == NULL) + return NULL; + + if (ir_func->ir_type != IR_FUNC_CALL) + return NULL; + + /* + * Lookup the function definition. + */ + ir_pos = ir_func->ir_expr1; + func_name = ir_eval_for_name(ir_func->ir_expr1); + debug("ir_eval_func_body(): Looking up %s\n", func_name); + + irsym = irs_lookup(func_name, IRS_ANY); + if (irsym == NULL || irsym->irsym_type != IRSYM_FUNCDEF) { + ir_error(ir_pos, + "%s isn't a function definition\n", + func_name); + return NULL; + } + + ir_func_def = irsym->irsym_value; + ir_statements = ir_func_def->ir_statements; + ir_parameters = ir_func_def->ir_declarations; + + debug("ir_eval_func_body(): Found definition for %s\n", + irsym->irsym_name); + + /* + * Set up parameter binding via eval-and-copy-in. + * + * First pass evaluates each parameter expression and + * builds a temporary list of each eval() parameter. + * These evaluations need to be done before the function + * scope is opened. + * + * Remember to dodge a possible parent IR_LIST node. + */ + debug("ir_eval_func_body(): Evaluating parameters\n"); + ir_p = ir_eval(ir_func->ir_expr2); + if (ir_p != NULL && ir_p->ir_type == IR_LIST) { + ir_p = ir_p->ir_first; + } + + /* + * Open an evaluation scope and symbol table for + * the function. + */ + irs_push_scope(IRS_FUNC_CALL); + + /* + * Second pass loops over each formal parameter + * and each actual expression simultaneously. + * + * Again remember to dodge a possible parent IR_LIST node. + */ + ir_f = ir_parameters; + if (ir_f != NULL && ir_f->ir_type == IR_LIST) { + ir_f = ir_f->ir_first; + } + + debug("ir_eval_func_body(): Binding parameter to formals\n"); + + ir_pos = ir_p; + while (ir_f != NULL && ir_p != NULL) { + param_name = ir_f->ir_lit_str; + + debug("ir_eval_func_body(): Binding parameter %s\n", + param_name); + + irsym = irs_create_local(param_name, IRSYM_VAR); + + irsym->irsym_value = ir_p; + ir_next = ir_p->ir_next; + ir_p->ir_next = ir_p->ir_prev = NULL; + + ir_f = ir_f->ir_next; + ir_p = ir_next; + ir_pos = ir_p; + } + + if (ir_f != NULL && ir_p == NULL) { + ir_error(ir_pos, + "Not enough parameters to %s (%s)\n", + func_name, + srcpos_string(ir_func_def->ir_srcpos)); + } + + if (ir_f == NULL && ir_p != NULL) { + ir_error(ir_pos, + "Too many parameters to %s (%s)\n", + func_name, + srcpos_string(ir_func_def->ir_srcpos)); + } + + /* + * And "invoke" it. + */ + ir_emit_statement_list(ir_statements); + + /* + * FIXME: Do parameter copy-out here? + */ + irs_scope = irs_pop_scope(); + + /* + * FIXME: This is a bit dodgy perhaps. + */ + return irs_scope; +} + + +struct ir * +ir_eval_func_call(struct ir *ir_func) +{ + struct ir_scope *irs_scope; + + /* + * Perform function body. + * Returned scope has "side effects". + * + * This context really just wants the return value, + * but we could debate using nodes and properties too? + */ + irs_scope = ir_eval_func_body(ir_func); + + if (!irs_scope) + return NULL; + + return irs_scope->irs_expr; +} + + +struct ir * +ir_eval(struct ir *ir) +{ + struct ir *ir_new; + struct ir *iri; + struct ir *ir1; + struct ir *ir2; + struct ir_symbol *irsym; + unsigned long long lit1; + unsigned long long lit2; + char *str; + int len; + + if (ir == NULL) + return NULL; + + ir_new = NULL; + + /* + * Perform IR node-specific evaluations. + */ + switch (ir->ir_type) { + case IR_LIT_STR: + case IR_LIT_BYTE: + case IR_LIT_CELL: + case IR_LIT_ADDR: + case IR_PROPNODENAME: + case IR_REF_PATH: + /* + * Values already present in the IR node. + */ + ir_new = ir_copy(ir); + break; + + case IR_REF_PHANDLE: + /* + * Promote a REF_PHANDLE of a LIT_STR to a + * direct REF_PHANDLE. + */ + ir_new = ir_copy(ir); + if (ir->ir_label) { + iri = ir_eval(ir->ir_label); + if (ir_is_string(iri)) { + ir_new->ir_label_name = iri->ir_lit_str; + } + } + break; + + case IR_LABEL: + ir_new = ir_copy(ir); + break; + + case IR_CVT_PROPNODENAME: + iri = ir_eval(ir->ir_expr1); + str = ir_eval_for_name(iri); + if (str) { + ir_new = ir_alloc(IR_PROPNODENAME, ir->ir_srcpos); + ir_new->ir_lit_str = str; + } + break; + + case IR_CVT_STRING: + iri = ir_eval(ir->ir_expr1); + ir_new = ir_eval_cvt_to_string(iri); + break; + + case IR_ID: + irsym = irs_lookup(ir->ir_lit_str, IRS_ANY); + if (irsym != NULL) { + ir_new = ir_eval(irsym->irsym_value); + } else { + ir_error(ir, + "Unknown value for \"%s\"\n", + ir->ir_lit_str); + } + break; + + case IR_LIST: + ir_new = ir_alloc(IR_LIST, ir->ir_srcpos); + for (iri = ir->ir_first; iri != NULL; iri = iri->ir_next) { + ir_list_append(ir_new, ir_eval(iri)); + } + break; + + case IR_SELECT: + /* + * Pick the ? or the : side. + */ + lit1 = ir_eval_for_addr(ir->ir_expr1); + if (lit1) { + ir_new = ir_eval(ir->ir_expr2); + } else { + ir_new = ir_eval(ir->ir_expr3); + } + break; + + case IR_OR: + lit1 = ir_eval_for_addr(ir->ir_expr1); + if (!lit1) { + lit1 = ir_eval_for_addr(ir->ir_expr2); + } + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = (lit1 != 0); + break; + + case IR_AND: + lit1 = ir_eval_for_addr(ir->ir_expr1); + if (lit1) { + lit1 = ir_eval_for_addr(ir->ir_expr2); + } + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = (lit1 != 0); + + break; + + case IR_BIT_OR: + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 | lit2; + break; + + case IR_BIT_XOR: + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 ^ lit2; + break; + + case IR_BIT_AND: + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 & lit2; + break; + + case IR_EQ: + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 == lit2; + break; + + case IR_LT: + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 < lit2; + break; + + case IR_LE: + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 <= lit2; + break; + + case IR_GT: + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 > lit2; + break; + + case IR_GE: + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 >= lit2; + break; + + case IR_NE: + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 != lit2; + break; + + case IR_LSHIFT: + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 << lit2; + break; + + case IR_RSHIFT: + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 >> lit2; + break; + + case IR_ADD: + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 + lit2; + break; + + case IR_MINUS: + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 - lit2; + break; + + case IR_MULT: + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 * lit2; + break; + + case IR_DIV: + /* FIXME: check for division by const 0 */ + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 / lit2; + break; + + case IR_MOD: + /* + * This is really a bit upside down due to not having + * a real typing system. Cope for now. + */ + ir1 = ir_eval(ir->ir_expr1); + ir2 = ir_eval(ir->ir_expr2); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + /* + * FIXME: check for division by const 0. + */ + lit1 = ir_eval_for_addr(ir->ir_expr1); + lit2 = ir_eval_for_addr(ir->ir_expr2); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1 % lit2; + + } else if (ir_is_string(ir1) || ir_is_string(ir2)) { + if (!ir_is_string(ir1)) + ir1 = ir_eval_cvt_to_string(ir1); + if (!ir_is_string(ir2)) + ir2 = ir_eval_cvt_to_string(ir2); + len = strlen(ir1->ir_lit_str) + + strlen(ir2->ir_lit_str) + 1; + str = xmalloc(len); + strcpy(str, ir1->ir_lit_str); + strcat(str, ir2->ir_lit_str); + str[len - 1] = 0; + ir_new = ir_alloc(IR_LIT_STR, ir->ir_srcpos); + ir_new->ir_lit_str = str; + } + break; + + case IR_BIT_COMPL: + lit1 = ir_eval_for_addr(ir->ir_expr1); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = ~lit1; + break; + + case IR_NOT: + lit1 = ir_eval_for_addr(ir->ir_expr1); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = !lit1; + break; + + case IR_UMINUS: + lit1 = ir_eval_for_addr(ir->ir_expr1); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = -lit1; + break; + + case IR_FUNC_CALL: + ir_new = ir_eval_func_call(ir); + break; + + case IR_BUILTIN: + ir_new = ir_eval_builtin(ir); + break; + + case IR_RANGE: + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir_eval(ir->ir_expr1); + ir_new->ir_expr2 = ir_eval(ir->ir_expr2); + break; + + case IR_CELL: + ir_new = ir_eval(ir->ir_expr1); + if (ir_is_constant(ir_new)) { + /* FIXME: Check for 32-bit range here? */ + ir_new->ir_type = IR_LIT_CELL; + } else if (ir_is_string(ir_new)) { + /* empty */ + } else { + ir_error(ir, "Can't determine CELL value\n"); + } + break; + + case IR_LITERAL: + lit1 = 0; + if (ir_eval_literal_guessing(ir->ir_lit_str, + 0, 64, &lit1) == 0) { + /* + * Smells like an number. + */ + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + ir_new->ir_literal = lit1; + } else { + /* + * Dunno what it is. Must be a string. + * FIXME: ir_eval_for_c_string() here? + */ + ir_new = ir_alloc(IR_LIT_STR, ir->ir_srcpos); + ir_new->ir_lit_str = strdup(ir->ir_lit_str); + } + break; + + case IR_INCBIN: + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir_eval(ir->ir_expr1); + ir_new->ir_expr2 = ir_eval(ir->ir_expr2); + ir_new->ir_expr3 = ir_eval(ir->ir_expr3); + break; + + case IR_PROP_DEF: + ir_error(ir, "Can't evaluate IR_PROP_DEF here.\n"); + break; + + case IR_ROOT: + case IR_RETURN: + case IR_IF: + case IR_FOR: + case IR_ASSIGN: + case IR_MEM_RESERVE: + case IR_FUNC_DEF: + case IR_PARAMDECL: + case IR_NODE: + ir_error(ir, + "Can't evaluate %s statements in expressions\n", + ir_type_string(ir->ir_type)); + break; + + default: + ir_error(ir, + "Unknown expression ir_type %s\n", + ir_type_string(ir->ir_type)); + } + + return ir_new; +} + + +struct ir * +ir_simplify(struct ir *ir, unsigned int ctxt) +{ + struct ir *ir1; + struct ir *ir2; + ir_type ir_type; + struct ir *ir_new; + unsigned long long lit1; + unsigned long long lit2; + unsigned long long ulit64; + + if (ir == NULL) + return NULL; + + /* + * First determine what the evaluation context will be + * for any sub-expression based on the current IR node. + */ + switch (ir->ir_type) { + case IR_CELL: + /* + * Pass new context down. + */ + ctxt = IR_EVAL_CTXT_CELL; + break; + + case IR_INCBIN: + case IR_IF: + case IR_FOR: + case IR_MEM_RESERVE: + /* + * These are always done in an ANY context. + */ + ctxt = IR_EVAL_CTXT_ANY; + break; + + default: + /* + * Use the supplied (parameter) context. + */ + break; + } + + + /* + * Perform IR node-specific optimizations. + */ + switch (ir->ir_type) { + case IR_ID: + case IR_LIT_STR: + case IR_LIT_BYTE: + case IR_LIT_CELL: + case IR_LIT_ADDR: + case IR_PROPNODENAME: + case IR_LABEL: + case IR_REF_PATH: + /* + * Already as simple as they can be. + */ + ir_new = ir_copy(ir); + break; + + case IR_REF_PHANDLE: + ir_new = ir_copy(ir); + ir_new->ir_label = ir_simplify(ir->ir_label, ctxt); + break; + + case IR_RETURN: + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir_simplify(ir->ir_expr1, ctxt); + break; + + case IR_MEM_RESERVE: + case IR_RANGE: + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir_simplify(ir->ir_expr1, ctxt); + ir_new->ir_expr2 = ir_simplify(ir->ir_expr2, ctxt); + break; + + case IR_CELL: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + if (ir1 && ir1->ir_type == IR_LIT_CELL) { + ir_new = ir1; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + } + break; + + case IR_PROP_DEF: + ir_new = ir_copy(ir); + ir_new->ir_label = ir_simplify(ir->ir_label, ctxt); + ir_new->ir_expr1 = ir_simplify(ir->ir_expr1, ctxt); + ir_new->ir_expr2 = ir_simplify(ir->ir_expr2, ctxt); + break; + + case IR_LITERAL: + /* + * Based on context, evaluate literal into 32 or 64 bits. + * LIT_ADDR could be a lie; it just means 64-bit. Feh. + */ + if (ctxt == IR_EVAL_CTXT_CELL) { + ulit64 = ir_eval_literal_str(ir->ir_lit_str, 0, 32); + ir_new = ir_alloc(IR_LIT_CELL, ir->ir_srcpos); + } else { + ulit64 = ir_eval_literal_str(ir->ir_lit_str, 0, 64); + ir_new = ir_alloc(IR_LIT_ADDR, ir->ir_srcpos); + } + ir_new->ir_literal = ulit64; + break; + + case IR_FUNC_CALL: + { + char *name; + irb_id irb; + + ir_new = ir_copy(ir); + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + name = ir_eval_for_name(ir1); + if (name) { + irb = ir_lookup_builtin_by_name(name); + if (irb != IRB_UNDEF) { + debug("ir_simplify(): Use builtin %s\n", + name); + ir_new->ir_type = IR_BUILTIN; + ir_new->ir_builtin_id = irb; + ir_new->ir_expr1 = ir2; + } else { + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + } else { + ir_error(ir1, "Unknown function %s\n", name); + } + } + break; + + case IR_BUILTIN: + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir_simplify(ir->ir_expr1, ctxt); + ir_new->ir_expr2 = ir_simplify(ir->ir_expr2, ctxt); + break; + + case IR_LIST: + ir_new = ir_alloc(IR_LIST, ir->ir_srcpos); + for (ir1 = ir->ir_first; ir1 != NULL; ir1 = ir1->ir_next) { + ir_list_append(ir_new, + ir_simplify(ir1, ctxt)); + } + break; + + case IR_INCBIN: + /* + * Ponder loading and caching files? + */ + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir_simplify(ir->ir_expr1, ctxt); + ir_new->ir_expr2 = ir_simplify(ir->ir_expr2, ctxt); + ir_new->ir_expr3 = ir_simplify(ir->ir_expr3, ctxt); + break; + + case IR_ASSIGN: + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir_simplify(ir->ir_expr1, ctxt); + ir_new->ir_expr2 = ir_simplify(ir->ir_expr2, ctxt); + break; + + case IR_IF: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + if (ir_is_constant(ir1)) { + /* + * Eliminate the IR_IF. + * Pick the THEN or ELSE statements only. + * FIXME: Fix leaking ir1 here. + */ + ulit64 = ir_eval_for_addr(ir1); + if (ulit64) { + /* + * Keep the THEN statements. + */ + ir_new = ir_simplify(ir->ir_statements, ctxt); + } else { + /* + * Keep the ELSE statements. + */ + ir_new = ir_simplify(ir->ir_statements2, ctxt); + } + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir1 = ir_simplify(ir->ir_statements, ctxt); + ir_new->ir_statements = ir1; + ir1 = ir_simplify(ir->ir_statements2, ctxt); + ir_new->ir_statements2 = ir1; + } + break; + + case IR_FOR: + /* + * Lots of optimizations possible here based on + * empty statements and trivial ranges. Later. + * FIXME: Do "for" simplification optimizations. + */ + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir_simplify(ir->ir_expr1, ctxt); + ir_new->ir_expr2 = ir_simplify(ir->ir_expr2, ctxt); + ir_new->ir_expr3 = ir_simplify(ir->ir_expr3, ctxt); + ir_new->ir_statements = ir_simplify(ir->ir_statements, ctxt); + break; + + case IR_SELECT: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + if (ir_is_constant(ir1)) { + /* + * Pick the ? or the : side. + * FIXME: Fix leaking ir1. + */ + ulit64 = ir_eval_for_addr(ir1); + if (ulit64) { + ir_new = ir_simplify(ir->ir_expr2, ctxt); + } else { + ir_new = ir_simplify(ir->ir_expr3, ctxt); + } + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir_simplify(ir->ir_expr1, ctxt); + ir_new->ir_expr2 = ir_simplify(ir->ir_expr2, ctxt); + ir_new->ir_expr3 = ir_simplify(ir->ir_expr3, ctxt); + } + break; + + case IR_CVT_PROPNODENAME: + /* + * IR_CVT_PROPNODENAME(IR_PROPNODENAME) == IR_PROPNODENAME, + * so drop the CVT. + */ + ir1 = ir_simplify(ir->ir_expr1, ctxt); + if (ir1 && ir1->ir_type == IR_PROPNODENAME) { + ir_new = ir1; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + } + break; + + case IR_CVT_STRING: + /* + * IR_CVT_STRING(IR_LIT_STR) == IR_LIT_STR, + * so drop the CVT. + */ + ir1 = ir_simplify(ir->ir_expr1, ctxt); + if (ir1 && ir1->ir_type == IR_LIT_STR) { + ir_new = ir1; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + } + break; + + case IR_OR: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + if (ir_is_constant(ir1)) { + ulit64 = ir_eval_for_addr(ir1); + if (ulit64) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = 1; + break; + } + } + + ir2 = ir_simplify(ir->ir_expr2, ctxt); + + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new->ir_literal = lit1 || lit2; + + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir_simplify(ir1, ctxt); + ir_new->ir_expr2 = ir_simplify(ir2, ctxt); + } + break; + + case IR_AND: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + if (ir_is_constant(ir1)) { + ulit64 = ir_eval_for_addr(ir1); + if (ulit64 == 0) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = 0; + break; + } + } + + ir2 = ir_simplify(ir->ir_expr2, ctxt); + + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new->ir_literal = lit1 && lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_BIT_OR: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 | lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_BIT_XOR: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 ^ lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_BIT_AND: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 & lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_EQ: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 == lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_LT: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 < lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_LE: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 <= lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_GT: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 > lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_GE: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 >= lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_NE: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 != lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_LSHIFT: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 << lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_RSHIFT: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 >> lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_ADD: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 + lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_MINUS: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 - lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_MULT: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 * lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_DIV: + /* FIXME: check for division by const 0 */ + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 / lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_MOD: + /* FIXME: check for division by const 0 */ + ir1 = ir_simplify(ir->ir_expr1, ctxt); + ir2 = ir_simplify(ir->ir_expr2, ctxt); + if (ir_is_constant(ir1) && ir_is_constant(ir2)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + lit2 = ir_eval_for_addr(ir2); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = lit1 % lit2; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + ir_new->ir_expr2 = ir2; + } + break; + + case IR_BIT_COMPL: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + if (ir_is_constant(ir1)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = ~lit1; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + } + break; + + case IR_NOT: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + if (ir_is_constant(ir1)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = !lit1; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + } + break; + + case IR_UMINUS: + ir1 = ir_simplify(ir->ir_expr1, ctxt); + if (ir_is_constant(ir1)) { + ir_type = (ctxt == IR_EVAL_CTXT_CELL) + ? IR_LIT_CELL : IR_LIT_ADDR; + lit1 = ir_eval_for_addr(ir1); + ir_new = ir_alloc(ir_type, ir->ir_srcpos); + ir_new->ir_literal = -lit1; + } else { + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir1; + } + break; + + case IR_ROOT: + ir_new = ir_copy(ir); + ir_new->ir_mem_reserves = + ir_simplify(ir->ir_mem_reserves, ctxt); + ir_new->ir_declarations = + ir_simplify(ir->ir_declarations, ctxt); + ir_new->ir_statements = + ir_simplify(ir->ir_statements, ctxt); + break; + + case IR_NODE: + ir_new = ir_copy(ir); + ir_new->ir_label = ir_simplify(ir->ir_label, ctxt); + ir_new->ir_name = ir_simplify(ir->ir_name, ctxt); + ir_new->ir_statements = + ir_simplify(ir->ir_statements, ctxt); + break; + + case IR_CONST_DEF: + ir_new = ir_copy(ir); + ir_new->ir_expr1 = ir_simplify(ir->ir_expr1, ctxt); + ir_new->ir_expr2 = ir_simplify(ir->ir_expr2, ctxt); + break; + + case IR_FUNC_DEF: + ir_new = ir_copy(ir); + ir_new->ir_name = ir_simplify(ir->ir_name, ctxt); + ir_new->ir_declarations = + ir_simplify(ir->ir_declarations, ctxt); + ir_new->ir_statements = + ir_simplify(ir->ir_statements, ctxt); + break; + + case IR_PARAMDECL: + default: + ir_new = NULL; + ir_error(ir, + "Can't simplify unknown ir_type %s\n", + ir_type_string(ir->ir_type)); + } + + return ir_new; +} -- 1.6.0.90.g436ed _______________________________________________ devicetree-discuss mailing list devicetree-discuss@ozlabs.org https://ozlabs.org/mailman/listinfo/devicetree-discuss