|
|
|
@ -16,7 +16,9 @@
|
|
|
|
|
#include <parse.h>
|
|
|
|
|
#include <expression.h>
|
|
|
|
|
#include <linearize.h>
|
|
|
|
|
#if 0
|
|
|
|
|
#include <flow.h>
|
|
|
|
|
#endif
|
|
|
|
|
#include <target.h>
|
|
|
|
|
|
|
|
|
|
static pseudo_t linearize_statement(struct dmr_C *C, struct entrypoint *ep, struct statement *stmt);
|
|
|
|
@ -823,6 +825,20 @@ static pseudo_t symbol_pseudo(struct dmr_C *C, struct entrypoint *ep, struct sym
|
|
|
|
|
return pseudo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int dmrC_value_size(long long value)
|
|
|
|
|
{
|
|
|
|
|
value >>= 8;
|
|
|
|
|
if (!value)
|
|
|
|
|
return 8;
|
|
|
|
|
value >>= 8;
|
|
|
|
|
if (!value)
|
|
|
|
|
return 16;
|
|
|
|
|
value >>= 16;
|
|
|
|
|
if (!value)
|
|
|
|
|
return 32;
|
|
|
|
|
return 64;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pseudo_t dmrC_value_pseudo(struct dmr_C *C, struct symbol *type, long long val)
|
|
|
|
|
{
|
|
|
|
|
int hash = val & (MAX_VAL_HASH-1);
|
|
|
|
@ -2304,6 +2320,7 @@ static struct entrypoint *linearize_fn(struct dmr_C *C, struct symbol *sym, stru
|
|
|
|
|
*/
|
|
|
|
|
dmrC_kill_unreachable_bbs(C, ep);
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
if (C->optimize) {
|
|
|
|
|
|
|
|
|
|
if (show_details) {
|
|
|
|
@ -2377,6 +2394,7 @@ static struct entrypoint *linearize_fn(struct dmr_C *C, struct symbol *sym, stru
|
|
|
|
|
goto repeat;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Finally, add deathnotes to pseudos now that we have them */
|
|
|
|
|
if (C->dbg_dead)
|
|
|
|
@ -2402,6 +2420,264 @@ struct entrypoint *dmrC_linearize_symbol(struct dmr_C *C, struct symbol *sym)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mark_bb_reachable(struct basic_block *bb, unsigned long generation)
|
|
|
|
|
{
|
|
|
|
|
struct basic_block *child;
|
|
|
|
|
|
|
|
|
|
if (bb->generation == generation)
|
|
|
|
|
return;
|
|
|
|
|
bb->generation = generation;
|
|
|
|
|
FOR_EACH_PTR(bb->children, child) {
|
|
|
|
|
mark_bb_reachable(child, generation);
|
|
|
|
|
} END_FOR_EACH_PTR(child);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dmrC_kill_unreachable_bbs(struct dmr_C *C, struct entrypoint *ep)
|
|
|
|
|
{
|
|
|
|
|
struct basic_block *bb;
|
|
|
|
|
unsigned long generation = ++C->L->bb_generation;
|
|
|
|
|
|
|
|
|
|
mark_bb_reachable(ep->entry->bb, generation);
|
|
|
|
|
FOR_EACH_PTR(ep->bbs, bb) {
|
|
|
|
|
if (bb->generation == generation)
|
|
|
|
|
continue;
|
|
|
|
|
/* Mark it as being dead */
|
|
|
|
|
dmrC_kill_bb(C, bb);
|
|
|
|
|
bb->ep = NULL;
|
|
|
|
|
DELETE_CURRENT_PTR(bb);
|
|
|
|
|
} END_FOR_EACH_PTR(bb);
|
|
|
|
|
ptrlist_pack((struct ptr_list **) &ep->bbs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int delete_pseudo_user_list_entry(struct dmr_C *C, struct pseudo_user_list **list, pseudo_t *entry, int count)
|
|
|
|
|
{
|
|
|
|
|
struct pseudo_user *pu;
|
|
|
|
|
|
|
|
|
|
FOR_EACH_PTR(*list, pu) {
|
|
|
|
|
if (pu->userp == entry) {
|
|
|
|
|
MARK_CURRENT_DELETED(struct pseudo_user *, pu);
|
|
|
|
|
if (!--count)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
} END_FOR_EACH_PTR(pu);
|
|
|
|
|
assert(count <= 0);
|
|
|
|
|
out:
|
|
|
|
|
if (ptrlist_size((struct ptr_list *)*list) == 0)
|
|
|
|
|
*list = NULL;
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void remove_usage(struct dmr_C *C, pseudo_t p, pseudo_t *usep)
|
|
|
|
|
{
|
|
|
|
|
if (dmrC_has_use_list(p)) {
|
|
|
|
|
delete_pseudo_user_list_entry(C, &p->users, usep, 1);
|
|
|
|
|
if (!p->users)
|
|
|
|
|
dmrC_kill_instruction(C, p->def);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void concat_user_list(struct pseudo_user_list *src, struct pseudo_user_list **dst)
|
|
|
|
|
{
|
|
|
|
|
ptrlist_concat((struct ptr_list *)src, (struct ptr_list **)dst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dmrC_convert_instruction_target(struct dmr_C *C, struct instruction *insn, pseudo_t src)
|
|
|
|
|
{
|
|
|
|
|
pseudo_t target;
|
|
|
|
|
struct pseudo_user *pu;
|
|
|
|
|
/*
|
|
|
|
|
* Go through the "insn->users" list and replace them all..
|
|
|
|
|
*/
|
|
|
|
|
target = insn->target;
|
|
|
|
|
if (target == src)
|
|
|
|
|
return;
|
|
|
|
|
FOR_EACH_PTR(target->users, pu) {
|
|
|
|
|
if (*pu->userp != VOID_PSEUDO(C)) {
|
|
|
|
|
assert(*pu->userp == target);
|
|
|
|
|
*pu->userp = src;
|
|
|
|
|
}
|
|
|
|
|
} END_FOR_EACH_PTR(pu);
|
|
|
|
|
if (dmrC_has_use_list(src))
|
|
|
|
|
concat_user_list(target->users, &src->users);
|
|
|
|
|
target->users = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void kill_defs(struct dmr_C *C, struct instruction *insn)
|
|
|
|
|
{
|
|
|
|
|
pseudo_t target = insn->target;
|
|
|
|
|
|
|
|
|
|
if (!dmrC_has_use_list(target))
|
|
|
|
|
return;
|
|
|
|
|
if (target->def != insn)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
dmrC_convert_instruction_target(C, insn, VOID_PSEUDO(C));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dmrC_kill_bb(struct dmr_C *C, struct basic_block *bb)
|
|
|
|
|
{
|
|
|
|
|
struct instruction *insn;
|
|
|
|
|
struct basic_block *child, *parent;
|
|
|
|
|
|
|
|
|
|
FOR_EACH_PTR(bb->insns, insn) {
|
|
|
|
|
dmrC_kill_instruction_force(C, insn);
|
|
|
|
|
kill_defs(C, insn);
|
|
|
|
|
/*
|
|
|
|
|
* We kill unreachable instructions even if they
|
|
|
|
|
* otherwise aren't "killable" (e.g. volatile loads)
|
|
|
|
|
*/
|
|
|
|
|
} END_FOR_EACH_PTR(insn);
|
|
|
|
|
bb->insns = NULL;
|
|
|
|
|
|
|
|
|
|
FOR_EACH_PTR(bb->children, child) {
|
|
|
|
|
dmrC_remove_bb_from_list(&child->parents, bb, 0);
|
|
|
|
|
} END_FOR_EACH_PTR(child);
|
|
|
|
|
bb->children = NULL;
|
|
|
|
|
|
|
|
|
|
FOR_EACH_PTR(bb->parents, parent) {
|
|
|
|
|
dmrC_remove_bb_from_list(&parent->children, bb, 0);
|
|
|
|
|
} END_FOR_EACH_PTR(parent);
|
|
|
|
|
bb->parents = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void dmrC_kill_use(struct dmr_C *C, pseudo_t *usep)
|
|
|
|
|
{
|
|
|
|
|
if (usep) {
|
|
|
|
|
pseudo_t p = *usep;
|
|
|
|
|
*usep = VOID_PSEUDO(C);
|
|
|
|
|
remove_usage(C, p, usep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void kill_use_list(struct dmr_C *C, struct pseudo_list *list)
|
|
|
|
|
{
|
|
|
|
|
pseudo_t p;
|
|
|
|
|
FOR_EACH_PTR(list, p) {
|
|
|
|
|
if (p == VOID_PSEUDO(C))
|
|
|
|
|
continue;
|
|
|
|
|
dmrC_kill_use(C, THIS_ADDRESS(pseudo_t, p));
|
|
|
|
|
} END_FOR_EACH_PTR(p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* kill an instruction:
|
|
|
|
|
* - remove it from its bb
|
|
|
|
|
* - remove the usage of all its operands
|
|
|
|
|
* If forse is zero, the normal case, the function only for
|
|
|
|
|
* instructions free of (possible) side-effects. Otherwise
|
|
|
|
|
* the function does that unconditionally (must only be used
|
|
|
|
|
* for unreachable instructions.
|
|
|
|
|
*/
|
|
|
|
|
void dmrC_kill_insn(struct dmr_C *C, struct instruction *insn, int force)
|
|
|
|
|
{
|
|
|
|
|
if (!insn || !insn->bb)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
switch (insn->opcode) {
|
|
|
|
|
case OP_SEL:
|
|
|
|
|
case OP_RANGE:
|
|
|
|
|
dmrC_kill_use(C, &insn->src3);
|
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
|
|
case OP_ADD:
|
|
|
|
|
case OP_SUB:
|
|
|
|
|
case OP_MULU:
|
|
|
|
|
case OP_MULS:
|
|
|
|
|
case OP_DIVU:
|
|
|
|
|
case OP_DIVS:
|
|
|
|
|
case OP_MODU:
|
|
|
|
|
case OP_MODS:
|
|
|
|
|
case OP_SHL:
|
|
|
|
|
case OP_LSR:
|
|
|
|
|
case OP_ASR:
|
|
|
|
|
|
|
|
|
|
/* Logical */
|
|
|
|
|
case OP_AND:
|
|
|
|
|
case OP_OR:
|
|
|
|
|
case OP_XOR:
|
|
|
|
|
case OP_AND_BOOL:
|
|
|
|
|
case OP_OR_BOOL:
|
|
|
|
|
|
|
|
|
|
case OP_SET_EQ:
|
|
|
|
|
case OP_SET_NE:
|
|
|
|
|
case OP_SET_LE:
|
|
|
|
|
case OP_SET_GE:
|
|
|
|
|
case OP_SET_LT:
|
|
|
|
|
case OP_SET_GT:
|
|
|
|
|
case OP_SET_B:
|
|
|
|
|
case OP_SET_A:
|
|
|
|
|
case OP_SET_BE:
|
|
|
|
|
case OP_SET_AE:
|
|
|
|
|
dmrC_kill_use(C, &insn->src2);
|
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
|
|
case OP_CAST:
|
|
|
|
|
case OP_SCAST:
|
|
|
|
|
case OP_FPCAST:
|
|
|
|
|
case OP_PTRCAST:
|
|
|
|
|
case OP_SETVAL:
|
|
|
|
|
case OP_NOT: case OP_NEG:
|
|
|
|
|
case OP_SLICE:
|
|
|
|
|
dmrC_kill_use(C, &insn->src1);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_PHI:
|
|
|
|
|
kill_use_list(C, insn->phi_list);
|
|
|
|
|
break;
|
|
|
|
|
case OP_PHISOURCE:
|
|
|
|
|
dmrC_kill_use(C, &insn->phi_src);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_SYMADDR:
|
|
|
|
|
C->L->repeat_phase |= REPEAT_SYMBOL_CLEANUP;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_CBR:
|
|
|
|
|
/* fall through */
|
|
|
|
|
case OP_COMPUTEDGOTO:
|
|
|
|
|
dmrC_kill_use(C, &insn->cond);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_CALL:
|
|
|
|
|
if (!force) {
|
|
|
|
|
/* a "pure" function can be killed too */
|
|
|
|
|
if (!(insn->func->type == PSEUDO_SYM))
|
|
|
|
|
return;
|
|
|
|
|
if (!(insn->func->sym->ctype.modifiers & MOD_PURE))
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
kill_use_list(C, insn->arguments);
|
|
|
|
|
if (insn->func->type == PSEUDO_REG)
|
|
|
|
|
dmrC_kill_use(C, &insn->func);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_LOAD:
|
|
|
|
|
if (!force && insn->type->ctype.modifiers & MOD_VOLATILE)
|
|
|
|
|
return;
|
|
|
|
|
dmrC_kill_use(C, &insn->src);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_STORE:
|
|
|
|
|
if (!force)
|
|
|
|
|
return;
|
|
|
|
|
dmrC_kill_use(C, &insn->src);
|
|
|
|
|
dmrC_kill_use(C, &insn->target);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_ENTRY:
|
|
|
|
|
/* ignore */
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case OP_BR:
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
insn->bb = NULL;
|
|
|
|
|
C->L->repeat_phase |= REPEAT_CSE;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dmrC_init_linearizer(struct dmr_C *C) {
|
|
|
|
|
struct linearizer_state_t *L = (struct linearizer_state_t *)calloc(1, sizeof(struct linearizer_state_t));
|
|
|
|
|