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

Reply via email to