code wiki / (root) / nx_wasm_data.nx

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}