tjs-lang 0.9.0 → 0.9.1

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/src/lang/wasm.ts CHANGED
@@ -262,6 +262,19 @@ const SimdOp = {
262
262
  f32x4_sub: 0xe5,
263
263
  f32x4_mul: 0xe6,
264
264
  f32x4_div: 0xe7,
265
+ f32x4_min: 0xe8,
266
+ f32x4_max: 0xe9,
267
+
268
+ // f32x4 comparisons → v128 lane mask (all-1s / all-0s per lane)
269
+ f32x4_eq: 0x41,
270
+ f32x4_ne: 0x42,
271
+ f32x4_lt: 0x43,
272
+ f32x4_gt: 0x44,
273
+ f32x4_le: 0x45,
274
+ f32x4_ge: 0x46,
275
+
276
+ // Lane-wise blend: bitselect(a, b, mask) — bits of a where mask=1, else b.
277
+ v128_bitselect: 0x52,
265
278
  } as const
266
279
 
267
280
  /** Reverse lookup for SIMD opcodes */
@@ -1160,6 +1173,18 @@ function compileBinaryExpr(
1160
1173
  return [Op.f64_const, ...encodeF64(0)]
1161
1174
  }
1162
1175
 
1176
+ // Lint the i32/i32 division footgun: `/` with two integer operands (loop vars,
1177
+ // `0`-annotated params, int literals) does TRUNCATING integer division, and the
1178
+ // coercion to f64 only happens at the *next* operator — so `x / w - 0.5` is
1179
+ // silently `0 - 0.5` for all `x < w`. Warn once per block; add `+ 0.0` to an
1180
+ // operand to force f64 division. (UI-#4 — this is a warning, not an error:
1181
+ // integer division may be intended.)
1182
+ if (node.operator === '/' && opType === 'i32') {
1183
+ const msg =
1184
+ "integer division: '/' with two i32 operands truncates (result coerces to f64 only at the next operator). Add `+ 0.0` to an operand to force f64 division."
1185
+ if (!ctx.warnings.includes(msg)) ctx.warnings.push(msg)
1186
+ }
1187
+
1163
1188
  return [...leftCode, ...rightCode, opcode]
1164
1189
  }
1165
1190
 
@@ -1808,8 +1833,17 @@ function compileSIMDCall(
1808
1833
  case 'f32x4_add':
1809
1834
  case 'f32x4_sub':
1810
1835
  case 'f32x4_mul':
1811
- case 'f32x4_div': {
1812
- // Binary v128 op: f32x4_add(a, b) → v128
1836
+ case 'f32x4_div':
1837
+ case 'f32x4_min':
1838
+ case 'f32x4_max':
1839
+ case 'f32x4_eq':
1840
+ case 'f32x4_ne':
1841
+ case 'f32x4_lt':
1842
+ case 'f32x4_gt':
1843
+ case 'f32x4_le':
1844
+ case 'f32x4_ge': {
1845
+ // Binary v128 op: f(a, b) → v128. The comparisons (eq/ne/lt/gt/le/ge)
1846
+ // return a v128 lane mask (all-1s where true, all-0s else).
1813
1847
  code.push(...compileExpression(args[0], ctx))
1814
1848
  code.push(...compileExpression(args[1], ctx))
1815
1849
  const opMap: Record<string, number> = {
@@ -1817,11 +1851,36 @@ function compileSIMDCall(
1817
1851
  f32x4_sub: SimdOp.f32x4_sub,
1818
1852
  f32x4_mul: SimdOp.f32x4_mul,
1819
1853
  f32x4_div: SimdOp.f32x4_div,
1854
+ f32x4_min: SimdOp.f32x4_min,
1855
+ f32x4_max: SimdOp.f32x4_max,
1856
+ f32x4_eq: SimdOp.f32x4_eq,
1857
+ f32x4_ne: SimdOp.f32x4_ne,
1858
+ f32x4_lt: SimdOp.f32x4_lt,
1859
+ f32x4_gt: SimdOp.f32x4_gt,
1860
+ f32x4_le: SimdOp.f32x4_le,
1861
+ f32x4_ge: SimdOp.f32x4_ge,
1820
1862
  }
1821
1863
  code.push(...encodeSIMD(opMap[name]))
1822
1864
  return code
1823
1865
  }
1824
1866
 
1867
+ case 'f32x4_select': {
1868
+ // Branch-free lane blend: f32x4_select(mask, a, b) → a where the mask lane
1869
+ // is true (all-1s, e.g. from a compare), b where false. Maps to
1870
+ // v128.bitselect(a, b, mask) — bits of a where mask=1, of b where mask=0.
1871
+ if (args.length !== 3) {
1872
+ ctx.errors.push(
1873
+ `f32x4_select expects (mask, a, b) — 3 args, got ${args.length}`
1874
+ )
1875
+ return [Op.f64_const, ...encodeF64(0)]
1876
+ }
1877
+ code.push(...compileExpression(args[1], ctx)) // a (bitselect v1)
1878
+ code.push(...compileExpression(args[2], ctx)) // b (bitselect v2)
1879
+ code.push(...compileExpression(args[0], ctx)) // mask
1880
+ code.push(...encodeSIMD(SimdOp.v128_bitselect))
1881
+ return code
1882
+ }
1883
+
1825
1884
  case 'f32x4_neg':
1826
1885
  case 'f32x4_sqrt': {
1827
1886
  // Unary v128 op: f32x4_neg(a) → v128