1206 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1206 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include <assert.h>
 | 
						|
#include <string.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include "decompiler.h"
 | 
						|
#include "code_analyzer.h"
 | 
						|
#include "platform_util.h"
 | 
						|
 | 
						|
struct Block;
 | 
						|
 | 
						|
#define PCM_BRANCH_TARGET		(1<<0)
 | 
						|
#define PCM_OPCODE				(1<<1)
 | 
						|
#define PCM_HAS_BLOCK			(1<<2)
 | 
						|
#define PCM_NUM_BITS			3
 | 
						|
 | 
						|
#define NEXT_OP(cip, op)		cip += (dc->opdef[op].params + 1) * sizeof(cell_t)
 | 
						|
 | 
						|
struct Edge
 | 
						|
{
 | 
						|
	Edge *next;
 | 
						|
	Block *target;
 | 
						|
	uint32_t ip;
 | 
						|
};
 | 
						|
 | 
						|
struct Block
 | 
						|
{
 | 
						|
	cell_t ip_start;
 | 
						|
	cell_t ip_end;
 | 
						|
	Edge *edge_list;
 | 
						|
	Edge *final_edge;
 | 
						|
	Block *next_in_table;
 | 
						|
};
 | 
						|
 | 
						|
struct ControlFlowGraph
 | 
						|
{
 | 
						|
	int err;
 | 
						|
	unsigned int max_blocks;
 | 
						|
	unsigned int max_edges;
 | 
						|
	unsigned int used_blocks;
 | 
						|
	unsigned int used_edges;
 | 
						|
	Block *blocks;
 | 
						|
	Edge *edges;
 | 
						|
	Block *entry;
 | 
						|
	FunctionInfo *func;
 | 
						|
};
 | 
						|
 | 
						|
DefineNode::DefineNode(BaseNode *o, const char *fmt, ...) : BaseNode(_OP_DEFINE), other(o)
 | 
						|
{
 | 
						|
	va_list ap;
 | 
						|
	
 | 
						|
	va_start(ap, fmt);
 | 
						|
	Sp_FormatArgs(name, sizeof(name), fmt, ap);
 | 
						|
	va_end(ap);
 | 
						|
}
 | 
						|
 | 
						|
static FunctionInfo *sp_InferFuncPrototype(sp_decomp_t *dc, uint32_t code_addr, bool is_public)
 | 
						|
{
 | 
						|
	sp_tag_t *pTag;
 | 
						|
	sp_file_t *plugin;
 | 
						|
	FunctionInfo *info;
 | 
						|
	sp_fdbg_symbol_t *sym;
 | 
						|
 | 
						|
	plugin = dc->plugin;
 | 
						|
	info = new FunctionInfo;
 | 
						|
	memset(info, 0, sizeof(FunctionInfo));
 | 
						|
 | 
						|
	info->code_addr = code_addr;
 | 
						|
	
 | 
						|
	if ((sym = Sp_FindFunctionSym(plugin, code_addr)) != NULL)
 | 
						|
	{
 | 
						|
		Sp_Format(info->name, sizeof(info->name), "%s", plugin->debug.stringbase + sym->name);
 | 
						|
		if (sym->tagid != 0)
 | 
						|
		{
 | 
						|
			info->tag = Sp_FindTag(plugin, sym->tagid);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		Sp_Format(info->name, sizeof(info->name), "function_%x", code_addr);
 | 
						|
		for (uint32_t i = 0; i < plugin->num_publics; i++)
 | 
						|
		{
 | 
						|
			if (plugin->publics[i].code_offs == code_addr)
 | 
						|
			{
 | 
						|
				Sp_Format(info->name,
 | 
						|
					sizeof(info->name),
 | 
						|
					"%s",
 | 
						|
					plugin->publics[i].name);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	info->sym = sym;
 | 
						|
	info->is_public = is_public;
 | 
						|
	
 | 
						|
 | 
						|
	/* Search for all locals */
 | 
						|
	{
 | 
						|
		uint32_t i;
 | 
						|
		uint8_t *cursor;
 | 
						|
 | 
						|
		cursor = (uint8_t *)plugin->debug.symbols;
 | 
						|
		for (i = 0; i < plugin->debug.syms_num; i++)
 | 
						|
		{
 | 
						|
			sym = (sp_fdbg_symbol_t *)cursor;
 | 
						|
 | 
						|
			if (sym->ident == SP_SYM_VARIABLE
 | 
						|
				|| sym->ident == SP_SYM_REFARRAY
 | 
						|
				|| sym->ident == SP_SYM_REFERENCE
 | 
						|
				|| sym->ident == SP_SYM_ARRAY)
 | 
						|
			{
 | 
						|
				if (code_addr >= sym->codestart
 | 
						|
					&& code_addr < sym->codeend
 | 
						|
					&& sym->addr >= 0xC
 | 
						|
					&& (sym->addr & 0x3) == 0
 | 
						|
					&& ((sym->addr - 0xC) >> 2) < SP_MAX_EXEC_PARAMS
 | 
						|
					&& sym->vclass == 1)
 | 
						|
				{
 | 
						|
					unsigned int num;
 | 
						|
 | 
						|
					num = (sym->addr - 0xC) >> 2;
 | 
						|
					info->known_args[num].sym = sym;
 | 
						|
					if (num + 1 > info->num_known_args)
 | 
						|
					{
 | 
						|
						info->num_known_args = num + 1;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			cursor += sizeof(sp_fdbg_symbol_t);
 | 
						|
			if (sym->dimcount > 0)
 | 
						|
			{
 | 
						|
				cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* Map all arguments into place. */
 | 
						|
	for (unsigned int i = 0; i < info->num_known_args; i++)
 | 
						|
	{
 | 
						|
		if ((sym = info->known_args[i].sym) == NULL)
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		if (sym->tagid != 0 && (pTag = Sp_FindTag(plugin, sym->tagid)) != NULL)
 | 
						|
		{
 | 
						|
			info->known_args[i].tag = pTag;
 | 
						|
		}
 | 
						|
		info->known_args[i].name = plugin->debug.stringbase + sym->name;
 | 
						|
		
 | 
						|
		info->known_args[i].dimcount = sym->dimcount;
 | 
						|
		if (sym->dimcount > 0)
 | 
						|
		{
 | 
						|
			info->known_args[i].dims = 
 | 
						|
				(sp_fdbg_arraydim_t *)((char *)sym + sizeof(sp_fdbg_symbol_t));
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return info;
 | 
						|
}
 | 
						|
 | 
						|
static FunctionInfo *sp_GetFuncPrototype(sp_decomp_t *dc,
 | 
						|
										 uint32_t code_addr,
 | 
						|
										 bool known,
 | 
						|
										 bool is_public = false);
 | 
						|
static FunctionInfo *sp_GetFuncPrototype(sp_decomp_t *dc,
 | 
						|
										 uint32_t code_addr,
 | 
						|
										 bool known,
 | 
						|
										 bool is_public)
 | 
						|
{
 | 
						|
	FunctionInfo *func;
 | 
						|
 | 
						|
	func = dc->funcs;
 | 
						|
	while (func != NULL)
 | 
						|
	{
 | 
						|
		if (func->code_addr == code_addr)
 | 
						|
		{
 | 
						|
			return func;
 | 
						|
		}
 | 
						|
		func = func->next;
 | 
						|
	}
 | 
						|
 | 
						|
	/* See if this is public. */
 | 
						|
	if (!known)
 | 
						|
	{
 | 
						|
		is_public = false;
 | 
						|
		for (unsigned int i = 0; i < dc->plugin->num_publics; i++)
 | 
						|
		{
 | 
						|
			if (dc->plugin->publics[i].code_offs == code_addr)
 | 
						|
			{
 | 
						|
				is_public = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	func = sp_InferFuncPrototype(dc, code_addr, is_public);
 | 
						|
	func->next = dc->funcs;
 | 
						|
	dc->funcs = func;
 | 
						|
 | 
						|
	return func;
 | 
						|
}
 | 
						|
 | 
						|
static void sp_CacheLocals(sp_decomp_t *dc, FunctionInfo *info)
 | 
						|
{
 | 
						|
	sp_file_t *plugin;
 | 
						|
	sp_fdbg_symbol_t *sym;
 | 
						|
 | 
						|
	plugin = dc->plugin;
 | 
						|
 | 
						|
	/* Search for all locals */
 | 
						|
	{
 | 
						|
		uint32_t i;
 | 
						|
		uint8_t *cursor;
 | 
						|
 | 
						|
		cursor = (uint8_t *)plugin->debug.symbols;
 | 
						|
		for (i = 0; i < plugin->debug.syms_num; i++)
 | 
						|
		{
 | 
						|
			sym = (sp_fdbg_symbol_t *)cursor;
 | 
						|
 | 
						|
			if ((sym->codestart >= info->code_addr && sym->codestart < info->code_end)
 | 
						|
				&& (sym->codeend <= info->code_end && sym->codeend >= info->code_addr)
 | 
						|
				&& sym->vclass == 1)
 | 
						|
			{
 | 
						|
				info->num_known_vars++;
 | 
						|
			}
 | 
						|
 | 
						|
			cursor += sizeof(sp_fdbg_symbol_t);
 | 
						|
			if (sym->dimcount > 0)
 | 
						|
			{
 | 
						|
				cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* Map all locals into place. */
 | 
						|
	if (info->num_known_vars)
 | 
						|
	{
 | 
						|
		uint32_t i, num;
 | 
						|
		uint8_t *cursor;
 | 
						|
 | 
						|
		num = 0;
 | 
						|
		info->known_vars = new FuncVar[info->num_known_vars];
 | 
						|
		memset(info->known_vars, 0, sizeof(FuncVar) * info->num_known_vars);
 | 
						|
 | 
						|
		cursor = (uint8_t *)plugin->debug.symbols;
 | 
						|
		for (i = 0; i < plugin->debug.syms_num; i++)
 | 
						|
		{
 | 
						|
			sym = (sp_fdbg_symbol_t *)cursor;
 | 
						|
 | 
						|
			if (sym->ident == SP_SYM_VARIABLE
 | 
						|
				|| sym->ident == SP_SYM_REFARRAY
 | 
						|
				|| sym->ident == SP_SYM_REFERENCE
 | 
						|
				|| sym->ident == SP_SYM_ARRAY)
 | 
						|
			{
 | 
						|
				/* Detect all function variables. */
 | 
						|
				if ((sym->codestart >= info->code_addr && sym->codestart < info->code_end)
 | 
						|
					&& (sym->codeend <= info->code_end && sym->codeend >= info->code_addr)
 | 
						|
					&& sym->vclass == 1)
 | 
						|
				{
 | 
						|
					assert(num < info->num_known_vars);
 | 
						|
					info->known_vars[num].sym = sym;
 | 
						|
					info->known_vars[num].name = plugin->debug.stringbase + sym->name;
 | 
						|
					info->known_vars[num].tag = 
 | 
						|
						(sym->tagid == 0) ? NULL : Sp_FindTag(plugin, sym->tagid);
 | 
						|
					info->known_vars[num].dimcount = sym->dimcount;
 | 
						|
					info->known_vars[num].dims = 
 | 
						|
						(sym->dimcount == 0)
 | 
						|
						? NULL
 | 
						|
						: (sp_fdbg_arraydim_t *)(cursor + sizeof(sp_fdbg_symbol_t));
 | 
						|
					num++;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			cursor += sizeof(sp_fdbg_symbol_t);
 | 
						|
			if (sym->dimcount > 0)
 | 
						|
			{
 | 
						|
				cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		assert(num == info->num_known_vars);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void sp_PrintFunctionPrototype(PrintBuffer *printer, sp_decomp_t *dc, FunctionInfo *func)
 | 
						|
{
 | 
						|
	FuncVar *var;
 | 
						|
	
 | 
						|
	if (func->is_public)
 | 
						|
	{
 | 
						|
		printer->Append("public ");
 | 
						|
	}
 | 
						|
	if (func->tag != NULL)
 | 
						|
	{
 | 
						|
		printer->Append("%s:", func->tag->name);
 | 
						|
	}
 | 
						|
	printer->Append("%s(", func->name);
 | 
						|
 | 
						|
	for (unsigned int i = 0; i < func->num_known_args; i++)
 | 
						|
	{
 | 
						|
		var = &func->known_args[i];
 | 
						|
		if (var->sym == NULL)
 | 
						|
		{
 | 
						|
			printer->Append("unknown_arg_%d%s, ", i, i == func->num_known_args - 1 ? "" : ",");
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		if (var->sym->ident == SP_SYM_REFERENCE)
 | 
						|
		{
 | 
						|
			printer->Append("&");
 | 
						|
		}
 | 
						|
		if (var->tag != NULL)
 | 
						|
		{
 | 
						|
			printer->Append("%s:", var->tag->name);
 | 
						|
		}
 | 
						|
		printer->Append("%s", var->name);
 | 
						|
 | 
						|
		if (var->sym->dimcount > 0)
 | 
						|
		{
 | 
						|
			sp_fdbg_arraydim_t *dims = 
 | 
						|
				(sp_fdbg_arraydim_t *)((char *)var->sym + sizeof(sp_fdbg_symbol_t));
 | 
						|
 | 
						|
			for (uint16_t j = 0; j < var->sym->dimcount; j++)
 | 
						|
			{
 | 
						|
				if (dims[j].size == 0)
 | 
						|
				{
 | 
						|
					printer->Append("[]");
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					printer->Append("[%d]", dims[j].size);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (i != func->num_known_args - 1)
 | 
						|
		{
 | 
						|
			printer->Append(", ");
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	printer->Append(")");
 | 
						|
}
 | 
						|
 | 
						|
void sp_DebugExprTree(PrintBuffer *buffer, BaseNode *node, bool is_stmt=false)
 | 
						|
{
 | 
						|
	switch (node->op)
 | 
						|
	{
 | 
						|
	case _OP_DEFINE:
 | 
						|
		{
 | 
						|
			DefineNode *def = (DefineNode *)node;
 | 
						|
 | 
						|
			buffer->Append("new %s = ", def->name);
 | 
						|
			sp_DebugExprTree(buffer, def->other, is_stmt);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	case OP_CALL:
 | 
						|
		{
 | 
						|
			CallNode *call = (CallNode *)node;
 | 
						|
 | 
						|
			buffer->Append("%s(", call->func->name);
 | 
						|
			for (unsigned int i = 0; i < call->argc; i++)
 | 
						|
			{
 | 
						|
				sp_DebugExprTree(buffer, call->args[i], false);
 | 
						|
			}
 | 
						|
			buffer->Append(")");
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	case _OP_USE:
 | 
						|
		{
 | 
						|
			UseNode *unode = (UseNode *)node;
 | 
						|
 | 
						|
			buffer->Append("%s", unode->def->name);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	case _OP_VAR:
 | 
						|
		{
 | 
						|
			VarNode *var_node = (VarNode *)node;
 | 
						|
			buffer->Append("%s", var_node->var->name);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	case _OP_NEW:
 | 
						|
		{
 | 
						|
			DeclNode *d_node = (DeclNode *)node;
 | 
						|
 | 
						|
			if (d_node->expr == NULL)
 | 
						|
			{
 | 
						|
				buffer->Append("decl ");
 | 
						|
				sp_DebugExprTree(buffer, d_node->var, true);
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				buffer->Append("new ");
 | 
						|
				sp_DebugExprTree(buffer, d_node->var, true);
 | 
						|
				buffer->Append(" = ");
 | 
						|
				sp_DebugExprTree(buffer, d_node->expr, true);
 | 
						|
			}
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	case _OP_STMT:
 | 
						|
	case OP_RETN:
 | 
						|
		{
 | 
						|
			StmtNode *u_node = (StmtNode *)node;
 | 
						|
 | 
						|
			while (u_node != NULL)
 | 
						|
			{
 | 
						|
				buffer->Append("\t");
 | 
						|
				if (u_node->op == OP_RETN)
 | 
						|
				{
 | 
						|
					buffer->Append("return ");
 | 
						|
				}
 | 
						|
				sp_DebugExprTree(buffer, u_node->node, true);
 | 
						|
				buffer->Append(";\n");
 | 
						|
				u_node = u_node->next;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	case _OP_CONST:
 | 
						|
		{
 | 
						|
			ConstNode *c_node = (ConstNode *)node;
 | 
						|
 | 
						|
			buffer->Append("%d", c_node->val);
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	case OP_ADD:
 | 
						|
	case OP_ADD_C:
 | 
						|
	case OP_SMUL:
 | 
						|
	case OP_SMUL_C:
 | 
						|
	case OP_SDIV:
 | 
						|
	case OP_SUB_ALT:
 | 
						|
	case _OP_STORE:
 | 
						|
	case _OP_MODULO:
 | 
						|
		{
 | 
						|
			BinOpNode *bin_node = (BinOpNode *)node;
 | 
						|
 | 
						|
			if (!is_stmt)
 | 
						|
			{
 | 
						|
				buffer->Append("(");
 | 
						|
			}
 | 
						|
			sp_DebugExprTree(buffer, bin_node->left);
 | 
						|
 | 
						|
			switch (node->op)
 | 
						|
			{
 | 
						|
			case OP_ADD:
 | 
						|
			case OP_ADD_C:
 | 
						|
				{
 | 
						|
					buffer->Append(" + ");
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			case OP_SMUL:
 | 
						|
			case OP_SMUL_C:
 | 
						|
				{
 | 
						|
					buffer->Append(" * ");
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			case _OP_STORE:
 | 
						|
				{
 | 
						|
					buffer->Append(" = ");
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			case OP_SUB_ALT:
 | 
						|
				{
 | 
						|
					buffer->Append(" - ");
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			case OP_SDIV:
 | 
						|
				{
 | 
						|
					buffer->Append(" / ");
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			case _OP_MODULO:
 | 
						|
				{
 | 
						|
					buffer->Append(" % ");
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			sp_DebugExprTree(buffer, bin_node->right, node->op == _OP_STORE);
 | 
						|
			if (!is_stmt)
 | 
						|
			{
 | 
						|
				buffer->Append(")");
 | 
						|
			}
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void sp_DebugPrint(FunctionInfo *info, sp_decomp_t *dc, BaseNode *node)
 | 
						|
{
 | 
						|
	PrintBuffer buffer;
 | 
						|
 | 
						|
	sp_PrintFunctionPrototype(&buffer, dc, info);
 | 
						|
	buffer.Append("\n{\n");
 | 
						|
	sp_DebugExprTree(&buffer, node, true);
 | 
						|
	buffer.Append("}\n");
 | 
						|
	buffer.Dump(stdout);
 | 
						|
}
 | 
						|
 | 
						|
#define PCODE_VAL(pcode, offs)			*(cell_t *)((char *)(pcode)+(offs))
 | 
						|
#define PCODE_PARAM(pcode, offs, num)	*(cell_t *)((char *)(pcode)+(offs)+((num)*sizeof(cell_t)))
 | 
						|
 | 
						|
FuncVar *sp_FindGraphVar(FunctionInfo *func, ucell_t cip, cell_t sp, cell_t stack)
 | 
						|
{
 | 
						|
	if (stack >= 0xC)
 | 
						|
	{
 | 
						|
		if ((stack & 0x3) != 0)
 | 
						|
		{
 | 
						|
			return NULL;
 | 
						|
		}
 | 
						|
		stack -= 0xC;
 | 
						|
		stack >>= 2;
 | 
						|
		if (stack >= SP_MAX_EXEC_PARAMS)
 | 
						|
		{
 | 
						|
			return NULL;
 | 
						|
		}
 | 
						|
		if (func->known_args[stack].sym == NULL)
 | 
						|
		{
 | 
						|
			return NULL;
 | 
						|
		}
 | 
						|
		return &func->known_args[stack];
 | 
						|
	}
 | 
						|
	else if (stack < 0)
 | 
						|
	{
 | 
						|
		FuncVar *var;
 | 
						|
 | 
						|
		if (stack < sp)
 | 
						|
		{
 | 
						|
			return NULL;
 | 
						|
		}
 | 
						|
 | 
						|
		for (unsigned int i = 0; i < func->num_known_vars; i++)
 | 
						|
		{
 | 
						|
			var = &func->known_vars[i];
 | 
						|
 | 
						|
			if (cip >= var->sym->codestart
 | 
						|
				&& cip <= var->sym->codeend
 | 
						|
				&& stack == var->sym->addr)
 | 
						|
			{
 | 
						|
				return var;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
#define CHECK_VALUE(v)							\
 | 
						|
	assert(v != NULL);							\
 | 
						|
	if (v == NULL) {							\
 | 
						|
		err = SP_ERROR_INVALID_INSTRUCTION;		\
 | 
						|
		goto return_error;						\
 | 
						|
	}
 | 
						|
 | 
						|
void *operator new(size_t size, ControlFlowGraph *graph)
 | 
						|
{
 | 
						|
	return malloc(size);
 | 
						|
}
 | 
						|
 | 
						|
void *operator new[](size_t size, ControlFlowGraph *graph)
 | 
						|
{
 | 
						|
	return malloc(size);
 | 
						|
}
 | 
						|
 | 
						|
void operator delete(void *mem, ControlFlowGraph *graph)
 | 
						|
{
 | 
						|
	free(mem);
 | 
						|
}
 | 
						|
 | 
						|
void operator delete[](void *mem, ControlFlowGraph *graph)
 | 
						|
{
 | 
						|
	free(mem);
 | 
						|
}
 | 
						|
 | 
						|
#define MAX_STACK_ENTRIES		256
 | 
						|
 | 
						|
struct StackEntry
 | 
						|
{
 | 
						|
	StackEntry()
 | 
						|
	{
 | 
						|
	}
 | 
						|
	StackEntry(cell_t d, BaseNode *e) : depth(d), expr(e)
 | 
						|
	{
 | 
						|
	}
 | 
						|
	cell_t depth;
 | 
						|
	BaseNode *expr;
 | 
						|
};
 | 
						|
 | 
						|
int sp_AnalyzeGraph(sp_decomp_t *dc, ControlFlowGraph *graph)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
	cell_t sp;
 | 
						|
	Block *block;
 | 
						|
	cell_t *pcode;
 | 
						|
	cell_t cip, cip_end;
 | 
						|
	StmtNode *root, *rtemp;
 | 
						|
	StmtNode *root_tail;
 | 
						|
	BaseNode *pri, *alt;
 | 
						|
	FunctionInfo *func, *tfunc;
 | 
						|
	cell_t p1, p2, op;
 | 
						|
	FuncVar *v1, *v2;
 | 
						|
	unsigned int callnumber = 0;
 | 
						|
	unsigned int stack_entries = 0;
 | 
						|
	StackEntry eval_stack[MAX_STACK_ENTRIES];
 | 
						|
 | 
						|
	/* Init everything.
 | 
						|
	 * Stack pointer starts off at 0.
 | 
						|
	 */
 | 
						|
	sp = 0;
 | 
						|
	pri = NULL;
 | 
						|
	alt = NULL;
 | 
						|
	root = NULL;
 | 
						|
	func = graph->func;
 | 
						|
	block = graph->entry;
 | 
						|
	pcode = (cell_t *)dc->plugin->pcode;
 | 
						|
 | 
						|
	cip = block->ip_start;
 | 
						|
	cip_end = block->ip_end;
 | 
						|
 | 
						|
	root_tail = root = new (graph) StmtNode(_OP_STMT, NULL, NULL);
 | 
						|
 | 
						|
	while (cip < cip_end)
 | 
						|
	{
 | 
						|
		op = PCODE_VAL(pcode, cip);
 | 
						|
 | 
						|
		switch (op)
 | 
						|
		{
 | 
						|
		case OP_PROC:
 | 
						|
		case OP_BREAK:
 | 
						|
			{
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_CALL:
 | 
						|
		case OP_SYSREQ_N:
 | 
						|
			{
 | 
						|
				p1 = PCODE_PARAM(pcode, cip, 1);
 | 
						|
				if (op == OP_CALL)
 | 
						|
				{
 | 
						|
					tfunc = sp_GetFuncPrototype(dc, p1, false);
 | 
						|
				}
 | 
						|
				else if (op == OP_SYSREQ_N)
 | 
						|
				{
 | 
						|
					assert(dc->natives);
 | 
						|
					assert(uint32_t(p1) < dc->plugin->num_natives);
 | 
						|
					tfunc = &dc->natives[p1];
 | 
						|
				}
 | 
						|
				CHECK_VALUE(tfunc);
 | 
						|
 | 
						|
				/* Sanity checks, :TODO: runtime checks later. */
 | 
						|
				assert(stack_entries >= 1);
 | 
						|
 | 
						|
				if (op == OP_SYSREQ_N)
 | 
						|
				{
 | 
						|
					p2 = PCODE_PARAM(pcode, cip, 2);
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					/* Try to read the value off the stack. */
 | 
						|
					ConstNode *cnode;
 | 
						|
					cnode = (ConstNode *)eval_stack[stack_entries-1].expr;
 | 
						|
					/* Sanity checks, :TODO: runtime checks later. */
 | 
						|
					assert(cnode->op == _OP_CONST);
 | 
						|
					assert(cnode->val >= 0);
 | 
						|
					assert(uint32_t(cnode->val) == tfunc->num_known_args);
 | 
						|
					p2 = cnode->val;
 | 
						|
					stack_entries--;
 | 
						|
				}
 | 
						|
 | 
						|
				assert(uint32_t(p2) <= stack_entries);
 | 
						|
				
 | 
						|
				/* Build arg SSA */
 | 
						|
				BaseNode **nodes = new (graph) BaseNode *[p2];
 | 
						|
				for (cell_t argno = 0; argno < p2; argno++, stack_entries--)
 | 
						|
				{
 | 
						|
					nodes[argno] = eval_stack[stack_entries-1].expr;
 | 
						|
				}
 | 
						|
 | 
						|
				/* Get rid of any args we pushed. */
 | 
						|
				sp += p2 * sizeof(cell_t);
 | 
						|
				if (op == OP_CALL)
 | 
						|
				{
 | 
						|
					sp += sizeof(cell_t);
 | 
						|
				}
 | 
						|
				assert(sp <= 0);
 | 
						|
 | 
						|
				/* Build function call SSA */
 | 
						|
				pri = new (graph) CallNode(tfunc, nodes, p2);
 | 
						|
				pri = new (graph) DefineNode(pri, "call%03x", ++callnumber);
 | 
						|
				rtemp = new (graph) StmtNode(_OP_STMT, pri, NULL);
 | 
						|
				root_tail->next = rtemp;
 | 
						|
				root_tail = rtemp;
 | 
						|
				pri = new (graph) UseNode((DefineNode *)pri);
 | 
						|
				
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_CONST_PRI:
 | 
						|
			{
 | 
						|
				p1 = PCODE_PARAM(pcode, cip, 1);
 | 
						|
				pri = new (graph) ConstNode(p1);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_PUSH_C:
 | 
						|
		case OP_PUSH2_C:
 | 
						|
		case OP_PUSH3_C:
 | 
						|
		case OP_PUSH5_C:
 | 
						|
			{
 | 
						|
				for (int N = 1; N <= dc->opdef[op].params; N++)
 | 
						|
				{
 | 
						|
					p1 = PCODE_PARAM(pcode, cip, N);
 | 
						|
 | 
						|
					/* Adjust stack pointer. */
 | 
						|
					sp -= sizeof(cell_t);
 | 
						|
 | 
						|
					if ((v1 = sp_FindGraphVar(func, cip, sp, sp)) == NULL)
 | 
						|
					{
 | 
						|
						/* Add a new stack entry. */
 | 
						|
						assert(stack_entries < MAX_STACK_ENTRIES);
 | 
						|
						eval_stack[stack_entries++] = StackEntry(sp, new ConstNode(p1));
 | 
						|
					}
 | 
						|
					else
 | 
						|
					{
 | 
						|
						assert(v1->new_stmt == NULL);
 | 
						|
						if (v1->new_stmt == NULL)
 | 
						|
						{
 | 
						|
							v1->new_stmt = new (graph) DeclNode(
 | 
						|
								new (graph) VarNode(v1),
 | 
						|
								new (graph) ConstNode(p1));
 | 
						|
							rtemp = new (graph) StmtNode(
 | 
						|
								_OP_STMT,
 | 
						|
								v1->new_stmt,
 | 
						|
								NULL);
 | 
						|
							root_tail->next = rtemp;
 | 
						|
							root_tail = rtemp;
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_PUSH_S:
 | 
						|
			{
 | 
						|
				p1 = PCODE_PARAM(pcode, cip, 1);
 | 
						|
				v1 = sp_FindGraphVar(func, cip, sp, p1);
 | 
						|
				CHECK_VALUE(v1);
 | 
						|
 | 
						|
				/* Adjust stack pointer. */
 | 
						|
				sp -= sizeof(cell_t);
 | 
						|
 | 
						|
				/* Right now, don't allow aliasing or overflows. */
 | 
						|
				assert(sp_FindGraphVar(func, cip, sp, sp) == NULL);
 | 
						|
				assert(stack_entries < MAX_STACK_ENTRIES);
 | 
						|
 | 
						|
				/* Add a new stack entry. */
 | 
						|
				eval_stack[stack_entries++] = StackEntry(sp, new (graph) VarNode(v1));
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_PUSH_PRI:
 | 
						|
			{
 | 
						|
				CHECK_VALUE(pri);
 | 
						|
 | 
						|
				/* Adjust stack pointer. */
 | 
						|
				sp -= sizeof(cell_t);
 | 
						|
				
 | 
						|
				/* Right now, don't allow aliasing or overflows. */
 | 
						|
				assert(sp_FindGraphVar(func, cip, sp, sp) == NULL);
 | 
						|
				assert(stack_entries < MAX_STACK_ENTRIES);
 | 
						|
 | 
						|
				/* Add a new stack entry. */
 | 
						|
				eval_stack[stack_entries++] = StackEntry(sp, pri);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_POP_ALT:
 | 
						|
			{
 | 
						|
				assert(stack_entries > 0);
 | 
						|
				assert(eval_stack[stack_entries - 1].depth == sp);
 | 
						|
 | 
						|
				alt = eval_stack[--stack_entries].expr;
 | 
						|
				sp += sizeof(cell_t);
 | 
						|
				assert(sp <= 0);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_STACK:
 | 
						|
			{
 | 
						|
				p1 = PCODE_PARAM(pcode, cip, 1);
 | 
						|
				sp += p1;
 | 
						|
				assert(sp <= 0);
 | 
						|
				if (p1 < 0)
 | 
						|
				{
 | 
						|
					/* Find if we should track a new variable declaration. */
 | 
						|
					v1 = sp_FindGraphVar(func, cip, sp, sp);
 | 
						|
					CHECK_VALUE(v1);
 | 
						|
					assert(v1->new_stmt == NULL);
 | 
						|
					if (v1->new_stmt == NULL)
 | 
						|
					{
 | 
						|
						v1->new_stmt = new (graph) DeclNode(
 | 
						|
							new (graph) VarNode(v1),
 | 
						|
							NULL);
 | 
						|
						rtemp = new (graph) StmtNode(
 | 
						|
							_OP_STMT,
 | 
						|
							v1->new_stmt,
 | 
						|
							NULL);
 | 
						|
						root_tail->next = rtemp;
 | 
						|
						root_tail = rtemp;
 | 
						|
					}
 | 
						|
					assert(abs(p1) >> 2 == 1);
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					/* Erase anything left over on the evaluation stack. */
 | 
						|
					while (stack_entries > 0
 | 
						|
						   && eval_stack[stack_entries - 1].depth < sp)
 | 
						|
					{
 | 
						|
						assert(0);
 | 
						|
						stack_entries--;
 | 
						|
					}
 | 
						|
				}
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_ZERO_PRI:
 | 
						|
			{
 | 
						|
				pri = new (graph) ConstNode(0);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_MOVE_ALT:
 | 
						|
			{
 | 
						|
				CHECK_VALUE(pri);
 | 
						|
				alt = pri;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_LOAD_S_PRI:
 | 
						|
			{
 | 
						|
				p1 = PCODE_PARAM(pcode, cip, 1);
 | 
						|
				v1 = sp_FindGraphVar(func, cip, sp, p1);
 | 
						|
				CHECK_VALUE(v1);
 | 
						|
				pri = new (graph) VarNode(v1);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_LOAD_S_ALT:
 | 
						|
			{
 | 
						|
				p1 = PCODE_PARAM(pcode, cip, 1);
 | 
						|
				v1 = sp_FindGraphVar(func, cip, sp, p1);
 | 
						|
				CHECK_VALUE(v1);
 | 
						|
				alt = new (graph) VarNode(v1);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_STOR_S_PRI:
 | 
						|
			{
 | 
						|
				p1 = PCODE_PARAM(pcode, cip, 1);
 | 
						|
				v1 = sp_FindGraphVar(func, cip, sp, p1);
 | 
						|
				CHECK_VALUE(v1);
 | 
						|
				CHECK_VALUE(pri);
 | 
						|
				rtemp = new (graph) StmtNode(
 | 
						|
					_OP_STMT,
 | 
						|
					new (graph) BinOpNode(_OP_STORE, new (graph) VarNode(v1), pri),
 | 
						|
					NULL);
 | 
						|
				root_tail->next = rtemp;
 | 
						|
				root_tail = rtemp;
 | 
						|
 | 
						|
				/** 
 | 
						|
				 * We create a varnode to simulate a load here.
 | 
						|
				 * This helps accidental duplication of the expression tree.
 | 
						|
				 */
 | 
						|
				pri = new (graph) VarNode(v1);	
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_LOAD_S_BOTH:
 | 
						|
			{
 | 
						|
				p1 = PCODE_PARAM(pcode, cip, 1);
 | 
						|
				p2 = PCODE_PARAM(pcode, cip, 2);
 | 
						|
				v1 = sp_FindGraphVar(func, cip, sp, p1);
 | 
						|
				v2 = sp_FindGraphVar(func, cip, sp, p2);
 | 
						|
				CHECK_VALUE(v1);
 | 
						|
				CHECK_VALUE(v2);
 | 
						|
				pri = new (graph) VarNode(v1);
 | 
						|
				alt = new (graph) VarNode(v2);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_ADD:
 | 
						|
		case OP_SMUL:
 | 
						|
			{
 | 
						|
				CHECK_VALUE(pri);
 | 
						|
				CHECK_VALUE(alt);
 | 
						|
				pri = new (graph) BinOpNode(op, pri, alt);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_SUB_ALT:
 | 
						|
			{
 | 
						|
				CHECK_VALUE(pri);
 | 
						|
				CHECK_VALUE(alt);
 | 
						|
				pri = new (graph) BinOpNode(op, alt, pri);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_SDIV:
 | 
						|
			{
 | 
						|
				CHECK_VALUE(pri);
 | 
						|
				CHECK_VALUE(alt);
 | 
						|
				pri = new (graph) BinOpNode(OP_SDIV, pri, alt);
 | 
						|
				alt = new (graph) BinOpNode(_OP_MODULO, pri, alt);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_SDIV_ALT:
 | 
						|
			{
 | 
						|
				CHECK_VALUE(pri);
 | 
						|
				CHECK_VALUE(alt);
 | 
						|
				pri = new (graph) BinOpNode(OP_SDIV, alt, pri);
 | 
						|
				alt = new (graph) BinOpNode(_OP_MODULO, alt, pri);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_ADD_C:
 | 
						|
		case OP_SMUL_C:
 | 
						|
			{
 | 
						|
				p1 = PCODE_PARAM(pcode, cip, 1);
 | 
						|
				CHECK_VALUE(pri);
 | 
						|
				pri = new (graph) BinOpNode(op, pri, new (graph) ConstNode(p1));
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case OP_RETN:
 | 
						|
			{
 | 
						|
				CHECK_VALUE(pri);
 | 
						|
				rtemp = new (graph) StmtNode(op, pri, NULL);
 | 
						|
				root_tail->next = rtemp;
 | 
						|
				root_tail = rtemp;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			{
 | 
						|
				assert(0);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		NEXT_OP(cip, op);
 | 
						|
	}
 | 
						|
 | 
						|
	sp_DebugPrint(graph->func, dc, root->next);
 | 
						|
 | 
						|
	return SP_ERROR_NONE;
 | 
						|
 | 
						|
return_error:
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
#undef CHECK_FUNCVAR
 | 
						|
 | 
						|
inline Block *sp_BlockFromMap(sp_decomp_t *dc, ControlFlowGraph *graph, cell_t cip)
 | 
						|
{
 | 
						|
	cell_t val;
 | 
						|
 | 
						|
	assert(PCODE_VAL(dc->pcode_map, cip) & PCM_HAS_BLOCK);
 | 
						|
 | 
						|
	val = PCODE_VAL(dc->pcode_map, cip);
 | 
						|
	val >>= PCM_NUM_BITS;
 | 
						|
 | 
						|
	assert(val >= 0 && val < (cell_t)graph->used_blocks);
 | 
						|
 | 
						|
	return &graph->blocks[val];
 | 
						|
}
 | 
						|
 | 
						|
void sp_InsertBlock(sp_decomp_t *dc, unsigned int index, cell_t cip)
 | 
						|
{
 | 
						|
	cell_t val;
 | 
						|
 | 
						|
	val = PCODE_VAL(dc->pcode_map, cip);
 | 
						|
 | 
						|
	assert(!(val & PCM_HAS_BLOCK));
 | 
						|
	assert(index < 0x20000000);
 | 
						|
 | 
						|
	val |= PCM_HAS_BLOCK;
 | 
						|
	val |= index << 3;
 | 
						|
 | 
						|
	PCODE_VAL(dc->pcode_map, cip) = val;
 | 
						|
}
 | 
						|
 | 
						|
static Block *sp_BuildBlocks(sp_decomp_t *dc, ControlFlowGraph *graph, cell_t cip)
 | 
						|
{
 | 
						|
	cell_t op;
 | 
						|
	bool is_branch;
 | 
						|
	cell_t target;
 | 
						|
	Block *block;
 | 
						|
	Edge *edge_tail;
 | 
						|
	Block *outgoing;
 | 
						|
	cell_t cip_end;
 | 
						|
	cell_t *pcode;
 | 
						|
 | 
						|
	target = 0;
 | 
						|
	cip_end = graph->func->code_end;
 | 
						|
	pcode = (cell_t *)dc->plugin->pcode;
 | 
						|
 | 
						|
	if (PCODE_VAL(dc->pcode_map, cip) & PCM_HAS_BLOCK)
 | 
						|
	{
 | 
						|
		return sp_BlockFromMap(dc, graph, cip);
 | 
						|
	}
 | 
						|
 | 
						|
	/* We can't add a new block if we hit our limit. */
 | 
						|
	assert(graph->used_blocks < graph->max_blocks);
 | 
						|
	if (graph->used_blocks >= graph->max_blocks)
 | 
						|
	{
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	edge_tail = NULL;
 | 
						|
 | 
						|
	/* Reserve a new block. */
 | 
						|
	block = &graph->blocks[graph->used_blocks++];
 | 
						|
	block->ip_start = cip;
 | 
						|
	
 | 
						|
	/* Enter us into the map. */
 | 
						|
	sp_InsertBlock(dc, graph->used_blocks - 1, cip);
 | 
						|
 | 
						|
	/* Walk the code. */
 | 
						|
	while (cip < cip_end)
 | 
						|
	{
 | 
						|
		/* Check whether this is the start of a new block. */
 | 
						|
		if (cip != block->ip_start
 | 
						|
			&& (PCODE_VAL(dc->pcode_map, cip) & PCM_BRANCH_TARGET))
 | 
						|
		{
 | 
						|
			block->ip_end = cip;
 | 
						|
			if ((outgoing = sp_BuildBlocks(dc, graph, cip)) == NULL)
 | 
						|
			{
 | 
						|
				return NULL;
 | 
						|
			}
 | 
						|
			/* :TODO: add edge */
 | 
						|
			return block;
 | 
						|
		}
 | 
						|
 | 
						|
		is_branch = false;
 | 
						|
		op = PCODE_VAL(pcode, cip);
 | 
						|
		NEXT_OP(cip, op);
 | 
						|
 | 
						|
		if (is_branch)
 | 
						|
		{
 | 
						|
			/* Check that branch offsets go to valid instructions. */
 | 
						|
			if (!(PCODE_VAL(dc->pcode_map, target) & PCM_OPCODE))
 | 
						|
			{
 | 
						|
				graph->err = SP_ERROR_INVALID_INSTRUCTION;
 | 
						|
				return NULL;
 | 
						|
			}
 | 
						|
 | 
						|
			/* Get our target block. */
 | 
						|
			if ((outgoing = sp_BuildBlocks(dc, graph, target)) == NULL)
 | 
						|
			{
 | 
						|
				return NULL;
 | 
						|
			}
 | 
						|
			/* :TODO: add edge */
 | 
						|
		}
 | 
						|
		else if (op == OP_RETN /* :TODO: || op == OP_RET*/)
 | 
						|
		{
 | 
						|
			block->ip_end = cip;
 | 
						|
			return block;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	graph->err = SP_ERROR_INVALID_INSTRUCTION;
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int Sp_DecompFunction(sp_decomp_t *dc, uint32_t code_addr, bool is_public)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
	cell_t op;
 | 
						|
	ucell_t cip;
 | 
						|
	cell_t *pcode;
 | 
						|
	ucell_t cip_end;
 | 
						|
	sp_file_t *plugin;
 | 
						|
	FunctionInfo *func;
 | 
						|
	ControlFlowGraph graph;
 | 
						|
	
 | 
						|
	func = NULL;
 | 
						|
	plugin = dc->plugin;
 | 
						|
	memset(&graph, 0, sizeof(graph));
 | 
						|
 | 
						|
	if (code_addr >= plugin->pcode_size)
 | 
						|
	{
 | 
						|
		err = SP_ERROR_INVALID_INSTRUCTION;
 | 
						|
		goto return_error;
 | 
						|
	}
 | 
						|
 | 
						|
	pcode = (cell_t *)plugin->pcode;
 | 
						|
 | 
						|
	if (PCODE_VAL(pcode, code_addr) != OP_PROC)
 | 
						|
	{
 | 
						|
		err = SP_ERROR_INVALID_INSTRUCTION;
 | 
						|
		goto return_error;
 | 
						|
	}
 | 
						|
 | 
						|
	cip = code_addr + sizeof(cell_t);
 | 
						|
 | 
						|
	/* See if we have an existing function stored. */
 | 
						|
	func = sp_GetFuncPrototype(dc, code_addr, true, is_public);
 | 
						|
 | 
						|
	while (cip < plugin->pcode_size)
 | 
						|
	{
 | 
						|
		op = PCODE_VAL(pcode, cip);
 | 
						|
		if (op >= OP_TOTAL || op < 0)
 | 
						|
		{
 | 
						|
			err = SP_ERROR_INVALID_INSTRUCTION;
 | 
						|
		}
 | 
						|
		if (dc->opdef[op].name == NULL)
 | 
						|
		{
 | 
						|
			err = SP_ERROR_INVALID_INSTRUCTION;
 | 
						|
		}
 | 
						|
		if (op == OP_PROC)
 | 
						|
		{
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		NEXT_OP(cip, op);
 | 
						|
	}
 | 
						|
	cip_end = cip;
 | 
						|
 | 
						|
	func->code_end = cip_end;
 | 
						|
 | 
						|
	/* Now we can cache all local vars. */
 | 
						|
	sp_CacheLocals(dc, func);
 | 
						|
 | 
						|
	/** 
 | 
						|
	 * Make the entry point as the first block.
 | 
						|
	 * It needs to be flagged as well so it doesn't get double counted.
 | 
						|
	 */
 | 
						|
	memset(&PCODE_VAL(dc->pcode_map, code_addr), 0, cip_end - code_addr);
 | 
						|
		   
 | 
						|
	graph.max_blocks = 1;
 | 
						|
	graph.func = func;
 | 
						|
	PCODE_VAL(dc->pcode_map, code_addr) |= PCM_BRANCH_TARGET;
 | 
						|
 | 
						|
	/* Walk the code. */
 | 
						|
	{
 | 
						|
		cell_t target;
 | 
						|
		bool is_branch;
 | 
						|
 | 
						|
		target = 0;
 | 
						|
		cip = code_addr;
 | 
						|
		while (cip < cip_end)
 | 
						|
		{
 | 
						|
			/* Get opcode */
 | 
						|
			op = PCODE_VAL(pcode, cip);
 | 
						|
			/* Mark as not a branch */
 | 
						|
			is_branch = false;
 | 
						|
			/* Make sure we know this is a valid target */
 | 
						|
			PCODE_VAL(dc->pcode_map, cip) |= PCM_OPCODE;
 | 
						|
 | 
						|
			if (is_branch)
 | 
						|
			{
 | 
						|
				/**
 | 
						|
				 * Keep track of where branches are and how many blocks we need.
 | 
						|
				 */
 | 
						|
				if (!(PCODE_VAL(dc->pcode_map, target) & PCM_BRANCH_TARGET))
 | 
						|
				{
 | 
						|
					PCODE_VAL(dc->pcode_map, target) |= PCM_BRANCH_TARGET;
 | 
						|
					graph.max_blocks++;
 | 
						|
 | 
						|
					/**
 | 
						|
					 * If we're creating a new block, estimate that we need to create a new edge 
 | 
						|
					 * as well.
 | 
						|
					 */
 | 
						|
					graph.max_edges++;
 | 
						|
				}
 | 
						|
 | 
						|
                /**
 | 
						|
                 * In this algorithm, jumps do not terminate a block unless they are unconditional.
 | 
						|
                 * This reduces the number of blocks and edges.
 | 
						|
                 *
 | 
						|
                 * A property of this is that jumps are edges, so the number of edges is identical 
 | 
						|
				 * to the number of jumps.
 | 
						|
                 */
 | 
						|
				graph.max_edges++;
 | 
						|
			}
 | 
						|
 | 
						|
			NEXT_OP(cip, op);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* Initialize memory needed for the graph. */
 | 
						|
	graph.blocks = new Block[graph.max_blocks];
 | 
						|
	memset(graph.blocks, 0, sizeof(Block) * graph.max_blocks);
 | 
						|
	
 | 
						|
	if (graph.max_edges != 0)
 | 
						|
	{
 | 
						|
		graph.edges = new Edge[graph.max_edges];
 | 
						|
		memset(graph.edges, 0, sizeof(Edge) * graph.max_edges);
 | 
						|
	}
 | 
						|
 | 
						|
	if ((graph.entry = sp_BuildBlocks(dc, &graph, code_addr)) == NULL)
 | 
						|
	{
 | 
						|
		if ((err = graph.err) == SP_ERROR_NONE)
 | 
						|
		{
 | 
						|
			err = SP_ERROR_ABORTED;
 | 
						|
		}
 | 
						|
		goto return_error;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((err = sp_AnalyzeGraph(dc, &graph)) != SP_ERROR_NONE)
 | 
						|
	{
 | 
						|
		goto return_error;
 | 
						|
	}
 | 
						|
 | 
						|
	return SP_ERROR_NONE;
 | 
						|
 | 
						|
return_error:
 | 
						|
	return err;
 | 
						|
}
 |