Add test file for optimal convergence
[nasm.git] / labels.c
blob9a7fc6152ff6444bc265e5a7039bbc4e42c713e4
1 /* labels.c label handling for the Netwide Assembler
3 * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
4 * Julian Hall. All rights reserved. The software is
5 * redistributable under the license given in the file "LICENSE"
6 * distributed in the NASM archive.
7 */
9 #include "compiler.h"
11 #include <stdio.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <inttypes.h>
16 #include "nasm.h"
17 #include "nasmlib.h"
18 #include "hashtbl.h"
21 * A local label is one that begins with exactly one period. Things
22 * that begin with _two_ periods are NASM-specific things.
24 * If TASM compatibility is enabled, a local label can also begin with
25 * @@, so @@local is a TASM compatible local label. Note that we only
26 * check for the first @ symbol, although TASM requires both.
28 #define islocal(l) \
29 (tasm_compatible_mode ? \
30 (((l)[0] == '.' || (l)[0] == '@') && (l)[1] != '.') : \
31 ((l)[0] == '.' && (l)[1] != '.'))
32 #define islocalchar(c) \
33 (tasm_compatible_mode ? \
34 ((c) == '.' || (c) == '@') : \
35 ((c) == '.'))
37 #define LABEL_BLOCK 128 /* no. of labels/block */
38 #define LBLK_SIZE (LABEL_BLOCK*sizeof(union label))
40 #define END_LIST -3 /* don't clash with NO_SEG! */
41 #define END_BLOCK -2
42 #define BOGUS_VALUE -4
44 #define PERMTS_SIZE 4096 /* size of text blocks */
45 #if (PERMTS_SIZE > IDLEN_MAX)
46 #error "IPERMTS_SIZE must be less than or equal to IDLEN_MAX"
47 #endif
49 /* values for label.defn.is_global */
50 #define DEFINED_BIT 1
51 #define GLOBAL_BIT 2
52 #define EXTERN_BIT 4
54 #define NOT_DEFINED_YET 0
55 #define TYPE_MASK 3
56 #define LOCAL_SYMBOL (DEFINED_BIT)
57 #define GLOBAL_PLACEHOLDER (GLOBAL_BIT)
58 #define GLOBAL_SYMBOL (DEFINED_BIT|GLOBAL_BIT)
60 union label { /* actual label structures */
61 struct {
62 int32_t segment;
63 int64_t offset;
64 char *label, *special;
65 int is_global, is_norm;
66 } defn;
67 struct {
68 int32_t movingon;
69 int64_t dummy;
70 union label *next;
71 } admin;
74 struct permts { /* permanent text storage */
75 struct permts *next; /* for the linked list */
76 int size, usage; /* size and used space in ... */
77 char data[PERMTS_SIZE]; /* ... the data block itself */
80 extern int64_t global_offset_changed; /* defined in nasm.c */
82 static struct hash_table ltab; /* labels hash table */
83 static union label *ldata; /* all label data blocks */
84 static union label *lfree; /* labels free block */
85 static struct permts *perm_head; /* start of perm. text storage */
86 static struct permts *perm_tail; /* end of perm. text storage */
88 static void init_block(union label *blk);
89 static char *perm_copy(const char *string);
91 static char *prevlabel;
93 static bool initialized = false;
95 char lprefix[PREFIX_MAX] = { 0 };
96 char lpostfix[PREFIX_MAX] = { 0 };
99 * Internal routine: finds the `union label' corresponding to the
100 * given label name. Creates a new one, if it isn't found, and if
101 * `create' is true.
103 static union label *find_label(char *label, int create)
105 char *prev;
106 int prevlen, len;
107 union label *lptr, **lpp;
108 char label_str[IDLEN_MAX];
109 struct hash_insert ip;
111 if (islocal(label)) {
112 prev = prevlabel;
113 prevlen = strlen(prev);
114 len = strlen(label);
115 if (prevlen+len >= IDLEN_MAX)
116 return NULL; /* Error... */
117 memcpy(label_str, prev, prevlen);
118 memcpy(label_str+prevlen, label, len+1);
119 label = label_str;
120 } else {
121 prev = "";
122 prevlen = 0;
125 lpp = (union label **) hash_find(&ltab, label, &ip);
126 lptr = lpp ? *lpp : NULL;
128 if (lptr || !create)
129 return lptr;
131 /* Create a new label... */
132 if (lfree->admin.movingon == END_BLOCK) {
134 * must allocate a new block
136 lfree->admin.next =
137 (union label *)nasm_malloc(LBLK_SIZE);
138 lfree = lfree->admin.next;
139 init_block(lfree);
142 lfree->admin.movingon = BOGUS_VALUE;
143 lfree->defn.label = perm_copy(label);
144 lfree->defn.special = NULL;
145 lfree->defn.is_global = NOT_DEFINED_YET;
147 hash_add(&ip, lfree->defn.label, lfree);
148 return lfree++;
151 bool lookup_label(char *label, int32_t *segment, int64_t *offset)
153 union label *lptr;
155 if (!initialized)
156 return false;
158 lptr = find_label(label, 0);
159 if (lptr && (lptr->defn.is_global & DEFINED_BIT)) {
160 *segment = lptr->defn.segment;
161 *offset = lptr->defn.offset;
162 return true;
163 } else
164 return false;
167 bool is_extern(char *label)
169 union label *lptr;
171 if (!initialized)
172 return false;
174 lptr = find_label(label, 0);
175 return (lptr && (lptr->defn.is_global & EXTERN_BIT));
178 void redefine_label(char *label, int32_t segment, int64_t offset, char *special,
179 bool is_norm, bool isextrn, struct ofmt *ofmt,
180 efunc error)
182 union label *lptr;
183 int exi;
185 /* This routine possibly ought to check for phase errors. Most assemblers
186 * check for phase errors at this point. I don't know whether phase errors
187 * are even possible, nor whether they are checked somewhere else
190 (void)special; /* Don't warn that this parameter is unused */
191 (void)is_norm; /* Don't warn that this parameter is unused */
192 (void)isextrn; /* Don't warn that this parameter is unused */
193 (void)ofmt; /* Don't warn that this parameter is unused */
195 #ifdef DEBUG
196 #if DEBUG<3
197 if (!strncmp(label, "debugdump", 9))
198 #endif
199 error(ERR_DEBUG, "redefine_label (%s, %ld, %08lx, %s, %d, %d)",
200 label, segment, offset, special, is_norm, isextrn);
201 #endif
203 lptr = find_label(label, 1);
204 if (!lptr)
205 error(ERR_PANIC, "can't find label `%s' on pass two", label);
207 if (!islocal(label)) {
208 if (!islocalchar(*label) && lptr->defn.is_norm)
209 prevlabel = lptr->defn.label;
212 if (lptr->defn.offset != offset) global_offset_changed++;
213 lptr->defn.offset = offset;
214 lptr->defn.segment = segment;
216 if (pass0 == 1) {
217 exi = !!(lptr->defn.is_global & GLOBAL_BIT);
218 if (exi) {
219 char *xsymbol;
220 int slen;
221 slen = strlen(lprefix);
222 slen += strlen(lptr->defn.label);
223 slen += strlen(lpostfix);
224 slen++; /* room for that null char */
225 xsymbol = nasm_malloc(slen);
226 snprintf(xsymbol, slen, "%s%s%s", lprefix, lptr->defn.label,
227 lpostfix);
229 ofmt->symdef(xsymbol, segment, offset, exi,
230 special ? special : lptr->defn.special);
231 ofmt->current_dfmt->debug_deflabel(xsymbol, segment, offset,
232 exi,
233 special ? special : lptr->
234 defn.special);
235 /** nasm_free(xsymbol); ! outobj.c stores the pointer; ouch!!! **/
236 } else {
237 if ((lptr->defn.is_global & (GLOBAL_BIT | EXTERN_BIT)) !=
238 EXTERN_BIT) {
239 ofmt->symdef(lptr->defn.label, segment, offset, exi,
240 special ? special : lptr->defn.special);
241 ofmt->current_dfmt->debug_deflabel(label, segment, offset,
242 exi,
243 special ? special :
244 lptr->defn.special);
248 /* if (pass0 == 1) */
251 void define_label(char *label, int32_t segment, int64_t offset, char *special,
252 bool is_norm, bool isextrn, struct ofmt *ofmt, efunc error)
254 union label *lptr;
255 int exi;
257 #ifdef DEBUG
258 #if DEBUG<3
259 if (!strncmp(label, "debugdump", 9))
260 #endif
261 error(ERR_DEBUG, "define_label (%s, %ld, %08lx, %s, %d, %d)",
262 label, segment, offset, special, is_norm, isextrn);
263 #endif
264 lptr = find_label(label, 1);
265 if (lptr->defn.is_global & DEFINED_BIT) {
266 error(ERR_NONFATAL, "symbol `%s' redefined", label);
267 return;
269 lptr->defn.is_global |= DEFINED_BIT;
270 if (isextrn)
271 lptr->defn.is_global |= EXTERN_BIT;
273 if (!islocalchar(label[0]) && is_norm) /* not local, but not special either */
274 prevlabel = lptr->defn.label;
275 else if (islocal(label) && !*prevlabel) {
276 error(ERR_NONFATAL, "attempt to define a local label before any"
277 " non-local labels");
280 lptr->defn.segment = segment;
281 lptr->defn.offset = offset;
282 lptr->defn.is_norm = (!islocalchar(label[0]) && is_norm);
284 if (pass0 == 1 || (!is_norm && !isextrn && (segment > 0) && (segment & 1))) {
285 exi = !!(lptr->defn.is_global & GLOBAL_BIT);
286 if (exi) {
287 char *xsymbol;
288 int slen;
289 slen = strlen(lprefix);
290 slen += strlen(lptr->defn.label);
291 slen += strlen(lpostfix);
292 slen++; /* room for that null char */
293 xsymbol = nasm_malloc(slen);
294 snprintf(xsymbol, slen, "%s%s%s", lprefix, lptr->defn.label,
295 lpostfix);
297 ofmt->symdef(xsymbol, segment, offset, exi,
298 special ? special : lptr->defn.special);
299 ofmt->current_dfmt->debug_deflabel(xsymbol, segment, offset,
300 exi,
301 special ? special : lptr->
302 defn.special);
303 /** nasm_free(xsymbol); ! outobj.c stores the pointer; ouch!!! **/
304 } else {
305 if ((lptr->defn.is_global & (GLOBAL_BIT | EXTERN_BIT)) !=
306 EXTERN_BIT) {
307 ofmt->symdef(lptr->defn.label, segment, offset, exi,
308 special ? special : lptr->defn.special);
309 ofmt->current_dfmt->debug_deflabel(label, segment, offset,
310 exi,
311 special ? special :
312 lptr->defn.special);
315 } /* if (pass0 == 1) */
318 void define_common(char *label, int32_t segment, int32_t size, char *special,
319 struct ofmt *ofmt, efunc error)
321 union label *lptr;
323 lptr = find_label(label, 1);
324 if (lptr->defn.is_global & DEFINED_BIT) {
325 error(ERR_NONFATAL, "symbol `%s' redefined", label);
326 return;
328 lptr->defn.is_global |= DEFINED_BIT;
330 if (!islocalchar(label[0])) /* not local, but not special either */
331 prevlabel = lptr->defn.label;
332 else
333 error(ERR_NONFATAL, "attempt to define a local label as a "
334 "common variable");
336 lptr->defn.segment = segment;
337 lptr->defn.offset = 0;
339 ofmt->symdef(lptr->defn.label, segment, size, 2,
340 special ? special : lptr->defn.special);
341 ofmt->current_dfmt->debug_deflabel(lptr->defn.label, segment, size, 2,
342 special ? special : lptr->defn.
343 special);
346 void declare_as_global(char *label, char *special, efunc error)
348 union label *lptr;
350 if (islocal(label)) {
351 error(ERR_NONFATAL, "attempt to declare local symbol `%s' as"
352 " global", label);
353 return;
355 lptr = find_label(label, 1);
356 switch (lptr->defn.is_global & TYPE_MASK) {
357 case NOT_DEFINED_YET:
358 lptr->defn.is_global = GLOBAL_PLACEHOLDER;
359 lptr->defn.special = special ? perm_copy(special) : NULL;
360 break;
361 case GLOBAL_PLACEHOLDER: /* already done: silently ignore */
362 case GLOBAL_SYMBOL:
363 break;
364 case LOCAL_SYMBOL:
365 if (!(lptr->defn.is_global & EXTERN_BIT)) {
366 error(ERR_WARNING, "symbol `%s': GLOBAL directive "
367 "after symbol definition is an experimental feature", label);
368 lptr->defn.is_global = GLOBAL_SYMBOL;
370 break;
374 int init_labels(void)
376 hash_init(&ltab, HASH_LARGE);
378 ldata = lfree = (union label *)nasm_malloc(LBLK_SIZE);
379 init_block(lfree);
381 perm_head =
382 perm_tail = (struct permts *)nasm_malloc(sizeof(struct permts));
384 perm_head->next = NULL;
385 perm_head->size = PERMTS_SIZE;
386 perm_head->usage = 0;
388 prevlabel = "";
390 initialized = true;
392 return 0;
395 void cleanup_labels(void)
397 union label *lptr, *lhold;
399 initialized = false;
401 hash_free(&ltab);
403 lptr = lhold = ldata;
404 while (lptr) {
405 lptr = &lptr[LABEL_BLOCK-1];
406 lptr = lptr->admin.next;
407 nasm_free(lhold);
408 lhold = lptr;
411 while (perm_head) {
412 perm_tail = perm_head;
413 perm_head = perm_head->next;
414 nasm_free(perm_tail);
418 static void init_block(union label *blk)
420 int j;
422 for (j = 0; j < LABEL_BLOCK - 1; j++)
423 blk[j].admin.movingon = END_LIST;
424 blk[LABEL_BLOCK - 1].admin.movingon = END_BLOCK;
425 blk[LABEL_BLOCK - 1].admin.next = NULL;
428 static char *perm_copy(const char *string)
430 char *p;
431 int len = strlen(string)+1;
433 if (perm_tail->size - perm_tail->usage < len) {
434 perm_tail->next =
435 (struct permts *)nasm_malloc(sizeof(struct permts));
436 perm_tail = perm_tail->next;
437 perm_tail->next = NULL;
438 perm_tail->size = PERMTS_SIZE;
439 perm_tail->usage = 0;
441 p = perm_tail->data + perm_tail->usage;
442 memcpy(p, string, len);
443 perm_tail->usage += len;
445 return p;
448 char *local_scope(char *label)
450 return islocal(label) ? prevlabel : "";
454 * Notes regarding bug involving redefinition of external segments.
456 * Up to and including v0.97, the following code didn't work. From 0.97
457 * developers release 2 onwards, it will generate an error.
459 * EXTERN extlabel
460 * newlabel EQU extlabel + 1
462 * The results of allowing this code through are that two import records
463 * are generated, one for 'extlabel' and one for 'newlabel'.
465 * The reason for this is an inadequacy in the defined interface between
466 * the label manager and the output formats. The problem lies in how the
467 * output format driver tells that a label is an external label for which
468 * a label import record must be produced. Most (all except bin?) produce
469 * the record if the segment number of the label is not one of the internal
470 * segments that the output driver is producing.
472 * A simple fix to this would be to make the output formats keep track of
473 * which symbols they've produced import records for, and make them not
474 * produce import records for segments that are already defined.
476 * The best way, which is slightly harder but reduces duplication of code
477 * and should therefore make the entire system smaller and more stable is
478 * to change the interface between assembler, define_label(), and
479 * the output module. The changes that are needed are:
481 * The semantics of the 'isextern' flag passed to define_label() need
482 * examining. This information may or may not tell us what we need to
483 * know (ie should we be generating an import record at this point for this
484 * label). If these aren't the semantics, the semantics should be changed
485 * to this.
487 * The output module interface needs changing, so that the `isextern' flag
488 * is passed to the module, so that it can be easily tested for.