|
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 |
} |