sourcepawn/decompiler/code_analyzer.cpp
author David Anderson <dvander@alliedmods.net>
Tue Sep 23 22:00:47 2008 -0700 (2008-09-23)
changeset 2322 2125ced692ac
parent 2320 88550c031d23
child 2325 7a24ee5557fb
permissions -rw-r--r--
Added some notion of def-use to unfold copy propagation across registers and the eval stack.
At some point we will need to walk the expr tree to fold this back where appropriate, i.e. single use for calls.
dvander@2320
     1
#include <assert.h>
dvander@2320
     2
#include <string.h>
dvander@2320
     3
#include <stdlib.h>
dvander@2320
     4
#include "decompiler.h"
dvander@2320
     5
#include "code_analyzer.h"
dvander@2320
     6
#include "platform_util.h"
dvander@2320
     7
dvander@2320
     8
struct Block;
dvander@2320
     9
dvander@2320
    10
#define PCM_BRANCH_TARGET		(1<<0)
dvander@2320
    11
#define PCM_OPCODE				(1<<1)
dvander@2320
    12
#define PCM_HAS_BLOCK			(1<<2)
dvander@2320
    13
#define PCM_NUM_BITS			3
dvander@2320
    14
dvander@2320
    15
#define NEXT_OP(cip, op)		cip += (dc->opdef[op].params + 1) * sizeof(cell_t)
dvander@2320
    16
dvander@2320
    17
struct Edge
dvander@2320
    18
{
dvander@2320
    19
	Edge *next;
dvander@2320
    20
	Block *target;
dvander@2320
    21
	uint32_t ip;
dvander@2320
    22
};
dvander@2320
    23
dvander@2320
    24
struct Block
dvander@2320
    25
{
dvander@2320
    26
	cell_t ip_start;
dvander@2320
    27
	cell_t ip_end;
dvander@2320
    28
	Edge *edge_list;
dvander@2320
    29
	Edge *final_edge;
dvander@2320
    30
	Block *next_in_table;
dvander@2320
    31
};
dvander@2320
    32
dvander@2320
    33
struct ControlFlowGraph
dvander@2320
    34
{
dvander@2320
    35
	int err;
dvander@2320
    36
	unsigned int max_blocks;
dvander@2320
    37
	unsigned int max_edges;
dvander@2320
    38
	unsigned int used_blocks;
dvander@2320
    39
	unsigned int used_edges;
dvander@2320
    40
	Block *blocks;
dvander@2320
    41
	Edge *edges;
dvander@2320
    42
	Block *entry;
dvander@2320
    43
	FunctionInfo *func;
dvander@2320
    44
};
dvander@2320
    45
dvander@2322
    46
DefineNode::DefineNode(BaseNode *o, const char *fmt, ...) : BaseNode(_OP_DEFINE), other(o)
dvander@2322
    47
{
dvander@2322
    48
	va_list ap;
dvander@2322
    49
	
dvander@2322
    50
	va_start(ap, fmt);
dvander@2322
    51
	Sp_FormatArgs(name, sizeof(name), fmt, ap);
dvander@2322
    52
	va_end(ap);
dvander@2322
    53
}
dvander@2322
    54
dvander@2320
    55
static FunctionInfo *sp_InferFuncPrototype(sp_decomp_t *dc, uint32_t code_addr, bool is_public)
dvander@2320
    56
{
dvander@2320
    57
	sp_tag_t *pTag;
dvander@2320
    58
	sp_file_t *plugin;
dvander@2320
    59
	FunctionInfo *info;
dvander@2320
    60
	sp_fdbg_symbol_t *sym;
dvander@2320
    61
dvander@2320
    62
	plugin = dc->plugin;
dvander@2320
    63
	info = new FunctionInfo;
dvander@2320
    64
	memset(info, 0, sizeof(FunctionInfo));
dvander@2320
    65
dvander@2320
    66
	info->code_addr = code_addr;
dvander@2320
    67
	
dvander@2320
    68
	if ((sym = Sp_FindFunctionSym(plugin, code_addr)) != NULL)
dvander@2320
    69
	{
dvander@2320
    70
		Sp_Format(info->name, sizeof(info->name), "%s", plugin->debug.stringbase + sym->name);
dvander@2320
    71
		if (sym->tagid != 0)
dvander@2320
    72
		{
dvander@2320
    73
			info->tag = Sp_FindTag(plugin, sym->tagid);
dvander@2320
    74
		}
dvander@2320
    75
	}
dvander@2320
    76
	else
dvander@2320
    77
	{
dvander@2320
    78
		Sp_Format(info->name, sizeof(info->name), "function_%x", code_addr);
dvander@2320
    79
		for (uint32_t i = 0; i < plugin->num_publics; i++)
dvander@2320
    80
		{
dvander@2320
    81
			if (plugin->publics[i].code_offs == code_addr)
dvander@2320
    82
			{
dvander@2320
    83
				Sp_Format(info->name,
dvander@2320
    84
					sizeof(info->name),
dvander@2320
    85
					"%s",
dvander@2320
    86
					plugin->publics[i].name);
dvander@2320
    87
				break;
dvander@2320
    88
			}
dvander@2320
    89
		}
dvander@2320
    90
	}
dvander@2320
    91
	info->sym = sym;
dvander@2320
    92
	info->is_public = is_public;
dvander@2320
    93
	
dvander@2320
    94
dvander@2320
    95
	/* Search for all locals */
dvander@2320
    96
	{
dvander@2320
    97
		uint32_t i;
dvander@2320
    98
		uint8_t *cursor;
dvander@2320
    99
dvander@2320
   100
		cursor = (uint8_t *)plugin->debug.symbols;
dvander@2320
   101
		for (i = 0; i < plugin->debug.syms_num; i++)
dvander@2320
   102
		{
dvander@2320
   103
			sym = (sp_fdbg_symbol_t *)cursor;
dvander@2320
   104
dvander@2320
   105
			if (sym->ident == SP_SYM_VARIABLE
dvander@2320
   106
				|| sym->ident == SP_SYM_REFARRAY
dvander@2320
   107
				|| sym->ident == SP_SYM_REFERENCE
dvander@2320
   108
				|| sym->ident == SP_SYM_ARRAY)
dvander@2320
   109
			{
dvander@2320
   110
				if (code_addr >= sym->codestart
dvander@2320
   111
					&& code_addr < sym->codeend
dvander@2320
   112
					&& sym->addr >= 0xC
dvander@2320
   113
					&& (sym->addr & 0x3) == 0
dvander@2320
   114
					&& ((sym->addr - 0xC) >> 2) < SP_MAX_EXEC_PARAMS
dvander@2320
   115
					&& sym->vclass == 1)
dvander@2320
   116
				{
dvander@2320
   117
					unsigned int num;
dvander@2320
   118
dvander@2320
   119
					num = (sym->addr - 0xC) >> 2;
dvander@2320
   120
					info->known_args[num].sym = sym;
dvander@2320
   121
					if (num + 1 > info->num_known_args)
dvander@2320
   122
					{
dvander@2320
   123
						info->num_known_args = num + 1;
dvander@2320
   124
					}
dvander@2320
   125
				}
dvander@2320
   126
			}
dvander@2320
   127
dvander@2320
   128
			cursor += sizeof(sp_fdbg_symbol_t);
dvander@2320
   129
			if (sym->dimcount > 0)
dvander@2320
   130
			{
dvander@2320
   131
				cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount;
dvander@2320
   132
			}
dvander@2320
   133
		}
dvander@2320
   134
	}
dvander@2320
   135
dvander@2320
   136
	/* Map all arguments into place. */
dvander@2320
   137
	for (unsigned int i = 0; i < info->num_known_args; i++)
dvander@2320
   138
	{
dvander@2320
   139
		if ((sym = info->known_args[i].sym) == NULL)
dvander@2320
   140
		{
dvander@2320
   141
			continue;
dvander@2320
   142
		}
dvander@2320
   143
		if (sym->tagid != 0 && (pTag = Sp_FindTag(plugin, sym->tagid)) != NULL)
dvander@2320
   144
		{
dvander@2320
   145
			info->known_args[i].tag = pTag;
dvander@2320
   146
		}
dvander@2320
   147
		info->known_args[i].name = plugin->debug.stringbase + sym->name;
dvander@2320
   148
		
dvander@2320
   149
		if (sym->dimcount > 0)
dvander@2320
   150
		{
dvander@2320
   151
			info->known_args[i].dims = 
dvander@2320
   152
				(sp_fdbg_arraydim_t *)((char *)sym + sizeof(sp_fdbg_symbol_t));
dvander@2320
   153
		}
dvander@2320
   154
	}
dvander@2320
   155
dvander@2320
   156
	return info;
dvander@2320
   157
}
dvander@2320
   158
dvander@2320
   159
static FunctionInfo *sp_GetFuncPrototype(sp_decomp_t *dc,
dvander@2320
   160
										 uint32_t code_addr,
dvander@2320
   161
										 bool known,
dvander@2320
   162
										 bool is_public = false);
dvander@2320
   163
static FunctionInfo *sp_GetFuncPrototype(sp_decomp_t *dc,
dvander@2320
   164
										 uint32_t code_addr,
dvander@2320
   165
										 bool known,
dvander@2320
   166
										 bool is_public)
dvander@2320
   167
{
dvander@2320
   168
	FunctionInfo *func;
dvander@2320
   169
dvander@2320
   170
	func = dc->funcs;
dvander@2320
   171
	while (func != NULL)
dvander@2320
   172
	{
dvander@2320
   173
		if (func->code_addr == code_addr)
dvander@2320
   174
		{
dvander@2320
   175
			return func;
dvander@2320
   176
		}
dvander@2320
   177
		func = func->next;
dvander@2320
   178
	}
dvander@2320
   179
dvander@2320
   180
	/* See if this is public. */
dvander@2320
   181
	if (!known)
dvander@2320
   182
	{
dvander@2320
   183
		is_public = false;
dvander@2320
   184
		for (unsigned int i = 0; i < dc->plugin->num_publics; i++)
dvander@2320
   185
		{
dvander@2320
   186
			if (dc->plugin->publics[i].code_offs == code_addr)
dvander@2320
   187
			{
dvander@2320
   188
				is_public = true;
dvander@2320
   189
				break;
dvander@2320
   190
			}
dvander@2320
   191
		}
dvander@2320
   192
	}
dvander@2320
   193
dvander@2320
   194
	func = sp_InferFuncPrototype(dc, code_addr, is_public);
dvander@2320
   195
	func->next = dc->funcs;
dvander@2320
   196
	dc->funcs = func;
dvander@2320
   197
dvander@2320
   198
	return func;
dvander@2320
   199
}
dvander@2320
   200
dvander@2320
   201
static void sp_CacheLocals(sp_decomp_t *dc, FunctionInfo *info)
dvander@2320
   202
{
dvander@2320
   203
	sp_file_t *plugin;
dvander@2320
   204
	sp_fdbg_symbol_t *sym;
dvander@2320
   205
dvander@2320
   206
	plugin = dc->plugin;
dvander@2320
   207
dvander@2320
   208
	/* Search for all locals */
dvander@2320
   209
	{
dvander@2320
   210
		uint32_t i;
dvander@2320
   211
		uint8_t *cursor;
dvander@2320
   212
dvander@2320
   213
		cursor = (uint8_t *)plugin->debug.symbols;
dvander@2320
   214
		for (i = 0; i < plugin->debug.syms_num; i++)
dvander@2320
   215
		{
dvander@2320
   216
			sym = (sp_fdbg_symbol_t *)cursor;
dvander@2320
   217
dvander@2320
   218
			if ((sym->codestart >= info->code_addr && sym->codestart < info->code_end)
dvander@2320
   219
				&& (sym->codeend <= info->code_end && sym->codeend >= info->code_addr)
dvander@2320
   220
				&& sym->vclass == 1)
dvander@2320
   221
			{
dvander@2320
   222
				info->num_known_vars++;
dvander@2320
   223
			}
dvander@2320
   224
dvander@2320
   225
			cursor += sizeof(sp_fdbg_symbol_t);
dvander@2320
   226
			if (sym->dimcount > 0)
dvander@2320
   227
			{
dvander@2320
   228
				cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount;
dvander@2320
   229
			}
dvander@2320
   230
		}
dvander@2320
   231
	}
dvander@2320
   232
dvander@2320
   233
	/* Map all locals into place. */
dvander@2320
   234
	if (info->num_known_vars)
dvander@2320
   235
	{
dvander@2320
   236
		uint32_t i, num;
dvander@2320
   237
		uint8_t *cursor;
dvander@2320
   238
dvander@2320
   239
		num = 0;
dvander@2320
   240
		info->known_vars = new FuncVar[info->num_known_vars];
dvander@2320
   241
		memset(info->known_vars, 0, sizeof(FuncVar) * info->num_known_vars);
dvander@2320
   242
dvander@2320
   243
		cursor = (uint8_t *)plugin->debug.symbols;
dvander@2320
   244
		for (i = 0; i < plugin->debug.syms_num; i++)
dvander@2320
   245
		{
dvander@2320
   246
			sym = (sp_fdbg_symbol_t *)cursor;
dvander@2320
   247
dvander@2320
   248
			if (sym->ident == SP_SYM_VARIABLE
dvander@2320
   249
				|| sym->ident == SP_SYM_REFARRAY
dvander@2320
   250
				|| sym->ident == SP_SYM_REFERENCE
dvander@2320
   251
				|| sym->ident == SP_SYM_ARRAY)
dvander@2320
   252
			{
dvander@2320
   253
				/* Detect all function variables. */
dvander@2320
   254
				if ((sym->codestart >= info->code_addr && sym->codestart < info->code_end)
dvander@2320
   255
					&& (sym->codeend <= info->code_end && sym->codeend >= info->code_addr)
dvander@2320
   256
					&& sym->vclass == 1)
dvander@2320
   257
				{
dvander@2320
   258
					assert(num < info->num_known_vars);
dvander@2320
   259
					info->known_vars[num].sym = sym;
dvander@2320
   260
					info->known_vars[num].name = plugin->debug.stringbase + sym->name;
dvander@2320
   261
					info->known_vars[num].tag = 
dvander@2320
   262
						(sym->tagid == 0) ? NULL : Sp_FindTag(plugin, sym->tagid);
dvander@2320
   263
					info->known_vars[num].dims = 
dvander@2320
   264
						(sym->dimcount == 0)
dvander@2320
   265
						? NULL
dvander@2320
   266
						: (sp_fdbg_arraydim_t *)(cursor + sizeof(sp_fdbg_symbol_t));
dvander@2320
   267
					num++;
dvander@2320
   268
				}
dvander@2320
   269
			}
dvander@2320
   270
dvander@2320
   271
			cursor += sizeof(sp_fdbg_symbol_t);
dvander@2320
   272
			if (sym->dimcount > 0)
dvander@2320
   273
			{
dvander@2320
   274
				cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount;
dvander@2320
   275
			}
dvander@2320
   276
		}
dvander@2320
   277
dvander@2320
   278
		assert(num == info->num_known_vars);
dvander@2320
   279
	}
dvander@2320
   280
}
dvander@2320
   281
dvander@2320
   282
static void sp_PrintFunctionPrototype(PrintBuffer *printer, sp_decomp_t *dc, FunctionInfo *func)
dvander@2320
   283
{
dvander@2320
   284
	FuncVar *var;
dvander@2320
   285
	
dvander@2320
   286
	if (func->is_public)
dvander@2320
   287
	{
dvander@2320
   288
		printer->Append("public ");
dvander@2320
   289
	}
dvander@2320
   290
	if (func->tag != NULL)
dvander@2320
   291
	{
dvander@2320
   292
		printer->Append("%s:", func->tag->name);
dvander@2320
   293
	}
dvander@2320
   294
	printer->Append("%s(", func->name);
dvander@2320
   295
dvander@2320
   296
	for (unsigned int i = 0; i < func->num_known_args; i++)
dvander@2320
   297
	{
dvander@2320
   298
		var = &func->known_args[i];
dvander@2320
   299
		if (var->sym == NULL)
dvander@2320
   300
		{
dvander@2320
   301
			printer->Append("unknown_arg_%d%s, ", i, i == func->num_known_args - 1 ? "" : ",");
dvander@2320
   302
			continue;
dvander@2320
   303
		}
dvander@2320
   304
		if (var->sym->ident == SP_SYM_REFERENCE)
dvander@2320
   305
		{
dvander@2320
   306
			printer->Append("&");
dvander@2320
   307
		}
dvander@2320
   308
		if (var->tag != NULL)
dvander@2320
   309
		{
dvander@2320
   310
			printer->Append("%s:", var->tag->name);
dvander@2320
   311
		}
dvander@2320
   312
		printer->Append("%s", var->name);
dvander@2320
   313
dvander@2320
   314
		if (var->sym->dimcount > 0)
dvander@2320
   315
		{
dvander@2320
   316
			sp_fdbg_arraydim_t *dims = 
dvander@2320
   317
				(sp_fdbg_arraydim_t *)((char *)var->sym + sizeof(sp_fdbg_symbol_t));
dvander@2320
   318
dvander@2320
   319
			for (uint16_t j = 0; j < var->sym->dimcount; j++)
dvander@2320
   320
			{
dvander@2320
   321
				if (dims[j].size == 0)
dvander@2320
   322
				{
dvander@2320
   323
					printer->Append("[]");
dvander@2320
   324
				}
dvander@2320
   325
				else
dvander@2320
   326
				{
dvander@2320
   327
					printer->Append("[%d]", dims[j].size);
dvander@2320
   328
				}
dvander@2320
   329
			}
dvander@2320
   330
		}
dvander@2320
   331
dvander@2320
   332
		if (i != func->num_known_args - 1)
dvander@2320
   333
		{
dvander@2320
   334
			printer->Append(", ");
dvander@2320
   335
		}
dvander@2320
   336
	}
dvander@2320
   337
dvander@2320
   338
	printer->Append(")");
dvander@2320
   339
}
dvander@2320
   340
dvander@2320
   341
void sp_DebugExprTree(PrintBuffer *buffer, BaseNode *node, bool is_stmt=false)
dvander@2320
   342
{
dvander@2320
   343
	switch (node->op)
dvander@2320
   344
	{
dvander@2322
   345
	case _OP_DEFINE:
dvander@2322
   346
		{
dvander@2322
   347
			DefineNode *def = (DefineNode *)node;
dvander@2322
   348
dvander@2322
   349
			buffer->Append("new %s = ", def->name);
dvander@2322
   350
			sp_DebugExprTree(buffer, def->other, is_stmt);
dvander@2322
   351
			break;
dvander@2322
   352
		}
dvander@2320
   353
	case OP_CALL:
dvander@2320
   354
		{
dvander@2320
   355
			CallNode *call = (CallNode *)node;
dvander@2320
   356
dvander@2320
   357
			buffer->Append("%s(", call->func->name);
dvander@2320
   358
			for (unsigned int i = 0; i < call->argc; i++)
dvander@2320
   359
			{
dvander@2320
   360
				sp_DebugExprTree(buffer, call->args[i], false);
dvander@2320
   361
			}
dvander@2320
   362
			buffer->Append(")");
dvander@2320
   363
			break;
dvander@2320
   364
		}
dvander@2322
   365
	case _OP_USE:
dvander@2322
   366
		{
dvander@2322
   367
			UseNode *unode = (UseNode *)node;
dvander@2322
   368
dvander@2322
   369
			buffer->Append("%s", unode->def->name);
dvander@2322
   370
			break;
dvander@2322
   371
		}
dvander@2320
   372
	case _OP_VAR:
dvander@2320
   373
		{
dvander@2320
   374
			VarNode *var_node = (VarNode *)node;
dvander@2320
   375
			buffer->Append("%s", var_node->var->name);
dvander@2320
   376
			break;
dvander@2320
   377
		}
dvander@2320
   378
	case _OP_NEW:
dvander@2320
   379
		{
dvander@2320
   380
			DeclNode *d_node = (DeclNode *)node;
dvander@2320
   381
dvander@2320
   382
			if (d_node->expr == NULL)
dvander@2320
   383
			{
dvander@2320
   384
				buffer->Append("decl ");
dvander@2320
   385
				sp_DebugExprTree(buffer, d_node->var, true);
dvander@2320
   386
			}
dvander@2320
   387
			else
dvander@2320
   388
			{
dvander@2320
   389
				buffer->Append("new ");
dvander@2320
   390
				sp_DebugExprTree(buffer, d_node->var, true);
dvander@2320
   391
				buffer->Append(" = ");
dvander@2320
   392
				sp_DebugExprTree(buffer, d_node->expr, true);
dvander@2320
   393
			}
dvander@2320
   394
dvander@2320
   395
			break;
dvander@2320
   396
		}
dvander@2320
   397
	case _OP_STMT:
dvander@2320
   398
	case OP_RETN:
dvander@2320
   399
		{
dvander@2320
   400
			StmtNode *u_node = (StmtNode *)node;
dvander@2320
   401
dvander@2320
   402
			while (u_node != NULL)
dvander@2320
   403
			{
dvander@2320
   404
				buffer->Append("\t");
dvander@2320
   405
				if (u_node->op == OP_RETN)
dvander@2320
   406
				{
dvander@2320
   407
					buffer->Append("return ");
dvander@2320
   408
				}
dvander@2320
   409
				sp_DebugExprTree(buffer, u_node->node, true);
dvander@2320
   410
				buffer->Append(";\n");
dvander@2320
   411
				u_node = u_node->next;
dvander@2320
   412
			}
dvander@2320
   413
			break;
dvander@2320
   414
		}
dvander@2320
   415
	case _OP_CONST:
dvander@2320
   416
		{
dvander@2320
   417
			ConstNode *c_node = (ConstNode *)node;
dvander@2320
   418
dvander@2320
   419
			buffer->Append("%d", c_node->val);
dvander@2320
   420
dvander@2320
   421
			break;
dvander@2320
   422
		}
dvander@2320
   423
	case OP_ADD:
dvander@2320
   424
	case OP_ADD_C:
dvander@2320
   425
	case OP_SMUL:
dvander@2320
   426
	case OP_SMUL_C:
dvander@2320
   427
	case OP_SDIV:
dvander@2320
   428
	case OP_SUB_ALT:
dvander@2320
   429
	case _OP_STORE:
dvander@2320
   430
	case _OP_MODULO:
dvander@2320
   431
		{
dvander@2320
   432
			BinOpNode *bin_node = (BinOpNode *)node;
dvander@2320
   433
dvander@2320
   434
			if (!is_stmt)
dvander@2320
   435
			{
dvander@2320
   436
				buffer->Append("(");
dvander@2320
   437
			}
dvander@2320
   438
			sp_DebugExprTree(buffer, bin_node->left);
dvander@2320
   439
dvander@2320
   440
			switch (node->op)
dvander@2320
   441
			{
dvander@2320
   442
			case OP_ADD:
dvander@2320
   443
			case OP_ADD_C:
dvander@2320
   444
				{
dvander@2320
   445
					buffer->Append(" + ");
dvander@2320
   446
					break;
dvander@2320
   447
				}
dvander@2320
   448
			case OP_SMUL:
dvander@2320
   449
			case OP_SMUL_C:
dvander@2320
   450
				{
dvander@2320
   451
					buffer->Append(" * ");
dvander@2320
   452
					break;
dvander@2320
   453
				}
dvander@2320
   454
			case _OP_STORE:
dvander@2320
   455
				{
dvander@2320
   456
					buffer->Append(" = ");
dvander@2320
   457
					break;
dvander@2320
   458
				}
dvander@2320
   459
			case OP_SUB_ALT:
dvander@2320
   460
				{
dvander@2320
   461
					buffer->Append(" - ");
dvander@2320
   462
					break;
dvander@2320
   463
				}
dvander@2320
   464
			case OP_SDIV:
dvander@2320
   465
				{
dvander@2320
   466
					buffer->Append(" / ");
dvander@2320
   467
					break;
dvander@2320
   468
				}
dvander@2320
   469
			case _OP_MODULO:
dvander@2320
   470
				{
dvander@2320
   471
					buffer->Append(" % ");
dvander@2320
   472
					break;
dvander@2320
   473
				}
dvander@2320
   474
			}
dvander@2320
   475
dvander@2320
   476
			sp_DebugExprTree(buffer, bin_node->right, node->op == _OP_STORE);
dvander@2320
   477
			if (!is_stmt)
dvander@2320
   478
			{
dvander@2320
   479
				buffer->Append(")");
dvander@2320
   480
			}
dvander@2320
   481
dvander@2320
   482
			break;
dvander@2320
   483
		}
dvander@2320
   484
	}
dvander@2320
   485
}
dvander@2320
   486
dvander@2320
   487
void sp_DebugPrint(FunctionInfo *info, sp_decomp_t *dc, BaseNode *node)
dvander@2320
   488
{
dvander@2320
   489
	PrintBuffer buffer;
dvander@2320
   490
dvander@2320
   491
	sp_PrintFunctionPrototype(&buffer, dc, info);
dvander@2320
   492
	buffer.Append("\n{\n");
dvander@2320
   493
	sp_DebugExprTree(&buffer, node, true);
dvander@2320
   494
	buffer.Append("}\n");
dvander@2320
   495
	buffer.Dump(stdout);
dvander@2320
   496
}
dvander@2320
   497
dvander@2320
   498
#define PCODE_VAL(pcode, offs)			*(cell_t *)((char *)(pcode)+(offs))
dvander@2320
   499
#define PCODE_PARAM(pcode, offs, num)	*(cell_t *)((char *)(pcode)+(offs)+((num)*sizeof(cell_t)))
dvander@2320
   500
dvander@2320
   501
FuncVar *sp_FindGraphVar(FunctionInfo *func, ucell_t cip, cell_t sp, cell_t stack)
dvander@2320
   502
{
dvander@2320
   503
	if (stack >= 0xC)
dvander@2320
   504
	{
dvander@2320
   505
		if ((stack & 0x3) != 0)
dvander@2320
   506
		{
dvander@2320
   507
			return NULL;
dvander@2320
   508
		}
dvander@2320
   509
		stack -= 0xC;
dvander@2320
   510
		stack >>= 2;
dvander@2320
   511
		if (stack >= SP_MAX_EXEC_PARAMS)
dvander@2320
   512
		{
dvander@2320
   513
			return NULL;
dvander@2320
   514
		}
dvander@2320
   515
		if (func->known_args[stack].sym == NULL)
dvander@2320
   516
		{
dvander@2320
   517
			return NULL;
dvander@2320
   518
		}
dvander@2320
   519
		return &func->known_args[stack];
dvander@2320
   520
	}
dvander@2320
   521
	else if (stack < 0)
dvander@2320
   522
	{
dvander@2320
   523
		FuncVar *var;
dvander@2320
   524
dvander@2320
   525
		if (stack < sp)
dvander@2320
   526
		{
dvander@2320
   527
			return NULL;
dvander@2320
   528
		}
dvander@2320
   529
dvander@2320
   530
		for (unsigned int i = 0; i < func->num_known_vars; i++)
dvander@2320
   531
		{
dvander@2320
   532
			var = &func->known_vars[i];
dvander@2320
   533
dvander@2320
   534
			if (cip >= var->sym->codestart
dvander@2320
   535
				&& cip <= var->sym->codeend
dvander@2320
   536
				&& stack == var->sym->addr)
dvander@2320
   537
			{
dvander@2320
   538
				return var;
dvander@2320
   539
			}
dvander@2320
   540
		}
dvander@2320
   541
dvander@2320
   542
		return NULL;
dvander@2320
   543
	}
dvander@2320
   544
	else
dvander@2320
   545
	{
dvander@2320
   546
		return NULL;
dvander@2320
   547
	}
dvander@2320
   548
}
dvander@2320
   549
dvander@2320
   550
#define CHECK_VALUE(v)							\
dvander@2320
   551
	assert(v != NULL);							\
dvander@2320
   552
	if (v == NULL) {							\
dvander@2320
   553
		err = SP_ERROR_INVALID_INSTRUCTION;		\
dvander@2320
   554
		goto return_error;						\
dvander@2320
   555
	}
dvander@2320
   556
dvander@2320
   557
void *operator new(size_t size, ControlFlowGraph *graph)
dvander@2320
   558
{
dvander@2320
   559
	return malloc(size);
dvander@2320
   560
}
dvander@2320
   561
dvander@2320
   562
void *operator new[](size_t size, ControlFlowGraph *graph)
dvander@2320
   563
{
dvander@2320
   564
	return malloc(size);
dvander@2320
   565
}
dvander@2320
   566
dvander@2320
   567
void operator delete(void *mem, ControlFlowGraph *graph)
dvander@2320
   568
{
dvander@2320
   569
	free(mem);
dvander@2320
   570
}
dvander@2320
   571
dvander@2320
   572
void operator delete[](void *mem, ControlFlowGraph *graph)
dvander@2320
   573
{
dvander@2320
   574
	free(mem);
dvander@2320
   575
}
dvander@2320
   576
dvander@2320
   577
#define MAX_STACK_ENTRIES		256
dvander@2320
   578
dvander@2320
   579
struct StackEntry
dvander@2320
   580
{
dvander@2320
   581
	StackEntry()
dvander@2320
   582
	{
dvander@2320
   583
	}
dvander@2320
   584
	StackEntry(cell_t d, BaseNode *e) : depth(d), expr(e)
dvander@2320
   585
	{
dvander@2320
   586
	}
dvander@2320
   587
	cell_t depth;
dvander@2320
   588
	BaseNode *expr;
dvander@2320
   589
};
dvander@2320
   590
dvander@2320
   591
int sp_AnalyzeGraph(sp_decomp_t *dc, ControlFlowGraph *graph)
dvander@2320
   592
{
dvander@2320
   593
	int err;
dvander@2320
   594
	cell_t sp;
dvander@2320
   595
	Block *block;
dvander@2320
   596
	cell_t *pcode;
dvander@2320
   597
	cell_t cip, cip_end;
dvander@2320
   598
	StmtNode *root, *rtemp;
dvander@2320
   599
	StmtNode *root_tail;
dvander@2320
   600
	BaseNode *pri, *alt;
dvander@2320
   601
	FunctionInfo *func, *tfunc;
dvander@2320
   602
	cell_t p1, p2, op;
dvander@2320
   603
	FuncVar *v1, *v2;
dvander@2322
   604
	unsigned int callnumber = 0;
dvander@2320
   605
	unsigned int stack_entries = 0;
dvander@2320
   606
	StackEntry eval_stack[MAX_STACK_ENTRIES];
dvander@2320
   607
dvander@2320
   608
	/* Init everything.
dvander@2320
   609
	 * Stack pointer starts off at 0.
dvander@2320
   610
	 */
dvander@2320
   611
	sp = 0;
dvander@2320
   612
	pri = NULL;
dvander@2320
   613
	alt = NULL;
dvander@2320
   614
	root = NULL;
dvander@2320
   615
	func = graph->func;
dvander@2320
   616
	block = graph->entry;
dvander@2320
   617
	pcode = (cell_t *)dc->plugin->pcode;
dvander@2320
   618
dvander@2320
   619
	cip = block->ip_start;
dvander@2320
   620
	cip_end = block->ip_end;
dvander@2320
   621
dvander@2320
   622
	root_tail = root = new (graph) StmtNode(_OP_STMT, NULL, NULL);
dvander@2320
   623
dvander@2320
   624
	while (cip < cip_end)
dvander@2320
   625
	{
dvander@2320
   626
		op = PCODE_VAL(pcode, cip);
dvander@2320
   627
dvander@2320
   628
		switch (op)
dvander@2320
   629
		{
dvander@2320
   630
		case OP_PROC:
dvander@2320
   631
		case OP_BREAK:
dvander@2320
   632
			{
dvander@2320
   633
				break;
dvander@2320
   634
			}
dvander@2320
   635
		case OP_CALL:
dvander@2320
   636
			{
dvander@2320
   637
				p1 = PCODE_PARAM(pcode, cip, 1);
dvander@2320
   638
				tfunc = sp_GetFuncPrototype(dc, p1, false);
dvander@2320
   639
				CHECK_VALUE(tfunc);
dvander@2320
   640
dvander@2320
   641
				/* Sanity checks, :TODO: runtime checks later. */
dvander@2320
   642
				assert(stack_entries >= 1);
dvander@2320
   643
dvander@2320
   644
				ConstNode *cnode;
dvander@2320
   645
				cnode = (ConstNode *)eval_stack[stack_entries-1].expr;
dvander@2320
   646
				stack_entries--;
dvander@2320
   647
dvander@2320
   648
				/* Sanity checks, :TODO: runtime checks later. */
dvander@2320
   649
				assert(cnode->op == _OP_CONST);
dvander@2320
   650
				assert(cnode->val >= 0);
dvander@2320
   651
				assert(cnode->val == tfunc->num_known_args);
dvander@2320
   652
				assert(unsigned int(cnode->val) <= stack_entries);
dvander@2320
   653
				
dvander@2320
   654
				cell_t argc = cnode->val;
dvander@2320
   655
				BaseNode **nodes = new (graph) BaseNode *[cnode->val];
dvander@2320
   656
				for (cell_t argno = 0; argno < cnode->val; argno++, stack_entries--)
dvander@2320
   657
				{
dvander@2320
   658
					nodes[argno] = eval_stack[stack_entries-1].expr;
dvander@2320
   659
				}
dvander@2320
   660
dvander@2320
   661
				sp += (cnode->val + 1) * sizeof(cell_t);
dvander@2320
   662
				assert(sp <= 0);
dvander@2320
   663
dvander@2320
   664
				pri = new (graph) CallNode(tfunc, nodes, cnode->val);
dvander@2322
   665
				pri = new (graph) DefineNode(pri, "call%03x", ++callnumber);
dvander@2322
   666
				rtemp = new (graph) StmtNode(_OP_STMT, pri, NULL);
dvander@2322
   667
				root_tail->next = rtemp;
dvander@2322
   668
				root_tail = rtemp;
dvander@2322
   669
				pri = new (graph) UseNode((DefineNode *)pri);
dvander@2320
   670
				
dvander@2320
   671
				break;
dvander@2320
   672
			}
dvander@2320
   673
		case OP_CONST_PRI:
dvander@2320
   674
			{
dvander@2320
   675
				p1 = PCODE_PARAM(pcode, cip, 1);
dvander@2320
   676
				pri = new (graph) ConstNode(p1);
dvander@2320
   677
				break;
dvander@2320
   678
			}
dvander@2320
   679
		case OP_PUSH_C:
dvander@2320
   680
			{
dvander@2320
   681
				p1 = PCODE_PARAM(pcode, cip, 1);
dvander@2320
   682
dvander@2320
   683
				/* Adjust stack pointer. */
dvander@2320
   684
				sp -= sizeof(cell_t);
dvander@2320
   685
dvander@2320
   686
				if ((v1 = sp_FindGraphVar(func, cip, sp, sp)) == NULL)
dvander@2320
   687
				{
dvander@2320
   688
					/* Add a new stack entry. */
dvander@2320
   689
					assert(stack_entries < MAX_STACK_ENTRIES);
dvander@2320
   690
					eval_stack[stack_entries++] = StackEntry(sp, new ConstNode(p1));
dvander@2320
   691
				}
dvander@2320
   692
				else
dvander@2320
   693
				{
dvander@2320
   694
					assert(v1->new_stmt == NULL);
dvander@2320
   695
					if (v1->new_stmt == NULL)
dvander@2320
   696
					{
dvander@2320
   697
						v1->new_stmt = new (graph) DeclNode(
dvander@2320
   698
							new (graph) VarNode(v1),
dvander@2320
   699
							new (graph) ConstNode(p1));
dvander@2320
   700
						rtemp = new (graph) StmtNode(
dvander@2320
   701
							_OP_STMT,
dvander@2320
   702
							v1->new_stmt,
dvander@2320
   703
							NULL);
dvander@2320
   704
						root_tail->next = rtemp;
dvander@2320
   705
						root_tail = rtemp;
dvander@2320
   706
					}
dvander@2320
   707
				}
dvander@2320
   708
				break;
dvander@2320
   709
			}
dvander@2320
   710
		case OP_PUSH_S:
dvander@2320
   711
			{
dvander@2320
   712
				p1 = PCODE_PARAM(pcode, cip, 1);
dvander@2320
   713
				v1 = sp_FindGraphVar(func, cip, sp, p1);
dvander@2320
   714
				CHECK_VALUE(v1);
dvander@2320
   715
dvander@2320
   716
				/* Adjust stack pointer. */
dvander@2320
   717
				sp -= sizeof(cell_t);
dvander@2320
   718
dvander@2320
   719
				/* Right now, don't allow aliasing or overflows. */
dvander@2320
   720
				assert(sp_FindGraphVar(func, cip, sp, sp) == NULL);
dvander@2320
   721
				assert(stack_entries < MAX_STACK_ENTRIES);
dvander@2320
   722
dvander@2320
   723
				/* Add a new stack entry. */
dvander@2320
   724
				eval_stack[stack_entries++] = StackEntry(sp, new (graph) VarNode(v1));
dvander@2320
   725
				break;
dvander@2320
   726
			}
dvander@2320
   727
		case OP_PUSH_PRI:
dvander@2320
   728
			{
dvander@2320
   729
				CHECK_VALUE(pri);
dvander@2320
   730
dvander@2320
   731
				/* Adjust stack pointer. */
dvander@2320
   732
				sp -= sizeof(cell_t);
dvander@2320
   733
				
dvander@2320
   734
				/* Right now, don't allow aliasing or overflows. */
dvander@2320
   735
				assert(sp_FindGraphVar(func, cip, sp, sp) == NULL);
dvander@2320
   736
				assert(stack_entries < MAX_STACK_ENTRIES);
dvander@2320
   737
dvander@2320
   738
				/* Add a new stack entry. */
dvander@2320
   739
				eval_stack[stack_entries++] = StackEntry(sp, pri);
dvander@2320
   740
				break;
dvander@2320
   741
			}
dvander@2320
   742
		case OP_POP_ALT:
dvander@2320
   743
			{
dvander@2320
   744
				assert(stack_entries > 0);
dvander@2320
   745
				assert(eval_stack[stack_entries - 1].depth == sp);
dvander@2320
   746
dvander@2320
   747
				alt = eval_stack[--stack_entries].expr;
dvander@2320
   748
				sp += sizeof(cell_t);
dvander@2320
   749
				assert(sp <= 0);
dvander@2320
   750
				break;
dvander@2320
   751
			}
dvander@2320
   752
		case OP_STACK:
dvander@2320
   753
			{
dvander@2320
   754
				p1 = PCODE_PARAM(pcode, cip, 1);
dvander@2320
   755
				sp += p1;
dvander@2320
   756
				assert(sp <= 0);
dvander@2320
   757
				if (p1 < 0)
dvander@2320
   758
				{
dvander@2320
   759
					/* Find if we should track a new variable declaration. */
dvander@2320
   760
					v1 = sp_FindGraphVar(func, cip, sp, sp);
dvander@2320
   761
					CHECK_VALUE(v1);
dvander@2320
   762
					assert(v1->new_stmt == NULL);
dvander@2320
   763
					if (v1->new_stmt == NULL)
dvander@2320
   764
					{
dvander@2320
   765
						v1->new_stmt = new (graph) DeclNode(
dvander@2320
   766
							new (graph) VarNode(v1),
dvander@2320
   767
							NULL);
dvander@2320
   768
						rtemp = new (graph) StmtNode(
dvander@2320
   769
							_OP_STMT,
dvander@2320
   770
							v1->new_stmt,
dvander@2320
   771
							NULL);
dvander@2320
   772
						root_tail->next = rtemp;
dvander@2320
   773
						root_tail = rtemp;
dvander@2320
   774
					}
dvander@2320
   775
					assert(abs(p1) >> 2 == 1);
dvander@2320
   776
				}
dvander@2320
   777
				else
dvander@2320
   778
				{
dvander@2320
   779
					/* Erase anything left over on the evaluation stack. */
dvander@2320
   780
					while (stack_entries > 0
dvander@2320
   781
						   && eval_stack[stack_entries - 1].depth < sp)
dvander@2320
   782
					{
dvander@2320
   783
						assert(0);
dvander@2320
   784
						stack_entries--;
dvander@2320
   785
					}
dvander@2320
   786
				}
dvander@2320
   787
				break;
dvander@2320
   788
			}
dvander@2320
   789
		case OP_ZERO_PRI:
dvander@2320
   790
			{
dvander@2320
   791
				pri = new (graph) ConstNode(0);
dvander@2320
   792
				break;
dvander@2320
   793
			}
dvander@2320
   794
		case OP_MOVE_ALT:
dvander@2320
   795
			{
dvander@2320
   796
				CHECK_VALUE(pri);
dvander@2320
   797
				alt = pri;
dvander@2320
   798
				break;
dvander@2320
   799
			}
dvander@2320
   800
		case OP_LOAD_S_PRI:
dvander@2320
   801
			{
dvander@2320
   802
				p1 = PCODE_PARAM(pcode, cip, 1);
dvander@2320
   803
				v1 = sp_FindGraphVar(func, cip, sp, p1);
dvander@2320
   804
				CHECK_VALUE(v1);
dvander@2320
   805
				pri = new (graph) VarNode(v1);
dvander@2320
   806
				break;
dvander@2320
   807
			}
dvander@2320
   808
		case OP_LOAD_S_ALT:
dvander@2320
   809
			{
dvander@2320
   810
				p1 = PCODE_PARAM(pcode, cip, 1);
dvander@2320
   811
				v1 = sp_FindGraphVar(func, cip, sp, p1);
dvander@2320
   812
				CHECK_VALUE(v1);
dvander@2320
   813
				alt = new (graph) VarNode(v1);
dvander@2320
   814
				break;
dvander@2320
   815
			}
dvander@2320
   816
		case OP_STOR_S_PRI:
dvander@2320
   817
			{
dvander@2320
   818
				p1 = PCODE_PARAM(pcode, cip, 1);
dvander@2320
   819
				v1 = sp_FindGraphVar(func, cip, sp, p1);
dvander@2320
   820
				CHECK_VALUE(v1);
dvander@2320
   821
				CHECK_VALUE(pri);
dvander@2320
   822
				rtemp = new (graph) StmtNode(
dvander@2320
   823
					_OP_STMT,
dvander@2320
   824
					new (graph) BinOpNode(_OP_STORE, new (graph) VarNode(v1), pri),
dvander@2320
   825
					NULL);
dvander@2320
   826
				root_tail->next = rtemp;
dvander@2320
   827
				root_tail = rtemp;
dvander@2320
   828
dvander@2320
   829
				/** 
dvander@2320
   830
				 * We create a varnode to simulate a load here.
dvander@2320
   831
				 * This helps accidental duplication of the expression tree.
dvander@2320
   832
				 */
dvander@2320
   833
				pri = new (graph) VarNode(v1);	
dvander@2320
   834
				break;
dvander@2320
   835
			}
dvander@2320
   836
		case OP_LOAD_S_BOTH:
dvander@2320
   837
			{
dvander@2320
   838
				p1 = PCODE_PARAM(pcode, cip, 1);
dvander@2320
   839
				p2 = PCODE_PARAM(pcode, cip, 2);
dvander@2320
   840
				v1 = sp_FindGraphVar(func, cip, sp, p1);
dvander@2320
   841
				v2 = sp_FindGraphVar(func, cip, sp, p2);
dvander@2320
   842
				CHECK_VALUE(v1);
dvander@2320
   843
				CHECK_VALUE(v2);
dvander@2320
   844
				pri = new (graph) VarNode(v1);
dvander@2320
   845
				alt = new (graph) VarNode(v2);
dvander@2320
   846
				break;
dvander@2320
   847
			}
dvander@2320
   848
		case OP_ADD:
dvander@2320
   849
		case OP_SMUL:
dvander@2320
   850
			{
dvander@2320
   851
				CHECK_VALUE(pri);
dvander@2320
   852
				CHECK_VALUE(alt);
dvander@2320
   853
				pri = new (graph) BinOpNode(op, pri, alt);
dvander@2320
   854
				break;
dvander@2320
   855
			}
dvander@2320
   856
		case OP_SUB_ALT:
dvander@2320
   857
			{
dvander@2320
   858
				CHECK_VALUE(pri);
dvander@2320
   859
				CHECK_VALUE(alt);
dvander@2320
   860
				pri = new (graph) BinOpNode(op, alt, pri);
dvander@2320
   861
				break;
dvander@2320
   862
			}
dvander@2320
   863
		case OP_SDIV:
dvander@2320
   864
			{
dvander@2320
   865
				CHECK_VALUE(pri);
dvander@2320
   866
				CHECK_VALUE(alt);
dvander@2320
   867
				pri = new (graph) BinOpNode(OP_SDIV, pri, alt);
dvander@2320
   868
				alt = new (graph) BinOpNode(_OP_MODULO, pri, alt);
dvander@2320
   869
				break;
dvander@2320
   870
			}
dvander@2320
   871
		case OP_SDIV_ALT:
dvander@2320
   872
			{
dvander@2320
   873
				CHECK_VALUE(pri);
dvander@2320
   874
				CHECK_VALUE(alt);
dvander@2320
   875
				pri = new (graph) BinOpNode(OP_SDIV, alt, pri);
dvander@2320
   876
				alt = new (graph) BinOpNode(_OP_MODULO, alt, pri);
dvander@2320
   877
				break;
dvander@2320
   878
			}
dvander@2320
   879
		case OP_ADD_C:
dvander@2320
   880
		case OP_SMUL_C:
dvander@2320
   881
			{
dvander@2320
   882
				p1 = PCODE_PARAM(pcode, cip, 1);
dvander@2320
   883
				CHECK_VALUE(pri);
dvander@2320
   884
				pri = new (graph) BinOpNode(op, pri, new (graph) ConstNode(p1));
dvander@2320
   885
				break;
dvander@2320
   886
			}
dvander@2320
   887
		case OP_RETN:
dvander@2320
   888
			{
dvander@2320
   889
				CHECK_VALUE(pri);
dvander@2320
   890
				rtemp = new (graph) StmtNode(op, pri, NULL);
dvander@2320
   891
				root_tail->next = rtemp;
dvander@2320
   892
				root_tail = rtemp;
dvander@2320
   893
				break;
dvander@2320
   894
			}
dvander@2320
   895
		default:
dvander@2320
   896
			{
dvander@2320
   897
				assert(0);
dvander@2320
   898
				break;
dvander@2320
   899
			}
dvander@2320
   900
		}
dvander@2320
   901
dvander@2320
   902
		NEXT_OP(cip, op);
dvander@2320
   903
	}
dvander@2320
   904
dvander@2320
   905
	sp_DebugPrint(graph->func, dc, root->next);
dvander@2320
   906
dvander@2320
   907
	return SP_ERROR_NONE;
dvander@2320
   908
dvander@2320
   909
return_error:
dvander@2320
   910
	return err;
dvander@2320
   911
}
dvander@2320
   912
dvander@2320
   913
#undef CHECK_FUNCVAR
dvander@2320
   914
dvander@2320
   915
inline Block *sp_BlockFromMap(sp_decomp_t *dc, ControlFlowGraph *graph, cell_t cip)
dvander@2320
   916
{
dvander@2320
   917
	cell_t val;
dvander@2320
   918
dvander@2320
   919
	assert(PCODE_VAL(dc->pcode_map, cip) & PCM_HAS_BLOCK);
dvander@2320
   920
dvander@2320
   921
	val = PCODE_VAL(dc->pcode_map, cip);
dvander@2320
   922
	val >>= PCM_NUM_BITS;
dvander@2320
   923
dvander@2320
   924
	assert(val >= 0 && val < (cell_t)graph->used_blocks);
dvander@2320
   925
dvander@2320
   926
	return &graph->blocks[val];
dvander@2320
   927
}
dvander@2320
   928
dvander@2320
   929
void sp_InsertBlock(sp_decomp_t *dc, unsigned int index, cell_t cip)
dvander@2320
   930
{
dvander@2320
   931
	cell_t val;
dvander@2320
   932
dvander@2320
   933
	val = PCODE_VAL(dc->pcode_map, cip);
dvander@2320
   934
dvander@2320
   935
	assert(!(val & PCM_HAS_BLOCK));
dvander@2320
   936
	assert(index < 0x20000000);
dvander@2320
   937
dvander@2320
   938
	val |= PCM_HAS_BLOCK;
dvander@2320
   939
	val |= index << 3;
dvander@2320
   940
dvander@2320
   941
	PCODE_VAL(dc->pcode_map, cip) = val;
dvander@2320
   942
}
dvander@2320
   943
dvander@2320
   944
static Block *sp_BuildBlocks(sp_decomp_t *dc, ControlFlowGraph *graph, cell_t cip)
dvander@2320
   945
{
dvander@2320
   946
	cell_t op;
dvander@2320
   947
	bool is_branch;
dvander@2320
   948
	cell_t target;
dvander@2320
   949
	Block *block;
dvander@2320
   950
	Edge *edge_tail;
dvander@2320
   951
	Block *outgoing;
dvander@2320
   952
	cell_t cip_end;
dvander@2320
   953
	cell_t *pcode;
dvander@2320
   954
dvander@2320
   955
	target = 0;
dvander@2320
   956
	cip_end = graph->func->code_end;
dvander@2320
   957
	pcode = (cell_t *)dc->plugin->pcode;
dvander@2320
   958
dvander@2320
   959
	if (PCODE_VAL(dc->pcode_map, cip) & PCM_HAS_BLOCK)
dvander@2320
   960
	{
dvander@2320
   961
		return sp_BlockFromMap(dc, graph, cip);
dvander@2320
   962
	}
dvander@2320
   963
dvander@2320
   964
	/* We can't add a new block if we hit our limit. */
dvander@2320
   965
	assert(graph->used_blocks < graph->max_blocks);
dvander@2320
   966
	if (graph->used_blocks >= graph->max_blocks)
dvander@2320
   967
	{
dvander@2320
   968
		return NULL;
dvander@2320
   969
	}
dvander@2320
   970
dvander@2320
   971
	edge_tail = NULL;
dvander@2320
   972
dvander@2320
   973
	/* Reserve a new block. */
dvander@2320
   974
	block = &graph->blocks[graph->used_blocks++];
dvander@2320
   975
	block->ip_start = cip;
dvander@2320
   976
	
dvander@2320
   977
	/* Enter us into the map. */
dvander@2320
   978
	sp_InsertBlock(dc, graph->used_blocks - 1, cip);
dvander@2320
   979
dvander@2320
   980
	/* Walk the code. */
dvander@2320
   981
	while (cip < cip_end)
dvander@2320
   982
	{
dvander@2320
   983
		/* Check whether this is the start of a new block. */
dvander@2320
   984
		if (cip != block->ip_start
dvander@2320
   985
			&& (PCODE_VAL(dc->pcode_map, cip) & PCM_BRANCH_TARGET))
dvander@2320
   986
		{
dvander@2320
   987
			block->ip_end = cip;
dvander@2320
   988
			if ((outgoing = sp_BuildBlocks(dc, graph, cip)) == NULL)
dvander@2320
   989
			{
dvander@2320
   990
				return NULL;
dvander@2320
   991
			}
dvander@2320
   992
			/* :TODO: add edge */
dvander@2320
   993
			return block;
dvander@2320
   994
		}
dvander@2320
   995
dvander@2320
   996
		is_branch = false;
dvander@2320
   997
		op = PCODE_VAL(pcode, cip);
dvander@2320
   998
		NEXT_OP(cip, op);
dvander@2320
   999
dvander@2320
  1000
		if (is_branch)
dvander@2320
  1001
		{
dvander@2320
  1002
			/* Check that branch offsets go to valid instructions. */
dvander@2320
  1003
			if (!(PCODE_VAL(dc->pcode_map, target) & PCM_OPCODE))
dvander@2320
  1004
			{
dvander@2320
  1005
				graph->err = SP_ERROR_INVALID_INSTRUCTION;
dvander@2320
  1006
				return NULL;
dvander@2320
  1007
			}
dvander@2320
  1008
dvander@2320
  1009
			/* Get our target block. */
dvander@2320
  1010
			if ((outgoing = sp_BuildBlocks(dc, graph, target)) == NULL)
dvander@2320
  1011
			{
dvander@2320
  1012
				return NULL;
dvander@2320
  1013
			}
dvander@2320
  1014
			/* :TODO: add edge */
dvander@2320
  1015
		}
dvander@2320
  1016
		else if (op == OP_RETN /* :TODO: || op == OP_RET*/)
dvander@2320
  1017
		{
dvander@2320
  1018
			block->ip_end = cip;
dvander@2320
  1019
			return block;
dvander@2320
  1020
		}
dvander@2320
  1021
	}
dvander@2320
  1022
dvander@2320
  1023
	graph->err = SP_ERROR_INVALID_INSTRUCTION;
dvander@2320
  1024
dvander@2320
  1025
	return NULL;
dvander@2320
  1026
}
dvander@2320
  1027
dvander@2320
  1028
dvander@2320
  1029
int Sp_DecompFunction(sp_decomp_t *dc, uint32_t code_addr, bool is_public)
dvander@2320
  1030
{
dvander@2320
  1031
	int err;
dvander@2320
  1032
	cell_t op;
dvander@2320
  1033
	ucell_t cip;
dvander@2320
  1034
	cell_t *pcode;
dvander@2320
  1035
	ucell_t cip_end;
dvander@2320
  1036
	sp_file_t *plugin;
dvander@2320
  1037
	FunctionInfo *func;
dvander@2320
  1038
	ControlFlowGraph graph;
dvander@2320
  1039
	
dvander@2320
  1040
	func = NULL;
dvander@2320
  1041
	plugin = dc->plugin;
dvander@2320
  1042
	memset(&graph, 0, sizeof(graph));
dvander@2320
  1043
dvander@2320
  1044
	if (code_addr >= plugin->pcode_size)
dvander@2320
  1045
	{
dvander@2320
  1046
		err = SP_ERROR_INVALID_INSTRUCTION;
dvander@2320
  1047
		goto return_error;
dvander@2320
  1048
	}
dvander@2320
  1049
dvander@2320
  1050
	pcode = (cell_t *)plugin->pcode;
dvander@2320
  1051
dvander@2320
  1052
	if (PCODE_VAL(pcode, code_addr) != OP_PROC)
dvander@2320
  1053
	{
dvander@2320
  1054
		err = SP_ERROR_INVALID_INSTRUCTION;
dvander@2320
  1055
		goto return_error;
dvander@2320
  1056
	}
dvander@2320
  1057
dvander@2320
  1058
	cip = code_addr + sizeof(cell_t);
dvander@2320
  1059
dvander@2320
  1060
	/* See if we have an existing function stored. */
dvander@2320
  1061
	func = sp_GetFuncPrototype(dc, code_addr, true, is_public);
dvander@2320
  1062
dvander@2320
  1063
	while (cip < plugin->pcode_size)
dvander@2320
  1064
	{
dvander@2320
  1065
		op = PCODE_VAL(pcode, cip);
dvander@2320
  1066
		if (op >= OP_TOTAL || op < 0)
dvander@2320
  1067
		{
dvander@2320
  1068
			err = SP_ERROR_INVALID_INSTRUCTION;
dvander@2320
  1069
		}
dvander@2320
  1070
		if (dc->opdef[op].name == NULL)
dvander@2320
  1071
		{
dvander@2320
  1072
			err = SP_ERROR_INVALID_INSTRUCTION;
dvander@2320
  1073
		}
dvander@2320
  1074
		if (op == OP_PROC)
dvander@2320
  1075
		{
dvander@2320
  1076
			break;
dvander@2320
  1077
		}
dvander@2320
  1078
		NEXT_OP(cip, op);
dvander@2320
  1079
	}
dvander@2320
  1080
	cip_end = cip;
dvander@2320
  1081
dvander@2320
  1082
	func->code_end = cip_end;
dvander@2320
  1083
dvander@2320
  1084
	/* Now we can cache all local vars. */
dvander@2320
  1085
	sp_CacheLocals(dc, func);
dvander@2320
  1086
dvander@2320
  1087
	/** 
dvander@2320
  1088
	 * Make the entry point as the first block.
dvander@2320
  1089
	 * It needs to be flagged as well so it doesn't get double counted.
dvander@2320
  1090
	 */
dvander@2320
  1091
	memset(&PCODE_VAL(dc->pcode_map, code_addr), 0, cip_end - code_addr);
dvander@2320
  1092
		   
dvander@2320
  1093
	graph.max_blocks = 1;
dvander@2320
  1094
	graph.func = func;
dvander@2320
  1095
	PCODE_VAL(dc->pcode_map, code_addr) |= PCM_BRANCH_TARGET;
dvander@2320
  1096
dvander@2320
  1097
	/* Walk the code. */
dvander@2320
  1098
	{
dvander@2320
  1099
		cell_t target;
dvander@2320
  1100
		bool is_branch;
dvander@2320
  1101
dvander@2320
  1102
		target = 0;
dvander@2320
  1103
		cip = code_addr;
dvander@2320
  1104
		while (cip < cip_end)
dvander@2320
  1105
		{
dvander@2320
  1106
			/* Get opcode */
dvander@2320
  1107
			op = PCODE_VAL(pcode, cip);
dvander@2320
  1108
			/* Mark as not a branch */
dvander@2320
  1109
			is_branch = false;
dvander@2320
  1110
			/* Make sure we know this is a valid target */
dvander@2320
  1111
			PCODE_VAL(dc->pcode_map, cip) |= PCM_OPCODE;
dvander@2320
  1112
dvander@2320
  1113
			if (is_branch)
dvander@2320
  1114
			{
dvander@2320
  1115
				/**
dvander@2320
  1116
				 * Keep track of where branches are and how many blocks we need.
dvander@2320
  1117
				 */
dvander@2320
  1118
				if (!(PCODE_VAL(dc->pcode_map, target) & PCM_BRANCH_TARGET))
dvander@2320
  1119
				{
dvander@2320
  1120
					PCODE_VAL(dc->pcode_map, target) |= PCM_BRANCH_TARGET;
dvander@2320
  1121
					graph.max_blocks++;
dvander@2320
  1122
dvander@2320
  1123
					/**
dvander@2320
  1124
					 * If we're creating a new block, estimate that we need to create a new edge 
dvander@2320
  1125
					 * as well.
dvander@2320
  1126
					 */
dvander@2320
  1127
					graph.max_edges++;
dvander@2320
  1128
				}
dvander@2320
  1129
dvander@2320
  1130
                /**
dvander@2320
  1131
                 * In this algorithm, jumps do not terminate a block unless they are unconditional.
dvander@2320
  1132
                 * This reduces the number of blocks and edges.
dvander@2320
  1133
                 *
dvander@2320
  1134
                 * A property of this is that jumps are edges, so the number of edges is identical 
dvander@2320
  1135
				 * to the number of jumps.
dvander@2320
  1136
                 */
dvander@2320
  1137
				graph.max_edges++;
dvander@2320
  1138
			}
dvander@2320
  1139
dvander@2320
  1140
			NEXT_OP(cip, op);
dvander@2320
  1141
		}
dvander@2320
  1142
	}
dvander@2320
  1143
dvander@2320
  1144
	/* Initialize memory needed for the graph. */
dvander@2320
  1145
	graph.blocks = new Block[graph.max_blocks];
dvander@2320
  1146
	memset(graph.blocks, 0, sizeof(Block) * graph.max_blocks);
dvander@2320
  1147
	
dvander@2320
  1148
	if (graph.max_edges != 0)
dvander@2320
  1149
	{
dvander@2320
  1150
		graph.edges = new Edge[graph.max_edges];
dvander@2320
  1151
		memset(graph.edges, 0, sizeof(Edge) * graph.max_edges);
dvander@2320
  1152
	}
dvander@2320
  1153
dvander@2320
  1154
	if ((graph.entry = sp_BuildBlocks(dc, &graph, code_addr)) == NULL)
dvander@2320
  1155
	{
dvander@2320
  1156
		if ((err = graph.err) == SP_ERROR_NONE)
dvander@2320
  1157
		{
dvander@2320
  1158
			err = SP_ERROR_ABORTED;
dvander@2320
  1159
		}
dvander@2320
  1160
		goto return_error;
dvander@2320
  1161
	}
dvander@2320
  1162
dvander@2320
  1163
	if ((err = sp_AnalyzeGraph(dc, &graph)) == NULL)
dvander@2320
  1164
	{
dvander@2320
  1165
		goto return_error;
dvander@2320
  1166
	}
dvander@2320
  1167
dvander@2320
  1168
	return SP_ERROR_NONE;
dvander@2320
  1169
dvander@2320
  1170
return_error:
dvander@2320
  1171
	return err;
dvander@2320
  1172
}