@hsusulist/luc 0.2.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -9
- package/bin/luc.js +23 -2
- package/package.json +4 -5
- package/scripts/build-linux.js +34 -0
- package/scripts/postinstall.js +6 -0
- package/scripts/postpack.js +14 -0
- package/scripts/prepack.js +27 -0
- package/src/lcode-linux/lcode.c +290 -0
- package/src/luc.h +303 -0
- package/src/luc_core.c +5407 -0
- package/src/luc_libs.c +5262 -0
- package/src/luc_trans.c +1744 -0
package/src/luc_trans.c
ADDED
|
@@ -0,0 +1,1744 @@
|
|
|
1
|
+
/* JIT + AOT writer (Windows x64; no-op elsewhere) */
|
|
2
|
+
/* build: cl /c /O2 luc_trans.c or gcc -c -O2 -std=c99 luc_trans.c */
|
|
3
|
+
/* API: LUC_JIT_FALLBACK, luc_jit_run/call/shutdown, luc_aot_build/embedded */
|
|
4
|
+
/* DESIGN: one Proto per unit; whitelist-only; numeric ops heap-free; */
|
|
5
|
+
/* list GET/SETTABLE inlined under guards; no alloc/call (except MOD/POW) or raise, */
|
|
6
|
+
/* so bail returns -1 for bit-identical interpreter re-run, no deopt/GC roots. */
|
|
7
|
+
/* whitelist: MOVE LOADK LOADNIL LOADBOOL ADD SUB MUL DIV MOD POW UNM NOT */
|
|
8
|
+
/* EQ NE LT LE GT GE GETTABLE SETTABLE JMP JMPIF JMPIFNOT FORPREP FORLOOP CLOSE(no-op) RETURN(0/1) */
|
|
9
|
+
/* Kinds: K_INT<K_NUM, K_BOOL, K_NIL, K_LIST; guard-created so folds bail first. */
|
|
10
|
+
/* K_INT from integral LOADK, closed +,-,*,unm (overflow bails), FORPREP step guard. */
|
|
11
|
+
/* ABI: int fn(args RCX, nargs EDX, out R8); -1=bail, n=results in out[]. */
|
|
12
|
+
/* out[] only num/bool/nil (no GC root); C wrapper stores to LucState.stack. */
|
|
13
|
+
/* Frame: RBX=args RSI=out R12D=nargs; shadow+XMM saves+slot homes+list descs, zeroed. */
|
|
14
|
+
/* Homes: one XMM or frame slot per reg, hot loop regs pinned (mixed-pin); XMM0-4 scratch, */
|
|
15
|
+
/* XMM5 only if no CALL (MOD/POW); XMM6+ saved/restored; large frames stack-probed. */
|
|
16
|
+
/* Descs: cache arr/alen/stok per list def; safe (no alloc/call); hoists checks to preheader. */
|
|
17
|
+
|
|
18
|
+
#include "luc.h"
|
|
19
|
+
#include <stddef.h>
|
|
20
|
+
|
|
21
|
+
#define LUC_JIT_FALLBACK (-1)
|
|
22
|
+
|
|
23
|
+
int luc_jit_run(Closure *cl);
|
|
24
|
+
int luc_jit_call(LucState *L,int func,int nargs,int nres);
|
|
25
|
+
void luc_jit_shutdown(void);
|
|
26
|
+
int luc_aot_build(const char *src,int srclen,const char *outpath);
|
|
27
|
+
int luc_aot_embedded(char **psrc,int *plen);
|
|
28
|
+
|
|
29
|
+
/* Value layout derived, never assumed. */
|
|
30
|
+
#define VSZ ((int)sizeof(Value))
|
|
31
|
+
#define VOFT ((int)offsetof(Value,t))
|
|
32
|
+
#define VOFU ((int)offsetof(Value,u))
|
|
33
|
+
typedef char luc_trans_value_assert[(sizeof(Value)==16 && offsetof(Value,u)==8)?1:-1];
|
|
34
|
+
|
|
35
|
+
#if defined(_WIN32)
|
|
36
|
+
|
|
37
|
+
#include <windows.h>
|
|
38
|
+
|
|
39
|
+
/* 1. byte buffer */
|
|
40
|
+
|
|
41
|
+
typedef struct { unsigned char *p; int n, cap, bad; } Buf;
|
|
42
|
+
|
|
43
|
+
static void buf_init(Buf *b,int cap){
|
|
44
|
+
b->p=(unsigned char*)malloc((size_t)cap);
|
|
45
|
+
b->n=0; b->cap=b->p?cap:0; b->bad=b->p?0:1;
|
|
46
|
+
}
|
|
47
|
+
static void buf_free(Buf *b){ free(b->p); b->p=NULL; b->n=b->cap=0; }
|
|
48
|
+
static int buf_need(Buf *b,int k){
|
|
49
|
+
if(b->bad) return 0;
|
|
50
|
+
if(b->n+k<=b->cap) return 1;
|
|
51
|
+
int nc=b->cap?b->cap:64;
|
|
52
|
+
while(nc<b->n+k){
|
|
53
|
+
if(nc>(1<<28)){ b->bad=1; return 0; }
|
|
54
|
+
nc*=2;
|
|
55
|
+
}
|
|
56
|
+
unsigned char *np=(unsigned char*)realloc(b->p,(size_t)nc);
|
|
57
|
+
if(!np){ b->bad=1; return 0; }
|
|
58
|
+
b->p=np; b->cap=nc; return 1;
|
|
59
|
+
}
|
|
60
|
+
static void e1(Buf *b,int v){ if(buf_need(b,1)) b->p[b->n++]=(unsigned char)v; }
|
|
61
|
+
static void e2(Buf *b,unsigned v){ if(buf_need(b,2)){ b->p[b->n++]=(unsigned char)(v&0xFF); b->p[b->n++]=(unsigned char)((v>>8)&0xFF);} }
|
|
62
|
+
static void e4(Buf *b,unsigned v){
|
|
63
|
+
if(buf_need(b,4)){ for(int i=0;i<4;i++) b->p[b->n++]=(unsigned char)((v>>(8*i))&0xFF); }
|
|
64
|
+
}
|
|
65
|
+
static void e8(Buf *b,unsigned long long v){
|
|
66
|
+
if(buf_need(b,8)){ for(int i=0;i<8;i++) b->p[b->n++]=(unsigned char)((v>>(8*i))&0xFF); }
|
|
67
|
+
}
|
|
68
|
+
static void eblob(Buf *b,const void *src,int len){
|
|
69
|
+
if(len>0 && buf_need(b,len)){ memcpy(b->p+b->n,src,(size_t)len); b->n+=len; }
|
|
70
|
+
}
|
|
71
|
+
static void patch32(Buf *b,int at,int val){
|
|
72
|
+
if(b->bad || at<0 || at+4>b->n) return;
|
|
73
|
+
unsigned u=(unsigned)val;
|
|
74
|
+
for(int i=0;i<4;i++) b->p[at+i]=(unsigned char)((u>>(8*i))&0xFF);
|
|
75
|
+
}
|
|
76
|
+
static void patch_rel(Buf *b,int at,int target){ patch32(b,at,target-(at+4)); }
|
|
77
|
+
|
|
78
|
+
/* 2. x86-64 emitter */
|
|
79
|
+
|
|
80
|
+
enum { R_RAX=0,R_RCX=1,R_RDX=2,R_RBX=3,R_RSP=4,R_RBP=5,R_RSI=6,R_RDI=7 };
|
|
81
|
+
/* condition codes */
|
|
82
|
+
enum { CC_B=2,CC_AE=3,CC_E=4,CC_NE=5,CC_BE=6,CC_A=7,CC_S=8,CC_P=0xA,CC_NP=0xB,
|
|
83
|
+
CC_L=0xC,CC_GE=0xD,CC_LE=0xE,CC_G=0xF };
|
|
84
|
+
|
|
85
|
+
/* modrm [rsp+disp32]: mod=10 rm=100+sib 0x24 */
|
|
86
|
+
static void mrm_rsp(Buf *b,int reg,int disp){
|
|
87
|
+
e1(b,0x80|((reg&7)<<3)|4); e1(b,0x24); e4(b,(unsigned)disp);
|
|
88
|
+
}
|
|
89
|
+
/* modrm [base+disp32], base=rbx/rsi/rcx/rdx/rax (not rsp/rbp) */
|
|
90
|
+
static void mrm_b(Buf *b,int reg,int base,int disp){
|
|
91
|
+
e1(b,0x80|((reg&7)<<3)|(base&7)); e4(b,(unsigned)disp);
|
|
92
|
+
}
|
|
93
|
+
/* modrm [rip+disp32]: mod=00 rm=101 */
|
|
94
|
+
static void mrm_rip(Buf *b,int reg,int disp){
|
|
95
|
+
e1(b,0x00|((reg&7)<<3)|5); e4(b,(unsigned)disp);
|
|
96
|
+
}
|
|
97
|
+
/* modrm reg,reg */
|
|
98
|
+
static void mrm_rr(Buf *b,int reg,int rm){ e1(b,0xC0|((reg&7)<<3)|(rm&7)); }
|
|
99
|
+
|
|
100
|
+
static void emit_push(Buf *b,int r){ if(r>=8) e1(b,0x41); e1(b,0x50+(r&7)); }
|
|
101
|
+
static void emit_pop (Buf *b,int r){ if(r>=8) e1(b,0x41); e1(b,0x58+(r&7)); }
|
|
102
|
+
static void emit_sub_rsp(Buf *b,int imm){ e1(b,0x48); e1(b,0x81); mrm_rr(b,5,R_RSP); e4(b,(unsigned)imm); }
|
|
103
|
+
static void emit_add_rsp(Buf *b,int imm){ e1(b,0x48); e1(b,0x81); mrm_rr(b,0,R_RSP); e4(b,(unsigned)imm); }
|
|
104
|
+
static void emit_mov_rr64(Buf *b,int dst,int src){ /* mov dst,src */
|
|
105
|
+
int rex=0x48|((src>=8)?4:0)|((dst>=8)?1:0);
|
|
106
|
+
e1(b,rex); e1(b,0x89); mrm_rr(b,src,dst);
|
|
107
|
+
}
|
|
108
|
+
static void emit_mov_rr32(Buf *b,int dst,int src){
|
|
109
|
+
int rex=((src>=8)?4:0)|((dst>=8)?1:0);
|
|
110
|
+
if(rex) e1(b,0x40|rex);
|
|
111
|
+
e1(b,0x89); mrm_rr(b,src,dst);
|
|
112
|
+
}
|
|
113
|
+
static void emit_mov_r64_imm(Buf *b,int r,unsigned long long v){
|
|
114
|
+
e1(b,0x48|((r>=8)?1:0)); e1(b,0xB8+(r&7)); e8(b,v);
|
|
115
|
+
}
|
|
116
|
+
static void emit_mov_r32_imm(Buf *b,int r,unsigned v){
|
|
117
|
+
if(r>=8) e1(b,0x41);
|
|
118
|
+
e1(b,0xB8+(r&7)); e4(b,v);
|
|
119
|
+
}
|
|
120
|
+
static void emit_xor_eax(Buf *b){ e1(b,0x31); mrm_rr(b,R_RAX,R_RAX); }
|
|
121
|
+
static void emit_ret(Buf *b){ e1(b,0xC3); }
|
|
122
|
+
|
|
123
|
+
/* [rsp+disp32] moves (frame homes+descs) */
|
|
124
|
+
static void emit_ld_r64_rsp(Buf *b,int r,int d){ e1(b,0x48|((r>=8)?4:0)); e1(b,0x8B); mrm_rsp(b,r,d); }
|
|
125
|
+
static void emit_st_r64_rsp(Buf *b,int r,int d){ e1(b,0x48|((r>=8)?4:0)); e1(b,0x89); mrm_rsp(b,r,d); }
|
|
126
|
+
static void emit_ld_r32_rsp(Buf *b,int r,int d){ if(r>=8) e1(b,0x44); e1(b,0x8B); mrm_rsp(b,r,d); }
|
|
127
|
+
static void emit_st_r32_rsp(Buf *b,int r,int d){ if(r>=8) e1(b,0x44); e1(b,0x89); mrm_rsp(b,r,d); }
|
|
128
|
+
static void emit_mov_m32_imm_rsp(Buf *b,int d,int imm){ e1(b,0xC7); mrm_rsp(b,0,d); e4(b,(unsigned)imm); }
|
|
129
|
+
static void emit_cmp_m32_imm_rsp(Buf *b,int d,int imm){ e1(b,0x81); mrm_rsp(b,7,d); e4(b,(unsigned)imm); }
|
|
130
|
+
|
|
131
|
+
/* mov rax,[base+d] (rbx/rsi table ptr load) */
|
|
132
|
+
static void emit_ld_rax_mb(Buf *b,int base,int d){ e1(b,0x48); e1(b,0x8B); mrm_b(b,R_RAX,base,d); }
|
|
133
|
+
/* movq r64<->xmmX (raw 8 bytes) */
|
|
134
|
+
static void emit_movq_r_x(Buf *b,int r,int x){
|
|
135
|
+
e1(b,0x66); e1(b,0x48|((r>=8)?1:0)|((x>=8)?4:0)); e1(b,0x0F); e1(b,0x7E); mrm_rr(b,x,r);
|
|
136
|
+
}
|
|
137
|
+
static void emit_movq_x_r(Buf *b,int x,int r){
|
|
138
|
+
e1(b,0x66); e1(b,0x48|((x>=8)?4:0)|((r>=8)?1:0)); e1(b,0x0F); e1(b,0x6E); mrm_rr(b,x,r);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* SSE: REX.R covers XMM8-15; no REX if all regs <8. */
|
|
142
|
+
static void sse_rsp(Buf *b,int pfx,int opc,int x,int d){
|
|
143
|
+
if(pfx) e1(b,pfx);
|
|
144
|
+
if(x>=8) e1(b,0x44);
|
|
145
|
+
e1(b,0x0F); e1(b,opc); mrm_rsp(b,x,d);
|
|
146
|
+
}
|
|
147
|
+
static void sse_mb(Buf *b,int pfx,int opc,int x,int base,int d){
|
|
148
|
+
if(pfx) e1(b,pfx);
|
|
149
|
+
if(x>=8) e1(b,0x44);
|
|
150
|
+
e1(b,0x0F); e1(b,opc); mrm_b(b,x,base,d);
|
|
151
|
+
}
|
|
152
|
+
static void sse_rip(Buf *b,int pfx,int opc,int x,int d){
|
|
153
|
+
if(pfx) e1(b,pfx);
|
|
154
|
+
if(x>=8) e1(b,0x44);
|
|
155
|
+
e1(b,0x0F); e1(b,opc); mrm_rip(b,x,d);
|
|
156
|
+
}
|
|
157
|
+
static void sse_rr(Buf *b,int pfx,int opc,int xd,int xs){
|
|
158
|
+
int rex=0x40|((xd>=8)?4:0)|((xs>=8)?1:0);
|
|
159
|
+
if(pfx) e1(b,pfx);
|
|
160
|
+
if(rex!=0x40) e1(b,rex);
|
|
161
|
+
e1(b,0x0F); e1(b,opc); mrm_rr(b,xd,xs);
|
|
162
|
+
}
|
|
163
|
+
#define SSE_MOVSD_LD 0x10
|
|
164
|
+
#define SSE_MOVSD_ST 0x11
|
|
165
|
+
#define SSE_MOVAPD 0x28
|
|
166
|
+
#define SSE_ADD 0x58
|
|
167
|
+
#define SSE_SUB 0x5C
|
|
168
|
+
#define SSE_MUL 0x59
|
|
169
|
+
#define SSE_DIV 0x5E
|
|
170
|
+
#define SSE_COMISD 0x2F
|
|
171
|
+
#define SSE_XORPD 0x57
|
|
172
|
+
|
|
173
|
+
static void emit_cvtsi2sd_x_eax(Buf *b,int x){ e1(b,0xF2); if(x>=8) e1(b,0x44); e1(b,0x0F); e1(b,0x2A); mrm_rr(b,x,R_RAX); }
|
|
174
|
+
/* cvtsi2sd xmmX,r32 */
|
|
175
|
+
static void emit_cvtsi2sd_x_r32(Buf *b,int x,int r){
|
|
176
|
+
e1(b,0xF2);
|
|
177
|
+
{ int rex=((x>=8)?4:0)|((r>=8)?1:0); if(rex) e1(b,0x40|rex); }
|
|
178
|
+
e1(b,0x0F); e1(b,0x2A); mrm_rr(b,x,r);
|
|
179
|
+
}
|
|
180
|
+
/* cvttsd2si r32,xmmX: OOR/NaN gives INT_MIN, rejected below. */
|
|
181
|
+
static void emit_cvttsd2si_r32_x(Buf *b,int r,int x){
|
|
182
|
+
e1(b,0xF2);
|
|
183
|
+
{ int rex=((r>=8)?4:0)|((x>=8)?1:0); if(rex) e1(b,0x40|rex); }
|
|
184
|
+
e1(b,0x0F); e1(b,0x2C); mrm_rr(b,r,x);
|
|
185
|
+
}
|
|
186
|
+
/* cvttsd2si r32,[rsp+d] */
|
|
187
|
+
static void emit_cvttsd2si_r32_rsp(Buf *b,int r,int d){
|
|
188
|
+
e1(b,0xF2); if(r>=8) e1(b,0x44); e1(b,0x0F); e1(b,0x2C); mrm_rsp(b,r,d);
|
|
189
|
+
}
|
|
190
|
+
/* movaps [rsp+d]<->xmmX (16B aligned XMM saves) */
|
|
191
|
+
static void emit_movaps_st_rsp(Buf *b,int x,int d){
|
|
192
|
+
if(x>=8) e1(b,0x44);
|
|
193
|
+
e1(b,0x0F); e1(b,0x29); mrm_rsp(b,x,d);
|
|
194
|
+
}
|
|
195
|
+
static void emit_movaps_ld_rsp(Buf *b,int x,int d){
|
|
196
|
+
if(x>=8) e1(b,0x44);
|
|
197
|
+
e1(b,0x0F); e1(b,0x28); mrm_rsp(b,x,d);
|
|
198
|
+
}
|
|
199
|
+
static void emit_setcc_al(Buf *b,int cc){ e1(b,0x0F); e1(b,0x90+cc); mrm_rr(b,0,R_RAX); }
|
|
200
|
+
static void emit_setcc_cl(Buf *b,int cc){ e1(b,0x0F); e1(b,0x90+cc); mrm_rr(b,0,R_RCX); }
|
|
201
|
+
static void emit_movzx_eax_al(Buf *b){ e1(b,0x0F); e1(b,0xB6); mrm_rr(b,R_RAX,R_RAX); }
|
|
202
|
+
static void emit_and_al_cl(Buf *b){ e1(b,0x20); mrm_rr(b,R_RCX,R_RAX); }
|
|
203
|
+
static void emit_or_al_cl (Buf *b){ e1(b,0x08); mrm_rr(b,R_RCX,R_RAX); }
|
|
204
|
+
|
|
205
|
+
static int emit_jcc(Buf *b,int cc){ e1(b,0x0F); e1(b,0x80+cc); int at=b->n; e4(b,0); return at; }
|
|
206
|
+
static int emit_jmp (Buf *b){ e1(b,0xE9); int at=b->n; e4(b,0); return at; }
|
|
207
|
+
|
|
208
|
+
/* cmp dword [base+d], imm32 */
|
|
209
|
+
static void emit_cmp_m32_imm(Buf *b,int base,int d,int imm){
|
|
210
|
+
e1(b,0x81); mrm_b(b,7,base,d); e4(b,(unsigned)imm);
|
|
211
|
+
}
|
|
212
|
+
/* cmp r32,imm32 (r8-r15 ok) */
|
|
213
|
+
static void emit_cmp_r32_imm(Buf *b,int r,int imm){
|
|
214
|
+
if(r>=8) e1(b,0x41);
|
|
215
|
+
e1(b,0x81); mrm_rr(b,7,r); e4(b,(unsigned)imm);
|
|
216
|
+
}
|
|
217
|
+
/* cmp r64,r64 */
|
|
218
|
+
static void emit_cmp_rr64(Buf *b,int a,int bb){
|
|
219
|
+
e1(b,0x48|((bb>=8)?4:0)|((a>=8)?1:0)); e1(b,0x39); mrm_rr(b,bb,a);
|
|
220
|
+
}
|
|
221
|
+
/* mov [rsi+d],imm32/eax */
|
|
222
|
+
static void emit_mov_m32_imm_rsi(Buf *b,int d,int imm){
|
|
223
|
+
e1(b,0xC7); mrm_b(b,0,R_RSI,d); e4(b,(unsigned)imm);
|
|
224
|
+
}
|
|
225
|
+
static void emit_mov_m32_eax_rsi(Buf *b,int d){ e1(b,0x89); mrm_b(b,R_RAX,R_RSI,d); }
|
|
226
|
+
|
|
227
|
+
/* table-indexing helpers */
|
|
228
|
+
#define TAB_ARR_OFF ((int)offsetof(Table,arr))
|
|
229
|
+
#define TAB_ALEN_OFF ((int)offsetof(Table,alen))
|
|
230
|
+
#define SIB_DX_CX 0x0A /* scale0 RCX index, RDX base */
|
|
231
|
+
static void emit_ld_r64_raxoff(Buf *b,int r,int off){ /* mov r64,[rax+off] */
|
|
232
|
+
e1(b,0x48|((r>=8)?4:0)); e1(b,0x8B); mrm_b(b,r,R_RAX,off);
|
|
233
|
+
}
|
|
234
|
+
static void emit_ld_r32_raxoff(Buf *b,int r,int off){ /* mov r32,[rax+off] */
|
|
235
|
+
if(r>=8) e1(b,0x44); e1(b,0x8B); mrm_b(b,r,R_RAX,off);
|
|
236
|
+
}
|
|
237
|
+
static void emit_cmp_ecx_r8d(Buf *b){ e1(b,0x44); e1(b,0x39); e1(b,0xC1); } /* cmp ecx,r8d */
|
|
238
|
+
static void emit_shl_rcx(Buf *b,int imm){ e1(b,0x48); e1(b,0xC1); e1(b,0xE1); e1(b,imm); }
|
|
239
|
+
static void emit_test_ecx(Buf *b){ e1(b,0x85); e1(b,0xC9); } /* test ecx,ecx */
|
|
240
|
+
static void emit_mov_ecx_r8d(Buf *b){ e1(b,0x41); e1(b,0x8B); e1(b,0xC8); } /* mov ecx,r8d */
|
|
241
|
+
static void emit_dec_ecx(Buf *b){ e1(b,0xFF); e1(b,0xC9); } /* dec ecx */
|
|
242
|
+
static void emit_ld_r32_sib(Buf *b,int r){ /* mov r32,[rdx+rcx] */
|
|
243
|
+
if(r>=8) e1(b,0x44); e1(b,0x8B); e1(b,((r&7)<<3)|4); e1(b,SIB_DX_CX);
|
|
244
|
+
}
|
|
245
|
+
static void emit_ld_r64_sib8(Buf *b,int r){ /* mov r64,[rdx+rcx+8] */
|
|
246
|
+
e1(b,0x48|((r>=8)?4:0)); e1(b,0x8B);
|
|
247
|
+
e1(b,0x40|((r&7)<<3)|4); e1(b,SIB_DX_CX); e1(b,VOFU);
|
|
248
|
+
}
|
|
249
|
+
static void emit_movsd_ld_sib8(Buf *b,int x){ /* movsd xmmX,[rdx+rcx+8] */
|
|
250
|
+
e1(b,0xF2); if(x>=8) e1(b,0x44); e1(b,0x0F); e1(b,0x10);
|
|
251
|
+
e1(b,0x40|((x&7)<<3)|4); e1(b,SIB_DX_CX); e1(b,VOFU);
|
|
252
|
+
}
|
|
253
|
+
static void emit_mov_m32_imm_sib(Buf *b,int imm){ /* mov dword [rdx+rcx],imm */
|
|
254
|
+
e1(b,0xC7); e1(b,0x04); e1(b,SIB_DX_CX); e4(b,(unsigned)imm);
|
|
255
|
+
}
|
|
256
|
+
static void emit_movsd_st_sib8(Buf *b,int x){ /* movsd [rdx+rcx+8],xmmX */
|
|
257
|
+
e1(b,0xF2); if(x>=8) e1(b,0x44); e1(b,0x0F); e1(b,0x11);
|
|
258
|
+
e1(b,0x40|((x&7)<<3)|4); e1(b,SIB_DX_CX); e1(b,VOFU);
|
|
259
|
+
}
|
|
260
|
+
static void emit_cmp_eax_imm32(Buf *b,int imm){ e1(b,0x3D); e4(b,(unsigned)imm); } /* cmp eax,imm32 */
|
|
261
|
+
static void emit_cmp_r8d_0(Buf *b){ e1(b,0x41); e1(b,0x83); e1(b,0xF8); e1(b,0); } /* cmp r8d,0 */
|
|
262
|
+
|
|
263
|
+
/* call absolute host addr; 32B shadow already reserved. */
|
|
264
|
+
static void emit_call_abs(Buf *b,void *fnp){
|
|
265
|
+
unsigned long long a; memcpy(&a,&fnp,sizeof a);
|
|
266
|
+
emit_mov_r64_imm(b,R_RAX,a);
|
|
267
|
+
e1(b,0xFF); mrm_rr(b,2,R_RAX); /* call rax */
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/* 3. MOD/POW helpers only */
|
|
271
|
+
|
|
272
|
+
/* interpreter-compatible mod: x-floor(x/y)*y */
|
|
273
|
+
static double jit_mod(double x,double y){ return x-floor(x/y)*y; }
|
|
274
|
+
static double jit_pow(double x,double y){ return pow(x,y); }
|
|
275
|
+
|
|
276
|
+
/* 4. kinds + analysis */
|
|
277
|
+
|
|
278
|
+
enum { K_NONE=0, K_INT, K_NUM, K_BOOL, K_NIL, K_LIST, K_BAD };
|
|
279
|
+
#define KISNUM(k) ((k)==K_INT||(k)==K_NUM)
|
|
280
|
+
|
|
281
|
+
/* EQ/NE modes */
|
|
282
|
+
enum { CM_NUM=0, CM_PTR, CM_FALSE, CM_TRUE };
|
|
283
|
+
|
|
284
|
+
typedef struct {
|
|
285
|
+
unsigned char kb,kc,ka,kr,vis,iguard,cmode;
|
|
286
|
+
} OpInfo;
|
|
287
|
+
|
|
288
|
+
typedef struct {
|
|
289
|
+
Proto *p;
|
|
290
|
+
int nregs, nslots;
|
|
291
|
+
int framesz, slotoff, dscoff, nsave, npin, nused;
|
|
292
|
+
int pic; /* 1=no absolute addrs (PIC) */
|
|
293
|
+
OpInfo *info;
|
|
294
|
+
unsigned char *istarget;
|
|
295
|
+
unsigned char **bs; /* block-entry kinds (targets only) */
|
|
296
|
+
unsigned char *pinit; /* param kinds (K_NUM/LIST) */
|
|
297
|
+
unsigned char *asbase,*asnum,*askey,*needdsc;
|
|
298
|
+
int *dsc, ndsc; /* desc index or -1 */
|
|
299
|
+
int *home; /* XMM home or -1 (frame) */
|
|
300
|
+
int savoff[16]; /* XMM save offset or -1 */
|
|
301
|
+
int *depth;
|
|
302
|
+
int *wl, nwl;
|
|
303
|
+
char err[160];
|
|
304
|
+
} Ana;
|
|
305
|
+
|
|
306
|
+
static int ana_fail(Ana *a,const char *what,int pc){
|
|
307
|
+
if(!a->err[0]) snprintf(a->err,sizeof a->err,"%s at pc %d",what,pc);
|
|
308
|
+
return 0;
|
|
309
|
+
}
|
|
310
|
+
/* kind lattice: NONE->INT/NUM/BOOL/NIL/LIST->NUM/BAD */
|
|
311
|
+
static int kmerge(unsigned char *dst,const unsigned char *src,int n){
|
|
312
|
+
int changed=0;
|
|
313
|
+
for(int i=0;i<n;i++){
|
|
314
|
+
unsigned char d=dst[i], s=src[i], r;
|
|
315
|
+
if(d==s) r=d;
|
|
316
|
+
else if(d==K_NONE) r=s;
|
|
317
|
+
else if(s==K_NONE) r=d;
|
|
318
|
+
else if(KISNUM(d)&&KISNUM(s)) r=K_NUM;
|
|
319
|
+
else r=K_BAD;
|
|
320
|
+
if(r!=d){ dst[i]=r; changed=1; }
|
|
321
|
+
}
|
|
322
|
+
return changed;
|
|
323
|
+
}
|
|
324
|
+
static void wl_push(Ana *a,int pc){
|
|
325
|
+
for(int i=0;i<a->nwl;i++) if(a->wl[i]==pc) return;
|
|
326
|
+
a->wl[a->nwl++]=pc;
|
|
327
|
+
}
|
|
328
|
+
static int succ_to(Ana *a,int pc,const unsigned char *cur){
|
|
329
|
+
if(pc<0 || pc>=a->p->ncode) return ana_fail(a,"branch out of range",pc);
|
|
330
|
+
if(!a->bs[pc]){
|
|
331
|
+
a->bs[pc]=(unsigned char*)malloc((size_t)a->nregs);
|
|
332
|
+
if(!a->bs[pc]) return ana_fail(a,"out of memory",pc);
|
|
333
|
+
memcpy(a->bs[pc],cur,(size_t)a->nregs);
|
|
334
|
+
wl_push(a,pc);
|
|
335
|
+
} else if(kmerge(a->bs[pc],cur,a->nregs)) wl_push(a,pc);
|
|
336
|
+
return 1;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
static int op_hasB(int op);
|
|
340
|
+
static int op_hasC(int op);
|
|
341
|
+
static int is_supported_op(int op){
|
|
342
|
+
switch(op){
|
|
343
|
+
case OP_MOVE: case OP_LOADK: case OP_LOADNIL: case OP_LOADBOOL:
|
|
344
|
+
case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_MOD:
|
|
345
|
+
case OP_POW: case OP_UNM: case OP_NOT:
|
|
346
|
+
case OP_EQ: case OP_NE: case OP_LT: case OP_LE: case OP_GT: case OP_GE:
|
|
347
|
+
case OP_GETTABLE: case OP_SETTABLE:
|
|
348
|
+
case OP_ADDEQ: case OP_SUBEQ: case OP_MULEQ: case OP_DIVEQ:
|
|
349
|
+
case OP_JMP: case OP_JMPIF: case OP_JMPIFNOT:
|
|
350
|
+
case OP_FORPREP: case OP_FORLOOP:
|
|
351
|
+
case OP_CLOSE: case OP_RETURN:
|
|
352
|
+
return 1;
|
|
353
|
+
default: return 0;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/* integral finite double: round-trips or INT_MIN (rejected). */
|
|
357
|
+
static int const_is_int(double d){
|
|
358
|
+
if(!(d==d)) return 0; /* NaN */
|
|
359
|
+
if(!(d>=-1.0e300 && d<=1.0e300)) return 0; /* +-Inf/OOR */
|
|
360
|
+
return d==floor(d);
|
|
361
|
+
}
|
|
362
|
+
static void mark_list(Ana *a,unsigned char *cur,int r){
|
|
363
|
+
cur[r]=K_LIST;
|
|
364
|
+
if(r<a->nregs) a->needdsc[r]=1;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/* walk straight-line region, update info[] */
|
|
368
|
+
static int ana_region(Ana *a,int startpc){
|
|
369
|
+
Proto *p=a->p;
|
|
370
|
+
int n=a->nregs;
|
|
371
|
+
unsigned char *cur=(unsigned char*)malloc((size_t)n);
|
|
372
|
+
if(!cur) return ana_fail(a,"out of memory",startpc);
|
|
373
|
+
memcpy(cur,a->bs[startpc],(size_t)n);
|
|
374
|
+
|
|
375
|
+
int pc=startpc, ok=1;
|
|
376
|
+
for(;;){
|
|
377
|
+
if(pc>=p->ncode){ ok=ana_fail(a,"fell off code",pc); break; }
|
|
378
|
+
if(pc!=startpc && a->istarget[pc]){ ok=succ_to(a,pc,cur); break; }
|
|
379
|
+
|
|
380
|
+
uint32_t ins=p->code[pc];
|
|
381
|
+
int op=GET_OP(ins), A=GET_A(ins), B=GET_B(ins), C=GET_C(ins);
|
|
382
|
+
OpInfo *in=&a->info[pc];
|
|
383
|
+
in->vis=1; in->kb=K_NONE; in->kc=K_NONE; in->ka=K_NONE;
|
|
384
|
+
in->iguard=0; in->cmode=CM_NUM;
|
|
385
|
+
|
|
386
|
+
if(!is_supported_op(op)){ ok=ana_fail(a,"unsupported opcode",pc); break; }
|
|
387
|
+
if(A>=n){ ok=ana_fail(a,"register out of frame",pc); break; }
|
|
388
|
+
if((op_hasB(op)&&B>=n)||(op_hasC(op)&&C>=n)){ ok=ana_fail(a,"register out of frame",pc); break; }
|
|
389
|
+
|
|
390
|
+
switch(op){
|
|
391
|
+
case OP_MOVE:
|
|
392
|
+
in->kb=cur[B];
|
|
393
|
+
if(cur[B]==K_NONE||cur[B]==K_BAD){ ok=ana_fail(a,"MOVE of untyped reg",pc); break; }
|
|
394
|
+
if(cur[B]==K_LIST) mark_list(a,cur,A);
|
|
395
|
+
else cur[A]=cur[B];
|
|
396
|
+
break;
|
|
397
|
+
case OP_LOADK: {
|
|
398
|
+
int bx=GET_Bx(ins);
|
|
399
|
+
if(bx<0||bx>=p->nk||p->k[bx].t!=LT_NUM){ ok=ana_fail(a,"non-numeric constant",pc); break; }
|
|
400
|
+
cur[A]=const_is_int(p->k[bx].u.n)?K_INT:K_NUM;
|
|
401
|
+
break; }
|
|
402
|
+
case OP_LOADNIL: cur[A]=K_NIL; break;
|
|
403
|
+
case OP_LOADBOOL:
|
|
404
|
+
/* LUC has no LOADBOOL skip; refuse it (keeps CFG sound). */
|
|
405
|
+
if(C!=0){ ok=ana_fail(a,"LOADBOOL with skip",pc); break; }
|
|
406
|
+
cur[A]=K_BOOL;
|
|
407
|
+
break;
|
|
408
|
+
case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV:
|
|
409
|
+
case OP_MOD: case OP_POW:
|
|
410
|
+
if((op==OP_MOD||op==OP_POW) && a->pic){ ok=ana_fail(a,"MOD/POW needs a runtime call",pc); break; }
|
|
411
|
+
if(!KISNUM(cur[B])||!KISNUM(cur[C])){ ok=ana_fail(a,"non-numeric arithmetic",pc); break; }
|
|
412
|
+
in->kb=cur[B]; in->kc=cur[C];
|
|
413
|
+
/* +,-,* closed on ints; overflow bails at index guard. */
|
|
414
|
+
if((op==OP_ADD||op==OP_SUB||op==OP_MUL)&&cur[B]==K_INT&&cur[C]==K_INT) cur[A]=K_INT;
|
|
415
|
+
else cur[A]=K_NUM;
|
|
416
|
+
break;
|
|
417
|
+
case OP_UNM:
|
|
418
|
+
if(!KISNUM(cur[B])){ ok=ana_fail(a,"non-numeric negation",pc); break; }
|
|
419
|
+
in->kb=cur[B]; cur[A]=(cur[B]==K_INT)?K_INT:K_NUM;
|
|
420
|
+
break;
|
|
421
|
+
case OP_NOT:
|
|
422
|
+
if(cur[B]==K_NONE||cur[B]==K_BAD){ ok=ana_fail(a,"'not' of untyped reg",pc); break; }
|
|
423
|
+
in->kb=cur[B]; cur[A]=K_BOOL;
|
|
424
|
+
break;
|
|
425
|
+
case OP_LT: case OP_LE: case OP_GT: case OP_GE:
|
|
426
|
+
if(!KISNUM(cur[B])||!KISNUM(cur[C])){ ok=ana_fail(a,"non-numeric comparison",pc); break; }
|
|
427
|
+
in->kb=cur[B]; in->kc=cur[C]; cur[A]=K_BOOL;
|
|
428
|
+
break;
|
|
429
|
+
case OP_EQ: case OP_NE: {
|
|
430
|
+
/* EQ/NE by kind class; guard-created so folds bail first. */
|
|
431
|
+
unsigned char kb=cur[B],kc=cur[C];
|
|
432
|
+
if(kb==K_NONE||kb==K_BAD||kc==K_NONE||kc==K_BAD){ ok=ana_fail(a,"comparison of untyped reg",pc); break; }
|
|
433
|
+
int cb = KISNUM(kb)?0 : kb==K_BOOL?1 : kb==K_NIL?2 : 3;
|
|
434
|
+
int cc2= KISNUM(kc)?0 : kc==K_BOOL?1 : kc==K_NIL?2 : 3;
|
|
435
|
+
if(cb!=cc2) in->cmode=CM_FALSE;
|
|
436
|
+
else if(cb==2) in->cmode=CM_TRUE;
|
|
437
|
+
else if(cb==3) in->cmode=CM_PTR;
|
|
438
|
+
else in->cmode=CM_NUM;
|
|
439
|
+
in->kb=kb; in->kc=kc; cur[A]=K_BOOL;
|
|
440
|
+
break; }
|
|
441
|
+
case OP_GETTABLE: {
|
|
442
|
+
if(cur[B]!=K_LIST||!KISNUM(cur[C])){ ok=ana_fail(a,"gettable needs list+int",pc); break; }
|
|
443
|
+
in->kb=K_LIST; in->kc=cur[C]; in->iguard=(cur[C]==K_INT);
|
|
444
|
+
if(in->kr!=K_NUM&&in->kr!=K_LIST){ ok=ana_fail(a,"ambiguous gettable result",pc); break; }
|
|
445
|
+
if(in->kr==K_LIST) mark_list(a,cur,A);
|
|
446
|
+
else cur[A]=K_NUM;
|
|
447
|
+
break; }
|
|
448
|
+
case OP_SETTABLE:
|
|
449
|
+
if(cur[A]!=K_LIST||!KISNUM(cur[B])||!KISNUM(cur[C])){ ok=ana_fail(a,"settable needs list+int+num",pc); break; }
|
|
450
|
+
in->ka=K_LIST; in->kb=cur[B]; in->kc=cur[C]; in->iguard=(cur[B]==K_INT);
|
|
451
|
+
break;
|
|
452
|
+
case OP_ADDEQ: case OP_SUBEQ: case OP_MULEQ: case OP_DIVEQ:
|
|
453
|
+
/* read-modify-write; base stays K_LIST */
|
|
454
|
+
if(cur[A]!=K_LIST||!KISNUM(cur[B])||!KISNUM(cur[C])){ ok=ana_fail(a,"compeq needs list+int+num",pc); break; }
|
|
455
|
+
a->needdsc[A]=1; /* A checked above */
|
|
456
|
+
in->ka=K_LIST; in->kb=cur[B]; in->kc=cur[C]; in->iguard=(cur[B]==K_INT);
|
|
457
|
+
break;
|
|
458
|
+
case OP_CLOSE: break; /* no upvalues in whitelisted proto */
|
|
459
|
+
case OP_JMP:
|
|
460
|
+
ok=succ_to(a,pc+1+GET_sBx(ins),cur);
|
|
461
|
+
goto region_done;
|
|
462
|
+
case OP_JMPIF: case OP_JMPIFNOT: {
|
|
463
|
+
if(cur[A]==K_NONE||cur[A]==K_BAD){ ok=ana_fail(a,"branch on untyped reg",pc); break; }
|
|
464
|
+
in->ka=cur[A];
|
|
465
|
+
int tgt=pc+1+GET_sBx(ins);
|
|
466
|
+
int truthy = KISNUM(cur[A])||cur[A]==K_LIST; /* 0.0 is truthy */
|
|
467
|
+
int falsy = (cur[A]==K_NIL);
|
|
468
|
+
int always = (truthy&&op==OP_JMPIF)||(falsy&&op==OP_JMPIFNOT);
|
|
469
|
+
int never = (truthy&&op==OP_JMPIFNOT)||(falsy&&op==OP_JMPIF);
|
|
470
|
+
if(always){ ok=succ_to(a,tgt,cur); goto region_done; }
|
|
471
|
+
if(!never){ if(!succ_to(a,tgt,cur)){ ok=0; goto region_done; } }
|
|
472
|
+
break; }
|
|
473
|
+
case OP_FORPREP: {
|
|
474
|
+
if(A+3>=n){ ok=ana_fail(a,"loop registers out of frame",pc); break; }
|
|
475
|
+
if(!KISNUM(cur[A])||!KISNUM(cur[A+1])){ ok=ana_fail(a,"non-numeric loop bounds",pc); break; }
|
|
476
|
+
/* integral if st/tg integral */
|
|
477
|
+
unsigned char kk=(cur[A]==K_INT&&cur[A+1]==K_INT)?K_INT:K_NUM;
|
|
478
|
+
cur[A]=cur[A+1]=cur[A+2]=cur[A+3]=kk;
|
|
479
|
+
ok=succ_to(a,pc+1+GET_sBx(ins),cur);
|
|
480
|
+
goto region_done; }
|
|
481
|
+
case OP_FORLOOP: {
|
|
482
|
+
if(A+3>=n){ ok=ana_fail(a,"loop registers out of frame",pc); break; }
|
|
483
|
+
if(!KISNUM(cur[A])||!KISNUM(cur[A+1])||!KISNUM(cur[A+2])){ ok=ana_fail(a,"loop state clobbered",pc); break; }
|
|
484
|
+
unsigned char kk=(cur[A]==K_INT&&cur[A+2]==K_INT)?K_INT:K_NUM;
|
|
485
|
+
cur[A]=kk; cur[A+3]=kk;
|
|
486
|
+
if(!succ_to(a,pc+1+GET_sBx(ins),cur)){ ok=0; goto region_done; }
|
|
487
|
+
break; }
|
|
488
|
+
case OP_RETURN: {
|
|
489
|
+
if(B==0){ ok=ana_fail(a,"variable-length return",pc); break; }
|
|
490
|
+
int nr=B-1;
|
|
491
|
+
if(nr>1){ ok=ana_fail(a,"multiple return values",pc); break; }
|
|
492
|
+
if(nr==1){
|
|
493
|
+
if(cur[A]==K_NONE||cur[A]==K_BAD){ ok=ana_fail(a,"return of untyped reg",pc); break; }
|
|
494
|
+
if(cur[A]==K_LIST){ ok=ana_fail(a,"cannot return a table",pc); break; }
|
|
495
|
+
in->ka=KISNUM(cur[A])?K_NUM:cur[A];
|
|
496
|
+
}
|
|
497
|
+
goto region_done; }
|
|
498
|
+
default: ok=ana_fail(a,"unsupported opcode",pc); break;
|
|
499
|
+
}
|
|
500
|
+
if(!ok) break;
|
|
501
|
+
pc++;
|
|
502
|
+
}
|
|
503
|
+
region_done:
|
|
504
|
+
free(cur);
|
|
505
|
+
return ok;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
static int ana_run(Ana *a){
|
|
509
|
+
Proto *p=a->p;
|
|
510
|
+
int nc=p->ncode;
|
|
511
|
+
for(int pc=0;pc<nc;pc++){
|
|
512
|
+
int op=GET_OP(p->code[pc]);
|
|
513
|
+
if(op==OP_JMP||op==OP_JMPIF||op==OP_JMPIFNOT||op==OP_FORPREP||op==OP_FORLOOP){
|
|
514
|
+
int t=pc+1+GET_sBx(p->code[pc]);
|
|
515
|
+
if(t<0||t>=nc) return ana_fail(a,"branch out of range",pc);
|
|
516
|
+
a->istarget[t]=1;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
a->bs[0]=(unsigned char*)calloc((size_t)a->nregs,1);
|
|
520
|
+
if(!a->bs[0]) return ana_fail(a,"out of memory",0);
|
|
521
|
+
for(int i=0;i<p->nparams && i<a->nregs;i++){
|
|
522
|
+
a->bs[0][i]=a->pinit[i];
|
|
523
|
+
if(a->pinit[i]==K_LIST) a->needdsc[i]=1;
|
|
524
|
+
}
|
|
525
|
+
a->istarget[0]=1;
|
|
526
|
+
a->nwl=0; wl_push(a,0);
|
|
527
|
+
|
|
528
|
+
int guard=0;
|
|
529
|
+
while(a->nwl>0){
|
|
530
|
+
if(++guard>200000) return ana_fail(a,"analysis did not converge",0);
|
|
531
|
+
int pc=a->wl[--a->nwl];
|
|
532
|
+
if(!ana_region(a,pc)) return 0;
|
|
533
|
+
}
|
|
534
|
+
return 1;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/* 5. codegen */
|
|
538
|
+
|
|
539
|
+
#define DSCSZ 24 /* arr(8) alen(4) stok(4)+pad */
|
|
540
|
+
|
|
541
|
+
typedef struct { int at, topc; } Fix;
|
|
542
|
+
|
|
543
|
+
typedef struct {
|
|
544
|
+
Buf b;
|
|
545
|
+
Ana *a;
|
|
546
|
+
int *pcoff;
|
|
547
|
+
Fix *fix; int nfix, fixcap;
|
|
548
|
+
int *bailfix, nbail, bailcap;
|
|
549
|
+
int *retfix, nret, retcap;
|
|
550
|
+
int kcv, kcr; /* EDI caches validated int key */
|
|
551
|
+
} Gen;
|
|
552
|
+
|
|
553
|
+
static int gen_addfix(Gen *g,int at,int topc){
|
|
554
|
+
if(g->nfix==g->fixcap){
|
|
555
|
+
int nc=g->fixcap?g->fixcap*2:64;
|
|
556
|
+
Fix *nf=(Fix*)realloc(g->fix,sizeof(Fix)*(size_t)nc);
|
|
557
|
+
if(!nf) return 0;
|
|
558
|
+
g->fix=nf; g->fixcap=nc;
|
|
559
|
+
}
|
|
560
|
+
g->fix[g->nfix].at=at; g->fix[g->nfix].topc=topc; g->nfix++;
|
|
561
|
+
return 1;
|
|
562
|
+
}
|
|
563
|
+
static int gen_addlist(int **arr,int *n,int *cap,int at){
|
|
564
|
+
if(*n==*cap){
|
|
565
|
+
int nc=*cap?*cap*2:64;
|
|
566
|
+
int *na=(int*)realloc(*arr,sizeof(int)*(size_t)nc);
|
|
567
|
+
if(!na) return 0;
|
|
568
|
+
*arr=na; *cap=nc;
|
|
569
|
+
}
|
|
570
|
+
(*arr)[(*n)++]=at; return 1;
|
|
571
|
+
}
|
|
572
|
+
#define SLOTO(g,i) ((g)->a->slotoff+8*(i))
|
|
573
|
+
#define DSCO(g,r) ((g)->a->dscoff+DSCSZ*(g)->a->dsc[r])
|
|
574
|
+
#define BAIL() do{ int _at=emit_jmp(&g->b); if(!gen_addlist(&g->bailfix,&g->nbail,&g->bailcap,_at)) return 0; }while(0)
|
|
575
|
+
#define BAILCC(cc) do{ int _at=emit_jcc(&g->b,(cc)); if(!gen_addlist(&g->bailfix,&g->nbail,&g->bailcap,_at)) return 0; }while(0)
|
|
576
|
+
#define RETJMP() do{ int _at=emit_jmp(&g->b); if(!gen_addlist(&g->retfix,&g->nret,&g->retcap,_at)) return 0; }while(0)
|
|
577
|
+
#define JUMPTO(pc) do{ int _at=emit_jmp(&g->b); if(!gen_addfix(g,_at,(pc))) return 0; }while(0)
|
|
578
|
+
#define JCCTO(cc,pc) do{ int _at=emit_jcc(&g->b,(cc)); if(!gen_addfix(g,_at,(pc))) return 0; }while(0)
|
|
579
|
+
|
|
580
|
+
/* cmp r32,[rsp+d] */
|
|
581
|
+
static void emit_cmp_r32_rsp(Buf *b,int r,int d){
|
|
582
|
+
if(r>=8) e1(b,0x44); e1(b,0x3B); mrm_rsp(b,r,d);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/* one home per slot: XMM(home[s]) or frame slot */
|
|
586
|
+
static void g_ldx(Gen *g,int x,int s){
|
|
587
|
+
int h=g->a->home[s];
|
|
588
|
+
if(h>=0) sse_rr(&g->b,0x66,SSE_MOVAPD,x,h);
|
|
589
|
+
else sse_rsp(&g->b,0xF2,SSE_MOVSD_LD,x,SLOTO(g,s));
|
|
590
|
+
}
|
|
591
|
+
static void g_stx(Gen *g,int s,int x){
|
|
592
|
+
int h=g->a->home[s];
|
|
593
|
+
if(h>=0) sse_rr(&g->b,0x66,SSE_MOVAPD,h,x);
|
|
594
|
+
else sse_rsp(&g->b,0xF2,SSE_MOVSD_ST,x,SLOTO(g,s));
|
|
595
|
+
}
|
|
596
|
+
static void g_op0(Gen *g,int sop,int s){
|
|
597
|
+
int h=g->a->home[s];
|
|
598
|
+
if(h>=0) sse_rr(&g->b,0xF2,sop,0,h);
|
|
599
|
+
else sse_rsp(&g->b,0xF2,sop,0,SLOTO(g,s));
|
|
600
|
+
}
|
|
601
|
+
static void g_cmp0(Gen *g,int s){
|
|
602
|
+
int h=g->a->home[s];
|
|
603
|
+
if(h>=0) sse_rr(&g->b,0x66,SSE_COMISD,0,h);
|
|
604
|
+
else sse_rsp(&g->b,0x66,SSE_COMISD,0,SLOTO(g,s));
|
|
605
|
+
}
|
|
606
|
+
static void g_ldptr(Gen *g,int s){ /* RAX=raw bits */
|
|
607
|
+
int h=g->a->home[s];
|
|
608
|
+
if(h>=0) emit_movq_r_x(&g->b,R_RAX,h);
|
|
609
|
+
else emit_ld_r64_rsp(&g->b,R_RAX,SLOTO(g,s));
|
|
610
|
+
}
|
|
611
|
+
static void g_stptr(Gen *g,int s){ /* slot=RAX raw bits */
|
|
612
|
+
int h=g->a->home[s];
|
|
613
|
+
if(h>=0) emit_movq_x_r(&g->b,h,R_RAX);
|
|
614
|
+
else emit_st_r64_rsp(&g->b,R_RAX,SLOTO(g,s));
|
|
615
|
+
}
|
|
616
|
+
static void g_const(Gen *g,int s,double d){
|
|
617
|
+
unsigned long long bits; memcpy(&bits,&d,sizeof bits);
|
|
618
|
+
emit_mov_r64_imm(&g->b,R_RAX,bits);
|
|
619
|
+
g_stptr(g,s);
|
|
620
|
+
}
|
|
621
|
+
static void g_kill(Gen *g,int r){ if(g->kcv&&g->kcr==r) g->kcv=0; }
|
|
622
|
+
|
|
623
|
+
/* Build list desc from Table* in RAX; safe to cache (no alloc/call). */
|
|
624
|
+
static void g_dscbuild(Gen *g,int r){
|
|
625
|
+
Buf *b=&g->b; int d=DSCO(g,r);
|
|
626
|
+
emit_ld_r64_raxoff(b,R_RDX,TAB_ARR_OFF);
|
|
627
|
+
emit_ld_r32_raxoff(b,8,TAB_ALEN_OFF);
|
|
628
|
+
emit_st_r64_rsp(b,R_RDX,d);
|
|
629
|
+
emit_st_r32_rsp(b,8,d+8);
|
|
630
|
+
emit_mov_m32_imm_rsp(b,d+12,0);
|
|
631
|
+
emit_cmp_r8d_0(b);
|
|
632
|
+
int j1=emit_jcc(b,CC_LE);
|
|
633
|
+
emit_mov_ecx_r8d(b); emit_dec_ecx(b); emit_shl_rcx(b,4);
|
|
634
|
+
emit_ld_r32_sib(b,R_RAX); /* EAX=arr[i].t */
|
|
635
|
+
emit_cmp_eax_imm32(b,LT_NIL);
|
|
636
|
+
int j2=emit_jcc(b,CC_E);
|
|
637
|
+
emit_mov_m32_imm_rsp(b,d+12,1);
|
|
638
|
+
patch_rel(b,j1,b->n); patch_rel(b,j2,b->n);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/* ECX=guarded int key; EDI caches it across read/modify/write. */
|
|
642
|
+
static int g_keyint(Gen *g,int s,int iguard){
|
|
643
|
+
Buf *b=&g->b;
|
|
644
|
+
if(g->kcv && g->kcr==s){ emit_mov_rr32(b,R_RCX,R_RDI); return 1; }
|
|
645
|
+
int h=g->a->home[s], xk;
|
|
646
|
+
if(h>=0) xk=h; else { g_ldx(g,0,s); xk=0; }
|
|
647
|
+
emit_cvttsd2si_r32_x(b,R_RCX,xk);
|
|
648
|
+
if(!iguard){ /* K_INT proves exactness */
|
|
649
|
+
emit_cvtsi2sd_x_r32(b,1,R_RCX);
|
|
650
|
+
sse_rr(b,0x66,SSE_COMISD,xk,1);
|
|
651
|
+
BAILCC(CC_P); BAILCC(CC_NE);
|
|
652
|
+
}
|
|
653
|
+
emit_test_ecx(b); BAILCC(CC_S); /* negative bails */
|
|
654
|
+
emit_mov_rr32(b,R_RDI,R_RCX);
|
|
655
|
+
g->kcv=1; g->kcr=s;
|
|
656
|
+
return 1;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/* dest=base[key] via hoisted desc; per-elem key/bounds/type checks. */
|
|
660
|
+
static int gen_gettable(Gen *g,int A,int B,int C,int kr,int iguard){
|
|
661
|
+
Buf *b=&g->b; Ana *a=g->a;
|
|
662
|
+
if(a->dsc[B]<0) return 0;
|
|
663
|
+
int d=DSCO(g,B);
|
|
664
|
+
if(!g_keyint(g,C,iguard)) return 0;
|
|
665
|
+
emit_cmp_r32_rsp(b,R_RCX,d+8); BAILCC(CC_AE);
|
|
666
|
+
emit_ld_r64_rsp(b,R_RDX,d);
|
|
667
|
+
emit_shl_rcx(b,4);
|
|
668
|
+
emit_ld_r32_sib(b,R_RAX); /* EAX=arr[i].t */
|
|
669
|
+
if(kr==K_NUM){
|
|
670
|
+
emit_cmp_eax_imm32(b,LT_NUM); BAILCC(CC_NE);
|
|
671
|
+
emit_movsd_ld_sib8(b,0);
|
|
672
|
+
g_stx(g,A,0);
|
|
673
|
+
} else {
|
|
674
|
+
if(a->dsc[A]<0) return 0;
|
|
675
|
+
emit_cmp_eax_imm32(b,LT_LIST); BAILCC(CC_NE);
|
|
676
|
+
emit_ld_r64_sib8(b,R_RAX); /* payload at +8, not +0 */
|
|
677
|
+
g_stptr(g,A);
|
|
678
|
+
g_dscbuild(g,A);
|
|
679
|
+
}
|
|
680
|
+
return !b->bad;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/* base[key]=num; list/arr/alen/tail guards hoisted to preheader. */
|
|
684
|
+
static int gen_settable(Gen *g,int A,int B,int C,int iguard){
|
|
685
|
+
Buf *b=&g->b; Ana *a=g->a;
|
|
686
|
+
if(a->dsc[A]<0) return 0;
|
|
687
|
+
int d=DSCO(g,A);
|
|
688
|
+
emit_cmp_m32_imm_rsp(b,d+12,0); BAILCC(CC_E); /* stok */
|
|
689
|
+
if(!g_keyint(g,B,iguard)) return 0;
|
|
690
|
+
emit_cmp_r32_rsp(b,R_RCX,d+8); BAILCC(CC_AE);
|
|
691
|
+
emit_ld_r64_rsp(b,R_RDX,d);
|
|
692
|
+
emit_shl_rcx(b,4);
|
|
693
|
+
emit_mov_m32_imm_sib(b,LT_NUM);
|
|
694
|
+
g_ldx(g,0,C);
|
|
695
|
+
emit_movsd_st_sib8(b,0);
|
|
696
|
+
return !b->bad;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/* base[key]<op>=num: one index for read+write, pure XMM op. */
|
|
700
|
+
static int gen_compeq(Gen *g,int A,int B,int C,int sop,int iguard){
|
|
701
|
+
Buf *b=&g->b; Ana *a=g->a;
|
|
702
|
+
if(a->dsc[A]<0) return 0;
|
|
703
|
+
int d=DSCO(g,A);
|
|
704
|
+
emit_cmp_m32_imm_rsp(b,d+12,0); BAILCC(CC_E); /* stok */
|
|
705
|
+
if(!g_keyint(g,B,iguard)) return 0;
|
|
706
|
+
emit_cmp_r32_rsp(b,R_RCX,d+8); BAILCC(CC_AE);
|
|
707
|
+
emit_ld_r64_rsp(b,R_RDX,d);
|
|
708
|
+
emit_shl_rcx(b,4);
|
|
709
|
+
emit_ld_r32_sib(b,R_RAX); /* EAX=arr[i].t */
|
|
710
|
+
emit_cmp_eax_imm32(b,LT_NUM); BAILCC(CC_NE);
|
|
711
|
+
emit_movsd_ld_sib8(b,0);
|
|
712
|
+
g_ldx(g,1,C);
|
|
713
|
+
sse_rr(b,0xF2,sop,0,1);
|
|
714
|
+
emit_mov_m32_imm_sib(b,LT_NUM);
|
|
715
|
+
emit_movsd_st_sib8(b,0);
|
|
716
|
+
return !b->bad;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
static void emit_neg_x0(Gen *g){
|
|
720
|
+
emit_mov_r64_imm(&g->b,R_RAX,0x8000000000000000ULL);
|
|
721
|
+
emit_movq_x_r(&g->b,1,R_RAX);
|
|
722
|
+
sse_rr(&g->b,0x66,SSE_XORPD,0,1);
|
|
723
|
+
}
|
|
724
|
+
/* xmm0:=bool(cc), stored to slot */
|
|
725
|
+
static void emit_setbool(Gen *g,int cc,int slot){
|
|
726
|
+
emit_setcc_al(&g->b,cc);
|
|
727
|
+
emit_movzx_eax_al(&g->b);
|
|
728
|
+
emit_cvtsi2sd_x_eax(&g->b,0);
|
|
729
|
+
g_stx(g,slot,0);
|
|
730
|
+
}
|
|
731
|
+
static void emit_stack_probe(Buf *b,int framesz){
|
|
732
|
+
if(framesz<4096) return;
|
|
733
|
+
for(int off=4096;off<framesz;off+=4096) emit_mov_m32_imm_rsp(b,-off,0);
|
|
734
|
+
emit_mov_m32_imm_rsp(b,-(framesz-8),0);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
static int gen_prologue(Gen *g){
|
|
738
|
+
Buf *b=&g->b;
|
|
739
|
+
Ana *a=g->a;
|
|
740
|
+
emit_push(b,R_RBP); emit_push(b,R_RBX); emit_push(b,R_RSI); emit_push(b,R_RDI);
|
|
741
|
+
emit_push(b,12); emit_push(b,13); emit_push(b,14); emit_push(b,15);
|
|
742
|
+
emit_mov_rr64(b,R_RBX,R_RCX); /* args */
|
|
743
|
+
emit_mov_rr64(b,R_RSI,8); /* out (R8) */
|
|
744
|
+
emit_mov_rr32(b,12,R_RDX); /* nargs -> r12d */
|
|
745
|
+
emit_stack_probe(b,a->framesz);
|
|
746
|
+
emit_sub_rsp(b,a->framesz);
|
|
747
|
+
for(int x=6;x<16;x++) if(a->savoff[x]>=0) emit_movaps_st_rsp(b,x,a->savoff[x]);
|
|
748
|
+
|
|
749
|
+
/* zero homes+descs (unreachable reads stay defined) */
|
|
750
|
+
sse_rr(b,0x66,SSE_XORPD,0,0);
|
|
751
|
+
for(int i=0;i<a->nslots;i++){
|
|
752
|
+
if(a->home[i]>=0) sse_rr(b,0x66,SSE_XORPD,a->home[i],a->home[i]);
|
|
753
|
+
else sse_rsp(b,0xF2,SSE_MOVSD_ST,0,SLOTO(g,i));
|
|
754
|
+
}
|
|
755
|
+
if(a->ndsc>0){
|
|
756
|
+
emit_mov_r64_imm(b,R_RAX,0);
|
|
757
|
+
for(int i=0;i<a->ndsc;i++){
|
|
758
|
+
int d=a->dscoff+DSCSZ*i;
|
|
759
|
+
emit_st_r64_rsp(b,R_RAX,d);
|
|
760
|
+
emit_st_r64_rsp(b,R_RAX,d+8);
|
|
761
|
+
emit_st_r64_rsp(b,R_RAX,d+16);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
/* param guards: nargs and arg types match pinit */
|
|
765
|
+
for(int i=0;i<a->p->nparams;i++){
|
|
766
|
+
emit_cmp_r32_imm(b,12,i);
|
|
767
|
+
BAILCC(CC_BE); /* nargs<=i */
|
|
768
|
+
if(a->pinit[i]==K_LIST){
|
|
769
|
+
emit_cmp_m32_imm(b,R_RBX,i*VSZ+VOFT,LT_LIST);
|
|
770
|
+
BAILCC(CC_NE);
|
|
771
|
+
emit_ld_rax_mb(b,R_RBX,i*VSZ+VOFU);
|
|
772
|
+
g_stptr(g,i);
|
|
773
|
+
if(a->dsc[i]<0) return 0;
|
|
774
|
+
g_dscbuild(g,i);
|
|
775
|
+
} else {
|
|
776
|
+
emit_cmp_m32_imm(b,R_RBX,i*VSZ+VOFT,LT_NUM);
|
|
777
|
+
BAILCC(CC_NE);
|
|
778
|
+
sse_mb(b,0xF2,SSE_MOVSD_LD,0,R_RBX,i*VSZ+VOFU);
|
|
779
|
+
g_stx(g,i,0);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
return !b->bad;
|
|
783
|
+
}
|
|
784
|
+
static void gen_epilogue(Gen *g){
|
|
785
|
+
Buf *b=&g->b;
|
|
786
|
+
Ana *a=g->a;
|
|
787
|
+
for(int x=6;x<16;x++) if(a->savoff[x]>=0) emit_movaps_ld_rsp(b,x,a->savoff[x]);
|
|
788
|
+
emit_add_rsp(b,a->framesz);
|
|
789
|
+
emit_pop(b,15); emit_pop(b,14); emit_pop(b,13); emit_pop(b,12);
|
|
790
|
+
emit_pop(b,R_RDI); emit_pop(b,R_RSI); emit_pop(b,R_RBX); emit_pop(b,R_RBP);
|
|
791
|
+
emit_ret(b);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
static int gen_body(Gen *g){
|
|
795
|
+
Ana *a=g->a;
|
|
796
|
+
Proto *p=a->p;
|
|
797
|
+
Buf *b=&g->b;
|
|
798
|
+
|
|
799
|
+
for(int pc=0;pc<p->ncode;pc++){
|
|
800
|
+
g->pcoff[pc]=b->n;
|
|
801
|
+
OpInfo *in=&a->info[pc];
|
|
802
|
+
if(a->istarget[pc]) g->kcv=0; /* joins drop key cache */
|
|
803
|
+
if(!in->vis) continue; /* unreachable */
|
|
804
|
+
|
|
805
|
+
uint32_t ins=p->code[pc];
|
|
806
|
+
int op=GET_OP(ins), A=GET_A(ins), B=GET_B(ins), C=GET_C(ins);
|
|
807
|
+
|
|
808
|
+
switch(op){
|
|
809
|
+
case OP_MOVE:
|
|
810
|
+
g_kill(g,A);
|
|
811
|
+
if(in->kb==K_NIL) break; /* nil has no bits */
|
|
812
|
+
if(in->kb==K_LIST){
|
|
813
|
+
if(a->dsc[A]<0||a->dsc[B]<0) return 0;
|
|
814
|
+
g_ldptr(g,B); g_stptr(g,A);
|
|
815
|
+
emit_ld_r64_rsp(b,R_RAX,DSCO(g,B)); /* copy arr */
|
|
816
|
+
emit_st_r64_rsp(b,R_RAX,DSCO(g,A));
|
|
817
|
+
emit_ld_r64_rsp(b,R_RAX,DSCO(g,B)+8); /* alen+stok */
|
|
818
|
+
emit_st_r64_rsp(b,R_RAX,DSCO(g,A)+8);
|
|
819
|
+
} else if(a->home[A]>=0||a->home[B]>=0){
|
|
820
|
+
g_ldx(g,0,B); g_stx(g,A,0);
|
|
821
|
+
} else {
|
|
822
|
+
emit_ld_r64_rsp(b,R_RAX,SLOTO(g,B));
|
|
823
|
+
emit_st_r64_rsp(b,R_RAX,SLOTO(g,A));
|
|
824
|
+
}
|
|
825
|
+
break;
|
|
826
|
+
case OP_LOADK:
|
|
827
|
+
g_kill(g,A);
|
|
828
|
+
g_const(g,A,p->k[GET_Bx(ins)].u.n);
|
|
829
|
+
break;
|
|
830
|
+
case OP_LOADNIL:
|
|
831
|
+
g_kill(g,A);
|
|
832
|
+
break;
|
|
833
|
+
case OP_LOADBOOL:
|
|
834
|
+
g_kill(g,A);
|
|
835
|
+
g_const(g,A,B?1.0:0.0);
|
|
836
|
+
break;
|
|
837
|
+
case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: {
|
|
838
|
+
int sop = op==OP_ADD?SSE_ADD : op==OP_SUB?SSE_SUB :
|
|
839
|
+
op==OP_MUL?SSE_MUL : SSE_DIV;
|
|
840
|
+
g_ldx(g,0,B);
|
|
841
|
+
g_op0(g,sop,C);
|
|
842
|
+
g_kill(g,A);
|
|
843
|
+
g_stx(g,A,0);
|
|
844
|
+
break; }
|
|
845
|
+
case OP_MOD: case OP_POW:
|
|
846
|
+
g_ldx(g,0,B);
|
|
847
|
+
g_ldx(g,1,C);
|
|
848
|
+
emit_call_abs(b, op==OP_MOD? (void*)jit_mod : (void*)jit_pow);
|
|
849
|
+
g_kill(g,A);
|
|
850
|
+
g_stx(g,A,0);
|
|
851
|
+
break;
|
|
852
|
+
case OP_UNM:
|
|
853
|
+
g_ldx(g,0,B);
|
|
854
|
+
emit_neg_x0(g);
|
|
855
|
+
g_kill(g,A);
|
|
856
|
+
g_stx(g,A,0);
|
|
857
|
+
break;
|
|
858
|
+
case OP_NOT:
|
|
859
|
+
g_kill(g,A);
|
|
860
|
+
if(in->kb==K_NIL) g_const(g,A,1.0);
|
|
861
|
+
else if(in->kb!=K_BOOL) g_const(g,A,0.0); /* num/list truthy */
|
|
862
|
+
else {
|
|
863
|
+
g_ldx(g,0,B);
|
|
864
|
+
sse_rr(b,0x66,SSE_XORPD,1,1);
|
|
865
|
+
sse_rr(b,0x66,SSE_COMISD,0,1);
|
|
866
|
+
emit_setbool(g,CC_E,A);
|
|
867
|
+
}
|
|
868
|
+
break;
|
|
869
|
+
/* COMISD unordered; swapped LT/LE false for NaN. */
|
|
870
|
+
case OP_LT:
|
|
871
|
+
g_ldx(g,0,C); g_cmp0(g,B); g_kill(g,A); emit_setbool(g,CC_A,A);
|
|
872
|
+
break;
|
|
873
|
+
case OP_LE:
|
|
874
|
+
g_ldx(g,0,C); g_cmp0(g,B); g_kill(g,A); emit_setbool(g,CC_AE,A);
|
|
875
|
+
break;
|
|
876
|
+
case OP_GT:
|
|
877
|
+
g_ldx(g,0,B); g_cmp0(g,C); g_kill(g,A); emit_setbool(g,CC_A,A);
|
|
878
|
+
break;
|
|
879
|
+
case OP_GE:
|
|
880
|
+
g_ldx(g,0,B); g_cmp0(g,C); g_kill(g,A); emit_setbool(g,CC_AE,A);
|
|
881
|
+
break;
|
|
882
|
+
case OP_EQ: case OP_NE: {
|
|
883
|
+
int iseq=(op==OP_EQ);
|
|
884
|
+
if(in->cmode==CM_TRUE||in->cmode==CM_FALSE){
|
|
885
|
+
int t=(in->cmode==CM_TRUE)?1:0;
|
|
886
|
+
g_kill(g,A);
|
|
887
|
+
g_const(g,A,(iseq?t:!t)?1.0:0.0);
|
|
888
|
+
break;
|
|
889
|
+
}
|
|
890
|
+
if(in->cmode==CM_PTR){
|
|
891
|
+
g_ldptr(g,B);
|
|
892
|
+
emit_mov_rr64(b,R_RCX,R_RAX);
|
|
893
|
+
g_ldptr(g,C);
|
|
894
|
+
emit_cmp_rr64(b,R_RCX,R_RAX);
|
|
895
|
+
g->kcv=0; /* RCX clobbered */
|
|
896
|
+
g_kill(g,A);
|
|
897
|
+
emit_setcc_al(b,iseq?CC_E:CC_NE);
|
|
898
|
+
emit_movzx_eax_al(b);
|
|
899
|
+
emit_cvtsi2sd_x_eax(b,0);
|
|
900
|
+
g_stx(g,A,0);
|
|
901
|
+
break;
|
|
902
|
+
}
|
|
903
|
+
/* numbers use ==, so fold parity for NaN. */
|
|
904
|
+
g_ldx(g,0,B);
|
|
905
|
+
g_cmp0(g,C);
|
|
906
|
+
g_kill(g,A);
|
|
907
|
+
if(iseq){ emit_setcc_al(b,CC_E); emit_setcc_cl(b,CC_NP); emit_and_al_cl(b); }
|
|
908
|
+
else { emit_setcc_al(b,CC_NE); emit_setcc_cl(b,CC_P); emit_or_al_cl(b); }
|
|
909
|
+
emit_movzx_eax_al(b);
|
|
910
|
+
emit_cvtsi2sd_x_eax(b,0);
|
|
911
|
+
g_stx(g,A,0);
|
|
912
|
+
break; }
|
|
913
|
+
case OP_GETTABLE:
|
|
914
|
+
g_kill(g,A);
|
|
915
|
+
if(!gen_gettable(g,A,B,C,in->kr,in->iguard)) return 0;
|
|
916
|
+
break;
|
|
917
|
+
case OP_SETTABLE:
|
|
918
|
+
if(!gen_settable(g,A,B,C,in->iguard)) return 0;
|
|
919
|
+
break;
|
|
920
|
+
case OP_ADDEQ: case OP_SUBEQ: case OP_MULEQ: case OP_DIVEQ: {
|
|
921
|
+
int sop = op==OP_ADDEQ?SSE_ADD : op==OP_SUBEQ?SSE_SUB :
|
|
922
|
+
op==OP_MULEQ?SSE_MUL : SSE_DIV;
|
|
923
|
+
if(!gen_compeq(g,A,B,C,sop,in->iguard)) return 0;
|
|
924
|
+
break; }
|
|
925
|
+
case OP_CLOSE:
|
|
926
|
+
break;
|
|
927
|
+
case OP_JMP:
|
|
928
|
+
JUMPTO(pc+1+GET_sBx(ins));
|
|
929
|
+
break;
|
|
930
|
+
case OP_JMPIF: case OP_JMPIFNOT: {
|
|
931
|
+
int tgt=pc+1+GET_sBx(ins);
|
|
932
|
+
if(in->ka==K_NIL){ if(op==OP_JMPIFNOT) JUMPTO(tgt); break; }
|
|
933
|
+
if(in->ka!=K_BOOL){ if(op==OP_JMPIF) JUMPTO(tgt); break; }
|
|
934
|
+
g_ldx(g,0,A);
|
|
935
|
+
sse_rr(b,0x66,SSE_XORPD,1,1);
|
|
936
|
+
sse_rr(b,0x66,SSE_COMISD,0,1);
|
|
937
|
+
JCCTO(op==OP_JMPIF?CC_NE:CC_E,tgt);
|
|
938
|
+
break; }
|
|
939
|
+
case OP_FORPREP: {
|
|
940
|
+
/* FORPREP normalizes loop; step 0/NaN bails. */
|
|
941
|
+
g_ldx(g,4,A); /* xmm4=st */
|
|
942
|
+
sse_rr(b,0x66,SSE_XORPD,1,1);
|
|
943
|
+
sse_rr(b,0x66,SSE_COMISD,4,1);
|
|
944
|
+
BAILCC(CC_P);
|
|
945
|
+
BAILCC(CC_E);
|
|
946
|
+
int jpos=emit_jcc(b,CC_A);
|
|
947
|
+
g_ldx(g,2,A+1); /* tg */
|
|
948
|
+
sse_rr(b,0xF2,SSE_SUB,2,4); /* tg-st */
|
|
949
|
+
g_stx(g,A,2);
|
|
950
|
+
sse_rr(b,0x66,SSE_XORPD,1,1);
|
|
951
|
+
g_stx(g,A+1,1);
|
|
952
|
+
int jend=emit_jmp(b);
|
|
953
|
+
patch_rel(b,jpos,b->n);
|
|
954
|
+
sse_rr(b,0xF2,SSE_MOVSD_LD,0,4);
|
|
955
|
+
emit_neg_x0(g);
|
|
956
|
+
g_stx(g,A,0); /* A=-st */
|
|
957
|
+
patch_rel(b,jend,b->n);
|
|
958
|
+
g_stx(g,A+2,4);
|
|
959
|
+
g_const(g,A+3,0.0);
|
|
960
|
+
for(int k=0;k<4;k++) g_kill(g,A+k);
|
|
961
|
+
JUMPTO(pc+1+GET_sBx(ins));
|
|
962
|
+
break; }
|
|
963
|
+
case OP_FORLOOP: {
|
|
964
|
+
g_ldx(g,0,A);
|
|
965
|
+
g_op0(g,SSE_ADD,A+2); /* xmm0=idx */
|
|
966
|
+
g_ldx(g,2,A+2);
|
|
967
|
+
sse_rr(b,0x66,SSE_XORPD,3,3);
|
|
968
|
+
sse_rr(b,0x66,SSE_COMISD,2,3); /* comisd st,0 */
|
|
969
|
+
int jdown=emit_jcc(b,CC_BE); /* NaN too */
|
|
970
|
+
g_ldx(g,1,A+1);
|
|
971
|
+
sse_rr(b,0x66,SSE_COMISD,1,0); /* lim vs idx */
|
|
972
|
+
int jt1=emit_jcc(b,CC_A);
|
|
973
|
+
int jo1=emit_jmp(b);
|
|
974
|
+
patch_rel(b,jdown,b->n);
|
|
975
|
+
g_ldx(g,1,A+1);
|
|
976
|
+
sse_rr(b,0x66,SSE_COMISD,0,1); /* idx vs lim */
|
|
977
|
+
int jt2=emit_jcc(b,CC_AE);
|
|
978
|
+
int jo2=emit_jmp(b);
|
|
979
|
+
patch_rel(b,jt1,b->n);
|
|
980
|
+
patch_rel(b,jt2,b->n);
|
|
981
|
+
g_kill(g,A); g_kill(g,A+3);
|
|
982
|
+
g_stx(g,A,0);
|
|
983
|
+
g_stx(g,A+3,0);
|
|
984
|
+
JUMPTO(pc+1+GET_sBx(ins));
|
|
985
|
+
patch_rel(b,jo1,b->n);
|
|
986
|
+
patch_rel(b,jo2,b->n);
|
|
987
|
+
break; }
|
|
988
|
+
case OP_RETURN: {
|
|
989
|
+
int nr=GET_B(ins)-1;
|
|
990
|
+
if(nr==0){ emit_xor_eax(b); RETJMP(); break; }
|
|
991
|
+
if(in->ka==K_NUM){
|
|
992
|
+
emit_mov_m32_imm_rsi(b,VOFT,LT_NUM);
|
|
993
|
+
g_ldx(g,0,A);
|
|
994
|
+
sse_mb(b,0xF2,SSE_MOVSD_ST,0,R_RSI,VOFU);
|
|
995
|
+
} else if(in->ka==K_BOOL){
|
|
996
|
+
int h=a->home[A];
|
|
997
|
+
if(h>=0) emit_cvttsd2si_r32_x(b,R_RAX,h);
|
|
998
|
+
else emit_cvttsd2si_r32_rsp(b,R_RAX,SLOTO(g,A));
|
|
999
|
+
emit_mov_m32_eax_rsi(b,VOFU);
|
|
1000
|
+
emit_mov_m32_imm_rsi(b,VOFT,LT_BOOL);
|
|
1001
|
+
} else {
|
|
1002
|
+
emit_mov_m32_imm_rsi(b,VOFT,LT_NIL);
|
|
1003
|
+
emit_mov_m32_imm_rsi(b,VOFU,0);
|
|
1004
|
+
}
|
|
1005
|
+
emit_mov_r32_imm(b,R_RAX,1);
|
|
1006
|
+
RETJMP();
|
|
1007
|
+
break; }
|
|
1008
|
+
default:
|
|
1009
|
+
return 0;
|
|
1010
|
+
}
|
|
1011
|
+
if(b->bad) return 0;
|
|
1012
|
+
}
|
|
1013
|
+
/* fallthrough returns no values */
|
|
1014
|
+
emit_xor_eax(b);
|
|
1015
|
+
RETJMP();
|
|
1016
|
+
return !b->bad;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
static int gen_finish(Gen *g){
|
|
1020
|
+
Buf *b=&g->b;
|
|
1021
|
+
int bail=b->n;
|
|
1022
|
+
emit_mov_r32_imm(b,R_RAX,0xFFFFFFFFu); /* -1 */
|
|
1023
|
+
int tore=emit_jmp(b);
|
|
1024
|
+
int epi=b->n;
|
|
1025
|
+
gen_epilogue(g);
|
|
1026
|
+
patch_rel(b,tore,epi);
|
|
1027
|
+
for(int i=0;i<g->nbail;i++) patch_rel(b,g->bailfix[i],bail);
|
|
1028
|
+
for(int i=0;i<g->nret;i++) patch_rel(b,g->retfix[i],epi);
|
|
1029
|
+
for(int i=0;i<g->nfix;i++){
|
|
1030
|
+
int topc=g->fix[i].topc;
|
|
1031
|
+
if(topc<0||topc>=g->a->p->ncode) return 0;
|
|
1032
|
+
int off=g->a->info[topc].vis? g->pcoff[topc] : bail;
|
|
1033
|
+
patch_rel(b,g->fix[i].at,off);
|
|
1034
|
+
}
|
|
1035
|
+
return !b->bad;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/* Register operands; AsBx/const ops use only A (+A+3 for loops). */
|
|
1039
|
+
static int op_hasB(int op){
|
|
1040
|
+
switch(op){
|
|
1041
|
+
case OP_MOVE:
|
|
1042
|
+
case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV:
|
|
1043
|
+
case OP_MOD: case OP_POW:
|
|
1044
|
+
case OP_UNM: case OP_NOT:
|
|
1045
|
+
case OP_EQ: case OP_NE: case OP_LT: case OP_LE: case OP_GT: case OP_GE:
|
|
1046
|
+
case OP_GETTABLE: case OP_SETTABLE:
|
|
1047
|
+
case OP_ADDEQ: case OP_SUBEQ: case OP_MULEQ: case OP_DIVEQ:
|
|
1048
|
+
return 1;
|
|
1049
|
+
default: return 0;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
static int op_hasC(int op){
|
|
1053
|
+
switch(op){
|
|
1054
|
+
case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV:
|
|
1055
|
+
case OP_MOD: case OP_POW:
|
|
1056
|
+
case OP_EQ: case OP_NE: case OP_LT: case OP_LE: case OP_GT: case OP_GE:
|
|
1057
|
+
case OP_GETTABLE: case OP_SETTABLE:
|
|
1058
|
+
case OP_ADDEQ: case OP_SUBEQ: case OP_MULEQ: case OP_DIVEQ:
|
|
1059
|
+
return 1;
|
|
1060
|
+
default: return 0;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
static int op_hasA(int op){ return op!=OP_JMP && op!=OP_CLOSE; }
|
|
1064
|
+
|
|
1065
|
+
/* Prescan param/result kinds; ambiguous defaults bail to interpreter. */
|
|
1066
|
+
static void prescan_kinds(Ana *a){
|
|
1067
|
+
Proto *p=a->p;
|
|
1068
|
+
unsigned char *asbase=a->asbase, *asnum=a->asnum, *askey=a->askey;
|
|
1069
|
+
for(int iter=0;iter<16;iter++){
|
|
1070
|
+
int changed=0;
|
|
1071
|
+
for(int pc=0;pc<p->ncode;pc++){
|
|
1072
|
+
uint32_t ins=p->code[pc];
|
|
1073
|
+
int op=GET_OP(ins), A=GET_A(ins), B=GET_B(ins), C=GET_C(ins);
|
|
1074
|
+
switch(op){
|
|
1075
|
+
case OP_GETTABLE:
|
|
1076
|
+
if(B<a->nregs&&!asbase[B]){ asbase[B]=1; changed=1; }
|
|
1077
|
+
if(C<a->nregs){ if(!asnum[C]){ asnum[C]=1; changed=1; } askey[C]=1; }
|
|
1078
|
+
break;
|
|
1079
|
+
case OP_SETTABLE:
|
|
1080
|
+
if(A<a->nregs&&!asbase[A]){ asbase[A]=1; changed=1; }
|
|
1081
|
+
if(B<a->nregs){ if(!asnum[B]){ asnum[B]=1; changed=1; } askey[B]=1; }
|
|
1082
|
+
if(C<a->nregs&&!asnum[C]){ asnum[C]=1; changed=1; }
|
|
1083
|
+
break;
|
|
1084
|
+
case OP_ADDEQ: case OP_SUBEQ: case OP_MULEQ: case OP_DIVEQ:
|
|
1085
|
+
if(A<a->nregs&&!asbase[A]){ asbase[A]=1; changed=1; }
|
|
1086
|
+
if(B<a->nregs){ if(!asnum[B]){ asnum[B]=1; changed=1; } askey[B]=1; }
|
|
1087
|
+
if(C<a->nregs&&!asnum[C]){ asnum[C]=1; changed=1; }
|
|
1088
|
+
break;
|
|
1089
|
+
case OP_MOVE:
|
|
1090
|
+
/* Forward B->A only; backward poisons numeric temps to K_LIST. */
|
|
1091
|
+
if(B<a->nregs && A<a->nregs){
|
|
1092
|
+
if(asbase[B]&&!asbase[A]){ asbase[A]=1; changed=1; }
|
|
1093
|
+
if(asnum[B]&&!asnum[A]){ asnum[A]=1; changed=1; }
|
|
1094
|
+
}
|
|
1095
|
+
break;
|
|
1096
|
+
case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV:
|
|
1097
|
+
case OP_MOD: case OP_POW: case OP_UNM:
|
|
1098
|
+
case OP_EQ: case OP_NE: case OP_LT: case OP_LE: case OP_GT: case OP_GE:
|
|
1099
|
+
if(B<a->nregs&&!asnum[B]){ asnum[B]=1; changed=1; }
|
|
1100
|
+
if(op_hasC(op)&&C<a->nregs&&!asnum[C]){ asnum[C]=1; changed=1; }
|
|
1101
|
+
break;
|
|
1102
|
+
case OP_NOT: case OP_JMPIF: case OP_JMPIFNOT:
|
|
1103
|
+
if(A<a->nregs&&!asnum[A]){ asnum[A]=1; changed=1; }
|
|
1104
|
+
break;
|
|
1105
|
+
case OP_FORPREP: case OP_FORLOOP:
|
|
1106
|
+
/* Keep loop regs unforced; numeric checks stay in ana_region. */
|
|
1107
|
+
break;
|
|
1108
|
+
default: break;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
if(!changed) break;
|
|
1112
|
+
}
|
|
1113
|
+
for(int i=0;i<p->nparams && i<a->nregs;i++)
|
|
1114
|
+
a->pinit[i]=(asbase[i]&&!asnum[i])?K_LIST:K_NUM;
|
|
1115
|
+
for(int pc=0;pc<p->ncode;pc++){
|
|
1116
|
+
uint32_t ins=p->code[pc];
|
|
1117
|
+
if(GET_OP(ins)!=OP_GETTABLE) continue;
|
|
1118
|
+
int A=GET_A(ins);
|
|
1119
|
+
if(A>=a->nregs) continue;
|
|
1120
|
+
if(asbase[A]&&!asnum[A]) a->info[pc].kr=K_LIST;
|
|
1121
|
+
else if(asnum[A]&&!asbase[A]) a->info[pc].kr=K_NUM;
|
|
1122
|
+
else if(!asbase[A]&&!asnum[A]) a->info[pc].kr=K_NUM;
|
|
1123
|
+
else a->info[pc].kr=K_BAD;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/* Homes go to hot inner-loop regs (mixed-pin); cold stay in frame. */
|
|
1128
|
+
static void assign_homes(Ana *a){
|
|
1129
|
+
Proto *p=a->p;
|
|
1130
|
+
for(int pc=0;pc<p->ncode;pc++){
|
|
1131
|
+
int op=GET_OP(p->code[pc]);
|
|
1132
|
+
if(op==OP_JMP||op==OP_JMPIF||op==OP_JMPIFNOT||op==OP_FORLOOP){
|
|
1133
|
+
int t=pc+1+GET_sBx(p->code[pc]);
|
|
1134
|
+
if(t>=0&&t<=pc) for(int i=t;i<=pc;i++) a->depth[i]++;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
long long *w=(long long*)calloc((size_t)a->nregs,sizeof(long long));
|
|
1138
|
+
if(!w) return;
|
|
1139
|
+
int hasmp=0;
|
|
1140
|
+
for(int pc=0;pc<p->ncode;pc++){
|
|
1141
|
+
uint32_t ins=p->code[pc];
|
|
1142
|
+
int op=GET_OP(ins), A=GET_A(ins), B=GET_B(ins), C=GET_C(ins);
|
|
1143
|
+
if(!a->info[pc].vis) continue;
|
|
1144
|
+
if(op==OP_MOD||op==OP_POW) hasmp=1;
|
|
1145
|
+
int d=a->depth[pc]; if(d>10) d=10;
|
|
1146
|
+
long long wt=1LL<<d;
|
|
1147
|
+
if(op_hasA(op)&&A<a->nregs) w[A]+=wt;
|
|
1148
|
+
if(op_hasB(op)&&B<a->nregs) w[B]+=wt;
|
|
1149
|
+
if(op_hasC(op)&&C<a->nregs) w[C]+=wt;
|
|
1150
|
+
if(op==OP_FORPREP||op==OP_FORLOOP)
|
|
1151
|
+
for(int k=0;k<4;k++) if(A+k<a->nregs) w[A+k]+=wt;
|
|
1152
|
+
}
|
|
1153
|
+
/* XMM5 volatile: only if no CALL (MOD/POW helpers). */
|
|
1154
|
+
int base=hasmp?6:5, navail=16-base;
|
|
1155
|
+
for(int x=0;x<16;x++) a->savoff[x]=-1;
|
|
1156
|
+
for(int k=0;k<navail;k++){
|
|
1157
|
+
int best=-1; long long bw=0;
|
|
1158
|
+
for(int r=0;r<a->nregs;r++)
|
|
1159
|
+
if(a->home[r]<0 && w[r]>bw){ bw=w[r]; best=r; }
|
|
1160
|
+
if(best<0) break;
|
|
1161
|
+
a->home[best]=base+k;
|
|
1162
|
+
a->npin++;
|
|
1163
|
+
}
|
|
1164
|
+
for(int r=0;r<a->nregs;r++) if(w[r]>0) a->nused++;
|
|
1165
|
+
a->nsave=0;
|
|
1166
|
+
for(int x=6;x<16;x++){
|
|
1167
|
+
int used=0;
|
|
1168
|
+
for(int r=0;r<a->nregs;r++) if(a->home[r]==x) used=1;
|
|
1169
|
+
if(used) a->savoff[x]=32+16*(a->nsave++);
|
|
1170
|
+
}
|
|
1171
|
+
free(w);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
/* Translate Proto; pic=1 bans absolute addrs for embedding. */
|
|
1175
|
+
static int translate_proto(Proto *p,int pic,unsigned char **out,int *outlen,
|
|
1176
|
+
char *err,size_t errcap)
|
|
1177
|
+
{
|
|
1178
|
+
Ana a; Gen g;
|
|
1179
|
+
int okresult=0;
|
|
1180
|
+
memset(&a,0,sizeof a); memset(&g,0,sizeof g);
|
|
1181
|
+
if(err&&errcap) err[0]=0;
|
|
1182
|
+
|
|
1183
|
+
if(!p||p->ncode<=0){ if(err) snprintf(err,errcap,"empty prototype"); return 0; }
|
|
1184
|
+
if(p->ncode>65536){ if(err) snprintf(err,errcap,"prototype too large (%d instructions)",p->ncode); return 0; }
|
|
1185
|
+
if(p->nup>0){ if(err) snprintf(err,errcap,"prototype captures upvalues"); return 0; }
|
|
1186
|
+
if(p->np>0){ if(err) snprintf(err,errcap,"prototype defines closures"); return 0; }
|
|
1187
|
+
if(p->isvararg){ if(err) snprintf(err,errcap,"vararg prototype"); return 0; }
|
|
1188
|
+
if(p->nparams<0){ if(err) snprintf(err,errcap,"bad parameter count"); return 0; }
|
|
1189
|
+
if(p->maxstack<1||p->maxstack>LUC_MAXREG){ if(err) snprintf(err,errcap,"bad frame size"); return 0; }
|
|
1190
|
+
|
|
1191
|
+
a.p=p; a.pic=pic;
|
|
1192
|
+
a.nregs = p->maxstack+8;
|
|
1193
|
+
if(a.nregs>LUC_MAXREG+8) a.nregs=LUC_MAXREG+8;
|
|
1194
|
+
if(p->nparams>a.nregs){ if(err) snprintf(err,errcap,"more parameters than registers"); return 0; }
|
|
1195
|
+
a.nslots = a.nregs;
|
|
1196
|
+
|
|
1197
|
+
a.info=(OpInfo*)calloc((size_t)p->ncode,sizeof(OpInfo));
|
|
1198
|
+
a.istarget=(unsigned char*)calloc((size_t)p->ncode,1);
|
|
1199
|
+
a.bs=(unsigned char**)calloc((size_t)p->ncode,sizeof(unsigned char*));
|
|
1200
|
+
a.wl=(int*)malloc(sizeof(int)*(size_t)p->ncode);
|
|
1201
|
+
a.depth=(int*)calloc((size_t)p->ncode,sizeof(int));
|
|
1202
|
+
a.pinit=(unsigned char*)calloc((size_t)(p->nparams>0?p->nparams:1),1);
|
|
1203
|
+
a.asbase=(unsigned char*)calloc((size_t)a.nregs,1);
|
|
1204
|
+
a.asnum=(unsigned char*)calloc((size_t)a.nregs,1);
|
|
1205
|
+
a.askey=(unsigned char*)calloc((size_t)a.nregs,1);
|
|
1206
|
+
a.needdsc=(unsigned char*)calloc((size_t)a.nregs,1);
|
|
1207
|
+
a.dsc=(int*)malloc(sizeof(int)*(size_t)a.nregs);
|
|
1208
|
+
a.home=(int*)malloc(sizeof(int)*(size_t)a.nregs);
|
|
1209
|
+
g.pcoff=(int*)calloc((size_t)p->ncode,sizeof(int));
|
|
1210
|
+
if(!a.info||!a.istarget||!a.bs||!a.wl||!a.depth||!a.pinit||!a.asbase||
|
|
1211
|
+
!a.asnum||!a.askey||!a.needdsc||!a.dsc||!a.home||!g.pcoff){
|
|
1212
|
+
if(err) snprintf(err,errcap,"out of memory");
|
|
1213
|
+
goto done;
|
|
1214
|
+
}
|
|
1215
|
+
for(int i=0;i<a.nregs;i++){ a.dsc[i]=-1; a.home[i]=-1; }
|
|
1216
|
+
for(int i=0;i<p->nparams;i++) a.pinit[i]=K_NUM;
|
|
1217
|
+
prescan_kinds(&a);
|
|
1218
|
+
if(!ana_run(&a)){
|
|
1219
|
+
if(err) snprintf(err,errcap,"%s",a.err[0]?a.err:"not translatable");
|
|
1220
|
+
goto done;
|
|
1221
|
+
}
|
|
1222
|
+
a.ndsc=0;
|
|
1223
|
+
for(int r=0;r<a.nregs;r++) if(a.needdsc[r]) a.dsc[r]=a.ndsc++;
|
|
1224
|
+
assign_homes(&a);
|
|
1225
|
+
|
|
1226
|
+
/* frame: shadow|XMM saves|slot homes|list descs */
|
|
1227
|
+
a.slotoff = 32+16*a.nsave;
|
|
1228
|
+
a.dscoff = a.slotoff+8*a.nslots;
|
|
1229
|
+
{ int s=a.dscoff+DSCSZ*a.ndsc;
|
|
1230
|
+
s=(s+15)&~15;
|
|
1231
|
+
a.framesz=s+8; /* keep RSP aligned at CALLs */
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
g.a=&a;
|
|
1235
|
+
buf_init(&g.b,65536); /* 64KB, grows as needed */
|
|
1236
|
+
if(g.b.bad){ if(err) snprintf(err,errcap,"out of memory"); goto done; }
|
|
1237
|
+
if(!gen_prologue(&g)||!gen_body(&g)||!gen_finish(&g)){
|
|
1238
|
+
if(err) snprintf(err,errcap,"code emission failed");
|
|
1239
|
+
goto done;
|
|
1240
|
+
}
|
|
1241
|
+
*out=(unsigned char*)malloc((size_t)g.b.n);
|
|
1242
|
+
if(!*out){ if(err) snprintf(err,errcap,"out of memory"); goto done; }
|
|
1243
|
+
memcpy(*out,g.b.p,(size_t)g.b.n);
|
|
1244
|
+
*outlen=g.b.n;
|
|
1245
|
+
okresult=1;
|
|
1246
|
+
if(getenv("LUC_JIT_LOG")){
|
|
1247
|
+
const char *mode = a.npin==0? "memory-slot"
|
|
1248
|
+
: (a.npin>=a.nused? "pinned-XMM" : "mixed-pin");
|
|
1249
|
+
fprintf(stderr,"lucjit: %s mode, maxstack=%d, ncode=%d, bytes=%d\n",
|
|
1250
|
+
mode,p->maxstack,p->ncode,g.b.n);
|
|
1251
|
+
}
|
|
1252
|
+
done:
|
|
1253
|
+
buf_free(&g.b);
|
|
1254
|
+
free(g.fix); free(g.bailfix); free(g.retfix); free(g.pcoff);
|
|
1255
|
+
if(a.bs){ for(int i=0;i<p->ncode;i++) free(a.bs[i]); free(a.bs); }
|
|
1256
|
+
free(a.info); free(a.istarget); free(a.wl); free(a.depth); free(a.pinit);
|
|
1257
|
+
free(a.asbase); free(a.asnum); free(a.askey); free(a.needdsc);
|
|
1258
|
+
free(a.dsc); free(a.home);
|
|
1259
|
+
return okresult;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/* 6. JIT cache */
|
|
1263
|
+
|
|
1264
|
+
typedef int (*JitFn)(const Value *args,int nargs,Value *out);
|
|
1265
|
+
|
|
1266
|
+
typedef struct { Proto *p; void *mem; size_t memsz; JitFn fn; int failed; } JitEnt;
|
|
1267
|
+
|
|
1268
|
+
static JitEnt *g_jit=NULL;
|
|
1269
|
+
static int g_jitn=0, g_jitcap=0;
|
|
1270
|
+
|
|
1271
|
+
static JitEnt *jit_find(Proto *p){
|
|
1272
|
+
for(int i=0;i<g_jitn;i++) if(g_jit[i].p==p) return &g_jit[i];
|
|
1273
|
+
return NULL;
|
|
1274
|
+
}
|
|
1275
|
+
static JitEnt *jit_add(Proto *p){
|
|
1276
|
+
if(g_jitn==g_jitcap){
|
|
1277
|
+
int nc=g_jitcap?g_jitcap*2:64;
|
|
1278
|
+
JitEnt *ne=(JitEnt*)realloc(g_jit,sizeof(JitEnt)*(size_t)nc);
|
|
1279
|
+
if(!ne) return NULL;
|
|
1280
|
+
g_jit=ne; g_jitcap=nc;
|
|
1281
|
+
}
|
|
1282
|
+
JitEnt *e=&g_jit[g_jitn++];
|
|
1283
|
+
e->p=p; e->mem=NULL; e->memsz=0; e->fn=NULL; e->failed=0;
|
|
1284
|
+
return e;
|
|
1285
|
+
}
|
|
1286
|
+
static void *jit_map(const unsigned char *code,int len,size_t *sz){
|
|
1287
|
+
SIZE_T n=(SIZE_T)len;
|
|
1288
|
+
void *m=VirtualAlloc(NULL,n,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE);
|
|
1289
|
+
if(!m) return NULL;
|
|
1290
|
+
memcpy(m,code,(size_t)len);
|
|
1291
|
+
DWORD old=0;
|
|
1292
|
+
if(!VirtualProtect(m,n,PAGE_EXECUTE_READ,&old)){ VirtualFree(m,0,MEM_RELEASE); return NULL; }
|
|
1293
|
+
FlushInstructionCache(GetCurrentProcess(),m,n);
|
|
1294
|
+
*sz=(size_t)n;
|
|
1295
|
+
return m;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
/* Compile once, return native entry or NULL. */
|
|
1299
|
+
static JitFn jit_get(Proto *p){
|
|
1300
|
+
JitEnt *e=jit_find(p);
|
|
1301
|
+
if(e){ return e->failed? NULL : e->fn; }
|
|
1302
|
+
e=jit_add(p);
|
|
1303
|
+
if(!e) return NULL;
|
|
1304
|
+
unsigned char *img=NULL; int imglen=0; char err[160]; err[0]=0;
|
|
1305
|
+
if(!translate_proto(p,0,&img,&imglen,err,sizeof err)){
|
|
1306
|
+
e->failed=1;
|
|
1307
|
+
if(getenv("LUC_JIT_LOG"))
|
|
1308
|
+
fprintf(stderr,"lucjit: fallback (%s), maxstack=%d, ncode=%d\n",
|
|
1309
|
+
err[0]?err:"?",p->maxstack,p->ncode);
|
|
1310
|
+
return NULL;
|
|
1311
|
+
}
|
|
1312
|
+
size_t sz=0;
|
|
1313
|
+
void *mem=jit_map(img,imglen,&sz);
|
|
1314
|
+
free(img);
|
|
1315
|
+
if(!mem){ e->failed=1; return NULL; }
|
|
1316
|
+
e->mem=mem; e->memsz=sz;
|
|
1317
|
+
memcpy(&e->fn,&mem,sizeof e->fn); /* avoid fn-ptr cast UB */
|
|
1318
|
+
return e->fn;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
void luc_jit_shutdown(void){
|
|
1322
|
+
for(int i=0;i<g_jitn;i++) if(g_jit[i].mem) VirtualFree(g_jit[i].mem,0,MEM_RELEASE);
|
|
1323
|
+
free(g_jit); g_jit=NULL; g_jitn=g_jitcap=0;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
/* vm_call hook: same LT_FUNC contract; FALLBACK means interpret. */
|
|
1327
|
+
int luc_jit_call(LucState *L,int func,int nargs,int nres){
|
|
1328
|
+
if(!L||func<0) return LUC_JIT_FALLBACK;
|
|
1329
|
+
Value f=L->stack[func];
|
|
1330
|
+
if(f.t!=LT_FUNC) return LUC_JIT_FALLBACK;
|
|
1331
|
+
Closure *cl=AS_CL(f);
|
|
1332
|
+
JitFn fn=jit_get(cl->p);
|
|
1333
|
+
if(!fn) return LUC_JIT_FALLBACK;
|
|
1334
|
+
|
|
1335
|
+
Value out[2];
|
|
1336
|
+
out[0]=NIL; out[1]=NIL;
|
|
1337
|
+
int n=fn(&L->stack[func+1],nargs,out);
|
|
1338
|
+
if(n<0) return LUC_JIT_FALLBACK; /* guard failed */
|
|
1339
|
+
|
|
1340
|
+
int want = nres>=0? nres : n;
|
|
1341
|
+
ensure_stack(L,func+want+8); /* native part done */
|
|
1342
|
+
for(int i=0;i<n && i<want;i++) L->stack[func+i]=out[i];
|
|
1343
|
+
for(int i=n;i<want;i++) L->stack[func+i]=NIL;
|
|
1344
|
+
L->top=func+want;
|
|
1345
|
+
return want;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/* Top-level run; main chunk usually falls back (globals/calls). */
|
|
1349
|
+
int luc_jit_run(Closure *cl){
|
|
1350
|
+
if(!cl||!cl->p) return LUC_JIT_FALLBACK;
|
|
1351
|
+
JitFn fn=jit_get(cl->p);
|
|
1352
|
+
if(!fn) return LUC_JIT_FALLBACK;
|
|
1353
|
+
Value out[2]; out[0]=NIL; out[1]=NIL;
|
|
1354
|
+
int n=fn(NULL,0,out);
|
|
1355
|
+
if(n<0) return LUC_JIT_FALLBACK;
|
|
1356
|
+
if(n>=1 && out[0].t==LT_NUM) return (int)out[0].u.n;
|
|
1357
|
+
return 0;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
/* 7. PE32+ writer */
|
|
1361
|
+
|
|
1362
|
+
#define PE_IMAGEBASE 0x140000000ULL
|
|
1363
|
+
#define PE_SECALIGN 0x1000
|
|
1364
|
+
#define PE_FILEALIGN 0x200
|
|
1365
|
+
|
|
1366
|
+
static unsigned pe_align(unsigned v,unsigned a){ return (v+a-1)&~(a-1); }
|
|
1367
|
+
|
|
1368
|
+
/* kernel32 imports, IAT order */
|
|
1369
|
+
static const char *const PE_IMPORTS[3]={ "ExitProcess", "WriteFile", "GetStdHandle" };
|
|
1370
|
+
#define IMP_EXITPROCESS 0
|
|
1371
|
+
#define IMP_WRITEFILE 1
|
|
1372
|
+
#define IMP_GETSTDHANDLE 2
|
|
1373
|
+
|
|
1374
|
+
/* .rdata layout (section RVA offsets) */
|
|
1375
|
+
#define RD_DESC 0 /* 2 x IMAGE_IMPORT_DESCRIPTOR (40) */
|
|
1376
|
+
#define RD_ILT 40 /* 4 qwords */
|
|
1377
|
+
#define RD_IAT 72 /* 4 qwords */
|
|
1378
|
+
#define RD_NAMES 104 /* hint/name blobs, then "kernel32.dll" */
|
|
1379
|
+
|
|
1380
|
+
static void pe_build_rdata(Buf *rd,unsigned rva,unsigned *iat_rva){
|
|
1381
|
+
unsigned nameoff[3], dlloff;
|
|
1382
|
+
Buf nb; buf_init(&nb,256);
|
|
1383
|
+
for(int i=0;i<3;i++){
|
|
1384
|
+
if(nb.n&1) e1(&nb,0);
|
|
1385
|
+
nameoff[i]=RD_NAMES+(unsigned)nb.n;
|
|
1386
|
+
e2(&nb,0);
|
|
1387
|
+
eblob(&nb,PE_IMPORTS[i],(int)strlen(PE_IMPORTS[i])+1);
|
|
1388
|
+
}
|
|
1389
|
+
if(nb.n&1) e1(&nb,0);
|
|
1390
|
+
dlloff=RD_NAMES+(unsigned)nb.n;
|
|
1391
|
+
eblob(&nb,"kernel32.dll",13);
|
|
1392
|
+
|
|
1393
|
+
/* descriptor[0] */
|
|
1394
|
+
e4(rd,rva+RD_ILT); e4(rd,0); e4(rd,0); e4(rd,rva+dlloff); e4(rd,rva+RD_IAT);
|
|
1395
|
+
/* descriptor[1] = terminator */
|
|
1396
|
+
for(int i=0;i<5;i++) e4(rd,0);
|
|
1397
|
+
/* ILT then IAT: both point at the hint/name entries */
|
|
1398
|
+
for(int t=0;t<2;t++){
|
|
1399
|
+
for(int i=0;i<3;i++) e8(rd,(unsigned long long)(rva+nameoff[i]));
|
|
1400
|
+
e8(rd,0);
|
|
1401
|
+
}
|
|
1402
|
+
eblob(rd,nb.p,nb.n);
|
|
1403
|
+
buf_free(&nb);
|
|
1404
|
+
*iat_rva=rva+RD_IAT;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
/* call [rip + (iat slot)] */
|
|
1408
|
+
static void emit_call_import(Buf *b,unsigned code_rva,unsigned iat_rva,int idx){
|
|
1409
|
+
e1(b,0xFF); e1(b,0x15);
|
|
1410
|
+
unsigned here=code_rva+(unsigned)b->n+4;
|
|
1411
|
+
e4(b,(unsigned)((int)(iat_rva+8u*(unsigned)idx)-(int)here));
|
|
1412
|
+
}
|
|
1413
|
+
static void emit_lea_rip(Buf *b,int reg,unsigned code_rva,unsigned target_rva){
|
|
1414
|
+
int rex=0x48|((reg>=8)?4:0);
|
|
1415
|
+
e1(b,rex); e1(b,0x8D); e1(b,0x05|((reg&7)<<3));
|
|
1416
|
+
unsigned here=code_rva+(unsigned)b->n+4;
|
|
1417
|
+
e4(b,(unsigned)((int)target_rva-(int)here));
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
/* Standalone PE32+: native PIC + stub runs it, exits with number. */
|
|
1421
|
+
static int pe_write_exe(const char *path,const unsigned char *native,int nativelen,
|
|
1422
|
+
char *err,size_t errcap)
|
|
1423
|
+
{
|
|
1424
|
+
static const char msg[]="luc: native image bailed out (value out of the numeric subset)\r\n";
|
|
1425
|
+
Buf text, rdata, data;
|
|
1426
|
+
int rc=0;
|
|
1427
|
+
unsigned text_rva=PE_SECALIGN, rdata_rva, data_rva, iat_rva=0;
|
|
1428
|
+
unsigned textsz, rdatasz, datasz;
|
|
1429
|
+
unsigned char *dos=NULL;
|
|
1430
|
+
FILE *f=NULL;
|
|
1431
|
+
|
|
1432
|
+
buf_init(&text,4096); buf_init(&rdata,1024); buf_init(&data,1024);
|
|
1433
|
+
if(text.bad||rdata.bad||data.bad){ snprintf(err,errcap,"out of memory"); goto out; }
|
|
1434
|
+
|
|
1435
|
+
/* sizes first; stub measured in pass 0 (fixed-width rel32/imm32). */
|
|
1436
|
+
for(int pass=0;pass<2;pass++){
|
|
1437
|
+
text.n=0; rdata.n=0; data.n=0;
|
|
1438
|
+
|
|
1439
|
+
/* .data: out Value, scratch, message */
|
|
1440
|
+
unsigned d_out=0, d_nw=16, d_msg=24;
|
|
1441
|
+
datasz = d_msg + (unsigned)sizeof msg;
|
|
1442
|
+
(void)d_out; (void)d_nw;
|
|
1443
|
+
|
|
1444
|
+
if(pass==1){
|
|
1445
|
+
for(unsigned i=0;i<datasz;i++) e1(&data,0);
|
|
1446
|
+
memcpy(data.p+d_msg,msg,sizeof msg);
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
/* entry stub first, then native image */
|
|
1450
|
+
unsigned stub_rva = text_rva;
|
|
1451
|
+
unsigned nat_rva;
|
|
1452
|
+
/* stub len fixed; measured in pass 0 */
|
|
1453
|
+
static int stublen=0;
|
|
1454
|
+
if(pass==0) nat_rva = stub_rva+256; else nat_rva = stub_rva+(unsigned)stublen;
|
|
1455
|
+
|
|
1456
|
+
rdata_rva = pe_align(text_rva + (unsigned)(pass==0? 256+nativelen
|
|
1457
|
+
: stublen+nativelen), PE_SECALIGN);
|
|
1458
|
+
{ Buf tmp; buf_init(&tmp,1024);
|
|
1459
|
+
if(tmp.bad){ snprintf(err,errcap,"out of memory"); goto out; }
|
|
1460
|
+
unsigned dummy=0;
|
|
1461
|
+
pe_build_rdata(&tmp,rdata_rva,&dummy);
|
|
1462
|
+
rdatasz=(unsigned)tmp.n;
|
|
1463
|
+
buf_free(&tmp);
|
|
1464
|
+
}
|
|
1465
|
+
data_rva = pe_align(rdata_rva+rdatasz,PE_SECALIGN);
|
|
1466
|
+
if(pass==1) pe_build_rdata(&rdata,rdata_rva,&iat_rva);
|
|
1467
|
+
else { Buf t2; buf_init(&t2,1024); pe_build_rdata(&t2,rdata_rva,&iat_rva); buf_free(&t2); }
|
|
1468
|
+
|
|
1469
|
+
/* ---- entry stub ---- */
|
|
1470
|
+
emit_sub_rsp(&text,0x38); /* 56 == 8 (mod 16) */
|
|
1471
|
+
emit_xor_eax(&text);
|
|
1472
|
+
emit_mov_rr64(&text,R_RCX,R_RAX); /* args = NULL */
|
|
1473
|
+
emit_mov_rr32(&text,R_RDX,R_RAX); /* nargs = 0 */
|
|
1474
|
+
emit_lea_rip(&text,8,text_rva,data_rva+0); /* r8 = &out */
|
|
1475
|
+
e1(&text,0xE8); /* call native */
|
|
1476
|
+
{ unsigned here=text_rva+(unsigned)text.n+4;
|
|
1477
|
+
e4(&text,(unsigned)((int)nat_rva-(int)here)); }
|
|
1478
|
+
e1(&text,0x85); mrm_rr(&text,R_RAX,R_RAX); /* test eax,eax */
|
|
1479
|
+
int jfail=emit_jcc(&text,CC_S);
|
|
1480
|
+
/* out[0].t == LT_NUM ? */
|
|
1481
|
+
e1(&text,0x81); e1(&text,0x3D);
|
|
1482
|
+
{ unsigned here=text_rva+(unsigned)text.n+8;
|
|
1483
|
+
e4(&text,(unsigned)((int)(data_rva+0+(unsigned)VOFT)-(int)here)); }
|
|
1484
|
+
e4(&text,(unsigned)LT_NUM);
|
|
1485
|
+
int jnotnum=emit_jcc(&text,CC_NE);
|
|
1486
|
+
e1(&text,0xF2); e1(&text,0x0F); e1(&text,0x2C); e1(&text,0x0D); /* cvttsd2si ecx,[rip+..] */
|
|
1487
|
+
{ unsigned here=text_rva+(unsigned)text.n+4;
|
|
1488
|
+
e4(&text,(unsigned)((int)(data_rva+0+(unsigned)VOFU)-(int)here)); }
|
|
1489
|
+
int jexit=emit_jmp(&text);
|
|
1490
|
+
patch_rel(&text,jnotnum,text.n);
|
|
1491
|
+
e1(&text,0x31); mrm_rr(&text,R_RCX,R_RCX); /* xor ecx,ecx */
|
|
1492
|
+
patch_rel(&text,jexit,text.n);
|
|
1493
|
+
emit_call_import(&text,text_rva,iat_rva,IMP_EXITPROCESS);
|
|
1494
|
+
/* failure path: write the message to stderr, exit 1 */
|
|
1495
|
+
patch_rel(&text,jfail,text.n);
|
|
1496
|
+
emit_mov_r32_imm(&text,R_RCX,0xFFFFFFF4u); /* STD_ERROR_HANDLE (-12) */
|
|
1497
|
+
emit_call_import(&text,text_rva,iat_rva,IMP_GETSTDHANDLE);
|
|
1498
|
+
emit_mov_rr64(&text,R_RCX,R_RAX);
|
|
1499
|
+
emit_lea_rip(&text,R_RDX,text_rva,data_rva+24);
|
|
1500
|
+
emit_mov_r32_imm(&text,8,(unsigned)(sizeof msg-1));
|
|
1501
|
+
emit_lea_rip(&text,9,text_rva,data_rva+16);
|
|
1502
|
+
e1(&text,0x48); e1(&text,0xC7); mrm_rsp(&text,0,0x20); e4(&text,0); /* 5th arg = NULL */
|
|
1503
|
+
emit_call_import(&text,text_rva,iat_rva,IMP_WRITEFILE);
|
|
1504
|
+
emit_mov_r32_imm(&text,R_RCX,1);
|
|
1505
|
+
emit_call_import(&text,text_rva,iat_rva,IMP_EXITPROCESS);
|
|
1506
|
+
e1(&text,0xCC); /* int3 backstop */
|
|
1507
|
+
|
|
1508
|
+
if(pass==0){ stublen=text.n; continue; }
|
|
1509
|
+
|
|
1510
|
+
eblob(&text,native,nativelen);
|
|
1511
|
+
textsz=(unsigned)text.n;
|
|
1512
|
+
if(text.bad||rdata.bad||data.bad){ snprintf(err,errcap,"out of memory"); goto out; }
|
|
1513
|
+
|
|
1514
|
+
/* ---- headers ---- */
|
|
1515
|
+
unsigned hdrsz = pe_align(0x80+4+20+240+3*40,PE_FILEALIGN);
|
|
1516
|
+
unsigned text_ptr = hdrsz;
|
|
1517
|
+
unsigned text_raw = pe_align(textsz,PE_FILEALIGN);
|
|
1518
|
+
unsigned rdata_ptr = text_ptr+text_raw;
|
|
1519
|
+
unsigned rdata_raw = pe_align(rdatasz,PE_FILEALIGN);
|
|
1520
|
+
unsigned data_ptr = rdata_ptr+rdata_raw;
|
|
1521
|
+
unsigned data_raw = pe_align(datasz,PE_FILEALIGN);
|
|
1522
|
+
unsigned imgsz = data_rva+pe_align(datasz,PE_SECALIGN);
|
|
1523
|
+
|
|
1524
|
+
Buf h; buf_init(&h,hdrsz+16);
|
|
1525
|
+
if(h.bad){ snprintf(err,errcap,"out of memory"); goto out; }
|
|
1526
|
+
/* DOS header + stub */
|
|
1527
|
+
e2(&h,0x5A4D); for(int i=0;i<29;i++) e2(&h,0);
|
|
1528
|
+
h.n=0x3C; e4(&h,0x80);
|
|
1529
|
+
h.n=0x40;
|
|
1530
|
+
{ static const unsigned char stub[]={
|
|
1531
|
+
0x0E,0x1F,0xBA,0x0E,0x00,0xB4,0x09,0xCD,0x21,0xB8,0x01,0x4C,0xCD,0x21,
|
|
1532
|
+
'T','h','i','s',' ','p','r','o','g','r','a','m',' ','c','a','n','n','o','t',
|
|
1533
|
+
' ','b','e',' ','r','u','n',' ','i','n',' ','D','O','S',' ','m','o','d','e',
|
|
1534
|
+
'.','\r','\r','\n','$',0,0,0,0,0,0,0 };
|
|
1535
|
+
eblob(&h,stub,(int)sizeof stub);
|
|
1536
|
+
}
|
|
1537
|
+
while(h.n<0x80) e1(&h,0);
|
|
1538
|
+
/* PE signature + COFF header */
|
|
1539
|
+
e4(&h,0x00004550);
|
|
1540
|
+
e2(&h,0x8664); /* Machine = AMD64 */
|
|
1541
|
+
e2(&h,3); /* NumberOfSections */
|
|
1542
|
+
e4(&h,0); e4(&h,0); e4(&h,0); /* timestamp, symtab, nsyms */
|
|
1543
|
+
e2(&h,240); /* SizeOfOptionalHeader */
|
|
1544
|
+
e2(&h,0x0022); /* EXECUTABLE_IMAGE | LARGE_ADDRESS_AWARE */
|
|
1545
|
+
/* optional header */
|
|
1546
|
+
e2(&h,0x020B); /* PE32+ */
|
|
1547
|
+
e1(&h,14); e1(&h,0);
|
|
1548
|
+
e4(&h,text_raw); e4(&h,rdata_raw+data_raw); e4(&h,0);
|
|
1549
|
+
e4(&h,text_rva); /* AddressOfEntryPoint = stub */
|
|
1550
|
+
e4(&h,text_rva); /* BaseOfCode */
|
|
1551
|
+
e8(&h,PE_IMAGEBASE);
|
|
1552
|
+
e4(&h,PE_SECALIGN); e4(&h,PE_FILEALIGN);
|
|
1553
|
+
e2(&h,6); e2(&h,0); /* OS version */
|
|
1554
|
+
e2(&h,0); e2(&h,0); /* image version */
|
|
1555
|
+
e2(&h,6); e2(&h,0); /* subsystem version */
|
|
1556
|
+
e4(&h,0);
|
|
1557
|
+
e4(&h,imgsz); e4(&h,hdrsz);
|
|
1558
|
+
e4(&h,0); /* checksum */
|
|
1559
|
+
e2(&h,3); /* console subsystem */
|
|
1560
|
+
e2(&h,0x8160); /* dynamic base/NX/terminal-server aware */
|
|
1561
|
+
e8(&h,0x100000); e8(&h,0x1000);/* stack reserve/commit */
|
|
1562
|
+
e8(&h,0x100000); e8(&h,0x1000);/* heap reserve/commit */
|
|
1563
|
+
e4(&h,0);
|
|
1564
|
+
e4(&h,16); /* NumberOfRvaAndSizes */
|
|
1565
|
+
for(int i=0;i<16;i++){
|
|
1566
|
+
if(i==1){ e4(&h,rdata_rva+RD_DESC); e4(&h,40); }
|
|
1567
|
+
else if(i==12){ e4(&h,iat_rva); e4(&h,32); }
|
|
1568
|
+
else { e4(&h,0); e4(&h,0); }
|
|
1569
|
+
}
|
|
1570
|
+
/* sections */
|
|
1571
|
+
struct { const char *nm; unsigned vs,va,rs,pr,ch; } sec[3]={
|
|
1572
|
+
{".text", textsz, text_rva, text_raw, text_ptr, 0x60000020u},
|
|
1573
|
+
{".rdata",rdatasz,rdata_rva,rdata_raw,rdata_ptr,0x40000040u},
|
|
1574
|
+
{".data", datasz, data_rva, data_raw, data_ptr, 0xC0000040u}
|
|
1575
|
+
};
|
|
1576
|
+
for(int i=0;i<3;i++){
|
|
1577
|
+
char nm[8]; memset(nm,0,8);
|
|
1578
|
+
memcpy(nm,sec[i].nm,strlen(sec[i].nm));
|
|
1579
|
+
eblob(&h,nm,8);
|
|
1580
|
+
e4(&h,sec[i].vs); e4(&h,sec[i].va);
|
|
1581
|
+
e4(&h,sec[i].rs); e4(&h,sec[i].pr);
|
|
1582
|
+
e4(&h,0); e4(&h,0); e2(&h,0); e2(&h,0);
|
|
1583
|
+
e4(&h,sec[i].ch);
|
|
1584
|
+
}
|
|
1585
|
+
while(h.n<(int)hdrsz) e1(&h,0);
|
|
1586
|
+
if(h.bad){ buf_free(&h); snprintf(err,errcap,"out of memory"); goto out; }
|
|
1587
|
+
|
|
1588
|
+
f=fopen(path,"wb");
|
|
1589
|
+
if(!f){ buf_free(&h); snprintf(err,errcap,"cannot create '%s'",path); goto out; }
|
|
1590
|
+
fwrite(h.p,1,(size_t)h.n,f);
|
|
1591
|
+
buf_free(&h);
|
|
1592
|
+
fwrite(text.p,1,(size_t)textsz,f);
|
|
1593
|
+
for(unsigned i=textsz;i<text_raw;i++) fputc(0,f);
|
|
1594
|
+
fwrite(rdata.p,1,(size_t)rdatasz,f);
|
|
1595
|
+
for(unsigned i=rdatasz;i<rdata_raw;i++) fputc(0,f);
|
|
1596
|
+
fwrite(data.p,1,(size_t)datasz,f);
|
|
1597
|
+
for(unsigned i=datasz;i<data_raw;i++) fputc(0,f);
|
|
1598
|
+
if(ferror(f)){ snprintf(err,errcap,"write error on '%s'",path); fclose(f); f=NULL; goto out; }
|
|
1599
|
+
fclose(f); f=NULL;
|
|
1600
|
+
rc=1;
|
|
1601
|
+
}
|
|
1602
|
+
out:
|
|
1603
|
+
if(f) fclose(f);
|
|
1604
|
+
free(dos);
|
|
1605
|
+
buf_free(&text); buf_free(&rdata); buf_free(&data);
|
|
1606
|
+
return rc;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
/* 8. AOT */
|
|
1610
|
+
|
|
1611
|
+
#define AOT_MAGIC "LUCAOT1" /* 8 bytes with NUL */
|
|
1612
|
+
|
|
1613
|
+
/* Self-clone host image + append script (has full runtime). */
|
|
1614
|
+
static int aot_selfclone(const char *outpath,const char *src,int srclen,char *err,size_t errcap){
|
|
1615
|
+
char self[MAX_PATH];
|
|
1616
|
+
DWORD n=GetModuleFileNameA(NULL,self,MAX_PATH);
|
|
1617
|
+
if(n==0||n>=MAX_PATH){ snprintf(err,errcap,"cannot locate the running luc executable"); return 0; }
|
|
1618
|
+
FILE *in=fopen(self,"rb");
|
|
1619
|
+
if(!in){ snprintf(err,errcap,"cannot read '%s'",self); return 0; }
|
|
1620
|
+
if(fseek(in,0,SEEK_END)!=0){ fclose(in); snprintf(err,errcap,"cannot size '%s'",self); return 0; }
|
|
1621
|
+
long total=ftell(in);
|
|
1622
|
+
if(total<=0){ fclose(in); snprintf(err,errcap,"cannot size '%s'",self); return 0; }
|
|
1623
|
+
|
|
1624
|
+
/* drop old payload (no nesting) */
|
|
1625
|
+
long keep=total;
|
|
1626
|
+
if(total>12){
|
|
1627
|
+
char tr[12];
|
|
1628
|
+
if(fseek(in,total-12,SEEK_SET)==0 && fread(tr,1,12,in)==12 &&
|
|
1629
|
+
memcmp(tr+4,AOT_MAGIC,8)==0){
|
|
1630
|
+
unsigned pl; memcpy(&pl,tr,4);
|
|
1631
|
+
if((long)pl+12<=total) keep=total-12-(long)pl;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
unsigned char *img=(unsigned char*)malloc((size_t)keep);
|
|
1635
|
+
if(!img){ fclose(in); snprintf(err,errcap,"out of memory"); return 0; }
|
|
1636
|
+
if(fseek(in,0,SEEK_SET)!=0 || fread(img,1,(size_t)keep,in)!=(size_t)keep){
|
|
1637
|
+
free(img); fclose(in); snprintf(err,errcap,"cannot read '%s'",self); return 0;
|
|
1638
|
+
}
|
|
1639
|
+
fclose(in);
|
|
1640
|
+
|
|
1641
|
+
FILE *out=fopen(outpath,"wb");
|
|
1642
|
+
if(!out){ free(img); snprintf(err,errcap,"cannot create '%s'",outpath); return 0; }
|
|
1643
|
+
fwrite(img,1,(size_t)keep,out);
|
|
1644
|
+
free(img);
|
|
1645
|
+
fwrite(src,1,(size_t)srclen,out);
|
|
1646
|
+
unsigned pl=(unsigned)srclen;
|
|
1647
|
+
fwrite(&pl,1,4,out);
|
|
1648
|
+
fwrite(AOT_MAGIC,1,8,out);
|
|
1649
|
+
if(ferror(out)){ fclose(out); snprintf(err,errcap,"write error on '%s'",outpath); return 0; }
|
|
1650
|
+
fclose(out);
|
|
1651
|
+
return 1;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
/* Recover appended script at startup. */
|
|
1655
|
+
int luc_aot_embedded(char **psrc,int *plen){
|
|
1656
|
+
char self[MAX_PATH];
|
|
1657
|
+
DWORD n=GetModuleFileNameA(NULL,self,MAX_PATH);
|
|
1658
|
+
if(n==0||n>=MAX_PATH) return 0;
|
|
1659
|
+
FILE *f=fopen(self,"rb");
|
|
1660
|
+
if(!f) return 0;
|
|
1661
|
+
int ok=0;
|
|
1662
|
+
if(fseek(f,0,SEEK_END)==0){
|
|
1663
|
+
long total=ftell(f);
|
|
1664
|
+
char tr[12];
|
|
1665
|
+
if(total>12 && fseek(f,total-12,SEEK_SET)==0 && fread(tr,1,12,f)==12 &&
|
|
1666
|
+
memcmp(tr+4,AOT_MAGIC,8)==0){
|
|
1667
|
+
unsigned pl; memcpy(&pl,tr,4);
|
|
1668
|
+
if(pl>0 && (long)pl+12<=total && fseek(f,total-12-(long)pl,SEEK_SET)==0){
|
|
1669
|
+
char *s=(char*)malloc((size_t)pl+1);
|
|
1670
|
+
if(s){
|
|
1671
|
+
if(fread(s,1,(size_t)pl,f)==(size_t)pl){
|
|
1672
|
+
s[pl]=0; *psrc=s; *plen=(int)pl; ok=1;
|
|
1673
|
+
} else free(s);
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
fclose(f);
|
|
1679
|
+
return ok;
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
/* Protected compile; syntax error reports instead of longjmp. */
|
|
1683
|
+
static Closure *aot_compile(const char *src,int srclen,const char *name,char *err,size_t errcap){
|
|
1684
|
+
ErrJmp ej; ej.prev=V.errjmp; V.errjmp=&ej;
|
|
1685
|
+
Closure *cl=NULL;
|
|
1686
|
+
if(setjmp(ej.jb)==0) cl=luc_compile(src,srclen,name);
|
|
1687
|
+
else {
|
|
1688
|
+
Str *m=tostr(V.errval);
|
|
1689
|
+
snprintf(err,errcap,"%s",m?m->s:"compile error");
|
|
1690
|
+
cl=NULL;
|
|
1691
|
+
}
|
|
1692
|
+
V.errjmp=ej.prev;
|
|
1693
|
+
return cl;
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
int luc_aot_build(const char *src,int srclen,const char *outpath){
|
|
1697
|
+
char err[512]; err[0]=0;
|
|
1698
|
+
if(!src||srclen<=0||!outpath||!*outpath){
|
|
1699
|
+
fprintf(stderr,"luc build: nothing to build\n");
|
|
1700
|
+
return 1;
|
|
1701
|
+
}
|
|
1702
|
+
Closure *cl=aot_compile(src,srclen,outpath,err,sizeof err);
|
|
1703
|
+
if(!cl){ fprintf(stderr,"luc build: %s\n",err); return 1; }
|
|
1704
|
+
|
|
1705
|
+
const char *want_pe=getenv("LUC_AOT_PE");
|
|
1706
|
+
if(want_pe && *want_pe=='1'){
|
|
1707
|
+
unsigned char *img=NULL; int imglen=0; char terr[160];
|
|
1708
|
+
if(!translate_proto(cl->p,1,&img,&imglen,terr,sizeof terr)){
|
|
1709
|
+
fprintf(stderr,
|
|
1710
|
+
"luc build: LUC_AOT_PE=1 emits a runtime-free executable, which only\n"
|
|
1711
|
+
" supports the native numeric subset; this program needs the\n"
|
|
1712
|
+
" runtime (%s).\n"
|
|
1713
|
+
" Unset LUC_AOT_PE to build a self-contained executable.\n",terr);
|
|
1714
|
+
return 1;
|
|
1715
|
+
}
|
|
1716
|
+
int ok=pe_write_exe(outpath,img,imglen,err,sizeof err);
|
|
1717
|
+
free(img);
|
|
1718
|
+
if(!ok){ fprintf(stderr,"luc build: %s\n",err); return 1; }
|
|
1719
|
+
printf("luc build: wrote %s (native, runtime-free)\n",outpath);
|
|
1720
|
+
return 0;
|
|
1721
|
+
}
|
|
1722
|
+
if(!aot_selfclone(outpath,src,srclen,err,sizeof err)){
|
|
1723
|
+
fprintf(stderr,"luc build: %s\n",err);
|
|
1724
|
+
return 1;
|
|
1725
|
+
}
|
|
1726
|
+
printf("luc build: wrote %s (self-contained)\n",outpath);
|
|
1727
|
+
return 0;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
#else /* !_WIN32: no native; always interpret */
|
|
1731
|
+
|
|
1732
|
+
int luc_jit_run(Closure *cl){ (void)cl; return LUC_JIT_FALLBACK; }
|
|
1733
|
+
int luc_jit_call(LucState *L,int func,int nargs,int nres){
|
|
1734
|
+
(void)L;(void)func;(void)nargs;(void)nres; return LUC_JIT_FALLBACK;
|
|
1735
|
+
}
|
|
1736
|
+
void luc_jit_shutdown(void){}
|
|
1737
|
+
int luc_aot_embedded(char **psrc,int *plen){ (void)psrc;(void)plen; return 0; }
|
|
1738
|
+
int luc_aot_build(const char *src,int srclen,const char *outpath){
|
|
1739
|
+
(void)src;(void)srclen;(void)outpath;
|
|
1740
|
+
fprintf(stderr,"luc build: AOT output is implemented for Windows x64 only\n");
|
|
1741
|
+
return 1;
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
#endif
|