/* GNU PIC coff optimizing functions
Copyright 2005 Craig Franklin
Copyright 2015-2016 Molnár Károly
*/
#include "stdhdr.h"
#include "libgputils.h"
#define COPT_NULL 0
#define COPT_BRA14E_CURR_PAGE (1 << 0)
#define COPT_BRA14E_OTHER_PAGE (1 << 1)
#define COPT_GOTO_CURR_PAGE (1 << 2)
#define COPT_GOTO_OTHER_PAGE (1 << 3)
#define COPT_CALL_CURR_PAGE (1 << 4)
#define COPT_CALL_OTHER_PAGE (1 << 5)
#define COPT_PAGESEL_CURR_PAGE (1 << 6)
#define COPT_PAGESEL_OTHER_PAGE (1 << 7)
#define COPT_BANKSEL (1 << 8)
/* Only PIC14E and PIC14EX. */
#define COPT_BRA14E_MASK (COPT_BRA14E_CURR_PAGE | COPT_BRA14E_OTHER_PAGE)
#define COPT_GOTO_MASK (COPT_GOTO_CURR_PAGE | COPT_GOTO_OTHER_PAGE)
#define COPT_CALL_MASK (COPT_CALL_CURR_PAGE | COPT_CALL_OTHER_PAGE)
/* Only PIC14E and PIC14EX. */
#define COPT_REL_BRANCH_CURR_PAGE_MASK COPT_BRA14E_CURR_PAGE
#define COPT_REL_BRANCH_OTHER_PAGE_MASK COPT_BRA14E_OTHER_PAGE
#define COPT_REL_BRANCH_MASK COPT_BRA14E_MASK
#define COPT_ABS_BRANCH_CURR_PAGE_MASK (COPT_GOTO_CURR_PAGE | COPT_CALL_CURR_PAGE)
#define COPT_ABS_BRANCH_OTHER_PAGE_MASK (COPT_GOTO_OTHER_PAGE | COPT_CALL_OTHER_PAGE)
#define COPT_ABS_BRANCH_MASK (COPT_ABS_BRANCH_CURR_PAGE_MASK | COPT_ABS_BRANCH_OTHER_PAGE_MASK)
#define COPT_BRANCH_CURR_PAGE_MASK (COPT_REL_BRANCH_CURR_PAGE_MASK | COPT_ABS_BRANCH_CURR_PAGE_MASK)
#define COPT_BRANCH_OTHER_PAGE_MASK (COPT_REL_BRANCH_OTHER_PAGE_MASK | COPT_ABS_BRANCH_OTHER_PAGE_MASK)
#define COPT_BRANCH_MASK (COPT_BRANCH_CURR_PAGE_MASK | COPT_BRANCH_OTHER_PAGE_MASK)
#define COPT_PAGESEL_MASK (COPT_PAGESEL_CURR_PAGE | COPT_PAGESEL_OTHER_PAGE)
/* Number of reloc_properties_t type in an array. */
#define RELOC_PIPE_LENGTH 4
typedef struct reloc_properties {
gp_reloc_t *relocation;
const gp_symbol_t *label; /* If exists so label which is linked here to. */
const insn_t *instruction; /* The actual instruction. */
unsigned state; /* For COPT_... constants. */
bool s_protected;
uint32_t target_page;
uint32_t reloc_page;
uint32_t reloc_byte_addr;
uint32_t reloc_insn_addr;
uint32_t reloc_byte_length;
uint32_t reloc_insn_length;
uint32_t ram_bank;
} reloc_properties_t;
static reloc_properties_t reloc_pipe[RELOC_PIPE_LENGTH];
static gp_section_t **section_array;
static unsigned num_sections;
static gp_symbol_t **register_array;
static unsigned num_registers;
static bool first_banksel = false;
/*------------------------------------------------------------------------------------------------*/
/* Remove any weak symbols in the object. */
FUNC(void) gp_coffopt_remove_weak(gp_object_t *Object) {
gp_debug("Removing weak symbols from \"%s\".", Object->filename);
/* Search the symbol table for extern symbols. */
gp_symbol_list_t::iterator it = Object->symbol_list.begin();
while (it!=Object->symbol_list.end()) {
if (it->gp_coffgen_is_external_symbol() && !it->gp_coffgen_symbol_has_reloc(COFF_SYM_RELOC_ALL)) {
gp_debug(" removed weak symbol \"%s\"", it->name);
/* It is not allowed to deleted because the gplink/cod.c will need this. */
gp_coffgen_move_reserve_symbol(Object, &*it);
}
it++;
}
}
/*------------------------------------------------------------------------------------------------*/
/* Remove any relocatable section that doesn't have a symbol pointed to by a relocation. */
FUNC(void) gp_coffopt_remove_dead_sections(gp_object_t *Object, int Pass, bool Enable_cinit_wanings)
{
gp_section_t *section;
gp_section_t *section_next;
bool section_removed;
do {
section_removed = false;
gp_debug("Removing dead sections pass %i.", Pass);
gp_coffgen_check_relocations(Object, Enable_cinit_wanings);
section = Object->section_list.first;
while (section) {
section_next = section->next;
if (FlagIsClr(section->opt_flags, OPT_FLAGS_PROTECTED_SECTION)) {
gp_debug("Removing section \"%s\".", section->name);
/* It is not allowed to deleted because the gplink/cod.c will need these. */
gp_coffgen_move_reserve_section_symbols(Object, section);
/* It is not allowed to deleted because the gplink/cod.c will need this. */
gp_coffgen_move_reserve_section(Object, section);
section_removed = true;
}
section = section_next;
}
/* take another pass */
++Pass;
} while (section_removed);
}
/*------------------------------------------------------------------------------------------------*/
/* Deletes all states from relocation pipe. */
static void _reloc_pipe_clear(void)
{
memset(reloc_pipe, 0, sizeof(reloc_pipe));
}
/*------------------------------------------------------------------------------------------------*/
/* Moves the contents of relocation tube forward or backward. */
static void _reloc_pipe_shift(bool Forward)
{
size_t i;
if (Forward) {
/* Moves forward.
reloc_pipe[last - 1] --> reloc_pipe[last] -- The oldest relocation.
.
.
.
reloc_pipe[first] --> reloc_pipe[first+1]
reloc_pipe[first] <-- 0 -- The current relocation. */
for (i = RELOC_PIPE_LENGTH - 1; i > 0; --i) {
memcpy(&reloc_pipe[i], &reloc_pipe[i - 1], sizeof(reloc_properties_t));
}
memset(&reloc_pipe[0], 0, sizeof(reloc_properties_t));
}
else {
/* Moves backward.
reloc_pipe[first + 1] --> reloc_pipe[first] -- The current relocation.
.
.
.
reloc_pipe[last] --> reloc_pipe[last - 1]
reloc_pipe[last] <-- 0 -- The oldest relocation. */
for (i = 0; i < (RELOC_PIPE_LENGTH - 1); ++i) {
memcpy(&reloc_pipe[i], &reloc_pipe[i + 1], sizeof(reloc_properties_t));
}
memset(&reloc_pipe[RELOC_PIPE_LENGTH - 1], 0, sizeof(reloc_properties_t));
}
}
/*------------------------------------------------------------------------------------------------*/
/* Deletes one state from the relocation pipe. */
static void _reloc_pipe_delete_state(size_t State_index)
{
assert(State_index < RELOC_PIPE_LENGTH);
while (State_index < (RELOC_PIPE_LENGTH - 1)) {
memcpy(&reloc_pipe[State_index], &reloc_pipe[State_index + 1], sizeof(reloc_properties_t));
++State_index;
}
memset(&reloc_pipe[RELOC_PIPE_LENGTH - 1], 0, sizeof(reloc_properties_t));
}
/*------------------------------------------------------------------------------------------------*/
/* Make page address from an instruction address. */
static uint32_t _page_addr_from_insn_addr(proc_class_t Class, uint32_t Insn_addr)
{
return gp_processor_page_addr(Class, Insn_addr);
}
/*------------------------------------------------------------------------------------------------*/
/* Make page address from an byte address. */
static uint32_t
_page_addr_from_byte_addr(proc_class_t Class, uint32_t Byte_addr)
{
return gp_processor_page_addr(Class, gp_processor_insn_from_byte_c(Class, Byte_addr));
}
/*------------------------------------------------------------------------------------------------*/
/* Decrease relocation addresses in a given list. */
static void _reloc_decrease_addresses(proc_class_t Class, gp_reloc_t *Relocation, uint32_t Relocation_page,
uint32_t Insn_offset, uint32_t Byte_offset)
{
gp_reloc_t *reloc;
gp_symbol_t *symbol;
const gp_section_t *section;
if (Relocation == NULL) {
return;
}
reloc = Relocation;
while (reloc) {
if (reloc->address >= Byte_offset) {
reloc->address -= Byte_offset;
symbol = reloc->symbol;
section = symbol->section;
/* Prevents the modification of symbols on the other pages. */
if (FlagIsSet(section->flags, STYP_ROM_AREA) &&
(_page_addr_from_insn_addr(Class, symbol->value) == Relocation_page)) {
/* Prevents the multiple modifications of symbol. */
if (FlagIsClr(symbol->opt_flags, OPT_FLAGS_GPCOFFOPT_MODULE)) {
symbol->value -= Insn_offset;
FlagSet(symbol->opt_flags, OPT_FLAGS_GPCOFFOPT_MODULE);
}
}
}
reloc = reloc->next;
}
}
/*------------------------------------------------------------------------------------------------*/
static void _label_arrays_make(proc_class_t Class)
{
gp_section_t *section;
unsigned i;
if ((section_array == NULL) || (num_sections == 0)) {
return;
}
for (i = 0; i < num_sections; ++i) {
section = section_array[i];
section->num_labels = 0;
section->label_array = gp_symbol_make_label_array(section, Class->org_to_byte_shift,
&(section->num_labels));
}
}
/*------------------------------------------------------------------------------------------------*/
static void
_label_arrays_free(void)
{
gp_section_t *section;
unsigned i;
if ((section_array == NULL) || (num_sections == 0)) {
return;
}
for (i = 0; i < num_sections; ++i) {
section = section_array[i];
if (section->label_array) {
free(section->label_array);
section->label_array = NULL;
}
section->num_labels = 0;
}
}
/*------------------------------------------------------------------------------------------------*/
/* Sets or clears section optimize flag in a given list. */
static void _label_clear_opt_flag(void)
{
gp_section_t *section;
gp_symbol_t *label;
unsigned i;
unsigned j;
if ((section_array == NULL) || (num_sections == 0)) {
return;
}
for (i = 0; i < num_sections; ++i) {
section = section_array[i];
for (j = 0; j < section->num_labels; ++j) {
label = section->label_array[j];
/* This will be modifiable. */
FlagClr(label->opt_flags, OPT_FLAGS_GPCOFFOPT_MODULE);
}
}
}
/*------------------------------------------------------------------------------------------------*/
/* Decrease label addresses in a given list. */
static void _label_array_decrease_addresses(proc_class_t Class, gp_section_t *Section, uint32_t Start_address,
uint32_t Insn_offset)
{
unsigned i;
gp_symbol_t *label;
for (i = 0; i < Section->num_labels; ++i) {
label = Section->label_array[i];
/* Prevents the multiple modifications of symbol. */
if (label->value >= (int)Start_address) {
if (FlagIsClr(label->opt_flags, OPT_FLAGS_GPCOFFOPT_MODULE)) {
label->value -= Insn_offset;
FlagSet(label->opt_flags, OPT_FLAGS_GPCOFFOPT_MODULE);
}
}
}
}
/*------------------------------------------------------------------------------------------------*/
/* Decrease section addresses in a given list. */
static void _sections_decrease_start_address(proc_class_t Class, const gp_section_t *Section, uint32_t Insn_offset,
uint32_t Byte_offset)
{
gp_section_t *section;
unsigned i;
uint32_t byte_address;
uint32_t insn_address;
gp_symvalue_t value_prev;
if ((section_array == NULL) || (num_sections < 1)) {
return;
}
value_prev = 0;
for (i = 0; i < num_sections; ++i) {
section = section_array[i];
/* Prevents the modification of sections on other pages. */
if (section->address > Section->address) {
/* We must not modify an absolute section. */
if (FlagIsClr(section->flags, STYP_ABS)) {
byte_address = section->address - Byte_offset;
insn_address = gp_processor_insn_from_byte_c(Class, byte_address);
gp_mem_b_move(section->data, section->address, byte_address, section->size);
section->address = byte_address;
gp_symbol_t*symbol = &*section->symbol;
if (symbol) {
value_prev = symbol->value;
symbol->value -= Insn_offset;
assert((gp_symvalue_t)insn_address == symbol->value);
}
_label_array_decrease_addresses(Class, section, value_prev, Insn_offset);
}
}
}
}
/*------------------------------------------------------------------------------------------------*/
/* Decrease line number addresses in a given list. */
static void _linenum_decrease_addresses(proc_class_t Class, gp_section_t *First_section,
uint32_t Relocation_page, uint32_t Start_address, uint32_t Byte_offset)
{
gp_section_t *section;
gp_linenum_t *linenum;
section = First_section;
while (section) {
/* We must not modify an absolute section. */
if (FlagIsClr(section->flags, STYP_ABS)) {
linenum = section->line_number_list.first;
while (linenum) {
/* Prevents the modification of linenumbers on other pages. */
if ((_page_addr_from_byte_addr(Class, linenum->address) == Relocation_page) &&
(linenum->address >= Start_address)) {
linenum->address -= Byte_offset;
}
linenum = linenum->next;
}
}
section = section->next;
}
}
/*------------------------------------------------------------------------------------------------*/
/* Destroys an instruction from data memory of given section. */
static void _destroy_insn(proc_class_t Class, gp_section_t *Section, uint32_t Byte_address, uint32_t Byte_length,
const char *Symbol_name)
{
gp_mem_b_delete_area(Section->data, Byte_address, Byte_length);
Section->size -= Byte_length;
}
/*------------------------------------------------------------------------------------------------*/
/* Destroys a Pagesel directive and updates all related addresses. */
static void _destroy_insn_and_update_addr(proc_class_t Class, gp_section_t *First_section, gp_section_t *Section, unsigned Insn_index) {
unsigned i;
uint32_t start_page;
uint32_t byte_addr_curr;
uint32_t byte_length_curr;
uint32_t insn_addr_curr;
uint32_t insn_length_curr;
uint32_t byte_addr_next;
const char *sym_name;
byte_addr_curr = reloc_pipe[Insn_index].reloc_byte_addr;
byte_length_curr = reloc_pipe[Insn_index].reloc_byte_length;
insn_addr_curr = reloc_pipe[Insn_index].reloc_insn_addr;
insn_length_curr = reloc_pipe[Insn_index].reloc_insn_length;
byte_addr_next = byte_addr_curr + byte_length_curr;
start_page = reloc_pipe[Insn_index].reloc_page;
sym_name = (reloc_pipe[Insn_index].relocation->symbol) ?
reloc_pipe[Insn_index].relocation->symbol->name : NULL;
_destroy_insn(Class, Section, byte_addr_curr, byte_length_curr, sym_name);
gp_symbol_delete_by_value(Section->label_array, &Section->num_labels, insn_addr_curr);
gp_coffgen_del_linenum_by_address_area(Section, byte_addr_curr, byte_addr_next - 1);
_linenum_decrease_addresses(Class, First_section, start_page, byte_addr_next, byte_length_curr);
/* Enable modification of address only in program memory. */
_label_clear_opt_flag();
_sections_decrease_start_address(Class, Section, insn_length_curr, byte_length_curr);
_reloc_decrease_addresses(Class, reloc_pipe[Insn_index].relocation->next, start_page, insn_length_curr,
byte_length_curr);
gp_coffgen_del_reloc(Section, reloc_pipe[Insn_index].relocation);
/* Decrease the address of instruction in newer (younger) states. */
for (i = 0; i < Insn_index; ++i) {
reloc_pipe[i].reloc_byte_addr -= byte_length_curr;
reloc_pipe[i].reloc_insn_addr -= insn_length_curr;
}
_reloc_pipe_delete_state(Insn_index);
}
/*------------------------------------------------------------------------------------------------*/
static bool
_insn_isReturn(proc_class_t Class, const gp_section_t *Section, unsigned Byte_addr)
{
uint16_t data;
const insn_t *instruction;
if (Class->find_insn == NULL) {
return false;
}
if (Class->i_memory_get(Section->data, Byte_addr, &data, NULL, NULL) != W_USED_ALL) {
return false;
}
instruction = Class->find_insn(Class, data);
if (instruction == NULL) {
return false;
}
switch (instruction->icode) {
case ICODE_RETFIE:
case ICODE_RETI:
case ICODE_RETIW:
case ICODE_RETLW:
case ICODE_RETP:
case ICODE_RETURN:
return true;
default:
return false;
}
}
/*------------------------------------------------------------------------------------------------*/
/* Analyze and add a pnew relocation state unto the relocation pipe. */
static void _pagesel_reloc_analyze(proc_class_t Class, gp_section_t *Section, gp_reloc_t *Relocation,
unsigned Num_pages)
{
const gp_symbol_t *symbol;
uint16_t data;
uint32_t reloc_byte_addr;
uint32_t reloc_insn_addr;
uint32_t reloc_byte_length;
uint32_t value;
uint32_t reloc_page;
uint32_t target_page;
symbol = Relocation->symbol;
reloc_byte_addr = Section->address + Relocation->address;
value = (uint32_t)symbol->value + Relocation->offset;
if (Class->i_memory_get(Section->data, reloc_byte_addr, &data, NULL, NULL) != W_USED_ALL) {
gp_error("No instruction at 0x%0*X in program memory!", Class->addr_digits, reloc_byte_addr);
assert(0);
}
reloc_insn_addr = gp_processor_insn_from_byte_c(Class, reloc_byte_addr);
reloc_page = gp_processor_page_addr(Class, reloc_insn_addr);
target_page = gp_processor_page_addr(Class, value);
/* No relocation. */
if ((reloc_pipe[1].relocation == NULL) ||
/* A relocation which is not interesting. */
(reloc_pipe[1].state == COPT_NULL) ||
/* A relocation which is too far away. Meanwhile there is at least one other instruction. */
((reloc_pipe[1].reloc_insn_addr + reloc_pipe[1].reloc_insn_length) != reloc_insn_addr)) {
/* Clears the contents of status pipe. */
_reloc_pipe_clear();
}
reloc_pipe[0].relocation = Relocation;
reloc_pipe[0].label = gp_symbol_find_by_value(Section->label_array, Section->num_labels, reloc_insn_addr);
reloc_pipe[0].instruction = (Class->find_insn) ? Class->find_insn(Class, data) : NULL;
reloc_pipe[0].state = COPT_NULL;
reloc_pipe[0].s_protected = ((reloc_pipe[0].label) && (reloc_pipe[0].label->reloc_count_all_section > 1)) ? true : false;
reloc_pipe[0].target_page = target_page;
reloc_pipe[0].reloc_page = reloc_page;
reloc_pipe[0].reloc_byte_addr = reloc_byte_addr;
reloc_pipe[0].reloc_insn_addr = reloc_insn_addr;
reloc_byte_length = 0;
switch (Relocation->type) {
case RELOC_ALL:
break;
case RELOC_CALL:
/* call function */
reloc_pipe[0].state = (reloc_page == target_page) ? COPT_CALL_CURR_PAGE : COPT_CALL_OTHER_PAGE;
reloc_byte_length = 2;
break;
case RELOC_GOTO:
/* goto label */
reloc_pipe[0].state = (reloc_page == target_page) ? COPT_GOTO_CURR_PAGE : COPT_GOTO_OTHER_PAGE;
reloc_byte_length = 2;
break;
case RELOC_LOW:
break;
case RELOC_HIGH: {
/* high(value) */
if ((reloc_pipe[0].instruction) && (reloc_pipe[0].instruction->icode == ICODE_MOVLP)) {
/* movlp high(value) */
reloc_pipe[0].state = (reloc_page == target_page) ? COPT_PAGESEL_CURR_PAGE : COPT_PAGESEL_OTHER_PAGE;
}
reloc_byte_length = 2;
break;
}
case RELOC_UPPER:
case RELOC_P:
case RELOC_BANKSEL:
case RELOC_IBANKSEL:
case RELOC_F:
case RELOC_TRIS:
case RELOC_TRIS_3BIT:
case RELOC_MOVLR:
case RELOC_MOVLB:
case RELOC_GOTO2:
case RELOC_FF1:
case RELOC_FF2:
case RELOC_LFSR1:
case RELOC_LFSR2:
break;
case RELOC_BRA:
/* bra label */
reloc_pipe[0].state = (reloc_page == target_page) ? COPT_BRA14E_CURR_PAGE : COPT_BRA14E_OTHER_PAGE;
reloc_byte_length = 2;
break;
case RELOC_CONDBRA:
case RELOC_ACCESS:
break;
case RELOC_PAGESEL_WREG:
/* PIC12, PIC12E, PIC12I
movlw value
movwf STATUS
OR
PIC14
movlw value
movwf PCLATH */
reloc_pipe[0].state = (reloc_page == target_page) ? COPT_PAGESEL_CURR_PAGE : COPT_PAGESEL_OTHER_PAGE;
reloc_byte_length = Class->pagesel_byte_length(Num_pages, true);
break;
case RELOC_PAGESEL_BITS:
/* PIC12, PIC12E, PIC12I
bcf STATUS, x
bsf STATUS, x
OR
PIC14
bcf PCLATH, x
bsf PCLATH, x */
case RELOC_PAGESEL_MOVLP:
/* PIC14E, PIC14EX
movlp value */
reloc_pipe[0].state = (reloc_page == target_page) ? COPT_PAGESEL_CURR_PAGE : COPT_PAGESEL_OTHER_PAGE;
reloc_byte_length = Class->pagesel_byte_length(Num_pages, false);
break;
/* unimplemented relocations */
case RELOC_PAGESEL:
case RELOC_SCNSZ_LOW:
case RELOC_SCNSZ_HIGH:
case RELOC_SCNSZ_UPPER:
case RELOC_SCNEND_LOW:
case RELOC_SCNEND_HIGH:
case RELOC_SCNEND_UPPER:
case RELOC_SCNEND_LFSR1:
case RELOC_SCNEND_LFSR2:
default: {
if (symbol->name) {
gp_error("Unimplemented relocation = %s (%u) in section \"%s\" at symbol \"%s\".",
gp_coffgen_reloc_type_to_str(Relocation->type),
Relocation->type, Section->name, symbol->name);
}
else {
gp_error("Unimplemented relocation = %s (%u) in section \"%s\".",
gp_coffgen_reloc_type_to_str(Relocation->type),
Relocation->type, Section->name);
}
assert(0);
}
}
reloc_pipe[0].reloc_byte_length = reloc_byte_length;
reloc_pipe[0].reloc_insn_length = gp_processor_insn_from_byte_c(Class, reloc_byte_length);
}
/*------------------------------------------------------------------------------------------------*/
/* If possible according to the rules, then removes a Pagesel directive. */
static bool
_pagesel_remove(proc_class_t Class, gp_section_t *First_section, gp_section_t *Section,
bool Completion)
{
unsigned saturation;
unsigned byte_addr_next;
saturation = !!reloc_pipe[0].relocation;
saturation += !!reloc_pipe[1].relocation;
saturation += !!reloc_pipe[2].relocation;
saturation += !!reloc_pipe[3].relocation;
if (saturation == 0) {
/* The State Pipe is empty. */
return false;
}
if (Completion) {
/* This is the last relocation on chain (a code section). */
if ((reloc_pipe[0].state == COPT_PAGESEL_CURR_PAGE) && (!reloc_pipe[0].s_protected)) {
byte_addr_next = reloc_pipe[0].reloc_byte_addr + reloc_pipe[0].reloc_byte_length;
if (_insn_isReturn(Class, Section, byte_addr_next)) {
/*
reloc_pipe[0] pagesel current_page <-- UNNECESSARY if not PROTECTED
byte_addr_next: return (or these: retfie, retlw, reti, retp)
*/
_destroy_insn_and_update_addr(Class, First_section, Section, 0);
_reloc_pipe_shift(false);
return true;
}
}
}
if (saturation >= 2) {
/* The saturation of State Pipe at least 1/2. */
if ((reloc_pipe[1].state == COPT_CALL_CURR_PAGE) &&
(reloc_pipe[0].state == COPT_PAGESEL_CURR_PAGE) && (!reloc_pipe[0].s_protected)) {
/*
reloc_pipe[1] call function_on_current_page
reloc_pipe[0] pagesel current_page <--------- UNNECESSARY if not PROTECTED
*/
_destroy_insn_and_update_addr(Class, First_section, Section, 0);
}
else if ((reloc_pipe[1].state == COPT_PAGESEL_CURR_PAGE) && (!reloc_pipe[1].s_protected) &&
(reloc_pipe[0].state == COPT_PAGESEL_OTHER_PAGE)) {
/*
reloc_pipe[1] pagesel current_page <------- UNNECESSARY if not PROTECTED
reloc_pipe[0] pagesel other_page
*/
_destroy_insn_and_update_addr(Class, First_section, Section, 1);
}
else if ((reloc_pipe[1].state == COPT_PAGESEL_CURR_PAGE) && (!reloc_pipe[1].s_protected) &&
(reloc_pipe[0].state & COPT_ABS_BRANCH_CURR_PAGE_MASK)) {
/*
reloc_pipe[1] pagesel current_page <------ UNNECESSARY if not PROTECTED
reloc_pipe[0] goto label_on_current_page
OR
reloc_pipe[1] pagesel current_page <--------- UNNECESSARY if not PROTECTED
reloc_pipe[0] call function_on_current_page
*/
_destroy_insn_and_update_addr(Class, First_section, Section, 1);
}
else if ((reloc_pipe[1].state & COPT_PAGESEL_MASK) && (!reloc_pipe[1].s_protected) &&
(reloc_pipe[0].state & COPT_REL_BRANCH_MASK)) {
/*
The 'bra' is a relative jump, no need to Pagesel.
reloc_pipe[1] pagesel current_or_other_page <----- UNNECESSARY if not PROTECTED
reloc_pipe[0] bra label_on_current_or_other_page
*/
gp_warning("Strange relocation = %s (%u) with = %s (%u) in section \"%s\" at symbol \"%s\".",
gp_coffgen_reloc_type_to_str(reloc_pipe[1].relocation->type), reloc_pipe[1].relocation->type,
gp_coffgen_reloc_type_to_str(reloc_pipe[0].relocation->type), reloc_pipe[0].relocation->type,
Section->name, reloc_pipe[0].relocation->symbol->name);
_destroy_insn_and_update_addr(Class, First_section, Section, 1);
}
} /* if (saturation >= 2) */
if (saturation >= 3) {
/* The saturation of State Pipe at least 3/4. */
if ((reloc_pipe[2].state == COPT_CALL_OTHER_PAGE) &&
(reloc_pipe[1].state == COPT_PAGESEL_CURR_PAGE) &&
(reloc_pipe[0].state == COPT_PAGESEL_CURR_PAGE)) {
/*
reloc_pipe[2] call function_on_other_page
reloc_pipe[1] pagesel current_page <------- clear PROTECTED
reloc_pipe[0] pagesel current_page <------- set PROTECTED
*/
reloc_pipe[1].s_protected = false;
reloc_pipe[0].s_protected = true;
}
else if ((reloc_pipe[2].state == COPT_CALL_OTHER_PAGE) &&
(reloc_pipe[1].state == COPT_PAGESEL_OTHER_PAGE) && (!reloc_pipe[1].s_protected) &&
(reloc_pipe[2].target_page == reloc_pipe[1].target_page) &&
(reloc_pipe[0].state == COPT_CALL_OTHER_PAGE) &&
(reloc_pipe[1].target_page == reloc_pipe[0].target_page)) {
/*
reloc_pipe[2] call function_on_other_page
reloc_pipe[1] pagesel other_page <--------- UNNECESSARY if not PROTECTED
reloc_pipe[0] call function_on_other_page
*/
_destroy_insn_and_update_addr(Class, First_section, Section, 1);
}
else if ((reloc_pipe[2].state == COPT_CALL_CURR_PAGE) &&
(reloc_pipe[1].state == COPT_PAGESEL_CURR_PAGE) && (!reloc_pipe[1].s_protected) &&
(reloc_pipe[0].state == COPT_PAGESEL_CURR_PAGE)) {
/*
reloc_pipe[2] call function_on_current_page
reloc_pipe[1] pagesel current_page <--------- UNNECESSARY if not PROTECTED
reloc_pipe[0] pagesel current_page
*/
_destroy_insn_and_update_addr(Class, First_section, Section, 1);
}
else if ((reloc_pipe[2].state == COPT_CALL_CURR_PAGE) &&
(reloc_pipe[1].state == COPT_PAGESEL_CURR_PAGE) && (!reloc_pipe[1].s_protected) &&
(reloc_pipe[0].state == COPT_CALL_CURR_PAGE)) {
/*
reloc_pipe[2] call function_on_current_page
reloc_pipe[1] pagesel current_page <--------- UNNECESSARY if not PROTECTED
reloc_pipe[0] call function_on_current_page
*/
_destroy_insn_and_update_addr(Class, First_section, Section, 1);
}
} /* if (saturation >= 3) */
if (saturation == 4) {
/* The State Pipe is full. */
if ((reloc_pipe[3].state == COPT_CALL_OTHER_PAGE) &&
(reloc_pipe[2].state == COPT_PAGESEL_CURR_PAGE) && (!reloc_pipe[2].s_protected) &&
(reloc_pipe[1].state == COPT_PAGESEL_CURR_PAGE) &&
(reloc_pipe[0].state == COPT_CALL_CURR_PAGE)) {
/*
reloc_pipe[3] call function_on_other_page
reloc_pipe[2] pagesel current_page <------- UNNECESSARY if not PROTECTED
reloc_pipe[1] pagesel current_page
reloc_pipe[0] call function_on_current_page
*/
_destroy_insn_and_update_addr(Class, First_section, Section, 2);
}
else if ((reloc_pipe[3].state == COPT_CALL_OTHER_PAGE) &&
(reloc_pipe[2].state == COPT_PAGESEL_CURR_PAGE) && (!reloc_pipe[2].s_protected) &&
(reloc_pipe[1].state == COPT_PAGESEL_OTHER_PAGE) &&
(reloc_pipe[0].state == COPT_CALL_OTHER_PAGE)) {
/*
reloc_pipe[3] call function_on_other_page
reloc_pipe[2] pagesel current_page <------- UNNECESSARY if not PROTECTED
reloc_pipe[1] pagesel other_page
reloc_pipe[0] call function_on_other_page
*/
_destroy_insn_and_update_addr(Class, First_section, Section, 2);
}
} /* if (saturation == 4) */
return true;
}
/*------------------------------------------------------------------------------------------------*/
/* Deletes the unnecessary Pagesel directives from an object. */
FUNC(void) gp_coffopt_remove_unnecessary_pagesel(gp_object_t *Object) {
proc_class_t pclass;
gp_section_t *first_section;
gp_section_t *section;
gp_reloc_t *reloc_curr;
gp_reloc_t *reloc_next;
unsigned num_pages;
unsigned i;
pclass = Object->pclass;
/* Only case of PIC12 and PIC14 families. */
if ((pclass != PROC_CLASS_PIC12) && (pclass != PROC_CLASS_PIC12E) &&
(pclass != PROC_CLASS_PIC12I) && (pclass != PROC_CLASS_SX) &&
(pclass != PROC_CLASS_PIC14) && (pclass != PROC_CLASS_PIC14E) &&
(pclass != PROC_CLASS_PIC14EX)) {
return;
}
section_array = NULL;
num_sections = 0;
register_array = NULL;
num_registers = 0;
gp_debug("Removing unnecessary pagesel instructions.");
_reloc_pipe_clear();
num_pages = gp_processor_num_pages(Object->processor);
first_section = Object->section_list.first;
section = first_section;
while (section) {
_reloc_pipe_clear();
if (gp_coffgen_section_has_data(section)) {
num_sections = 0;
section_array = gp_coffgen_make_section_array(Object, &num_sections,
gp_processor_page_addr(pclass, gp_processor_insn_from_byte_c(pclass, section->address)),
STYP_ROM_AREA);
_label_arrays_make(pclass);
if (section->label_array) {
reloc_curr = section->relocation_list.first;
if (reloc_curr) {
i = 0;
do {
reloc_next = reloc_curr->next;
_pagesel_reloc_analyze(pclass, section, reloc_curr, num_pages);
reloc_curr = reloc_next;
_pagesel_remove(pclass, first_section, section, (bool)!reloc_curr);
_reloc_pipe_shift(true);
++i;
} while (reloc_curr);
}
}
_label_arrays_free();
if (section_array) {
free(section_array);
}
}
section = section->next;
} /* while (section) */
}
/*------------------------------------------------------------------------------------------------*/
/* Analyze and add a pnew relocation state unto the relocation pipe. */
static bool
_banksel_reloc_analyze(proc_class_t Class, pic_processor_t Processor, gp_section_t *Section,
gp_reloc_t *Relocation, unsigned Num_pages)
{
const gp_symbol_t *symbol;
uint16_t data;
uint32_t reloc_byte_addr;
uint32_t reloc_insn_addr;
uint32_t reloc_byte_length;
uint32_t reloc_page;
uint32_t value;
uint32_t ram_bank;
bool need_clear;
bool there_is_banksel;
symbol = Relocation->symbol;
reloc_byte_addr = Section->address + Relocation->address;
reloc_insn_addr = gp_processor_insn_from_byte_c(Class, reloc_byte_addr);
value = (uint32_t)symbol->value + Relocation->offset;
reloc_page = gp_processor_page_addr(Class, reloc_insn_addr);
if (Class->i_memory_get(Section->data, reloc_byte_addr, &data, NULL, NULL) != W_USED_ALL) {
gp_error("No instruction at 0x%0*X in program memory!", Class->addr_digits, reloc_byte_addr);
assert(0);
}
reloc_byte_length = 0;
ram_bank = 0;
need_clear = false;
there_is_banksel = false;
switch (Relocation->type) {
case RELOC_ALL:
break;
case RELOC_CALL:
case RELOC_GOTO:
need_clear = true;
break;
case RELOC_LOW:
case RELOC_HIGH:
case RELOC_UPPER:
case RELOC_P:
break;
case RELOC_BANKSEL:
ram_bank = gp_processor_bank_addr(Processor, value);
reloc_byte_length = Class->banksel_byte_length(Num_pages, false);
there_is_banksel = true;
break;
case RELOC_IBANKSEL:
break;
case RELOC_F:
case RELOC_TRIS:
case RELOC_TRIS_3BIT:
case RELOC_MOVLR:
break;
case RELOC_MOVLB:
ram_bank = gp_processor_bank_addr(Processor, value);
reloc_byte_length = 2;
there_is_banksel = true;
break;
case RELOC_GOTO2:
need_clear = true;
break;
case RELOC_FF1:
case RELOC_FF2:
case RELOC_LFSR1:
case RELOC_LFSR2:
break;
case RELOC_BRA:
case RELOC_CONDBRA:
need_clear = true;
break;
case RELOC_ACCESS:
case RELOC_PAGESEL_WREG:
case RELOC_PAGESEL_BITS:
case RELOC_PAGESEL_MOVLP:
break;
/* unimplemented relocations */
case RELOC_PAGESEL:
case RELOC_SCNSZ_LOW:
case RELOC_SCNSZ_HIGH:
case RELOC_SCNSZ_UPPER:
case RELOC_SCNEND_LOW:
case RELOC_SCNEND_HIGH:
case RELOC_SCNEND_UPPER:
case RELOC_SCNEND_LFSR1:
case RELOC_SCNEND_LFSR2:
default: {
if (symbol->name) {
gp_error("Unimplemented relocation = %s (%u) in section \"%s\" at symbol \"%s\".",
gp_coffgen_reloc_type_to_str(Relocation->type),
Relocation->type, Section->name, symbol->name);
}
else {
gp_error("Unimplemented relocation = %s (%u) in section \"%s\".",
gp_coffgen_reloc_type_to_str(Relocation->type),
Relocation->type, Section->name);
}
assert(0);
}
}
if (need_clear) {
_reloc_pipe_clear();
return false;
}
if (there_is_banksel) {
_reloc_pipe_shift(true);
reloc_pipe[0].relocation = Relocation;
reloc_pipe[0].label = gp_symbol_find_by_value(Section->label_array, Section->num_labels, reloc_insn_addr);
reloc_pipe[0].instruction = (Class->find_insn) ? Class->find_insn(Class, data) : NULL;
reloc_pipe[0].state = COPT_BANKSEL;
reloc_pipe[0].s_protected = ((reloc_pipe[0].label) && (reloc_pipe[0].label->reloc_count_all_section > 1)) ? true : false;
reloc_pipe[0].reloc_page = reloc_page;
reloc_pipe[0].reloc_byte_addr = reloc_byte_addr;
reloc_pipe[0].reloc_insn_addr = reloc_insn_addr;
reloc_pipe[0].reloc_byte_length = reloc_byte_length;
reloc_pipe[0].reloc_insn_length = gp_processor_insn_from_byte_c(Class, reloc_byte_length);
reloc_pipe[0].ram_bank = ram_bank;
if (!first_banksel) {
/* This is the first Banksel directive of section. Absolutely must protect it. */
reloc_pipe[0].s_protected = true;
first_banksel = true;
}
return true;
}
return false;
}
/*------------------------------------------------------------------------------------------------*/
/* If possible according to the rules, then removes a Pagesel directive. */
static bool
_banksel_remove(proc_class_t Class, gp_section_t *First_section, gp_section_t *Section)
{
unsigned saturation;
saturation = !!reloc_pipe[0].relocation;
saturation += !!reloc_pipe[1].relocation;
saturation += !!reloc_pipe[2].relocation;
saturation += !!reloc_pipe[3].relocation;
if (saturation == 0) {
/* The State Pipe is empty. */
return false;
}
if (saturation >= 2) {
if ((reloc_pipe[1].state == COPT_BANKSEL) &&
(reloc_pipe[0].state == COPT_BANKSEL) &&
(reloc_pipe[1].ram_bank == reloc_pipe[0].ram_bank)) {
if (!reloc_pipe[1].s_protected) {
/*
reloc_pipe[1] banksel Z <--------- UNNECESSARY if not PROTECTED
reloc_pipe[0] banksel Z
*/
_destroy_insn_and_update_addr(Class, First_section, Section, 1);
}
else if (!reloc_pipe[0].s_protected) {
/*
reloc_pipe[1] banksel Z
reloc_pipe[0] banksel Z <--------- UNNECESSARY if not PROTECTED
*/
_destroy_insn_and_update_addr(Class, First_section, Section, 0);
}
}
} /* if (saturation >= 2) */
return true;
}
/*------------------------------------------------------------------------------------------------*/
/* Deletes the unnecessary Banksel directives from an object. */
FUNC(void) gp_coffopt_remove_unnecessary_banksel(gp_object_t *Object) {
proc_class_t pclass;
pic_processor_t processor;
gp_section_t *first_section;
gp_section_t *section;
gp_reloc_t *reloc_curr;
gp_reloc_t *reloc_next;
unsigned num_banks;
unsigned i;
bool may_remove;
pclass = Object->pclass;
processor = Object->processor;
if ((pclass != PROC_CLASS_PIC12) && (pclass != PROC_CLASS_PIC12E) &&
(pclass != PROC_CLASS_PIC12I) && (pclass != PROC_CLASS_SX) &&
(pclass != PROC_CLASS_PIC14) && (pclass != PROC_CLASS_PIC14E) &&
(pclass != PROC_CLASS_PIC14EX) && (pclass != PROC_CLASS_PIC16) &&
(pclass != PROC_CLASS_PIC16E)) {
return;
}
section_array = NULL;
num_sections = 0;
register_array = NULL;
num_registers = 0;
gp_debug("Removing unnecessary banksel instructions.");
num_registers = 0;
register_array = gp_symbol_make_register_array(Object, num_registers);
if (register_array == NULL) {
return;
}
_reloc_pipe_clear();
num_banks = gp_processor_num_banks(Object->processor);
first_section = Object->section_list.first;
section = first_section;
while (section) {
first_banksel = false;
_reloc_pipe_clear();
if (gp_coffgen_section_has_data(section)) {
num_sections = 0;
section_array = gp_coffgen_make_section_array(Object, &num_sections,
gp_processor_page_addr(pclass, gp_processor_insn_from_byte_c(pclass, section->address)),
STYP_ROM_AREA);
_label_arrays_make(pclass);
reloc_curr = section->relocation_list.first;
if (reloc_curr) {
i = 0;
while (reloc_curr) {
reloc_next = reloc_curr->next;
may_remove = _banksel_reloc_analyze(pclass, processor, section, reloc_curr, num_banks);
if (may_remove) {
_banksel_remove(pclass, first_section, section);
}
reloc_curr = reloc_next;
++i;
}
}
_label_arrays_free();
if (section_array) {
free(section_array);
}
} /* if (gp_coffgen_section_has_data(section)) */
section = section->next;
} /* while (section) */
free(register_array);
}
Detected encoding: UTF-8 | 0
|