nx_wasm_data.nx source
↩ module page · 185 lines · 7953 B
1// Static-data layout for the WAT backend. Addresses are module-relative, never host pointers.
2import "nx_syscalls.nx"
3import "nx_types.nx"
4import "nx_ir.nx"
5import "nx_outbuf.nx"
6
7const WD_PAGE: i64 = 65536 // WebAssembly memory page.
8const WD_CELL: i64 = 8 // Nishi scalar global alignment.
9const WD_GLOBAL_STRIDE: i64 = 80 // IR global pool ABI.
10const WD_MAX_BYTES: i64 = 4294967296 // wasm32 address space.
11static wd_addresses: *i64
12static wd_count: i64
13static wd_init_address: i64
14
15func wd_fail(msg: *u8) -> i64 {
16 var n: i64=0
17 while msg[n]!=(0 as u8) { n=n+1 }
18 sys_write(2,msg,n)
19 sys_exit(65)
20 return 0
21}
22// Zero leaves the historical source-derived layout unchanged. The driver alone sets these options.
23static wd_arena_pages_flag: i64
24static wd_static_limit_pages_flag: i64
25static wd_static_base_bytes: i64
26static wd_static_end_bytes: i64
27// Page syntax is deliberately strict: malformed prefixes and signed/overflowing input never become sizes.
28func wd_parse_pages(s: *u8) -> i64 {
29 var n: i64=0; var i: i64=0
30 let maximum: i64=WD_MAX_BYTES/WD_PAGE
31 while s[i]!=(0 as u8) {
32 let c: i64=s[i] as i64
33 if c<48 || c>57 { return 0-1 }
34 let digit: i64=c-48
35 if n>(maximum-digit)/10 { return 0-1 }
36 n=n*10+digit; i=i+1
37 }
38 if i==0 || n==0 || n>maximum { return 0-1 }
39 return n
40}
41// Returns derived pages or a negative named error code; no emission or process exit.
42// Byte units here permit exact-fit qualification while the CLI supplies page-aligned reservations.
43func wd_prepare_checked(m: *Module,pages: i64,shared: i64,limit_bytes: i64) -> i64 {
44 if wd_addresses!=(0 as *i64) { sys_munmap(wd_addresses as *u8,wd_count*WD_CELL) }
45 wd_addresses=0 as *i64; wd_count=0; wd_init_address=0
46 wd_static_base_bytes=0; wd_static_end_bytes=0
47 if pages<0 || pages>WD_MAX_BYTES/WD_PAGE { return 0-10 }
48 var limit: i64=WD_MAX_BYTES
49 if limit_bytes!=0 {
50 if limit_bytes<0 || limit_bytes>WD_MAX_BYTES { return 0-11 }
51 limit=limit_bytes
52 }
53 var end: i64=pages*WD_PAGE
54 if limit<end { return 0-11 }
55 wd_static_base_bytes=end
56 if m.n_globals<0 { return 0-1 }
57 if m.n_globals>WD_MAX_BYTES/WD_CELL { return 0-2 }
58 wd_count=m.n_globals
59 if wd_count==0 { wd_static_end_bytes=end; return pages }
60 wd_addresses=sys_mmap(wd_count*WD_CELL) as *i64
61 if (wd_addresses as i64)<=0 { wd_addresses=0 as *i64; return 0-3 }
62 var k: i64=0
63 while k<wd_count {
64 let g: *Global=(m.globals as i64+k*WD_GLOBAL_STRIDE) as *Global
65 if g.len<0 { return 0-4 }
66 let pad: i64=(WD_CELL-end%WD_CELL)%WD_CELL
67 if pad>WD_MAX_BYTES-end { return 0-5 }
68 if pad>limit-end { return 0-11 }
69 end=end+pad; wd_addresses[k]=end
70 if g.len>WD_MAX_BYTES-end { return 0-6 }
71 if g.len>limit-end { return 0-11 }
72 end=end+g.len
73 if g.is_string==1 {
74 if end==WD_MAX_BYTES { return 0-7 }
75 if end==limit { return 0-11 }
76 end=end+1
77 }
78 k=k+1
79 }
80 if shared==1 {
81 let pad: i64=(WD_CELL-end%WD_CELL)%WD_CELL
82 if pad+WD_CELL>WD_MAX_BYTES-end { return 0-8 }
83 if pad+WD_CELL>limit-end { return 0-11 }
84 end=end+pad; wd_init_address=end; end=end+WD_CELL
85 }
86 wd_static_end_bytes=end
87 return end/WD_PAGE + ((end%WD_PAGE+WD_PAGE-1)/WD_PAGE)
88}
89func wd_prepare(m: *Module,pages: i64,shared: i64) -> i64 {
90 var base_pages: i64=pages
91 if wd_arena_pages_flag!=0 { base_pages=wd_arena_pages_flag }
92 if wd_static_limit_pages_flag<0 || wd_static_limit_pages_flag>WD_MAX_BYTES/WD_PAGE { return wd_fail("WAT DATA: static limit pages outside wasm32 extent\n" as *u8) }
93 let result: i64=wd_prepare_checked(m,base_pages,shared,wd_static_limit_pages_flag*WD_PAGE)
94 if result==0-1 { return wd_fail("WAT DATA: negative global count\n" as *u8) }
95 if result==0-2 { return wd_fail("WAT DATA: address table exceeds wasm32 extent\n" as *u8) }
96 if result==0-3 { return wd_fail("WAT DATA: cannot allocate global address table\n" as *u8) }
97 if result==0-4 { return wd_fail("WAT DATA: negative global extent\n" as *u8) }
98 if result==0-5 { return wd_fail("WAT DATA: alignment exceeds wasm32 extent\n" as *u8) }
99 if result==0-6 { return wd_fail("WAT DATA: global exceeds wasm32 extent\n" as *u8) }
100 if result==0-7 { return wd_fail("WAT DATA: string terminator exceeds wasm32 extent\n" as *u8) }
101 if result==0-8 { return wd_fail("WAT DATA: initialization state exceeds wasm32 extent\n" as *u8) }
102 if result==0-10 { return wd_fail("WAT DATA: arena pages outside wasm32 extent\n" as *u8) }
103 if result==0-11 { return wd_fail("WAT DATA: static layout exceeds supplied limit or limit is below arena base\n" as *u8) }
104 // A lower static base never shrinks the engine's independently declared memory minimum.
105 if result<pages { return pages }
106 return result
107}
108func wd_address(id: i64) -> i64 {
109 if id<0 { return wd_fail("WAT DATA: negative global reference\n" as *u8) }
110 if id>=wd_count { return wd_fail("WAT DATA: global reference outside module\n" as *u8) }
111 return wd_addresses[id]
112}
113func wd_hex(o: *OutBuf,v: i64) -> i64 {
114 out_char(o,92)
115 var a: i64=(v>>4)&15
116 var b: i64=v&15
117 if a<10 { out_char(o,48+a) } else { out_char(o,97+a-10) }
118 if b<10 { out_char(o,48+b) } else { out_char(o,97+b-10) }
119 return 0
120}
121func wd_emit(m: *Module,o: *OutBuf) -> i64 {
122 var k: i64=0
123 while k<m.n_globals {
124 let g: *Global=(m.globals as i64+k*WD_GLOBAL_STRIDE) as *Global
125 if g.zero_init==0 {
126 if g.bytes==(0 as *u8) { return wd_fail("WAT DATA: initialized global has no bytes\n" as *u8) }
127 out_str(o," (data (i32.const ")
128 out_i64(o,wd_address(k))
129 out_str(o,") ")
130 out_char(o,34)
131 var j: i64=0
132 while j<g.len { wd_hex(o,g.bytes[j] as i64);j=j+1 }
133 if g.is_string==1 { wd_hex(o,0) }
134 out_char(o,34)
135 out_str(o,")\n")
136 }
137 k=k+1
138 }
139 return 0
140}
141
142func wd_i32(o: *OutBuf,value: i64) -> i64 {
143 var signed: i64=value
144 if value>=WD_MAX_BYTES/2 { signed=value-WD_MAX_BYTES }
145 out_str(o," i32.const ");out_i64(o,signed);out_char(o,10)
146 return 0
147}
148// Passive segments never overwrite shared memory during worker instantiation.
149// The elected initializer publishes READY only after all copies finish.
150func wd_emit_shared(m: *Module,o: *OutBuf) -> i64 {
151 if m.n_globals==0 { return 0 }
152 var k: i64=0
153 while k<m.n_globals {
154 let g: *Global=(m.globals as i64+k*WD_GLOBAL_STRIDE) as *Global
155 if g.zero_init==0 {
156 if g.bytes==(0 as *u8) { return wd_fail("WAT DATA: initialized global has no bytes\n" as *u8) }
157 out_str(o," (data ");out_char(o,34)
158 var j: i64=0
159 while j<g.len { wd_hex(o,g.bytes[j] as i64);j=j+1 }
160 if g.is_string==1 { wd_hex(o,0) }
161 out_char(o,34);out_str(o,")\n")
162 }
163 k=k+1
164 }
165 out_str(o," (func $_nx_data_init (result i64)\n (local $state i64)\n")
166 wd_i32(o,wd_init_address);wd_i32(o,0);wd_i32(o,1)
167 out_str(o," i32.atomic.rmw.cmpxchg\n i64.extend_i32_u\n local.set $state\n local.get $state\n i64.const 0\n i64.eq\n if\n")
168 k=0
169 var segment: i64=0
170 while k<m.n_globals {
171 let g: *Global=(m.globals as i64+k*WD_GLOBAL_STRIDE) as *Global
172 if g.zero_init==0 {
173 wd_i32(o,wd_address(k));wd_i32(o,0)
174 wd_i32(o,g.len+g.is_string)
175 out_str(o," memory.init ");out_i64(o,segment);out_char(o,10)
176 segment=segment+1
177 }
178 k=k+1
179 }
180 wd_i32(o,wd_init_address);wd_i32(o,2)
181 out_str(o," i32.atomic.store\n i64.const 2\n local.set $state\n end\n local.get $state\n )\n (export ")
182 out_char(o,34);out_str(o,"_nx_data_init");out_char(o,34)
183 out_str(o," (func $_nx_data_init))\n")
184 return 0
185}