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