1 /*
2 * YASM assembler virtual line mapping handling (for parse stage)
3 *
4 * Copyright (C) 2002-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 "coretype.h"
30 #include "hamt.h"
31
32 #include "errwarn.h"
33 #include "linemap.h"
34
35
36 typedef struct line_mapping {
37 /* monotonically increasing virtual line */
38 unsigned long line;
39
40 /* related info */
41 /* "original" source filename */
42 /*@null@*/ /*@dependent@*/ const char *filename;
43 /* "original" source base line number */
44 unsigned long file_line;
45 /* "original" source line number increment (for following lines) */
46 unsigned long line_inc;
47 } line_mapping;
48
49 typedef struct line_source_info {
50 /* first bytecode on line; NULL if no bytecodes on line */
51 /*@null@*/ /*@dependent@*/ yasm_bytecode *bc;
52
53 /* source code line */
54 /*@owned@*/ char *source;
55 } line_source_info;
56
57 struct yasm_linemap {
58 /* Shared storage for filenames */
59 /*@only@*/ /*@null@*/ HAMT *filenames;
60
61 /* Current virtual line number. */
62 unsigned long current;
63
64 /* Mappings from virtual to physical line numbers */
65 struct line_mapping *map_vector;
66 unsigned long map_size;
67 unsigned long map_allocated;
68
69 /* Bytecode and source line information */
70 /*@only@*/ line_source_info *source_info;
71 size_t source_info_size;
72 };
73
74 static void
filename_delete_one(void * d)75 filename_delete_one(/*@only@*/ void *d)
76 {
77 yasm_xfree(d);
78 }
79
80 void
yasm_linemap_set(yasm_linemap * linemap,const char * filename,unsigned long virtual_line,unsigned long file_line,unsigned long line_inc)81 yasm_linemap_set(yasm_linemap *linemap, const char *filename,
82 unsigned long virtual_line, unsigned long file_line,
83 unsigned long line_inc)
84 {
85 char *copy;
86 unsigned long i;
87 int replace = 0;
88 line_mapping *mapping = NULL;
89
90 if (virtual_line == 0) {
91 virtual_line = linemap->current;
92 }
93
94 /* Replace all existing mappings that have line numbers >= this one. */
95 for (i = linemap->map_size; i > 0; i--) {
96 if (linemap->map_vector[i-1].line < virtual_line) {
97 if (i < linemap->map_size) {
98 mapping = &linemap->map_vector[i];
99 linemap->map_size = i + 1;
100 }
101 break;
102 }
103 }
104
105 if (mapping == NULL) {
106 /* Create a new mapping in the map */
107 if (linemap->map_size >= linemap->map_allocated) {
108 /* allocate another size bins when full for 2x space */
109 linemap->map_vector = yasm_xrealloc(linemap->map_vector,
110 2*linemap->map_allocated*sizeof(line_mapping));
111 linemap->map_allocated *= 2;
112 }
113 mapping = &linemap->map_vector[linemap->map_size];
114 linemap->map_size++;
115 }
116
117 /* Fill it */
118
119 if (!filename) {
120 if (linemap->map_size >= 2)
121 mapping->filename =
122 linemap->map_vector[linemap->map_size-2].filename;
123 else
124 filename = "unknown";
125 }
126 if (filename) {
127 /* Copy the filename (via shared storage) */
128 copy = yasm__xstrdup(filename);
129 /*@-aliasunique@*/
130 mapping->filename = HAMT_insert(linemap->filenames, copy, copy,
131 &replace, filename_delete_one);
132 /*@=aliasunique@*/
133 }
134
135 mapping->line = virtual_line;
136 mapping->file_line = file_line;
137 mapping->line_inc = line_inc;
138 }
139
140 unsigned long
yasm_linemap_poke(yasm_linemap * linemap,const char * filename,unsigned long file_line)141 yasm_linemap_poke(yasm_linemap *linemap, const char *filename,
142 unsigned long file_line)
143 {
144 unsigned long line;
145 line_mapping *mapping;
146
147 linemap->current++;
148 yasm_linemap_set(linemap, filename, 0, file_line, 0);
149
150 mapping = &linemap->map_vector[linemap->map_size-1];
151
152 line = linemap->current;
153
154 linemap->current++;
155 yasm_linemap_set(linemap, mapping->filename, 0,
156 mapping->file_line +
157 mapping->line_inc*(linemap->current-2-mapping->line),
158 mapping->line_inc);
159
160 return line;
161 }
162
163 yasm_linemap *
yasm_linemap_create(void)164 yasm_linemap_create(void)
165 {
166 size_t i;
167 yasm_linemap *linemap = yasm_xmalloc(sizeof(yasm_linemap));
168
169 linemap->filenames = HAMT_create(0, yasm_internal_error_);
170
171 linemap->current = 1;
172
173 /* initialize mapping vector */
174 linemap->map_vector = yasm_xmalloc(8*sizeof(line_mapping));
175 linemap->map_size = 0;
176 linemap->map_allocated = 8;
177
178 /* initialize source line information array */
179 linemap->source_info_size = 2;
180 linemap->source_info = yasm_xmalloc(linemap->source_info_size *
181 sizeof(line_source_info));
182 for (i=0; i<linemap->source_info_size; i++) {
183 linemap->source_info[i].bc = NULL;
184 linemap->source_info[i].source = NULL;
185 }
186
187 return linemap;
188 }
189
190 void
yasm_linemap_destroy(yasm_linemap * linemap)191 yasm_linemap_destroy(yasm_linemap *linemap)
192 {
193 size_t i;
194 for (i=0; i<linemap->source_info_size; i++) {
195 if (linemap->source_info[i].source)
196 yasm_xfree(linemap->source_info[i].source);
197 }
198 yasm_xfree(linemap->source_info);
199
200 yasm_xfree(linemap->map_vector);
201
202 if (linemap->filenames)
203 HAMT_destroy(linemap->filenames, filename_delete_one);
204
205 yasm_xfree(linemap);
206 }
207
208 unsigned long
yasm_linemap_get_current(yasm_linemap * linemap)209 yasm_linemap_get_current(yasm_linemap *linemap)
210 {
211 return linemap->current;
212 }
213
214 void
yasm_linemap_add_source(yasm_linemap * linemap,yasm_bytecode * bc,const char * source)215 yasm_linemap_add_source(yasm_linemap *linemap, yasm_bytecode *bc,
216 const char *source)
217 {
218 size_t i;
219
220 while (linemap->current > linemap->source_info_size) {
221 /* allocate another size bins when full for 2x space */
222 linemap->source_info = yasm_xrealloc(linemap->source_info,
223 2*linemap->source_info_size*sizeof(line_source_info));
224 for (i=linemap->source_info_size; i<linemap->source_info_size*2; i++) {
225 linemap->source_info[i].bc = NULL;
226 linemap->source_info[i].source = NULL;
227 }
228 linemap->source_info_size *= 2;
229 }
230
231 /* Delete existing info for that line (if any) */
232 if (linemap->source_info[linemap->current-1].source)
233 yasm_xfree(linemap->source_info[linemap->current-1].source);
234
235 linemap->source_info[linemap->current-1].bc = bc;
236 linemap->source_info[linemap->current-1].source = yasm__xstrdup(source);
237 }
238
239 unsigned long
yasm_linemap_goto_next(yasm_linemap * linemap)240 yasm_linemap_goto_next(yasm_linemap *linemap)
241 {
242 return ++(linemap->current);
243 }
244
245 void
yasm_linemap_lookup(yasm_linemap * linemap,unsigned long line,const char ** filename,unsigned long * file_line)246 yasm_linemap_lookup(yasm_linemap *linemap, unsigned long line,
247 const char **filename, unsigned long *file_line)
248 {
249 line_mapping *mapping;
250 unsigned long vindex, step;
251
252 assert(line <= linemap->current);
253
254 /* Binary search through map to find highest line_index <= index */
255 vindex = 0;
256 /* start step as the greatest power of 2 <= size */
257 step = 1;
258 while (step*2<=linemap->map_size)
259 step*=2;
260 while (step>0) {
261 if (vindex+step < linemap->map_size
262 && linemap->map_vector[vindex+step].line <= line)
263 vindex += step;
264 step /= 2;
265 }
266 mapping = &linemap->map_vector[vindex];
267
268 *filename = mapping->filename;
269 *file_line = (line ? mapping->file_line + mapping->line_inc*(line-mapping->line) : 0);
270 }
271
272 int
yasm_linemap_traverse_filenames(yasm_linemap * linemap,void * d,int (* func)(const char * filename,void * d))273 yasm_linemap_traverse_filenames(yasm_linemap *linemap, /*@null@*/ void *d,
274 int (*func) (const char *filename, void *d))
275 {
276 return HAMT_traverse(linemap->filenames, d, (int (*) (void *, void *))func);
277 }
278
279 int
yasm_linemap_get_source(yasm_linemap * linemap,unsigned long line,yasm_bytecode ** bcp,const char ** sourcep)280 yasm_linemap_get_source(yasm_linemap *linemap, unsigned long line,
281 yasm_bytecode **bcp, const char **sourcep)
282 {
283 if (line > linemap->source_info_size) {
284 *bcp = NULL;
285 *sourcep = NULL;
286 return 1;
287 }
288
289 *bcp = linemap->source_info[line-1].bc;
290 *sourcep = linemap->source_info[line-1].source;
291
292 return (!(*sourcep));
293 }
294