--- /dev/null	Sat Nov 16 11:16:55 2019
+++ new/usr/src/tools/smatch/src/ssa.c	Sat Nov 16 11:16:55 2019
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: MIT
+//
+// SSA conversion
+// Copyright (C) 2005 Luc Van Oostenryck
+//
+
+#include <assert.h>
+#include "ssa.h"
+#include "lib.h"
+#include "sset.h"
+#include "dominate.h"
+#include "flowgraph.h"
+#include "linearize.h"
+#include "flow.h"			// for convert_load_instruction()
+
+
+// Is it possible and desirable for this to be promoted to a pseudo?
+static inline bool is_promotable(struct symbol *type)
+{
+	struct symbol *member;
+	int bf_seen;
+	int nbr;
+
+	if (type->type == SYM_NODE)
+		type = type->ctype.base_type;
+	switch (type->type) {
+	case SYM_ENUM:
+	case SYM_BITFIELD:
+	case SYM_PTR:
+	case SYM_RESTRICT:	// OK, always integer types
+		return 1;
+	case SYM_STRUCT:
+		// we allow a single scalar field
+		// but a run of bitfields count for 1
+		nbr = 0;
+		bf_seen = 0;
+		FOR_EACH_PTR(type->symbol_list, member) {
+			if (is_bitfield_type(member)) {
+				if (bf_seen)
+					continue;
+				bf_seen = 1;
+			} else {
+				bf_seen = 0;
+			}
+			if (!is_scalar_type(member))
+				return 0;
+			if (nbr++)
+				return 0;
+		} END_FOR_EACH_PTR(member);
+		if (bf_seen && (type->bit_size > long_ctype.bit_size))
+			return 0;
+		return 1;
+	case SYM_UNION:
+		// FIXME: should be like struct but has problem
+		//        when used with/for type cohercion
+		// -----> OK if only same sized integral types
+		FOR_EACH_PTR(type->symbol_list, member) {
+			if (member->bit_size != type->bit_size)
+				return 0;
+			if (!is_integral_type(member))
+				return 0;
+		} END_FOR_EACH_PTR(member);
+		return 1;
+	default:
+		break;
+	}
+	if (type->ctype.base_type == &int_type)
+		return 1;
+	if (type->ctype.base_type == &fp_type)
+		return 1;
+	return 0;
+}
+
+static bool insn_before(struct instruction *a, struct instruction *b)
+{
+	struct basic_block *bb = a->bb;
+	struct instruction *insn;
+
+	assert(b->bb == bb);
+	FOR_EACH_PTR(bb->insns, insn) {
+		if (insn == a)
+			return true;
+		if (insn == b)
+			return false;
+	} END_FOR_EACH_PTR(insn);
+	assert(0);
+}
+
+static void kill_store(struct instruction *insn)
+{
+	remove_use(&insn->src);
+	remove_use(&insn->target);
+	insn->bb = NULL;
+}
+
+static void rewrite_local_var(struct basic_block *bb, pseudo_t addr, int nbr_stores, int nbr_uses)
+{
+	struct instruction *insn;
+	pseudo_t val = NULL;
+
+	if (!bb)
+		return;
+
+	FOR_EACH_PTR(bb->insns, insn) {
+
+		if (!insn->bb || insn->src != addr)
+			continue;
+		switch (insn->opcode) {
+		case OP_LOAD:
+			if (!val)
+				val = undef_pseudo();
+			convert_load_instruction(insn, val);
+			break;
+		case OP_STORE:
+			val = insn->target;
+			// can't use kill_instruction() unless
+			// we add a fake user to val
+			kill_store(insn);
+			break;
+		}
+	} END_FOR_EACH_PTR(insn);
+}
+
+static bool rewrite_single_store(struct instruction *store)
+{
+	pseudo_t addr = store->src;
+	struct pseudo_user *pu;
+
+	FOR_EACH_PTR(addr->users, pu) {
+		struct instruction *insn = pu->insn;
+
+		if (insn->opcode != OP_LOAD)
+			continue;
+
+		// Let's try to replace the value of the load
+		// by the value from the store. This is only valid
+		// if the store dominate the load.
+
+		if (insn->bb == store->bb) {
+			// the load and the store are in the same BB
+			// we can convert if the load is after the store.
+			if (!insn_before(store, insn))
+				continue;
+		} else if (!domtree_dominates(store->bb, insn->bb)) {
+			// we can't convert this load
+			continue;
+		}
+
+		// OK, we can rewrite this load
+
+		// undefs ?
+
+		convert_load_instruction(insn, store->target);
+	} END_FOR_EACH_PTR(pu);
+
+	// is there some unconverted loads?
+	if (pseudo_user_list_size(addr->users) > 1)
+		return false;
+
+	kill_store(store);
+	return true;
+}
+
+static struct sset *processed;
+
+// we would like to know:
+// is there one or more stores?
+// are all loads & stores local/done in a single block?
+static void ssa_convert_one_var(struct entrypoint *ep, struct symbol *var)
+{
+	struct basic_block_list *alpha = NULL;
+	struct basic_block_list *idf = NULL;
+	struct basic_block *samebb = NULL;
+	struct instruction *store = NULL;
+	struct basic_block *bb;
+	struct pseudo_user *pu;
+	unsigned long mod = var->ctype.modifiers;
+	bool local = true;
+	int nbr_stores = 0;
+	int nbr_uses   = 0;
+	pseudo_t addr;
+
+	/* Never used as a symbol? */
+	addr = var->pseudo;
+	if (!addr)
+		return;
+
+	/* We don't do coverage analysis of volatiles.. */
+	if (mod & MOD_VOLATILE)
+		return;
+
+	/* ..and symbols with external visibility need more care */
+	mod &= (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE);
+	if (mod)
+		goto external_visibility;
+
+	if (!is_promotable(var))
+		return;
+
+	// 1) insert in the worklist all BBs that may modify var
+	sset_reset(processed);
+	FOR_EACH_PTR(addr->users, pu) {
+		struct instruction *insn = pu->insn;
+		struct basic_block *bb = insn->bb;
+
+		switch (insn->opcode) {
+		case OP_STORE:
+			nbr_stores++;
+			store = insn;
+			if (!sset_testset(processed, bb->nr))
+				add_bb(&alpha, bb);
+			/* fall through */
+		case OP_LOAD:
+			if (local) {
+				if (!samebb)
+					samebb = bb;
+				else if (samebb != bb)
+					local = false;
+			}
+			nbr_uses++;
+			break;
+		case OP_SYMADDR:
+			mod |= MOD_ADDRESSABLE;
+			goto external_visibility;
+		default:
+			warning(var->pos, "symbol '%s' pseudo used in unexpected way",
+				show_ident(var->ident));
+		}
+	} END_FOR_EACH_PTR(pu);
+
+	if (nbr_stores == 1) {
+		if (rewrite_single_store(store))
+			return;
+	}
+
+	// if all uses are local to a single block
+	// they can easily be rewritten and doesn't need phi-nodes
+	// FIXME: could be done for extended BB too
+	if (local) {
+		rewrite_local_var(samebb, addr, nbr_stores, nbr_uses);
+		return;
+	}
+
+	idf_compute(ep, &idf, alpha);
+	FOR_EACH_PTR(idf, bb) {
+		struct instruction *node = insert_phi_node(bb, var);
+		node->phi_var = var->pseudo;
+	} END_FOR_EACH_PTR(bb);
+	var->torename = 1;
+
+external_visibility:
+	if (mod & (MOD_NONLOCAL | MOD_STATIC))
+		return;
+	kill_dead_stores(ep, addr, !mod);
+}
+
+static pseudo_t lookup_var(struct basic_block *bb, struct symbol *var)
+{
+	do {
+		pseudo_t val = phi_map_lookup(bb->phi_map, var);
+		if (val)
+			return val;
+	} while ((bb = bb->idom));
+	return undef_pseudo();
+}
+
+static struct instruction_list *phis_all;
+static struct instruction_list *phis_used;
+
+static void ssa_rename_insn(struct basic_block *bb, struct instruction *insn)
+{
+	struct symbol *var;
+	pseudo_t addr;
+	pseudo_t val;
+
+	switch (insn->opcode) {
+	case OP_STORE:
+		addr = insn->src;
+		if (addr->type != PSEUDO_SYM)
+			break;
+		var = addr->sym;
+		if (!var || !var->torename)
+			break;
+		phi_map_update(&bb->phi_map, var, insn->target);
+		kill_store(insn);
+		break;
+	case OP_LOAD:
+		addr = insn->src;
+		if (addr->type != PSEUDO_SYM)
+			break;
+		var = addr->sym;
+		if (!var || !var->torename)
+			break;
+		val = lookup_var(bb, var);
+		convert_load_instruction(insn, val);
+		break;
+	case OP_PHI:
+		var = insn->type;
+		if (!var || !var->torename)
+			break;
+		phi_map_update(&bb->phi_map, var, insn->target);
+		add_instruction(&phis_all, insn);
+		break;
+	}
+}
+
+static void ssa_rename_insns(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+
+	FOR_EACH_PTR(ep->bbs, bb) {
+		struct instruction *insn;
+		FOR_EACH_PTR(bb->insns, insn) {
+			if (!insn->bb)
+				continue;
+			ssa_rename_insn(bb, insn);
+		} END_FOR_EACH_PTR(insn);
+	} END_FOR_EACH_PTR(bb);
+}
+
+static void mark_phi_used(pseudo_t val)
+{
+	struct instruction *node;
+
+	if (val->type != PSEUDO_REG)
+		return;
+	node = val->def;
+	if (node->opcode != OP_PHI)
+		return;
+	if (node->used)
+		return;
+	node->used = 1;
+	add_instruction(&phis_used, node);
+}
+
+static void ssa_rename_phi(struct instruction *insn)
+{
+	struct basic_block *par;
+	struct symbol *var;
+
+	if (!insn->phi_var)
+		return;
+	var = insn->phi_var->sym;
+	if (!var->torename)
+		return;
+	FOR_EACH_PTR(insn->bb->parents, par) {
+		struct instruction *term = delete_last_instruction(&par->insns);
+		pseudo_t val = lookup_var(par, var);
+		pseudo_t phi = alloc_phi(par, val, var);
+		phi->ident = var->ident;
+		add_instruction(&par->insns, term);
+		use_pseudo(insn, phi, add_pseudo(&insn->phi_list, phi));
+		mark_phi_used(val);
+	} END_FOR_EACH_PTR(par);
+}
+
+static void ssa_rename_phis(struct entrypoint *ep)
+{
+	struct instruction *phi;
+
+	phis_used = NULL;
+	FOR_EACH_PTR(phis_all, phi) {
+		if (has_users(phi->target)) {
+			phi->used = 1;
+			add_instruction(&phis_used, phi);
+		}
+	} END_FOR_EACH_PTR(phi);
+
+	FOR_EACH_PTR(phis_used, phi) {
+		if (!phi->bb)
+			continue;
+		ssa_rename_phi(phi);
+	} END_FOR_EACH_PTR(phi);
+}
+
+void ssa_convert(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+	pseudo_t pseudo;
+	int first, last;
+
+	// calculate the number of BBs
+	first = ep->entry->bb->nr;
+	last = first;
+	FOR_EACH_PTR(ep->bbs, bb) {
+		int nr = bb->nr;
+		if (nr > last)
+			last = nr;
+	} END_FOR_EACH_PTR(bb);
+
+	processed = sset_init(first, last);
+
+	// try to promote memory accesses to pseudos
+	FOR_EACH_PTR(ep->accesses, pseudo) {
+		ssa_convert_one_var(ep, pseudo->sym);
+	} END_FOR_EACH_PTR(pseudo);
+
+	// rename the converted accesses
+	phis_all = phis_used = NULL;
+	ssa_rename_insns(ep);
+	ssa_rename_phis(ep);
+}
