blob: 4d2e1bd16dc2bf2233e36cc0e32108431622c4c4 [file] [log] [blame]
/*
* Basic optimizer (equivalent to the NASM 2-pass 'no optimizer' design)
*
* Copyright (C) 2001 Peter Johnson
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define YASM_LIB_INTERNAL
#define YASM_BC_INTERNAL
#include <libyasm.h>
/*@unused@*/ RCSID("$IdPath$");
#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(yasm_section *sect,
/*@unused@*/ /*@null@*/ void *d);
static /*@null@*/ yasm_intnum *
basic_optimize_calc_bc_dist_1(yasm_section *sect,
/*@null@*/ yasm_bytecode *precbc1,
/*@null@*/ yasm_bytecode *precbc2)
{
unsigned int dist;
yasm_intnum *intn;
if (yasm_section_get_opt_flags(sect) == SECTFLAG_NONE) {
/* Section not started. Optimize it (recursively). */
basic_optimize_section_1(sect, NULL);
}
/* 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 (precbc2) {
if (precbc2->opt_flags == BCFLAG_DONE) {
dist = precbc2->offset + precbc2->len;
if (precbc1) {
if (precbc1->opt_flags == BCFLAG_DONE) {
if (dist < precbc1->offset + precbc1->len) {
intn = yasm_intnum_new_uint(precbc1->offset +
precbc1->len - dist);
yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL,
precbc1->line);
return intn;
}
dist -= precbc1->offset + precbc1->len;
} else {
return NULL;
}
}
return yasm_intnum_new_uint(dist);
} else {
return NULL;
}
} else {
if (precbc1) {
if (precbc1->opt_flags == BCFLAG_DONE) {
intn = yasm_intnum_new_uint(precbc1->offset + precbc1->len);
yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL, precbc1->line);
return intn;
} else {
return NULL;
}
} else {
return yasm_intnum_new_uint(0);
}
}
}
typedef struct basic_optimize_data {
/*@observer@*/ yasm_bytecode *precbc;
/*@observer@*/ const yasm_section *sect;
int saw_unknown;
} basic_optimize_data;
static int
basic_optimize_bytecode_1(/*@observer@*/ yasm_bytecode *bc, void *d)
{
basic_optimize_data *data = (basic_optimize_data *)d;
yasm_bc_resolve_flags bcr_retval;
/* 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).
*/
bcr_retval = yasm_bc_resolve(bc, 0, data->sect,
basic_optimize_calc_bc_dist_1);
if (bcr_retval & YASM_BC_RESOLVE_UNKNOWN_LEN) {
if (!(bcr_retval & YASM_BC_RESOLVE_ERROR))
yasm__error(bc->line, N_("circular reference detected."));
data->saw_unknown = -1;
return 0;
}
bc->opt_flags = BCFLAG_DONE;
return 0;
}
static int
basic_optimize_section_1(yasm_section *sect, /*@null@*/ void *d)
{
/*@null@*/ int *saw_unknown = (int *)d;
basic_optimize_data data;
unsigned long flags;
int retval;
data.precbc = NULL;
data.sect = sect;
data.saw_unknown = 0;
/* Don't even bother if we're in-progress or done. */
flags = yasm_section_get_opt_flags(sect);
if (flags == SECTFLAG_INPROGRESS)
return 1;
if (flags == SECTFLAG_DONE)
return 0;
yasm_section_set_opt_flags(sect, SECTFLAG_INPROGRESS);
retval = yasm_bcs_traverse(yasm_section_get_bytecodes(sect), &data,
basic_optimize_bytecode_1);
if (retval != 0)
return retval;
if (data.saw_unknown != 0 && saw_unknown)
*saw_unknown = data.saw_unknown;
yasm_section_set_opt_flags(sect, SECTFLAG_DONE);
return 0;
}
static int
basic_optimize_bytecode_2(/*@observer@*/ yasm_bytecode *bc, /*@null@*/ void *d)
{
basic_optimize_data *data = (basic_optimize_data *)d;
assert(data != NULL);
if (bc->opt_flags != BCFLAG_DONE)
yasm_internal_error(N_("Optimizer pass 1 missed a bytecode!"));
if (!data->precbc)
bc->offset = 0;
else
bc->offset = data->precbc->offset + data->precbc->len;
data->precbc = bc;
if (yasm_bc_resolve(bc, 1, data->sect, yasm_common_calc_bc_dist) < 0)
return -1;
return 0;
}
static int
basic_optimize_section_2(yasm_section *sect, /*@unused@*/ /*@null@*/ void *d)
{
basic_optimize_data data;
data.precbc = NULL;
data.sect = sect;
if (yasm_section_get_opt_flags(sect) != SECTFLAG_DONE)
yasm_internal_error(N_("Optimizer pass 1 missed a section!"));
return yasm_bcs_traverse(yasm_section_get_bytecodes(sect), &data,
basic_optimize_bytecode_2);
}
static void
basic_optimize(yasm_sectionhead *sections)
{
int saw_unknown = 0;
/* 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 (yasm_sections_traverse(sections, &saw_unknown,
basic_optimize_section_1) < 0 ||
saw_unknown != 0)
return;
/* Check completion of all sections and save bytecode changes */
yasm_sections_traverse(sections, NULL, basic_optimize_section_2);
}
/* Define optimizer structure -- see optimizer.h for details */
yasm_optimizer yasm_basic_LTX_optimizer = {
"Only the most basic optimizations",
"basic",
basic_optimize
};