3 ** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
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).
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.
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.
23 #include "lj_ircall.h"
26 #include "lj_dispatch.h"
38 /* ------------------------------------------------------------------------ */
40 /* DynASM glue definitions. */
42 #define Dst_DECL BuildCtx *ctx
43 #define Dst_REF (ctx->D)
46 #include "../dynasm/dasm_proto.h"
48 /* Glue macros for DynASM. */
49 static int collect_reloc(BuildCtx
*ctx
, uint8_t *addr
, int idx
, int type
);
51 #define DASM_EXTERN(ctx, addr, idx, type) \
52 collect_reloc(ctx, addr, idx, type)
54 /* ------------------------------------------------------------------------ */
56 /* Avoid trouble if cross-compiling for an x86 target. Speed doesn't matter. */
57 #define DASM_ALIGNED_WRITES 1
59 /* Embed architecture-specific DynASM encoder. */
60 #if LJ_TARGET_X86ORX64
61 #include "../dynasm/dasm_x86.h"
63 #include "../dynasm/dasm_arm.h"
65 #include "../dynasm/dasm_arm64.h"
67 #include "../dynasm/dasm_ppc.h"
69 #include "../dynasm/dasm_mips.h"
71 #error "No support for this architecture (yet)"
74 /* Embed generated architecture-specific backend. */
75 #include "buildvm_arch.h"
77 /* ------------------------------------------------------------------------ */
79 void owrite(BuildCtx
*ctx
, const void *ptr
, size_t sz
)
81 if (fwrite(ptr
, 1, sz
, ctx
->fp
) != sz
) {
82 fprintf(stderr
, "Error: cannot write to output file: %s\n",
88 /* ------------------------------------------------------------------------ */
90 /* Emit code as raw bytes. Only used for DynASM debugging. */
91 static void emit_raw(BuildCtx
*ctx
)
93 owrite(ctx
, ctx
->code
, ctx
->codesz
);
96 /* -- Build machine code -------------------------------------------------- */
98 static const char *sym_decorate(BuildCtx
*ctx
,
99 const char *prefix
, const char *suffix
)
104 const char *symprefix
= ctx
->mode
== BUILD_machasm
? "_" : "";
105 #elif LJ_TARGET_XBOX360
106 const char *symprefix
= "";
108 const char *symprefix
= ctx
->mode
!= BUILD_elfasm
? "_" : "";
110 sprintf(name
, "%s%s%s", symprefix
, prefix
, suffix
);
111 p
= strchr(name
, '@');
113 #if LJ_TARGET_X86ORX64
114 if (!LJ_64
&& (ctx
->mode
== BUILD_coffasm
|| ctx
->mode
== BUILD_peobj
))
115 name
[0] = name
[1] == 'R' ? '_' : '@'; /* Just for _RtlUnwind@16. */
118 #elif LJ_TARGET_PPC && !LJ_TARGET_CONSOLE
124 p
= (char *)malloc(strlen(name
)+1); /* MSVC doesn't like strdup. */
129 #define NRELOCSYM (sizeof(extnames)/sizeof(extnames[0])-1)
131 static int relocmap
[NRELOCSYM
];
133 /* Collect external relocations. */
134 static int collect_reloc(BuildCtx
*ctx
, uint8_t *addr
, int idx
, int type
)
136 if (ctx
->nreloc
>= BUILD_MAX_RELOC
) {
137 fprintf(stderr
, "Error: too many relocations, increase BUILD_MAX_RELOC.\n");
140 if (relocmap
[idx
] < 0) {
141 relocmap
[idx
] = ctx
->nrelocsym
;
142 ctx
->relocsym
[ctx
->nrelocsym
] = sym_decorate(ctx
, "", extnames
[idx
]);
145 ctx
->reloc
[ctx
->nreloc
].ofs
= (int32_t)(addr
- ctx
->code
);
146 ctx
->reloc
[ctx
->nreloc
].sym
= relocmap
[idx
];
147 ctx
->reloc
[ctx
->nreloc
].type
= type
;
149 #if LJ_TARGET_XBOX360
150 return (int)(ctx
->code
- addr
) + 4; /* Encode symbol offset of .text. */
152 return 0; /* Encode symbol offset of 0. */
156 /* Naive insertion sort. Performance doesn't matter here. */
157 static void sym_insert(BuildCtx
*ctx
, int32_t ofs
,
158 const char *prefix
, const char *suffix
)
160 ptrdiff_t i
= ctx
->nsym
++;
162 if (ctx
->sym
[i
-1].ofs
<= ofs
)
164 ctx
->sym
[i
] = ctx
->sym
[i
-1];
167 ctx
->sym
[i
].ofs
= ofs
;
168 ctx
->sym
[i
].name
= sym_decorate(ctx
, prefix
, suffix
);
171 /* Build the machine code. */
172 static int build_code(BuildCtx
*ctx
)
177 /* Initialize DynASM structures. */
178 ctx
->nglob
= GLOB__MAX
;
179 ctx
->glob
= (void **)malloc(ctx
->nglob
*sizeof(void *));
180 memset(ctx
->glob
, 0, ctx
->nglob
*sizeof(void *));
183 ctx
->globnames
= globnames
;
184 ctx
->extnames
= extnames
;
185 ctx
->relocsym
= (const char **)malloc(NRELOCSYM
*sizeof(const char *));
187 for (i
= 0; i
< (int)NRELOCSYM
; i
++) relocmap
[i
] = -1;
189 ctx
->dasm_ident
= DASM_IDENT
;
190 ctx
->dasm_arch
= DASM_ARCH
;
192 dasm_init(Dst
, DASM_MAXSECTION
);
193 dasm_setupglobal(Dst
, ctx
->glob
, ctx
->nglob
);
194 dasm_setup(Dst
, build_actionlist
);
196 /* Call arch-specific backend to emit the code. */
197 ctx
->npc
= build_backend(ctx
);
199 /* Finalize the code. */
200 (void)dasm_checkstep(Dst
, -1);
201 if ((status
= dasm_link(Dst
, &ctx
->codesz
))) return status
;
202 ctx
->code
= (uint8_t *)malloc(ctx
->codesz
);
203 if ((status
= dasm_encode(Dst
, (void *)ctx
->code
))) return status
;
205 /* Allocate symbol table and bytecode offsets. */
206 ctx
->beginsym
= sym_decorate(ctx
, "", LABEL_PREFIX
"vm_asm_begin");
207 ctx
->sym
= (BuildSym
*)malloc((ctx
->npc
+ctx
->nglob
+1)*sizeof(BuildSym
));
209 ctx
->bc_ofs
= (int32_t *)malloc(ctx
->npc
*sizeof(int32_t));
211 /* Collect the opcodes (PC labels). */
212 for (i
= 0; i
< ctx
->npc
; i
++) {
213 int32_t ofs
= dasm_getpclabel(Dst
, i
);
214 if (ofs
< 0) return 0x22000000|i
;
215 ctx
->bc_ofs
[i
] = ofs
;
217 !(i
== BC_JFORI
|| i
== BC_JFORL
|| i
== BC_JITERL
|| i
== BC_JLOOP
||
218 i
== BC_IFORL
|| i
== BC_IITERL
|| i
== BC_ILOOP
)) &&
219 (LJ_HASFFI
|| i
!= BC_KCDATA
))
220 sym_insert(ctx
, ofs
, LABEL_PREFIX_BC
, bc_names
[i
]);
223 /* Collect the globals (named labels). */
224 for (i
= 0; i
< ctx
->nglob
; i
++) {
225 const char *gl
= globnames
[i
];
226 int len
= (int)strlen(gl
);
228 fprintf(stderr
, "Error: undefined global %s\n", gl
);
231 /* Skip the _Z symbols. */
232 if (!(len
>= 2 && gl
[len
-2] == '_' && gl
[len
-1] == 'Z'))
233 sym_insert(ctx
, (int32_t)((uint8_t *)(ctx
->glob
[i
]) - ctx
->code
),
234 LABEL_PREFIX
, globnames
[i
]);
237 /* Close the address range. */
238 sym_insert(ctx
, (int32_t)ctx
->codesz
, "", "");
246 /* -- Generate VM enums --------------------------------------------------- */
248 const char *const bc_names
[] = {
249 #define BCNAME(name, ma, mb, mc, mt) #name,
256 const char *const ir_names
[] = {
257 #define IRNAME(name, m, m1, m2) #name,
263 const char *const irt_names
[] = {
264 #define IRTNAME(name, size) #name,
270 const char *const irfpm_names
[] = {
271 #define FPMNAME(name) #name,
277 const char *const irfield_names
[] = {
278 #define FLNAME(name, ofs) #name,
284 const char *const ircall_names
[] = {
285 #define IRCALLNAME(cond, name, nargs, kind, type, flags) #name,
286 IRCALLDEF(IRCALLNAME
)
291 static const char *const trace_errors
[] = {
292 #define TREDEF(name, msg) msg,
293 #include "lj_traceerr.h"
299 static const char *lower(char *buf
, const char *s
)
303 *p
++ = (*s
>= 'A' && *s
<= 'Z') ? *s
+0x20 : *s
;
311 /* Emit C source code for bytecode-related definitions. */
312 static void emit_bcdef(BuildCtx
*ctx
)
315 fprintf(ctx
->fp
, "/* This is a generated file. DO NOT EDIT! */\n\n");
316 fprintf(ctx
->fp
, "LJ_DATADEF const uint16_t lj_bc_ofs[] = {\n");
317 for (i
= 0; i
< ctx
->npc
; i
++) {
319 fprintf(ctx
->fp
, ",\n");
320 fprintf(ctx
->fp
, "%d", ctx
->bc_ofs
[i
]);
324 /* Emit VM definitions as Lua code for debug modules. */
325 static void emit_vmdef(BuildCtx
*ctx
)
331 fprintf(ctx
->fp
, "-- This is a generated file. DO NOT EDIT!\n\n");
332 fprintf(ctx
->fp
, "assert(require(\"jit\").version == \"%s\", \"LuaJIT core/library version mismatch\")\n\n", LUAJIT_VERSION
);
333 fprintf(ctx
->fp
, "return {\n\n");
335 fprintf(ctx
->fp
, "bcnames = \"");
336 for (i
= 0; bc_names
[i
]; i
++) fprintf(ctx
->fp
, "%-6s", bc_names
[i
]);
337 fprintf(ctx
->fp
, "\",\n\n");
340 fprintf(ctx
->fp
, "irnames = \"");
341 for (i
= 0; ir_names
[i
]; i
++) fprintf(ctx
->fp
, "%-6s", ir_names
[i
]);
342 fprintf(ctx
->fp
, "\",\n\n");
344 fprintf(ctx
->fp
, "irfpm = { [0]=");
345 for (i
= 0; irfpm_names
[i
]; i
++)
346 fprintf(ctx
->fp
, "\"%s\", ", lower(buf
, irfpm_names
[i
]));
347 fprintf(ctx
->fp
, "},\n\n");
349 fprintf(ctx
->fp
, "irfield = { [0]=");
350 for (i
= 0; irfield_names
[i
]; i
++) {
352 lower(buf
, irfield_names
[i
]);
353 p
= strchr(buf
, '_');
355 fprintf(ctx
->fp
, "\"%s\", ", buf
);
357 fprintf(ctx
->fp
, "},\n\n");
359 fprintf(ctx
->fp
, "ircall = {\n[0]=");
360 for (i
= 0; ircall_names
[i
]; i
++)
361 fprintf(ctx
->fp
, "\"%s\",\n", ircall_names
[i
]);
362 fprintf(ctx
->fp
, "},\n\n");
364 fprintf(ctx
->fp
, "traceerr = {\n[0]=");
365 for (i
= 0; trace_errors
[i
]; i
++)
366 fprintf(ctx
->fp
, "\"%s\",\n", trace_errors
[i
]);
367 fprintf(ctx
->fp
, "},\n\n");
371 /* -- Argument parsing ---------------------------------------------------- */
373 /* Build mode names. */
374 static const char *const modenames
[] = {
375 #define BUILDNAME(name) #name,
381 /* Print usage information and exit. */
382 static void usage(void)
385 fprintf(stderr
, LUAJIT_VERSION
" VM builder.\n");
386 fprintf(stderr
, LUAJIT_COPYRIGHT
", " LUAJIT_URL
"\n");
387 fprintf(stderr
, "Target architecture: " LJ_ARCH_NAME
"\n\n");
388 fprintf(stderr
, "Usage: buildvm -m mode [-o outfile] [infiles...]\n\n");
389 fprintf(stderr
, "Available modes:\n");
390 for (i
= 0; i
< BUILD__MAX
; i
++)
391 fprintf(stderr
, " %s\n", modenames
[i
]);
395 /* Parse the output mode name. */
396 static BuildMode
parsemode(const char *mode
)
399 for (i
= 0; modenames
[i
]; i
++)
400 if (!strcmp(mode
, modenames
[i
]))
403 return (BuildMode
)-1;
406 /* Parse arguments. */
407 static void parseargs(BuildCtx
*ctx
, char **argv
)
411 ctx
->mode
= (BuildMode
)-1;
413 for (i
= 1; (a
= argv
[i
]) != NULL
; i
++) {
425 if (a
[2] || argv
[i
] == NULL
) goto err
;
426 ctx
->mode
= parsemode(argv
[i
]);
430 if (a
[2] || argv
[i
] == NULL
) goto err
;
431 ctx
->outname
= argv
[i
];
440 if (ctx
->mode
== (BuildMode
)-1) goto err
;
443 int main(int argc
, char **argv
)
446 BuildCtx
*ctx
= &ctx_
;
449 if (sizeof(void *) != 4*LJ_32
+8*LJ_64
) {
450 fprintf(stderr
,"Error: pointer size mismatch in cross-build.\n");
451 fprintf(stderr
,"Try: make HOST_CC=\"gcc -m32\" CROSS=...\n\n");
456 parseargs(ctx
, argv
);
458 if ((status
= build_code(ctx
))) {
459 fprintf(stderr
,"Error: DASM error %08x\n", status
);
473 if (ctx
->outname
[0] == '-' && ctx
->outname
[1] == '\0') {
477 _setmode(_fileno(stdout
), _O_BINARY
); /* Yuck. */
479 } else if (!(ctx
->fp
= fopen(ctx
->outname
, binmode
? "wb" : "w"))) {
480 fprintf(stderr
, "Error: cannot open output file '%s': %s\n",
481 ctx
->outname
, strerror(errno
));
505 fprintf(ctx
->fp
, "}\n\n");
520 if (ferror(ctx
->fp
)) {
521 fprintf(stderr
, "Error: cannot write to output file: %s\n",