gofront 1.6.1 → 1.8.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.
Files changed (132) hide show
  1. package/README.md +45 -49
  2. package/package.json +12 -8
  3. package/src/backend/js/expressions.js +1627 -148
  4. package/src/backend/js/fmt-runtime.js +573 -0
  5. package/src/backend/js/index.js +508 -102
  6. package/src/backend/js/runtime.js +1997 -106
  7. package/src/backend/js/statements.js +931 -87
  8. package/src/backend/js/stdlib/bits.js +87 -14
  9. package/src/backend/js/stdlib/builder.js +41 -6
  10. package/src/backend/js/stdlib/bytes.js +253 -7
  11. package/src/backend/js/stdlib/errors.js +7 -1
  12. package/src/backend/js/stdlib/fmt.js +349 -10
  13. package/src/backend/js/stdlib/index.js +19 -2
  14. package/src/backend/js/stdlib/json.js +1167 -0
  15. package/src/backend/js/stdlib/maps.js +0 -8
  16. package/src/backend/js/stdlib/math.js +17 -1
  17. package/src/backend/js/stdlib/simd.js +34 -0
  18. package/src/backend/js/stdlib/slices.js +36 -6
  19. package/src/backend/js/stdlib/strconv.js +10 -5
  20. package/src/backend/js/stdlib/strings.js +83 -4
  21. package/src/backend/js/stdlib/testing.js +7 -0
  22. package/src/backend/js/stdlib/time.js +79 -55
  23. package/src/backend/js/stdlib/unicode.js +1 -1
  24. package/src/backend/js/stdlib/utf8.js +1 -1
  25. package/src/backend/js/templ.js +38 -76
  26. package/src/backend/js/type-info.js +272 -0
  27. package/src/backend/shared/fmt-types.js +188 -0
  28. package/src/backend/wasm/boundary.js +446 -72
  29. package/src/backend/wasm/emit-builtins.js +320 -13
  30. package/src/backend/wasm/emit-exprs.js +1404 -199
  31. package/src/backend/wasm/emit-maps.js +61 -13
  32. package/src/backend/wasm/emit-stmts.js +1359 -164
  33. package/src/backend/wasm/emit.js +518 -74
  34. package/src/backend/wasm/encode.js +128 -13
  35. package/src/backend/wasm/globals.js +452 -0
  36. package/src/backend/wasm/index.js +194 -2489
  37. package/src/backend/wasm/method-expr.js +381 -0
  38. package/src/backend/wasm/module-emitter.js +2240 -0
  39. package/src/backend/wasm/monomorph.js +62 -4
  40. package/src/backend/wasm/optimize.js +4 -2
  41. package/src/backend/wasm/prescan.js +397 -0
  42. package/src/backend/wasm/stdlib/bytes.js +1804 -127
  43. package/src/backend/wasm/stdlib/core.js +1574 -199
  44. package/src/backend/wasm/stdlib/index.js +3 -5
  45. package/src/backend/wasm/stdlib/io.js +1567 -150
  46. package/src/backend/wasm/stdlib/packages.js +2 -0
  47. package/src/backend/wasm/stdlib/simd.js +499 -0
  48. package/src/backend/wasm/stdlib/time.js +1041 -12
  49. package/src/backend/wasm/types.js +221 -15
  50. package/src/backend/wasm/variadic.js +93 -0
  51. package/src/backend/wasm/wat.js +84 -20
  52. package/src/{cli-core.js → cli/cli-core.js} +236 -10
  53. package/src/{test-runner.js → cli/test-runner.js} +332 -39
  54. package/src/{vendor.js → cli/vendor.js} +89 -15
  55. package/src/compiler.js +107 -269
  56. package/src/dts-parser.js +71 -14
  57. package/src/index.js +15 -169
  58. package/src/{lexer.js → lexer/lexer.js} +102 -55
  59. package/src/{tokens.js → lexer/tokens.js} +1 -0
  60. package/src/link/sourcemap.js +97 -0
  61. package/src/link/stdlib.js +515 -0
  62. package/src/link/wasm.js +294 -0
  63. package/src/lower/bounds.js +394 -0
  64. package/src/lower/boxing.js +1 -6
  65. package/src/lower/captures.js +176 -32
  66. package/src/lower/embedding.js +2 -1
  67. package/src/lower/index.js +26 -18
  68. package/src/lower/json-codecs.js +1398 -0
  69. package/src/lower/ownership.js +22 -1
  70. package/src/lower/range.js +86 -1
  71. package/src/lower/reachability.js +132 -0
  72. package/src/parser/declarations.js +58 -13
  73. package/src/parser/expressions.js +29 -7
  74. package/src/parser/index.js +2 -1
  75. package/src/parser/statements.js +37 -22
  76. package/src/parser/struct-tag.js +144 -0
  77. package/src/{templ-parser.js → parser/templ.js} +138 -15
  78. package/src/parser/types.js +130 -13
  79. package/src/resolver.js +22 -1
  80. package/src/typechecker/assignability.js +215 -11
  81. package/src/typechecker/const-fold.js +356 -0
  82. package/src/typechecker/constants.js +256 -0
  83. package/src/typechecker/expressions.js +960 -99
  84. package/src/typechecker/index.js +491 -72
  85. package/src/typechecker/init-order.js +330 -0
  86. package/src/typechecker/resolve.js +596 -52
  87. package/src/typechecker/statements.js +157 -17
  88. package/src/typechecker/stdlib/core.js +333 -55
  89. package/src/typechecker/stdlib/excluded.js +898 -0
  90. package/src/typechecker/stdlib/extended.js +359 -26
  91. package/src/typechecker/{stdlib.js → stdlib/index.js} +6 -4
  92. package/src/typechecker/stdlib/simd.js +123 -0
  93. package/src/typechecker/termination.js +1 -1
  94. package/src/typechecker/types.js +165 -26
  95. package/stdlib/bytes/iter.go +192 -0
  96. package/stdlib/bytes/search.go +240 -0
  97. package/stdlib/bytes/split.go +218 -0
  98. package/stdlib/cmp/cmp.go +65 -0
  99. package/stdlib/encoding/json/errors.go +31 -0
  100. package/stdlib/encoding/json/indent.go +71 -0
  101. package/stdlib/encoding/json/number.go +12 -0
  102. package/stdlib/encoding/json/stream.go +173 -0
  103. package/stdlib/errors/overlay.go +12 -0
  104. package/stdlib/hash/hash.go +53 -0
  105. package/stdlib/internal/jsonrt/dec.go +738 -0
  106. package/stdlib/internal/jsonrt/enc.go +366 -0
  107. package/stdlib/internal/jsonrt/indent.go +135 -0
  108. package/stdlib/internal/jsonrt/number.go +27 -0
  109. package/stdlib/internal/jsonrt/reader.go +73 -0
  110. package/stdlib/internal/jsonrt/scanner.go +649 -0
  111. package/stdlib/io/io.go +346 -0
  112. package/stdlib/iter/iter.go +18 -0
  113. package/stdlib/maps/iter.go +65 -0
  114. package/stdlib/math/extra.go +270 -0
  115. package/stdlib/slices/extra.go +73 -0
  116. package/stdlib/slices/iter.go +113 -0
  117. package/stdlib/sort/sort.go +561 -0
  118. package/stdlib/strconv/quote.go +341 -0
  119. package/stdlib/strings/extra.go +35 -0
  120. package/stdlib/strings/iter.go +122 -0
  121. package/stdlib/sync/extra.go +105 -0
  122. package/stdlib/unicode/unicode.go +112 -0
  123. package/stdlib/unicode/utf8/bytes.go +239 -0
  124. package/src/backend/wasm/const-fold.js +0 -205
  125. package/src/backend/wasm/emit-stdlib.js +0 -13
  126. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  127. /package/src/{colors.js → cli/colors.js} +0 -0
  128. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  129. /package/src/{minifier.js → cli/minifier.js} +0 -0
  130. /package/src/{project-config.js → cli/project-config.js} +0 -0
  131. /package/src/{pwa.js → cli/pwa.js} +0 -0
  132. /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
@@ -96,6 +96,8 @@ export class ExtendedPackagesEmitter {
96
96
  this.pushInstruction("drop");
97
97
  if (method === "TryLock" || method === "TryRLock") {
98
98
  this.pushInstruction({ op: "i32.const", value: 1 });
99
+ } else if (method === "RLocker") {
100
+ this.pushInstruction({ op: "ref.null", type: "any" });
99
101
  }
100
102
  }
101
103
 
@@ -0,0 +1,499 @@
1
+ // src/backend/wasm/stdlib/simd.js
2
+ // FunctionEmitter mixin: gofront/simd (F32x4 vector math, load/store, composite literals).
3
+
4
+ // Methods lowered to one instruction over the receiver and its vector arguments.
5
+ const SIMD_LANEWISE_OPS = {
6
+ Add: "f32x4.add",
7
+ Sub: "f32x4.sub",
8
+ Mul: "f32x4.mul",
9
+ Div: "f32x4.div",
10
+ Min: "f32x4.min",
11
+ Max: "f32x4.max",
12
+ Neg: "f32x4.neg",
13
+ Abs: "f32x4.abs",
14
+ Sqrt: "f32x4.sqrt",
15
+ Floor: "f32x4.floor",
16
+ Ceil: "f32x4.ceil",
17
+ Trunc: "f32x4.trunc",
18
+ RoundToEven: "f32x4.nearest",
19
+ Equal: "f32x4.eq",
20
+ NotEqual: "f32x4.ne",
21
+ Less: "f32x4.lt",
22
+ LessEqual: "f32x4.le",
23
+ Greater: "f32x4.gt",
24
+ GreaterEqual: "f32x4.ge",
25
+ };
26
+
27
+ const SIMD_MASK_OPS = {
28
+ And: "v128.and",
29
+ Or: "v128.or",
30
+ Xor: "v128.xor",
31
+ AndNot: "v128.andnot",
32
+ Not: "v128.not",
33
+ Any: "v128.any_true",
34
+ All: "i32x4.all_true",
35
+ };
36
+
37
+ // Methods with their own emitter: name -> FunctionEmitter method.
38
+ const SIMD_SPECIAL_METHODS = {
39
+ MulAdd: "_emitSimdMulAdd",
40
+ Sum: "_emitSimdSum",
41
+ Swizzle: "_emitSimdSwizzle",
42
+ Dot3: "_emitSimdDot3",
43
+ Dot4: "_emitSimdDot4",
44
+ Lane: "_emitSimdLane",
45
+ WithLane: "_emitSimdWithLane",
46
+ Splat: "_emitSimdSplat",
47
+ };
48
+
49
+ const SIMD_MASK_SPECIAL_METHODS = {
50
+ Bits: "_emitSimdMaskBits",
51
+ Lane: "_emitSimdMaskLane",
52
+ };
53
+
54
+ export class SimdEmitter {
55
+ emitSimdCompositeLit(lit) {
56
+ const elems = lit.elems ?? [];
57
+ if (elems.length === 0) {
58
+ this.pushInstruction({ op: "v128.const", bytes: new Array(16).fill(0) });
59
+ return;
60
+ }
61
+
62
+ const constVals = elems.map((e) => this._getConstFloatValue(e));
63
+ const allConst = constVals.every((v) => v !== null);
64
+
65
+ if (allConst) {
66
+ const vals = [0, 0, 0, 0];
67
+ for (let i = 0; i < Math.min(elems.length, 4); i++) {
68
+ vals[i] = constVals[i];
69
+ }
70
+ this.pushInstruction({ op: "v128.const", values: vals });
71
+ return;
72
+ }
73
+
74
+ // Runtime elements: splat lane 0, replace lanes 1, 2, 3
75
+ this.emitExpr(elems[0], "f32");
76
+ this.pushInstruction("f32x4.splat");
77
+ for (let i = 1; i < 4; i++) {
78
+ if (elems[i]) {
79
+ this.emitExpr(elems[i], "f32");
80
+ } else {
81
+ this.pushInstruction({ op: "f32.const", value: 0.0 });
82
+ }
83
+ this.pushInstruction({ op: "f32x4.replace_lane", lane: i });
84
+ }
85
+ }
86
+
87
+ _emitSimdDot(recvExpr, otherExpr, count) {
88
+ this.emitExpr(recvExpr, "v128");
89
+ this.emitExpr(otherExpr, "v128");
90
+ this.pushInstruction("f32x4.mul");
91
+ const tmp = this.acquireTemp("v128");
92
+ this.pushInstruction({ op: "local.set", index: tmp });
93
+ this.pushInstruction({ op: "local.get", index: tmp });
94
+ this.pushInstruction({ op: "f32x4.extract_lane", lane: 0 });
95
+ for (let i = 1; i < count; i++) {
96
+ this.pushInstruction({ op: "local.get", index: tmp });
97
+ this.pushInstruction({ op: "f32x4.extract_lane", lane: i });
98
+ this.pushInstruction("f32.add");
99
+ }
100
+ this.releaseTemp(tmp, "v128");
101
+ }
102
+
103
+ _isSimdMaskType(goType) {
104
+ return (
105
+ goType?.name === "simd.Mask32x4" ||
106
+ goType?.name === "Mask32x4" ||
107
+ goType?.simd === "Mask32x4" ||
108
+ goType?.underlying?.simd === "Mask32x4"
109
+ );
110
+ }
111
+
112
+ // Lane-wise equality of two vectors on the stack -> i32 (Go array `==`:
113
+ // NaN != NaN, -0 == +0).
114
+ _emitSimdEq(goType) {
115
+ this.pushInstruction(
116
+ this._isSimdMaskType(goType) ? "i32x4.eq" : "f32x4.eq",
117
+ );
118
+ this.pushInstruction("i32x4.all_true");
119
+ }
120
+
121
+ _emitSimdSum(recvExpr) {
122
+ this.emitExpr(recvExpr, "v128");
123
+ const tmp = this.acquireTemp("v128");
124
+ this.pushInstruction({ op: "local.set", index: tmp });
125
+ this.pushInstruction({ op: "local.get", index: tmp });
126
+ this.pushInstruction({ op: "f32x4.extract_lane", lane: 0 });
127
+ for (let i = 1; i < 4; i++) {
128
+ this.pushInstruction({ op: "local.get", index: tmp });
129
+ this.pushInstruction({ op: "f32x4.extract_lane", lane: i });
130
+ this.pushInstruction("f32.add");
131
+ }
132
+ this.releaseTemp(tmp, "v128");
133
+ }
134
+
135
+ // Lane i (0-3 from a, 4-7 from b) as the four byte indices of an i8x16.shuffle.
136
+ _simdShuffleBytes(lanes) {
137
+ const out = [];
138
+ for (const lane of lanes) {
139
+ for (let k = 0; k < 4; k++) out.push(lane * 4 + k);
140
+ }
141
+ return out;
142
+ }
143
+
144
+ _simdLaneConsts(nodes, max) {
145
+ return nodes.map((n) => {
146
+ const v = this._constIntValue(n);
147
+ if (v === null || v < 0 || v > max)
148
+ throw new Error(`simd: lane index must be a constant in [0, ${max}]`);
149
+ return v;
150
+ });
151
+ }
152
+
153
+ _emitSimdSwizzle(recvExpr, args) {
154
+ const lanes = this._simdLaneConsts(args, 3);
155
+ const tmp = this.acquireTemp("v128");
156
+ this.emitExpr(recvExpr, "v128");
157
+ this.pushInstruction({ op: "local.tee", index: tmp });
158
+ this.pushInstruction({ op: "local.get", index: tmp });
159
+ this.pushInstruction({
160
+ op: "i8x16.shuffle",
161
+ lanes: this._simdShuffleBytes(lanes),
162
+ });
163
+ this.releaseTemp(tmp, "v128");
164
+ }
165
+
166
+ _emitSimdSelect(args) {
167
+ const maskTmp = this.acquireTemp("v128");
168
+ this.emitExpr(args[0], "v128");
169
+ this.pushInstruction({ op: "local.set", index: maskTmp });
170
+ this.emitExpr(args[1], "v128");
171
+ this.emitExpr(args[2], "v128");
172
+ this.pushInstruction({ op: "local.get", index: maskTmp });
173
+ this.pushInstruction("v128.bitselect");
174
+ this.releaseTemp(maskTmp, "v128");
175
+ }
176
+
177
+ _emitSimdShuffle(args) {
178
+ const lanes = this._simdLaneConsts(args.slice(2, 6), 7);
179
+ this.emitExpr(args[0], "v128");
180
+ this.emitExpr(args[1], "v128");
181
+ this.pushInstruction({
182
+ op: "i8x16.shuffle",
183
+ lanes: this._simdShuffleBytes(lanes),
184
+ });
185
+ }
186
+
187
+ _emitSimdOp(op, recvExpr, args) {
188
+ this.emitExpr(recvExpr, "v128");
189
+ for (const arg of args) this.emitExpr(arg, "v128");
190
+ this.pushInstruction(op);
191
+ }
192
+
193
+ _emitSimdMaskBits(recvExpr) {
194
+ this._emitSimdOp("i32x4.bitmask", recvExpr, []);
195
+ this.pushInstruction("i64.extend_i32_u");
196
+ }
197
+
198
+ _emitSimdMaskLane(recvExpr, args) {
199
+ const [lane] = this._simdLaneConsts([args[0]], 3);
200
+ this.emitExpr(recvExpr, "v128");
201
+ this.pushInstruction({ op: "i32x4.extract_lane", lane });
202
+ this.pushInstruction({ op: "i32.const", value: 0 });
203
+ this.pushInstruction("i32.ne");
204
+ }
205
+
206
+ _emitSimdMulAdd(recvExpr, args) {
207
+ // Two roundings (mul, then add), not fused.
208
+ this._emitSimdOp("f32x4.mul", recvExpr, [args[0]]);
209
+ this.emitExpr(args[1], "v128");
210
+ this.pushInstruction("f32x4.add");
211
+ }
212
+
213
+ _emitSimdDot3(recvExpr, args) {
214
+ this._emitSimdDot(recvExpr, args[0], 3);
215
+ }
216
+
217
+ _emitSimdDot4(recvExpr, args) {
218
+ this._emitSimdDot(recvExpr, args[0], 4);
219
+ }
220
+
221
+ _emitSimdLane(recvExpr, args) {
222
+ const lane = this._constIntValue(args[0]) ?? 0;
223
+ this.emitExpr(recvExpr, "v128");
224
+ this.pushInstruction({ op: "f32x4.extract_lane", lane });
225
+ }
226
+
227
+ _emitSimdWithLane(recvExpr, args) {
228
+ const lane = this._constIntValue(args[0]) ?? 0;
229
+ this.emitExpr(recvExpr, "v128");
230
+ this.emitExpr(args[1], "f32");
231
+ this.pushInstruction({ op: "f32x4.replace_lane", lane });
232
+ }
233
+
234
+ _emitSimdSplat(_recvExpr, args) {
235
+ this.emitExpr(args[0], "f32");
236
+ this.pushInstruction("f32x4.splat");
237
+ }
238
+
239
+ emitSimdMethodCall(method, recvExpr, args, _targetWasmType, isMask = false) {
240
+ const ops = isMask ? SIMD_MASK_OPS : SIMD_LANEWISE_OPS;
241
+ const specials = isMask ? SIMD_MASK_SPECIAL_METHODS : SIMD_SPECIAL_METHODS;
242
+ if (ops[method]) {
243
+ this._emitSimdOp(ops[method], recvExpr, args);
244
+ return true;
245
+ }
246
+ if (!specials[method]) return false;
247
+ this[specials[method]](recvExpr, args);
248
+ return true;
249
+ }
250
+
251
+ emitSimdPackageCall(fn, args, _targetWasmType) {
252
+ switch (fn) {
253
+ case "Select":
254
+ this._emitSimdSelect(args);
255
+ return true;
256
+ case "Shuffle":
257
+ this._emitSimdShuffle(args);
258
+ return true;
259
+ case "Splat":
260
+ this.emitExpr(args[0], "f32");
261
+ this.pushInstruction("f32x4.splat");
262
+ return true;
263
+ case "NewF32x4":
264
+ this.emitExpr(args[0], "f32");
265
+ this.pushInstruction("f32x4.splat");
266
+ this.emitExpr(args[1], "f32");
267
+ this.pushInstruction({ op: "f32x4.replace_lane", lane: 1 });
268
+ this.emitExpr(args[2], "f32");
269
+ this.pushInstruction({ op: "f32x4.replace_lane", lane: 2 });
270
+ this.emitExpr(args[3], "f32");
271
+ this.pushInstruction({ op: "f32x4.replace_lane", lane: 3 });
272
+ return true;
273
+ case "LoadF32x4":
274
+ this.emitSimdLoad(args[0], args[1]);
275
+ return true;
276
+ case "StoreF32x4":
277
+ this.emitSimdStore(args[0], args[1], args[2]);
278
+ return true;
279
+ case "LoadF32x4Slice":
280
+ this.emitSimdSliceAccess(args[0], args[1], null);
281
+ return true;
282
+ case "StoreF32x4Slice":
283
+ this.emitSimdSliceAccess(args[0], args[1], args[2]);
284
+ return true;
285
+ case "Pad4":
286
+ this._emitSimdPad4(args[0]);
287
+ return true;
288
+ default:
289
+ return false;
290
+ }
291
+ }
292
+
293
+ _emitSharedFloat32Address(bufNode, indexNode) {
294
+ const { wType, typeIndex } = this.mod.getSharedType();
295
+ const bufTmp = this.acquireTemp(wType);
296
+ const iTmp = this.acquireTemp("i32");
297
+
298
+ this.emitExpr(bufNode, wType);
299
+ this.pushInstruction({ op: "local.set", index: bufTmp });
300
+
301
+ // Bounds check 1: if unsigned(len) < 4 branch to OOB
302
+ this.pushInstruction({ op: "local.get", index: bufTmp });
303
+ this.pushInstruction({ op: "struct.get", typeIndex, fieldIndex: 1 });
304
+ this.pushInstruction({ op: "i32.const", value: 4 });
305
+ this.pushInstruction("i32.lt_u");
306
+ this._emitBoundsBranch();
307
+
308
+ if (this._isI32IndexExpr(indexNode)) {
309
+ this.emitExpr(indexNode, "i32");
310
+ this.pushInstruction({ op: "local.set", index: iTmp });
311
+
312
+ // Bounds check: if unsigned(i) > len - 4 branch to OOB
313
+ this.pushInstruction({ op: "local.get", index: iTmp });
314
+ this.pushInstruction({ op: "local.get", index: bufTmp });
315
+ this.pushInstruction({ op: "struct.get", typeIndex, fieldIndex: 1 });
316
+ this.pushInstruction({ op: "i32.const", value: 4 });
317
+ this.pushInstruction("i32.sub");
318
+ this.pushInstruction("i32.gt_u");
319
+ this._emitBoundsBranch();
320
+ } else {
321
+ const idx64 = this.acquireTemp("i64");
322
+ this.emitExpr(indexNode, "i64");
323
+ this.pushInstruction({ op: "local.set", index: idx64 });
324
+
325
+ // Bounds check in 64-bit: if unsigned(idx64) > unsigned(len - 4) branch to OOB
326
+ this.pushInstruction({ op: "local.get", index: idx64 });
327
+ this.pushInstruction({ op: "local.get", index: bufTmp });
328
+ this.pushInstruction({ op: "struct.get", typeIndex, fieldIndex: 1 });
329
+ this.pushInstruction({ op: "i32.const", value: 4 });
330
+ this.pushInstruction("i32.sub");
331
+ this.pushInstruction("i64.extend_i32_u");
332
+ this.pushInstruction("i64.gt_u");
333
+ this._emitBoundsBranch();
334
+
335
+ this.pushInstruction({ op: "local.get", index: idx64 });
336
+ this.pushInstruction("i32.wrap_i64");
337
+ this.pushInstruction({ op: "local.set", index: iTmp });
338
+ this.releaseTemp(idx64, "i64");
339
+ }
340
+
341
+ // Address: buf.base + (i << 2)
342
+ this.pushInstruction({ op: "local.get", index: bufTmp });
343
+ this.pushInstruction({ op: "struct.get", typeIndex, fieldIndex: 0 });
344
+ this.pushInstruction({ op: "local.get", index: iTmp });
345
+ this.pushInstruction({ op: "i32.const", value: 2 });
346
+ this.pushInstruction("i32.shl");
347
+ this.pushInstruction("i32.add");
348
+
349
+ this.releaseTemp(bufTmp, wType);
350
+ this.releaseTemp(iTmp, "i32");
351
+ }
352
+
353
+ emitSimdLoad(bufNode, indexNode) {
354
+ this._emitSharedFloat32Address(bufNode, indexNode);
355
+ this.pushInstruction({ op: "v128.load", align: 2, offset: 0 });
356
+ }
357
+
358
+ emitSimdStore(bufNode, indexNode, valNode) {
359
+ this._emitSharedFloat32Address(bufNode, indexNode);
360
+ this.emitExpr(valNode, "v128");
361
+ this.pushInstruction({ op: "v128.store", align: 2, offset: 0 });
362
+ }
363
+
364
+ // Pad4(n) = (n + 3) &^ 3; a Go int, so always computed as i64.
365
+ _emitSimdPad4(nNode) {
366
+ this.emitExpr(nNode, "i64");
367
+ this.pushInstruction({ op: "i64.const", value: 3n });
368
+ this.pushInstruction("i64.add");
369
+ this.pushInstruction({ op: "i64.const", value: -4n });
370
+ this.pushInstruction("i64.and");
371
+ }
372
+
373
+ // LoadF32x4Slice (valNode null) and StoreF32x4Slice on a []float32: a
374
+ // GC array cannot be read as v128, so each lane is moved on its own.
375
+ emitSimdSliceAccess(sliceNode, indexNode, valNode) {
376
+ const sliceType = sliceNode._type ?? this._resolveExprGoType(sliceNode);
377
+ const elemGoType = this._getArrayElemType(sliceType);
378
+ const arrInfo = this.mod.getArrayType(elemGoType);
379
+ const sliceInfo = this.mod.getSliceType(elemGoType);
380
+ const sTI = sliceInfo.typeIndex;
381
+ const sliceWType = { kind: "ref", nullable: true, typeIndex: sTI };
382
+ const sliceTmp = this.acquireTemp(sliceWType);
383
+ const idx64 = this.acquireTemp("i64");
384
+ const baseTmp = this.acquireTemp("i32");
385
+ const vecTmp = valNode ? this.acquireTemp("v128") : null;
386
+ const lenOf = () => {
387
+ this.pushInstruction({ op: "local.get", index: sliceTmp });
388
+ this.pushInstruction({ op: "struct.get", typeIndex: sTI, fieldIndex: 2 });
389
+ };
390
+
391
+ this.emitExpr(sliceNode, sliceWType);
392
+ this.pushInstruction({ op: "local.set", index: sliceTmp });
393
+ if (this._isI32IndexExpr(indexNode)) {
394
+ this.emitExpr(indexNode, "i32");
395
+ this.pushInstruction("i64.extend_i32_s");
396
+ } else {
397
+ this.emitExpr(indexNode, "i64");
398
+ }
399
+ this.pushInstruction({ op: "local.set", index: idx64 });
400
+ if (valNode) {
401
+ this.emitExpr(valNode, "v128");
402
+ this.pushInstruction({ op: "local.set", index: vecTmp });
403
+ }
404
+
405
+ // Out of range when len < 4 or unsigned(i) > len - 4 (negative i wraps).
406
+ lenOf();
407
+ this.pushInstruction({ op: "i32.const", value: 4 });
408
+ this.pushInstruction("i32.lt_u");
409
+ this._emitBoundsBranch();
410
+ this.pushInstruction({ op: "local.get", index: idx64 });
411
+ lenOf();
412
+ this.pushInstruction({ op: "i32.const", value: 4 });
413
+ this.pushInstruction("i32.sub");
414
+ this.pushInstruction("i64.extend_i32_u");
415
+ this.pushInstruction("i64.gt_u");
416
+ this._emitBoundsBranch();
417
+
418
+ this.pushInstruction({ op: "local.get", index: sliceTmp });
419
+ this.pushInstruction({ op: "struct.get", typeIndex: sTI, fieldIndex: 1 });
420
+ this.pushInstruction({ op: "local.get", index: idx64 });
421
+ this.pushInstruction("i32.wrap_i64");
422
+ this.pushInstruction("i32.add");
423
+ this.pushInstruction({ op: "local.set", index: baseTmp });
424
+
425
+ const elemAt = (lane) => {
426
+ this.pushInstruction({ op: "local.get", index: sliceTmp });
427
+ this.pushInstruction({ op: "struct.get", typeIndex: sTI, fieldIndex: 0 });
428
+ this.pushInstruction({ op: "local.get", index: baseTmp });
429
+ if (lane) {
430
+ this.pushInstruction({ op: "i32.const", value: lane });
431
+ this.pushInstruction("i32.add");
432
+ }
433
+ };
434
+ if (valNode) {
435
+ for (let lane = 0; lane < 4; lane++) {
436
+ elemAt(lane);
437
+ this.pushInstruction({ op: "local.get", index: vecTmp });
438
+ this.pushInstruction({ op: "f32x4.extract_lane", lane });
439
+ this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
440
+ }
441
+ } else {
442
+ elemAt(0);
443
+ this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
444
+ this.pushInstruction("f32x4.splat");
445
+ for (let lane = 1; lane < 4; lane++) {
446
+ elemAt(lane);
447
+ this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
448
+ this.pushInstruction({ op: "f32x4.replace_lane", lane });
449
+ }
450
+ }
451
+ if (vecTmp !== null) this.releaseTemp(vecTmp, "v128");
452
+ this.releaseTemp(baseTmp, "i32");
453
+ this.releaseTemp(idx64, "i64");
454
+ this.releaseTemp(sliceTmp, sliceWType);
455
+ }
456
+
457
+ _constIntValue(node) {
458
+ if (!node) return null;
459
+ if (typeof node._constInt === "number") return node._constInt;
460
+ if (node.kind === "BasicLit" && node.litKind === "INT") {
461
+ return Number(node.value);
462
+ }
463
+ if (node.kind === "Ident") {
464
+ const c = this.mod?.resolveConst?.(node.name);
465
+ if (c && c.kind === "BasicLit") {
466
+ return Number(c.value);
467
+ }
468
+ }
469
+ if (node.kind === "ParenExpr") {
470
+ return this._constIntValue(node.expr);
471
+ }
472
+ if (node.kind === "UnaryExpr" && node.op === "+") {
473
+ return this._constIntValue(node.operand);
474
+ }
475
+ return null;
476
+ }
477
+
478
+ _getConstFloatValue(e) {
479
+ if (!e) return null;
480
+ if (
481
+ e.kind === "BasicLit" &&
482
+ (e.litKind === "FLOAT" || e.litKind === "INT")
483
+ ) {
484
+ return Number(e.value);
485
+ }
486
+ if (e.kind === "UnaryExpr" && (e.op === "-" || e.op === "+")) {
487
+ const v = this._getConstFloatValue(e.operand);
488
+ return v !== null ? (e.op === "-" ? -v : v) : null;
489
+ }
490
+ if (e.kind === "Ident") {
491
+ const c = this.mod?.resolveConst?.(e.name);
492
+ if (c) return this._getConstFloatValue(c);
493
+ }
494
+ if (e.kind === "ParenExpr") {
495
+ return this._getConstFloatValue(e.expr);
496
+ }
497
+ return null;
498
+ }
499
+ }