1 | /* |
2 | ** LuaJIT VM builder: Assembler source code emitter. |
3 | ** Copyright (C) 2005-2014 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 LJ_TARGET_PS3 |
112 | #define TOCPREFIX "." |
113 | #else |
114 | #define TOCPREFIX "" |
115 | #endif |
116 | if ((ins >> 26) == 16) { |
117 | fprintf(ctx->fp, "\t%s %d, %d, " TOCPREFIX "%s\n" , |
118 | (ins & 1) ? "bcl" : "bc" , (ins >> 21) & 31, (ins >> 16) & 31, sym); |
119 | } else if ((ins >> 26) == 18) { |
120 | fprintf(ctx->fp, "\t%s " TOCPREFIX "%s\n" , (ins & 1) ? "bl" : "b" , sym); |
121 | } else { |
122 | fprintf(stderr, |
123 | "Error: unsupported opcode %08x for %s symbol relocation.\n" , |
124 | ins, sym); |
125 | exit(1); |
126 | } |
127 | #elif LJ_TARGET_MIPS |
128 | fprintf(stderr, |
129 | "Error: unsupported opcode %08x for %s symbol relocation.\n" , |
130 | ins, sym); |
131 | exit(1); |
132 | #else |
133 | #error "missing relocation support for this architecture" |
134 | #endif |
135 | } |
136 | #endif |
137 | |
138 | #if LJ_TARGET_ARM |
139 | #define ELFASM_PX "%%" |
140 | #else |
141 | #define ELFASM_PX "@" |
142 | #endif |
143 | |
144 | /* Emit an assembler label. */ |
145 | static void emit_asm_label(BuildCtx *ctx, const char *name, int size, int isfunc) |
146 | { |
147 | switch (ctx->mode) { |
148 | case BUILD_elfasm: |
149 | #if LJ_TARGET_PS3 |
150 | if (!strncmp(name, "lj_vm_" , 6) && |
151 | strcmp(name, ctx->beginsym) && |
152 | !strstr(name, "hook" )) { |
153 | fprintf(ctx->fp, |
154 | "\n\t.globl %s\n" |
155 | "\t.section \".opd\",\"aw\"\n" |
156 | "%s:\n" |
157 | "\t.long .%s,.TOC.@tocbase32\n" |
158 | "\t.size %s,8\n" |
159 | "\t.previous\n" |
160 | "\t.globl .%s\n" |
161 | "\t.hidden .%s\n" |
162 | "\t.type .%s, " ELFASM_PX "function\n" |
163 | "\t.size .%s, %d\n" |
164 | ".%s:\n" , |
165 | name, name, name, name, name, name, name, name, size, name); |
166 | break; |
167 | } |
168 | #endif |
169 | fprintf(ctx->fp, |
170 | "\n\t.globl %s\n" |
171 | "\t.hidden %s\n" |
172 | "\t.type %s, " ELFASM_PX "%s\n" |
173 | "\t.size %s, %d\n" |
174 | "%s:\n" , |
175 | name, name, name, isfunc ? "function" : "object" , name, size, name); |
176 | break; |
177 | case BUILD_coffasm: |
178 | fprintf(ctx->fp, "\n\t.globl %s\n" , name); |
179 | if (isfunc) |
180 | fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n" , name); |
181 | fprintf(ctx->fp, "%s:\n" , name); |
182 | break; |
183 | case BUILD_machasm: |
184 | fprintf(ctx->fp, |
185 | "\n\t.private_extern %s\n" |
186 | "%s:\n" , name, name); |
187 | break; |
188 | default: |
189 | break; |
190 | } |
191 | } |
192 | |
193 | /* Emit alignment. */ |
194 | static void emit_asm_align(BuildCtx *ctx, int bits) |
195 | { |
196 | switch (ctx->mode) { |
197 | case BUILD_elfasm: |
198 | case BUILD_coffasm: |
199 | fprintf(ctx->fp, "\t.p2align %d\n" , bits); |
200 | break; |
201 | case BUILD_machasm: |
202 | fprintf(ctx->fp, "\t.align %d\n" , bits); |
203 | break; |
204 | default: |
205 | break; |
206 | } |
207 | } |
208 | |
209 | /* ------------------------------------------------------------------------ */ |
210 | |
211 | /* Emit assembler source code. */ |
212 | void emit_asm(BuildCtx *ctx) |
213 | { |
214 | int i, rel; |
215 | |
216 | fprintf(ctx->fp, "\t.file \"buildvm_%s.dasc\"\n" , ctx->dasm_arch); |
217 | fprintf(ctx->fp, "\t.text\n" ); |
218 | emit_asm_align(ctx, 4); |
219 | |
220 | #if LJ_TARGET_PS3 |
221 | emit_asm_label(ctx, ctx->beginsym, ctx->codesz, 0); |
222 | #else |
223 | emit_asm_label(ctx, ctx->beginsym, 0, 0); |
224 | #endif |
225 | if (ctx->mode != BUILD_machasm) |
226 | fprintf(ctx->fp, ".Lbegin:\n" ); |
227 | |
228 | #if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND |
229 | /* This should really be moved into buildvm_arm.dasc. */ |
230 | fprintf(ctx->fp, |
231 | ".fnstart\n" |
232 | ".save {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n" |
233 | ".pad #28\n" ); |
234 | #endif |
235 | #if LJ_TARGET_MIPS |
236 | fprintf(ctx->fp, ".set nomips16\n.abicalls\n.set noreorder\n.set nomacro\n" ); |
237 | #endif |
238 | |
239 | for (i = rel = 0; i < ctx->nsym; i++) { |
240 | int32_t ofs = ctx->sym[i].ofs; |
241 | int32_t next = ctx->sym[i+1].ofs; |
242 | #if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND && LJ_HASFFI |
243 | if (!strcmp(ctx->sym[i].name, "lj_vm_ffi_call" )) |
244 | fprintf(ctx->fp, |
245 | ".globl lj_err_unwind_arm\n" |
246 | ".personality lj_err_unwind_arm\n" |
247 | ".fnend\n" |
248 | ".fnstart\n" |
249 | ".save {r4, r5, r11, lr}\n" |
250 | ".setfp r11, sp\n" ); |
251 | #endif |
252 | emit_asm_label(ctx, ctx->sym[i].name, next - ofs, 1); |
253 | while (rel < ctx->nreloc && ctx->reloc[rel].ofs <= next) { |
254 | BuildReloc *r = &ctx->reloc[rel]; |
255 | int n = r->ofs - ofs; |
256 | #if LJ_TARGET_X86ORX64 |
257 | if (ctx->mode == BUILD_machasm && r->type != 0) { |
258 | emit_asm_reloc_mach(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]); |
259 | } else { |
260 | emit_asm_bytes(ctx, ctx->code+ofs, n); |
261 | emit_asm_reloc(ctx, r->type, ctx->relocsym[r->sym]); |
262 | } |
263 | ofs += n+4; |
264 | #else |
265 | emit_asm_wordreloc(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]); |
266 | ofs += n; |
267 | #endif |
268 | rel++; |
269 | } |
270 | #if LJ_TARGET_X86ORX64 |
271 | emit_asm_bytes(ctx, ctx->code+ofs, next-ofs); |
272 | #else |
273 | emit_asm_words(ctx, ctx->code+ofs, next-ofs); |
274 | #endif |
275 | } |
276 | |
277 | #if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND |
278 | fprintf(ctx->fp, |
279 | #if !LJ_HASFFI |
280 | ".globl lj_err_unwind_arm\n" |
281 | ".personality lj_err_unwind_arm\n" |
282 | #endif |
283 | ".fnend\n" ); |
284 | #endif |
285 | |
286 | fprintf(ctx->fp, "\n" ); |
287 | switch (ctx->mode) { |
288 | case BUILD_elfasm: |
289 | #if !LJ_TARGET_PS3 |
290 | fprintf(ctx->fp, "\t.section .note.GNU-stack,\"\"," ELFASM_PX "progbits\n" ); |
291 | #endif |
292 | #if LJ_TARGET_PPCSPE |
293 | /* Soft-float ABI + SPE. */ |
294 | fprintf(ctx->fp, "\t.gnu_attribute 4, 2\n\t.gnu_attribute 8, 3\n" ); |
295 | #elif LJ_TARGET_PPC && !LJ_TARGET_PS3 |
296 | /* Hard-float ABI. */ |
297 | fprintf(ctx->fp, "\t.gnu_attribute 4, 1\n" ); |
298 | #endif |
299 | /* fallthrough */ |
300 | case BUILD_coffasm: |
301 | fprintf(ctx->fp, "\t.ident \"%s\"\n" , ctx->dasm_ident); |
302 | break; |
303 | case BUILD_machasm: |
304 | fprintf(ctx->fp, |
305 | "\t.cstring\n" |
306 | "\t.ascii \"%s\\0\"\n" , ctx->dasm_ident); |
307 | break; |
308 | default: |
309 | break; |
310 | } |
311 | fprintf(ctx->fp, "\n" ); |
312 | } |
313 | |
314 | |