1/*
2 * Section utility functions
3 *
4 *  Copyright (C) 2001-2007  Peter Johnson
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27#include "util.h"
28
29#include <limits.h>
30
31#include "libyasm-stdint.h"
32#include "coretype.h"
33#include "hamt.h"
34#include "valparam.h"
35#include "assocdat.h"
36
37#include "linemap.h"
38#include "errwarn.h"
39#include "intnum.h"
40#include "expr.h"
41#include "value.h"
42#include "symrec.h"
43
44#include "bytecode.h"
45#include "arch.h"
46#include "section.h"
47
48#include "dbgfmt.h"
49#include "objfmt.h"
50
51#include "inttree.h"
52
53
54struct yasm_section {
55    /*@reldef@*/ STAILQ_ENTRY(yasm_section) link;
56
57    /*@dependent@*/ yasm_object *object;    /* Pointer to parent object */
58
59    /*@owned@*/ char *name;     /* strdup()'ed name (given by user) */
60
61    /* associated data; NULL if none */
62    /*@null@*/ /*@only@*/ yasm__assoc_data *assoc_data;
63
64    unsigned long align;        /* Section alignment */
65
66    unsigned long opt_flags;    /* storage for optimizer flags */
67
68    int code;                   /* section contains code (instructions) */
69    int res_only;               /* allow only resb family of bytecodes? */
70    int def;                    /* "default" section, e.g. not specified by
71                                   using section directive */
72
73    /* the bytecodes for the section's contents */
74    /*@reldef@*/ STAILQ_HEAD(yasm_bytecodehead, yasm_bytecode) bcs;
75
76    /* the relocations for the section */
77    /*@reldef@*/ STAILQ_HEAD(yasm_relochead, yasm_reloc) relocs;
78
79    void (*destroy_reloc) (/*@only@*/ void *reloc);
80};
81
82static void yasm_section_destroy(/*@only@*/ yasm_section *sect);
83
84/* Wrapper around directive for HAMT insertion */
85typedef struct yasm_directive_wrap {
86    const yasm_directive *directive;
87} yasm_directive_wrap;
88
89/*
90 * Standard "builtin" object directives.
91 */
92
93static void
94dir_extern(yasm_object *object, yasm_valparamhead *valparams,
95           yasm_valparamhead *objext_valparams, unsigned long line)
96{
97    yasm_valparam *vp = yasm_vps_first(valparams);
98    yasm_symrec *sym;
99    sym = yasm_symtab_declare(object->symtab, yasm_vp_id(vp), YASM_SYM_EXTERN,
100                              line);
101    if (objext_valparams) {
102        yasm_valparamhead *vps = yasm_vps_create();
103        *vps = *objext_valparams;   /* structure copy */
104        yasm_vps_initialize(objext_valparams);  /* don't double-free */
105        yasm_symrec_set_objext_valparams(sym, vps);
106    }
107}
108
109static void
110dir_global(yasm_object *object, yasm_valparamhead *valparams,
111           yasm_valparamhead *objext_valparams, unsigned long line)
112{
113    yasm_valparam *vp = yasm_vps_first(valparams);
114    yasm_symrec *sym;
115    sym = yasm_symtab_declare(object->symtab, yasm_vp_id(vp), YASM_SYM_GLOBAL,
116                              line);
117    if (objext_valparams) {
118        yasm_valparamhead *vps = yasm_vps_create();
119        *vps = *objext_valparams;   /* structure copy */
120        yasm_vps_initialize(objext_valparams);  /* don't double-free */
121        yasm_symrec_set_objext_valparams(sym, vps);
122    }
123}
124
125static void
126dir_common(yasm_object *object, yasm_valparamhead *valparams,
127           yasm_valparamhead *objext_valparams, unsigned long line)
128{
129    yasm_valparam *vp = yasm_vps_first(valparams);
130    yasm_valparam *vp2 = yasm_vps_next(vp);
131    yasm_expr *size = yasm_vp_expr(vp2, object->symtab, line);
132    yasm_symrec *sym;
133
134    if (!size) {
135        yasm_error_set(YASM_ERROR_SYNTAX,
136                       N_("no size specified in %s declaration"), "COMMON");
137        return;
138    }
139    sym = yasm_symtab_declare(object->symtab, yasm_vp_id(vp), YASM_SYM_COMMON,
140                              line);
141    yasm_symrec_set_common_size(sym, size);
142    if (objext_valparams) {
143        yasm_valparamhead *vps = yasm_vps_create();
144        *vps = *objext_valparams;   /* structure copy */
145        yasm_vps_initialize(objext_valparams);  /* don't double-free */
146        yasm_symrec_set_objext_valparams(sym, vps);
147    }
148}
149
150static void
151dir_section(yasm_object *object, yasm_valparamhead *valparams,
152            yasm_valparamhead *objext_valparams, unsigned long line)
153{
154    yasm_section *new_section =
155        yasm_objfmt_section_switch(object, valparams, objext_valparams, line);
156    if (new_section)
157        object->cur_section = new_section;
158    else
159        yasm_error_set(YASM_ERROR_SYNTAX,
160                       N_("invalid argument to directive `%s'"), "SECTION");
161}
162
163static const yasm_directive object_directives[] = {
164    { ".extern",        "gas",  dir_extern,     YASM_DIR_ID_REQUIRED },
165    { ".global",        "gas",  dir_global,     YASM_DIR_ID_REQUIRED },
166    { ".globl",         "gas",  dir_global,     YASM_DIR_ID_REQUIRED },
167    { "extern",         "nasm", dir_extern,     YASM_DIR_ID_REQUIRED },
168    { "global",         "nasm", dir_global,     YASM_DIR_ID_REQUIRED },
169    { "common",         "nasm", dir_common,     YASM_DIR_ID_REQUIRED },
170    { "section",        "nasm", dir_section,    YASM_DIR_ARG_REQUIRED },
171    { "segment",        "nasm", dir_section,    YASM_DIR_ARG_REQUIRED },
172    { NULL, NULL, NULL, 0 }
173};
174
175static void
176directive_level2_delete(/*@only@*/ void *data)
177{
178    yasm_xfree(data);
179}
180
181static void
182directive_level1_delete(/*@only@*/ void *data)
183{
184    HAMT_destroy(data, directive_level2_delete);
185}
186
187static void
188directives_add(yasm_object *object, /*@null@*/ const yasm_directive *dir)
189{
190    if (!dir)
191        return;
192
193    while (dir->name) {
194        HAMT *level2 = HAMT_search(object->directives, dir->parser);
195        int replace;
196        yasm_directive_wrap *wrap = yasm_xmalloc(sizeof(yasm_directive_wrap));
197
198        if (!level2) {
199            replace = 0;
200            level2 = HAMT_insert(object->directives, dir->parser,
201                                 HAMT_create(1, yasm_internal_error_),
202                                 &replace, directive_level1_delete);
203        }
204        replace = 0;
205        wrap->directive = dir;
206        HAMT_insert(level2, dir->name, wrap, &replace,
207                    directive_level2_delete);
208        dir++;
209    }
210}
211
212/*@-compdestroy@*/
213yasm_object *
214yasm_object_create(const char *src_filename, const char *obj_filename,
215                   /*@kept@*/ yasm_arch *arch,
216                   const yasm_objfmt_module *objfmt_module,
217                   const yasm_dbgfmt_module *dbgfmt_module)
218{
219    yasm_object *object = yasm_xmalloc(sizeof(yasm_object));
220    int matched, i;
221
222    object->src_filename = yasm__xstrdup(src_filename);
223    object->obj_filename = yasm__xstrdup(obj_filename);
224
225    /* No prefix/suffix */
226    object->global_prefix = yasm__xstrdup("");
227    object->global_suffix = yasm__xstrdup("");
228
229    /* Create empty symbol table */
230    object->symtab = yasm_symtab_create();
231
232    /* Initialize sections linked list */
233    STAILQ_INIT(&object->sections);
234
235    /* Create directives HAMT */
236    object->directives = HAMT_create(1, yasm_internal_error_);
237
238    /* Initialize the target architecture */
239    object->arch = arch;
240
241    /* Initialize things to NULL in case of error */
242    object->dbgfmt = NULL;
243
244    /* Initialize the object format */
245    object->objfmt = yasm_objfmt_create(objfmt_module, object);
246    if (!object->objfmt) {
247        yasm_error_set(YASM_ERROR_GENERAL,
248            N_("object format `%s' does not support architecture `%s' machine `%s'"),
249            objfmt_module->keyword, ((yasm_arch_base *)arch)->module->keyword,
250            yasm_arch_get_machine(arch));
251        goto error;
252    }
253
254    /* Get a fresh copy of objfmt_module as it may have changed. */
255    objfmt_module = ((yasm_objfmt_base *)object->objfmt)->module;
256
257    /* Add an initial "default" section to object */
258    object->cur_section = yasm_objfmt_add_default_section(object);
259
260    /* Check to see if the requested debug format is in the allowed list
261     * for the active object format.
262     */
263    matched = 0;
264    for (i=0; objfmt_module->dbgfmt_keywords[i]; i++)
265        if (yasm__strcasecmp(objfmt_module->dbgfmt_keywords[i],
266                             dbgfmt_module->keyword) == 0)
267            matched = 1;
268    if (!matched) {
269        yasm_error_set(YASM_ERROR_GENERAL,
270            N_("`%s' is not a valid debug format for object format `%s'"),
271            dbgfmt_module->keyword, objfmt_module->keyword);
272        goto error;
273    }
274
275    /* Initialize the debug format */
276    object->dbgfmt = yasm_dbgfmt_create(dbgfmt_module, object);
277    if (!object->dbgfmt) {
278        yasm_error_set(YASM_ERROR_GENERAL,
279            N_("debug format `%s' does not work with object format `%s'"),
280            dbgfmt_module->keyword, objfmt_module->keyword);
281        goto error;
282    }
283
284    /* Add directives to HAMT.  Note ordering here determines priority. */
285    directives_add(object,
286                   ((yasm_objfmt_base *)object->objfmt)->module->directives);
287    directives_add(object,
288                   ((yasm_dbgfmt_base *)object->dbgfmt)->module->directives);
289    directives_add(object,
290                   ((yasm_arch_base *)object->arch)->module->directives);
291    directives_add(object, object_directives);
292
293    return object;
294
295error:
296    yasm_object_destroy(object);
297    return NULL;
298}
299/*@=compdestroy@*/
300
301/*@-onlytrans@*/
302yasm_section *
303yasm_object_get_general(yasm_object *object, const char *name,
304                        unsigned long align, int code, int res_only,
305                        int *isnew, unsigned long line)
306{
307    yasm_section *s;
308    yasm_bytecode *bc;
309
310    /* Search through current sections to see if we already have one with
311     * that name.
312     */
313    STAILQ_FOREACH(s, &object->sections, link) {
314        if (strcmp(s->name, name) == 0) {
315            *isnew = 0;
316            return s;
317        }
318    }
319
320    /* No: we have to allocate and create a new one. */
321
322    /* Okay, the name is valid; now allocate and initialize */
323    s = yasm_xcalloc(1, sizeof(yasm_section));
324    STAILQ_INSERT_TAIL(&object->sections, s, link);
325
326    s->object = object;
327    s->name = yasm__xstrdup(name);
328    s->assoc_data = NULL;
329    s->align = align;
330
331    /* Initialize bytecodes with one empty bytecode (acts as "prior" for first
332     * real bytecode in section.
333     */
334    STAILQ_INIT(&s->bcs);
335    bc = yasm_bc_create_common(NULL, NULL, 0);
336    bc->section = s;
337    bc->offset = 0;
338    STAILQ_INSERT_TAIL(&s->bcs, bc, link);
339
340    /* Initialize relocs */
341    STAILQ_INIT(&s->relocs);
342    s->destroy_reloc = NULL;
343
344    s->code = code;
345    s->res_only = res_only;
346    s->def = 0;
347
348    /* Initialize object format specific data */
349    yasm_objfmt_init_new_section(s, line);
350
351    *isnew = 1;
352    return s;
353}
354/*@=onlytrans@*/
355
356int
357yasm_object_directive(yasm_object *object, const char *name,
358                      const char *parser, yasm_valparamhead *valparams,
359                      yasm_valparamhead *objext_valparams,
360                      unsigned long line)
361{
362    HAMT *level2;
363    yasm_directive_wrap *wrap;
364
365    level2 = HAMT_search(object->directives, parser);
366    if (!level2)
367        return 1;
368
369    wrap = HAMT_search(level2, name);
370    if (!wrap)
371        return 1;
372
373    yasm_call_directive(wrap->directive, object, valparams, objext_valparams,
374                        line);
375    return 0;
376}
377
378void
379yasm_object_set_source_fn(yasm_object *object, const char *src_filename)
380{
381    yasm_xfree(object->src_filename);
382    object->src_filename = yasm__xstrdup(src_filename);
383}
384
385void
386yasm_object_set_global_prefix(yasm_object *object, const char *prefix)
387{
388    yasm_xfree(object->global_prefix);
389    object->global_prefix = yasm__xstrdup(prefix);
390}
391
392void
393yasm_object_set_global_suffix(yasm_object *object, const char *suffix)
394{
395    yasm_xfree(object->global_suffix);
396    object->global_suffix = yasm__xstrdup(suffix);
397}
398
399int
400yasm_section_is_code(yasm_section *sect)
401{
402    return sect->code;
403}
404
405unsigned long
406yasm_section_get_opt_flags(const yasm_section *sect)
407{
408    return sect->opt_flags;
409}
410
411void
412yasm_section_set_opt_flags(yasm_section *sect, unsigned long opt_flags)
413{
414    sect->opt_flags = opt_flags;
415}
416
417int
418yasm_section_is_default(const yasm_section *sect)
419{
420    return sect->def;
421}
422
423void
424yasm_section_set_default(yasm_section *sect, int def)
425{
426    sect->def = def;
427}
428
429yasm_object *
430yasm_section_get_object(const yasm_section *sect)
431{
432    return sect->object;
433}
434
435void *
436yasm_section_get_data(yasm_section *sect,
437                      const yasm_assoc_data_callback *callback)
438{
439    return yasm__assoc_data_get(sect->assoc_data, callback);
440}
441
442void
443yasm_section_add_data(yasm_section *sect,
444                      const yasm_assoc_data_callback *callback, void *data)
445{
446    sect->assoc_data = yasm__assoc_data_add(sect->assoc_data, callback, data);
447}
448
449void
450yasm_object_destroy(yasm_object *object)
451{
452    yasm_section *cur, *next;
453
454    /* Delete object format, debug format, and arch.  This can be called
455     * due to an error in yasm_object_create(), so look out for NULLs.
456     */
457    if (object->objfmt)
458        yasm_objfmt_destroy(object->objfmt);
459    if (object->dbgfmt)
460        yasm_dbgfmt_destroy(object->dbgfmt);
461
462    /* Delete sections */
463    cur = STAILQ_FIRST(&object->sections);
464    while (cur) {
465        next = STAILQ_NEXT(cur, link);
466        yasm_section_destroy(cur);
467        cur = next;
468    }
469
470    /* Delete directives HAMT */
471    HAMT_destroy(object->directives, directive_level1_delete);
472
473    /* Delete prefix/suffix */
474    yasm_xfree(object->global_prefix);
475    yasm_xfree(object->global_suffix);
476
477    /* Delete associated filenames */
478    yasm_xfree(object->src_filename);
479    yasm_xfree(object->obj_filename);
480
481    /* Delete symbol table */
482    yasm_symtab_destroy(object->symtab);
483
484    /* Delete architecture */
485    if (object->arch)
486        yasm_arch_destroy(object->arch);
487
488    yasm_xfree(object);
489}
490
491void
492yasm_object_print(const yasm_object *object, FILE *f, int indent_level)
493{
494    yasm_section *cur;
495
496    /* Print symbol table */
497    fprintf(f, "%*sSymbol Table:\n", indent_level, "");
498    yasm_symtab_print(object->symtab, f, indent_level+1);
499
500    /* Print sections and bytecodes */
501    STAILQ_FOREACH(cur, &object->sections, link) {
502        fprintf(f, "%*sSection:\n", indent_level, "");
503        yasm_section_print(cur, f, indent_level+1, 1);
504    }
505}
506
507void
508yasm_object_finalize(yasm_object *object, yasm_errwarns *errwarns)
509{
510    yasm_section *sect;
511
512    /* Iterate through sections */
513    STAILQ_FOREACH(sect, &object->sections, link) {
514        yasm_bytecode *cur = STAILQ_FIRST(&sect->bcs);
515        yasm_bytecode *prev;
516
517        /* Skip our locally created empty bytecode first. */
518        prev = cur;
519        cur = STAILQ_NEXT(cur, link);
520
521        /* Iterate through the remainder, if any. */
522        while (cur) {
523            /* Finalize */
524            yasm_bc_finalize(cur, prev);
525            yasm_errwarn_propagate(errwarns, cur->line);
526            prev = cur;
527            cur = STAILQ_NEXT(cur, link);
528        }
529    }
530}
531
532int
533yasm_object_sections_traverse(yasm_object *object, /*@null@*/ void *d,
534                              int (*func) (yasm_section *sect,
535                                           /*@null@*/ void *d))
536{
537    yasm_section *cur;
538
539    STAILQ_FOREACH(cur, &object->sections, link) {
540        int retval = func(cur, d);
541        if (retval != 0)
542            return retval;
543    }
544    return 0;
545}
546
547/*@-onlytrans@*/
548yasm_section *
549yasm_object_find_general(yasm_object *object, const char *name)
550{
551    yasm_section *cur;
552
553    STAILQ_FOREACH(cur, &object->sections, link) {
554        if (strcmp(cur->name, name) == 0)
555            return cur;
556    }
557    return NULL;
558}
559/*@=onlytrans@*/
560
561void
562yasm_section_add_reloc(yasm_section *sect, yasm_reloc *reloc,
563                       void (*destroy_func) (/*@only@*/ void *reloc))
564{
565    STAILQ_INSERT_TAIL(&sect->relocs, reloc, link);
566    if (!destroy_func)
567        yasm_internal_error(N_("NULL destroy function given to add_reloc"));
568    else if (sect->destroy_reloc && destroy_func != sect->destroy_reloc)
569        yasm_internal_error(N_("different destroy function given to add_reloc"));
570    sect->destroy_reloc = destroy_func;
571}
572
573/*@null@*/ yasm_reloc *
574yasm_section_relocs_first(yasm_section *sect)
575{
576    return STAILQ_FIRST(&sect->relocs);
577}
578
579#undef yasm_section_reloc_next
580/*@null@*/ yasm_reloc *
581yasm_section_reloc_next(yasm_reloc *reloc)
582{
583    return STAILQ_NEXT(reloc, link);
584}
585
586void
587yasm_reloc_get(yasm_reloc *reloc, yasm_intnum **addrp, yasm_symrec **symp)
588{
589    *addrp = reloc->addr;
590    *symp = reloc->sym;
591}
592
593
594yasm_bytecode *
595yasm_section_bcs_first(yasm_section *sect)
596{
597    return STAILQ_FIRST(&sect->bcs);
598}
599
600yasm_bytecode *
601yasm_section_bcs_last(yasm_section *sect)
602{
603    return STAILQ_LAST(&sect->bcs, yasm_bytecode, link);
604}
605
606yasm_bytecode *
607yasm_section_bcs_append(yasm_section *sect, yasm_bytecode *bc)
608{
609    if (bc) {
610        if (bc->callback) {
611            bc->section = sect;     /* record parent section */
612            STAILQ_INSERT_TAIL(&sect->bcs, bc, link);
613            return bc;
614        } else
615            yasm_xfree(bc);
616    }
617    return (yasm_bytecode *)NULL;
618}
619
620int
621yasm_section_bcs_traverse(yasm_section *sect,
622                          /*@null@*/ yasm_errwarns *errwarns,
623                          /*@null@*/ void *d,
624                          int (*func) (yasm_bytecode *bc, /*@null@*/ void *d))
625{
626    yasm_bytecode *cur = STAILQ_FIRST(&sect->bcs);
627
628    /* Skip our locally created empty bytecode first. */
629    cur = STAILQ_NEXT(cur, link);
630
631    /* Iterate through the remainder, if any. */
632    while (cur) {
633        int retval = func(cur, d);
634        if (errwarns)
635            yasm_errwarn_propagate(errwarns, cur->line);
636        if (retval != 0)
637            return retval;
638        cur = STAILQ_NEXT(cur, link);
639    }
640    return 0;
641}
642
643const char *
644yasm_section_get_name(const yasm_section *sect)
645{
646    return sect->name;
647}
648
649void
650yasm_section_set_align(yasm_section *sect, unsigned long align,
651                       unsigned long line)
652{
653    sect->align = align;
654}
655
656unsigned long
657yasm_section_get_align(const yasm_section *sect)
658{
659    return sect->align;
660}
661
662static void
663yasm_section_destroy(yasm_section *sect)
664{
665    yasm_bytecode *cur, *next;
666    yasm_reloc *r_cur, *r_next;
667
668    if (!sect)
669        return;
670
671    yasm_xfree(sect->name);
672    yasm__assoc_data_destroy(sect->assoc_data);
673
674    /* Delete bytecodes */
675    cur = STAILQ_FIRST(&sect->bcs);
676    while (cur) {
677        next = STAILQ_NEXT(cur, link);
678        yasm_bc_destroy(cur);
679        cur = next;
680    }
681
682    /* Delete relocations */
683    r_cur = STAILQ_FIRST(&sect->relocs);
684    while (r_cur) {
685        r_next = STAILQ_NEXT(r_cur, link);
686        yasm_intnum_destroy(r_cur->addr);
687        sect->destroy_reloc(r_cur);
688        r_cur = r_next;
689    }
690
691    yasm_xfree(sect);
692}
693
694void
695yasm_section_print(const yasm_section *sect, FILE *f, int indent_level,
696                   int print_bcs)
697{
698    if (!sect) {
699        fprintf(f, "%*s(none)\n", indent_level, "");
700        return;
701    }
702
703    fprintf(f, "%*sname=%s\n", indent_level, "", sect->name);
704
705    if (sect->assoc_data) {
706        fprintf(f, "%*sAssociated data:\n", indent_level, "");
707        yasm__assoc_data_print(sect->assoc_data, f, indent_level+1);
708    }
709
710    if (print_bcs) {
711        yasm_bytecode *cur;
712
713        fprintf(f, "%*sBytecodes:\n", indent_level, "");
714
715        STAILQ_FOREACH(cur, &sect->bcs, link) {
716            fprintf(f, "%*sNext Bytecode:\n", indent_level+1, "");
717            yasm_bc_print(cur, f, indent_level+2);
718        }
719    }
720}
721
722/*
723 * Robertson (1977) optimizer
724 * Based (somewhat loosely) on the algorithm given in:
725 *   MRC Technical Summary Report # 1779
726 *   CODE GENERATION FOR SHORT/LONG ADDRESS MACHINES
727 *   Edward L. Robertson
728 *   Mathematics Research Center
729 *   University of Wisconsin-Madison
730 *   610 Walnut Street
731 *   Madison, Wisconsin 53706
732 *   August 1977
733 *
734 * Key components of algorithm:
735 *  - start assuming all short forms
736 *  - build spans for short->long transition dependencies
737 *  - if a long form is needed, walk the dependencies and update
738 * Major differences from Robertson's algorithm:
739 *  - detection of cycles
740 *  - any difference of two locations is allowed
741 *  - handling of alignment/org gaps (offset setting)
742 *  - handling of multiples
743 *
744 * Data structures:
745 *  - Interval tree to store spans and associated data
746 *  - Queues QA and QB
747 *
748 * Each span keeps track of:
749 *  - Associated bytecode (bytecode that depends on the span length)
750 *  - Active/inactive state (starts out active)
751 *  - Sign (negative/positive; negative being "backwards" in address)
752 *  - Current length in bytes
753 *  - New length in bytes
754 *  - Negative/Positive thresholds
755 *  - Span ID (unique within each bytecode)
756 *
757 * How org and align and any other offset-based bytecodes are handled:
758 *
759 * Some portions are critical values that must not depend on any bytecode
760 * offset (either relative or absolute).
761 *
762 * All offset-setters (ORG and ALIGN) are put into a linked list in section
763 * order (e.g. increasing offset order).  Each span keeps track of the next
764 * offset-setter following the span's associated bytecode.
765 *
766 * When a bytecode is expanded, the next offset-setter is examined.  The
767 * offset-setter may be able to absorb the expansion (e.g. any offset
768 * following it would not change), or it may have to move forward (in the
769 * case of align) or error (in the case of org).  If it has to move forward,
770 * following offset-setters must also be examined for absorption or moving
771 * forward.  In either case, the ongoing offset is updated as well as the
772 * lengths of any spans dependent on the offset-setter.
773 *
774 * Alignment/ORG value is critical value.
775 * Cannot be combined with TIMES.
776 *
777 * How times is handled:
778 *
779 * TIMES: Handled separately from bytecode "raw" size.  If not span-dependent,
780 *      trivial (just multiplied in at any bytecode size increase).  Span
781 *      dependent times update on any change (span ID 0).  If the resultant
782 *      next bytecode offset would be less than the old next bytecode offset,
783 *      error.  Otherwise increase offset and update dependent spans.
784 *
785 * To reduce interval tree size, a first expansion pass is performed
786 * before the spans are added to the tree.
787 *
788 * Basic algorithm outline:
789 *
790 * 1. Initialization:
791 *  a. Number bytecodes sequentially (via bc_index) and calculate offsets
792 *     of all bytecodes assuming minimum length, building a list of all
793 *     dependent spans as we go.
794 *     "minimum" here means absolute minimum:
795 *      - align/org (offset-based) bumps offset as normal
796 *      - times values (with span-dependent values) assumed to be 0
797 *  b. Iterate over spans.  Set span length based on bytecode offsets
798 *     determined in 1a.  If span is "certainly" long because the span
799 *     is an absolute reference to another section (or external) or the
800 *     distance calculated based on the minimum length is greater than the
801 *     span's threshold, expand the span's bytecode, and if no further
802 *     expansion can result, mark span as inactive.
803 *  c. Iterate over bytecodes to update all bytecode offsets based on new
804 *     (expanded) lengths calculated in 1b.
805 *  d. Iterate over active spans.  Add span to interval tree.  Update span's
806 *     length based on new bytecode offsets determined in 1c.  If span's
807 *     length exceeds long threshold, add that span to Q.
808 * 2. Main loop:
809 *   While Q not empty:
810 *     Expand BC dependent on span at head of Q (and remove span from Q).
811 *     Update span:
812 *       If BC no longer dependent on span, mark span as inactive.
813 *       If BC has new thresholds for span, update span.
814 *     If BC increased in size, for each active span that contains BC:
815 *       Increase span length by difference between short and long BC length.
816 *       If span exceeds long threshold (or is flagged to recalculate on any
817 *       change), add it to tail of Q.
818 * 3. Final pass over bytecodes to generate final offsets.
819 */
820
821typedef struct yasm_span yasm_span;
822
823typedef struct yasm_offset_setter {
824    /* Linked list in section order (e.g. offset order) */
825    /*@reldef@*/ STAILQ_ENTRY(yasm_offset_setter) link;
826
827    /*@dependent@*/ yasm_bytecode *bc;
828
829    unsigned long cur_val, new_val;
830    unsigned long thres;
831} yasm_offset_setter;
832
833typedef struct yasm_span_term {
834    yasm_bytecode *precbc, *precbc2;
835    yasm_span *span;        /* span this term is a member of */
836    long cur_val, new_val;
837    unsigned int subst;
838} yasm_span_term;
839
840struct yasm_span {
841    /*@reldef@*/ TAILQ_ENTRY(yasm_span) link;   /* for allocation tracking */
842    /*@reldef@*/ STAILQ_ENTRY(yasm_span) linkq; /* for Q */
843
844    /*@dependent@*/ yasm_bytecode *bc;
845
846    yasm_value depval;
847
848    /* span term for relative portion of value */
849    yasm_span_term *rel_term;
850    /* span terms in absolute portion of value */
851    yasm_span_term *terms;
852    yasm_expr__item *items;
853    unsigned int num_terms;
854
855    long cur_val;
856    long new_val;
857
858    long neg_thres;
859    long pos_thres;
860
861    int id;
862
863    int active;
864
865    /* NULL-terminated array of spans that led to this span.  Used only for
866     * checking for circular references (cycles) with id=0 spans.
867     */
868    yasm_span **backtrace;
869    int backtrace_size;
870
871    /* First offset setter following this span's bytecode */
872    yasm_offset_setter *os;
873};
874
875typedef struct optimize_data {
876    /*@reldef@*/ TAILQ_HEAD(yasm_span_head, yasm_span) spans;
877    /*@reldef@*/ STAILQ_HEAD(yasm_span_shead, yasm_span) QA, QB;
878    /*@only@*/ IntervalTree *itree;
879    /*@reldef@*/ STAILQ_HEAD(offset_setters_head, yasm_offset_setter)
880        offset_setters;
881    long len_diff;      /* used only for optimize_term_expand */
882    yasm_span *span;    /* used only for check_cycle */
883    yasm_offset_setter *os;
884} optimize_data;
885
886static yasm_span *
887create_span(yasm_bytecode *bc, int id, /*@null@*/ const yasm_value *value,
888            long neg_thres, long pos_thres, yasm_offset_setter *os)
889{
890    yasm_span *span = yasm_xmalloc(sizeof(yasm_span));
891
892    span->bc = bc;
893    if (value)
894        yasm_value_init_copy(&span->depval, value);
895    else
896        yasm_value_initialize(&span->depval, NULL, 0);
897    span->rel_term = NULL;
898    span->terms = NULL;
899    span->items = NULL;
900    span->num_terms = 0;
901    span->cur_val = 0;
902    span->new_val = 0;
903    span->neg_thres = neg_thres;
904    span->pos_thres = pos_thres;
905    span->id = id;
906    span->active = 1;
907    span->backtrace = NULL;
908    span->backtrace_size = 0;
909    span->os = os;
910
911    return span;
912}
913
914static void
915optimize_add_span(void *add_span_data, yasm_bytecode *bc, int id,
916                  const yasm_value *value, long neg_thres, long pos_thres)
917{
918    optimize_data *optd = (optimize_data *)add_span_data;
919    yasm_span *span;
920    span = create_span(bc, id, value, neg_thres, pos_thres, optd->os);
921    TAILQ_INSERT_TAIL(&optd->spans, span, link);
922}
923
924static void
925add_span_term(unsigned int subst, yasm_bytecode *precbc,
926              yasm_bytecode *precbc2, void *d)
927{
928    yasm_span *span = d;
929    yasm_intnum *intn;
930
931    if (subst >= span->num_terms) {
932        /* Linear expansion since total number is essentially always small */
933        span->num_terms = subst+1;
934        span->terms = yasm_xrealloc(span->terms,
935                                    span->num_terms*sizeof(yasm_span_term));
936    }
937    span->terms[subst].precbc = precbc;
938    span->terms[subst].precbc2 = precbc2;
939    span->terms[subst].span = span;
940    span->terms[subst].subst = subst;
941
942    intn = yasm_calc_bc_dist(precbc, precbc2);
943    if (!intn)
944        yasm_internal_error(N_("could not calculate bc distance"));
945    span->terms[subst].cur_val = 0;
946    span->terms[subst].new_val = yasm_intnum_get_int(intn);
947    yasm_intnum_destroy(intn);
948}
949
950static void
951span_create_terms(yasm_span *span)
952{
953    unsigned int i;
954
955    /* Split out sym-sym terms in absolute portion of dependent value */
956    if (span->depval.abs) {
957        span->num_terms = yasm_expr__bc_dist_subst(&span->depval.abs, span,
958                                                   add_span_term);
959        if (span->num_terms > 0) {
960            span->items = yasm_xmalloc(span->num_terms*sizeof(yasm_expr__item));
961            for (i=0; i<span->num_terms; i++) {
962                /* Create items with dummy value */
963                span->items[i].type = YASM_EXPR_INT;
964                span->items[i].data.intn = yasm_intnum_create_int(0);
965
966                /* Check for circular references */
967                if (span->id <= 0 &&
968                    ((span->bc->bc_index > span->terms[i].precbc->bc_index &&
969                      span->bc->bc_index <= span->terms[i].precbc2->bc_index) ||
970                     (span->bc->bc_index > span->terms[i].precbc2->bc_index &&
971                      span->bc->bc_index <= span->terms[i].precbc->bc_index)))
972                    yasm_error_set(YASM_ERROR_VALUE,
973                                   N_("circular reference detected"));
974            }
975        }
976    }
977
978    /* Create term for relative portion of dependent value */
979    if (span->depval.rel) {
980        yasm_bytecode *rel_precbc;
981        int sym_local;
982
983        sym_local = yasm_symrec_get_label(span->depval.rel, &rel_precbc);
984        if (span->depval.wrt || span->depval.seg_of || span->depval.section_rel
985            || !sym_local)
986            return;     /* we can't handle SEG, WRT, or external symbols */
987        if (rel_precbc->section != span->bc->section)
988            return;     /* not in this section */
989        if (!span->depval.curpos_rel)
990            return;     /* not PC-relative */
991
992        span->rel_term = yasm_xmalloc(sizeof(yasm_span_term));
993        span->rel_term->precbc = NULL;
994        span->rel_term->precbc2 = rel_precbc;
995        span->rel_term->span = span;
996        span->rel_term->subst = ~0U;
997
998        span->rel_term->cur_val = 0;
999        span->rel_term->new_val = yasm_bc_next_offset(rel_precbc) -
1000            span->bc->offset;
1001    }
1002}
1003
1004/* Recalculate span value based on current span replacement values.
1005 * Returns 1 if span needs expansion (e.g. exceeded thresholds).
1006 */
1007static int
1008recalc_normal_span(yasm_span *span)
1009{
1010    span->new_val = 0;
1011
1012    if (span->depval.abs) {
1013        yasm_expr *abs_copy = yasm_expr_copy(span->depval.abs);
1014        /*@null@*/ /*@dependent@*/ yasm_intnum *num;
1015
1016        /* Update sym-sym terms and substitute back into expr */
1017        unsigned int i;
1018        for (i=0; i<span->num_terms; i++)
1019            yasm_intnum_set_int(span->items[i].data.intn,
1020                                span->terms[i].new_val);
1021        yasm_expr__subst(abs_copy, span->num_terms, span->items);
1022        num = yasm_expr_get_intnum(&abs_copy, 0);
1023        if (num)
1024            span->new_val = yasm_intnum_get_int(num);
1025        else
1026            span->new_val = LONG_MAX; /* too complex; force to longest form */
1027        yasm_expr_destroy(abs_copy);
1028    }
1029
1030    if (span->rel_term) {
1031        if (span->new_val != LONG_MAX && span->rel_term->new_val != LONG_MAX)
1032            span->new_val += span->rel_term->new_val >> span->depval.rshift;
1033        else
1034            span->new_val = LONG_MAX;   /* too complex; force to longest form */
1035    } else if (span->depval.rel)
1036        span->new_val = LONG_MAX;   /* too complex; force to longest form */
1037
1038    if (span->new_val == LONG_MAX)
1039        span->active = 0;
1040
1041    /* If id<=0, flag update on any change */
1042    if (span->id <= 0)
1043        return (span->new_val != span->cur_val);
1044
1045    return (span->new_val < span->neg_thres
1046            || span->new_val > span->pos_thres);
1047}
1048
1049/* Updates all bytecode offsets.  For offset-based bytecodes, calls expand
1050 * to determine new length.
1051 */
1052static int
1053update_all_bc_offsets(yasm_object *object, yasm_errwarns *errwarns)
1054{
1055    yasm_section *sect;
1056    int saw_error = 0;
1057
1058    STAILQ_FOREACH(sect, &object->sections, link) {
1059        unsigned long offset = 0;
1060
1061        yasm_bytecode *bc = STAILQ_FIRST(&sect->bcs);
1062        yasm_bytecode *prevbc;
1063
1064        /* Skip our locally created empty bytecode first. */
1065        prevbc = bc;
1066        bc = STAILQ_NEXT(bc, link);
1067
1068        /* Iterate through the remainder, if any. */
1069        while (bc) {
1070            if (bc->callback->special == YASM_BC_SPECIAL_OFFSET) {
1071                /* Recalculate/adjust len of offset-based bytecodes here */
1072                long neg_thres = 0;
1073                long pos_thres = (long)yasm_bc_next_offset(bc);
1074                int retval = yasm_bc_expand(bc, 1, 0,
1075                                            (long)yasm_bc_next_offset(prevbc),
1076                                            &neg_thres, &pos_thres);
1077                yasm_errwarn_propagate(errwarns, bc->line);
1078                if (retval < 0)
1079                    saw_error = 1;
1080            }
1081            bc->offset = offset;
1082            offset += bc->len*bc->mult_int;
1083            prevbc = bc;
1084            bc = STAILQ_NEXT(bc, link);
1085        }
1086    }
1087    return saw_error;
1088}
1089
1090static void
1091span_destroy(/*@only@*/ yasm_span *span)
1092{
1093    unsigned int i;
1094
1095    yasm_value_delete(&span->depval);
1096    if (span->rel_term)
1097        yasm_xfree(span->rel_term);
1098    if (span->terms)
1099        yasm_xfree(span->terms);
1100    if (span->items) {
1101        for (i=0; i<span->num_terms; i++)
1102            yasm_intnum_destroy(span->items[i].data.intn);
1103        yasm_xfree(span->items);
1104    }
1105    if (span->backtrace)
1106        yasm_xfree(span->backtrace);
1107    yasm_xfree(span);
1108}
1109
1110static void
1111optimize_cleanup(optimize_data *optd)
1112{
1113    yasm_span *s1, *s2;
1114    yasm_offset_setter *os1, *os2;
1115
1116    IT_destroy(optd->itree);
1117
1118    s1 = TAILQ_FIRST(&optd->spans);
1119    while (s1) {
1120        s2 = TAILQ_NEXT(s1, link);
1121        span_destroy(s1);
1122        s1 = s2;
1123    }
1124
1125    os1 = STAILQ_FIRST(&optd->offset_setters);
1126    while (os1) {
1127        os2 = STAILQ_NEXT(os1, link);
1128        yasm_xfree(os1);
1129        os1 = os2;
1130    }
1131}
1132
1133static void
1134optimize_itree_add(IntervalTree *itree, yasm_span *span, yasm_span_term *term)
1135{
1136    long precbc_index, precbc2_index;
1137    unsigned long low, high;
1138
1139    /* Update term length */
1140    if (term->precbc)
1141        precbc_index = term->precbc->bc_index;
1142    else
1143        precbc_index = span->bc->bc_index-1;
1144
1145    if (term->precbc2)
1146        precbc2_index = term->precbc2->bc_index;
1147    else
1148        precbc2_index = span->bc->bc_index-1;
1149
1150    if (precbc_index < precbc2_index) {
1151        low = precbc_index+1;
1152        high = precbc2_index;
1153    } else if (precbc_index > precbc2_index) {
1154        low = precbc2_index+1;
1155        high = precbc_index;
1156    } else
1157        return;     /* difference is same bc - always 0! */
1158
1159    IT_insert(itree, (long)low, (long)high, term);
1160}
1161
1162static void
1163check_cycle(IntervalTreeNode *node, void *d)
1164{
1165    optimize_data *optd = d;
1166    yasm_span_term *term = node->data;
1167    yasm_span *depspan = term->span;
1168    int i;
1169    int depspan_bt_alloc;
1170
1171    /* Only check for cycles in id=0 spans */
1172    if (depspan->id > 0)
1173        return;
1174
1175    /* Check for a circular reference by looking to see if this dependent
1176     * span is in our backtrace.
1177     */
1178    if (optd->span->backtrace) {
1179        for (i=0; i<optd->span->backtrace_size; i++) {
1180            if (optd->span->backtrace[i] == depspan)
1181                yasm_error_set(YASM_ERROR_VALUE,
1182                               N_("circular reference detected"));
1183        }
1184    }
1185
1186    /* Add our complete backtrace and ourselves to backtrace of dependent
1187     * span.
1188     */
1189    if (!depspan->backtrace) {
1190        depspan->backtrace = yasm_xmalloc((optd->span->backtrace_size+1)*
1191                                          sizeof(yasm_span *));
1192        if (optd->span->backtrace_size > 0)
1193            memcpy(depspan->backtrace, optd->span->backtrace,
1194                   optd->span->backtrace_size*sizeof(yasm_span *));
1195        depspan->backtrace[optd->span->backtrace_size] = optd->span;
1196        depspan->backtrace_size = optd->span->backtrace_size+1;
1197        return;
1198    }
1199
1200    /* Add our complete backtrace, checking for duplicates */
1201    depspan_bt_alloc = depspan->backtrace_size;
1202    for (i=0; i<optd->span->backtrace_size; i++) {
1203        int present = 0;
1204        int j;
1205        for (j=0; j<depspan->backtrace_size; j++) {
1206            if (optd->span->backtrace[i] == optd->span->backtrace[j]) {
1207                present = 1;
1208                break;
1209            }
1210        }
1211        if (present)
1212            continue;
1213        /* Not already in array; add it. */
1214        if (depspan->backtrace_size >= depspan_bt_alloc)
1215        {
1216            depspan_bt_alloc *= 2;
1217            depspan->backtrace =
1218                yasm_xrealloc(depspan->backtrace,
1219                              depspan_bt_alloc*sizeof(yasm_span *));
1220        }
1221        depspan->backtrace[depspan->backtrace_size] = optd->span->backtrace[i];
1222        depspan->backtrace_size++;
1223    }
1224
1225    /* Add ourselves. */
1226    if (depspan->backtrace_size >= depspan_bt_alloc)
1227    {
1228        depspan_bt_alloc++;
1229        depspan->backtrace =
1230            yasm_xrealloc(depspan->backtrace,
1231                          depspan_bt_alloc*sizeof(yasm_span *));
1232    }
1233    depspan->backtrace[depspan->backtrace_size] = optd->span;
1234    depspan->backtrace_size++;
1235}
1236
1237static void
1238optimize_term_expand(IntervalTreeNode *node, void *d)
1239{
1240    optimize_data *optd = d;
1241    yasm_span_term *term = node->data;
1242    yasm_span *span = term->span;
1243    long len_diff = optd->len_diff;
1244    long precbc_index, precbc2_index;
1245
1246    /* Don't expand inactive spans */
1247    if (!span->active)
1248        return;
1249
1250    /* Update term length */
1251    if (term->precbc)
1252        precbc_index = term->precbc->bc_index;
1253    else
1254        precbc_index = span->bc->bc_index-1;
1255
1256    if (term->precbc2)
1257        precbc2_index = term->precbc2->bc_index;
1258    else
1259        precbc2_index = span->bc->bc_index-1;
1260
1261    if (precbc_index < precbc2_index)
1262        term->new_val += len_diff;
1263    else
1264        term->new_val -= len_diff;
1265
1266    /* If already on Q, don't re-add */
1267    if (span->active == 2)
1268        return;
1269
1270    /* Update term and check against thresholds */
1271    if (!recalc_normal_span(span))
1272        return; /* didn't exceed thresholds, we're done */
1273
1274    /* Exceeded thresholds, need to add to Q for expansion */
1275    if (span->id <= 0)
1276        STAILQ_INSERT_TAIL(&optd->QA, span, linkq);
1277    else
1278        STAILQ_INSERT_TAIL(&optd->QB, span, linkq);
1279    span->active = 2;       /* Mark as being in Q */
1280}
1281
1282void
1283yasm_object_optimize(yasm_object *object, yasm_errwarns *errwarns)
1284{
1285    yasm_section *sect;
1286    unsigned long bc_index = 0;
1287    int saw_error = 0;
1288    optimize_data optd;
1289    yasm_span *span, *span_temp;
1290    yasm_offset_setter *os;
1291    int retval;
1292    unsigned int i;
1293
1294    TAILQ_INIT(&optd.spans);
1295    STAILQ_INIT(&optd.offset_setters);
1296    optd.itree = IT_create();
1297
1298    /* Create an placeholder offset setter for spans to point to; this will
1299     * get updated if/when we actually run into one.
1300     */
1301    os = yasm_xmalloc(sizeof(yasm_offset_setter));
1302    os->bc = NULL;
1303    os->cur_val = 0;
1304    os->new_val = 0;
1305    os->thres = 0;
1306    STAILQ_INSERT_TAIL(&optd.offset_setters, os, link);
1307    optd.os = os;
1308
1309    /* Step 1a */
1310    STAILQ_FOREACH(sect, &object->sections, link) {
1311        unsigned long offset = 0;
1312
1313        yasm_bytecode *bc = STAILQ_FIRST(&sect->bcs);
1314        yasm_bytecode *prevbc;
1315
1316        bc->bc_index = bc_index++;
1317
1318        /* Skip our locally created empty bytecode first. */
1319        prevbc = bc;
1320        bc = STAILQ_NEXT(bc, link);
1321
1322        /* Iterate through the remainder, if any. */
1323        while (bc) {
1324            bc->bc_index = bc_index++;
1325            bc->offset = offset;
1326
1327            retval = yasm_bc_calc_len(bc, optimize_add_span, &optd);
1328            yasm_errwarn_propagate(errwarns, bc->line);
1329            if (retval)
1330                saw_error = 1;
1331            else {
1332                if (bc->callback->special == YASM_BC_SPECIAL_OFFSET) {
1333                    /* Remember it as offset setter */
1334                    os->bc = bc;
1335                    os->thres = yasm_bc_next_offset(bc);
1336
1337                    /* Create new placeholder */
1338                    os = yasm_xmalloc(sizeof(yasm_offset_setter));
1339                    os->bc = NULL;
1340                    os->cur_val = 0;
1341                    os->new_val = 0;
1342                    os->thres = 0;
1343                    STAILQ_INSERT_TAIL(&optd.offset_setters, os, link);
1344                    optd.os = os;
1345
1346                    if (bc->multiple) {
1347                        yasm_error_set(YASM_ERROR_VALUE,
1348                            N_("cannot combine multiples and setting assembly position"));
1349                        yasm_errwarn_propagate(errwarns, bc->line);
1350                        saw_error = 1;
1351                    }
1352                }
1353
1354                offset += bc->len*bc->mult_int;
1355            }
1356
1357            prevbc = bc;
1358            bc = STAILQ_NEXT(bc, link);
1359        }
1360    }
1361
1362    if (saw_error) {
1363        optimize_cleanup(&optd);
1364        return;
1365    }
1366
1367    /* Step 1b */
1368    TAILQ_FOREACH_SAFE(span, &optd.spans, link, span_temp) {
1369        span_create_terms(span);
1370        if (yasm_error_occurred()) {
1371            yasm_errwarn_propagate(errwarns, span->bc->line);
1372            saw_error = 1;
1373        } else if (recalc_normal_span(span)) {
1374            retval = yasm_bc_expand(span->bc, span->id, span->cur_val,
1375                                    span->new_val, &span->neg_thres,
1376                                    &span->pos_thres);
1377            yasm_errwarn_propagate(errwarns, span->bc->line);
1378            if (retval < 0)
1379                saw_error = 1;
1380            else if (retval > 0) {
1381                if (!span->active) {
1382                    yasm_error_set(YASM_ERROR_VALUE,
1383                        N_("secondary expansion of an external/complex value"));
1384                    yasm_errwarn_propagate(errwarns, span->bc->line);
1385                    saw_error = 1;
1386                }
1387            } else {
1388                TAILQ_REMOVE(&optd.spans, span, link);
1389                span_destroy(span);
1390                continue;
1391            }
1392        }
1393        span->cur_val = span->new_val;
1394    }
1395
1396    if (saw_error) {
1397        optimize_cleanup(&optd);
1398        return;
1399    }
1400
1401    /* Step 1c */
1402    if (update_all_bc_offsets(object, errwarns)) {
1403        optimize_cleanup(&optd);
1404        return;
1405    }
1406
1407    /* Step 1d */
1408    STAILQ_INIT(&optd.QB);
1409    TAILQ_FOREACH(span, &optd.spans, link) {
1410        yasm_intnum *intn;
1411
1412        /* Update span terms based on new bc offsets */
1413        for (i=0; i<span->num_terms; i++) {
1414            intn = yasm_calc_bc_dist(span->terms[i].precbc,
1415                                     span->terms[i].precbc2);
1416            if (!intn)
1417                yasm_internal_error(N_("could not calculate bc distance"));
1418            span->terms[i].cur_val = span->terms[i].new_val;
1419            span->terms[i].new_val = yasm_intnum_get_int(intn);
1420            yasm_intnum_destroy(intn);
1421        }
1422        if (span->rel_term) {
1423            span->rel_term->cur_val = span->rel_term->new_val;
1424            if (span->rel_term->precbc2)
1425                span->rel_term->new_val =
1426                    yasm_bc_next_offset(span->rel_term->precbc2) -
1427                    span->bc->offset;
1428            else
1429                span->rel_term->new_val = span->bc->offset -
1430                    yasm_bc_next_offset(span->rel_term->precbc);
1431        }
1432
1433        if (recalc_normal_span(span)) {
1434            /* Exceeded threshold, add span to QB */
1435            STAILQ_INSERT_TAIL(&optd.QB, span, linkq);
1436            span->active = 2;
1437        }
1438    }
1439
1440    /* Do we need step 2?  If not, go ahead and exit. */
1441    if (STAILQ_EMPTY(&optd.QB)) {
1442        optimize_cleanup(&optd);
1443        return;
1444    }
1445
1446    /* Update offset-setters values */
1447    STAILQ_FOREACH(os, &optd.offset_setters, link) {
1448        if (!os->bc)
1449            continue;
1450        os->thres = yasm_bc_next_offset(os->bc);
1451        os->new_val = os->bc->offset;
1452        os->cur_val = os->new_val;
1453    }
1454
1455    /* Build up interval tree */
1456    TAILQ_FOREACH(span, &optd.spans, link) {
1457        for (i=0; i<span->num_terms; i++)
1458            optimize_itree_add(optd.itree, span, &span->terms[i]);
1459        if (span->rel_term)
1460            optimize_itree_add(optd.itree, span, span->rel_term);
1461    }
1462
1463    /* Look for cycles in times expansion (span.id==0) */
1464    TAILQ_FOREACH(span, &optd.spans, link) {
1465        if (span->id > 0)
1466            continue;
1467        optd.span = span;
1468        IT_enumerate(optd.itree, (long)span->bc->bc_index,
1469                     (long)span->bc->bc_index, &optd, check_cycle);
1470        if (yasm_error_occurred()) {
1471            yasm_errwarn_propagate(errwarns, span->bc->line);
1472            saw_error = 1;
1473        }
1474    }
1475
1476    if (saw_error) {
1477        optimize_cleanup(&optd);
1478        return;
1479    }
1480
1481    /* Step 2 */
1482    STAILQ_INIT(&optd.QA);
1483    while (!STAILQ_EMPTY(&optd.QA) || !(STAILQ_EMPTY(&optd.QB))) {
1484        unsigned long orig_len;
1485        long offset_diff;
1486
1487        /* QA is for TIMES, update those first, then update non-TIMES.
1488         * This is so that TIMES can absorb increases before we look at
1489         * expanding non-TIMES BCs.
1490         */
1491        if (!STAILQ_EMPTY(&optd.QA)) {
1492            span = STAILQ_FIRST(&optd.QA);
1493            STAILQ_REMOVE_HEAD(&optd.QA, linkq);
1494        } else {
1495            span = STAILQ_FIRST(&optd.QB);
1496            STAILQ_REMOVE_HEAD(&optd.QB, linkq);
1497        }
1498
1499        if (!span->active)
1500            continue;
1501        span->active = 1;   /* no longer in Q */
1502
1503        /* Make sure we ended up ultimately exceeding thresholds; due to
1504         * offset BCs we may have been placed on Q and then reduced in size
1505         * again.
1506         */
1507        if (!recalc_normal_span(span))
1508            continue;
1509
1510        orig_len = span->bc->len * span->bc->mult_int;
1511
1512        retval = yasm_bc_expand(span->bc, span->id, span->cur_val,
1513                                span->new_val, &span->neg_thres,
1514                                &span->pos_thres);
1515        yasm_errwarn_propagate(errwarns, span->bc->line);
1516
1517        if (retval < 0) {
1518            /* error */
1519            saw_error = 1;
1520            continue;
1521        } else if (retval > 0) {
1522            /* another threshold, keep active */
1523            for (i=0; i<span->num_terms; i++)
1524                span->terms[i].cur_val = span->terms[i].new_val;
1525            if (span->rel_term)
1526                span->rel_term->cur_val = span->rel_term->new_val;
1527            span->cur_val = span->new_val;
1528        } else
1529            span->active = 0;       /* we're done with this span */
1530
1531        optd.len_diff = span->bc->len * span->bc->mult_int - orig_len;
1532        if (optd.len_diff == 0)
1533            continue;   /* didn't increase in size */
1534
1535        /* Iterate over all spans dependent across the bc just expanded */
1536        IT_enumerate(optd.itree, (long)span->bc->bc_index,
1537                     (long)span->bc->bc_index, &optd, optimize_term_expand);
1538
1539        /* Iterate over offset-setters that follow the bc just expanded.
1540         * Stop iteration if:
1541         *  - no more offset-setters in this section
1542         *  - offset-setter didn't move its following offset
1543         */
1544        os = span->os;
1545        offset_diff = optd.len_diff;
1546        while (os->bc && os->bc->section == span->bc->section
1547               && offset_diff != 0) {
1548            unsigned long old_next_offset = os->cur_val + os->bc->len;
1549            long neg_thres_temp;
1550
1551            if (offset_diff < 0 && (unsigned long)(-offset_diff) > os->new_val)
1552                yasm_internal_error(N_("org/align went to negative offset"));
1553            os->new_val += offset_diff;
1554
1555            orig_len = os->bc->len;
1556            retval = yasm_bc_expand(os->bc, 1, (long)os->cur_val,
1557                                    (long)os->new_val, &neg_thres_temp,
1558                                    (long *)&os->thres);
1559            yasm_errwarn_propagate(errwarns, os->bc->line);
1560
1561            offset_diff = os->new_val + os->bc->len - old_next_offset;
1562            optd.len_diff = os->bc->len - orig_len;
1563            if (optd.len_diff != 0)
1564                IT_enumerate(optd.itree, (long)os->bc->bc_index,
1565                     (long)os->bc->bc_index, &optd, optimize_term_expand);
1566
1567            os->cur_val = os->new_val;
1568            os = STAILQ_NEXT(os, link);
1569        }
1570    }
1571
1572    if (saw_error) {
1573        optimize_cleanup(&optd);
1574        return;
1575    }
1576
1577    /* Step 3 */
1578    update_all_bc_offsets(object, errwarns);
1579    optimize_cleanup(&optd);
1580}
1581