Logo Search packages:      
Sourcecode: yasm version File versions  Download package

basic-optimizer.c

/*
 * 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.
 */
#include <util.h>
/*@unused@*/ RCSID("$Id: basic-optimizer.c 1137 2004-09-04 01:24:57Z peter $");

#define YASM_LIB_INTERNAL
#define YASM_BC_INTERNAL
#include <libyasm.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(yasm_section *sect,
                            /*@unused@*/ /*@null@*/ void *d);

static /*@null@*/ yasm_intnum *
basic_optimize_calc_bc_dist_1(yasm_bytecode *precbc1, yasm_bytecode *precbc2)
{
    unsigned int dist;
    yasm_intnum *intn;

    if (precbc1->section != precbc2->section)
      yasm_internal_error(N_("Trying to calc_bc_dist between sections"));

    if (yasm_section_get_opt_flags(precbc1->section) == SECTFLAG_NONE) {
      /* Section not started.  Optimize it (recursively). */
      basic_optimize_section_1(precbc1->section, 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 != yasm_section_bcs_first(precbc2->section)) {
      if (precbc2->opt_flags == BCFLAG_DONE) {
          dist = precbc2->offset + precbc2->len;
          if (precbc1 != yasm_section_bcs_first(precbc1->section)) {
            if (precbc1->opt_flags == BCFLAG_DONE) {
                if (dist < precbc1->offset + precbc1->len) {
                  intn = yasm_intnum_create_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_create_uint(dist);
      } else {
          return NULL;
      }
    } else {
      if (precbc1 != yasm_section_bcs_first(precbc1->section)) {
          if (precbc1->opt_flags == BCFLAG_DONE) {
            intn = yasm_intnum_create_uint(precbc1->offset + precbc1->len);
            yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL, precbc1->line);
            return intn;
          } else {
            return NULL;
          }
      } else {
          return yasm_intnum_create_uint(0);
      }
    }
}

typedef struct basic_optimize_data {
    /*@observer@*/ yasm_bytecode *precbc;
    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;

    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, 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 = yasm_section_bcs_first(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_section_bcs_traverse(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!"));

    bc->offset = data->precbc->offset + data->precbc->len;
    data->precbc = bc;

    if (yasm_bc_resolve(bc, 1, 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 = yasm_section_bcs_first(sect);

    if (yasm_section_get_opt_flags(sect) != SECTFLAG_DONE)
      yasm_internal_error(N_("Optimizer pass 1 missed a section!"));

    return yasm_section_bcs_traverse(sect, &data, basic_optimize_bytecode_2);
}

static void
basic_optimize(yasm_object *object)
{
    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_object_sections_traverse(object, &saw_unknown,
                              basic_optimize_section_1) < 0 ||
      saw_unknown != 0)
      return;

    /* Check completion of all sections and save bytecode changes */
    yasm_object_sections_traverse(object, NULL, basic_optimize_section_2);
}

/* Define optimizer structure -- see optimizer.h for details */
yasm_optimizer_module yasm_basic_LTX_optimizer = {
    YASM_OPTIMIZER_VERSION,
    "Only the most basic optimizations",
    "basic",
    basic_optimize
};

Generated by  Doxygen 1.6.0   Back to index