NASM 0.95
[platform/upstream/nasm.git] / labels.c
1 /* labels.c  label handling for the Netwide Assembler
2  *
3  * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
4  * Julian Hall. All rights reserved. The software is
5  * redistributable under the licence given in the file "Licence"
6  * distributed in the NASM archive.
7  */
8
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include "nasm.h"
13 #include "nasmlib.h"
14
15 /*
16  * A local label is one that begins with exactly one period. Things
17  * that begin with _two_ periods are NASM-specific things.
18  */
19 #define islocal(l) ((l)[0] == '.' && (l)[1] != '.')
20
21 #define LABEL_BLOCK  320               /* no. of labels/block */
22 #define LBLK_SIZE    (LABEL_BLOCK*sizeof(union label))
23 #define LABEL_HASHES 32                /* no. of hash table entries */
24
25 #define END_LIST -3                    /* don't clash with NO_SEG! */
26 #define END_BLOCK -2
27 #define BOGUS_VALUE -4
28
29 #define PERMTS_SIZE  4096              /* size of text blocks */
30
31 /* values for label.defn.is_global */
32 #define NOT_DEFINED_YET 0
33 #define LOCAL_SYMBOL 1
34 #define GLOBAL_SYMBOL 2
35 #define GLOBAL_PLACEHOLDER 3
36
37 union label {                          /* actual label structures */
38     struct {
39         long segment, offset;
40         char *label;
41         int is_global;
42     } defn;
43     struct {
44         long movingon, dummy;
45         union label *next;
46     } admin;
47 };
48
49 struct permts {                        /* permanent text storage */
50     struct permts *next;               /* for the linked list */
51     int size, usage;                   /* size and used space in ... */
52     char data[PERMTS_SIZE];            /* ... the data block itself */
53 };
54
55 static union label *ltab[LABEL_HASHES];/* using a hash table */
56 static union label *lfree[LABEL_HASHES];/* pointer into the above */
57 static struct permts *perm_head;      /* start of perm. text storage */
58 static struct permts *perm_tail;      /* end of perm. text storage */
59
60 static void init_block (union label *blk);
61 static char *perm_copy (char *string1, char *string2);
62
63 static char *prevlabel;
64
65 /*
66  * Internal routine: finds the `union label' corresponding to the
67  * given label name. Creates a new one, if it isn't found, and if
68  * `create' is TRUE.
69  */
70 static union label *find_label (char *label, int create) {
71     int hash = 0;
72     char *p, *prev;
73     int prevlen;
74     union label *lptr;
75
76     if (islocal(label))
77         prev = prevlabel;
78     else
79         prev = "";
80     prevlen = strlen(prev);
81     p = prev;
82     while (*p) hash += *p++;
83     p = label;
84     while (*p) hash += *p++;
85     hash %= LABEL_HASHES;
86     lptr = ltab[hash];
87     while (lptr->admin.movingon != END_LIST) {
88         if (lptr->admin.movingon == END_BLOCK) {
89             lptr = lptr->admin.next;
90             if (!lptr)
91                 break;
92         }
93         if (!strncmp(lptr->defn.label, prev, prevlen) &&
94             !strcmp(lptr->defn.label+prevlen, label))
95             return lptr;
96         lptr++;
97     }
98     if (create) {
99         if (lfree[hash]->admin.movingon == END_BLOCK) {
100             /*
101              * must allocate a new block
102              */
103             lfree[hash]->admin.next = (union label *) nasm_malloc (LBLK_SIZE);
104             lfree[hash] = lfree[hash]->admin.next;
105             init_block(lfree[hash]);
106         }
107
108         lfree[hash]->admin.movingon = BOGUS_VALUE;
109         lfree[hash]->defn.label = perm_copy (prev, label);
110         lfree[hash]->defn.is_global = NOT_DEFINED_YET;
111         return lfree[hash]++;
112     } else
113         return NULL;
114 }
115
116 int lookup_label (char *label, long *segment, long *offset) {
117     union label *lptr;
118
119     lptr = find_label (label, 0);
120     if (lptr && (lptr->defn.is_global == LOCAL_SYMBOL ||
121                  lptr->defn.is_global == GLOBAL_SYMBOL)) {
122         *segment = lptr->defn.segment;
123         *offset = lptr->defn.offset;
124         return 1;
125     } else
126         return 0;
127 }
128
129 void define_label_stub (char *label, efunc error) {
130     union label *lptr;
131
132     if (!islocal(label)) {
133         lptr = find_label (label, 1);
134         if (!lptr)
135             error (ERR_PANIC, "can't find label `%s' on pass two", label);
136         if (*label != '.')
137             prevlabel = lptr->defn.label;
138     }
139 }
140
141 void define_label (char *label, long segment, long offset,
142                    struct ofmt *ofmt, efunc error) {
143     union label *lptr;
144
145     lptr = find_label (label, 1);
146     switch (lptr->defn.is_global) {
147       case NOT_DEFINED_YET:
148         lptr->defn.is_global = LOCAL_SYMBOL;
149         break;
150       case GLOBAL_PLACEHOLDER:
151         lptr->defn.is_global = GLOBAL_SYMBOL;
152         break;
153       default:
154         error(ERR_NONFATAL, "symbol `%s' redefined", label);
155         return;
156     }
157
158     if (label[0] != '.')               /* not local, but not special either */
159         prevlabel = lptr->defn.label;
160     else if (label[1] != '.' && !*prevlabel)
161         error(ERR_NONFATAL, "attempt to define a local label before any"
162               " non-local labels");
163
164     lptr->defn.segment = segment;
165     lptr->defn.offset = offset;
166
167     ofmt->symdef (lptr->defn.label, segment, offset,
168                   lptr->defn.is_global == GLOBAL_SYMBOL);
169 }
170
171 void define_common (char *label, long segment, long size,
172                     struct ofmt *ofmt, efunc error) {
173     union label *lptr;
174
175     lptr = find_label (label, 1);
176     switch (lptr->defn.is_global) {
177       case NOT_DEFINED_YET:
178         lptr->defn.is_global = LOCAL_SYMBOL;
179         break;
180       case GLOBAL_PLACEHOLDER:
181         lptr->defn.is_global = GLOBAL_SYMBOL;
182         break;
183       default:
184         error(ERR_NONFATAL, "symbol `%s' redefined", label);
185         return;
186     }
187
188     if (label[0] != '.')               /* not local, but not special either */
189         prevlabel = lptr->defn.label;
190     else
191         error(ERR_NONFATAL, "attempt to define a local label as a "
192               "common variable");
193
194     lptr->defn.segment = segment;
195     lptr->defn.offset = 0;
196
197     ofmt->symdef (lptr->defn.label, segment, size, 2);
198 }
199
200 void declare_as_global (char *label, efunc error) {
201     union label *lptr;
202
203     if (islocal(label)) {
204         error(ERR_NONFATAL, "attempt to declare local symbol `%s' as"
205               " global", label);
206         return;
207     }
208     lptr = find_label (label, 1);
209     switch (lptr->defn.is_global) {
210       case NOT_DEFINED_YET:
211         lptr->defn.is_global = GLOBAL_PLACEHOLDER;
212         break;
213       case GLOBAL_PLACEHOLDER:         /* already done: silently ignore */
214       case GLOBAL_SYMBOL:
215         break;
216       case LOCAL_SYMBOL:
217         error(ERR_NONFATAL, "symbol `%s': [GLOBAL] directive must"
218               " appear before symbol definition", label);
219         break;
220     }
221 }
222
223 int init_labels (void) {
224     int i;
225
226     for (i=0; i<LABEL_HASHES; i++) {
227         ltab[i] = (union label *) nasm_malloc (LBLK_SIZE);
228         if (!ltab[i])
229             return -1;                 /* can't initialise, panic */
230         init_block (ltab[i]);
231         lfree[i] = ltab[i];
232     }
233
234     perm_head = perm_tail = (struct permts *) nasm_malloc (sizeof(struct permts));
235     if (!perm_head)
236         return -1;
237
238     perm_head->next = NULL;
239     perm_head->size = PERMTS_SIZE;
240     perm_head->usage = 0;
241
242     prevlabel = "";
243
244     return 0;
245 }
246
247 void cleanup_labels (void) {
248     int i;
249
250     for (i=0; i<LABEL_HASHES; i++) {
251         union label *lptr, *lhold;
252
253         lptr = lhold = ltab[i];
254
255         while (lptr) {
256             while (lptr->admin.movingon != END_BLOCK) lptr++;
257             lptr = lptr->admin.next;
258             nasm_free (lhold);
259             lhold = lptr;
260         }
261     }
262
263     while (perm_head) {
264         perm_tail = perm_head;
265         perm_head = perm_head->next;
266         nasm_free (perm_tail);
267     }
268 }
269
270 static void init_block (union label *blk) {
271     int j;
272
273     for (j=0; j<LABEL_BLOCK-1; j++)
274         blk[j].admin.movingon = END_LIST;
275     blk[LABEL_BLOCK-1].admin.movingon = END_BLOCK;
276     blk[LABEL_BLOCK-1].admin.next = NULL;
277 }
278
279 static char *perm_copy (char *string1, char *string2) {
280     char *p, *q;
281     int len = strlen(string1)+strlen(string2)+1;
282
283     if (perm_tail->size - perm_tail->usage < len) {
284         perm_tail->next = (struct permts *)nasm_malloc(sizeof(struct permts));
285         perm_tail = perm_tail->next;
286         perm_tail->next = NULL;
287         perm_tail->size = PERMTS_SIZE;
288         perm_tail->usage = 0;
289     }
290     p = q = perm_tail->data + perm_tail->usage;
291     while ( (*q = *string1++) ) q++;
292     while ( (*q++ = *string2++) );
293     perm_tail->usage = q - perm_tail->data;
294
295     return p;
296 }