/* expr.c expression construction and manipulations Copyright (C) 2001 Bill Currie Author: Bill Currie Date: 2001/06/15 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: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include "QF/alloc.h" #include "QF/dstring.h" #include "QF/mathlib.h" #include "QF/va.h" #include "tools/qfcc/include/qfcc.h" #include "tools/qfcc/include/algebra.h" #include "tools/qfcc/include/class.h" #include "tools/qfcc/include/def.h" #include "tools/qfcc/include/defspace.h" #include "tools/qfcc/include/diagnostic.h" #include "tools/qfcc/include/emit.h" #include "tools/qfcc/include/evaluate.h" #include "tools/qfcc/include/expr.h" #include "tools/qfcc/include/function.h" #include "tools/qfcc/include/idstuff.h" #include "tools/qfcc/include/method.h" #include "tools/qfcc/include/options.h" #include "tools/qfcc/include/reloc.h" #include "tools/qfcc/include/rua-lang.h" #include "tools/qfcc/include/shared.h" #include "tools/qfcc/include/strpool.h" #include "tools/qfcc/include/struct.h" #include "tools/qfcc/include/symtab.h" #include "tools/qfcc/include/target.h" #include "tools/qfcc/include/type.h" #include "tools/qfcc/include/value.h" ALLOC_STATE (expr_t, exprs); ALLOC_STATE (ex_listitem_t, listitems); const type_t * get_type (const expr_t *e) { const type_t *type = nullptr; switch (e->type) { case ex_inout: if (!e->inout.out) { internal_error (e, "inout with no out"); } type = get_type (e->inout.out); break; case ex_xvalue: bug (e, "should xvalue happen here?"); return get_type (e->xvalue.expr); case ex_branch: type = e->branch.ret_type; break; case ex_labelref: case ex_adjstk: case ex_with: type = &type_void; case ex_memset: return nullptr; case ex_error: return nullptr; case ex_return: case ex_decl: case ex_loop: case ex_select: case ex_message: case ex_process: internal_error (e, "unexpected expression type: %s", expr_names[e->type]); case ex_label: case ex_switch: case ex_caselabel: return nullptr; case ex_compound: type = e->compound.type; break; case ex_bool: if (options.code.progsversion == PROG_ID_VERSION) return &type_float; return &type_int; case ex_nil: if (e->nil) { type = e->nil; break; } // fall through case ex_state: return &type_void; break; case ex_block: if (e->block.result) { return get_type (e->block.result); } return &type_void; case ex_expr: case ex_uexpr: type = e->expr.type; break; case ex_def: type = e->def->type; break; case ex_symbol: type = e->symbol->type; break; case ex_temp: type = e->temp.type; break; case ex_value: type = e->value->type; break; case ex_vector: type = e->vector.type; break; case ex_selector: return &type_SEL; case ex_alias: type = e->alias.type; break; case ex_address: type = e->address.type; break; case ex_assign: return get_type (e->assign.dst); case ex_args: return &type_va_list; case ex_horizontal: type = e->hop.type; break; case ex_swizzle: type = e->swizzle.type; break; case ex_extend: type = e->extend.type; break; case ex_multivec: type = e->multivec.type; break; case ex_list: if (e->list.head) { auto last = (ex_listitem_t *) e->list.tail; return get_type (last->expr); } return nullptr; case ex_type: return nullptr; case ex_incop: return get_type (e->incop.expr); case ex_cond: //FIXME true_expr and false_expr need to have the same type, //unless one is nil return get_type (e->cond.true_expr); case ex_field: type = e->field.type; break; case ex_array: type = e->array.type; break; case ex_intrinsic: type = e->intrinsic.res_type; break; case ex_count: internal_error (e, "invalid expression"); } return type ? unalias_type (type) : nullptr; } etype_t extract_type (const expr_t *e) { auto type = get_type (e); if (type) return type->type; return ev_type_count; } const expr_t * type_mismatch (const expr_t *e1, const expr_t *e2, int op) { if (options.verbosity >= 2) { print_expr (e1); print_expr (e2); } return error (e1, "type mismatch: %s %s %s", get_type_string (get_type (e1)), get_op_string (op), get_type_string (get_type (e2))); } const expr_t * param_mismatch (const expr_t *e, int param, const char *fn, const type_t *t1, const type_t *t2) { return error (e, "type mismatch for parameter %d of %s: " "expected %s, got %s", param, fn, get_type_string (t1), get_type_string (t2)); } const expr_t * reference_error (const expr_t *e, const type_t *dst, const type_t *src) { return error (e, "cannot bind reference of type %s to %s", get_type_string (dst), get_type_string (src)); } const expr_t * test_error (const expr_t *e, const type_t *t) { dstring_t *s = dstring_newstr (); print_type_str (s, t); auto err = error (e, "%s cannot be tested", s->str); dstring_delete (s); return err; } expr_t * new_expr (void) { expr_t *e; ALLOC (16384, expr_t, exprs, e); e->loc = pr.loc; return e; } void restore_src_loc (expr_t **e) { if (*e) { pr.loc = (*e)->loc; FREE (exprs, *e); } } expr_t * set_src_loc (const expr_t *e) { if (!e) { return nullptr; } // save the current source location auto n = new_expr (); n->type = ex_error; pr.loc = e->loc; return n; } ex_listitem_t * new_listitem (const expr_t *e) { ex_listitem_t *li; ALLOC (16384, ex_listitem_t, listitems, li); li->expr = e; return li; } void list_append (ex_list_t *list, const expr_t *expr) { if (!list->tail) { list->tail = &list->head; } auto li = new_listitem (expr); *list->tail = li; list->tail = &li->next; } void list_append_list (ex_list_t *dst, const ex_list_t *src) { if (!dst->tail) { dst->tail = &dst->head; } for (auto s = src->head; s; s = s->next) { auto li = new_listitem (s->expr); *dst->tail = li; dst->tail = &li->next; } } void list_prepend (ex_list_t *list, const expr_t *expr) { if (!list->tail) { list->tail = &list->head; } auto li = new_listitem (expr); li->next = list->head; list->head = li; if (list->tail == &list->head) { list->tail = &li->next; } } expr_t * expr_append_expr (expr_t *list, const expr_t *expr) { if (list->type != ex_list) { internal_error (list, "not a list expression"); } list_append (&list->list, expr); return list; } expr_t * expr_prepend_expr (expr_t *list, const expr_t *expr) { if (list->type != ex_list) { internal_error (list, "not a list expression"); } list_prepend (&list->list, expr); return list; } expr_t * expr_append_list (expr_t *list, ex_list_t *append) { if (list->type != ex_list) { internal_error (list, "not a list expression"); } *list->list.tail = append->head; list->list.tail = append->tail; return list; } expr_t * expr_prepend_list (expr_t *list, ex_list_t *prepend) { if (list->type != ex_list) { internal_error (list, "not a list expression"); } if (!list->list.head) { list->list.tail = prepend->tail; } *prepend->tail = list->list.head; list->list.head = prepend->head; return list; } expr_t * new_list_expr (const expr_t *first) { auto list = new_expr (); list->type = ex_list; list->list.head = 0; list->list.tail = &list->list.head; if (first) { expr_append_expr (list, first); } return list; } int list_count (const ex_list_t *list) { int count = 0; for (auto li = list->head; li; li = li->next) { count++; } return count; } void list_scatter (const ex_list_t *list, const expr_t **exprs) { for (auto li = list->head; li; li = li->next) { *exprs++ = li->expr; } } void list_scatter_rev (const ex_list_t *list, const expr_t **exprs) { int count = list_count (list); for (auto li = list->head; li; li = li->next) { exprs[--count] = li->expr; } } void list_gather (ex_list_t *list, const expr_t **exprs, int count) { if (!list->tail) { list->tail = &list->head; } while (count-- > 0) { auto li = new_listitem (*exprs++); *list->tail = li; list->tail = &li->next; } } const char * new_label_name (void) { static int label = 0; int lnum = ++label; const char *fname = current_func->sym->name; const char *lname; lname = save_string (va (0, "$%s_%d", fname, lnum)); return lname; } expr_t * new_error_expr (void) { expr_t *e = new_expr (); e->type = ex_error; return e; } expr_t * new_state_expr (const expr_t *frame, const expr_t *think, const expr_t *step) { expr_t *s = new_expr (); s->type = ex_state; s->state.frame = frame; s->state.think = think; s->state.step = step; return s; } expr_t * new_boolean_expr (ex_boollist_t *true_list, ex_boollist_t *false_list, const expr_t *e) { expr_t *b = new_expr (); b->type = ex_bool; b->boolean.true_list = true_list; b->boolean.false_list = false_list; b->boolean.e = e; return b; } expr_t * new_label_expr (void) { expr_t *l = new_expr (); l->type = ex_label; l->label.name = new_label_name (); l->label.id = current_target.label_id++; return l; } const expr_t * named_label_expr (symbol_t *label) { symbol_t *sym; expr_t *l; if (!current_func) { // XXX this might be only an error internal_error (0, "label defined outside of function scope"); } sym = symtab_lookup (current_func->label_scope, label->name); if (sym) { return sym->expr; } l = new_label_expr (); l->label.name = save_string (va (0, "%s_%s", l->label.name, label->name)); l->label.symbol = label; label->sy_type = sy_expr; label->expr = l; symtab_addsymbol (current_func->label_scope, label); return label->expr; } expr_t * new_label_ref (const ex_label_t *label) { expr_t *l = new_expr (); l->type = ex_labelref; l->labelref.label = label; ((ex_label_t *) label)->used++; return l; } expr_t * new_block_expr (const expr_t *old) { scoped_src_loc (old); expr_t *b = new_expr (); b->type = ex_block; if (old) { if (old->type != ex_block) { internal_error (old, "not a block expression"); } b->block = old->block; } b->block.return_addr = __builtin_return_address (0); return b; } expr_t * new_binary_expr (int op, const expr_t *e1, const expr_t *e2) { if (e1->type == ex_error) { internal_error (e1, "error expr in new_binary_expr"); } if (e2 && e2->type == ex_error) { internal_error (e2, "error expr in new_binary_expr"); } expr_t *e = new_expr (); e->type = ex_expr; e->nodag = e1->nodag | e2->nodag; e->expr.constant = is_constexpr (e1) && is_constexpr (e2); e->expr.op = op; e->expr.e1 = e1; e->expr.e2 = e2; return e; } expr_t * build_block_expr (expr_t *list, bool set_result) { if (list->type != ex_list) { return list; } expr_t *b = new_block_expr (0); b->block.list = list->list; if (set_result && b->block.list.tail != &b->block.list.head) { auto last = (ex_listitem_t *) b->block.list.tail; b->block.result = last->expr; } return b; } expr_t * new_unary_expr (int op, const expr_t *e1) { if (e1 && e1->type == ex_error) { internal_error (e1, "error expr in new_unary_expr"); } expr_t *e = new_expr (); e->type = ex_uexpr; e->nodag = e1->nodag; e->expr.constant = is_constexpr (e1); e->expr.op = op; e->expr.e1 = e1; return e; } const expr_t * paren_expr (const expr_t *e) { auto paren = new_expr (); *paren = *e; paren->paren = true; return paren; } expr_t * new_horizontal_expr (int op, const expr_t *vec, type_t *type) { if (vec->type == ex_error) { return (expr_t *) vec; } auto vec_type = get_type (vec); if (!(is_math (vec_type) || is_boolean (vec_type)) || is_scalar (vec_type)) { internal_error (vec, "horizontal operand not a vector type"); } if (!is_scalar (type)) { internal_error (vec, "horizontal result not a scalar type"); } expr_t *e = new_expr (); e->type = ex_horizontal; e->hop.op = op; e->hop.vec = vec; e->hop.type = type; return e; } const expr_t * new_swizzle_expr (const expr_t *src, const char *swizzle) { if (is_error (src)) { return (expr_t *) src; } auto src_type = get_type (src); if (is_reference (src_type)) { src_type = dereference_type (src_type); src = pointer_deref (src); } int src_width = type_width (src_type); ex_swizzle_t swiz = {}; #define m(x) (1 << ((x) - 'a')) #define v(x, mask) (((x) & 0x60) == 0x60 && (m(x) & (mask))) #define vind(x) ((x) & 3) #define cind(x) (-(((x) >> 3) ^ (x)) & 3) #define tind(x) ((((~(x+1)>>2)&1) + x + 1) & 3) const int color = m('r') | m('g') | m('b') | m('a'); const int vector = m('x') | m('y') | m('z') | m('w'); const int texture = m('s') | m('t') | m('p') | m('q'); int type_mask = 0; int comp_count = 0; for (const char *s = swizzle; *s; s++) { if (comp_count >= 4) { return error (src, "too many components in swizzle"); } if (*s == '0') { swiz.zero |= 1 << comp_count; comp_count++; } else if (*s == '-') { swiz.neg |= 1 << comp_count; } else { int ind = 0; int mask = 0; if (v (*s, vector)) { ind = vind (*s); mask = 1; } else if (v (*s, color)) { ind = cind (*s); mask = 2; } else if (v (*s, texture)) { ind = tind (*s); mask = 4; } if (!mask) { return error (src, "invalid component in swizzle"); } if (type_mask & ~mask) { return error (src, "mixed components in swizzle"); } if (ind >= src_width) { return error (src, "swizzle component out of bounds"); } type_mask |= mask; swiz.source[comp_count++] = ind; } } swiz.src = src; swiz.type = vector_type (base_type (src_type), comp_count); expr_t *expr = new_expr (); expr->type = ex_swizzle; expr->swizzle = swiz; return expr; } const expr_t * new_extend_expr (const expr_t *src, const type_t *type, int ext, bool rev) { expr_t *expr = new_expr (); expr->type = ex_extend; expr->extend.src = src; expr->extend.extend = ext; expr->extend.reverse = rev; expr->extend.type = type; return evaluate_constexpr (expr); } expr_t * new_def_expr (def_t *def) { expr_t *e = new_expr (); e->type = ex_def; e->def = def; return e; } expr_t * new_symbol_expr (symbol_t *symbol) { expr_t *e = new_expr (); e->type = ex_symbol; e->symbol = symbol; return e; } expr_t * new_temp_def_expr (const type_t *type) { expr_t *e = new_expr (); e->type = ex_temp; e->temp.type = unalias_type (type); return e; } expr_t * new_nil_expr (void) { expr_t *e = new_expr (); e->type = ex_nil; return e; } expr_t * new_args_expr (void) { expr_t *e = new_expr (); e->type = ex_args; return e; } const expr_t * new_value_expr (ex_value_t *value, bool implicit) { expr_t *e = new_expr (); e->type = ex_value; e->value = value; e->implicit = implicit; return e; } const expr_t * new_zero_expr (const type_t *type) { pr_type_t zero[type_size (type)] = {}; return new_value_expr (new_type_value (type, zero), false); } const expr_t * new_name_expr (const char *name) { expr_t *e = new_expr (); symbol_t *sym; sym = symtab_lookup (current_symtab, name); if (!sym) sym = new_symbol (name); e->type = ex_symbol; e->symbol = sym; return e; } const expr_t * new_string_expr (const char *string_val) { return new_value_expr (new_string_val (string_val), false); } const expr_t * new_double_expr (double double_val, bool implicit) { return new_value_expr (new_double_val (double_val), implicit); } const expr_t * new_float_expr (float float_val, bool implicit) { return new_value_expr (new_float_val (float_val), implicit); } const expr_t * new_vector_expr (const float *vector_val) { return new_value_expr (new_vector_val (vector_val), false); } const expr_t * new_entity_expr (int entity_val) { return new_value_expr (new_entity_val (entity_val), false); } const expr_t * new_deffield_expr (int field_val, const type_t *type, def_t *def) { return new_value_expr (new_field_val (field_val, type, def), false); } const expr_t * new_func_expr (int func_val, const type_t *type) { return new_value_expr (new_func_val (func_val, type), false); } const expr_t * new_pointer_expr (int val, const type_t *type, def_t *def) { return new_value_expr (new_pointer_val (val, type, def, 0), false); } const expr_t * new_quaternion_expr (const float *quaternion_val) { return new_value_expr (new_quaternion_val (quaternion_val), false); } const expr_t * new_bool_expr (bool bool_val) { pr_type_t data = { .value = -bool_val }; auto val = new_type_value (&type_bool, &data); return new_value_expr (val, false); } const expr_t * new_lbool_expr (bool lbool_val) { pr_type_t data[2] = { { .value = -lbool_val }, { .value = -lbool_val }, }; auto val = new_type_value (&type_lbool, data); return new_value_expr (val, false); } const expr_t * new_int_expr (int int_val, bool implicit) { return new_value_expr (new_int_val (int_val), implicit); } const expr_t * new_uint_expr (unsigned uint_val) { return new_value_expr (new_uint_val (uint_val), false); } const expr_t * new_long_expr (pr_long_t long_val, bool implicit) { return new_value_expr (new_long_val (long_val), implicit); } bool is_long_val (const expr_t *e) { if (e->type == ex_nil) { return true; } if (e->type == ex_value && e->value->lltype == ev_long) { return true; } if (e->type == ex_symbol && e->symbol->sy_type == sy_const) { auto type = e->symbol->type; if (is_long (type)) { return true; } } if (e->type == ex_def && e->def->constant) { auto type = e->def->type; if (is_long (type)) { return true; } } return false; } bool is_ulong_val (const expr_t *e) { if (e->type == ex_nil) { return true; } if (e->type == ex_value && e->value->lltype == ev_ulong) { return true; } if (e->type == ex_symbol && e->symbol->sy_type == sy_const) { auto type = e->symbol->type; if (is_ulong (type)) { return true; } } if (e->type == ex_def && e->def->constant) { auto type = e->def->type; if (is_ulong (type)) { return true; } } return false; } pr_long_t expr_long (const expr_t *e) { if (e->type == ex_nil) { return 0; } if (e->type == ex_value && e->value->lltype == ev_long) { return e->value->long_val; } internal_error (e, "not a long constant"); } const expr_t * new_ulong_expr (pr_ulong_t ulong_val) { return new_value_expr (new_ulong_val (ulong_val), false); } pr_ulong_t expr_ulong (const expr_t *e) { if (e->type == ex_nil) { return 0; } if (e->type == ex_value && e->value->lltype == ev_ulong) { return e->value->ulong_val; } internal_error (e, "not a ulong constant"); } const expr_t * new_short_expr (short short_val) { return new_value_expr (new_short_val (short_val), false); } const expr_t * new_ushort_expr (unsigned short ushort_val) { return new_value_expr (new_ushort_val (ushort_val), false); } bool is_error (const expr_t *e) { return e && e->type == ex_error; } bool is_constant (const expr_t *e) { while (e->type == ex_alias) { e = e->alias.expr; } if (e->type == ex_nil || e->type == ex_value || e->type == ex_labelref || (e->type == ex_symbol && e->symbol->sy_type == sy_const) || (e->type == ex_symbol && e->symbol->sy_type == sy_def && e->symbol->def->constant)) return true; return false; } bool is_constexpr (const expr_t *e) { while (e->type == ex_alias) { e = e->alias.expr; } if ((e->type == ex_expr || e->type == ex_uexpr) && e->expr.constant) { return true; } if (e->type == ex_symbol) { return e->symbol->is_constexpr; } return is_constant (e); } bool is_variable (const expr_t *e) { while (e->type == ex_alias) { e = e->alias.expr; } if (e->type == ex_def || (e->type == ex_symbol && e->symbol->sy_type == sy_def) || e->type == ex_temp) { return true; } return false; } bool is_selector (const expr_t *e) { return e->type == ex_selector; } const expr_t * constant_expr (const expr_t *e) { symbol_t *sym; ex_value_t *value; if (!is_constant (e)) return e; if (e->type == ex_nil || e->type == ex_value || e->type == ex_labelref) return e; if (e->type != ex_symbol) return e; sym = e->symbol; if (sym->sy_type == sy_const) { value = sym->value; } else if (sym->sy_type == sy_def && sym->def->constant) { //FIXME pointers and fields internal_error (e, "what to do here?"); //memset (&value, 0, sizeof (value)); //memcpy (&value.v, &D_INT (sym->def), //type_size (sym->def->type) * sizeof (pr_type_t)); } else { return e; } scoped_src_loc (e); return new_value_expr (value, false); } bool is_nil (const expr_t *e) { return e->type == ex_nil; } bool is_string_val (const expr_t *e) { if (e->type == ex_nil) return true; if (e->type == ex_value && e->value->lltype == ev_string) return true; if (e->type == ex_symbol && e->symbol->sy_type == sy_const && e->symbol->type->type == ev_string) return true; return false; } const char * expr_string (const expr_t *e) { if (e->type == ex_nil) return nullptr; if (e->type == ex_value && e->value->lltype == ev_string) return e->value->string_val; internal_error (e, "not a string constant"); } bool is_float_val (const expr_t *e) { if (e->type == ex_nil) return true; if (e->type == ex_value && e->value->lltype == ev_float) return true; if (e->type == ex_symbol && e->symbol->sy_type == sy_const && is_float (e->symbol->type)) return true; return false; } bool is_double_val (const expr_t *e) { if (e->type == ex_nil) return true; if (e->type == ex_value && e->value->lltype == ev_double) return true; if (e->type == ex_symbol && e->symbol->sy_type == sy_const && is_double (e->symbol->type)) return true; return false; } double expr_double (const expr_t *e) { if (e->type == ex_nil) return 0; if (e->type == ex_value && e->value->lltype == ev_double) return e->value->double_val; if (e->type == ex_symbol && e->symbol->sy_type == sy_const && e->symbol->type->type == ev_double) return e->symbol->value->double_val; if (e->type == ex_symbol && e->symbol->sy_type == sy_def && e->symbol->def->constant && is_double (e->symbol->def->type)) return D_DOUBLE (e->symbol->def); internal_error (e, "not a double constant"); } float expr_float (const expr_t *e) { if (e->type == ex_nil) return 0; if (e->type == ex_value && e->value->lltype == ev_float) return e->value->float_val; if (e->type == ex_symbol && e->symbol->sy_type == sy_const && e->symbol->type->type == ev_float) return e->symbol->value->float_val; if (e->type == ex_symbol && e->symbol->sy_type == sy_def && e->symbol->def->constant && is_float (e->symbol->def->type)) return D_FLOAT (e->symbol->def); internal_error (e, "not a float constant"); } bool is_vector_val (const expr_t *e) { if (e->type == ex_nil) return true; if (e->type == ex_value && e->value->lltype == ev_vector) return true; if (e->type == ex_symbol && e->symbol->sy_type == sy_const && e->symbol->type->type == ev_vector) return true; return false; } const float * expr_vector (const expr_t *e) { if (e->type == ex_nil) return vec3_origin; if (e->type == ex_value && e->value->lltype == ev_vector) return e->value->vector_val; if (e->type == ex_symbol && e->symbol->sy_type == sy_const && e->symbol->type->type == ev_vector) return e->symbol->value->vector_val; if (e->type == ex_symbol && e->symbol->sy_type == sy_def && e->symbol->def->constant && e->symbol->def->type->type == ev_vector) return D_VECTOR (e->symbol->def); internal_error (e, "not a vector constant"); } bool is_quaternion_val (const expr_t *e) { if (e->type == ex_nil) return true; if (e->type == ex_value && e->value->lltype == ev_quaternion) return true; if (e->type == ex_symbol && e->symbol->sy_type == sy_const && e->symbol->type->type == ev_quaternion) return true; return false; } const float * expr_quaternion (const expr_t *e) { if (e->type == ex_nil) return quat_origin; if (e->type == ex_value && e->value->lltype == ev_quaternion) return e->value->quaternion_val; if (e->type == ex_symbol && e->symbol->sy_type == sy_const && e->symbol->type->type == ev_quaternion) return e->symbol->value->quaternion_val; if (e->type == ex_symbol && e->symbol->sy_type == sy_def && e->symbol->def->constant && e->symbol->def->type->type == ev_quaternion) return D_QUAT (e->symbol->def); internal_error (e, "not a quaternion constant"); } bool is_int_val (const expr_t *e) { if (e->type == ex_nil) { return true; } if (e->type == ex_value && e->value->lltype == ev_int) { return true; } if (e->type == ex_symbol && e->symbol->sy_type == sy_const) { auto type = e->symbol->type; if (!is_long (type) && !is_ulong (type) && is_integral (type)) { return true; } } if (e->type == ex_def && e->def->constant) { auto type = e->def->type; if (!is_long (type) && !is_ulong (type) && is_integral (type)) { return true; } } return false; } int expr_int (const expr_t *e) { if (e->type == ex_nil) { return 0; } if (e->type == ex_value && e->value->lltype == ev_int) { return e->value->int_val; } if (e->type == ex_value && e->value->lltype == ev_short) { return e->value->short_val; } if (e->type == ex_symbol && e->symbol->sy_type == sy_const && (e->symbol->type->type == ev_int || is_enum (e->symbol->type))) { return e->symbol->value->int_val; } if (e->type == ex_symbol && e->symbol->sy_type == sy_def && e->symbol->def->constant && is_integral (e->symbol->def->type)) { return D_INT (e->symbol->def); } if (e->type == ex_def && e->def->constant && is_integral (e->def->type)) { return D_INT (e->def); } internal_error (e, "not an int constant"); } bool is_uint_val (const expr_t *e) { if (e->type == ex_nil) { return true; } if (e->type == ex_value && e->value->lltype == ev_uint) { return true; } if (e->type == ex_symbol && e->symbol->sy_type == sy_const && is_integral (e->symbol->type)) { return true; } if (e->type == ex_def && e->def->constant && is_integral (e->def->type)) { return true; } return false; } unsigned expr_uint (const expr_t *e) { if (e->type == ex_nil) { return 0; } if (e->type == ex_value && e->value->lltype == ev_uint) { return e->value->uint_val; } if (e->type == ex_symbol && e->symbol->sy_type == sy_const && e->symbol->type->type == ev_uint) { return e->symbol->value->uint_val; } if (e->type == ex_symbol && e->symbol->sy_type == sy_def && e->symbol->def->constant && is_integral (e->symbol->def->type)) { return D_INT (e->symbol->def); } if (e->type == ex_def && e->def->constant && is_integral (e->def->type)) { return D_INT (e->def); } internal_error (e, "not an unsigned constant"); } bool is_short_val (const expr_t *e) { if (e->type == ex_nil) { return true; } if (e->type == ex_value && e->value->lltype == ev_short) { return true; } if (e->type == ex_symbol && e->symbol->sy_type == sy_const && e->symbol->type->type == ev_short) { return true; } return false; } short expr_short (const expr_t *e) { if (e->type == ex_nil) { return 0; } if (e->type == ex_value && e->value->lltype == ev_short) { return e->value->short_val; } if (e->type == ex_symbol && e->symbol->sy_type == sy_const && e->symbol->type->type == ev_short) { return e->symbol->value->short_val; } internal_error (e, "not a short constant"); } unsigned short expr_ushort (const expr_t *e) { if (e->type == ex_nil) { return 0; } if (e->type == ex_value && e->value->lltype == ev_ushort) { return e->value->ushort_val; } if (e->type == ex_symbol && e->symbol->sy_type == sy_const && e->symbol->type->type == ev_ushort) { return e->symbol->value->ushort_val; } internal_error (e, "not a ushort constant"); } bool is_integral_val (const expr_t *e) { if (is_constant (e)) { if (is_int_val (e)) { return true; } if (is_uint_val (e)) { return true; } if (is_short_val (e)) { return true; } if (is_long_val (e)) { return true; } if (is_ulong_val (e)) { return true; } } return false; } bool is_floating_val (const expr_t *e) { if (is_constant (e)) { if (is_float_val (e)) { return true; } if (is_double_val (e)) { return true; } } return false; } pr_long_t expr_integral (const expr_t *e) { if (is_constant (e)) { if (is_int_val (e)) { return expr_int (e); } if (is_uint_val (e)) { return expr_uint (e); } if (is_short_val (e)) { return expr_short (e); } if (is_long_val (e)) { return expr_long (e); } if (is_ulong_val (e)) { return expr_ulong (e); } } internal_error (e, "not an integral constant"); } double expr_floating (const expr_t *e) { if (is_constant (e)) { if (is_float_val (e)) { return expr_float (e); } if (is_double_val (e)) { return expr_double (e); } } internal_error (e, "not an integral constant"); } bool is_pointer_val (const expr_t *e) { if (e->type == ex_value && e->value->lltype == ev_ptr) { return true; } return false; } bool is_math_val (const expr_t *e) { while (e->type == ex_alias) { e = e->alias.expr; } if (e->type == ex_value && is_math (e->value->type)) { return true; } if (e->type == ex_symbol && e->symbol->sy_type == sy_const && is_math (e->symbol->value->type)) { return true; } return false; } const expr_t * new_alias_expr (const type_t *type, const expr_t *expr) { if (is_pointer (type) && expr->type == ex_address) { auto new = new_address_expr (type, expr->address.lvalue, expr->address.offset); new->address.type = type; return new; } if (expr->type == ex_alias) { if (expr->alias.offset) { return new_offset_alias_expr (type, expr, 0); } expr = expr->alias.expr; } // this can happen when constant folding an offset pointer results in // a noop due to the offset being 0 and thus casting back to the original // type if (type == get_type (expr)) { return (expr_t *) expr; } expr_t *alias = new_expr (); alias->type = ex_alias; alias->alias.type = type; alias->alias.expr = expr; alias->loc = expr->loc; return edag_add_expr (alias); } const expr_t * new_offset_alias_expr (const type_t *type, const expr_t *expr, int offset) { if (expr->type == ex_alias && expr->alias.offset) { const expr_t *ofs_expr = expr->alias.offset; if (!is_constant (ofs_expr)) { internal_error (ofs_expr, "non-constant offset for alias expr"); } offset += expr_int (ofs_expr); if (expr->alias.expr->type == ex_alias) { internal_error (expr, "alias expr of alias expr"); } expr = expr->alias.expr; } expr_t *alias = new_expr (); alias->type = ex_alias; alias->alias.type = type; alias->alias.expr = edag_add_expr (expr); alias->alias.offset = edag_add_expr (new_int_expr (offset, false)); alias->loc = expr->loc; return edag_add_expr (evaluate_constexpr (alias)); } expr_t * new_address_expr (const type_t *lvtype, const expr_t *lvalue, const expr_t *offset) { expr_t *addr = new_expr (); addr->type = ex_address; addr->address.type = pointer_type (lvtype); addr->address.lvalue = lvalue; addr->address.offset = offset; return addr; } expr_t * new_assign_expr (const expr_t *dst, const expr_t *src) { expr_t *addr = new_expr (); addr->type = ex_assign; addr->assign.dst = dst; addr->assign.src = src; return addr; } expr_t * new_return_expr (const expr_t *ret_val) { expr_t *retrn = new_expr (); retrn->type = ex_return; retrn->retrn.ret_val = ret_val; return retrn; } expr_t * new_adjstk_expr (int mode, int offset) { expr_t *adj = new_expr (); adj->type = ex_adjstk; adj->adjstk.mode = mode; adj->adjstk.offset = offset; return adj; } expr_t * new_with_expr (int mode, int reg, const expr_t *val) { expr_t *with = new_expr (); with->type = ex_with; with->with.mode = mode; with->with.reg = reg; with->with.with = val; return with; } static const expr_t * param_expr (const char *name, const type_t *type) { symbol_t *sym; expr_t *sym_expr; sym = make_symbol (name, &type_param, pr.symtab->space, sc_extern); if (!sym->table) symtab_addsymbol (pr.symtab, sym); sym_expr = new_symbol_expr (sym); return new_alias_expr (type, sym_expr); } const expr_t * new_ret_expr (type_t *type) { return param_expr (".return", type); } const expr_t * new_param_expr (const type_t *type, int num) { return param_expr (va (0, ".param_%d", num), type); } expr_t * new_memset_expr (const expr_t *dst, const expr_t *val, const expr_t *count) { expr_t *e = new_expr (); e->type = ex_memset; e->memset.dst = dst; e->memset.val = val; e->memset.count = count; return e; } expr_t * new_type_expr (const type_t *type) { expr_t *e = new_expr (); e->type = ex_type; e->typ.type = type; return e; } expr_t * append_expr (expr_t *block, const expr_t *e) { if (block->type != ex_block) { internal_error (block, "not a block expression"); } if (e == block) { internal_error (block, "adding block to itself"); } if (!e || e->type == ex_error) return block; list_append (&block->block.list, e); return block; } expr_t * prepend_expr (expr_t *block, const expr_t *e) { if (block->type != ex_block) internal_error (block, "not a block expression"); if (!e || e->type == ex_error) return block; list_prepend (&block->block.list, e); return block; } symbol_t * get_name (const expr_t *e) { if (e->type == ex_symbol) { return e->symbol; } return nullptr; } symbol_t * get_struct_field (const type_t *t1, const expr_t *e1, const expr_t *e2) { t1 = unalias_type (t1); symtab_t *strct = t1->symtab; symbol_t *sym = get_name (e2); symbol_t *field; if (!strct) { error (e1, "dereferencing pointer to incomplete type"); return nullptr; } if (!sym) { error (e2, "field reference is not a name"); return nullptr; } field = symtab_lookup (strct, sym->name); if (!field && !is_entity(t1) && !is_nonscalar (t1)) { const char *name = t1->name; if (!strncmp (name, "tag ", 4)) { name += 4; } error (e2, "'%s' has no member named '%s'", name, sym->name); } return field; } static const expr_t * swizzle_expr (const expr_t *vec, const expr_t *swizzle) { auto sym = get_name (swizzle); if (!sym) { // error already reported return vec; } return new_swizzle_expr (vec, sym->name); } const expr_t * field_expr (const expr_t *e1, const expr_t *e2) { const type_t *t1, *t2; expr_t *e; if (e1->type == ex_error) return e1; if (e1->type == ex_symbol && e1->symbol->sy_type == sy_namespace) { if (e2->type != ex_symbol) { return error (e2, "symbol required for namespace access"); } auto namespace = e1->symbol->namespace; auto sym = symtab_lookup (namespace, e2->symbol->name); if (!sym) { return error (e2, "%s not in %s namespace", e2->symbol->name, e1->symbol->name); } return new_symbol_expr (sym); } t1 = get_type (e1); if (!t1) { return new_error_expr (); } if (is_entity (t1)) { symbol_t *field = 0; if (e2->type == ex_symbol) field = get_struct_field (&type_entity, e1, e2); if (field) { e2 = new_deffield_expr (0, field->type, field->def); e = new_binary_expr ('.', e1, e2); e->expr.type = field->type; return e; } else { t2 = get_type (e2); if (e2->type == ex_error) return e2; if (t2->type == ev_field) { e = new_binary_expr ('.', e1, e2); e->expr.type = t2->fldptr.type; return e; } } } else if (is_pointer (t1)) { if (is_struct (t1->fldptr.type) || is_union (t1->fldptr.type)) { symbol_t *field; field = get_struct_field (t1->fldptr.type, e1, e2); if (!field) return e1; const expr_t *offset = new_short_expr (field->offset); e1 = offset_pointer_expr (e1, offset); if (e1->type == ex_error) { return e1; } e1 = cast_expr (pointer_type (field->type), e1); return unary_expr ('.', e1); } else if (is_class (t1->fldptr.type)) { class_t *class = t1->fldptr.type->class; symbol_t *sym = e2->symbol;//FIXME need to check symbol_t *ivar; int protected = class_access (current_class, class); ivar = class_find_ivar (class, protected, sym->name); if (!ivar) return new_error_expr (); const expr_t *offset = new_short_expr (ivar->offset); e1 = offset_pointer_expr (e1, offset); e1 = cast_expr (pointer_type (ivar->type), e1); if (e1->type == ex_error) { return e1; } return unary_expr ('.', e1); } } else if (is_algebra (t1)) { return algebra_field_expr (e1, e2); } else if (is_nonscalar (t1) || is_struct (t1) || is_union (t1)) { symbol_t *field; field = get_struct_field (t1, e1, e2); if (!field) { if (is_nonscalar (t1)) { return swizzle_expr (e1, e2); } return e1; } if (e1->type == ex_expr && e1->expr.op == '.' && is_entity(get_type (e1->expr.e1))) { // undo the . expression e2 = e1->expr.e2; e1 = e1->expr.e1; // offset the field expresion if (e2->type == ex_symbol) { symbol_t *sym; def_t *def; sym = symtab_lookup (pr.entity_fields, e2->symbol->name); if (!sym) { internal_error (e2, "failed to find entity field %s", e2->symbol->name); } def = sym->def; e2 = new_deffield_expr (0, field->type, def); } else if (e2->type != ex_value || e2->value->lltype != ev_field) { internal_error (e2, "unexpected field exression"); } auto fv = new_field_val (e2->value->pointer.val + field->offset, field->type, e2->value->pointer.def); scoped_src_loc (e2); e2 = new_value_expr (fv, false); // create a new . expression return field_expr (e1, e2); } else { if (e1->type == ex_uexpr && e1->expr.op == '.') { const expr_t *offset = new_short_expr (field->offset); e1 = offset_pointer_expr (e1->expr.e1, offset); e1 = cast_expr (pointer_type (field->type), e1); return unary_expr ('.', e1); } else { return new_offset_alias_expr (field->type, e1, field->offset); } } } else if (is_class (t1)) { //Class instance variables aren't allowed and thus declaring one //is treated as an error, so this is a follow-on error. return error (e1, "class instance access"); } return type_mismatch (e1, e2, '.'); } const expr_t * convert_from_bool (const expr_t *e, const type_t *type) { const expr_t *zero; const expr_t *one; expr_t *enum_zero, *enum_one; if (is_float (type)) { one = new_float_expr (1, false); zero = new_float_expr (0, false); } else if (is_int (type)) { one = new_int_expr (1, false); zero = new_int_expr (0, false); } else if (is_bool (type)) { one = new_bool_expr (1); zero = new_bool_expr (0); } else if (is_lbool (type)) { one = new_lbool_expr (1); zero = new_lbool_expr (0); } else if (is_enum (type) && enum_as_bool (type, &enum_zero, &enum_one)) { zero = enum_zero; one = enum_one; } else if (is_uint (type)) { one = new_uint_expr (1); zero = new_uint_expr (0); } else { return error (e, "can't convert from boolean value"); } auto cond = new_expr (); *cond = *e; return conditional_expr (cond, one, zero); } const expr_t * convert_nil (const expr_t *e, const type_t *t) { scoped_src_loc (e); auto nil = new_expr (); nil->type = ex_nil; nil->nil = t; return nil; } bool is_compare (int op) { if (op == QC_EQ || op == QC_NE || op == QC_LE || op == QC_GE || op == QC_LT || op == QC_GT || op == '>' || op == '<') return true; return false; } bool is_shift (int op) { if (op == QC_SHL || op == QC_SHR) { return true; } return false; } bool is_math_op (int op) { if (op == '*' || op == '/' || op == '+' || op == '-' || op == '%' || op == QC_MOD || op == QC_SCALE || op == QC_GEOMETRIC || op == QC_QMUL || op == QC_QVMUL || op == QC_VQMUL || op == QC_HADAMARD || op == QC_CROSS || op == QC_DOT || op == QC_OUTER || op == QC_WEDGE || op == QC_REGRESSIVE) { return true; } return false; } bool is_logic (int op) { if (op == QC_OR || op == QC_AND) return true; return false; } bool is_deref (const expr_t *e) { return e->type == ex_uexpr && e->expr.op == '.'; } bool is_temp (const expr_t *e) { return e->type == ex_temp; } bool has_function_call (const expr_t *e) { switch (e->type) { case ex_bool: return has_function_call (e->boolean.e); case ex_block: if (e->block.is_call) return true; case ex_list: for (auto li = e->block.list.head; li; li = li->next) if (has_function_call (li->expr)) return true; return false; case ex_expr: return (has_function_call (e->expr.e1) || has_function_call (e->expr.e2)); case ex_uexpr: return has_function_call (e->expr.e1); case ex_alias: return has_function_call (e->alias.expr); case ex_address: return has_function_call (e->address.lvalue); case ex_assign: return (has_function_call (e->assign.dst) || has_function_call (e->assign.src)); case ex_branch: if (e->branch.type == pr_branch_call) { return true; } if (e->branch.type == pr_branch_jump) { return false; } return has_function_call (e->branch.test); case ex_inout: // in is just a cast of out, if it's not null return has_function_call (e->inout.out); case ex_return: return has_function_call (e->retrn.ret_val); case ex_horizontal: return has_function_call (e->hop.vec); case ex_swizzle: return has_function_call (e->swizzle.src); case ex_extend: return has_function_call (e->extend.src); case ex_message: return true; case ex_error: case ex_state: case ex_label: case ex_labelref: case ex_def: case ex_symbol: case ex_temp: case ex_vector: case ex_selector: case ex_nil: case ex_value: case ex_compound: case ex_memset: case ex_adjstk: case ex_with: case ex_args: case ex_type: case ex_decl: case ex_loop: case ex_select: case ex_intrinsic: case ex_caselabel: return false; case ex_multivec: for (auto c = e->multivec.components.head; c; c = c->next) { if (has_function_call (c->expr)) { return true; } } return false; case ex_incop: return has_function_call (e->incop.expr); case ex_cond: return (has_function_call (e->cond.test) || has_function_call (e->cond.true_expr) || has_function_call (e->cond.false_expr)); case ex_field: return has_function_call (e->field.object); case ex_array: return (has_function_call (e->array.base) || has_function_call (e->array.index)); case ex_switch: return has_function_call (e->switchblock.test); case ex_xvalue: bug (e, "should xvalue happen here?"); return has_function_call (e->xvalue.expr); case ex_process: internal_error (e, "unexpected expression type"); case ex_count: break; } internal_error (e, "invalid expression type"); } bool is_function_call (const expr_t *e) { if (e->type != ex_block || !e->block.is_call) { return false; } e = e->block.result; return e->type == ex_branch && e->branch.type == pr_branch_call; } const expr_t * asx_expr (int op, const expr_t *e1, const expr_t *e2) { if (e1->type == ex_error) return e1; else if (e2->type == ex_error) return e2; else { e2 = paren_expr (e2); return assign_expr (e1, binary_expr (op, e1, e2)); } } const expr_t * branch_expr (int op, const expr_t *test, const expr_t *label) { // need to translate op due to precedence rules dictating the layout // of the token ids static pr_branch_e branch_type [] = { pr_branch_eq, pr_branch_ne, pr_branch_lt, pr_branch_gt, pr_branch_le, pr_branch_ge, }; if (op < QC_EQ || op > QC_LE) { internal_error (label, "invalid op: %d", op); } if (label && label->type != ex_label) { internal_error (label, "not a label"); } if (label) { ((expr_t *) label)->label.used++; } expr_t *branch = new_expr (); branch->type = ex_branch; branch->branch.type = branch_type[op - QC_EQ]; branch->branch.target = label; branch->branch.test = test; return branch; } const expr_t * goto_expr (const expr_t *label) { if (label && label->type != ex_label) { internal_error (label, "not a label"); } if (label) { ((expr_t *) label)->label.used++;//FIXME cast } expr_t *branch = new_expr (); branch->type = ex_branch; branch->branch.type = pr_branch_jump; branch->branch.target = label; return branch; } const expr_t * jump_table_expr (const expr_t *table, const expr_t *index) { expr_t *branch = new_expr (); branch->type = ex_branch; branch->branch.type = pr_branch_jump; branch->branch.target = table;//FIXME separate? all branch types can branch->branch.index = index; return branch; } expr_t * new_call_expr (const expr_t *func, const expr_t *args, const type_t *ret_type) { expr_t *branch = new_expr (); branch->type = ex_branch; branch->branch.type = pr_branch_call; branch->branch.target = func; branch->branch.args = args; branch->branch.ret_type = ret_type; return branch; } const expr_t * new_message_expr (const expr_t *receiver, struct keywordarg_s *message) { auto msg = new_expr (); msg->type = ex_message; msg->message.receiver = receiver; msg->message.message = message; return msg; } const expr_t * conditional_expr (const expr_t *cond, const expr_t *e1, const expr_t *e2) { if (cond->type == ex_error) return cond; if (e1->type == ex_error) return e1; if (e2->type == ex_error) return e2; expr_t *c = (expr_t *) convert_bool (cond, true); if (c->type == ex_error) return c; scoped_src_loc (cond); expr_t *block = new_block_expr (0); auto type1 = get_type (e1); auto type2 = get_type (e2); expr_t *tlabel = new_label_expr (); expr_t *flabel = new_label_expr (); expr_t *elabel = new_label_expr (); backpatch (c->boolean.true_list, tlabel); backpatch (c->boolean.false_list, flabel); if (!type_same (type1, type2)) { if (!type_assignable (type1, type2) && !type_assignable (type2, type1)) { type1 = 0; } if (!type_assignable (type1, type2)) { type1 = type2; } if (type_promotes (type2, type1)) { type1 = type2; } } block->block.result = type1 ? new_temp_def_expr (type1) : 0; append_expr (block, c); append_expr ((expr_t *) c->boolean.e, flabel);//FIXME cast if (block->block.result) append_expr (block, assign_expr (block->block.result, e2)); else append_expr (block, e2); append_expr (block, goto_expr (elabel)); append_expr (block, tlabel); if (block->block.result) append_expr (block, assign_expr (block->block.result, e1)); else append_expr (block, e1); append_expr (block, elabel); return block; } expr_t * new_incop_expr (int op, const expr_t *e, bool postop) { auto incop = new_expr (); incop->type = ex_incop; incop->incop = (ex_incop_t) { .op = op, .postop = postop, .expr = e, }; return incop; } expr_t * new_cond_expr (const expr_t *test, const expr_t *true_expr, const expr_t *false_expr) { auto cond = new_expr (); cond->type = ex_cond; cond->cond = (ex_cond_t) { .test = test, .true_expr = true_expr, .false_expr = false_expr, }; return cond; } expr_t * new_field_expr (const expr_t *object, const expr_t *member) { auto field = new_expr (); field->type = ex_field; field->field = (ex_field_t) { .object = object, .member = member, }; return field; } expr_t * new_array_expr (const expr_t *base, const expr_t *index) { auto array = new_expr (); array->type = ex_array; array->array = (ex_array_t) { .base = base, .index = index, }; return array; } expr_t * new_decl_expr (specifier_t spec, symtab_t *symtab) { auto decl = new_expr (); decl->type = ex_decl; decl->decl = (ex_decl_t) { .spec = spec, .symtab = symtab, }; return decl; } expr_t * new_loop_expr (bool not, bool do_while, const expr_t *test, const expr_t *body, const expr_t *continue_label, const expr_t *continue_body, const expr_t *break_label) { auto loop = new_expr (); loop->type = ex_loop; loop->loop = (ex_loop_t) { .test = test, .body = body, .continue_label = continue_label, .continue_body = continue_body, .break_label = break_label, .do_while = do_while, .not = not, }; return loop; } expr_t * new_select_expr (bool not, const expr_t *test, const expr_t *true_body, const expr_t *els, const expr_t *false_body) { auto select = new_expr (); select->type = ex_select; select->select = (ex_select_t) { .not = not, .test = test, .true_body = true_body, .els = els, .false_body = false_body, }; return select; } expr_t * new_intrinsic_expr (const expr_t *expr_list) { auto intrinsic = new_expr (); intrinsic->type = ex_intrinsic; if (expr_list) { int count = list_count (&expr_list->list); const expr_t *exprs[count + 1] = {}; list_scatter (&expr_list->list, exprs); intrinsic->intrinsic = (ex_intrinsic_t) { .opcode = exprs[0], }; list_gather (&intrinsic->intrinsic.operands, exprs + 1, count - 1); } return intrinsic; } expr_t * new_switch_expr (const expr_t *test, const expr_t *body, const expr_t *break_label) { auto swtch = new_expr (); swtch->type = ex_switch; swtch->switchblock = (ex_switch_t) { .test = test, .body = body, .break_label = break_label, }; return swtch; } expr_t * new_caselabel_expr (const expr_t *value, const expr_t *end_value) { if (end_value && !value) { internal_error (end_value, "case label with end value but no value"); } auto cl = new_expr (); cl->type = ex_caselabel; cl->caselabel = (ex_caselabel_t) { .value = value, .end_value = end_value, }; return cl; } expr_t * new_xvalue_expr (const expr_t *expr, bool lvalue) { auto xv = new_expr (); xv->type = ex_xvalue; xv->xvalue = (ex_xvalue_t) { .expr = expr, .lvalue = lvalue, }; return xv; } expr_t * new_process_expr (const expr_t *expr) { auto xv = new_expr (); xv->type = ex_process; xv->process = (ex_process_t) { .expr = expr, }; return xv; } expr_t * new_decl (symbol_t *sym, const expr_t *init) { auto expr = new_symbol_expr (sym); if (init) { expr = new_assign_expr (expr, init); } return expr; } expr_t * append_decl (expr_t *decl, symbol_t *sym, const expr_t *init) { auto expr = new_decl (sym, init); list_append (&decl->decl.list, expr); return decl; } expr_t * append_decl_list (expr_t *decl, const expr_t *list) { if (list->type != ex_list) { internal_error (list, "not a list expression"); } list_append_list (&decl->decl.list, &list->list); return decl; } const expr_t * incop_expr (int op, const expr_t *e, int postop) { if (e->type == ex_error) return e; auto one = new_int_expr (1, false); // int constants get auto-cast to float if (is_scalar (get_type (e))) { one = cast_expr (get_type (e), one); } if (postop) { expr_t *t1, *t2; auto type = get_type (e); expr_t *block = new_block_expr (0); if (e->type == ex_error) // get_type failed return e; t1 = new_temp_def_expr (type); t2 = new_temp_def_expr (type); append_expr (block, assign_expr (t1, e)); append_expr (block, assign_expr (t2, binary_expr (op, t1, one))); if (e->type == ex_uexpr && e->expr.op == '.') e = deref_pointer_expr (address_expr (e, 0)); append_expr (block, assign_expr (e, t2)); block->block.result = t1; return block; } else { return asx_expr (op, e, one); } } const expr_t * array_expr (const expr_t *array, const expr_t *index) { auto array_type = get_type (array); auto index_type = get_type (index); const type_t *ele_type; const expr_t *base; const expr_t *ptr; int ind = 0; if (array->type == ex_error) return array; if (index->type == ex_error) return index; if (!is_pointer (array_type) && !is_array (array_type) && !is_nonscalar (array_type) && !is_matrix (array_type)) return error (array, "not an array"); if (!is_integral (index_type)) return error (index, "invalid array index type"); if (is_short_val (index)) ind = expr_short (index); if (is_int_val (index)) ind = expr_int (index); if (is_array (array_type) && array_type->array.count && is_constant (index) && (ind < array_type->array.base || ind - array_type->array.base >= array_type->array.count)) { return error (index, "array index out of bounds"); } if (is_nonscalar (array_type) && !is_matrix (array_type) && is_constant (index) && (ind < 0 || ind >= array_type->width)) { return error (index, "array index out of bounds"); } if (is_matrix (array_type) && is_constant (index) && (ind < 0 || ind >= array_type->columns)) { return error (index, "array index out of bounds"); } if (is_array (array_type)) { ele_type = dereference_type (array_type); base = new_int_expr (array_type->array.base, false); } else if (is_pointer (array_type)) { ele_type = array_type->fldptr.type; base = new_int_expr (0, false); } else { ele_type = base_type (array_type); if (is_matrix (array_type)) { ele_type = column_type (array_type); } if (array->type == ex_uexpr && array->expr.op == '.') { auto vec = offset_pointer_expr (array->expr.e1, index); vec = cast_expr (pointer_type (ele_type), vec); return unary_expr ('.', vec); } base = new_int_expr (0, false); } auto scale = new_int_expr (type_size (ele_type), false); auto offset = binary_expr ('*', base, scale); index = binary_expr ('*', index, scale); index = binary_expr ('-', index, offset); if (is_short_val (index)) ind = expr_short (index); if (is_int_val (index)) ind = expr_int (index); if (is_array (array_type)) { if (array->type == ex_uexpr && array->expr.op == '.') { ptr = array->expr.e1; } else { auto alias = new_offset_alias_expr (ele_type, array, 0); ptr = new_address_expr (ele_type, alias, 0); } } else if (is_nonscalar (array_type) || is_matrix (array_type)) { auto alias = new_offset_alias_expr (ele_type, array, 0); ptr = new_address_expr (ele_type, alias, 0); } else { ptr = array; } ptr = offset_pointer_expr (ptr, index); ptr = cast_expr (pointer_type (ele_type), ptr); auto e = unary_expr ('.', ptr); return e; } const expr_t * deref_pointer_expr (const expr_t *pointer) { auto pointer_type = get_type (pointer); if (pointer->type == ex_error) return pointer; if (pointer_type->type != ev_ptr) return error (pointer, "not a pointer"); return unary_expr ('.', pointer); } const expr_t * offset_pointer_expr (const expr_t *pointer, const expr_t *offset) { auto ptr_type = get_type (pointer); if (!is_pointer (ptr_type)) { internal_error (pointer, "not a pointer"); } if (!is_integral (get_type (offset))) { internal_error (offset, "pointer offset is not an integer type"); } const expr_t *ptr; if (pointer->type == ex_alias && !pointer->alias.offset && is_integral (get_type (pointer->alias.expr))) { ptr = pointer->alias.expr; } else if (pointer->type == ex_address && is_constant (offset)) { if (pointer->address.offset) { offset = binary_expr ('+', pointer->address.offset, offset); } ptr = new_address_expr (ptr_type->fldptr.type, pointer->address.lvalue, offset); return ptr; } else { ptr = cast_expr (&type_int, pointer); } if (ptr->type == ex_error) { return ptr; } ptr = binary_expr ('+', ptr, offset); return cast_expr (ptr_type, ptr); } const expr_t * address_expr (const expr_t *e1, const type_t *t) { expr_t *e; if (e1->type == ex_error) return e1; if (!t) t = get_type (e1); switch (e1->type) { case ex_def: { auto def = e1->def; auto type = def->type; //FIXME this test should be in statements.c if (options.code.progsversion == PROG_VERSION && (def->local || def->param)) { e = new_address_expr (t, e1, 0); return e; } if (is_array (type)) { auto ptrval = new_pointer_val (0, t, def, 0); return new_value_expr (ptrval, false); } else { return new_pointer_expr (0, t, def); } } break; case ex_symbol: if (e1->symbol->sy_type == sy_def) { auto def = e1->symbol->def; auto type = def->type; //FIXME this test should be in statements.c if (options.code.progsversion == PROG_VERSION && (def->local || def->param)) { return new_address_expr (t, e1, 0); } if (is_array (type)) { auto ptrval = new_pointer_val (0, t, def, 0); return new_value_expr (ptrval, false); } else { return new_pointer_expr (0, t, def); } break; } return error (e1, "invalid type for unary &"); case ex_expr: if (e1->expr.op == '.') { e = new_address_expr (e1->expr.type, e1->expr.e1, e1->expr.e2); break; } return error (e1, "invalid type for unary &"); case ex_uexpr: if (e1->expr.op == '.') { auto p = e1->expr.e1; if (p->type == ex_expr && p->expr.op == '.') { p = new_address_expr (p->expr.type, p->expr.e1, p->expr.e2); } return p; } return error (e1, "invalid type for unary &"); case ex_label: return new_label_ref (&e1->label); case ex_temp: e = new_address_expr (t, e1, 0); break; case ex_alias: if (!t) { t = e1->alias.type; } return new_address_expr (t, e1, 0); default: return error (e1, "invalid type for unary &"); } return e; } const expr_t * build_if_statement (bool not, const expr_t *test, const expr_t *s1, const expr_t *els, const expr_t *s2) { expr_t *if_expr; expr_t *tl = new_label_expr (); expr_t *fl = new_label_expr (); if (els && !s2) { warning (els, "suggest braces around empty body in an ‘else’ statement"); } if (!els && !s1) { warning (test, "suggest braces around empty body in an ‘if’ statement"); } auto saved_loc = pr.loc; pr.loc = test->loc; if_expr = new_block_expr (0); test = convert_bool (test, true); if (test->type != ex_error) { if (not) { backpatch (test->boolean.true_list, fl); backpatch (test->boolean.false_list, tl); } else { backpatch (test->boolean.true_list, tl); backpatch (test->boolean.false_list, fl); } append_expr ((expr_t *) test->boolean.e, tl);//FIXME cast append_expr (if_expr, test); } append_expr (if_expr, s1); if (els) { pr.loc = els->loc; } if (s2) { expr_t *nl = new_label_expr (); append_expr (if_expr, goto_expr (nl)); append_expr (if_expr, fl); append_expr (if_expr, s2); append_expr (if_expr, nl); } else { append_expr (if_expr, fl); } pr.loc = saved_loc; return if_expr; } const expr_t * build_while_statement (bool not, const expr_t *test, const expr_t *statement, const expr_t *break_label, const expr_t *continue_label) { const expr_t *l1 = new_label_expr (); const expr_t *l2 = break_label; expr_t *while_expr; auto saved_loc = pr.loc; pr.loc = test->loc; while_expr = new_block_expr (0); append_expr (while_expr, goto_expr (continue_label)); append_expr (while_expr, l1); append_expr (while_expr, statement); append_expr (while_expr, continue_label); test = convert_bool (test, true); if (test->type != ex_error) { if (not) { backpatch (test->boolean.true_list, l2); backpatch (test->boolean.false_list, l1); } else { backpatch (test->boolean.true_list, l1); backpatch (test->boolean.false_list, l2); } append_expr ((expr_t *) test->boolean.e, l2);//FIXME cast append_expr (while_expr, test); } pr.loc = saved_loc; return while_expr; } const expr_t * build_do_while_statement (const expr_t *statement, bool not, const expr_t *test, const expr_t *break_label, const expr_t *continue_label) { expr_t *l1 = new_label_expr (); auto saved_loc = pr.loc; expr_t *do_while_expr; if (!statement) { warning (break_label, "suggest braces around empty body in a ‘do’ statement"); } pr.loc = test->loc; do_while_expr = new_block_expr (0); append_expr (do_while_expr, l1); append_expr (do_while_expr, statement); append_expr (do_while_expr, continue_label); test = convert_bool (test, true); if (test->type != ex_error) { if (not) { backpatch (test->boolean.true_list, break_label); backpatch (test->boolean.false_list, l1); } else { backpatch (test->boolean.true_list, l1); backpatch (test->boolean.false_list, break_label); } append_expr ((expr_t *) test->boolean.e, break_label);//FIXME append_expr (do_while_expr, test); } pr.loc = saved_loc; return do_while_expr; } const expr_t * build_for_statement (const expr_t *init, const expr_t *test, const expr_t *next, const expr_t *statement, const expr_t *break_label, const expr_t *continue_label) { expr_t *tl = new_label_expr (); const expr_t *fl = break_label; expr_t *l1 = 0; const expr_t *t; auto saved_loc = pr.loc; expr_t *for_expr; if (next) t = next; else if (test) t = test; else if (init) t = init; else t = continue_label; pr.loc = t->loc; for_expr = new_block_expr (0); append_expr (for_expr, init); if (test) { l1 = new_label_expr (); append_expr (for_expr, goto_expr (l1)); } append_expr (for_expr, tl); append_expr (for_expr, statement); append_expr (for_expr, continue_label); append_expr (for_expr, next); if (test) { append_expr (for_expr, l1); test = convert_bool (test, true); if (test->type != ex_error) { backpatch (test->boolean.true_list, tl); backpatch (test->boolean.false_list, fl); append_expr ((expr_t *) test->boolean.e, fl);//FIXME cast append_expr (for_expr, test); } } else { append_expr (for_expr, goto_expr (tl)); append_expr (for_expr, fl); } pr.loc = saved_loc; return for_expr; } const expr_t * build_state_expr (const expr_t *e, rua_ctx_t *ctx) { int count = e ? list_count (&e->list) : 0; if (count < 2) { return error (e, "not enough state arguments"); } if (count > 3) { return error (e, "too many state arguments"); } const expr_t *state_args[3] = {}; list_scatter (&e->list, state_args); const expr_t *frame = state_args[0]; const expr_t *think = state_args[1]; const expr_t *step = state_args[2]; if (think->type == ex_symbol) think = think_expr (think->symbol, ctx); if (is_int_val (frame)) frame = cast_expr (&type_float, frame); if (!type_assignable (&type_float, get_type (frame))) return error (frame, "invalid type for frame number"); if (extract_type (think) != ev_func) return error (think, "invalid type for think"); if (step) { if (is_int_val (step)) step = cast_expr (&type_float, step); if (!type_assignable (&type_float, get_type (step))) return error (step, "invalid type for step"); } return new_state_expr (frame, think, step); } const expr_t * think_expr (symbol_t *think_sym, rua_ctx_t *ctx) { symbol_t *sym; if (think_sym->table) return new_symbol_expr (think_sym); sym = symtab_lookup (current_symtab, "think"); if (sym && sym->sy_type == sy_def && sym->type && sym->type->type == ev_field && sym->type->fldptr.type->type == ev_func) { think_sym->type = sym->type->fldptr.type; } else { think_sym->type = &type_func; } think_sym = function_symbol ((specifier_t) { .sym = think_sym }, ctx); make_function (think_sym, 0, current_symtab->space, current_storage); return new_symbol_expr (think_sym); } const expr_t * encode_expr (const type_t *type) { dstring_t *encoding = dstring_newstr (); encode_type (encoding, type); auto e = new_string_expr (encoding->str); free (encoding); return e; } const expr_t * sizeof_expr (const expr_t *expr, const type_t *type) { if (!((!expr) ^ (!type))) internal_error (0, 0); if (!type) type = get_type (expr); if (type) { expr = new_int_expr (type_aligned_size (type), false); } return expr; } static bool can_inline_list (const ex_list_t *list, symbol_t *fsym) { int count = list_count (list); const expr_t *arg_exprs[count + 1] = {}; list_scatter (list, arg_exprs); for (int i = 0; i < count; i++) { if (!can_inline (arg_exprs[i], fsym)) { return false; } } return true; } bool can_inline (const expr_t *expr, symbol_t *fsym) { if (!expr) { return true; } switch (expr->type) { case ex_block: return can_inline_list (&expr->block.list, fsym); case ex_expr: if (expr->expr.op == QC_AND || expr->expr.op == QC_OR) { //FIXME return false; } return (can_inline (expr->expr.e1, fsym) && can_inline (expr->expr.e2, fsym)); case ex_uexpr: return can_inline (expr->expr.e1, fsym); case ex_symbol: auto sym = expr->symbol; if (sym->table == fsym->table && strcmp (sym->name, fsym->name) == 0) { // recursive function, however to get here, no conditional // expresions were found warning (expr, "infinite recursion in %s", fsym->name); return false; } return true; case ex_vector: return can_inline_list (&expr->vector.list, fsym); case ex_selector: return can_inline (expr->selector.sel_ref, fsym); case ex_message: if (!can_inline (expr->message.receiver, fsym)) { return false; } for (auto k = expr->message.message; k; k = k->next) { if (!can_inline (k->expr, fsym)) { return false; } } return true; case ex_compound: for (auto ele = expr->compound.head; ele; ele = ele->next) { if (!can_inline (ele->expr, fsym)) { return false; } } return true; case ex_memset: return (can_inline (expr->memset.dst, fsym) && can_inline (expr->memset.val, fsym) && can_inline (expr->memset.count, fsym)); case ex_alias: return (can_inline (expr->alias.expr, fsym) && can_inline (expr->alias.offset, fsym)); case ex_address: return (can_inline (expr->address.lvalue, fsym) && can_inline (expr->address.offset, fsym)); case ex_assign: return (can_inline (expr->assign.dst, fsym) && can_inline (expr->assign.src, fsym)); case ex_branch: if (expr->branch.type != pr_branch_call) { notice (expr, "%s", expr_names[expr->type]); return false; } if (!can_inline (expr->branch.target, fsym)) { notice (expr, "%s", expr_names[expr->type]); return false; } auto args = (expr_t *) expr->branch.args; if (!args) { return true; } return can_inline_list (&args->list, fsym); case ex_return: if (expr->retrn.ret_val) { return can_inline (expr->retrn.ret_val, fsym); } return true; case ex_horizontal: return can_inline (expr->hop.vec, fsym); case ex_swizzle: return can_inline (expr->swizzle.src, fsym); case ex_extend: return can_inline (expr->extend.src, fsym); case ex_incop: return can_inline (expr->incop.expr, fsym); case ex_list: return can_inline_list (&expr->list, fsym); case ex_cond: return (can_inline (expr->cond.test, fsym) && can_inline (expr->cond.true_expr, fsym) && can_inline (expr->cond.false_expr, fsym)); case ex_field: return (can_inline (expr->field.object, fsym) && can_inline (expr->field.member, fsym)); case ex_array: return (can_inline (expr->array.base, fsym) && can_inline (expr->array.index, fsym)); case ex_decl: return can_inline_list (&expr->decl.list, fsym); case ex_xvalue: auto xvalue = expr->xvalue.expr; if (xvalue->type == ex_symbol && xvalue->symbol->sy_type == sy_xvalue) { if (expr->xvalue.lvalue) { xvalue = xvalue->symbol->xvalue.lvalue; } else { xvalue = xvalue->symbol->xvalue.rvalue; } } return can_inline (xvalue, fsym); case ex_def: case ex_temp: case ex_nil: case ex_value: case ex_type: case ex_adjstk: case ex_with: case ex_args: return true; case ex_loop://FIXME case ex_state://FIXME case ex_bool://FIXME case ex_label://FIXME case ex_labelref://FIXME case ex_select://FIXME case ex_switch: return false; case ex_error: case ex_inout: case ex_caselabel: case ex_multivec: case ex_intrinsic: case ex_process: case ex_count: internal_error (expr, "unexpected expr %s", expr_names[expr->type]); } internal_error (expr, "invald expr type %d", expr->type); }