/* * sparse/dissect.c * * Started by Oleg Nesterov * * Licensed under the Open Software License version 1.1 */ #include "dissect.h" #define U_VOID 0x00 #define U_SELF ((1 << U_SHIFT) - 1) #define U_MASK (U_R_VAL | U_W_VAL | U_R_AOF) #define DO_LIST(l__, p__, expr__) \ do { \ typeof(l__->list[0]) p__; \ FOR_EACH_PTR(l__, p__) \ expr__; \ END_FOR_EACH_PTR(p__); \ } while (0) #define DO_2_LIST(l1__,l2__, p1__,p2__, expr__) \ do { \ typeof(l1__->list[0]) p1__; \ typeof(l2__->list[0]) p2__; \ PREPARE_PTR_LIST(l1__, p1__); \ FOR_EACH_PTR(l2__, p2__) \ expr__; \ NEXT_PTR_LIST(p1__); \ END_FOR_EACH_PTR(p2__); \ FINISH_PTR_LIST(p1__); \ } while (0) typedef unsigned usage_t; static struct reporter *reporter; static struct symbol *return_type; static void do_sym_list(struct symbol_list *list); static struct symbol *base_type(struct symbol *sym), *do_initializer(struct symbol *type, struct expression *expr), *do_expression(usage_t mode, struct expression *expr), *do_statement(usage_t mode, struct statement *stmt); static inline int is_ptr(struct symbol *type) { return type->type == SYM_PTR || type->type == SYM_ARRAY; } static inline usage_t u_rval(usage_t mode) { return mode & (U_R_VAL | (U_MASK << U_SHIFT)) ? U_R_VAL : 0; } static inline usage_t u_addr(usage_t mode) { return mode = mode & U_MASK ? U_R_AOF | (mode & U_W_AOF) : 0; } static usage_t u_lval(struct symbol *type) { int wptr = is_ptr(type) && !(type->ctype.modifiers & MOD_CONST); return wptr || type == &bad_ctype ? U_W_AOF | U_R_VAL : U_R_VAL; } static usage_t fix_mode(struct symbol *type, usage_t mode) { mode &= (U_SELF | (U_SELF << U_SHIFT)); switch (type->type) { case SYM_BASETYPE: if (!type->ctype.base_type) break; case SYM_ENUM: case SYM_BITFIELD: if (mode & U_MASK) mode &= U_SELF; default: break; case SYM_FN: if (mode & U_R_VAL) mode |= U_R_AOF; mode &= ~(U_R_VAL | U_W_AOF); break; case SYM_ARRAY: if (mode & (U_MASK << U_SHIFT)) mode >>= U_SHIFT; else if (mode != U_W_VAL) mode = u_addr(mode); } if (!(mode & U_R_AOF)) mode &= ~U_W_AOF; return mode; } static inline struct symbol *no_member(struct ident *name) { static struct symbol sym = { .type = SYM_BAD, }; sym.ctype.base_type = &bad_ctype; sym.ident = name; return &sym; } static struct symbol *report_member(mode_t mode, struct position *pos, struct symbol *type, struct symbol *mem) { struct symbol *ret = mem->ctype.base_type; if (reporter->r_member) reporter->r_member(fix_mode(ret, mode), pos, type, mem); return ret; } static void report_implicit(usage_t mode, struct position *pos, struct symbol *type) { if (type->type != SYM_STRUCT && type->type != SYM_UNION) return; if (!reporter->r_member) return; if (type->ident != NULL) reporter->r_member(mode, pos, type, NULL); DO_LIST(type->symbol_list, mem, report_implicit(mode, pos, base_type(mem))); } static inline struct symbol *expr_symbol(struct expression *expr) { struct symbol *sym = expr->symbol; if (!sym) { sym = lookup_symbol(expr->symbol_name, NS_SYMBOL); if (!sym) { sym = alloc_symbol(expr->pos, SYM_BAD); bind_symbol(sym, expr->symbol_name, NS_SYMBOL); sym->ctype.modifiers = MOD_EXTERN; } } if (!sym->ctype.base_type) sym->ctype.base_type = &bad_ctype; return sym; } static struct symbol *report_symbol(usage_t mode, struct expression *expr) { struct symbol *sym = expr_symbol(expr); struct symbol *ret = base_type(sym); if (0 && ret->type == SYM_ENUM) return report_member(mode, &expr->pos, ret, expr->symbol); if (reporter->r_symbol) reporter->r_symbol(fix_mode(ret, mode), &expr->pos, sym); return ret; } static inline struct ident *mk_name(struct ident *root, struct ident *node) { char name[256]; snprintf(name, sizeof(name), "%.*s:%.*s", root ? root->len : 0, root ? root->name : "", node ? node->len : 0, node ? node->name : ""); return built_in_ident(name); } static void examine_sym_node(struct symbol *node, struct ident *root) { struct symbol *base; struct ident *name; if (node->examined) return; node->examined = 1; name = node->ident; while ((base = node->ctype.base_type) != NULL) switch (base->type) { case SYM_TYPEOF: node->ctype.base_type = do_expression(U_VOID, base->initializer); break; case SYM_ARRAY: do_expression(U_R_VAL, base->array_size); case SYM_PTR: case SYM_FN: node = base; break; case SYM_STRUCT: case SYM_UNION: //case SYM_ENUM: if (base->evaluated) return; if (!base->symbol_list) return; base->evaluated = 1; if (!base->ident && name) base->ident = mk_name(root, name); if (base->ident && reporter->r_symdef) reporter->r_symdef(base); DO_LIST(base->symbol_list, mem, examine_sym_node(mem, base->ident ?: root)); default: return; } } static struct symbol *base_type(struct symbol *sym) { if (!sym) return &bad_ctype; if (sym->type == SYM_NODE) examine_sym_node(sym, NULL); return sym->ctype.base_type // builtin_fn_type ?: &bad_ctype; } static struct symbol *__lookup_member(struct symbol *type, struct ident *name, int *p_addr) { struct symbol *node; int addr = 0; FOR_EACH_PTR(type->symbol_list, node) if (!name) { if (addr == *p_addr) return node; } else if (node->ident == NULL) { node = __lookup_member(node->ctype.base_type, name, NULL); if (node) goto found; } else if (node->ident == name) { found: if (p_addr) *p_addr = addr; return node; } addr++; END_FOR_EACH_PTR(node); return NULL; } static struct symbol *lookup_member(struct symbol *type, struct ident *name, int *addr) { return __lookup_member(type, name, addr) ?: no_member(name); } static struct expression *peek_preop(struct expression *expr, int op) { do { if (expr->type != EXPR_PREOP) break; if (expr->op == op) return expr->unop; if (expr->op == '(') expr = expr->unop; else break; } while (expr); return NULL; } static struct symbol *do_expression(usage_t mode, struct expression *expr) { struct symbol *ret = &int_ctype; again: if (expr) switch (expr->type) { default: warning(expr->pos, "bad expr->type: %d", expr->type); case EXPR_TYPE: // [struct T]; Why ??? case EXPR_VALUE: case EXPR_FVALUE: break; case EXPR_LABEL: ret = &label_ctype; break; case EXPR_STRING: ret = &string_ctype; break; case EXPR_STATEMENT: ret = do_statement(mode, expr->statement); break; case EXPR_SIZEOF: case EXPR_ALIGNOF: case EXPR_PTRSIZEOF: do_expression(U_VOID, expr->cast_expression); break; case EXPR_COMMA: do_expression(U_VOID, expr->left); ret = do_expression(mode, expr->right); break; case EXPR_CAST: case EXPR_FORCE_CAST: //case EXPR_IMPLIED_CAST: ret = base_type(expr->cast_type); do_initializer(ret, expr->cast_expression); break; case EXPR_COMPARE: case EXPR_LOGICAL: mode = u_rval(mode); do_expression(mode, expr->left); do_expression(mode, expr->right); break; case EXPR_CONDITIONAL: //case EXPR_SELECT: do_expression(expr->cond_true ? U_R_VAL : U_R_VAL | mode, expr->conditional); ret = do_expression(mode, expr->cond_true); ret = do_expression(mode, expr->cond_false); break; case EXPR_CALL: ret = do_expression(U_R_PTR, expr->fn); if (is_ptr(ret)) ret = ret->ctype.base_type; DO_2_LIST(ret->arguments, expr->args, arg, val, do_expression(u_lval(base_type(arg)), val)); ret = ret->type == SYM_FN ? base_type(ret) : &bad_ctype; break; case EXPR_ASSIGNMENT: mode |= U_W_VAL | U_R_VAL; if (expr->op == '=') mode &= ~U_R_VAL; ret = do_expression(mode, expr->left); report_implicit(mode, &expr->pos, ret); mode = expr->op == '=' ? u_lval(ret) : U_R_VAL; do_expression(mode, expr->right); break; case EXPR_BINOP: { struct symbol *l, *r; mode |= u_rval(mode); l = do_expression(mode, expr->left); r = do_expression(mode, expr->right); if (expr->op != '+' && expr->op != '-') ; else if (!is_ptr_type(r)) ret = l; else if (!is_ptr_type(l)) ret = r; } break; case EXPR_PREOP: case EXPR_POSTOP: { struct expression *unop = expr->unop; switch (expr->op) { case SPECIAL_INCREMENT: case SPECIAL_DECREMENT: mode |= U_W_VAL | U_R_VAL; default: mode |= u_rval(mode); case '(': ret = do_expression(mode, unop); break; case '&': if ((expr = peek_preop(unop, '*'))) goto again; ret = alloc_symbol(unop->pos, SYM_PTR); ret->ctype.base_type = do_expression(u_addr(mode), unop); break; case '*': if ((expr = peek_preop(unop, '&'))) goto again; if (mode & (U_MASK << U_SHIFT)) mode |= U_R_VAL; mode <<= U_SHIFT; if (mode & (U_R_AOF << U_SHIFT)) mode |= U_R_VAL; if (mode & (U_W_VAL << U_SHIFT)) mode |= U_W_AOF; ret = do_expression(mode, unop); ret = is_ptr(ret) ? base_type(ret) : &bad_ctype; } } break; case EXPR_DEREF: { struct symbol *p_type; usage_t p_mode; p_mode = mode & U_SELF; if (!(mode & U_MASK) && (mode & (U_MASK << U_SHIFT))) p_mode = U_R_VAL; p_type = do_expression(p_mode, expr->deref); ret = report_member(mode, &expr->pos, p_type, lookup_member(p_type, expr->member, NULL)); } break; case EXPR_SYMBOL: ret = report_symbol(mode, expr); } return ret; } static void do_asm_xputs(usage_t mode, struct expression_list *xputs) { int nr = 0; DO_LIST(xputs, expr, if (++nr % 3 == 0) do_expression(U_W_AOF | mode, expr)); } static struct symbol *do_statement(usage_t mode, struct statement *stmt) { struct symbol *ret = &void_ctype; if (stmt) switch (stmt->type) { default: warning(stmt->pos, "bad stmt->type: %d", stmt->type); case STMT_NONE: case STMT_RANGE: case STMT_CONTEXT: break; case STMT_DECLARATION: do_sym_list(stmt->declaration); break; case STMT_EXPRESSION: ret = do_expression(mode, stmt->expression); break; case STMT_RETURN: do_expression(u_lval(return_type), stmt->expression); break; case STMT_ASM: do_expression(U_R_VAL, stmt->asm_string); do_asm_xputs(U_W_VAL, stmt->asm_outputs); do_asm_xputs(U_R_VAL, stmt->asm_inputs); break; case STMT_COMPOUND: { int count; count = statement_list_size(stmt->stmts); DO_LIST(stmt->stmts, st, ret = do_statement(--count ? U_VOID : mode, st)); } break; case STMT_ITERATOR: do_sym_list(stmt->iterator_syms); do_statement(U_VOID, stmt->iterator_pre_statement); do_expression(U_R_VAL, stmt->iterator_pre_condition); do_statement(U_VOID, stmt->iterator_post_statement); do_statement(U_VOID, stmt->iterator_statement); do_expression(U_R_VAL, stmt->iterator_post_condition); break; case STMT_IF: do_expression(U_R_VAL, stmt->if_conditional); do_statement(U_VOID, stmt->if_true); do_statement(U_VOID, stmt->if_false); break; case STMT_SWITCH: do_expression(U_R_VAL, stmt->switch_expression); do_statement(U_VOID, stmt->switch_statement); break; case STMT_CASE: do_expression(U_R_VAL, stmt->case_expression); do_expression(U_R_VAL, stmt->case_to); do_statement(U_VOID, stmt->case_statement); break; case STMT_GOTO: do_expression(U_R_PTR, stmt->goto_expression); break; case STMT_LABEL: do_statement(mode, stmt->label_statement); } return ret; } static struct symbol *do_initializer(struct symbol *type, struct expression *expr) { struct symbol *m_type; struct expression *m_expr; int m_addr; if (expr) switch (expr->type) { default: do_expression(u_lval(type), expr); break; case EXPR_INDEX: do_initializer(base_type(type), expr->idx_expression); break; case EXPR_INITIALIZER: m_addr = 0; FOR_EACH_PTR(expr->expr_list, m_expr) if (type->type == SYM_ARRAY) { m_type = base_type(type); if (m_expr->type == EXPR_INDEX) m_expr = m_expr->idx_expression; } else { struct position *pos = &m_expr->pos; struct ident *m_name = NULL; if (m_expr->type == EXPR_IDENTIFIER) { m_name = m_expr->expr_ident; m_expr = m_expr->ident_expression; } m_type = report_member(U_W_VAL, pos, type, lookup_member(type, m_name, &m_addr)); if (m_expr->type != EXPR_INITIALIZER) report_implicit(U_W_VAL, pos, m_type); } do_initializer(m_type, m_expr); m_addr++; END_FOR_EACH_PTR(m_expr); } return type; } static inline struct symbol *do_symbol(struct symbol *sym) { struct symbol *type; type = base_type(sym); if (reporter->r_symdef) reporter->r_symdef(sym); switch (type->type) { default: if (!sym->initializer) break; if (reporter->r_symbol) reporter->r_symbol(U_W_VAL, &sym->pos, sym); do_initializer(type, sym->initializer); break; case SYM_FN: do_sym_list(type->arguments); return_type = base_type(type); do_statement(U_VOID, sym->ctype.modifiers & MOD_INLINE ? type->inline_stmt : type->stmt); } return type; } static void do_sym_list(struct symbol_list *list) { DO_LIST(list, sym, do_symbol(sym)); } void dissect(struct symbol_list *list, struct reporter *rep) { reporter = rep; do_sym_list(list); }