mirror of https://github.com/yasm/yasm.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
272 lines
7.6 KiB
272 lines
7.6 KiB
/* |
|
* Basic optimizer (equivalent to the NASM 2-pass 'no optimizer' design) |
|
* |
|
* Copyright (C) 2001 Peter Johnson |
|
* |
|
* This file is part of YASM. |
|
* |
|
* YASM is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation; either version 2 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* YASM is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program; if not, write to the Free Software |
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
*/ |
|
#include "util.h" |
|
/*@unused@*/ RCSID("$IdPath$"); |
|
|
|
#include "errwarn.h" |
|
#include "intnum.h" |
|
#include "expr.h" |
|
#include "symrec.h" |
|
|
|
#include "bytecode.h" |
|
#include "section.h" |
|
|
|
#include "bc-int.h" |
|
|
|
#include "optimizer.h" |
|
|
|
|
|
#define SECTFLAG_NONE 0UL |
|
#define SECTFLAG_INPROGRESS (1UL<<0) |
|
#define SECTFLAG_DONE (1UL<<1) |
|
|
|
#define BCFLAG_NONE 0UL |
|
#define BCFLAG_INPROGRESS (1UL<<0) |
|
#define BCFLAG_DONE (1UL<<1) |
|
|
|
static int basic_optimize_section_1(section *sect, |
|
/*@unused@*/ /*@null@*/ void *d); |
|
|
|
static /*@only@*/ /*@null@*/ intnum * |
|
basic_optimize_resolve_label(symrec *sym, int withstart) |
|
{ |
|
/*@dependent@*/ section *sect; |
|
/*@dependent@*/ /*@null@*/ bytecode *precbc; |
|
/*@null@*/ bytecode *bc; |
|
/*@null@*/ expr *startexpr; |
|
/*@dependent@*/ /*@null@*/ const intnum *start; |
|
unsigned long startval = 0; |
|
|
|
if (!symrec_get_label(sym, §, &precbc)) |
|
return NULL; |
|
|
|
/* determine actual bc from preceding bc (how labels are stored) */ |
|
if (!precbc) |
|
bc = bcs_first(section_get_bytecodes(sect)); |
|
else |
|
bc = bcs_next(precbc); |
|
assert(bc != NULL); |
|
|
|
if (section_get_opt_flags(sect) == SECTFLAG_NONE) { |
|
/* Section not started. Optimize it (recursively). */ |
|
basic_optimize_section_1(sect, NULL); |
|
} |
|
|
|
/* Figure out the starting offset of the entire section */ |
|
if (withstart || section_is_absolute(sect)) { |
|
startexpr = expr_copy(section_get_start(sect)); |
|
assert(startexpr != NULL); |
|
expr_expand_labelequ(startexpr, sect, 1, basic_optimize_resolve_label); |
|
start = expr_get_intnum(&startexpr); |
|
if (!start) |
|
return NULL; |
|
startval = intnum_get_uint(start); |
|
expr_delete(startexpr); |
|
} |
|
|
|
/* If a section is done, the following will always succeed. If it's in- |
|
* progress, this will fail if the bytecode comes AFTER the current one. |
|
*/ |
|
if (precbc && precbc->opt_flags == BCFLAG_DONE) |
|
return intnum_new_int(startval + precbc->offset + precbc->len); |
|
if (bc->opt_flags == BCFLAG_DONE) |
|
return intnum_new_int(startval + bc->offset); |
|
|
|
return NULL; |
|
} |
|
|
|
static /*@only@*/ /*@null@*/ intnum * |
|
basic_optimize_resolve_label_2(symrec *sym, int withstart) |
|
{ |
|
/*@dependent@*/ section *sect; |
|
/*@dependent@*/ /*@null@*/ bytecode *precbc; |
|
/*@null@*/ bytecode *bc; |
|
/*@null@*/ expr *startexpr; |
|
/*@dependent@*/ /*@null@*/ const intnum *start; |
|
unsigned long startval = 0; |
|
|
|
if (!symrec_get_label(sym, §, &precbc)) |
|
return NULL; |
|
|
|
/* determine actual bc from preceding bc (how labels are stored) */ |
|
if (!precbc) |
|
bc = bcs_first(section_get_bytecodes(sect)); |
|
else |
|
bc = bcs_next(precbc); |
|
assert(bc != NULL); |
|
|
|
/* Figure out the starting offset of the entire section */ |
|
if (withstart || section_is_absolute(sect)) { |
|
startexpr = expr_copy(section_get_start(sect)); |
|
assert(startexpr != NULL); |
|
expr_expand_labelequ(startexpr, sect, 1, |
|
basic_optimize_resolve_label_2); |
|
start = expr_get_intnum(&startexpr); |
|
if (!start) |
|
return NULL; |
|
startval = intnum_get_uint(start); |
|
expr_delete(startexpr); |
|
} |
|
|
|
/* If a section is done, the following will always succeed. If it's in- |
|
* progress, this will fail if the bytecode comes AFTER the current one. |
|
*/ |
|
if (precbc) |
|
return intnum_new_int(startval + precbc->offset + precbc->len); |
|
else |
|
return intnum_new_int(startval + bc->offset); |
|
} |
|
|
|
typedef struct basic_optimize_data { |
|
bytecode *precbc; |
|
const section *sect; |
|
} basic_optimize_data; |
|
|
|
static int |
|
basic_optimize_bytecode_1(bytecode *bc, void *d) |
|
{ |
|
basic_optimize_data *data = (basic_optimize_data *)d; |
|
|
|
/* Don't even bother if we're in-progress or done. */ |
|
if (bc->opt_flags == BCFLAG_INPROGRESS) |
|
return 1; |
|
if (bc->opt_flags == BCFLAG_DONE) |
|
return 0; |
|
|
|
bc->opt_flags = BCFLAG_INPROGRESS; |
|
|
|
if (!data->precbc) |
|
bc->offset = 0; |
|
else |
|
bc->offset = data->precbc->offset + data->precbc->len; |
|
data->precbc = bc; |
|
|
|
/* We're doing just a single pass, so essentially ignore whether the size |
|
* is minimum or not, and just check for indeterminate length (indicative |
|
* of circular reference). |
|
*/ |
|
if (bc_resolve(bc, 0, data->sect, basic_optimize_resolve_label) < 0) { |
|
ErrorAt(bc->line, _("Circular reference detected.")); |
|
return -1; |
|
} |
|
|
|
bc->opt_flags = BCFLAG_DONE; |
|
|
|
return 0; |
|
} |
|
|
|
static int |
|
basic_optimize_section_1(section *sect, /*@unused@*/ /*@null@*/ void *d) |
|
{ |
|
basic_optimize_data data; |
|
unsigned long flags; |
|
int retval; |
|
|
|
data.precbc = NULL; |
|
data.sect = sect; |
|
|
|
/* Don't even bother if we're in-progress or done. */ |
|
flags = section_get_opt_flags(sect); |
|
if (flags == SECTFLAG_INPROGRESS) |
|
return 1; |
|
if (flags == SECTFLAG_DONE) |
|
return 0; |
|
|
|
section_set_opt_flags(sect, SECTFLAG_INPROGRESS); |
|
|
|
retval = bcs_traverse(section_get_bytecodes(sect), &data, |
|
basic_optimize_bytecode_1); |
|
if (retval != 0) |
|
return retval; |
|
|
|
section_set_opt_flags(sect, SECTFLAG_DONE); |
|
|
|
return 0; |
|
} |
|
|
|
static int |
|
basic_optimize_bytecode_2(bytecode *bc, /*@null@*/ void *d) |
|
{ |
|
basic_optimize_data *data = (basic_optimize_data *)d; |
|
|
|
assert(data != NULL); |
|
|
|
if (bc->opt_flags != BCFLAG_DONE) { |
|
InternalError(_("Optimizer pass 1 missed a bytecode!")); |
|
return -1; |
|
} |
|
|
|
if (!data->precbc) |
|
bc->offset = 0; |
|
else |
|
bc->offset = data->precbc->offset + data->precbc->len; |
|
data->precbc = bc; |
|
|
|
if (bc_resolve(bc, 1, data->sect, basic_optimize_resolve_label_2) < 0) |
|
return -1; |
|
return 0; |
|
} |
|
|
|
static int |
|
basic_optimize_section_2(section *sect, /*@unused@*/ /*@null@*/ void *d) |
|
{ |
|
basic_optimize_data data; |
|
|
|
data.precbc = NULL; |
|
data.sect = sect; |
|
|
|
if (section_get_opt_flags(sect) != SECTFLAG_DONE) { |
|
InternalError(_("Optimizer pass 1 missed a section!")); |
|
return -1; |
|
} |
|
return bcs_traverse(section_get_bytecodes(sect), &data, |
|
basic_optimize_bytecode_2); |
|
} |
|
|
|
static void |
|
basic_optimize(sectionhead *sections) |
|
{ |
|
/* Optimization process: (essentially NASM's pass 1) |
|
* Determine the size of all bytecodes. |
|
* Forward references are /not/ resolved (only backward references are |
|
* computed and sized). |
|
* Check "critical" expressions (must be computable on the first pass, |
|
* i.e. depend only on symbols before it). |
|
* Differences from NASM: |
|
* - right-hand side of EQU is /not/ a critical expr (as the entire file |
|
* has already been parsed, we know all their values at this point). |
|
* - not strictly top->bottom scanning; we scan through a section and |
|
* hop to other sections as necessary. |
|
*/ |
|
if (sections_traverse(sections, NULL, basic_optimize_section_1) < 0) |
|
return; |
|
|
|
/* Check completion of all sections and save bytecode changes */ |
|
sections_traverse(sections, NULL, basic_optimize_section_2); |
|
} |
|
|
|
/* Define optimizer structure -- see optimizer.h for details */ |
|
optimizer basic_optimizer = { |
|
"Only the most basic optimizations", |
|
"basic", |
|
basic_optimize |
|
};
|
|
|