gofront 1.7.0 → 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.
- package/README.md +13 -13
- package/package.json +8 -37
- package/src/backend/js/expressions.js +1341 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +375 -57
- package/src/backend/js/runtime.js +1460 -133
- package/src/backend/js/statements.js +900 -86
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +2 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +8 -3
- package/src/backend/js/stdlib/json.js +247 -52
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +10 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +436 -80
- package/src/backend/wasm/emit-builtins.js +303 -13
- package/src/backend/wasm/emit-exprs.js +1397 -203
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +512 -77
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +0 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +200 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -20
- package/src/{cli-core.js → cli/cli-core.js} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +107 -269
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +176 -32
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +69 -9
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +176 -17
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +931 -122
- package/src/typechecker/index.js +459 -67
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +451 -56
- package/src/typechecker/statements.js +90 -9
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +322 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +149 -25
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
- /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
|
@@ -1,21 +1,51 @@
|
|
|
1
1
|
// src/backend/wasm/emit-exprs.js
|
|
2
2
|
// FunctionEmitter mixin: expression emission (literals, operators, indexing, conversions).
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
constFromLit,
|
|
6
|
+
constIntValue,
|
|
7
|
+
constToLit,
|
|
8
|
+
foldConstExpr,
|
|
9
|
+
} from "../../typechecker/const-fold.js";
|
|
10
|
+
import {
|
|
11
|
+
compositeLitIndices,
|
|
12
|
+
compositeLitMaxIndex,
|
|
13
|
+
} from "../../typechecker/types.js";
|
|
5
14
|
import {
|
|
6
15
|
getMapKeyValTypes,
|
|
7
16
|
isAnyType,
|
|
8
17
|
isArrayType,
|
|
18
|
+
isComplex,
|
|
19
|
+
isComplex64,
|
|
9
20
|
isFuncType,
|
|
10
21
|
isMapType,
|
|
11
22
|
isPointerToStruct,
|
|
23
|
+
isRuneSlice,
|
|
24
|
+
isScalarType,
|
|
12
25
|
isSigned,
|
|
13
26
|
isSliceType,
|
|
14
27
|
isStringType,
|
|
15
28
|
isStructType,
|
|
29
|
+
isTestingT,
|
|
16
30
|
toWasmType,
|
|
17
31
|
} from "./types.js";
|
|
18
32
|
|
|
33
|
+
// Engine limit on `array.new_fixed` operands.
|
|
34
|
+
const MAX_NEW_FIXED = 10000;
|
|
35
|
+
|
|
36
|
+
// An untyped integral float constant (`1e18 + 1`) in an integer context is
|
|
37
|
+
// emitted from its exact integer value.
|
|
38
|
+
function constLitFor(val, targetWasmType) {
|
|
39
|
+
const i = constIntValue(val);
|
|
40
|
+
if (
|
|
41
|
+
val.k === "FLOAT" &&
|
|
42
|
+
i !== null &&
|
|
43
|
+
(targetWasmType === "i64" || targetWasmType === "i32")
|
|
44
|
+
)
|
|
45
|
+
return constToLit({ k: "INT", v: i });
|
|
46
|
+
return constToLit(val);
|
|
47
|
+
}
|
|
48
|
+
|
|
19
49
|
export class ExprsEmitter {
|
|
20
50
|
emitExpr(expr, targetWasmType = null) {
|
|
21
51
|
if (!expr) return;
|
|
@@ -36,7 +66,7 @@ export class ExprsEmitter {
|
|
|
36
66
|
}
|
|
37
67
|
|
|
38
68
|
if (
|
|
39
|
-
isStringType(goType, this.mod.checker) ||
|
|
69
|
+
(isStringType(goType, this.mod.checker) && goType?.kind !== "named") ||
|
|
40
70
|
(expr.kind === "BasicLit" && expr.litKind === "STRING")
|
|
41
71
|
) {
|
|
42
72
|
this._emitRawExpr(expr, "externref");
|
|
@@ -50,6 +80,7 @@ export class ExprsEmitter {
|
|
|
50
80
|
// for type switches and assertions.
|
|
51
81
|
if (isPointerToStruct(goType, this.mod.checker, this.mod)) {
|
|
52
82
|
this._emitRawExpr(expr, this.toWasmType(goType));
|
|
83
|
+
this._emitNilAwareAny(goType, expr);
|
|
53
84
|
return;
|
|
54
85
|
}
|
|
55
86
|
if (isStructType(goType, this.mod.checker, this.mod)) {
|
|
@@ -78,6 +109,14 @@ export class ExprsEmitter {
|
|
|
78
109
|
) {
|
|
79
110
|
const refWType = this.toWasmType(goType);
|
|
80
111
|
this._emitRawExpr(expr, refWType);
|
|
112
|
+
// An array stored in an interface is a copy.
|
|
113
|
+
if (isArrayType(goType, this.mod.checker))
|
|
114
|
+
this._emitCopyIfValue(expr, goType, refWType);
|
|
115
|
+
if (
|
|
116
|
+
isFuncType(goType, this.mod.checker) ||
|
|
117
|
+
isMapType(goType, this.mod.checker)
|
|
118
|
+
)
|
|
119
|
+
this._emitNilAwareAny(goType, expr);
|
|
81
120
|
return;
|
|
82
121
|
}
|
|
83
122
|
|
|
@@ -92,6 +131,23 @@ export class ExprsEmitter {
|
|
|
92
131
|
this._emitRawExpr(expr, targetWasmType);
|
|
93
132
|
}
|
|
94
133
|
|
|
134
|
+
// The nullable ref on the stack becomes an interface value: a nil map, func
|
|
135
|
+
// or pointer turns into the marker of its type (never a nil interface).
|
|
136
|
+
_emitNilAwareAny(goType, expr) {
|
|
137
|
+
if (expr.kind === "UnaryExpr" && expr.op === "&") return;
|
|
138
|
+
if (expr.kind === "CompositeLit" || expr.kind === "FuncLit") return;
|
|
139
|
+
const nilBox = this.mod.getNilBoxType(goType);
|
|
140
|
+
const tmp = this.acquireTemp(this.toWasmType(goType));
|
|
141
|
+
this.pushInstruction({ op: "local.tee", index: tmp });
|
|
142
|
+
this.pushInstruction("ref.is_null");
|
|
143
|
+
this.pushInstruction({ op: "if", blockType: "anyref" });
|
|
144
|
+
this.pushInstruction({ op: "global.get", index: nilBox.globalIndex });
|
|
145
|
+
this.pushInstruction("else");
|
|
146
|
+
this.pushInstruction({ op: "local.get", index: tmp });
|
|
147
|
+
this.pushInstruction("end");
|
|
148
|
+
this.releaseTemp(tmp, this.toWasmType(goType));
|
|
149
|
+
}
|
|
150
|
+
|
|
95
151
|
_emitBoxValue(box) {
|
|
96
152
|
this.pushInstruction({
|
|
97
153
|
op: "ref.null",
|
|
@@ -116,6 +172,9 @@ export class ExprsEmitter {
|
|
|
116
172
|
return { kind: "nil", name: "nil" };
|
|
117
173
|
}
|
|
118
174
|
}
|
|
175
|
+
if (expr.kind === "ImagLit") {
|
|
176
|
+
return { kind: "untyped", base: "complex128" };
|
|
177
|
+
}
|
|
119
178
|
if (expr.kind === "Ident") {
|
|
120
179
|
const local = this.resolveLocal(expr.name);
|
|
121
180
|
if (local) return local.goType;
|
|
@@ -128,23 +187,46 @@ export class ExprsEmitter {
|
|
|
128
187
|
return null;
|
|
129
188
|
}
|
|
130
189
|
|
|
190
|
+
_emitLocalConst(ident, targetWasmType) {
|
|
191
|
+
const lit = constLitFor(ident._const, targetWasmType);
|
|
192
|
+
if (!lit) return false;
|
|
193
|
+
lit._type = ident._type;
|
|
194
|
+
this.emitBasicLit(lit, targetWasmType);
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
|
|
131
198
|
_emitRawExpr(expr, targetWasmType = null) {
|
|
132
199
|
if (expr.kind === "BinaryExpr" || expr.kind === "UnaryExpr") {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
200
|
+
// The checker's exact value (src/typechecker/const-fold.js) when it has one.
|
|
201
|
+
const folded = expr._const
|
|
202
|
+
? constLitFor(expr._const, targetWasmType)
|
|
203
|
+
: foldConstExpr(expr, (name) => this.mod.pkgConsts?.get(name));
|
|
136
204
|
if (folded) {
|
|
137
205
|
folded._type = expr._type;
|
|
138
206
|
this.emitBasicLit(folded, targetWasmType);
|
|
139
207
|
return;
|
|
140
208
|
}
|
|
141
209
|
}
|
|
210
|
+
// A function-local constant is a local slot (so closures can capture it)
|
|
211
|
+
// but reads as the checker's exact value, which adapts to the context.
|
|
212
|
+
if (
|
|
213
|
+
expr.kind === "Ident" &&
|
|
214
|
+
expr._const &&
|
|
215
|
+
this.resolveLocal(expr.name) &&
|
|
216
|
+
this._emitLocalConst(expr, targetWasmType)
|
|
217
|
+
)
|
|
218
|
+
return;
|
|
219
|
+
if (this._emitFoldedIntConversion(expr, targetWasmType)) return;
|
|
142
220
|
|
|
143
221
|
switch (expr.kind) {
|
|
144
222
|
case "BasicLit":
|
|
145
223
|
this.emitBasicLit(expr, targetWasmType);
|
|
146
224
|
break;
|
|
147
225
|
|
|
226
|
+
case "ImagLit":
|
|
227
|
+
this.emitImagLit(expr, targetWasmType);
|
|
228
|
+
break;
|
|
229
|
+
|
|
148
230
|
case "Ident":
|
|
149
231
|
this.emitIdent(expr, targetWasmType);
|
|
150
232
|
break;
|
|
@@ -172,20 +254,7 @@ export class ExprsEmitter {
|
|
|
172
254
|
case "SelectorExpr": {
|
|
173
255
|
const deq = this._dequalify(expr);
|
|
174
256
|
if (deq) this.emitIdent(deq, targetWasmType);
|
|
175
|
-
else if (this.
|
|
176
|
-
this.emitMathConst(expr.field, targetWasmType);
|
|
177
|
-
else if (this._isUtf8Const(expr))
|
|
178
|
-
this.emitUtf8Const(expr.field, targetWasmType);
|
|
179
|
-
else if (this._isStrconvConst(expr))
|
|
180
|
-
this.emitStrconvConst(expr.field, targetWasmType);
|
|
181
|
-
else if (this._isPathConst(expr))
|
|
182
|
-
this.emitPathConst(expr.field, targetWasmType);
|
|
183
|
-
else if (this._isIoConst(expr))
|
|
184
|
-
this.emitIoConst(expr.field, targetWasmType);
|
|
185
|
-
else if (this._isTimeConst(expr))
|
|
186
|
-
this.emitTimeConst(expr.field, targetWasmType);
|
|
187
|
-
else if (this._isExtendedPkgConst?.(expr))
|
|
188
|
-
this.emitExtendedPkgConst(expr, targetWasmType);
|
|
257
|
+
else if (this._emitPkgConst(expr, targetWasmType)) break;
|
|
189
258
|
else this.emitSelectorExpr(expr);
|
|
190
259
|
break;
|
|
191
260
|
}
|
|
@@ -302,7 +371,7 @@ export class ExprsEmitter {
|
|
|
302
371
|
this.pushInstruction({ op: "local.get", index: mTmp });
|
|
303
372
|
this.emitExpr(elem.key, mapInfo.keyWType);
|
|
304
373
|
this.emitExpr(elem.value, mapInfo.valWType);
|
|
305
|
-
this.
|
|
374
|
+
this._emitCopyIfValue(elem.value, valType, mapInfo.valWType);
|
|
306
375
|
this.pushInstruction({
|
|
307
376
|
op: "call",
|
|
308
377
|
funcIndex: this.mod.resolveFuncIndex(mapInfo.setFuncName),
|
|
@@ -315,33 +384,11 @@ export class ExprsEmitter {
|
|
|
315
384
|
}
|
|
316
385
|
|
|
317
386
|
if (isSliceType(litType, this.mod.checker)) {
|
|
318
|
-
const elemType = litType
|
|
387
|
+
const elemType = this._getArrayElemType(litType);
|
|
319
388
|
const sliceInfo = this.mod.getSliceType(elemType);
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
this.pushInstruction({
|
|
324
|
-
op: "array.new_default",
|
|
325
|
-
typeIndex: sliceInfo.arrInfo.typeIndex,
|
|
326
|
-
});
|
|
327
|
-
this.pushInstruction({ op: "i32.const", value: 0 }); // offset
|
|
328
|
-
this.pushInstruction({ op: "i32.const", value: 0 }); // len
|
|
329
|
-
this.pushInstruction({ op: "i32.const", value: 0 }); // cap
|
|
330
|
-
this.pushInstruction({
|
|
331
|
-
op: "struct.new",
|
|
332
|
-
typeIndex: sliceInfo.typeIndex,
|
|
333
|
-
});
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
for (const elem of lit.elems) {
|
|
337
|
-
this.emitExpr(elem, sliceInfo.elemWType);
|
|
338
|
-
this._emitCopyIfValueStruct(elem, elem._type, sliceInfo.elemWType);
|
|
339
|
-
}
|
|
340
|
-
this.pushInstruction({
|
|
341
|
-
op: "array.new_fixed",
|
|
342
|
-
typeIndex: sliceInfo.arrInfo.typeIndex,
|
|
343
|
-
size: count,
|
|
344
|
-
});
|
|
389
|
+
const elems = lit.elems ?? [];
|
|
390
|
+
const count = compositeLitMaxIndex(elems) + 1;
|
|
391
|
+
this._emitArrayLitElems(elems, elemType, sliceInfo.arrInfo, count);
|
|
345
392
|
this.pushInstruction({ op: "i32.const", value: 0 }); // offset
|
|
346
393
|
this.pushInstruction({ op: "i32.const", value: count }); // len
|
|
347
394
|
this.pushInstruction({ op: "i32.const", value: count }); // cap
|
|
@@ -353,22 +400,12 @@ export class ExprsEmitter {
|
|
|
353
400
|
}
|
|
354
401
|
|
|
355
402
|
if (isArrayType(litType, this.mod.checker)) {
|
|
356
|
-
const elemType = litType
|
|
403
|
+
const elemType = this._getArrayElemType(litType);
|
|
357
404
|
const arrInfo = this.mod.getArrayType(elemType);
|
|
358
|
-
const
|
|
405
|
+
const elems = lit.elems ?? [];
|
|
406
|
+
const count = compositeLitMaxIndex(elems) + 1;
|
|
359
407
|
const targetSize = this._getArraySize(litType, count);
|
|
360
|
-
|
|
361
|
-
this.emitExpr(elem, arrInfo.elemWType);
|
|
362
|
-
this._emitCopyIfValueStruct(elem, elem._type, arrInfo.elemWType);
|
|
363
|
-
}
|
|
364
|
-
for (let i = count; i < targetSize; i++) {
|
|
365
|
-
this.emitZeroValue(elemType, arrInfo.elemWType);
|
|
366
|
-
}
|
|
367
|
-
this.pushInstruction({
|
|
368
|
-
op: "array.new_fixed",
|
|
369
|
-
typeIndex: arrInfo.typeIndex,
|
|
370
|
-
size: targetSize,
|
|
371
|
-
});
|
|
408
|
+
this._emitArrayLitElems(elems, elemType, arrInfo, targetSize);
|
|
372
409
|
return;
|
|
373
410
|
}
|
|
374
411
|
|
|
@@ -398,13 +435,13 @@ export class ExprsEmitter {
|
|
|
398
435
|
const f = fields[i];
|
|
399
436
|
if (isKeyed) {
|
|
400
437
|
if (map.has(f.name)) {
|
|
401
|
-
this.
|
|
438
|
+
this._emitFieldInit(map.get(f.name), f);
|
|
402
439
|
} else {
|
|
403
440
|
this.emitZeroValue(f.goType, f.wType);
|
|
404
441
|
}
|
|
405
442
|
} else {
|
|
406
443
|
if (i < (lit.elems?.length ?? 0)) {
|
|
407
|
-
this.
|
|
444
|
+
this._emitFieldInit(lit.elems[i], f);
|
|
408
445
|
} else {
|
|
409
446
|
this.emitZeroValue(f.goType, f.wType);
|
|
410
447
|
}
|
|
@@ -417,9 +454,169 @@ export class ExprsEmitter {
|
|
|
417
454
|
});
|
|
418
455
|
}
|
|
419
456
|
|
|
457
|
+
// Pushes a new array of `size` elements built from literal `elems`.
|
|
458
|
+
// Keyed elements (`{2: 7}`) go to their index; every other slot is the
|
|
459
|
+
// element zero value. With ascending keys and at most
|
|
460
|
+
// MAX_NEW_FIXED elements this is one `array.new_fixed`; otherwise the
|
|
461
|
+
// array is allocated first and the elements are stored in source order
|
|
462
|
+
// (Go evaluates them left to right whatever their keys).
|
|
463
|
+
_emitArrayLitElems(elems, elemType, arrInfo, size) {
|
|
464
|
+
const indices = compositeLitIndices(elems);
|
|
465
|
+
const ascending = indices.every((v, i) => i === 0 || v > indices[i - 1]);
|
|
466
|
+
const value = (e) => (e.kind === "KeyValueExpr" ? e.value : e);
|
|
467
|
+
const emitElem = (e) => {
|
|
468
|
+
this.emitExpr(e, arrInfo.elemWType);
|
|
469
|
+
// Copy by the element type: a struct stored in an `any` element
|
|
470
|
+
// is already boxed, and a keyed element wraps its value.
|
|
471
|
+
this._emitCopyIfValue(e, elemType, arrInfo.elemWType);
|
|
472
|
+
};
|
|
473
|
+
if (ascending && size <= MAX_NEW_FIXED) {
|
|
474
|
+
const slots = new Array(size).fill(null);
|
|
475
|
+
elems.forEach((e, i) => {
|
|
476
|
+
slots[indices[i]] = value(e);
|
|
477
|
+
});
|
|
478
|
+
for (const e of slots) {
|
|
479
|
+
if (e) emitElem(e);
|
|
480
|
+
else this.emitZeroValue(elemType, arrInfo.elemWType);
|
|
481
|
+
}
|
|
482
|
+
this.pushInstruction({
|
|
483
|
+
op: "array.new_fixed",
|
|
484
|
+
typeIndex: arrInfo.typeIndex,
|
|
485
|
+
size,
|
|
486
|
+
});
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const wType = { kind: "ref", nullable: true, typeIndex: arrInfo.typeIndex };
|
|
490
|
+
const arr = this.acquireTemp(wType);
|
|
491
|
+
const set = (idx, emit) => {
|
|
492
|
+
this.pushInstruction({ op: "local.get", index: arr });
|
|
493
|
+
this.pushInstruction({ op: "i32.const", value: idx });
|
|
494
|
+
emit();
|
|
495
|
+
this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
|
|
496
|
+
};
|
|
497
|
+
this.pushInstruction({ op: "i32.const", value: size });
|
|
498
|
+
this.pushInstruction({
|
|
499
|
+
op: "array.new_default",
|
|
500
|
+
typeIndex: arrInfo.typeIndex,
|
|
501
|
+
});
|
|
502
|
+
this.pushInstruction({ op: "local.set", index: arr });
|
|
503
|
+
// Numeric zero values are the array default; others are stored.
|
|
504
|
+
if (typeof arrInfo.elemWType !== "string") {
|
|
505
|
+
const given = new Set(indices);
|
|
506
|
+
for (let i = 0; i < size; i++)
|
|
507
|
+
if (!given.has(i))
|
|
508
|
+
set(i, () => this.emitZeroValue(elemType, arrInfo.elemWType));
|
|
509
|
+
}
|
|
510
|
+
elems.forEach((e, i) => {
|
|
511
|
+
set(indices[i], () => emitElem(value(e)));
|
|
512
|
+
});
|
|
513
|
+
this.pushInstruction({ op: "local.get", index: arr });
|
|
514
|
+
this.releaseTemp(arr, wType);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// A struct literal field initialiser; array values are copied in.
|
|
518
|
+
_emitFieldInit(value, f) {
|
|
519
|
+
this.emitExpr(value, f.wType);
|
|
520
|
+
if (f.goType?.kind !== "pointer" && isArrayType(f.goType, this.mod.checker))
|
|
521
|
+
this._emitCopyIfValue(value, f.goType, f.wType);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// With a pointer on the stack, branches to the function's `nil` block
|
|
525
|
+
// (_openNilBlock) when it is null. Outside one, the access traps.
|
|
526
|
+
_emitNilCheck(goType) {
|
|
527
|
+
if (goType?.kind !== "pointer") return;
|
|
528
|
+
this._emitNilBranch();
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
_emitNilBranch() {
|
|
532
|
+
const depth = this.resolveBranchDepthToRole("nil");
|
|
533
|
+
if (depth == null) return;
|
|
534
|
+
this.pushInstruction({ op: "br_on_null", depth });
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Emits the struct (or struct pointer) a field access or store goes
|
|
538
|
+
// through, nil-checked. A `*p` base is the pointee itself, not the copy a
|
|
539
|
+
// value read of `*p` makes, so a store through it lands in the pointee.
|
|
540
|
+
// Every selector read and every field store/op-assign/inc-dec starts here.
|
|
541
|
+
emitSelectorBase(node, wType = null, goType = node._type) {
|
|
542
|
+
const pointer = this._structDerefPointer(node);
|
|
543
|
+
if (pointer) {
|
|
544
|
+
this.emitSelectorBase(pointer);
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
this.emitExpr(node, wType ?? this.toWasmType(goType));
|
|
548
|
+
this._emitNilCheck(goType);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// For `*p` (possibly parenthesised) with `p` pointing at a struct, the
|
|
552
|
+
// pointer `p`, which is the struct's own reference; otherwise null.
|
|
553
|
+
_structDerefPointer(node) {
|
|
554
|
+
let base = node;
|
|
555
|
+
while (base?.kind === "ParenExpr") base = base.expr;
|
|
556
|
+
if (
|
|
557
|
+
(base?.kind !== "UnaryExpr" || base.op !== "*") &&
|
|
558
|
+
base?.kind !== "StarExpr"
|
|
559
|
+
)
|
|
560
|
+
return null;
|
|
561
|
+
const operand = base.operand ?? base.expr;
|
|
562
|
+
return this._derefStructInfo(operand) ? operand : null;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
_emitPkgConst(expr, targetWasmType) {
|
|
566
|
+
if (this._isMathConst(expr)) {
|
|
567
|
+
this.emitMathConst(expr.field, targetWasmType);
|
|
568
|
+
return true;
|
|
569
|
+
}
|
|
570
|
+
if (this._isUtf8Const(expr)) {
|
|
571
|
+
this.emitUtf8Const(expr.field, targetWasmType);
|
|
572
|
+
return true;
|
|
573
|
+
}
|
|
574
|
+
if (this._isStrconvConst(expr)) {
|
|
575
|
+
this.emitStrconvConst(expr.field, targetWasmType);
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
if (this._isPathConst(expr)) {
|
|
579
|
+
this.emitPathConst(expr.field, targetWasmType);
|
|
580
|
+
return true;
|
|
581
|
+
}
|
|
582
|
+
if (this._isIoConst(expr)) {
|
|
583
|
+
this.emitIoConst(expr.field, targetWasmType);
|
|
584
|
+
return true;
|
|
585
|
+
}
|
|
586
|
+
if (this._isOsConst?.(expr)) {
|
|
587
|
+
this.emitOsConst(expr.field, targetWasmType);
|
|
588
|
+
return true;
|
|
589
|
+
}
|
|
590
|
+
if (this._isTimeConst(expr)) {
|
|
591
|
+
this.emitTimeConst(expr.field, targetWasmType);
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
if (this._isBitsConst?.(expr)) {
|
|
595
|
+
this.emitBitsConst(expr.field, targetWasmType);
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
if (this._isExtendedPkgConst?.(expr)) {
|
|
599
|
+
this.emitExtendedPkgConst(expr, targetWasmType);
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
604
|
+
|
|
420
605
|
emitSelectorExpr(expr) {
|
|
606
|
+
if (isTestingT(expr.expr?._type) && expr.field === "N") {
|
|
607
|
+
this.emitExpr(expr.expr, "externref");
|
|
608
|
+
this.pushInstruction({
|
|
609
|
+
op: "call",
|
|
610
|
+
funcIndex: this.mod.getStdlibImportIndex("testing_n"),
|
|
611
|
+
});
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
421
614
|
const structInfo = this._resolveStructInfo(expr.expr);
|
|
422
615
|
if (!structInfo) {
|
|
616
|
+
if (expr.expr?.kind === "Ident" && expr.expr._type?.kind === "namespace")
|
|
617
|
+
throw new Error(
|
|
618
|
+
`${expr.expr.name}.${expr.field} cannot be used as a value in WASM`,
|
|
619
|
+
);
|
|
423
620
|
throw new Error(`Cannot resolve struct for selector: ${expr.field}`);
|
|
424
621
|
}
|
|
425
622
|
const path = this._resolveFieldPath(structInfo, expr.field);
|
|
@@ -429,8 +626,7 @@ export class ExprsEmitter {
|
|
|
429
626
|
);
|
|
430
627
|
}
|
|
431
628
|
|
|
432
|
-
|
|
433
|
-
this.emitExpr(expr.expr, baseWType);
|
|
629
|
+
this.emitSelectorBase(expr.expr);
|
|
434
630
|
|
|
435
631
|
for (let i = 0; i < path.length; i++) {
|
|
436
632
|
const step = path[i];
|
|
@@ -506,11 +702,9 @@ export class ExprsEmitter {
|
|
|
506
702
|
);
|
|
507
703
|
}
|
|
508
704
|
|
|
509
|
-
const elemGoType =
|
|
510
|
-
? baseType.
|
|
511
|
-
|
|
512
|
-
? baseType.base.elem
|
|
513
|
-
: baseType.elem;
|
|
705
|
+
const elemGoType = this._getArrayElemType(
|
|
706
|
+
isPtrToArr ? baseType.base : baseType,
|
|
707
|
+
);
|
|
514
708
|
const arrInfo = this.mod.getArrayType(elemGoType);
|
|
515
709
|
const sliceInfo = isSlice ? this.mod.getSliceType(elemGoType) : null;
|
|
516
710
|
|
|
@@ -604,11 +798,9 @@ export class ExprsEmitter {
|
|
|
604
798
|
);
|
|
605
799
|
}
|
|
606
800
|
|
|
607
|
-
const elemGoType =
|
|
608
|
-
? baseType.
|
|
609
|
-
|
|
610
|
-
? baseType.base.elem
|
|
611
|
-
: baseType.elem;
|
|
801
|
+
const elemGoType = this._getArrayElemType(
|
|
802
|
+
isPtrToArr ? baseType.base : baseType,
|
|
803
|
+
);
|
|
612
804
|
const arrInfo = this.mod.getArrayType(elemGoType);
|
|
613
805
|
const sliceInfo = this.mod.getSliceType(elemGoType);
|
|
614
806
|
|
|
@@ -757,8 +949,11 @@ export class ExprsEmitter {
|
|
|
757
949
|
const isValStruct =
|
|
758
950
|
isStructType(elemGoType, this.mod.checker, this.mod) &&
|
|
759
951
|
!isPointerToStruct(elemGoType, this.mod.checker, this.mod);
|
|
952
|
+
const isValArray =
|
|
953
|
+
elemGoType?.kind !== "pointer" &&
|
|
954
|
+
isArrayType(elemGoType, this.mod.checker);
|
|
760
955
|
|
|
761
|
-
if (!isValStruct) {
|
|
956
|
+
if (!isValStruct && !isValArray) {
|
|
762
957
|
this.pushInstruction({
|
|
763
958
|
op: "array.copy",
|
|
764
959
|
typeIndexDst: arrInfo.typeIndex,
|
|
@@ -831,7 +1026,9 @@ export class ExprsEmitter {
|
|
|
831
1026
|
this.pushInstruction("i32.add");
|
|
832
1027
|
this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
|
|
833
1028
|
|
|
834
|
-
if (
|
|
1029
|
+
if (isValArray) {
|
|
1030
|
+
this.emitCopyArray(elemGoType);
|
|
1031
|
+
} else if (sInfo) {
|
|
835
1032
|
this.emitCloneStruct(sInfo, arrInfo.elemWType);
|
|
836
1033
|
}
|
|
837
1034
|
|
|
@@ -869,7 +1066,9 @@ export class ExprsEmitter {
|
|
|
869
1066
|
this.pushInstruction("i32.add");
|
|
870
1067
|
this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
|
|
871
1068
|
|
|
872
|
-
if (
|
|
1069
|
+
if (isValArray) {
|
|
1070
|
+
this.emitCopyArray(elemGoType);
|
|
1071
|
+
} else if (sInfo) {
|
|
873
1072
|
this.emitCloneStruct(sInfo, arrInfo.elemWType);
|
|
874
1073
|
}
|
|
875
1074
|
|
|
@@ -942,53 +1141,95 @@ export class ExprsEmitter {
|
|
|
942
1141
|
return this.toWasmType(expr._type) === "i32";
|
|
943
1142
|
}
|
|
944
1143
|
|
|
1144
|
+
// Panics with the generic out-of-range message (shared buffers, SIMD
|
|
1145
|
+
// loads) when the condition on the stack is true.
|
|
945
1146
|
_emitBoundsBranch() {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1147
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1148
|
+
this.pushInstruction({
|
|
1149
|
+
op: "call",
|
|
1150
|
+
funcIndex: this.mod.getBoundsPanicFuncIndex(),
|
|
1151
|
+
});
|
|
1152
|
+
this.pushInstruction("unreachable");
|
|
1153
|
+
this.pushInstruction("end");
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// If the condition on the stack is true, panics with Go's index message;
|
|
1157
|
+
// `pushIdx64` and `emitLen` push the i64 index and the i32 length.
|
|
1158
|
+
_emitIndexPanicIf(pushIdx64, emitLen) {
|
|
1159
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1160
|
+
pushIdx64();
|
|
1161
|
+
emitLen();
|
|
1162
|
+
this.pushInstruction({
|
|
1163
|
+
op: "call",
|
|
1164
|
+
funcIndex: this.mod.getIndexPanicFuncIndex(),
|
|
1165
|
+
});
|
|
1166
|
+
this.pushInstruction("unreachable");
|
|
1167
|
+
this.pushInstruction("end");
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// Registers an index-check site and pushes its id: a failing check
|
|
1171
|
+
// carries the index and this id to the handler, which re-emits `emitLen`
|
|
1172
|
+
// for that site (its temps still hold the operand at the branch).
|
|
1173
|
+
_pushOobSite(emitLen) {
|
|
1174
|
+
this.oobSites ??= [];
|
|
1175
|
+
this.oobSites.push(emitLen);
|
|
1176
|
+
this.pushInstruction({ op: "i32.const", value: this.oobSites.length - 1 });
|
|
958
1177
|
}
|
|
959
1178
|
|
|
960
1179
|
// Pushes the bounds-checked i32 index onto the stack. `emitLen` must push
|
|
961
|
-
// the i32 length.
|
|
962
|
-
//
|
|
1180
|
+
// the i32 length. Inside a function body a failing check branches to the
|
|
1181
|
+
// shared handler (`_closeOobBlocks`) with the index and a site id, and the
|
|
1182
|
+
// handler reports Go's message; elsewhere the check panics inline.
|
|
963
1183
|
_emitCheckedIndex(indexNode, emitLen) {
|
|
964
|
-
const
|
|
965
|
-
(indexNode.kind === "BasicLit" && indexNode.litKind === "INT") ||
|
|
966
|
-
(indexNode.kind === "Ident" && this.resolveLocal(indexNode.name));
|
|
1184
|
+
const depth32 = this.resolveBranchDepthToOob(32);
|
|
967
1185
|
if (this._isI32IndexExpr(indexNode)) {
|
|
1186
|
+
const idx = this.acquireTemp("i32");
|
|
968
1187
|
this.emitExpr(indexNode, "i32");
|
|
969
|
-
|
|
970
|
-
if (
|
|
971
|
-
this.
|
|
1188
|
+
this.pushInstruction({ op: "local.tee", index: idx });
|
|
1189
|
+
if (depth32 !== null) {
|
|
1190
|
+
this._pushOobSite(emitLen);
|
|
1191
|
+
this.pushInstruction({ op: "local.get", index: idx });
|
|
1192
|
+
emitLen();
|
|
1193
|
+
this.pushInstruction("i32.ge_u");
|
|
1194
|
+
this.pushInstruction({ op: "br_if", depth: depth32 });
|
|
1195
|
+
this.pushInstruction("drop");
|
|
972
1196
|
} else {
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
this.pushInstruction(
|
|
1197
|
+
this.pushInstruction({ op: "local.get", index: idx });
|
|
1198
|
+
emitLen();
|
|
1199
|
+
this.pushInstruction("i32.ge_u");
|
|
1200
|
+
this._emitIndexPanicIf(() => {
|
|
1201
|
+
this.pushInstruction({ op: "local.get", index: idx });
|
|
1202
|
+
this.pushInstruction("i64.extend_i32_s");
|
|
1203
|
+
}, emitLen);
|
|
976
1204
|
}
|
|
977
|
-
|
|
978
|
-
this.pushInstruction("i32.ge_u");
|
|
979
|
-
this._emitBoundsBranch();
|
|
980
|
-
if (tmp !== null) this.releaseTemp(tmp, "i32");
|
|
1205
|
+
this.releaseTemp(idx, "i32");
|
|
981
1206
|
return;
|
|
982
1207
|
}
|
|
983
1208
|
|
|
984
1209
|
const idx64 = this.acquireTemp("i64");
|
|
985
1210
|
this.emitExpr(indexNode, "i64");
|
|
986
1211
|
this.pushInstruction({ op: "local.tee", index: idx64 });
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1212
|
+
if (depth32 !== null) {
|
|
1213
|
+
this._pushOobSite(emitLen);
|
|
1214
|
+
this.pushInstruction({ op: "local.get", index: idx64 });
|
|
1215
|
+
emitLen();
|
|
1216
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1217
|
+
this.pushInstruction("i64.ge_u");
|
|
1218
|
+
this.pushInstruction({
|
|
1219
|
+
op: "br_if",
|
|
1220
|
+
depth: this.resolveBranchDepthToOob(64),
|
|
1221
|
+
});
|
|
1222
|
+
this.pushInstruction("drop");
|
|
1223
|
+
} else {
|
|
1224
|
+
emitLen();
|
|
1225
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1226
|
+
this.pushInstruction("i64.ge_u");
|
|
1227
|
+
this._emitIndexPanicIf(
|
|
1228
|
+
() => this.pushInstruction({ op: "local.get", index: idx64 }),
|
|
1229
|
+
emitLen,
|
|
1230
|
+
);
|
|
1231
|
+
this.pushInstruction({ op: "local.get", index: idx64 });
|
|
1232
|
+
}
|
|
992
1233
|
this.pushInstruction("i32.wrap_i64");
|
|
993
1234
|
this.releaseTemp(idx64, "i64");
|
|
994
1235
|
}
|
|
@@ -1086,7 +1327,83 @@ export class ExprsEmitter {
|
|
|
1086
1327
|
}
|
|
1087
1328
|
}
|
|
1088
1329
|
|
|
1330
|
+
// Integer conversions of constants (`int64(1e18 + 1)`) keep the exact value.
|
|
1331
|
+
_emitFoldedIntConversion(expr, targetWasmType) {
|
|
1332
|
+
if (expr.kind !== "TypeConversion" && expr.kind !== "CallExpr")
|
|
1333
|
+
return false;
|
|
1334
|
+
if (expr._const?.k !== "INT") return false;
|
|
1335
|
+
if (
|
|
1336
|
+
targetWasmType != null &&
|
|
1337
|
+
targetWasmType !== "i32" &&
|
|
1338
|
+
targetWasmType !== "i64"
|
|
1339
|
+
)
|
|
1340
|
+
return false;
|
|
1341
|
+
const lit = constToLit(expr._const);
|
|
1342
|
+
lit._type = expr._type;
|
|
1343
|
+
this.emitBasicLit(lit, targetWasmType);
|
|
1344
|
+
return true;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// A float literal; an integral one in an integer context (`var y int = 2.0`)
|
|
1348
|
+
// emits its exact integer value.
|
|
1349
|
+
_emitFloatLit(lit, wType) {
|
|
1350
|
+
const i =
|
|
1351
|
+
wType === "i32" || wType === "i64"
|
|
1352
|
+
? constIntValue(constFromLit(lit) ?? {})
|
|
1353
|
+
: null;
|
|
1354
|
+
if (i !== null) {
|
|
1355
|
+
this.pushInstruction(
|
|
1356
|
+
wType === "i32"
|
|
1357
|
+
? { op: "i32.const", value: Number(BigInt.asIntN(32, i)) }
|
|
1358
|
+
: { op: "i64.const", value: BigInt.asIntN(64, i) },
|
|
1359
|
+
);
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
const n = Number(lit.value);
|
|
1363
|
+
if (wType === "f32") {
|
|
1364
|
+
this.pushInstruction({ op: "f32.const", value: n });
|
|
1365
|
+
} else {
|
|
1366
|
+
this.pushInstruction({ op: "f64.const", value: n });
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
// "complex64" | "complex128" when `w` is the struct type of a complex value.
|
|
1371
|
+
_complexKindOfWType(w) {
|
|
1372
|
+
if (typeof w?.typeIndex !== "number") return null;
|
|
1373
|
+
for (const [kind, info] of this.mod.complexTypes) {
|
|
1374
|
+
if (info.typeIndex === w.typeIndex) return kind;
|
|
1375
|
+
}
|
|
1376
|
+
return null;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1089
1379
|
emitBasicLit(lit, targetWasmType) {
|
|
1380
|
+
const targetComplex = this._complexKindOfWType(targetWasmType);
|
|
1381
|
+
const isTargetComplex64 =
|
|
1382
|
+
lit._type?.name === "complex64" ||
|
|
1383
|
+
targetWasmType?.typeName === "$complex64" ||
|
|
1384
|
+
targetComplex === "complex64";
|
|
1385
|
+
const isTargetComplex128 =
|
|
1386
|
+
lit._type?.name === "complex128" ||
|
|
1387
|
+
lit._type?.base === "complex128" ||
|
|
1388
|
+
targetWasmType?.typeName === "$complex128" ||
|
|
1389
|
+
targetComplex === "complex128";
|
|
1390
|
+
if (isTargetComplex64 || isTargetComplex128) {
|
|
1391
|
+
const info = this.mod.getComplexType(
|
|
1392
|
+
isTargetComplex64 ? "complex64" : "complex128",
|
|
1393
|
+
);
|
|
1394
|
+
const s = String(lit.value);
|
|
1395
|
+
const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
|
|
1396
|
+
const n = Number(clean);
|
|
1397
|
+
this.pushInstruction({ op: `${info.elemWType}.const`, value: n });
|
|
1398
|
+
this.pushInstruction({ op: `${info.elemWType}.const`, value: 0.0 });
|
|
1399
|
+
this.pushInstruction({
|
|
1400
|
+
op: "struct.new",
|
|
1401
|
+
typeIndex: info.typeIndex,
|
|
1402
|
+
typeName: info.name,
|
|
1403
|
+
});
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1090
1407
|
const wType =
|
|
1091
1408
|
targetWasmType ??
|
|
1092
1409
|
toWasmType(lit._type, this.mod.checker) ??
|
|
@@ -1108,12 +1425,7 @@ export class ExprsEmitter {
|
|
|
1108
1425
|
}
|
|
1109
1426
|
|
|
1110
1427
|
if (lit.litKind === "FLOAT") {
|
|
1111
|
-
|
|
1112
|
-
if (wType === "f32") {
|
|
1113
|
-
this.pushInstruction({ op: "f32.const", value: n });
|
|
1114
|
-
} else {
|
|
1115
|
-
this.pushInstruction({ op: "f64.const", value: n });
|
|
1116
|
-
}
|
|
1428
|
+
this._emitFloatLit(lit, wType);
|
|
1117
1429
|
return;
|
|
1118
1430
|
}
|
|
1119
1431
|
|
|
@@ -1141,6 +1453,22 @@ export class ExprsEmitter {
|
|
|
1141
1453
|
}
|
|
1142
1454
|
}
|
|
1143
1455
|
|
|
1456
|
+
emitImagLit(lit, targetWasmType) {
|
|
1457
|
+
const is64 =
|
|
1458
|
+
lit._type?.name === "complex64" ||
|
|
1459
|
+
targetWasmType?.typeName === "$complex64";
|
|
1460
|
+
const complexName = is64 ? "complex64" : "complex128";
|
|
1461
|
+
const info = this.mod.getComplexType(complexName);
|
|
1462
|
+
const imVal = Number(lit.value);
|
|
1463
|
+
this.pushInstruction({ op: `${info.elemWType}.const`, value: 0.0 });
|
|
1464
|
+
this.pushInstruction({ op: `${info.elemWType}.const`, value: imVal });
|
|
1465
|
+
this.pushInstruction({
|
|
1466
|
+
op: "struct.new",
|
|
1467
|
+
typeIndex: info.typeIndex,
|
|
1468
|
+
typeName: info.name,
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1144
1472
|
_emitNilValue(goType, targetWasmType) {
|
|
1145
1473
|
const effectiveType = targetWasmType ?? this.toWasmType(goType);
|
|
1146
1474
|
if (
|
|
@@ -1264,6 +1592,11 @@ export class ExprsEmitter {
|
|
|
1264
1592
|
emitUnaryExpr(expr, targetWasmType = null) {
|
|
1265
1593
|
const { op, operand } = expr;
|
|
1266
1594
|
|
|
1595
|
+
if (isComplex(expr._type) || isComplex(operand._type)) {
|
|
1596
|
+
this._emitComplexUnary(expr, op, operand, targetWasmType);
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1267
1600
|
// Optimize negative numeric literals directly
|
|
1268
1601
|
if (op === "-" && operand.kind === "BasicLit") {
|
|
1269
1602
|
if (operand.litKind === "INT" || operand.litKind === "FLOAT") {
|
|
@@ -1321,106 +1654,484 @@ export class ExprsEmitter {
|
|
|
1321
1654
|
}
|
|
1322
1655
|
break;
|
|
1323
1656
|
|
|
1324
|
-
case "&":
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1657
|
+
case "&":
|
|
1658
|
+
return this._emitAddressOf(operand);
|
|
1659
|
+
|
|
1660
|
+
case "*":
|
|
1661
|
+
return this._emitDerefExpr(operand);
|
|
1662
|
+
|
|
1663
|
+
default:
|
|
1664
|
+
throw new Error(`Unsupported unary operator: ${op}`);
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
_emitComplexUnary(expr, op, operand, targetWasmType) {
|
|
1669
|
+
if (op === "+") {
|
|
1670
|
+
this.emitExpr(operand, targetWasmType);
|
|
1671
|
+
return;
|
|
1672
|
+
}
|
|
1673
|
+
if (op === "-") {
|
|
1674
|
+
if (operand.kind === "ImagLit") {
|
|
1675
|
+
this.emitImagLit(
|
|
1676
|
+
{ ...operand, value: `-${operand.value}` },
|
|
1677
|
+
targetWasmType,
|
|
1678
|
+
);
|
|
1345
1679
|
return;
|
|
1346
1680
|
}
|
|
1681
|
+
const is64 =
|
|
1682
|
+
isComplex64(expr._type) ||
|
|
1683
|
+
isComplex64(operand._type) ||
|
|
1684
|
+
targetWasmType?.typeName === "$complex64";
|
|
1685
|
+
const info = this.mod.getComplexType(is64 ? "complex64" : "complex128");
|
|
1686
|
+
const cWType = {
|
|
1687
|
+
kind: "ref",
|
|
1688
|
+
nullable: true,
|
|
1689
|
+
typeIndex: info.typeIndex,
|
|
1690
|
+
typeName: info.name,
|
|
1691
|
+
};
|
|
1692
|
+
const opTmp = this.acquireTemp(cWType);
|
|
1693
|
+
this.emitExpr(operand, cWType);
|
|
1694
|
+
this.pushInstruction({ op: "local.set", index: opTmp });
|
|
1347
1695
|
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
if (structInfo) {
|
|
1351
|
-
const baseWType = this.toWasmType(operand._type);
|
|
1352
|
-
this.emitExpr(operand, baseWType);
|
|
1353
|
-
this.emitCloneStruct(structInfo, baseWType);
|
|
1354
|
-
return;
|
|
1355
|
-
}
|
|
1356
|
-
const box = this.mod.getBoxType(operand._type?.base ?? operand._type);
|
|
1357
|
-
const baseWType = {
|
|
1358
|
-
kind: "ref",
|
|
1359
|
-
nullable: true,
|
|
1360
|
-
typeIndex: box.typeIndex,
|
|
1361
|
-
};
|
|
1362
|
-
this.emitExpr(operand, baseWType);
|
|
1696
|
+
for (let i = 0; i < 2; i++) {
|
|
1697
|
+
this.pushInstruction({ op: "local.get", index: opTmp });
|
|
1363
1698
|
this.pushInstruction({
|
|
1364
1699
|
op: "struct.get",
|
|
1365
|
-
typeIndex:
|
|
1366
|
-
|
|
1700
|
+
typeIndex: info.typeIndex,
|
|
1701
|
+
typeName: info.name,
|
|
1702
|
+
fieldIndex: i,
|
|
1367
1703
|
});
|
|
1368
|
-
|
|
1704
|
+
this.pushInstruction(`${info.elemWType}.neg`);
|
|
1369
1705
|
}
|
|
1370
1706
|
|
|
1371
|
-
|
|
1372
|
-
|
|
1707
|
+
this.pushInstruction({
|
|
1708
|
+
op: "struct.new",
|
|
1709
|
+
typeIndex: info.typeIndex,
|
|
1710
|
+
typeName: info.name,
|
|
1711
|
+
});
|
|
1712
|
+
this.releaseTemp(opTmp, cWType);
|
|
1373
1713
|
}
|
|
1374
1714
|
}
|
|
1375
1715
|
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1716
|
+
_emitAddressOf(operand) {
|
|
1717
|
+
if (operand.kind === "Ident") {
|
|
1718
|
+
const loc = this.resolveLocal(operand.name);
|
|
1719
|
+
if (loc?.isBoxed) {
|
|
1720
|
+
this.pushInstruction({ op: "local.get", index: loc.index });
|
|
1721
|
+
// A pointer to an array or struct is its reference itself.
|
|
1722
|
+
const t = loc.goType;
|
|
1723
|
+
if (
|
|
1724
|
+
this._isValueArray(t) ||
|
|
1725
|
+
(t?.kind !== "pointer" &&
|
|
1726
|
+
isStructType(t, this.mod.checker, this.mod) &&
|
|
1727
|
+
!isPointerToStruct(t, this.mod.checker, this.mod))
|
|
1728
|
+
)
|
|
1729
|
+
this.pushInstruction({
|
|
1730
|
+
op: "struct.get",
|
|
1731
|
+
typeIndex: loc.boxInfo.typeIndex,
|
|
1732
|
+
fieldIndex: 0,
|
|
1733
|
+
});
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
if (operand.kind === "CompositeLit") {
|
|
1738
|
+
if (isStructType(operand._type, this.mod.checker, this.mod)) {
|
|
1739
|
+
this.emitExpr(operand);
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
const structInfo = this._resolveStructInfo(operand);
|
|
1744
|
+
if (structInfo) {
|
|
1745
|
+
this.emitExpr(operand);
|
|
1387
1746
|
return;
|
|
1388
1747
|
}
|
|
1389
|
-
|
|
1390
|
-
if (
|
|
1391
|
-
this.emitExpr(
|
|
1392
|
-
this.pushInstruction({ op: "if", blockType: "i32" });
|
|
1393
|
-
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
1394
|
-
this.pushInstruction("else");
|
|
1395
|
-
this.emitExpr(right, "i32");
|
|
1396
|
-
this.pushInstruction("end");
|
|
1748
|
+
const opWType = this.toWasmType(operand._type);
|
|
1749
|
+
if (!isScalarType(operand._type, this.mod.checker, this.mod)) {
|
|
1750
|
+
this.emitExpr(operand, opWType);
|
|
1397
1751
|
return;
|
|
1398
1752
|
}
|
|
1753
|
+
const box = this.mod.getBoxType(operand._type);
|
|
1754
|
+
this.emitExpr(operand, opWType);
|
|
1755
|
+
this.pushInstruction({
|
|
1756
|
+
op: "struct.new",
|
|
1757
|
+
typeIndex: box.typeIndex,
|
|
1758
|
+
typeName: box.name,
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1399
1761
|
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1762
|
+
_isPointerGoType(t) {
|
|
1763
|
+
let x = t;
|
|
1764
|
+
if (x?.kind === "named" && x.underlying) x = x.underlying;
|
|
1765
|
+
return x?.kind === "pointer" || x?.kind === "PointerType";
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
_ptrBaseType(t) {
|
|
1769
|
+
let x = t;
|
|
1770
|
+
if (x?.kind === "named" && x.underlying) x = x.underlying;
|
|
1771
|
+
return x?.kind === "pointer" ? x.base : null;
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
// The struct a `*operand` reads or writes, or null when the operand points
|
|
1775
|
+
// at something else: `*pp` with `pp **T` loads a pointer, not a struct.
|
|
1776
|
+
_derefStructInfo(operand) {
|
|
1777
|
+
if (this._isPointerGoType(this._ptrBaseType(operand._type))) return null;
|
|
1778
|
+
return this._resolveStructInfo(operand);
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
_emitDerefExpr(operand) {
|
|
1782
|
+
const structInfo = this._derefStructInfo(operand);
|
|
1783
|
+
if (structInfo) {
|
|
1784
|
+
const baseWType = this.toWasmType(operand._type);
|
|
1785
|
+
this.emitExpr(operand, baseWType);
|
|
1786
|
+
this._emitNilCheck(operand._type);
|
|
1787
|
+
this.emitCloneStruct(structInfo, baseWType);
|
|
1403
1788
|
return;
|
|
1404
1789
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1790
|
+
const operandGoType =
|
|
1791
|
+
operand._type ??
|
|
1792
|
+
(operand.kind === "Ident"
|
|
1793
|
+
? this.resolveLocal(operand.name)?.goType
|
|
1794
|
+
: null) ??
|
|
1795
|
+
this._resolveExprGoType(operand);
|
|
1796
|
+
const baseGoType = operandGoType?.base ?? operandGoType;
|
|
1797
|
+
if (!isScalarType(baseGoType, this.mod.checker, this.mod)) {
|
|
1798
|
+
const baseWType = this.toWasmType(baseGoType);
|
|
1799
|
+
this.emitExpr(operand, baseWType);
|
|
1409
1800
|
return;
|
|
1410
1801
|
}
|
|
1802
|
+
const box = this.mod.getBoxType(baseGoType);
|
|
1803
|
+
const ptr = this.mod.getPtrType(baseGoType);
|
|
1804
|
+
const ptrWType = {
|
|
1805
|
+
kind: "ref",
|
|
1806
|
+
nullable: true,
|
|
1807
|
+
typeIndex: ptr.typeIndex,
|
|
1808
|
+
typeName: ptr.name,
|
|
1809
|
+
};
|
|
1810
|
+
this.emitExpr(operand, ptrWType);
|
|
1811
|
+
this._emitNilCheck(operandGoType);
|
|
1812
|
+
this.pushInstruction({
|
|
1813
|
+
op: "ref.cast",
|
|
1814
|
+
typeIndex: box.typeIndex,
|
|
1815
|
+
typeName: box.name,
|
|
1816
|
+
});
|
|
1817
|
+
this.pushInstruction({
|
|
1818
|
+
op: "struct.get",
|
|
1819
|
+
typeIndex: box.typeIndex,
|
|
1820
|
+
typeName: box.name,
|
|
1821
|
+
fieldIndex: 0,
|
|
1822
|
+
});
|
|
1823
|
+
}
|
|
1411
1824
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
op
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
(
|
|
1423
|
-
|
|
1825
|
+
_emitComplexEq(aTmp, bTmp, info, f, op) {
|
|
1826
|
+
for (let i = 0; i < 2; i++) {
|
|
1827
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
1828
|
+
this.pushInstruction({
|
|
1829
|
+
op: "struct.get",
|
|
1830
|
+
typeIndex: info.typeIndex,
|
|
1831
|
+
typeName: info.name,
|
|
1832
|
+
fieldIndex: i,
|
|
1833
|
+
});
|
|
1834
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1835
|
+
this.pushInstruction({
|
|
1836
|
+
op: "struct.get",
|
|
1837
|
+
typeIndex: info.typeIndex,
|
|
1838
|
+
typeName: info.name,
|
|
1839
|
+
fieldIndex: i,
|
|
1840
|
+
});
|
|
1841
|
+
this.pushInstruction(`${f}.eq`);
|
|
1842
|
+
}
|
|
1843
|
+
this.pushInstruction("i32.and");
|
|
1844
|
+
if (op === "!=") this.pushInstruction("i32.eqz");
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
_emitComplexAddSub(aTmp, bTmp, info, f, op) {
|
|
1848
|
+
const wasmOp = op === "+" ? `${f}.add` : `${f}.sub`;
|
|
1849
|
+
for (let i = 0; i < 2; i++) {
|
|
1850
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
1851
|
+
this.pushInstruction({
|
|
1852
|
+
op: "struct.get",
|
|
1853
|
+
typeIndex: info.typeIndex,
|
|
1854
|
+
typeName: info.name,
|
|
1855
|
+
fieldIndex: i,
|
|
1856
|
+
});
|
|
1857
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1858
|
+
this.pushInstruction({
|
|
1859
|
+
op: "struct.get",
|
|
1860
|
+
typeIndex: info.typeIndex,
|
|
1861
|
+
typeName: info.name,
|
|
1862
|
+
fieldIndex: i,
|
|
1863
|
+
});
|
|
1864
|
+
this.pushInstruction(wasmOp);
|
|
1865
|
+
}
|
|
1866
|
+
this.pushInstruction({
|
|
1867
|
+
op: "struct.new",
|
|
1868
|
+
typeIndex: info.typeIndex,
|
|
1869
|
+
typeName: info.name,
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
_emitComplexMul(aTmp, bTmp, info, f) {
|
|
1874
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
1875
|
+
this.pushInstruction({
|
|
1876
|
+
op: "struct.get",
|
|
1877
|
+
typeIndex: info.typeIndex,
|
|
1878
|
+
typeName: info.name,
|
|
1879
|
+
fieldIndex: 0,
|
|
1880
|
+
});
|
|
1881
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1882
|
+
this.pushInstruction({
|
|
1883
|
+
op: "struct.get",
|
|
1884
|
+
typeIndex: info.typeIndex,
|
|
1885
|
+
typeName: info.name,
|
|
1886
|
+
fieldIndex: 0,
|
|
1887
|
+
});
|
|
1888
|
+
this.pushInstruction(`${f}.mul`);
|
|
1889
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
1890
|
+
this.pushInstruction({
|
|
1891
|
+
op: "struct.get",
|
|
1892
|
+
typeIndex: info.typeIndex,
|
|
1893
|
+
typeName: info.name,
|
|
1894
|
+
fieldIndex: 1,
|
|
1895
|
+
});
|
|
1896
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1897
|
+
this.pushInstruction({
|
|
1898
|
+
op: "struct.get",
|
|
1899
|
+
typeIndex: info.typeIndex,
|
|
1900
|
+
typeName: info.name,
|
|
1901
|
+
fieldIndex: 1,
|
|
1902
|
+
});
|
|
1903
|
+
this.pushInstruction(`${f}.mul`);
|
|
1904
|
+
this.pushInstruction(`${f}.sub`);
|
|
1905
|
+
|
|
1906
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
1907
|
+
this.pushInstruction({
|
|
1908
|
+
op: "struct.get",
|
|
1909
|
+
typeIndex: info.typeIndex,
|
|
1910
|
+
typeName: info.name,
|
|
1911
|
+
fieldIndex: 0,
|
|
1912
|
+
});
|
|
1913
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1914
|
+
this.pushInstruction({
|
|
1915
|
+
op: "struct.get",
|
|
1916
|
+
typeIndex: info.typeIndex,
|
|
1917
|
+
typeName: info.name,
|
|
1918
|
+
fieldIndex: 1,
|
|
1919
|
+
});
|
|
1920
|
+
this.pushInstruction(`${f}.mul`);
|
|
1921
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
1922
|
+
this.pushInstruction({
|
|
1923
|
+
op: "struct.get",
|
|
1924
|
+
typeIndex: info.typeIndex,
|
|
1925
|
+
typeName: info.name,
|
|
1926
|
+
fieldIndex: 1,
|
|
1927
|
+
});
|
|
1928
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1929
|
+
this.pushInstruction({
|
|
1930
|
+
op: "struct.get",
|
|
1931
|
+
typeIndex: info.typeIndex,
|
|
1932
|
+
typeName: info.name,
|
|
1933
|
+
fieldIndex: 0,
|
|
1934
|
+
});
|
|
1935
|
+
this.pushInstruction(`${f}.mul`);
|
|
1936
|
+
this.pushInstruction(`${f}.add`);
|
|
1937
|
+
|
|
1938
|
+
this.pushInstruction({
|
|
1939
|
+
op: "struct.new",
|
|
1940
|
+
typeIndex: info.typeIndex,
|
|
1941
|
+
typeName: info.name,
|
|
1942
|
+
});
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
_emitComplexDiv(aTmp, bTmp, info, f) {
|
|
1946
|
+
const dTmp = this.acquireTemp(f);
|
|
1947
|
+
for (let i = 0; i < 2; i++) {
|
|
1948
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1949
|
+
this.pushInstruction({
|
|
1950
|
+
op: "struct.get",
|
|
1951
|
+
typeIndex: info.typeIndex,
|
|
1952
|
+
typeName: info.name,
|
|
1953
|
+
fieldIndex: i,
|
|
1954
|
+
});
|
|
1955
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1956
|
+
this.pushInstruction({
|
|
1957
|
+
op: "struct.get",
|
|
1958
|
+
typeIndex: info.typeIndex,
|
|
1959
|
+
typeName: info.name,
|
|
1960
|
+
fieldIndex: i,
|
|
1961
|
+
});
|
|
1962
|
+
this.pushInstruction(`${f}.mul`);
|
|
1963
|
+
if (i > 0) this.pushInstruction(`${f}.add`);
|
|
1964
|
+
}
|
|
1965
|
+
this.pushInstruction({ op: "local.set", index: dTmp });
|
|
1966
|
+
|
|
1967
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
1968
|
+
this.pushInstruction({
|
|
1969
|
+
op: "struct.get",
|
|
1970
|
+
typeIndex: info.typeIndex,
|
|
1971
|
+
typeName: info.name,
|
|
1972
|
+
fieldIndex: 0,
|
|
1973
|
+
});
|
|
1974
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1975
|
+
this.pushInstruction({
|
|
1976
|
+
op: "struct.get",
|
|
1977
|
+
typeIndex: info.typeIndex,
|
|
1978
|
+
typeName: info.name,
|
|
1979
|
+
fieldIndex: 0,
|
|
1980
|
+
});
|
|
1981
|
+
this.pushInstruction(`${f}.mul`);
|
|
1982
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
1983
|
+
this.pushInstruction({
|
|
1984
|
+
op: "struct.get",
|
|
1985
|
+
typeIndex: info.typeIndex,
|
|
1986
|
+
typeName: info.name,
|
|
1987
|
+
fieldIndex: 1,
|
|
1988
|
+
});
|
|
1989
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1990
|
+
this.pushInstruction({
|
|
1991
|
+
op: "struct.get",
|
|
1992
|
+
typeIndex: info.typeIndex,
|
|
1993
|
+
typeName: info.name,
|
|
1994
|
+
fieldIndex: 1,
|
|
1995
|
+
});
|
|
1996
|
+
this.pushInstruction(`${f}.mul`);
|
|
1997
|
+
this.pushInstruction(`${f}.add`);
|
|
1998
|
+
this.pushInstruction({ op: "local.get", index: dTmp });
|
|
1999
|
+
this.pushInstruction(`${f}.div`);
|
|
2000
|
+
|
|
2001
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
2002
|
+
this.pushInstruction({
|
|
2003
|
+
op: "struct.get",
|
|
2004
|
+
typeIndex: info.typeIndex,
|
|
2005
|
+
typeName: info.name,
|
|
2006
|
+
fieldIndex: 1,
|
|
2007
|
+
});
|
|
2008
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
2009
|
+
this.pushInstruction({
|
|
2010
|
+
op: "struct.get",
|
|
2011
|
+
typeIndex: info.typeIndex,
|
|
2012
|
+
typeName: info.name,
|
|
2013
|
+
fieldIndex: 0,
|
|
2014
|
+
});
|
|
2015
|
+
this.pushInstruction(`${f}.mul`);
|
|
2016
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
2017
|
+
this.pushInstruction({
|
|
2018
|
+
op: "struct.get",
|
|
2019
|
+
typeIndex: info.typeIndex,
|
|
2020
|
+
typeName: info.name,
|
|
2021
|
+
fieldIndex: 0,
|
|
2022
|
+
});
|
|
2023
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
2024
|
+
this.pushInstruction({
|
|
2025
|
+
op: "struct.get",
|
|
2026
|
+
typeIndex: info.typeIndex,
|
|
2027
|
+
typeName: info.name,
|
|
2028
|
+
fieldIndex: 1,
|
|
2029
|
+
});
|
|
2030
|
+
this.pushInstruction(`${f}.mul`);
|
|
2031
|
+
this.pushInstruction(`${f}.sub`);
|
|
2032
|
+
this.pushInstruction({ op: "local.get", index: dTmp });
|
|
2033
|
+
this.pushInstruction(`${f}.div`);
|
|
2034
|
+
|
|
2035
|
+
this.pushInstruction({
|
|
2036
|
+
op: "struct.new",
|
|
2037
|
+
typeIndex: info.typeIndex,
|
|
2038
|
+
typeName: info.name,
|
|
2039
|
+
});
|
|
2040
|
+
this.releaseTemp(dTmp, f);
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
emitComplexBinary(expr, targetWasmType) {
|
|
2044
|
+
const { op, left, right } = expr;
|
|
2045
|
+
const is64 =
|
|
2046
|
+
isComplex64(expr._type) ||
|
|
2047
|
+
isComplex64(left._type) ||
|
|
2048
|
+
isComplex64(right._type) ||
|
|
2049
|
+
targetWasmType?.typeName === "$complex64";
|
|
2050
|
+
const info = this.mod.getComplexType(is64 ? "complex64" : "complex128");
|
|
2051
|
+
const f = info.elemWType;
|
|
2052
|
+
const cWType = {
|
|
2053
|
+
kind: "ref",
|
|
2054
|
+
nullable: true,
|
|
2055
|
+
typeIndex: info.typeIndex,
|
|
2056
|
+
typeName: info.name,
|
|
2057
|
+
};
|
|
2058
|
+
const aTmp = this.acquireTemp(cWType);
|
|
2059
|
+
const bTmp = this.acquireTemp(cWType);
|
|
2060
|
+
this.emitExpr(left, cWType);
|
|
2061
|
+
this.pushInstruction({ op: "local.set", index: aTmp });
|
|
2062
|
+
this.emitExpr(right, cWType);
|
|
2063
|
+
this.pushInstruction({ op: "local.set", index: bTmp });
|
|
2064
|
+
|
|
2065
|
+
if (op === "==" || op === "!=") {
|
|
2066
|
+
this._emitComplexEq(aTmp, bTmp, info, f, op);
|
|
2067
|
+
} else if (op === "+" || op === "-") {
|
|
2068
|
+
this._emitComplexAddSub(aTmp, bTmp, info, f, op);
|
|
2069
|
+
} else if (op === "*") {
|
|
2070
|
+
this._emitComplexMul(aTmp, bTmp, info, f);
|
|
2071
|
+
} else if (op === "/") {
|
|
2072
|
+
this._emitComplexDiv(aTmp, bTmp, info, f);
|
|
2073
|
+
}
|
|
2074
|
+
this.releaseTemp(bTmp, cWType);
|
|
2075
|
+
this.releaseTemp(aTmp, cWType);
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
emitBinaryExpr(expr, targetWasmType = null) {
|
|
2079
|
+
const { op, left, right } = expr;
|
|
2080
|
+
|
|
2081
|
+
if (
|
|
2082
|
+
isComplex(expr._type) ||
|
|
2083
|
+
isComplex(left._type) ||
|
|
2084
|
+
isComplex(right._type)
|
|
2085
|
+
) {
|
|
2086
|
+
this.emitComplexBinary(expr, targetWasmType);
|
|
2087
|
+
return;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
// Short-circuiting logical operators
|
|
2091
|
+
if (op === "&&") {
|
|
2092
|
+
this.emitExpr(left, "i32");
|
|
2093
|
+
this.pushInstruction({ op: "if", blockType: "i32" });
|
|
2094
|
+
this.emitExpr(right, "i32");
|
|
2095
|
+
this.pushInstruction("else");
|
|
2096
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
2097
|
+
this.pushInstruction("end");
|
|
2098
|
+
return;
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
if (op === "||") {
|
|
2102
|
+
this.emitExpr(left, "i32");
|
|
2103
|
+
this.pushInstruction({ op: "if", blockType: "i32" });
|
|
2104
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
2105
|
+
this.pushInstruction("else");
|
|
2106
|
+
this.emitExpr(right, "i32");
|
|
2107
|
+
this.pushInstruction("end");
|
|
2108
|
+
return;
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
// Division & Modulo with zero-guard and overflow-guard
|
|
2112
|
+
if (op === "/" || op === "%") {
|
|
2113
|
+
this.emitDivRem(op, left, right, targetWasmType);
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
// Shifts with Go semantics
|
|
2118
|
+
if (op === "<<" || op === ">>") {
|
|
2119
|
+
this.emitShift(op, left, right, targetWasmType);
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
// Standard binary operators
|
|
2124
|
+
const isCmp =
|
|
2125
|
+
op === "==" ||
|
|
2126
|
+
op === "!=" ||
|
|
2127
|
+
op === "<" ||
|
|
2128
|
+
op === "<=" ||
|
|
2129
|
+
op === ">" ||
|
|
2130
|
+
op === ">=";
|
|
2131
|
+
|
|
2132
|
+
const isNil = (n) =>
|
|
2133
|
+
(n.kind === "BasicLit" && n.litKind === "NIL") ||
|
|
2134
|
+
(n.kind === "Ident" && n.name === "nil");
|
|
1424
2135
|
|
|
1425
2136
|
if ((op === "==" || op === "!=") && (isNil(left) || isNil(right))) {
|
|
1426
2137
|
const nonNil = isNil(right) ? left : right;
|
|
@@ -1453,10 +2164,17 @@ export class ExprsEmitter {
|
|
|
1453
2164
|
return;
|
|
1454
2165
|
}
|
|
1455
2166
|
|
|
1456
|
-
|
|
2167
|
+
// An untyped constant operand takes the type of the typed one
|
|
2168
|
+
// (`'a' < rune(b)`), so both sides share one wasm representation.
|
|
2169
|
+
const leftType =
|
|
2170
|
+
left._type?.kind === "untyped" &&
|
|
2171
|
+
right._type &&
|
|
2172
|
+
right._type.kind !== "untyped"
|
|
2173
|
+
? right._type
|
|
2174
|
+
: left._type;
|
|
1457
2175
|
const isLeftI32 = this._isI32IndexExpr(left);
|
|
1458
2176
|
const isRightI32 = this._isI32IndexExpr(right);
|
|
1459
|
-
const leftGoType =
|
|
2177
|
+
const leftGoType = leftType ?? this._resolveExprGoType(left);
|
|
1460
2178
|
const isIntType =
|
|
1461
2179
|
!leftGoType ||
|
|
1462
2180
|
(leftGoType.kind === "basic" &&
|
|
@@ -1497,6 +2215,72 @@ export class ExprsEmitter {
|
|
|
1497
2215
|
this.emitBinaryOp(op, wType, leftType);
|
|
1498
2216
|
}
|
|
1499
2217
|
|
|
2218
|
+
_findScalarPtrByWType(wType, map) {
|
|
2219
|
+
if (!map) return null;
|
|
2220
|
+
for (const info of map.values()) {
|
|
2221
|
+
if (info.typeIndex === wType.typeIndex || info.name === wType.typeName) {
|
|
2222
|
+
return info;
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
return null;
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
_getScalarPtrFromWType(wType) {
|
|
2229
|
+
if (!wType || typeof wType !== "object") return null;
|
|
2230
|
+
const ptr = this._findScalarPtrByWType(wType, this.mod.ptrTypes);
|
|
2231
|
+
if (ptr) {
|
|
2232
|
+
return {
|
|
2233
|
+
ptr,
|
|
2234
|
+
box: this.mod.getBoxType(ptr.goType),
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2237
|
+
const box = this._findScalarPtrByWType(wType, this.mod.boxTypes);
|
|
2238
|
+
if (box) {
|
|
2239
|
+
return {
|
|
2240
|
+
ptr: this.mod.getPtrType(box.goType),
|
|
2241
|
+
box,
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
return null;
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
_getScalarPtrFromGoType(goType) {
|
|
2248
|
+
let g = goType;
|
|
2249
|
+
if (g?.kind === "TypeName" && this.mod.checker?.types?.has(g.name)) {
|
|
2250
|
+
g = this.mod.checker.types.get(g.name);
|
|
2251
|
+
}
|
|
2252
|
+
while (g?.kind === "named" && g.underlying) {
|
|
2253
|
+
g = g.underlying;
|
|
2254
|
+
}
|
|
2255
|
+
const isPtr =
|
|
2256
|
+
g?.kind === "pointer" ||
|
|
2257
|
+
g?.kind === "PointerType" ||
|
|
2258
|
+
g?.kind === "StarExpr";
|
|
2259
|
+
if (!isPtr || (!g.base && !g.elem)) return null;
|
|
2260
|
+
const base = g.base || g.elem;
|
|
2261
|
+
if (!isScalarType(base, this.mod.checker, this.mod)) {
|
|
2262
|
+
return null;
|
|
2263
|
+
}
|
|
2264
|
+
return {
|
|
2265
|
+
ptr: this.mod.getPtrType(base),
|
|
2266
|
+
box: this.mod.getBoxType(base),
|
|
2267
|
+
};
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
_getScalarPtrInfo(wType, goType = null) {
|
|
2271
|
+
return (
|
|
2272
|
+
this._getScalarPtrFromGoType(goType) ?? this._getScalarPtrFromWType(wType)
|
|
2273
|
+
);
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
_isScalarPtrType(goType, wType = null) {
|
|
2277
|
+
return this._getScalarPtrInfo(wType, goType) !== null;
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
_emitScalarPtrEq(_ptrInfo) {
|
|
2281
|
+
this.pushInstruction("ref.eq");
|
|
2282
|
+
}
|
|
2283
|
+
|
|
1500
2284
|
_emitStructEq(structInfo) {
|
|
1501
2285
|
const bTmp = this.acquireTemp({
|
|
1502
2286
|
kind: "ref",
|
|
@@ -1543,11 +2327,18 @@ export class ExprsEmitter {
|
|
|
1543
2327
|
(f.goType?.kind === "named" ? f.goType.name : null),
|
|
1544
2328
|
);
|
|
1545
2329
|
this._emitStructEq(sInfo);
|
|
2330
|
+
} else if (this._isValueArray(f.goType)) {
|
|
2331
|
+
this._emitArrayEq(f.goType);
|
|
1546
2332
|
} else if (isStringType(f.goType, this.mod.checker)) {
|
|
1547
2333
|
const cmpIdx = this.mod.getStringCmpImportIndex("==");
|
|
1548
2334
|
this.pushInstruction({ op: "call", funcIndex: cmpIdx });
|
|
2335
|
+
} else if (f.wType === "v128") {
|
|
2336
|
+
this._emitSimdEq(f.goType);
|
|
1549
2337
|
} else if (f.wType === "anyref") {
|
|
1550
2338
|
this._emitAnyEq();
|
|
2339
|
+
} else if (this._isScalarPtrType(f.goType, f.wType)) {
|
|
2340
|
+
const ptrInfo = this._getScalarPtrInfo(f.wType, f.goType);
|
|
2341
|
+
this._emitScalarPtrEq(ptrInfo);
|
|
1551
2342
|
} else if (typeof f.wType === "object" && f.wType !== null) {
|
|
1552
2343
|
this.pushInstruction("ref.eq");
|
|
1553
2344
|
} else {
|
|
@@ -1578,7 +2369,110 @@ export class ExprsEmitter {
|
|
|
1578
2369
|
});
|
|
1579
2370
|
}
|
|
1580
2371
|
|
|
2372
|
+
_isValueArray(goType) {
|
|
2373
|
+
return goType?.kind !== "pointer" && isArrayType(goType, this.mod.checker);
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
// Go array equality: element-wise (arrays are values). Pops two arrays
|
|
2377
|
+
// and pushes an i32. A null array (a never-written slot) is the zero
|
|
2378
|
+
// array.
|
|
2379
|
+
_emitArrayEq(goType) {
|
|
2380
|
+
const elemType = this._getArrayElemType(goType);
|
|
2381
|
+
const arrInfo = this.mod.getArrayType(elemType);
|
|
2382
|
+
const arrWType = {
|
|
2383
|
+
kind: "ref",
|
|
2384
|
+
nullable: true,
|
|
2385
|
+
typeIndex: arrInfo.typeIndex,
|
|
2386
|
+
};
|
|
2387
|
+
const bTmp = this.acquireTemp(arrWType);
|
|
2388
|
+
const aTmp = this.acquireTemp(arrWType);
|
|
2389
|
+
for (const tmp of [bTmp, aTmp]) {
|
|
2390
|
+
this.pushInstruction({ op: "local.tee", index: tmp });
|
|
2391
|
+
this.pushInstruction("ref.is_null");
|
|
2392
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
2393
|
+
this.emitZeroValue(goType, arrWType);
|
|
2394
|
+
this.pushInstruction({ op: "local.set", index: tmp });
|
|
2395
|
+
this.pushInstruction("end");
|
|
2396
|
+
}
|
|
2397
|
+
const i = this.acquireTemp("i32");
|
|
2398
|
+
const resTmp = this.acquireTemp("i32");
|
|
2399
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
2400
|
+
this.pushInstruction({ op: "local.set", index: resTmp });
|
|
2401
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
2402
|
+
this.pushInstruction({ op: "local.set", index: i });
|
|
2403
|
+
this.pushInstruction({ op: "block", blockType: "void" });
|
|
2404
|
+
this.pushInstruction({ op: "loop", blockType: "void" });
|
|
2405
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
2406
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
2407
|
+
this.pushInstruction("array.len");
|
|
2408
|
+
this.pushInstruction("i32.ge_u");
|
|
2409
|
+
this.pushInstruction({ op: "br_if", depth: 1 });
|
|
2410
|
+
for (const tmp of [aTmp, bTmp]) {
|
|
2411
|
+
this.pushInstruction({ op: "local.get", index: tmp });
|
|
2412
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
2413
|
+
this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
|
|
2414
|
+
}
|
|
2415
|
+
this._emitElemEq(elemType, arrInfo.elemWType);
|
|
2416
|
+
this.pushInstruction("i32.eqz");
|
|
2417
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
2418
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
2419
|
+
this.pushInstruction({ op: "local.set", index: resTmp });
|
|
2420
|
+
this.pushInstruction({ op: "br", depth: 2 });
|
|
2421
|
+
this.pushInstruction("end");
|
|
2422
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
2423
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
2424
|
+
this.pushInstruction("i32.add");
|
|
2425
|
+
this.pushInstruction({ op: "local.set", index: i });
|
|
2426
|
+
this.pushInstruction({ op: "br", depth: 0 });
|
|
2427
|
+
this.pushInstruction("end");
|
|
2428
|
+
this.pushInstruction("end");
|
|
2429
|
+
this.pushInstruction({ op: "local.get", index: resTmp });
|
|
2430
|
+
this.releaseTemp(resTmp, "i32");
|
|
2431
|
+
this.releaseTemp(i, "i32");
|
|
2432
|
+
this.releaseTemp(aTmp, arrWType);
|
|
2433
|
+
this.releaseTemp(bTmp, arrWType);
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
// Equality of two array elements on the stack -> i32.
|
|
2437
|
+
_emitElemEq(goType, wType) {
|
|
2438
|
+
if (this._isValueArray(goType)) {
|
|
2439
|
+
this._emitArrayEq(goType);
|
|
2440
|
+
} else if (
|
|
2441
|
+
isStructType(goType, this.mod.checker, this.mod) &&
|
|
2442
|
+
!isPointerToStruct(goType, this.mod.checker, this.mod)
|
|
2443
|
+
) {
|
|
2444
|
+
this._emitStructEq(this._resolveStructInfo({ _type: goType }));
|
|
2445
|
+
} else {
|
|
2446
|
+
this.emitBinaryOp("==", wType, goType);
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
|
|
1581
2450
|
emitBinaryOp(op, wType, goType) {
|
|
2451
|
+
if ((op === "==" || op === "!=") && this._isValueArray(goType)) {
|
|
2452
|
+
this._emitArrayEq(goType);
|
|
2453
|
+
if (op === "!=") this.pushInstruction("i32.eqz");
|
|
2454
|
+
return;
|
|
2455
|
+
}
|
|
2456
|
+
if (isComplex(goType)) {
|
|
2457
|
+
const is64 = isComplex64(goType);
|
|
2458
|
+
const info = this.mod.getComplexType(is64 ? "complex64" : "complex128");
|
|
2459
|
+
const f = info.elemWType;
|
|
2460
|
+
const bTmp = this.acquireTemp(wType);
|
|
2461
|
+
const aTmp = this.acquireTemp(wType);
|
|
2462
|
+
this.pushInstruction({ op: "local.set", index: bTmp });
|
|
2463
|
+
this.pushInstruction({ op: "local.set", index: aTmp });
|
|
2464
|
+
if (op === "+" || op === "-") {
|
|
2465
|
+
this._emitComplexAddSub(aTmp, bTmp, info, f, op);
|
|
2466
|
+
} else if (op === "*") {
|
|
2467
|
+
this._emitComplexMul(aTmp, bTmp, info, f);
|
|
2468
|
+
} else if (op === "/") {
|
|
2469
|
+
this._emitComplexDiv(aTmp, bTmp, info, f);
|
|
2470
|
+
}
|
|
2471
|
+
this.releaseTemp(aTmp, wType);
|
|
2472
|
+
this.releaseTemp(bTmp, wType);
|
|
2473
|
+
return;
|
|
2474
|
+
}
|
|
2475
|
+
|
|
1582
2476
|
const signed = isSigned(goType);
|
|
1583
2477
|
|
|
1584
2478
|
switch (op) {
|
|
@@ -1646,8 +2540,13 @@ export class ExprsEmitter {
|
|
|
1646
2540
|
if (isStringType(goType, this.mod.checker) || wType === "externref") {
|
|
1647
2541
|
const cmpIdx = this.mod.getStringCmpImportIndex("==");
|
|
1648
2542
|
this.pushInstruction({ op: "call", funcIndex: cmpIdx });
|
|
2543
|
+
} else if (wType === "v128") {
|
|
2544
|
+
this._emitSimdEq(goType);
|
|
1649
2545
|
} else if (wType === "anyref") {
|
|
1650
2546
|
this._emitAnyEq();
|
|
2547
|
+
} else if (this._isScalarPtrType(goType, wType)) {
|
|
2548
|
+
const ptrInfo = this._getScalarPtrInfo(wType, goType);
|
|
2549
|
+
this._emitScalarPtrEq(ptrInfo);
|
|
1651
2550
|
} else if (typeof wType === "object" && wType !== null) {
|
|
1652
2551
|
this.pushInstruction("ref.eq");
|
|
1653
2552
|
} else {
|
|
@@ -1658,9 +2557,16 @@ export class ExprsEmitter {
|
|
|
1658
2557
|
if (isStringType(goType, this.mod.checker) || wType === "externref") {
|
|
1659
2558
|
const cmpIdx = this.mod.getStringCmpImportIndex("!=");
|
|
1660
2559
|
this.pushInstruction({ op: "call", funcIndex: cmpIdx });
|
|
2560
|
+
} else if (wType === "v128") {
|
|
2561
|
+
this._emitSimdEq(goType);
|
|
2562
|
+
this.pushInstruction("i32.eqz");
|
|
1661
2563
|
} else if (wType === "anyref") {
|
|
1662
2564
|
this._emitAnyEq();
|
|
1663
2565
|
this.pushInstruction("i32.eqz");
|
|
2566
|
+
} else if (this._isScalarPtrType(goType, wType)) {
|
|
2567
|
+
const ptrInfo = this._getScalarPtrInfo(wType, goType);
|
|
2568
|
+
this._emitScalarPtrEq(ptrInfo);
|
|
2569
|
+
this.pushInstruction("i32.eqz");
|
|
1664
2570
|
} else if (typeof wType === "object" && wType !== null) {
|
|
1665
2571
|
this.pushInstruction("ref.eq");
|
|
1666
2572
|
this.pushInstruction("i32.eqz");
|
|
@@ -1848,6 +2754,21 @@ export class ExprsEmitter {
|
|
|
1848
2754
|
this.pushInstruction({ op: "local.set", index: tmpVal });
|
|
1849
2755
|
|
|
1850
2756
|
this.emitExpr(right, rightWType);
|
|
2757
|
+
// A negative signed count panics before the shift is evaluated.
|
|
2758
|
+
if (isSigned(right._type) && right.kind !== "BasicLit") {
|
|
2759
|
+
const tmpCount = this.acquireTemp(rightWType);
|
|
2760
|
+
this.pushInstruction({ op: "local.tee", index: tmpCount });
|
|
2761
|
+
this.pushInstruction({
|
|
2762
|
+
op: `${rightWType}.const`,
|
|
2763
|
+
value: rightWType === "i64" ? 0n : 0,
|
|
2764
|
+
});
|
|
2765
|
+
this.pushInstruction(`${rightWType}.lt_s`);
|
|
2766
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
2767
|
+
this.emitPanic("runtime error: negative shift amount");
|
|
2768
|
+
this.pushInstruction("end");
|
|
2769
|
+
this.pushInstruction({ op: "local.get", index: tmpCount });
|
|
2770
|
+
this.releaseTemp(tmpCount, rightWType);
|
|
2771
|
+
}
|
|
1851
2772
|
if (wType === "i64" && rightWType === "i32") {
|
|
1852
2773
|
this.pushInstruction("i64.extend_i32_u");
|
|
1853
2774
|
} else if (wType === "i32" && rightWType === "i64") {
|
|
@@ -1890,32 +2811,91 @@ export class ExprsEmitter {
|
|
|
1890
2811
|
|
|
1891
2812
|
emitTypeConversion(conv) {
|
|
1892
2813
|
const { targetType, expr } = conv;
|
|
1893
|
-
const fromType = expr._type;
|
|
1894
|
-
const fromWType = toWasmType(fromType
|
|
1895
|
-
const toWType = toWasmType(targetType
|
|
2814
|
+
const fromType = expr._type ?? this._resolveExprGoType(expr);
|
|
2815
|
+
const fromWType = this.toWasmType(fromType);
|
|
2816
|
+
const toWType = this.toWasmType(targetType);
|
|
2817
|
+
|
|
2818
|
+
// `[]T(nil)` and `(*T)(nil)`: the target type's nil, not a converted value.
|
|
2819
|
+
if (
|
|
2820
|
+
(expr.kind === "Ident" && expr.name === "nil") ||
|
|
2821
|
+
(expr.kind === "BasicLit" && expr.litKind === "NIL")
|
|
2822
|
+
) {
|
|
2823
|
+
this._emitNilValue(targetType, toWType);
|
|
2824
|
+
return;
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
// `any(x)` boxes x exactly like an assignment to an `any` variable.
|
|
2828
|
+
if (isAnyType(targetType, this.mod.checker)) {
|
|
2829
|
+
this.emitExpr(expr, "anyref");
|
|
2830
|
+
return;
|
|
2831
|
+
}
|
|
1896
2832
|
|
|
1897
2833
|
this.emitExpr(expr, fromWType);
|
|
1898
2834
|
|
|
2835
|
+
if (isComplex(targetType)) {
|
|
2836
|
+
this._emitComplexConversion(targetType, fromType, fromWType);
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
// Struct to a different defined struct type: a copy in the target's type.
|
|
2841
|
+
if (
|
|
2842
|
+
isStructType(fromType, this.mod.checker, this.mod) &&
|
|
2843
|
+
isStructType(targetType, this.mod.checker, this.mod) &&
|
|
2844
|
+
!isPointerToStruct(fromType, this.mod.checker, this.mod) &&
|
|
2845
|
+
!isPointerToStruct(targetType, this.mod.checker, this.mod)
|
|
2846
|
+
) {
|
|
2847
|
+
const src = this._resolveStructInfo({ _type: fromType });
|
|
2848
|
+
const dst = this._resolveStructInfo({ _type: targetType });
|
|
2849
|
+
if (src && dst && src.typeIndex !== dst.typeIndex) {
|
|
2850
|
+
this.emitCloneStruct(src, fromWType, dst);
|
|
2851
|
+
return;
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
|
|
1899
2855
|
if (fromWType === toWType) {
|
|
1900
2856
|
this.emitNarrowIntWrap(targetType);
|
|
1901
2857
|
return;
|
|
1902
2858
|
}
|
|
1903
2859
|
|
|
2860
|
+
// A named byte slice (`type MyBytes []byte`) converts like `[]byte`.
|
|
2861
|
+
const isByteElem = (t) => {
|
|
2862
|
+
const elem = this._getSliceElemType(t);
|
|
2863
|
+
return elem?.name === "byte" || elem?.name === "uint8";
|
|
2864
|
+
};
|
|
1904
2865
|
if (
|
|
1905
2866
|
fromWType === "externref" &&
|
|
1906
2867
|
isSliceType(targetType, this.mod.checker) &&
|
|
1907
|
-
(targetType
|
|
2868
|
+
isByteElem(targetType)
|
|
1908
2869
|
) {
|
|
1909
|
-
this._emitStringToByteSlice(
|
|
2870
|
+
this._emitStringToByteSlice({
|
|
2871
|
+
kind: "slice",
|
|
2872
|
+
elem: this._getSliceElemType(targetType),
|
|
2873
|
+
});
|
|
1910
2874
|
return;
|
|
1911
2875
|
}
|
|
1912
2876
|
|
|
1913
2877
|
if (
|
|
1914
2878
|
toWType === "externref" &&
|
|
1915
2879
|
isSliceType(fromType, this.mod.checker) &&
|
|
1916
|
-
(fromType
|
|
2880
|
+
isByteElem(fromType)
|
|
1917
2881
|
) {
|
|
1918
|
-
this._emitByteSliceToString(
|
|
2882
|
+
this._emitByteSliceToString({
|
|
2883
|
+
kind: "slice",
|
|
2884
|
+
elem: this._getSliceElemType(fromType),
|
|
2885
|
+
});
|
|
2886
|
+
return;
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2889
|
+
if (
|
|
2890
|
+
fromWType === "externref" &&
|
|
2891
|
+
isRuneSlice(targetType, this.mod.checker)
|
|
2892
|
+
) {
|
|
2893
|
+
this._emitStringToRuneSlice(targetType);
|
|
2894
|
+
return;
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
if (toWType === "externref" && isRuneSlice(fromType, this.mod.checker)) {
|
|
2898
|
+
this._emitRuneSliceToString(fromType);
|
|
1919
2899
|
return;
|
|
1920
2900
|
}
|
|
1921
2901
|
|
|
@@ -1960,6 +2940,64 @@ export class ExprsEmitter {
|
|
|
1960
2940
|
}
|
|
1961
2941
|
}
|
|
1962
2942
|
|
|
2943
|
+
_emitComplexConversion(targetType, fromType, fromWType) {
|
|
2944
|
+
const isTarget64 = isComplex64(targetType);
|
|
2945
|
+
const targetInfo = this.mod.getComplexType(
|
|
2946
|
+
isTarget64 ? "complex64" : "complex128",
|
|
2947
|
+
);
|
|
2948
|
+
if (isComplex(fromType)) {
|
|
2949
|
+
const isFrom64 = isComplex64(fromType);
|
|
2950
|
+
if (isTarget64 === isFrom64) return;
|
|
2951
|
+
const fromInfo = this.mod.getComplexType(
|
|
2952
|
+
isFrom64 ? "complex64" : "complex128",
|
|
2953
|
+
);
|
|
2954
|
+
const fromWTypeRef = {
|
|
2955
|
+
kind: "ref",
|
|
2956
|
+
nullable: true,
|
|
2957
|
+
typeIndex: fromInfo.typeIndex,
|
|
2958
|
+
typeName: fromInfo.name,
|
|
2959
|
+
};
|
|
2960
|
+
const cTmp = this.acquireTemp(fromWTypeRef);
|
|
2961
|
+
this.pushInstruction({ op: "local.set", index: cTmp });
|
|
2962
|
+
|
|
2963
|
+
for (let i = 0; i < 2; i++) {
|
|
2964
|
+
this.pushInstruction({ op: "local.get", index: cTmp });
|
|
2965
|
+
this.pushInstruction({
|
|
2966
|
+
op: "struct.get",
|
|
2967
|
+
typeIndex: fromInfo.typeIndex,
|
|
2968
|
+
typeName: fromInfo.name,
|
|
2969
|
+
fieldIndex: i,
|
|
2970
|
+
});
|
|
2971
|
+
this.pushInstruction(isTarget64 ? "f32.demote_f64" : "f64.promote_f32");
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
this.pushInstruction({
|
|
2975
|
+
op: "struct.new",
|
|
2976
|
+
typeIndex: targetInfo.typeIndex,
|
|
2977
|
+
typeName: targetInfo.name,
|
|
2978
|
+
});
|
|
2979
|
+
this.releaseTemp(cTmp, fromWTypeRef);
|
|
2980
|
+
return;
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2983
|
+
// From numeric scalar
|
|
2984
|
+
const targetF = targetInfo.elemWType;
|
|
2985
|
+
const srcSign = isSigned(fromType) ? "s" : "u";
|
|
2986
|
+
if (fromWType === "i32" || fromWType === "i64") {
|
|
2987
|
+
this.pushInstruction(`${targetF}.convert_${fromWType}_${srcSign}`);
|
|
2988
|
+
} else if (fromWType === "f32" && targetF === "f64") {
|
|
2989
|
+
this.pushInstruction("f64.promote_f32");
|
|
2990
|
+
} else if (fromWType === "f64" && targetF === "f32") {
|
|
2991
|
+
this.pushInstruction("f32.demote_f64");
|
|
2992
|
+
}
|
|
2993
|
+
this.pushInstruction({ op: `${targetF}.const`, value: 0.0 });
|
|
2994
|
+
this.pushInstruction({
|
|
2995
|
+
op: "struct.new",
|
|
2996
|
+
typeIndex: targetInfo.typeIndex,
|
|
2997
|
+
typeName: targetInfo.name,
|
|
2998
|
+
});
|
|
2999
|
+
}
|
|
3000
|
+
|
|
1963
3001
|
_emitStringToByteSlice(targetType) {
|
|
1964
3002
|
const elemGo = targetType.elem;
|
|
1965
3003
|
const sliceInfo = this.mod.getSliceType(elemGo);
|
|
@@ -2030,6 +3068,162 @@ export class ExprsEmitter {
|
|
|
2030
3068
|
this.releaseTemp(strTmp, "externref");
|
|
2031
3069
|
}
|
|
2032
3070
|
|
|
3071
|
+
// Pushes the code point at UTF-16 index `iTmp` of `strTmp` into `cpTmp`
|
|
3072
|
+
// and advances `iTmp` past it (two units for a surrogate pair).
|
|
3073
|
+
_emitNextCodePoint(strTmp, iTmp, cpTmp) {
|
|
3074
|
+
this.pushInstruction({ op: "local.get", index: strTmp });
|
|
3075
|
+
this.pushInstruction({ op: "local.get", index: iTmp });
|
|
3076
|
+
this.pushInstruction({
|
|
3077
|
+
op: "call",
|
|
3078
|
+
funcIndex: this.mod.getStringCodePointAtImportIndex(),
|
|
3079
|
+
});
|
|
3080
|
+
this.pushInstruction({ op: "local.set", index: cpTmp });
|
|
3081
|
+
this.pushInstruction({ op: "local.get", index: iTmp });
|
|
3082
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
3083
|
+
this.pushInstruction("i32.add");
|
|
3084
|
+
this.pushInstruction({ op: "local.get", index: cpTmp });
|
|
3085
|
+
this.pushInstruction({ op: "i32.const", value: 0xffff });
|
|
3086
|
+
this.pushInstruction("i32.gt_u");
|
|
3087
|
+
this.pushInstruction("i32.add");
|
|
3088
|
+
this.pushInstruction({ op: "local.set", index: iTmp });
|
|
3089
|
+
}
|
|
3090
|
+
|
|
3091
|
+
// Emits `block loop (br_if 1 (i >= n)) body br 0 end end`.
|
|
3092
|
+
_emitCountedLoop(iTmp, nTmp, body) {
|
|
3093
|
+
this.pushInstruction({ op: "block", blockType: "void" });
|
|
3094
|
+
this.pushInstruction({ op: "loop", blockType: "void" });
|
|
3095
|
+
this.pushInstruction({ op: "local.get", index: iTmp });
|
|
3096
|
+
this.pushInstruction({ op: "local.get", index: nTmp });
|
|
3097
|
+
this.pushInstruction("i32.ge_s");
|
|
3098
|
+
this.pushInstruction({ op: "br_if", depth: 1 });
|
|
3099
|
+
body();
|
|
3100
|
+
this.pushInstruction({ op: "br", depth: 0 });
|
|
3101
|
+
this.pushInstruction("end");
|
|
3102
|
+
this.pushInstruction("end");
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
// []rune(s): one pass counts the code points, a second fills an exactly
|
|
3106
|
+
// sized array (cap == len, as for every slice GoFront creates).
|
|
3107
|
+
_emitStringToRuneSlice(targetType) {
|
|
3108
|
+
const sliceInfo = this.mod.getSliceType(this._getSliceElemType(targetType));
|
|
3109
|
+
const arrInfo = sliceInfo.arrInfo;
|
|
3110
|
+
const arrW = { kind: "ref", nullable: true, typeIndex: arrInfo.typeIndex };
|
|
3111
|
+
const strTmp = this.acquireTemp("externref");
|
|
3112
|
+
const unitsTmp = this.acquireTemp("i32");
|
|
3113
|
+
const iTmp = this.acquireTemp("i32");
|
|
3114
|
+
const kTmp = this.acquireTemp("i32");
|
|
3115
|
+
const cpTmp = this.acquireTemp("i32");
|
|
3116
|
+
const arrTmp = this.acquireTemp(arrW);
|
|
3117
|
+
this.pushInstruction({ op: "local.tee", index: strTmp });
|
|
3118
|
+
this.pushInstruction({
|
|
3119
|
+
op: "call",
|
|
3120
|
+
funcIndex: this.mod.getStringLenImportIndex(),
|
|
3121
|
+
});
|
|
3122
|
+
this.pushInstruction({ op: "local.set", index: unitsTmp });
|
|
3123
|
+
const resetIndex = () => {
|
|
3124
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
3125
|
+
this.pushInstruction({ op: "local.set", index: iTmp });
|
|
3126
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
3127
|
+
this.pushInstruction({ op: "local.set", index: kTmp });
|
|
3128
|
+
};
|
|
3129
|
+
const bumpK = () => {
|
|
3130
|
+
this.pushInstruction({ op: "local.get", index: kTmp });
|
|
3131
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
3132
|
+
this.pushInstruction("i32.add");
|
|
3133
|
+
this.pushInstruction({ op: "local.set", index: kTmp });
|
|
3134
|
+
};
|
|
3135
|
+
resetIndex();
|
|
3136
|
+
this._emitCountedLoop(iTmp, unitsTmp, () => {
|
|
3137
|
+
this._emitNextCodePoint(strTmp, iTmp, cpTmp);
|
|
3138
|
+
bumpK();
|
|
3139
|
+
});
|
|
3140
|
+
this.pushInstruction({ op: "local.get", index: kTmp });
|
|
3141
|
+
this.pushInstruction({
|
|
3142
|
+
op: "array.new_default",
|
|
3143
|
+
typeIndex: arrInfo.typeIndex,
|
|
3144
|
+
});
|
|
3145
|
+
this.pushInstruction({ op: "local.set", index: arrTmp });
|
|
3146
|
+
resetIndex();
|
|
3147
|
+
this._emitCountedLoop(iTmp, unitsTmp, () => {
|
|
3148
|
+
this._emitNextCodePoint(strTmp, iTmp, cpTmp);
|
|
3149
|
+
this.pushInstruction({ op: "local.get", index: arrTmp });
|
|
3150
|
+
this.pushInstruction({ op: "local.get", index: kTmp });
|
|
3151
|
+
this.pushInstruction({ op: "local.get", index: cpTmp });
|
|
3152
|
+
this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
|
|
3153
|
+
bumpK();
|
|
3154
|
+
});
|
|
3155
|
+
this.pushInstruction({ op: "local.get", index: arrTmp });
|
|
3156
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
3157
|
+
this.pushInstruction({ op: "local.get", index: kTmp });
|
|
3158
|
+
this.pushInstruction({ op: "local.get", index: kTmp });
|
|
3159
|
+
this.pushInstruction({ op: "struct.new", typeIndex: sliceInfo.typeIndex });
|
|
3160
|
+
this.releaseTemp(arrTmp, arrW);
|
|
3161
|
+
this.releaseTemp(cpTmp, "i32");
|
|
3162
|
+
this.releaseTemp(kTmp, "i32");
|
|
3163
|
+
this.releaseTemp(iTmp, "i32");
|
|
3164
|
+
this.releaseTemp(unitsTmp, "i32");
|
|
3165
|
+
this.releaseTemp(strTmp, "externref");
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
// string(runes): concatenates `str_from_code_point` of each element.
|
|
3169
|
+
_emitRuneSliceToString(fromType) {
|
|
3170
|
+
const sliceInfo = this.mod.getSliceType(this._getSliceElemType(fromType));
|
|
3171
|
+
const arrInfo = sliceInfo.arrInfo;
|
|
3172
|
+
const sliceW = {
|
|
3173
|
+
kind: "ref",
|
|
3174
|
+
nullable: true,
|
|
3175
|
+
typeIndex: sliceInfo.typeIndex,
|
|
3176
|
+
};
|
|
3177
|
+
const arrW = { kind: "ref", nullable: true, typeIndex: arrInfo.typeIndex };
|
|
3178
|
+
const sTmp = this.acquireTemp(sliceW);
|
|
3179
|
+
this.pushInstruction({ op: "local.set", index: sTmp });
|
|
3180
|
+
const arrTmp = this.acquireTemp(arrW);
|
|
3181
|
+
const offTmp = this.acquireTemp("i32");
|
|
3182
|
+
const lenTmp = this.acquireTemp("i32");
|
|
3183
|
+
const capTmp = this.acquireTemp("i32");
|
|
3184
|
+
this._emitSliceUnpack(sTmp, sliceInfo, {
|
|
3185
|
+
arr: arrTmp,
|
|
3186
|
+
off: offTmp,
|
|
3187
|
+
len: lenTmp,
|
|
3188
|
+
cap: capTmp,
|
|
3189
|
+
});
|
|
3190
|
+
this.releaseTemp(sTmp, sliceW);
|
|
3191
|
+
this.releaseTemp(capTmp, "i32");
|
|
3192
|
+
const accTmp = this.acquireTemp("externref");
|
|
3193
|
+
this.emitBasicLit({ kind: "BasicLit", litKind: "STRING", value: "" });
|
|
3194
|
+
this.pushInstruction({ op: "local.set", index: accTmp });
|
|
3195
|
+
const iTmp = this.acquireTemp("i32");
|
|
3196
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
3197
|
+
this.pushInstruction({ op: "local.set", index: iTmp });
|
|
3198
|
+
this._emitCountedLoop(iTmp, lenTmp, () => {
|
|
3199
|
+
this.pushInstruction({ op: "local.get", index: accTmp });
|
|
3200
|
+
this.pushInstruction({ op: "local.get", index: arrTmp });
|
|
3201
|
+
this.pushInstruction({ op: "local.get", index: offTmp });
|
|
3202
|
+
this.pushInstruction({ op: "local.get", index: iTmp });
|
|
3203
|
+
this.pushInstruction("i32.add");
|
|
3204
|
+
this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
|
|
3205
|
+
this.pushInstruction({
|
|
3206
|
+
op: "call",
|
|
3207
|
+
funcIndex: this.mod.getStringFromCodePointImportIndex(),
|
|
3208
|
+
});
|
|
3209
|
+
this.pushInstruction({
|
|
3210
|
+
op: "call",
|
|
3211
|
+
funcIndex: this.mod.getStringConcatImportIndex(),
|
|
3212
|
+
});
|
|
3213
|
+
this.pushInstruction({ op: "local.set", index: accTmp });
|
|
3214
|
+
this.pushInstruction({ op: "local.get", index: iTmp });
|
|
3215
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
3216
|
+
this.pushInstruction("i32.add");
|
|
3217
|
+
this.pushInstruction({ op: "local.set", index: iTmp });
|
|
3218
|
+
});
|
|
3219
|
+
this.pushInstruction({ op: "local.get", index: accTmp });
|
|
3220
|
+
this.releaseTemp(iTmp, "i32");
|
|
3221
|
+
this.releaseTemp(accTmp, "externref");
|
|
3222
|
+
this.releaseTemp(lenTmp, "i32");
|
|
3223
|
+
this.releaseTemp(offTmp, "i32");
|
|
3224
|
+
this.releaseTemp(arrTmp, arrW);
|
|
3225
|
+
}
|
|
3226
|
+
|
|
2033
3227
|
_emitByteSliceToString(fromType) {
|
|
2034
3228
|
const elemGo = fromType.elem;
|
|
2035
3229
|
const sliceInfo = this.mod.getSliceType(elemGo);
|