diff options
Diffstat (limited to 'src/host')
-rw-r--r-- | src/host/.gitignore | 3 | ||||
-rw-r--r-- | src/host/buildvm.c | 504 | ||||
-rw-r--r-- | src/host/buildvm.h | 104 | ||||
-rw-r--r-- | src/host/buildvm_asm.c | 283 | ||||
-rw-r--r-- | src/host/buildvm_fold.c | 229 | ||||
-rw-r--r-- | src/host/buildvm_lib.c | 377 | ||||
-rw-r--r-- | src/host/buildvm_peobj.c | 352 |
7 files changed, 1852 insertions, 0 deletions
diff --git a/src/host/.gitignore b/src/host/.gitignore new file mode 100644 index 00000000..762ac2a0 --- /dev/null +++ b/src/host/.gitignore | |||
@@ -0,0 +1,3 @@ | |||
1 | minilua | ||
2 | buildvm | ||
3 | buildvm_arch.h | ||
diff --git a/src/host/buildvm.c b/src/host/buildvm.c new file mode 100644 index 00000000..7dbf2cae --- /dev/null +++ b/src/host/buildvm.c | |||
@@ -0,0 +1,504 @@ | |||
1 | /* | ||
2 | ** LuaJIT VM builder. | ||
3 | ** Copyright (C) 2005-2012 Mike Pall. See Copyright Notice in luajit.h | ||
4 | ** | ||
5 | ** This is a tool to build the hand-tuned assembler code required for | ||
6 | ** LuaJIT's bytecode interpreter. It supports a variety of output formats | ||
7 | ** to feed different toolchains (see usage() below). | ||
8 | ** | ||
9 | ** This tool is not particularly optimized because it's only used while | ||
10 | ** _building_ LuaJIT. There's no point in distributing or installing it. | ||
11 | ** Only the object code generated by this tool is linked into LuaJIT. | ||
12 | ** | ||
13 | ** Caveat: some memory is not free'd, error handling is lazy. | ||
14 | ** It's a one-shot tool -- any effort fixing this would be wasted. | ||
15 | */ | ||
16 | |||
17 | #include "buildvm.h" | ||
18 | #include "lj_obj.h" | ||
19 | #include "lj_gc.h" | ||
20 | #include "lj_bc.h" | ||
21 | #include "lj_ir.h" | ||
22 | #include "lj_ircall.h" | ||
23 | #include "lj_frame.h" | ||
24 | #include "lj_dispatch.h" | ||
25 | #if LJ_HASFFI | ||
26 | #include "lj_ctype.h" | ||
27 | #include "lj_ccall.h" | ||
28 | #endif | ||
29 | #include "luajit.h" | ||
30 | |||
31 | #if defined(_WIN32) | ||
32 | #include <fcntl.h> | ||
33 | #include <io.h> | ||
34 | #endif | ||
35 | |||
36 | /* ------------------------------------------------------------------------ */ | ||
37 | |||
38 | /* DynASM glue definitions. */ | ||
39 | #define Dst ctx | ||
40 | #define Dst_DECL BuildCtx *ctx | ||
41 | #define Dst_REF (ctx->D) | ||
42 | #define DASM_CHECKS 1 | ||
43 | |||
44 | #include "../dynasm/dasm_proto.h" | ||
45 | |||
46 | /* Glue macros for DynASM. */ | ||
47 | static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type); | ||
48 | |||
49 | #define DASM_EXTERN(ctx, addr, idx, type) \ | ||
50 | collect_reloc(ctx, addr, idx, type) | ||
51 | |||
52 | /* ------------------------------------------------------------------------ */ | ||
53 | |||
54 | /* Avoid trouble if cross-compiling for an x86 target. Speed doesn't matter. */ | ||
55 | #define DASM_ALIGNED_WRITES 1 | ||
56 | |||
57 | /* Embed architecture-specific DynASM encoder. */ | ||
58 | #if LJ_TARGET_X86ORX64 | ||
59 | #include "../dynasm/dasm_x86.h" | ||
60 | #elif LJ_TARGET_ARM | ||
61 | #include "../dynasm/dasm_arm.h" | ||
62 | #elif LJ_TARGET_PPC | ||
63 | #include "../dynasm/dasm_ppc.h" | ||
64 | #elif LJ_TARGET_PPCSPE | ||
65 | #include "../dynasm/dasm_ppc.h" | ||
66 | #elif LJ_TARGET_MIPS | ||
67 | #include "../dynasm/dasm_mips.h" | ||
68 | #else | ||
69 | #error "No support for this architecture (yet)" | ||
70 | #endif | ||
71 | |||
72 | /* Embed generated architecture-specific backend. */ | ||
73 | #include "buildvm_arch.h" | ||
74 | |||
75 | /* ------------------------------------------------------------------------ */ | ||
76 | |||
77 | void owrite(BuildCtx *ctx, const void *ptr, size_t sz) | ||
78 | { | ||
79 | if (fwrite(ptr, 1, sz, ctx->fp) != sz) { | ||
80 | fprintf(stderr, "Error: cannot write to output file: %s\n", | ||
81 | strerror(errno)); | ||
82 | exit(1); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | /* ------------------------------------------------------------------------ */ | ||
87 | |||
88 | /* Emit code as raw bytes. Only used for DynASM debugging. */ | ||
89 | static void emit_raw(BuildCtx *ctx) | ||
90 | { | ||
91 | owrite(ctx, ctx->code, ctx->codesz); | ||
92 | } | ||
93 | |||
94 | /* -- Build machine code -------------------------------------------------- */ | ||
95 | |||
96 | static const char *sym_decorate(BuildCtx *ctx, | ||
97 | const char *prefix, const char *suffix) | ||
98 | { | ||
99 | char name[256]; | ||
100 | char *p; | ||
101 | #if LJ_64 | ||
102 | const char *symprefix = ctx->mode == BUILD_machasm ? "_" : ""; | ||
103 | #else | ||
104 | const char *symprefix = ctx->mode != BUILD_elfasm ? "_" : ""; | ||
105 | #endif | ||
106 | sprintf(name, "%s%s%s", symprefix, prefix, suffix); | ||
107 | p = strchr(name, '@'); | ||
108 | if (p) { | ||
109 | if (!LJ_64 && (ctx->mode == BUILD_coffasm || ctx->mode == BUILD_peobj)) | ||
110 | name[0] = '@'; | ||
111 | else | ||
112 | *p = '\0'; | ||
113 | } | ||
114 | p = (char *)malloc(strlen(name)+1); /* MSVC doesn't like strdup. */ | ||
115 | strcpy(p, name); | ||
116 | return p; | ||
117 | } | ||
118 | |||
119 | #define NRELOCSYM (sizeof(extnames)/sizeof(extnames[0])-1) | ||
120 | |||
121 | static int relocmap[NRELOCSYM]; | ||
122 | |||
123 | /* Collect external relocations. */ | ||
124 | static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type) | ||
125 | { | ||
126 | if (ctx->nreloc >= BUILD_MAX_RELOC) { | ||
127 | fprintf(stderr, "Error: too many relocations, increase BUILD_MAX_RELOC.\n"); | ||
128 | exit(1); | ||
129 | } | ||
130 | if (relocmap[idx] < 0) { | ||
131 | relocmap[idx] = ctx->nrelocsym; | ||
132 | ctx->relocsym[ctx->nrelocsym] = sym_decorate(ctx, "", extnames[idx]); | ||
133 | ctx->nrelocsym++; | ||
134 | } | ||
135 | ctx->reloc[ctx->nreloc].ofs = (int32_t)(addr - ctx->code); | ||
136 | ctx->reloc[ctx->nreloc].sym = relocmap[idx]; | ||
137 | ctx->reloc[ctx->nreloc].type = type; | ||
138 | ctx->nreloc++; | ||
139 | return 0; /* Encode symbol offset of 0. */ | ||
140 | } | ||
141 | |||
142 | /* Naive insertion sort. Performance doesn't matter here. */ | ||
143 | static void sym_insert(BuildCtx *ctx, int32_t ofs, | ||
144 | const char *prefix, const char *suffix) | ||
145 | { | ||
146 | ptrdiff_t i = ctx->nsym++; | ||
147 | while (i > 0) { | ||
148 | if (ctx->sym[i-1].ofs <= ofs) | ||
149 | break; | ||
150 | ctx->sym[i] = ctx->sym[i-1]; | ||
151 | i--; | ||
152 | } | ||
153 | ctx->sym[i].ofs = ofs; | ||
154 | ctx->sym[i].name = sym_decorate(ctx, prefix, suffix); | ||
155 | } | ||
156 | |||
157 | /* Build the machine code. */ | ||
158 | static int build_code(BuildCtx *ctx) | ||
159 | { | ||
160 | int status; | ||
161 | int i; | ||
162 | |||
163 | /* Initialize DynASM structures. */ | ||
164 | ctx->nglob = GLOB__MAX; | ||
165 | ctx->glob = (void **)malloc(ctx->nglob*sizeof(void *)); | ||
166 | memset(ctx->glob, 0, ctx->nglob*sizeof(void *)); | ||
167 | ctx->nreloc = 0; | ||
168 | |||
169 | ctx->globnames = globnames; | ||
170 | ctx->relocsym = (const char **)malloc(NRELOCSYM*sizeof(const char *)); | ||
171 | ctx->nrelocsym = 0; | ||
172 | for (i = 0; i < (int)NRELOCSYM; i++) relocmap[i] = -1; | ||
173 | |||
174 | ctx->dasm_ident = DASM_IDENT; | ||
175 | ctx->dasm_arch = DASM_ARCH; | ||
176 | |||
177 | dasm_init(Dst, DASM_MAXSECTION); | ||
178 | dasm_setupglobal(Dst, ctx->glob, ctx->nglob); | ||
179 | dasm_setup(Dst, build_actionlist); | ||
180 | |||
181 | /* Call arch-specific backend to emit the code. */ | ||
182 | ctx->npc = build_backend(ctx); | ||
183 | |||
184 | /* Finalize the code. */ | ||
185 | (void)dasm_checkstep(Dst, -1); | ||
186 | if ((status = dasm_link(Dst, &ctx->codesz))) return status; | ||
187 | ctx->code = (uint8_t *)malloc(ctx->codesz); | ||
188 | if ((status = dasm_encode(Dst, (void *)ctx->code))) return status; | ||
189 | |||
190 | /* Allocate symbol table and bytecode offsets. */ | ||
191 | ctx->beginsym = sym_decorate(ctx, "", LABEL_PREFIX "vm_asm_begin"); | ||
192 | ctx->sym = (BuildSym *)malloc((ctx->npc+ctx->nglob+1)*sizeof(BuildSym)); | ||
193 | ctx->nsym = 0; | ||
194 | ctx->bc_ofs = (int32_t *)malloc(ctx->npc*sizeof(int32_t)); | ||
195 | |||
196 | /* Collect the opcodes (PC labels). */ | ||
197 | for (i = 0; i < ctx->npc; i++) { | ||
198 | int32_t ofs = dasm_getpclabel(Dst, i); | ||
199 | if (ofs < 0) return 0x22000000|i; | ||
200 | ctx->bc_ofs[i] = ofs; | ||
201 | if ((LJ_HASJIT || | ||
202 | !(i == BC_JFORI || i == BC_JFORL || i == BC_JITERL || i == BC_JLOOP || | ||
203 | i == BC_IFORL || i == BC_IITERL || i == BC_ILOOP)) && | ||
204 | (LJ_HASFFI || i != BC_KCDATA)) | ||
205 | sym_insert(ctx, ofs, LABEL_PREFIX_BC, bc_names[i]); | ||
206 | } | ||
207 | |||
208 | /* Collect the globals (named labels). */ | ||
209 | for (i = 0; i < ctx->nglob; i++) { | ||
210 | const char *gl = globnames[i]; | ||
211 | int len = (int)strlen(gl); | ||
212 | if (!ctx->glob[i]) { | ||
213 | fprintf(stderr, "Error: undefined global %s\n", gl); | ||
214 | exit(2); | ||
215 | } | ||
216 | /* Skip the _Z symbols. */ | ||
217 | if (!(len >= 2 && gl[len-2] == '_' && gl[len-1] == 'Z')) | ||
218 | sym_insert(ctx, (int32_t)((uint8_t *)(ctx->glob[i]) - ctx->code), | ||
219 | LABEL_PREFIX, globnames[i]); | ||
220 | } | ||
221 | |||
222 | /* Close the address range. */ | ||
223 | sym_insert(ctx, (int32_t)ctx->codesz, "", ""); | ||
224 | ctx->nsym--; | ||
225 | |||
226 | dasm_free(Dst); | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | /* -- Generate VM enums --------------------------------------------------- */ | ||
232 | |||
233 | const char *const bc_names[] = { | ||
234 | #define BCNAME(name, ma, mb, mc, mt) #name, | ||
235 | BCDEF(BCNAME) | ||
236 | #undef BCNAME | ||
237 | NULL | ||
238 | }; | ||
239 | |||
240 | const char *const ir_names[] = { | ||
241 | #define IRNAME(name, m, m1, m2) #name, | ||
242 | IRDEF(IRNAME) | ||
243 | #undef IRNAME | ||
244 | NULL | ||
245 | }; | ||
246 | |||
247 | const char *const irt_names[] = { | ||
248 | #define IRTNAME(name) #name, | ||
249 | IRTDEF(IRTNAME) | ||
250 | #undef IRTNAME | ||
251 | NULL | ||
252 | }; | ||
253 | |||
254 | const char *const irfpm_names[] = { | ||
255 | #define FPMNAME(name) #name, | ||
256 | IRFPMDEF(FPMNAME) | ||
257 | #undef FPMNAME | ||
258 | NULL | ||
259 | }; | ||
260 | |||
261 | const char *const irfield_names[] = { | ||
262 | #define FLNAME(name, ofs) #name, | ||
263 | IRFLDEF(FLNAME) | ||
264 | #undef FLNAME | ||
265 | NULL | ||
266 | }; | ||
267 | |||
268 | const char *const ircall_names[] = { | ||
269 | #define IRCALLNAME(cond, name, nargs, kind, type, flags) #name, | ||
270 | IRCALLDEF(IRCALLNAME) | ||
271 | #undef IRCALLNAME | ||
272 | NULL | ||
273 | }; | ||
274 | |||
275 | static const char *const trace_errors[] = { | ||
276 | #define TREDEF(name, msg) msg, | ||
277 | #include "lj_traceerr.h" | ||
278 | NULL | ||
279 | }; | ||
280 | |||
281 | static const char *lower(char *buf, const char *s) | ||
282 | { | ||
283 | char *p = buf; | ||
284 | while (*s) { | ||
285 | *p++ = (*s >= 'A' && *s <= 'Z') ? *s+0x20 : *s; | ||
286 | s++; | ||
287 | } | ||
288 | *p = '\0'; | ||
289 | return buf; | ||
290 | } | ||
291 | |||
292 | /* Emit C source code for bytecode-related definitions. */ | ||
293 | static void emit_bcdef(BuildCtx *ctx) | ||
294 | { | ||
295 | int i; | ||
296 | fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n"); | ||
297 | fprintf(ctx->fp, "LJ_DATADEF const uint16_t lj_bc_ofs[] = {\n"); | ||
298 | for (i = 0; i < ctx->npc; i++) { | ||
299 | if (i != 0) | ||
300 | fprintf(ctx->fp, ",\n"); | ||
301 | fprintf(ctx->fp, "%d", ctx->bc_ofs[i]); | ||
302 | } | ||
303 | } | ||
304 | |||
305 | /* Emit VM definitions as Lua code for debug modules. */ | ||
306 | static void emit_vmdef(BuildCtx *ctx) | ||
307 | { | ||
308 | char buf[80]; | ||
309 | int i; | ||
310 | fprintf(ctx->fp, "-- This is a generated file. DO NOT EDIT!\n\n"); | ||
311 | fprintf(ctx->fp, "module(...)\n\n"); | ||
312 | |||
313 | fprintf(ctx->fp, "bcnames = \""); | ||
314 | for (i = 0; bc_names[i]; i++) fprintf(ctx->fp, "%-6s", bc_names[i]); | ||
315 | fprintf(ctx->fp, "\"\n\n"); | ||
316 | |||
317 | fprintf(ctx->fp, "irnames = \""); | ||
318 | for (i = 0; ir_names[i]; i++) fprintf(ctx->fp, "%-6s", ir_names[i]); | ||
319 | fprintf(ctx->fp, "\"\n\n"); | ||
320 | |||
321 | fprintf(ctx->fp, "irfpm = { [0]="); | ||
322 | for (i = 0; irfpm_names[i]; i++) | ||
323 | fprintf(ctx->fp, "\"%s\", ", lower(buf, irfpm_names[i])); | ||
324 | fprintf(ctx->fp, "}\n\n"); | ||
325 | |||
326 | fprintf(ctx->fp, "irfield = { [0]="); | ||
327 | for (i = 0; irfield_names[i]; i++) { | ||
328 | char *p; | ||
329 | lower(buf, irfield_names[i]); | ||
330 | p = strchr(buf, '_'); | ||
331 | if (p) *p = '.'; | ||
332 | fprintf(ctx->fp, "\"%s\", ", buf); | ||
333 | } | ||
334 | fprintf(ctx->fp, "}\n\n"); | ||
335 | |||
336 | fprintf(ctx->fp, "ircall = {\n[0]="); | ||
337 | for (i = 0; ircall_names[i]; i++) | ||
338 | fprintf(ctx->fp, "\"%s\",\n", ircall_names[i]); | ||
339 | fprintf(ctx->fp, "}\n\n"); | ||
340 | |||
341 | fprintf(ctx->fp, "traceerr = {\n[0]="); | ||
342 | for (i = 0; trace_errors[i]; i++) | ||
343 | fprintf(ctx->fp, "\"%s\",\n", trace_errors[i]); | ||
344 | fprintf(ctx->fp, "}\n\n"); | ||
345 | } | ||
346 | |||
347 | /* -- Argument parsing ---------------------------------------------------- */ | ||
348 | |||
349 | /* Build mode names. */ | ||
350 | static const char *const modenames[] = { | ||
351 | #define BUILDNAME(name) #name, | ||
352 | BUILDDEF(BUILDNAME) | ||
353 | #undef BUILDNAME | ||
354 | NULL | ||
355 | }; | ||
356 | |||
357 | /* Print usage information and exit. */ | ||
358 | static void usage(void) | ||
359 | { | ||
360 | int i; | ||
361 | fprintf(stderr, LUAJIT_VERSION " VM builder.\n"); | ||
362 | fprintf(stderr, LUAJIT_COPYRIGHT ", " LUAJIT_URL "\n"); | ||
363 | fprintf(stderr, "Target architecture: " LJ_ARCH_NAME "\n\n"); | ||
364 | fprintf(stderr, "Usage: buildvm -m mode [-o outfile] [infiles...]\n\n"); | ||
365 | fprintf(stderr, "Available modes:\n"); | ||
366 | for (i = 0; i < BUILD__MAX; i++) | ||
367 | fprintf(stderr, " %s\n", modenames[i]); | ||
368 | exit(1); | ||
369 | } | ||
370 | |||
371 | /* Parse the output mode name. */ | ||
372 | static BuildMode parsemode(const char *mode) | ||
373 | { | ||
374 | int i; | ||
375 | for (i = 0; modenames[i]; i++) | ||
376 | if (!strcmp(mode, modenames[i])) | ||
377 | return (BuildMode)i; | ||
378 | usage(); | ||
379 | return (BuildMode)-1; | ||
380 | } | ||
381 | |||
382 | /* Parse arguments. */ | ||
383 | static void parseargs(BuildCtx *ctx, char **argv) | ||
384 | { | ||
385 | const char *a; | ||
386 | int i; | ||
387 | ctx->mode = (BuildMode)-1; | ||
388 | ctx->outname = "-"; | ||
389 | for (i = 1; (a = argv[i]) != NULL; i++) { | ||
390 | if (a[0] != '-') | ||
391 | break; | ||
392 | switch (a[1]) { | ||
393 | case '-': | ||
394 | if (a[2]) goto err; | ||
395 | i++; | ||
396 | goto ok; | ||
397 | case '\0': | ||
398 | goto ok; | ||
399 | case 'm': | ||
400 | i++; | ||
401 | if (a[2] || argv[i] == NULL) goto err; | ||
402 | ctx->mode = parsemode(argv[i]); | ||
403 | break; | ||
404 | case 'o': | ||
405 | i++; | ||
406 | if (a[2] || argv[i] == NULL) goto err; | ||
407 | ctx->outname = argv[i]; | ||
408 | break; | ||
409 | default: err: | ||
410 | usage(); | ||
411 | break; | ||
412 | } | ||
413 | } | ||
414 | ok: | ||
415 | ctx->args = argv+i; | ||
416 | if (ctx->mode == (BuildMode)-1) goto err; | ||
417 | } | ||
418 | |||
419 | int main(int argc, char **argv) | ||
420 | { | ||
421 | BuildCtx ctx_; | ||
422 | BuildCtx *ctx = &ctx_; | ||
423 | int status, binmode; | ||
424 | |||
425 | if (sizeof(void *) != 4*LJ_32+8*LJ_64) { | ||
426 | fprintf(stderr,"Error: pointer size mismatch in cross-build.\n"); | ||
427 | fprintf(stderr,"Try: make HOST_CC=\"gcc -m32\" CROSS=... TARGET=...\n\n"); | ||
428 | return 1; | ||
429 | } | ||
430 | |||
431 | UNUSED(argc); | ||
432 | parseargs(ctx, argv); | ||
433 | |||
434 | if ((status = build_code(ctx))) { | ||
435 | fprintf(stderr,"Error: DASM error %08x\n", status); | ||
436 | return 1; | ||
437 | } | ||
438 | |||
439 | switch (ctx->mode) { | ||
440 | case BUILD_peobj: | ||
441 | case BUILD_raw: | ||
442 | binmode = 1; | ||
443 | break; | ||
444 | default: | ||
445 | binmode = 0; | ||
446 | break; | ||
447 | } | ||
448 | |||
449 | if (ctx->outname[0] == '-' && ctx->outname[1] == '\0') { | ||
450 | ctx->fp = stdout; | ||
451 | #if defined(_WIN32) | ||
452 | if (binmode) | ||
453 | _setmode(_fileno(stdout), _O_BINARY); /* Yuck. */ | ||
454 | #endif | ||
455 | } else if (!(ctx->fp = fopen(ctx->outname, binmode ? "wb" : "w"))) { | ||
456 | fprintf(stderr, "Error: cannot open output file '%s': %s\n", | ||
457 | ctx->outname, strerror(errno)); | ||
458 | exit(1); | ||
459 | } | ||
460 | |||
461 | switch (ctx->mode) { | ||
462 | case BUILD_elfasm: | ||
463 | case BUILD_coffasm: | ||
464 | case BUILD_machasm: | ||
465 | emit_asm(ctx); | ||
466 | emit_asm_debug(ctx); | ||
467 | break; | ||
468 | case BUILD_peobj: | ||
469 | emit_peobj(ctx); | ||
470 | break; | ||
471 | case BUILD_raw: | ||
472 | emit_raw(ctx); | ||
473 | break; | ||
474 | case BUILD_bcdef: | ||
475 | emit_bcdef(ctx); | ||
476 | emit_lib(ctx); | ||
477 | break; | ||
478 | case BUILD_vmdef: | ||
479 | emit_vmdef(ctx); | ||
480 | emit_lib(ctx); | ||
481 | break; | ||
482 | case BUILD_ffdef: | ||
483 | case BUILD_libdef: | ||
484 | case BUILD_recdef: | ||
485 | emit_lib(ctx); | ||
486 | break; | ||
487 | case BUILD_folddef: | ||
488 | emit_fold(ctx); | ||
489 | break; | ||
490 | default: | ||
491 | break; | ||
492 | } | ||
493 | |||
494 | fflush(ctx->fp); | ||
495 | if (ferror(ctx->fp)) { | ||
496 | fprintf(stderr, "Error: cannot write to output file: %s\n", | ||
497 | strerror(errno)); | ||
498 | exit(1); | ||
499 | } | ||
500 | fclose(ctx->fp); | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
diff --git a/src/host/buildvm.h b/src/host/buildvm.h new file mode 100644 index 00000000..2b7168ed --- /dev/null +++ b/src/host/buildvm.h | |||
@@ -0,0 +1,104 @@ | |||
1 | /* | ||
2 | ** LuaJIT VM builder. | ||
3 | ** Copyright (C) 2005-2012 Mike Pall. See Copyright Notice in luajit.h | ||
4 | */ | ||
5 | |||
6 | #ifndef _BUILDVM_H | ||
7 | #define _BUILDVM_H | ||
8 | |||
9 | #include <sys/types.h> | ||
10 | #include <stdio.h> | ||
11 | #include <stdlib.h> | ||
12 | #include <string.h> | ||
13 | #include <errno.h> | ||
14 | |||
15 | #include "lj_def.h" | ||
16 | #include "lj_arch.h" | ||
17 | |||
18 | /* Hardcoded limits. Increase as needed. */ | ||
19 | #define BUILD_MAX_RELOC 200 /* Max. number of relocations. */ | ||
20 | #define BUILD_MAX_FOLD 4096 /* Max. number of fold rules. */ | ||
21 | |||
22 | /* Prefix for scanned library definitions. */ | ||
23 | #define LIBDEF_PREFIX "LJLIB_" | ||
24 | |||
25 | /* Prefix for scanned fold definitions. */ | ||
26 | #define FOLDDEF_PREFIX "LJFOLD" | ||
27 | |||
28 | /* Prefixes for generated labels. */ | ||
29 | #define LABEL_PREFIX "lj_" | ||
30 | #define LABEL_PREFIX_BC LABEL_PREFIX "BC_" | ||
31 | #define LABEL_PREFIX_FF LABEL_PREFIX "ff_" | ||
32 | #define LABEL_PREFIX_CF LABEL_PREFIX "cf_" | ||
33 | #define LABEL_PREFIX_FFH LABEL_PREFIX "ffh_" | ||
34 | #define LABEL_PREFIX_LIBCF LABEL_PREFIX "lib_cf_" | ||
35 | #define LABEL_PREFIX_LIBINIT LABEL_PREFIX "lib_init_" | ||
36 | |||
37 | /* Forward declaration. */ | ||
38 | struct dasm_State; | ||
39 | |||
40 | /* Build modes. */ | ||
41 | #define BUILDDEF(_) \ | ||
42 | _(elfasm) _(coffasm) _(machasm) _(peobj) _(raw) \ | ||
43 | _(bcdef) _(ffdef) _(libdef) _(recdef) _(vmdef) \ | ||
44 | _(folddef) | ||
45 | |||
46 | typedef enum { | ||
47 | #define BUILDENUM(name) BUILD_##name, | ||
48 | BUILDDEF(BUILDENUM) | ||
49 | #undef BUILDENUM | ||
50 | BUILD__MAX | ||
51 | } BuildMode; | ||
52 | |||
53 | /* Code relocation. */ | ||
54 | typedef struct BuildReloc { | ||
55 | int32_t ofs; | ||
56 | int sym; | ||
57 | int type; | ||
58 | } BuildReloc; | ||
59 | |||
60 | typedef struct BuildSym { | ||
61 | const char *name; | ||
62 | int32_t ofs; | ||
63 | } BuildSym; | ||
64 | |||
65 | /* Build context structure. */ | ||
66 | typedef struct BuildCtx { | ||
67 | /* DynASM state pointer. Should be first member. */ | ||
68 | struct dasm_State *D; | ||
69 | /* Parsed command line. */ | ||
70 | BuildMode mode; | ||
71 | FILE *fp; | ||
72 | const char *outname; | ||
73 | char **args; | ||
74 | /* Code and symbols generated by DynASM. */ | ||
75 | uint8_t *code; | ||
76 | size_t codesz; | ||
77 | int npc, nglob, nsym, nreloc, nrelocsym; | ||
78 | void **glob; | ||
79 | BuildSym *sym; | ||
80 | const char **relocsym; | ||
81 | int32_t *bc_ofs; | ||
82 | const char *beginsym; | ||
83 | /* Strings generated by DynASM. */ | ||
84 | const char *const *globnames; | ||
85 | const char *dasm_ident; | ||
86 | const char *dasm_arch; | ||
87 | /* Relocations. */ | ||
88 | BuildReloc reloc[BUILD_MAX_RELOC]; | ||
89 | } BuildCtx; | ||
90 | |||
91 | extern void owrite(BuildCtx *ctx, const void *ptr, size_t sz); | ||
92 | extern void emit_asm(BuildCtx *ctx); | ||
93 | extern void emit_peobj(BuildCtx *ctx); | ||
94 | extern void emit_lib(BuildCtx *ctx); | ||
95 | extern void emit_fold(BuildCtx *ctx); | ||
96 | |||
97 | extern const char *const bc_names[]; | ||
98 | extern const char *const ir_names[]; | ||
99 | extern const char *const irt_names[]; | ||
100 | extern const char *const irfpm_names[]; | ||
101 | extern const char *const irfield_names[]; | ||
102 | extern const char *const ircall_names[]; | ||
103 | |||
104 | #endif | ||
diff --git a/src/host/buildvm_asm.c b/src/host/buildvm_asm.c new file mode 100644 index 00000000..f975eadc --- /dev/null +++ b/src/host/buildvm_asm.c | |||
@@ -0,0 +1,283 @@ | |||
1 | /* | ||
2 | ** LuaJIT VM builder: Assembler source code emitter. | ||
3 | ** Copyright (C) 2005-2012 Mike Pall. See Copyright Notice in luajit.h | ||
4 | */ | ||
5 | |||
6 | #include "buildvm.h" | ||
7 | #include "lj_bc.h" | ||
8 | |||
9 | /* ------------------------------------------------------------------------ */ | ||
10 | |||
11 | #if LJ_TARGET_X86ORX64 | ||
12 | /* Emit bytes piecewise as assembler text. */ | ||
13 | static void emit_asm_bytes(BuildCtx *ctx, uint8_t *p, int n) | ||
14 | { | ||
15 | int i; | ||
16 | for (i = 0; i < n; i++) { | ||
17 | if ((i & 15) == 0) | ||
18 | fprintf(ctx->fp, "\t.byte %d", p[i]); | ||
19 | else | ||
20 | fprintf(ctx->fp, ",%d", p[i]); | ||
21 | if ((i & 15) == 15) putc('\n', ctx->fp); | ||
22 | } | ||
23 | if ((n & 15) != 0) putc('\n', ctx->fp); | ||
24 | } | ||
25 | |||
26 | /* Emit relocation */ | ||
27 | static void emit_asm_reloc(BuildCtx *ctx, int type, const char *sym) | ||
28 | { | ||
29 | switch (ctx->mode) { | ||
30 | case BUILD_elfasm: | ||
31 | if (type) | ||
32 | fprintf(ctx->fp, "\t.long %s-.-4\n", sym); | ||
33 | else | ||
34 | fprintf(ctx->fp, "\t.long %s\n", sym); | ||
35 | break; | ||
36 | case BUILD_coffasm: | ||
37 | fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", sym); | ||
38 | if (type) | ||
39 | fprintf(ctx->fp, "\t.long %s-.-4\n", sym); | ||
40 | else | ||
41 | fprintf(ctx->fp, "\t.long %s\n", sym); | ||
42 | break; | ||
43 | default: /* BUILD_machasm for relative relocations handled below. */ | ||
44 | fprintf(ctx->fp, "\t.long %s\n", sym); | ||
45 | break; | ||
46 | } | ||
47 | } | ||
48 | |||
49 | static const char *const jccnames[] = { | ||
50 | "jo", "jno", "jb", "jnb", "jz", "jnz", "jbe", "ja", | ||
51 | "js", "jns", "jpe", "jpo", "jl", "jge", "jle", "jg" | ||
52 | }; | ||
53 | |||
54 | /* Emit relocation for the incredibly stupid OSX assembler. */ | ||
55 | static void emit_asm_reloc_mach(BuildCtx *ctx, uint8_t *cp, int n, | ||
56 | const char *sym) | ||
57 | { | ||
58 | const char *opname = NULL; | ||
59 | if (--n < 0) goto err; | ||
60 | if (cp[n] == 0xe8) { | ||
61 | opname = "call"; | ||
62 | } else if (cp[n] == 0xe9) { | ||
63 | opname = "jmp"; | ||
64 | } else if (cp[n] >= 0x80 && cp[n] <= 0x8f && n > 0 && cp[n-1] == 0x0f) { | ||
65 | opname = jccnames[cp[n]-0x80]; | ||
66 | n--; | ||
67 | } else { | ||
68 | err: | ||
69 | fprintf(stderr, "Error: unsupported opcode for %s symbol relocation.\n", | ||
70 | sym); | ||
71 | exit(1); | ||
72 | } | ||
73 | emit_asm_bytes(ctx, cp, n); | ||
74 | fprintf(ctx->fp, "\t%s %s\n", opname, sym); | ||
75 | } | ||
76 | #else | ||
77 | /* Emit words piecewise as assembler text. */ | ||
78 | static void emit_asm_words(BuildCtx *ctx, uint8_t *p, int n) | ||
79 | { | ||
80 | int i; | ||
81 | for (i = 0; i < n; i += 4) { | ||
82 | if ((i & 15) == 0) | ||
83 | fprintf(ctx->fp, "\t.long 0x%08x", *(uint32_t *)(p+i)); | ||
84 | else | ||
85 | fprintf(ctx->fp, ",0x%08x", *(uint32_t *)(p+i)); | ||
86 | if ((i & 15) == 12) putc('\n', ctx->fp); | ||
87 | } | ||
88 | if ((n & 15) != 0) putc('\n', ctx->fp); | ||
89 | } | ||
90 | |||
91 | /* Emit relocation as part of an instruction. */ | ||
92 | static void emit_asm_wordreloc(BuildCtx *ctx, uint8_t *p, int n, | ||
93 | const char *sym) | ||
94 | { | ||
95 | uint32_t ins; | ||
96 | emit_asm_words(ctx, p, n-4); | ||
97 | ins = *(uint32_t *)(p+n-4); | ||
98 | #if LJ_TARGET_ARM | ||
99 | if ((ins & 0xff000000u) == 0xfa000000u) { | ||
100 | fprintf(ctx->fp, "\tblx %s\n", sym); | ||
101 | } else if ((ins & 0x0e000000u) == 0x0a000000u) { | ||
102 | fprintf(ctx->fp, "\t%s%.2s %s\n", (ins & 0x01000000u) ? "bl" : "b", | ||
103 | "eqnecsccmiplvsvchilsgeltgtle" + 2*(ins >> 28), sym); | ||
104 | } else { | ||
105 | fprintf(stderr, | ||
106 | "Error: unsupported opcode %08x for %s symbol relocation.\n", | ||
107 | ins, sym); | ||
108 | exit(1); | ||
109 | } | ||
110 | #elif LJ_TARGET_PPC || LJ_TARGET_PPCSPE | ||
111 | if ((ins >> 26) == 16) { | ||
112 | fprintf(ctx->fp, "\t%s %d, %d, %s\n", | ||
113 | (ins & 1) ? "bcl" : "bc", (ins >> 21) & 31, (ins >> 16) & 31, sym); | ||
114 | } else if ((ins >> 26) == 18) { | ||
115 | fprintf(ctx->fp, "\t%s %s\n", (ins & 1) ? "bl" : "b", sym); | ||
116 | } else { | ||
117 | fprintf(stderr, | ||
118 | "Error: unsupported opcode %08x for %s symbol relocation.\n", | ||
119 | ins, sym); | ||
120 | exit(1); | ||
121 | } | ||
122 | #elif LJ_TARGET_MIPS | ||
123 | fprintf(stderr, | ||
124 | "Error: unsupported opcode %08x for %s symbol relocation.\n", | ||
125 | ins, sym); | ||
126 | exit(1); | ||
127 | #else | ||
128 | #error "missing relocation support for this architecture" | ||
129 | #endif | ||
130 | } | ||
131 | #endif | ||
132 | |||
133 | #if LJ_TARGET_ARM | ||
134 | #define ELFASM_PX "%%" | ||
135 | #else | ||
136 | #define ELFASM_PX "@" | ||
137 | #endif | ||
138 | |||
139 | /* Emit an assembler label. */ | ||
140 | static void emit_asm_label(BuildCtx *ctx, const char *name, int size, int isfunc) | ||
141 | { | ||
142 | switch (ctx->mode) { | ||
143 | case BUILD_elfasm: | ||
144 | fprintf(ctx->fp, | ||
145 | "\n\t.globl %s\n" | ||
146 | "\t.hidden %s\n" | ||
147 | "\t.type %s, " ELFASM_PX "%s\n" | ||
148 | "\t.size %s, %d\n" | ||
149 | "%s:\n", | ||
150 | name, name, name, isfunc ? "function" : "object", name, size, name); | ||
151 | break; | ||
152 | case BUILD_coffasm: | ||
153 | fprintf(ctx->fp, "\n\t.globl %s\n", name); | ||
154 | if (isfunc) | ||
155 | fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", name); | ||
156 | fprintf(ctx->fp, "%s:\n", name); | ||
157 | break; | ||
158 | case BUILD_machasm: | ||
159 | fprintf(ctx->fp, | ||
160 | "\n\t.private_extern %s\n" | ||
161 | "%s:\n", name, name); | ||
162 | break; | ||
163 | default: | ||
164 | break; | ||
165 | } | ||
166 | } | ||
167 | |||
168 | /* Emit alignment. */ | ||
169 | static void emit_asm_align(BuildCtx *ctx, int bits) | ||
170 | { | ||
171 | switch (ctx->mode) { | ||
172 | case BUILD_elfasm: | ||
173 | case BUILD_coffasm: | ||
174 | fprintf(ctx->fp, "\t.p2align %d\n", bits); | ||
175 | break; | ||
176 | case BUILD_machasm: | ||
177 | fprintf(ctx->fp, "\t.align %d\n", bits); | ||
178 | break; | ||
179 | default: | ||
180 | break; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | /* ------------------------------------------------------------------------ */ | ||
185 | |||
186 | /* Emit assembler source code. */ | ||
187 | void emit_asm(BuildCtx *ctx) | ||
188 | { | ||
189 | int i, rel; | ||
190 | |||
191 | fprintf(ctx->fp, "\t.file \"buildvm_%s.dasc\"\n", ctx->dasm_arch); | ||
192 | fprintf(ctx->fp, "\t.text\n"); | ||
193 | emit_asm_align(ctx, 4); | ||
194 | |||
195 | emit_asm_label(ctx, ctx->beginsym, 0, 0); | ||
196 | if (ctx->mode != BUILD_machasm) | ||
197 | fprintf(ctx->fp, ".Lbegin:\n"); | ||
198 | |||
199 | #if LJ_TARGET_ARM && defined(__GNUC__) && !defined(LUAJIT_NO_UNWIND) | ||
200 | /* This should really be moved into buildvm_arm.dasc. */ | ||
201 | fprintf(ctx->fp, | ||
202 | ".fnstart\n" | ||
203 | ".save {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n" | ||
204 | ".pad #28\n"); | ||
205 | #endif | ||
206 | #if LJ_TARGET_MIPS | ||
207 | fprintf(ctx->fp, ".set nomips16\n.abicalls\n.set noreorder\n.set nomacro\n"); | ||
208 | #endif | ||
209 | |||
210 | for (i = rel = 0; i < ctx->nsym; i++) { | ||
211 | int32_t ofs = ctx->sym[i].ofs; | ||
212 | int32_t next = ctx->sym[i+1].ofs; | ||
213 | #if LJ_TARGET_ARM && defined(__GNUC__) && !defined(LUAJIT_NO_UNWIND) && \ | ||
214 | LJ_HASFFI | ||
215 | if (!strcmp(ctx->sym[i].name, "lj_vm_ffi_call")) | ||
216 | fprintf(ctx->fp, | ||
217 | ".globl lj_err_unwind_arm\n" | ||
218 | ".personality lj_err_unwind_arm\n" | ||
219 | ".fnend\n" | ||
220 | ".fnstart\n" | ||
221 | ".save {r4, r5, r11, lr}\n" | ||
222 | ".setfp r11, sp\n"); | ||
223 | #endif | ||
224 | emit_asm_label(ctx, ctx->sym[i].name, next - ofs, 1); | ||
225 | while (rel < ctx->nreloc && ctx->reloc[rel].ofs <= next) { | ||
226 | BuildReloc *r = &ctx->reloc[rel]; | ||
227 | int n = r->ofs - ofs; | ||
228 | #if LJ_TARGET_X86ORX64 | ||
229 | if (ctx->mode == BUILD_machasm && r->type != 0) { | ||
230 | emit_asm_reloc_mach(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]); | ||
231 | } else { | ||
232 | emit_asm_bytes(ctx, ctx->code+ofs, n); | ||
233 | emit_asm_reloc(ctx, r->type, ctx->relocsym[r->sym]); | ||
234 | } | ||
235 | ofs += n+4; | ||
236 | #else | ||
237 | emit_asm_wordreloc(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]); | ||
238 | ofs += n; | ||
239 | #endif | ||
240 | rel++; | ||
241 | } | ||
242 | #if LJ_TARGET_X86ORX64 | ||
243 | emit_asm_bytes(ctx, ctx->code+ofs, next-ofs); | ||
244 | #else | ||
245 | emit_asm_words(ctx, ctx->code+ofs, next-ofs); | ||
246 | #endif | ||
247 | } | ||
248 | |||
249 | #if LJ_TARGET_ARM && defined(__GNUC__) && !defined(LUAJIT_NO_UNWIND) | ||
250 | fprintf(ctx->fp, | ||
251 | #if !LJ_HASFFI | ||
252 | ".globl lj_err_unwind_arm\n" | ||
253 | ".personality lj_err_unwind_arm\n" | ||
254 | #endif | ||
255 | ".fnend\n"); | ||
256 | #endif | ||
257 | |||
258 | fprintf(ctx->fp, "\n"); | ||
259 | switch (ctx->mode) { | ||
260 | case BUILD_elfasm: | ||
261 | fprintf(ctx->fp, "\t.section .note.GNU-stack,\"\"," ELFASM_PX "progbits\n"); | ||
262 | #if LJ_TARGET_PPCSPE | ||
263 | /* Soft-float ABI + SPE. */ | ||
264 | fprintf(ctx->fp, "\t.gnu_attribute 4, 2\n\t.gnu_attribute 8, 3\n"); | ||
265 | #elif LJ_TARGET_PPC | ||
266 | /* Hard-float ABI. */ | ||
267 | fprintf(ctx->fp, "\t.gnu_attribute 4, 1\n"); | ||
268 | #endif | ||
269 | /* fallthrough */ | ||
270 | case BUILD_coffasm: | ||
271 | fprintf(ctx->fp, "\t.ident \"%s\"\n", ctx->dasm_ident); | ||
272 | break; | ||
273 | case BUILD_machasm: | ||
274 | fprintf(ctx->fp, | ||
275 | "\t.cstring\n" | ||
276 | "\t.ascii \"%s\\0\"\n", ctx->dasm_ident); | ||
277 | break; | ||
278 | default: | ||
279 | break; | ||
280 | } | ||
281 | fprintf(ctx->fp, "\n"); | ||
282 | } | ||
283 | |||
diff --git a/src/host/buildvm_fold.c b/src/host/buildvm_fold.c new file mode 100644 index 00000000..73f4f80a --- /dev/null +++ b/src/host/buildvm_fold.c | |||
@@ -0,0 +1,229 @@ | |||
1 | /* | ||
2 | ** LuaJIT VM builder: IR folding hash table generator. | ||
3 | ** Copyright (C) 2005-2012 Mike Pall. See Copyright Notice in luajit.h | ||
4 | */ | ||
5 | |||
6 | #include "buildvm.h" | ||
7 | #include "lj_obj.h" | ||
8 | #include "lj_ir.h" | ||
9 | |||
10 | /* Context for the folding hash table generator. */ | ||
11 | static int lineno; | ||
12 | static int funcidx; | ||
13 | static uint32_t foldkeys[BUILD_MAX_FOLD]; | ||
14 | static uint32_t nkeys; | ||
15 | |||
16 | /* Try to fill the hash table with keys using the hash parameters. */ | ||
17 | static int tryhash(uint32_t *htab, uint32_t sz, uint32_t r, int dorol) | ||
18 | { | ||
19 | uint32_t i; | ||
20 | if (dorol && ((r & 31) == 0 || (r>>5) == 0)) | ||
21 | return 0; /* Avoid zero rotates. */ | ||
22 | memset(htab, 0xff, (sz+1)*sizeof(uint32_t)); | ||
23 | for (i = 0; i < nkeys; i++) { | ||
24 | uint32_t key = foldkeys[i]; | ||
25 | uint32_t k = key & 0xffffff; | ||
26 | uint32_t h = (dorol ? lj_rol(lj_rol(k, r>>5) - k, r&31) : | ||
27 | (((k << (r>>5)) - k) << (r&31))) % sz; | ||
28 | if (htab[h] != 0xffffffff) { /* Collision on primary slot. */ | ||
29 | if (htab[h+1] != 0xffffffff) { /* Collision on secondary slot. */ | ||
30 | /* Try to move the colliding key, if possible. */ | ||
31 | if (h < sz-1 && htab[h+2] == 0xffffffff) { | ||
32 | uint32_t k2 = htab[h+1] & 0xffffff; | ||
33 | uint32_t h2 = (dorol ? lj_rol(lj_rol(k2, r>>5) - k2, r&31) : | ||
34 | (((k2 << (r>>5)) - k2) << (r&31))) % sz; | ||
35 | if (h2 != h+1) return 0; /* Cannot resolve collision. */ | ||
36 | htab[h+2] = htab[h+1]; /* Move colliding key to secondary slot. */ | ||
37 | } else { | ||
38 | return 0; /* Collision. */ | ||
39 | } | ||
40 | } | ||
41 | htab[h+1] = key; | ||
42 | } else { | ||
43 | htab[h] = key; | ||
44 | } | ||
45 | } | ||
46 | return 1; /* Success, all keys could be stored. */ | ||
47 | } | ||
48 | |||
49 | /* Print the generated hash table. */ | ||
50 | static void printhash(BuildCtx *ctx, uint32_t *htab, uint32_t sz) | ||
51 | { | ||
52 | uint32_t i; | ||
53 | fprintf(ctx->fp, "static const uint32_t fold_hash[%d] = {\n0x%08x", | ||
54 | sz+1, htab[0]); | ||
55 | for (i = 1; i < sz+1; i++) | ||
56 | fprintf(ctx->fp, ",\n0x%08x", htab[i]); | ||
57 | fprintf(ctx->fp, "\n};\n\n"); | ||
58 | } | ||
59 | |||
60 | /* Exhaustive search for the shortest semi-perfect hash table. */ | ||
61 | static void makehash(BuildCtx *ctx) | ||
62 | { | ||
63 | uint32_t htab[BUILD_MAX_FOLD*2+1]; | ||
64 | uint32_t sz, r; | ||
65 | /* Search for the smallest hash table with an odd size. */ | ||
66 | for (sz = (nkeys|1); sz < BUILD_MAX_FOLD*2; sz += 2) { | ||
67 | /* First try all shift hash combinations. */ | ||
68 | for (r = 0; r < 32*32; r++) { | ||
69 | if (tryhash(htab, sz, r, 0)) { | ||
70 | printhash(ctx, htab, sz); | ||
71 | fprintf(ctx->fp, | ||
72 | "#define fold_hashkey(k)\t(((((k)<<%u)-(k))<<%u)%%%u)\n\n", | ||
73 | r>>5, r&31, sz); | ||
74 | return; | ||
75 | } | ||
76 | } | ||
77 | /* Then try all rotate hash combinations. */ | ||
78 | for (r = 0; r < 32*32; r++) { | ||
79 | if (tryhash(htab, sz, r, 1)) { | ||
80 | printhash(ctx, htab, sz); | ||
81 | fprintf(ctx->fp, | ||
82 | "#define fold_hashkey(k)\t(lj_rol(lj_rol((k),%u)-(k),%u)%%%u)\n\n", | ||
83 | r>>5, r&31, sz); | ||
84 | return; | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | fprintf(stderr, "Error: search for perfect hash failed\n"); | ||
89 | exit(1); | ||
90 | } | ||
91 | |||
92 | /* Parse one token of a fold rule. */ | ||
93 | static uint32_t nexttoken(char **pp, int allowlit, int allowany) | ||
94 | { | ||
95 | char *p = *pp; | ||
96 | if (p) { | ||
97 | uint32_t i; | ||
98 | char *q = strchr(p, ' '); | ||
99 | if (q) *q++ = '\0'; | ||
100 | *pp = q; | ||
101 | if (allowlit && !strncmp(p, "IRFPM_", 6)) { | ||
102 | for (i = 0; irfpm_names[i]; i++) | ||
103 | if (!strcmp(irfpm_names[i], p+6)) | ||
104 | return i; | ||
105 | } else if (allowlit && !strncmp(p, "IRFL_", 5)) { | ||
106 | for (i = 0; irfield_names[i]; i++) | ||
107 | if (!strcmp(irfield_names[i], p+5)) | ||
108 | return i; | ||
109 | } else if (allowlit && !strncmp(p, "IRCALL_", 7)) { | ||
110 | for (i = 0; ircall_names[i]; i++) | ||
111 | if (!strcmp(ircall_names[i], p+7)) | ||
112 | return i; | ||
113 | } else if (allowlit && !strncmp(p, "IRCONV_", 7)) { | ||
114 | for (i = 0; irt_names[i]; i++) { | ||
115 | const char *r = strchr(p+7, '_'); | ||
116 | if (r && !strncmp(irt_names[i], p+7, r-(p+7))) { | ||
117 | uint32_t j; | ||
118 | for (j = 0; irt_names[j]; j++) | ||
119 | if (!strcmp(irt_names[j], r+1)) | ||
120 | return (i << 5) + j; | ||
121 | } | ||
122 | } | ||
123 | } else if (allowlit && *p >= '0' && *p <= '9') { | ||
124 | for (i = 0; *p >= '0' && *p <= '9'; p++) | ||
125 | i = i*10 + (*p - '0'); | ||
126 | if (*p == '\0') | ||
127 | return i; | ||
128 | } else if (allowany && !strcmp("any", p)) { | ||
129 | return allowany; | ||
130 | } else { | ||
131 | for (i = 0; ir_names[i]; i++) | ||
132 | if (!strcmp(ir_names[i], p)) | ||
133 | return i; | ||
134 | } | ||
135 | fprintf(stderr, "Error: bad fold definition token \"%s\" at line %d\n", p, lineno); | ||
136 | exit(1); | ||
137 | } | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | /* Parse a fold rule. */ | ||
142 | static void foldrule(char *p) | ||
143 | { | ||
144 | uint32_t op = nexttoken(&p, 0, 0); | ||
145 | uint32_t left = nexttoken(&p, 0, 0x7f); | ||
146 | uint32_t right = nexttoken(&p, 1, 0x3ff); | ||
147 | uint32_t key = (funcidx << 24) | (op << 17) | (left << 10) | right; | ||
148 | uint32_t i; | ||
149 | if (nkeys >= BUILD_MAX_FOLD) { | ||
150 | fprintf(stderr, "Error: too many fold rules, increase BUILD_MAX_FOLD.\n"); | ||
151 | exit(1); | ||
152 | } | ||
153 | /* Simple insertion sort to detect duplicates. */ | ||
154 | for (i = nkeys; i > 0; i--) { | ||
155 | if ((foldkeys[i-1]&0xffffff) < (key & 0xffffff)) | ||
156 | break; | ||
157 | if ((foldkeys[i-1]&0xffffff) == (key & 0xffffff)) { | ||
158 | fprintf(stderr, "Error: duplicate fold definition at line %d\n", lineno); | ||
159 | exit(1); | ||
160 | } | ||
161 | foldkeys[i] = foldkeys[i-1]; | ||
162 | } | ||
163 | foldkeys[i] = key; | ||
164 | nkeys++; | ||
165 | } | ||
166 | |||
167 | /* Emit C source code for IR folding hash table. */ | ||
168 | void emit_fold(BuildCtx *ctx) | ||
169 | { | ||
170 | char buf[256]; /* We don't care about analyzing lines longer than that. */ | ||
171 | const char *fname = ctx->args[0]; | ||
172 | FILE *fp; | ||
173 | |||
174 | if (fname == NULL) { | ||
175 | fprintf(stderr, "Error: missing input filename\n"); | ||
176 | exit(1); | ||
177 | } | ||
178 | |||
179 | if (fname[0] == '-' && fname[1] == '\0') { | ||
180 | fp = stdin; | ||
181 | } else { | ||
182 | fp = fopen(fname, "r"); | ||
183 | if (!fp) { | ||
184 | fprintf(stderr, "Error: cannot open input file '%s': %s\n", | ||
185 | fname, strerror(errno)); | ||
186 | exit(1); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n"); | ||
191 | fprintf(ctx->fp, "static const FoldFunc fold_func[] = {\n"); | ||
192 | |||
193 | lineno = 0; | ||
194 | funcidx = 0; | ||
195 | nkeys = 0; | ||
196 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
197 | lineno++; | ||
198 | /* The prefix must be at the start of a line, otherwise it's ignored. */ | ||
199 | if (!strncmp(buf, FOLDDEF_PREFIX, sizeof(FOLDDEF_PREFIX)-1)) { | ||
200 | char *p = buf+sizeof(FOLDDEF_PREFIX)-1; | ||
201 | char *q = strchr(p, ')'); | ||
202 | if (p[0] == '(' && q) { | ||
203 | p++; | ||
204 | *q = '\0'; | ||
205 | foldrule(p); | ||
206 | } else if ((p[0] == 'F' || p[0] == 'X') && p[1] == '(' && q) { | ||
207 | p += 2; | ||
208 | *q = '\0'; | ||
209 | if (funcidx) | ||
210 | fprintf(ctx->fp, ",\n"); | ||
211 | if (p[-2] == 'X') | ||
212 | fprintf(ctx->fp, " %s", p); | ||
213 | else | ||
214 | fprintf(ctx->fp, " fold_%s", p); | ||
215 | funcidx++; | ||
216 | } else { | ||
217 | buf[strlen(buf)-1] = '\0'; | ||
218 | fprintf(stderr, "Error: unknown fold definition tag %s%s at line %d\n", | ||
219 | FOLDDEF_PREFIX, p, lineno); | ||
220 | exit(1); | ||
221 | } | ||
222 | } | ||
223 | } | ||
224 | fclose(fp); | ||
225 | fprintf(ctx->fp, "\n};\n\n"); | ||
226 | |||
227 | makehash(ctx); | ||
228 | } | ||
229 | |||
diff --git a/src/host/buildvm_lib.c b/src/host/buildvm_lib.c new file mode 100644 index 00000000..3231d3ad --- /dev/null +++ b/src/host/buildvm_lib.c | |||
@@ -0,0 +1,377 @@ | |||
1 | /* | ||
2 | ** LuaJIT VM builder: library definition compiler. | ||
3 | ** Copyright (C) 2005-2012 Mike Pall. See Copyright Notice in luajit.h | ||
4 | */ | ||
5 | |||
6 | #include "buildvm.h" | ||
7 | #include "lj_obj.h" | ||
8 | #include "lj_lib.h" | ||
9 | |||
10 | /* Context for library definitions. */ | ||
11 | static uint8_t obuf[8192]; | ||
12 | static uint8_t *optr; | ||
13 | static char modname[80]; | ||
14 | static size_t modnamelen; | ||
15 | static char funcname[80]; | ||
16 | static int modstate, regfunc; | ||
17 | static int ffid, recffid, ffasmfunc; | ||
18 | |||
19 | enum { | ||
20 | REGFUNC_OK, | ||
21 | REGFUNC_NOREG, | ||
22 | REGFUNC_NOREGUV | ||
23 | }; | ||
24 | |||
25 | static void libdef_name(const char *p, int kind) | ||
26 | { | ||
27 | size_t n = strlen(p); | ||
28 | if (kind != LIBINIT_STRING) { | ||
29 | if (n > modnamelen && p[modnamelen] == '_' && | ||
30 | !strncmp(p, modname, modnamelen)) { | ||
31 | p += modnamelen+1; | ||
32 | n -= modnamelen+1; | ||
33 | } | ||
34 | } | ||
35 | if (n > LIBINIT_MAXSTR) { | ||
36 | fprintf(stderr, "Error: string too long: '%s'\n", p); | ||
37 | exit(1); | ||
38 | } | ||
39 | if (optr+1+n+2 > obuf+sizeof(obuf)) { /* +2 for caller. */ | ||
40 | fprintf(stderr, "Error: output buffer overflow\n"); | ||
41 | exit(1); | ||
42 | } | ||
43 | *optr++ = (uint8_t)(n | kind); | ||
44 | memcpy(optr, p, n); | ||
45 | optr += n; | ||
46 | } | ||
47 | |||
48 | static void libdef_endmodule(BuildCtx *ctx) | ||
49 | { | ||
50 | if (modstate != 0) { | ||
51 | char line[80]; | ||
52 | const uint8_t *p; | ||
53 | int n; | ||
54 | if (modstate == 1) | ||
55 | fprintf(ctx->fp, " (lua_CFunction)0"); | ||
56 | fprintf(ctx->fp, "\n};\n"); | ||
57 | fprintf(ctx->fp, "static const uint8_t %s%s[] = {\n", | ||
58 | LABEL_PREFIX_LIBINIT, modname); | ||
59 | line[0] = '\0'; | ||
60 | for (n = 0, p = obuf; p < optr; p++) { | ||
61 | n += sprintf(line+n, "%d,", *p); | ||
62 | if (n >= 75) { | ||
63 | fprintf(ctx->fp, "%s\n", line); | ||
64 | n = 0; | ||
65 | line[0] = '\0'; | ||
66 | } | ||
67 | } | ||
68 | fprintf(ctx->fp, "%s%d\n};\n#endif\n\n", line, LIBINIT_END); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | static void libdef_module(BuildCtx *ctx, char *p, int arg) | ||
73 | { | ||
74 | UNUSED(arg); | ||
75 | if (ctx->mode == BUILD_libdef) { | ||
76 | libdef_endmodule(ctx); | ||
77 | optr = obuf; | ||
78 | *optr++ = (uint8_t)ffid; | ||
79 | *optr++ = (uint8_t)ffasmfunc; | ||
80 | *optr++ = 0; /* Hash table size. */ | ||
81 | modstate = 1; | ||
82 | fprintf(ctx->fp, "#ifdef %sMODULE_%s\n", LIBDEF_PREFIX, p); | ||
83 | fprintf(ctx->fp, "#undef %sMODULE_%s\n", LIBDEF_PREFIX, p); | ||
84 | fprintf(ctx->fp, "static const lua_CFunction %s%s[] = {\n", | ||
85 | LABEL_PREFIX_LIBCF, p); | ||
86 | } | ||
87 | modnamelen = strlen(p); | ||
88 | if (modnamelen > sizeof(modname)-1) { | ||
89 | fprintf(stderr, "Error: module name too long: '%s'\n", p); | ||
90 | exit(1); | ||
91 | } | ||
92 | strcpy(modname, p); | ||
93 | } | ||
94 | |||
95 | static int find_ffofs(BuildCtx *ctx, const char *name) | ||
96 | { | ||
97 | int i; | ||
98 | for (i = 0; i < ctx->nglob; i++) { | ||
99 | const char *gl = ctx->globnames[i]; | ||
100 | if (gl[0] == 'f' && gl[1] == 'f' && gl[2] == '_' && !strcmp(gl+3, name)) { | ||
101 | return (int)((uint8_t *)ctx->glob[i] - ctx->code); | ||
102 | } | ||
103 | } | ||
104 | fprintf(stderr, "Error: undefined fast function %s%s\n", | ||
105 | LABEL_PREFIX_FF, name); | ||
106 | exit(1); | ||
107 | } | ||
108 | |||
109 | static void libdef_func(BuildCtx *ctx, char *p, int arg) | ||
110 | { | ||
111 | if (arg != LIBINIT_CF) | ||
112 | ffasmfunc++; | ||
113 | if (ctx->mode == BUILD_libdef) { | ||
114 | if (modstate == 0) { | ||
115 | fprintf(stderr, "Error: no module for function definition %s\n", p); | ||
116 | exit(1); | ||
117 | } | ||
118 | if (regfunc == REGFUNC_NOREG) { | ||
119 | if (optr+1 > obuf+sizeof(obuf)) { | ||
120 | fprintf(stderr, "Error: output buffer overflow\n"); | ||
121 | exit(1); | ||
122 | } | ||
123 | *optr++ = LIBINIT_FFID; | ||
124 | } else { | ||
125 | if (arg != LIBINIT_ASM_) { | ||
126 | if (modstate != 1) fprintf(ctx->fp, ",\n"); | ||
127 | modstate = 2; | ||
128 | fprintf(ctx->fp, " %s%s", arg ? LABEL_PREFIX_FFH : LABEL_PREFIX_CF, p); | ||
129 | } | ||
130 | if (regfunc != REGFUNC_NOREGUV) obuf[2]++; /* Bump hash table size. */ | ||
131 | libdef_name(regfunc == REGFUNC_NOREGUV ? "" : p, arg); | ||
132 | } | ||
133 | } else if (ctx->mode == BUILD_ffdef) { | ||
134 | fprintf(ctx->fp, "FFDEF(%s)\n", p); | ||
135 | } else if (ctx->mode == BUILD_recdef) { | ||
136 | if (strlen(p) > sizeof(funcname)-1) { | ||
137 | fprintf(stderr, "Error: function name too long: '%s'\n", p); | ||
138 | exit(1); | ||
139 | } | ||
140 | strcpy(funcname, p); | ||
141 | } else if (ctx->mode == BUILD_vmdef) { | ||
142 | int i; | ||
143 | for (i = 1; p[i] && modname[i-1]; i++) | ||
144 | if (p[i] == '_') p[i] = '.'; | ||
145 | fprintf(ctx->fp, "\"%s\",\n", p); | ||
146 | } else if (ctx->mode == BUILD_bcdef) { | ||
147 | if (arg != LIBINIT_CF) | ||
148 | fprintf(ctx->fp, ",\n%d", find_ffofs(ctx, p)); | ||
149 | } | ||
150 | ffid++; | ||
151 | regfunc = REGFUNC_OK; | ||
152 | } | ||
153 | |||
154 | static uint32_t find_rec(char *name) | ||
155 | { | ||
156 | char *p = (char *)obuf; | ||
157 | uint32_t n; | ||
158 | for (n = 2; *p; n++) { | ||
159 | if (strcmp(p, name) == 0) | ||
160 | return n; | ||
161 | p += strlen(p)+1; | ||
162 | } | ||
163 | if (p+strlen(name)+1 >= (char *)obuf+sizeof(obuf)) { | ||
164 | fprintf(stderr, "Error: output buffer overflow\n"); | ||
165 | exit(1); | ||
166 | } | ||
167 | strcpy(p, name); | ||
168 | return n; | ||
169 | } | ||
170 | |||
171 | static void libdef_rec(BuildCtx *ctx, char *p, int arg) | ||
172 | { | ||
173 | UNUSED(arg); | ||
174 | if (ctx->mode == BUILD_recdef) { | ||
175 | char *q; | ||
176 | uint32_t n; | ||
177 | for (; recffid+1 < ffid; recffid++) | ||
178 | fprintf(ctx->fp, ",\n0"); | ||
179 | recffid = ffid; | ||
180 | if (*p == '.') p = funcname; | ||
181 | q = strchr(p, ' '); | ||
182 | if (q) *q++ = '\0'; | ||
183 | n = find_rec(p); | ||
184 | if (q) | ||
185 | fprintf(ctx->fp, ",\n0x%02x00+(%s)", n, q); | ||
186 | else | ||
187 | fprintf(ctx->fp, ",\n0x%02x00", n); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | static void memcpy_endian(void *dst, void *src, size_t n) | ||
192 | { | ||
193 | union { uint8_t b; uint32_t u; } host_endian; | ||
194 | host_endian.u = 1; | ||
195 | if (host_endian.b == LJ_ENDIAN_SELECT(1, 0)) { | ||
196 | memcpy(dst, src, n); | ||
197 | } else { | ||
198 | size_t i; | ||
199 | for (i = 0; i < n; i++) | ||
200 | ((uint8_t *)dst)[i] = ((uint8_t *)src)[n-i-1]; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | static void libdef_push(BuildCtx *ctx, char *p, int arg) | ||
205 | { | ||
206 | UNUSED(arg); | ||
207 | if (ctx->mode == BUILD_libdef) { | ||
208 | int len = (int)strlen(p); | ||
209 | if (*p == '"') { | ||
210 | if (len > 1 && p[len-1] == '"') { | ||
211 | p[len-1] = '\0'; | ||
212 | libdef_name(p+1, LIBINIT_STRING); | ||
213 | return; | ||
214 | } | ||
215 | } else if (*p >= '0' && *p <= '9') { | ||
216 | char *ep; | ||
217 | double d = strtod(p, &ep); | ||
218 | if (*ep == '\0') { | ||
219 | if (optr+1+sizeof(double) > obuf+sizeof(obuf)) { | ||
220 | fprintf(stderr, "Error: output buffer overflow\n"); | ||
221 | exit(1); | ||
222 | } | ||
223 | *optr++ = LIBINIT_NUMBER; | ||
224 | memcpy_endian(optr, &d, sizeof(double)); | ||
225 | optr += sizeof(double); | ||
226 | return; | ||
227 | } | ||
228 | } else if (!strcmp(p, "lastcl")) { | ||
229 | if (optr+1 > obuf+sizeof(obuf)) { | ||
230 | fprintf(stderr, "Error: output buffer overflow\n"); | ||
231 | exit(1); | ||
232 | } | ||
233 | *optr++ = LIBINIT_LASTCL; | ||
234 | return; | ||
235 | } else if (len > 4 && !strncmp(p, "top-", 4)) { | ||
236 | if (optr+2 > obuf+sizeof(obuf)) { | ||
237 | fprintf(stderr, "Error: output buffer overflow\n"); | ||
238 | exit(1); | ||
239 | } | ||
240 | *optr++ = LIBINIT_COPY; | ||
241 | *optr++ = (uint8_t)atoi(p+4); | ||
242 | return; | ||
243 | } | ||
244 | fprintf(stderr, "Error: bad value for %sPUSH(%s)\n", LIBDEF_PREFIX, p); | ||
245 | exit(1); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | static void libdef_set(BuildCtx *ctx, char *p, int arg) | ||
250 | { | ||
251 | UNUSED(arg); | ||
252 | if (ctx->mode == BUILD_libdef) { | ||
253 | if (p[0] == '!' && p[1] == '\0') p[0] = '\0'; /* Set env. */ | ||
254 | libdef_name(p, LIBINIT_STRING); | ||
255 | *optr++ = LIBINIT_SET; | ||
256 | obuf[2]++; /* Bump hash table size. */ | ||
257 | } | ||
258 | } | ||
259 | |||
260 | static void libdef_regfunc(BuildCtx *ctx, char *p, int arg) | ||
261 | { | ||
262 | UNUSED(ctx); UNUSED(p); | ||
263 | regfunc = arg; | ||
264 | } | ||
265 | |||
266 | typedef void (*LibDefFunc)(BuildCtx *ctx, char *p, int arg); | ||
267 | |||
268 | typedef struct LibDefHandler { | ||
269 | const char *suffix; | ||
270 | const char *stop; | ||
271 | const LibDefFunc func; | ||
272 | const int arg; | ||
273 | } LibDefHandler; | ||
274 | |||
275 | static const LibDefHandler libdef_handlers[] = { | ||
276 | { "MODULE_", " \t\r\n", libdef_module, 0 }, | ||
277 | { "CF(", ")", libdef_func, LIBINIT_CF }, | ||
278 | { "ASM(", ")", libdef_func, LIBINIT_ASM }, | ||
279 | { "ASM_(", ")", libdef_func, LIBINIT_ASM_ }, | ||
280 | { "REC(", ")", libdef_rec, 0 }, | ||
281 | { "PUSH(", ")", libdef_push, 0 }, | ||
282 | { "SET(", ")", libdef_set, 0 }, | ||
283 | { "NOREGUV", NULL, libdef_regfunc, REGFUNC_NOREGUV }, | ||
284 | { "NOREG", NULL, libdef_regfunc, REGFUNC_NOREG }, | ||
285 | { NULL, NULL, (LibDefFunc)0, 0 } | ||
286 | }; | ||
287 | |||
288 | /* Emit C source code for library function definitions. */ | ||
289 | void emit_lib(BuildCtx *ctx) | ||
290 | { | ||
291 | const char *fname; | ||
292 | |||
293 | if (ctx->mode == BUILD_ffdef || ctx->mode == BUILD_libdef || | ||
294 | ctx->mode == BUILD_recdef) | ||
295 | fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n"); | ||
296 | else if (ctx->mode == BUILD_vmdef) | ||
297 | fprintf(ctx->fp, "ffnames = {\n[0]=\"Lua\",\n\"C\",\n"); | ||
298 | if (ctx->mode == BUILD_recdef) | ||
299 | fprintf(ctx->fp, "static const uint16_t recff_idmap[] = {\n0,\n0x0100"); | ||
300 | recffid = ffid = FF_C+1; | ||
301 | ffasmfunc = 0; | ||
302 | |||
303 | while ((fname = *ctx->args++)) { | ||
304 | char buf[256]; /* We don't care about analyzing lines longer than that. */ | ||
305 | FILE *fp; | ||
306 | if (fname[0] == '-' && fname[1] == '\0') { | ||
307 | fp = stdin; | ||
308 | } else { | ||
309 | fp = fopen(fname, "r"); | ||
310 | if (!fp) { | ||
311 | fprintf(stderr, "Error: cannot open input file '%s': %s\n", | ||
312 | fname, strerror(errno)); | ||
313 | exit(1); | ||
314 | } | ||
315 | } | ||
316 | modstate = 0; | ||
317 | regfunc = REGFUNC_OK; | ||
318 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
319 | char *p; | ||
320 | for (p = buf; (p = strstr(p, LIBDEF_PREFIX)) != NULL; ) { | ||
321 | const LibDefHandler *ldh; | ||
322 | p += sizeof(LIBDEF_PREFIX)-1; | ||
323 | for (ldh = libdef_handlers; ldh->suffix != NULL; ldh++) { | ||
324 | size_t n, len = strlen(ldh->suffix); | ||
325 | if (!strncmp(p, ldh->suffix, len)) { | ||
326 | p += len; | ||
327 | n = ldh->stop ? strcspn(p, ldh->stop) : 0; | ||
328 | if (!p[n]) break; | ||
329 | p[n] = '\0'; | ||
330 | ldh->func(ctx, p, ldh->arg); | ||
331 | p += n+1; | ||
332 | break; | ||
333 | } | ||
334 | } | ||
335 | if (ldh->suffix == NULL) { | ||
336 | buf[strlen(buf)-1] = '\0'; | ||
337 | fprintf(stderr, "Error: unknown library definition tag %s%s\n", | ||
338 | LIBDEF_PREFIX, p); | ||
339 | exit(1); | ||
340 | } | ||
341 | } | ||
342 | } | ||
343 | fclose(fp); | ||
344 | if (ctx->mode == BUILD_libdef) { | ||
345 | libdef_endmodule(ctx); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | if (ctx->mode == BUILD_ffdef) { | ||
350 | fprintf(ctx->fp, "\n#undef FFDEF\n\n"); | ||
351 | fprintf(ctx->fp, | ||
352 | "#ifndef FF_NUM_ASMFUNC\n#define FF_NUM_ASMFUNC %d\n#endif\n\n", | ||
353 | ffasmfunc); | ||
354 | } else if (ctx->mode == BUILD_vmdef) { | ||
355 | fprintf(ctx->fp, "}\n\n"); | ||
356 | } else if (ctx->mode == BUILD_bcdef) { | ||
357 | int i; | ||
358 | fprintf(ctx->fp, "\n};\n\n"); | ||
359 | fprintf(ctx->fp, "LJ_DATADEF const uint16_t lj_bc_mode[] = {\n"); | ||
360 | fprintf(ctx->fp, "BCDEF(BCMODE)\n"); | ||
361 | for (i = ffasmfunc-1; i > 0; i--) | ||
362 | fprintf(ctx->fp, "BCMODE_FF,\n"); | ||
363 | fprintf(ctx->fp, "BCMODE_FF\n};\n\n"); | ||
364 | } else if (ctx->mode == BUILD_recdef) { | ||
365 | char *p = (char *)obuf; | ||
366 | fprintf(ctx->fp, "\n};\n\n"); | ||
367 | fprintf(ctx->fp, "static const RecordFunc recff_func[] = {\n" | ||
368 | "recff_nyi,\n" | ||
369 | "recff_c"); | ||
370 | while (*p) { | ||
371 | fprintf(ctx->fp, ",\nrecff_%s", p); | ||
372 | p += strlen(p)+1; | ||
373 | } | ||
374 | fprintf(ctx->fp, "\n};\n\n"); | ||
375 | } | ||
376 | } | ||
377 | |||
diff --git a/src/host/buildvm_peobj.c b/src/host/buildvm_peobj.c new file mode 100644 index 00000000..17b3293a --- /dev/null +++ b/src/host/buildvm_peobj.c | |||
@@ -0,0 +1,352 @@ | |||
1 | /* | ||
2 | ** LuaJIT VM builder: PE object emitter. | ||
3 | ** Copyright (C) 2005-2012 Mike Pall. See Copyright Notice in luajit.h | ||
4 | ** | ||
5 | ** Only used for building on Windows, since we cannot assume the presence | ||
6 | ** of a suitable assembler. The host and target byte order must match. | ||
7 | */ | ||
8 | |||
9 | #include "buildvm.h" | ||
10 | #include "lj_bc.h" | ||
11 | |||
12 | #if LJ_TARGET_X86ORX64 | ||
13 | |||
14 | /* Context for PE object emitter. */ | ||
15 | static char *strtab; | ||
16 | static size_t strtabofs; | ||
17 | |||
18 | /* -- PE object definitions ----------------------------------------------- */ | ||
19 | |||
20 | /* PE header. */ | ||
21 | typedef struct PEheader { | ||
22 | uint16_t arch; | ||
23 | uint16_t nsects; | ||
24 | uint32_t time; | ||
25 | uint32_t symtabofs; | ||
26 | uint32_t nsyms; | ||
27 | uint16_t opthdrsz; | ||
28 | uint16_t flags; | ||
29 | } PEheader; | ||
30 | |||
31 | /* PE section. */ | ||
32 | typedef struct PEsection { | ||
33 | char name[8]; | ||
34 | uint32_t vsize; | ||
35 | uint32_t vaddr; | ||
36 | uint32_t size; | ||
37 | uint32_t ofs; | ||
38 | uint32_t relocofs; | ||
39 | uint32_t lineofs; | ||
40 | uint16_t nreloc; | ||
41 | uint16_t nline; | ||
42 | uint32_t flags; | ||
43 | } PEsection; | ||
44 | |||
45 | /* PE relocation. */ | ||
46 | typedef struct PEreloc { | ||
47 | uint32_t vaddr; | ||
48 | uint32_t symidx; | ||
49 | uint16_t type; | ||
50 | } PEreloc; | ||
51 | |||
52 | /* Cannot use sizeof, because it pads up to the max. alignment. */ | ||
53 | #define PEOBJ_RELOC_SIZE (4+4+2) | ||
54 | |||
55 | /* PE symbol table entry. */ | ||
56 | typedef struct PEsym { | ||
57 | union { | ||
58 | char name[8]; | ||
59 | uint32_t nameref[2]; | ||
60 | } n; | ||
61 | uint32_t value; | ||
62 | int16_t sect; | ||
63 | uint16_t type; | ||
64 | uint8_t scl; | ||
65 | uint8_t naux; | ||
66 | } PEsym; | ||
67 | |||
68 | /* PE symbol table auxiliary entry for a section. */ | ||
69 | typedef struct PEsymaux { | ||
70 | uint32_t size; | ||
71 | uint16_t nreloc; | ||
72 | uint16_t nline; | ||
73 | uint32_t cksum; | ||
74 | uint16_t assoc; | ||
75 | uint8_t comdatsel; | ||
76 | uint8_t unused[3]; | ||
77 | } PEsymaux; | ||
78 | |||
79 | /* Cannot use sizeof, because it pads up to the max. alignment. */ | ||
80 | #define PEOBJ_SYM_SIZE (8+4+2+2+1+1) | ||
81 | |||
82 | /* PE object CPU specific defines. */ | ||
83 | #if LJ_TARGET_X86 | ||
84 | #define PEOBJ_ARCH_TARGET 0x014c | ||
85 | #define PEOBJ_RELOC_REL32 0x14 /* MS: REL32, GNU: DISP32. */ | ||
86 | #define PEOBJ_RELOC_DIR32 0x06 | ||
87 | #elif LJ_TARGET_X64 | ||
88 | #define PEOBJ_ARCH_TARGET 0x8664 | ||
89 | #define PEOBJ_RELOC_REL32 0x04 /* MS: REL32, GNU: DISP32. */ | ||
90 | #define PEOBJ_RELOC_DIR32 0x02 | ||
91 | #define PEOBJ_RELOC_ADDR32NB 0x03 | ||
92 | #endif | ||
93 | |||
94 | /* Section numbers (0-based). */ | ||
95 | enum { | ||
96 | PEOBJ_SECT_ABS = -2, | ||
97 | PEOBJ_SECT_UNDEF = -1, | ||
98 | PEOBJ_SECT_TEXT, | ||
99 | #if LJ_TARGET_X64 | ||
100 | PEOBJ_SECT_PDATA, | ||
101 | PEOBJ_SECT_XDATA, | ||
102 | #endif | ||
103 | PEOBJ_SECT_RDATA_Z, | ||
104 | PEOBJ_NSECTIONS | ||
105 | }; | ||
106 | |||
107 | /* Symbol types. */ | ||
108 | #define PEOBJ_TYPE_NULL 0 | ||
109 | #define PEOBJ_TYPE_FUNC 0x20 | ||
110 | |||
111 | /* Symbol storage class. */ | ||
112 | #define PEOBJ_SCL_EXTERN 2 | ||
113 | #define PEOBJ_SCL_STATIC 3 | ||
114 | |||
115 | /* -- PE object emitter --------------------------------------------------- */ | ||
116 | |||
117 | /* Emit PE object symbol. */ | ||
118 | static void emit_peobj_sym(BuildCtx *ctx, const char *name, uint32_t value, | ||
119 | int sect, int type, int scl) | ||
120 | { | ||
121 | PEsym sym; | ||
122 | size_t len = strlen(name); | ||
123 | if (!strtab) { /* Pass 1: only calculate string table length. */ | ||
124 | if (len > 8) strtabofs += len+1; | ||
125 | return; | ||
126 | } | ||
127 | if (len <= 8) { | ||
128 | memcpy(sym.n.name, name, len); | ||
129 | memset(sym.n.name+len, 0, 8-len); | ||
130 | } else { | ||
131 | sym.n.nameref[0] = 0; | ||
132 | sym.n.nameref[1] = (uint32_t)strtabofs; | ||
133 | memcpy(strtab + strtabofs, name, len); | ||
134 | strtab[strtabofs+len] = 0; | ||
135 | strtabofs += len+1; | ||
136 | } | ||
137 | sym.value = value; | ||
138 | sym.sect = (int16_t)(sect+1); /* 1-based section number. */ | ||
139 | sym.type = (uint16_t)type; | ||
140 | sym.scl = (uint8_t)scl; | ||
141 | sym.naux = 0; | ||
142 | owrite(ctx, &sym, PEOBJ_SYM_SIZE); | ||
143 | } | ||
144 | |||
145 | /* Emit PE object section symbol. */ | ||
146 | static void emit_peobj_sym_sect(BuildCtx *ctx, PEsection *pesect, int sect) | ||
147 | { | ||
148 | PEsym sym; | ||
149 | PEsymaux aux; | ||
150 | if (!strtab) return; /* Pass 1: no output. */ | ||
151 | memcpy(sym.n.name, pesect[sect].name, 8); | ||
152 | sym.value = 0; | ||
153 | sym.sect = (int16_t)(sect+1); /* 1-based section number. */ | ||
154 | sym.type = PEOBJ_TYPE_NULL; | ||
155 | sym.scl = PEOBJ_SCL_STATIC; | ||
156 | sym.naux = 1; | ||
157 | owrite(ctx, &sym, PEOBJ_SYM_SIZE); | ||
158 | memset(&aux, 0, sizeof(PEsymaux)); | ||
159 | aux.size = pesect[sect].size; | ||
160 | aux.nreloc = pesect[sect].nreloc; | ||
161 | owrite(ctx, &aux, PEOBJ_SYM_SIZE); | ||
162 | } | ||
163 | |||
164 | /* Emit Windows PE object file. */ | ||
165 | void emit_peobj(BuildCtx *ctx) | ||
166 | { | ||
167 | PEheader pehdr; | ||
168 | PEsection pesect[PEOBJ_NSECTIONS]; | ||
169 | uint32_t sofs; | ||
170 | int i, nrsym; | ||
171 | union { uint8_t b; uint32_t u; } host_endian; | ||
172 | |||
173 | host_endian.u = 1; | ||
174 | if (host_endian.b != LJ_ENDIAN_SELECT(1, 0)) { | ||
175 | fprintf(stderr, "Error: different byte order for host and target\n"); | ||
176 | exit(1); | ||
177 | } | ||
178 | |||
179 | sofs = sizeof(PEheader) + PEOBJ_NSECTIONS*sizeof(PEsection); | ||
180 | |||
181 | /* Fill in PE sections. */ | ||
182 | memset(&pesect, 0, PEOBJ_NSECTIONS*sizeof(PEsection)); | ||
183 | memcpy(pesect[PEOBJ_SECT_TEXT].name, ".text", sizeof(".text")-1); | ||
184 | pesect[PEOBJ_SECT_TEXT].ofs = sofs; | ||
185 | sofs += (pesect[PEOBJ_SECT_TEXT].size = (uint32_t)ctx->codesz); | ||
186 | pesect[PEOBJ_SECT_TEXT].relocofs = sofs; | ||
187 | sofs += (pesect[PEOBJ_SECT_TEXT].nreloc = (uint16_t)ctx->nreloc) * PEOBJ_RELOC_SIZE; | ||
188 | /* Flags: 60 = read+execute, 50 = align16, 20 = code. */ | ||
189 | pesect[PEOBJ_SECT_TEXT].flags = 0x60500020; | ||
190 | |||
191 | #if LJ_TARGET_X64 | ||
192 | memcpy(pesect[PEOBJ_SECT_PDATA].name, ".pdata", sizeof(".pdata")-1); | ||
193 | pesect[PEOBJ_SECT_PDATA].ofs = sofs; | ||
194 | sofs += (pesect[PEOBJ_SECT_PDATA].size = 6*4); | ||
195 | pesect[PEOBJ_SECT_PDATA].relocofs = sofs; | ||
196 | sofs += (pesect[PEOBJ_SECT_PDATA].nreloc = 6) * PEOBJ_RELOC_SIZE; | ||
197 | /* Flags: 40 = read, 30 = align4, 40 = initialized data. */ | ||
198 | pesect[PEOBJ_SECT_PDATA].flags = 0x40300040; | ||
199 | |||
200 | memcpy(pesect[PEOBJ_SECT_XDATA].name, ".xdata", sizeof(".xdata")-1); | ||
201 | pesect[PEOBJ_SECT_XDATA].ofs = sofs; | ||
202 | sofs += (pesect[PEOBJ_SECT_XDATA].size = 8*2+4+6*2); /* See below. */ | ||
203 | pesect[PEOBJ_SECT_XDATA].relocofs = sofs; | ||
204 | sofs += (pesect[PEOBJ_SECT_XDATA].nreloc = 1) * PEOBJ_RELOC_SIZE; | ||
205 | /* Flags: 40 = read, 30 = align4, 40 = initialized data. */ | ||
206 | pesect[PEOBJ_SECT_XDATA].flags = 0x40300040; | ||
207 | #endif | ||
208 | |||
209 | memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z", sizeof(".rdata$Z")-1); | ||
210 | pesect[PEOBJ_SECT_RDATA_Z].ofs = sofs; | ||
211 | sofs += (pesect[PEOBJ_SECT_RDATA_Z].size = (uint32_t)strlen(ctx->dasm_ident)+1); | ||
212 | /* Flags: 40 = read, 30 = align4, 40 = initialized data. */ | ||
213 | pesect[PEOBJ_SECT_RDATA_Z].flags = 0x40300040; | ||
214 | |||
215 | /* Fill in PE header. */ | ||
216 | pehdr.arch = PEOBJ_ARCH_TARGET; | ||
217 | pehdr.nsects = PEOBJ_NSECTIONS; | ||
218 | pehdr.time = 0; /* Timestamp is optional. */ | ||
219 | pehdr.symtabofs = sofs; | ||
220 | pehdr.opthdrsz = 0; | ||
221 | pehdr.flags = 0; | ||
222 | |||
223 | /* Compute the size of the symbol table: | ||
224 | ** @feat.00 + nsections*2 | ||
225 | ** + asm_start + nsym | ||
226 | ** + nrsym | ||
227 | */ | ||
228 | nrsym = ctx->nrelocsym; | ||
229 | pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+ctx->nsym + nrsym; | ||
230 | #if LJ_TARGET_X64 | ||
231 | pehdr.nsyms += 1; /* Symbol for lj_err_unwind_win64. */ | ||
232 | #endif | ||
233 | |||
234 | /* Write PE object header and all sections. */ | ||
235 | owrite(ctx, &pehdr, sizeof(PEheader)); | ||
236 | owrite(ctx, &pesect, sizeof(PEsection)*PEOBJ_NSECTIONS); | ||
237 | |||
238 | /* Write .text section. */ | ||
239 | owrite(ctx, ctx->code, ctx->codesz); | ||
240 | for (i = 0; i < ctx->nreloc; i++) { | ||
241 | PEreloc reloc; | ||
242 | reloc.vaddr = (uint32_t)ctx->reloc[i].ofs; | ||
243 | reloc.symidx = 1+2+ctx->reloc[i].sym; /* Reloc syms are after .text sym. */ | ||
244 | reloc.type = ctx->reloc[i].type ? PEOBJ_RELOC_REL32 : PEOBJ_RELOC_DIR32; | ||
245 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); | ||
246 | } | ||
247 | |||
248 | #if LJ_TARGET_X64 | ||
249 | { /* Write .pdata section. */ | ||
250 | uint32_t fcofs = (uint32_t)ctx->sym[ctx->nsym-1].ofs; | ||
251 | uint32_t pdata[3]; /* Start of .text, end of .text and .xdata. */ | ||
252 | PEreloc reloc; | ||
253 | pdata[0] = 0; pdata[1] = fcofs; pdata[2] = 0; | ||
254 | owrite(ctx, &pdata, sizeof(pdata)); | ||
255 | pdata[0] = fcofs; pdata[1] = (uint32_t)ctx->codesz; pdata[2] = 20; | ||
256 | owrite(ctx, &pdata, sizeof(pdata)); | ||
257 | reloc.vaddr = 0; reloc.symidx = 1+2+nrsym+2+2+1; | ||
258 | reloc.type = PEOBJ_RELOC_ADDR32NB; | ||
259 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); | ||
260 | reloc.vaddr = 4; reloc.symidx = 1+2+nrsym+2+2+1; | ||
261 | reloc.type = PEOBJ_RELOC_ADDR32NB; | ||
262 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); | ||
263 | reloc.vaddr = 8; reloc.symidx = 1+2+nrsym+2; | ||
264 | reloc.type = PEOBJ_RELOC_ADDR32NB; | ||
265 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); | ||
266 | reloc.vaddr = 12; reloc.symidx = 1+2+nrsym+2+2+1; | ||
267 | reloc.type = PEOBJ_RELOC_ADDR32NB; | ||
268 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); | ||
269 | reloc.vaddr = 16; reloc.symidx = 1+2+nrsym+2+2+1; | ||
270 | reloc.type = PEOBJ_RELOC_ADDR32NB; | ||
271 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); | ||
272 | reloc.vaddr = 20; reloc.symidx = 1+2+nrsym+2; | ||
273 | reloc.type = PEOBJ_RELOC_ADDR32NB; | ||
274 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); | ||
275 | } | ||
276 | { /* Write .xdata section. */ | ||
277 | uint16_t xdata[8+2+6]; | ||
278 | PEreloc reloc; | ||
279 | xdata[0] = 0x01|0x08|0x10; /* Ver. 1, uhandler/ehandler, prolog size 0. */ | ||
280 | xdata[1] = 0x0005; /* Number of unwind codes, no frame pointer. */ | ||
281 | xdata[2] = 0x4200; /* Stack offset 4*8+8 = aword*5. */ | ||
282 | xdata[3] = 0x3000; /* Push rbx. */ | ||
283 | xdata[4] = 0x6000; /* Push rsi. */ | ||
284 | xdata[5] = 0x7000; /* Push rdi. */ | ||
285 | xdata[6] = 0x5000; /* Push rbp. */ | ||
286 | xdata[7] = 0; /* Alignment. */ | ||
287 | xdata[8] = xdata[9] = 0; /* Relocated address of exception handler. */ | ||
288 | xdata[10] = 0x01; /* Ver. 1, no handler, prolog size 0. */ | ||
289 | xdata[11] = 0x1504; /* Number of unwind codes, fp = rbp, fpofs = 16. */ | ||
290 | xdata[12] = 0x0300; /* set_fpreg. */ | ||
291 | xdata[13] = 0x0200; /* stack offset 0*8+8 = aword*1. */ | ||
292 | xdata[14] = 0x3000; /* Push rbx. */ | ||
293 | xdata[15] = 0x5000; /* Push rbp. */ | ||
294 | owrite(ctx, &xdata, sizeof(xdata)); | ||
295 | reloc.vaddr = 2*8; reloc.symidx = 1+2+nrsym+2+2; | ||
296 | reloc.type = PEOBJ_RELOC_ADDR32NB; | ||
297 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); | ||
298 | } | ||
299 | #endif | ||
300 | |||
301 | /* Write .rdata$Z section. */ | ||
302 | owrite(ctx, ctx->dasm_ident, strlen(ctx->dasm_ident)+1); | ||
303 | |||
304 | /* Write symbol table. */ | ||
305 | strtab = NULL; /* 1st pass: collect string sizes. */ | ||
306 | for (;;) { | ||
307 | strtabofs = 4; | ||
308 | /* Mark as SafeSEH compliant. */ | ||
309 | emit_peobj_sym(ctx, "@feat.00", 1, | ||
310 | PEOBJ_SECT_ABS, PEOBJ_TYPE_NULL, PEOBJ_SCL_STATIC); | ||
311 | |||
312 | emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_TEXT); | ||
313 | for (i = 0; i < nrsym; i++) | ||
314 | emit_peobj_sym(ctx, ctx->relocsym[i], 0, | ||
315 | PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN); | ||
316 | |||
317 | #if LJ_TARGET_X64 | ||
318 | emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_PDATA); | ||
319 | emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_XDATA); | ||
320 | emit_peobj_sym(ctx, "lj_err_unwind_win64", 0, | ||
321 | PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN); | ||
322 | #endif | ||
323 | |||
324 | emit_peobj_sym(ctx, ctx->beginsym, 0, | ||
325 | PEOBJ_SECT_TEXT, PEOBJ_TYPE_NULL, PEOBJ_SCL_EXTERN); | ||
326 | for (i = 0; i < ctx->nsym; i++) | ||
327 | emit_peobj_sym(ctx, ctx->sym[i].name, (uint32_t)ctx->sym[i].ofs, | ||
328 | PEOBJ_SECT_TEXT, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN); | ||
329 | |||
330 | emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA_Z); | ||
331 | |||
332 | if (strtab) | ||
333 | break; | ||
334 | /* 2nd pass: alloc strtab, write syms and copy strings. */ | ||
335 | strtab = (char *)malloc(strtabofs); | ||
336 | *(uint32_t *)strtab = (uint32_t)strtabofs; | ||
337 | } | ||
338 | |||
339 | /* Write string table. */ | ||
340 | owrite(ctx, strtab, strtabofs); | ||
341 | } | ||
342 | |||
343 | #else | ||
344 | |||
345 | void emit_peobj(BuildCtx *ctx) | ||
346 | { | ||
347 | UNUSED(ctx); | ||
348 | fprintf(stderr, "Error: no PE object support for this target\n"); | ||
349 | exit(1); | ||
350 | } | ||
351 | |||
352 | #endif | ||