void WordcodeEmitter::undoRelativeOffsetInJump() { AvmAssert(isJumpInstruction(O[nextI - 1])); AvmAssert(I[nextI - 1] + 2 == dest); uintptr_t offset = I[nextI - 1][1]; if (offset == 0x80000000U) { // Forward branch, must find and nuke the backpatch backpatch_info *b = backpatches; backpatch_info *b2 = NULL; while (b != NULL && b->patch_loc != &I[nextI - 1][1]) b2 = b, b = b->next; AvmAssert(b != NULL); if (b2 == NULL) backpatches = b->next; else b2->next = b->next; // b is unlinked // Install the ABC byte offset from the backpatch structure (will be positive) I[nextI - 1][1] = uint32_t(b->target_pc - code_start); delete b; } else { // Backward branch AvmAssert((int32_t)I[nextI - 1][1] < 0); // Install the negative of the absolute word offset of the target I[nextI - 1][1] = -int32_t(buffer_offset + (dest - buffers->data) + (int32_t)I[nextI - 1][1]); } }
/* test if the current instruction `instr' is a BT or a BF */ int isUnconditionalJump(t_axe_instruction *instr) { if (isJumpInstruction(instr)) { if ((instr->opcode == BT) || (instr->opcode == BF)) return 1; } return 0; }
bool WordcodeEmitter::replace(uint32_t old_instr, uint32_t new_words, bool jump_has_been_translated) { // Undo any relative offsets in the last instruction, if that wasn't done by // the commit code. if (isJumpInstruction(O[nextI - 1]) && !jump_has_been_translated) undoRelativeOffsetInJump(); // Catenate unconsumed instructions onto R (it's easier than struggling with // moving instructions across buffer boundaries) uint32_t k = new_words; for ( uint32_t n=old_instr ; n < nextI ; n++ ) { uint32_t len = calculateInstructionWidth(O[n]); S[k] = O[n]; for ( uint32_t j=0 ; j < len ; j++ ) R[k++] = I[n][j]; } // Unlink the last buffer segment if we took everything from it, push it onto // a reserve (there can only ever be one free). We know I[nextI-1] points into the // current buffer, so check if I[0] is between the start of the buffer and // the last instruction. if (!(buffers->data <= I[0] && I[0] <= I[nextI-1])) { spare_buffer = buffers; buffers = buffers->next; spare_buffer->next = NULL; dest_limit = buffers->data + sizeof(buffers->data)/sizeof(buffers->data[0]); buffer_offset -= buffers->entries_used; } dest = I[0]; // Emit the various instructions from new_data, handling branches specially. // // At this point the instance variables state, I, O, nextI, backtrack_stack, // and backtrack_idx are dead, and all the data we need for emitting the // instructions are in S and R. In addition, dest has been rolled back and // points to the address of the first instruction in the peephole window, and // nothing is live in the code buffer beyond that point. It's as if we are // in a context where we're just emitting instructions. // // Consequently, we set state to 0 and start emitting instructions from S/R // normally, calling peep() after each instruction that was not replaced by // the current action. This works without having local copies of S and R // because peephole optimization cannot insert a replacement sequence that is // longer than the matched sequence; so the segments of S and R used by any // recursive match will not affect what we're doing here. Furthermore, 'dest' // is shared between this match and recursive matches, so if a recursive match // shortens the instruction sequence the correct value of dest will be used // when we get back to the present invocation of replace(). // Reset the machine. state = 0; uint32_t i=0; while (i < k) { uintptr_t op = S[i]; uintptr_t width = calculateInstructionWidth(op); CHECK(width); if (isJumpInstruction(op)) { *dest++ = R[i++]; int32_t offset = int32_t(R[i++]); if (offset >= 0) { // Forward jump // Install a new backpatch structure makeAndInsertBackpatch(code_start + offset, uint32_t(buffer_offset + (dest + (width - 1) - buffers->data))); } else { // Backward jump // Compute new jump offset *dest = -int32_t(buffer_offset + (dest + (width - 1) - buffers->data) + offset); dest++; } if (width >= 3) *dest++ = R[i++]; if (width >= 4) *dest++ = R[i++]; AvmAssert(width <= 4); } else { switch (width) { default: AvmAssert(!"Can't happen"); case 1: *dest++ = R[i++]; break; case 2: *dest++ = R[i++]; *dest++ = R[i++]; break; case 3: *dest++ = R[i++]; *dest++ = R[i++]; *dest++ = R[i++]; break; case 5: // OP_debug *dest++ = R[i++]; *dest++ = R[i++]; *dest++ = R[i++]; *dest++ = R[i++]; *dest++ = R[i++]; break; } } if (i-width >= new_words) peep((uint32_t)op, dest-width); } return true; // always }
/* set the def-use values for the current node */ void setDefUses(t_cflow_Graph *graph, t_cflow_Node *node) { t_axe_instruction *instr; t_cflow_var *varDest; t_cflow_var *varSource1; t_cflow_var *varSource2; /* preconditions */ if (graph == NULL) { cflow_errorcode = CFLOW_GRAPH_UNDEFINED; return; } if (node == NULL) { cflow_errorcode = CFLOW_INVALID_NODE; return; } if (node->instr == NULL) { cflow_errorcode = CFLOW_INVALID_INSTRUCTION; return; } if ((node->instr)->opcode == INVALID_OPCODE) { cflow_errorcode = CFLOW_INVALID_INSTRUCTION; return; } /* update the value of `instr' */ instr = node->instr; /* if the instruction is a Jump instruction then does nothing */ if ( isJumpInstruction(instr)) return; /* initialize the values of varDest, varSource1 and varSource2 */ varDest = NULL; varSource1 = NULL; varSource2 = NULL; /* update the values of the variables */ if (instr->reg_1 != NULL) varDest = allocVariable(graph, (instr->reg_1)->ID); if (instr->reg_2 != NULL) varSource1 = allocVariable(graph, (instr->reg_2)->ID); if (instr->reg_3 != NULL) varSource2 = allocVariable(graph, (instr->reg_3)->ID); switch(instr->opcode) { case LOAD : case AXE_READ : node->def = varDest; break; case STORE : case AXE_WRITE : (node->uses)[0] = varDest; break; case SGE : case SGT: case SLE : case SLT : case SNE : case SEQ : node->def = varDest; break; case HALT : case RET : case JSR : case NOP : break; case MOVA : node->def = varDest; break; case NOTB : case NOTL : case ROTRI : case ROTLI : case SHRI : case SHLI : case DIVI : case MULI : case EORBI : case ORBI : case ANDBI : case EORLI : case ORLI : case ANDLI : case SUBI : case ADDI : node->def = varDest; (node->uses)[0] = varSource1; break; default : if ((instr->reg_1)->indirect) (node->uses)[2] = varDest; else node->def = varDest; (node->uses)[0] = varSource1; (node->uses)[1] = varSource2; } }
void updateFlowGraph(t_cflow_Graph *graph) { t_list *current_element; t_basic_block *current_block; t_basic_block *successor; /* preconditions: graph should not be a NULL pointer */ if (graph == NULL){ cflow_errorcode = CFLOW_GRAPH_UNDEFINED; return; } successor = NULL; current_element = graph->blocks; while(current_element != NULL) { t_list *last_element; t_cflow_Node *last_node; t_axe_instruction *last_instruction; t_basic_block *jumpBlock; /* retrieve the current block */ current_block = (t_basic_block *) LDATA(current_element); assert(current_block != NULL); assert(current_block->nodes != NULL); /* get the last node of the basic block */ last_element = getLastElement(current_block->nodes); assert(last_element != NULL); last_node = (t_cflow_Node *) LDATA(last_element); assert(last_node != NULL); last_instruction = last_node->instr; assert(last_instruction != NULL); if (isHaltInstruction(last_instruction)) { setSucc(current_block, graph->endingBlock); setPred(graph->endingBlock, current_block); } else { if (isJumpInstruction(last_instruction)) { if ( (last_instruction->address == NULL) || ((last_instruction->address)->labelID == NULL) ) { cflow_errorcode = CFLOW_INVALID_LABEL_FOUND; return; } jumpBlock = searchLabel(graph , (last_instruction->address)->labelID); if (jumpBlock == NULL) { cflow_errorcode = CFLOW_INVALID_LABEL_FOUND; return; } /* add the jumpBlock to the list of successors of current_block */ /* add also current_block to the list of predecessors of jumpBlock */ setPred(jumpBlock, current_block); if (cflow_errorcode != CFLOW_OK) return; setSucc(current_block, jumpBlock); if (cflow_errorcode != CFLOW_OK) return; } if (!isUnconditionalJump(last_instruction)) { t_basic_block *nextBlock; t_list *next_element; next_element = LNEXT(current_element); if (next_element != NULL) { nextBlock = LDATA(next_element); assert(nextBlock != NULL); setSucc(current_block, nextBlock); setPred(nextBlock, current_block); } else { setSucc(current_block, graph->endingBlock); setPred(graph->endingBlock, current_block); } if (cflow_errorcode != CFLOW_OK) return; } } /* update the value of `current_element' */ current_element = LNEXT(current_element); } }