--- /dev/null	Fri Dec 21 15:00:12 2018
+++ new/usr/src/tools/smatch/src/dissect.c	Fri Dec 21 15:00:12 2018
@@ -0,0 +1,613 @@
+/*
+ * sparse/dissect.c
+ *
+ * Started by Oleg Nesterov <oleg@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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(usage_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_OFFSETOF: {
+		struct symbol *in = base_type(expr->in);
+
+		do {
+			if (expr->op == '.') {
+				in = report_member(U_VOID, &expr->pos, in,
+					lookup_member(in, expr->ident, NULL));
+			} else {
+				do_expression(U_R_VAL, expr->index);
+				in = in->ctype.base_type;
+			}
+		} while ((expr = expr->down));
+	}
+
+	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 {
+				int *m_atop = &m_addr;
+
+				m_type = type;
+				while (m_expr->type == EXPR_IDENTIFIER) {
+					m_type = report_member(U_W_VAL, &m_expr->pos, m_type,
+							lookup_member(m_type, m_expr->expr_ident, m_atop));
+					m_expr = m_expr->ident_expression;
+					m_atop = NULL;
+				}
+
+				if (m_atop) {
+					m_type = report_member(U_W_VAL, &m_expr->pos, m_type,
+							lookup_member(m_type, NULL, m_atop));
+				}
+
+				if (m_expr->type != EXPR_INITIALIZER)
+					report_implicit(U_W_VAL, &m_expr->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);
+}
