cpyte 4.3.2__tar.gz → 4.4.0__tar.gz

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 (47) hide show
  1. {cpyte-4.3.2/source/cpyte.egg-info → cpyte-4.4.0}/PKG-INFO +1 -1
  2. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/__init__.py +1 -1
  3. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/bytecoding.py +89 -60
  4. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/compiling.py +53 -12
  5. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/lsp_server.py +5 -3
  6. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/mainpie.py +209 -31
  7. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/runtime_scorpion.c +172 -18
  8. cpyte-4.4.0/source/cpyte/scorpion_abi.py +220 -0
  9. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/semantic_analasis.py +184 -18
  10. {cpyte-4.3.2 → cpyte-4.4.0/source/cpyte.egg-info}/PKG-INFO +1 -1
  11. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte.egg-info/SOURCES.txt +3 -0
  12. cpyte-4.4.0/test/test_scorpion_abi.py +359 -0
  13. cpyte-4.4.0/test/test_scorpion_dl.py +329 -0
  14. {cpyte-4.3.2 → cpyte-4.4.0}/MANIFEST.in +0 -0
  15. {cpyte-4.3.2 → cpyte-4.4.0}/pyproject.toml +0 -0
  16. {cpyte-4.3.2 → cpyte-4.4.0}/readme.md +0 -0
  17. {cpyte-4.3.2 → cpyte-4.4.0}/setup.cfg +0 -0
  18. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/__main__.py +0 -0
  19. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/_bignum_bc.py +0 -0
  20. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/_runtime_bc.py +0 -0
  21. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/astparse.py +0 -0
  22. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/bignum.c +0 -0
  23. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/clib.py +0 -0
  24. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/elf2sef.py +0 -0
  25. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/extension_hooks.py +0 -0
  26. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/formatter.py +0 -0
  27. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/gc_runtime.c +0 -0
  28. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/generate_bc.py +0 -0
  29. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/lexar.py +0 -0
  30. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/linker.py +0 -0
  31. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/mksef.py +0 -0
  32. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/optimizations.py +0 -0
  33. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/package_manifest.py +0 -0
  34. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/runtime.c +0 -0
  35. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/sef.py +0 -0
  36. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/ugc.h +0 -0
  37. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/ui.py +0 -0
  38. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/update_check.py +0 -0
  39. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/winjit_patch.py +0 -0
  40. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte/winjit_stubs.c +0 -0
  41. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte.egg-info/dependency_links.txt +0 -0
  42. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte.egg-info/entry_points.txt +0 -0
  43. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte.egg-info/requires.txt +0 -0
  44. {cpyte-4.3.2 → cpyte-4.4.0}/source/cpyte.egg-info/top_level.txt +0 -0
  45. {cpyte-4.3.2 → cpyte-4.4.0}/test/test_bignum_jit.py +0 -0
  46. {cpyte-4.3.2 → cpyte-4.4.0}/test/test_fuzz_del_has.py +0 -0
  47. {cpyte-4.3.2 → cpyte-4.4.0}/test/test_sef_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cpyte
3
- Version: 4.3.2
3
+ Version: 4.4.0
4
4
  Summary: The Cpyte programming language compiler
5
5
  Author: Hoang Duy Tung
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
- __version__ = "4.3.2"
1
+ __version__ = "4.4.0"
2
2
 
3
3
  # ---------------------------------------------------------------------------
4
4
  # Public library API.
@@ -261,6 +261,12 @@ class LLVM:
261
261
  # i32 fallthrough) lets `(size_t)ptr`/`(ptr)(size_t)` round-trips
262
262
  # preserve the full address instead of truncating to 32 bits.
263
263
  result = ir.IntType(64)
264
+ elif t in ("uint8", "char"):
265
+ result = ir.IntType(8)
266
+ elif t == "uint16":
267
+ result = ir.IntType(16)
268
+ elif t in ("uint32", "unsigned"):
269
+ result = ir.IntType(32)
264
270
  elif t == "bool":
265
271
  result = ir.IntType(1)
266
272
  elif t in ("float", "double"):
@@ -269,8 +275,6 @@ class LLVM:
269
275
  result = ir.VoidType()
270
276
  elif t == "str":
271
277
  result = ir.PointerType(ir.IntType(8))
272
- elif t == "char":
273
- result = ir.IntType(8)
274
278
  elif t == "void*" or t == "big" or t == "ubig":
275
279
  result = ir.PointerType(ir.IntType(8))
276
280
  elif t == "dynamic":
@@ -674,9 +678,11 @@ class LLVM:
674
678
  debug_instrument=False,
675
679
  debug_nids=None,
676
680
  debug_step_ids=None,
681
+ scorpion=False,
677
682
  ):
678
683
  self.module = ir.Module("main")
679
684
  self.no_gc = no_gc
685
+ self.scorpion = scorpion
680
686
  self.use_native_eh = use_native_eh
681
687
  # --- cpdb debug instrumentation (opt-in, off by default) -------------
682
688
  # When enabled, the emitter emits one `cpdbd_t_<nid>` tracer per step
@@ -813,6 +819,9 @@ class LLVM:
813
819
  input_str_fn = ir.Function(self.module, input_str_ty, "input_str")
814
820
  self.functions["input_str"] = input_str_fn
815
821
 
822
+ if self.scorpion:
823
+ self._declare_scorpion_abi()
824
+
816
825
  # BigNum runtime functions
817
826
  bignum_fns = [
818
827
  ("bigint_new", _i8ptr, []),
@@ -986,6 +995,38 @@ class LLVM:
986
995
  )
987
996
  self._code_skip.initializer = ir.Constant(ir.IntType(1), 0) # type: ignore[attr-defined]
988
997
 
998
+ def _declare_scorpion_abi(self):
999
+ """Declare the Scorpion ABI wrappers and register `ScorpionLibInfo`.
1000
+
1001
+ Enabled by the `#scorpion` source directive. Each wrapper is an extern
1002
+ resolved by `runtime_scorpion.c`, which ships the same `ecall`
1003
+ encoding the Xtensa-only inline wrappers in `abi/scorpion.h` use (the
1004
+ `a0`-`a7` register names are the RISC-V argument registers too, so the
1005
+ ABI is identical on both architectures).
1006
+
1007
+ `ScorpionLibInfo` is materialized here rather than as a parsed
1008
+ StructDef because it has no node in the AST: the kernel writes the
1009
+ exact 32-byte layout through `scorpion_lib_info()`, so the `uint16`
1010
+ tail must really be i16 (seven i32 + two i16, no padding).
1011
+ """
1012
+ from . import scorpion_abi
1013
+
1014
+ st = self.module.context.get_identified_type(
1015
+ f"struct.{scorpion_abi.LIB_INFO_NAME}"
1016
+ )
1017
+ if st.is_opaque:
1018
+ st.set_body(*[self.llvm_type(t) for _, t in scorpion_abi.LIB_INFO_FIELDS])
1019
+ self.structs[scorpion_abi.LIB_INFO_NAME] = st
1020
+ self.struct_fields[scorpion_abi.LIB_INFO_NAME] = scorpion_abi.lib_info_fields()
1021
+
1022
+ for name, (ret_type, params, vararg) in scorpion_abi.FUNCTIONS.items():
1023
+ fnty = ir.FunctionType(
1024
+ self.llvm_type(ret_type),
1025
+ [self.llvm_type(t) for _, t in params],
1026
+ var_arg=vararg,
1027
+ )
1028
+ self.functions[name] = ir.Function(self.module, fnty, name)
1029
+
989
1030
  # CRITICAL CONSTANT FOLDING BUG
990
1031
  def _isolate_const_prop_branch(self, body):
991
1032
  """Run one control-flow branch with isolated constant propagation.
@@ -1451,8 +1492,17 @@ class LLVM:
1451
1492
 
1452
1493
  if os.environ.get("CPYTE_JIT_DEBUG"):
1453
1494
  import sys as _sys
1454
- print("[bytecoding] runtime hooks:", [h.name for h in self._hook_registry.get(HookStage.RUNTIME)], file=_sys.stderr)
1455
- print("[bytecoding] import_src_files:", self.import_src_files, file=_sys.stderr)
1495
+
1496
+ print(
1497
+ "[bytecoding] runtime hooks:",
1498
+ [h.name for h in self._hook_registry.get(HookStage.RUNTIME)],
1499
+ file=_sys.stderr,
1500
+ )
1501
+ print(
1502
+ "[bytecoding] import_src_files:",
1503
+ self.import_src_files,
1504
+ file=_sys.stderr,
1505
+ )
1456
1506
 
1457
1507
  for node in structs:
1458
1508
  if isinstance(node, ClassDef):
@@ -1638,25 +1688,20 @@ class LLVM:
1638
1688
  name = target.name
1639
1689
  if name in self.locals:
1640
1690
  ptr = self.locals[name]
1641
- if not isinstance(
1642
- getattr(ptr.type, "pointee", None), ir.PointerType
1643
- ):
1644
- self.builder.store(
1645
- ir.Constant(ptr.type.pointee, None), ptr
1646
- )
1691
+ if not isinstance(getattr(ptr.type, "pointee", None), ir.PointerType):
1692
+ self.builder.store(ir.Constant(ptr.type.pointee, None), ptr)
1647
1693
  for table in (self.locals, self.local_types, self.ssa_values):
1648
1694
  table.pop(name, None)
1649
1695
  self.const_vars.pop(name, None)
1650
1696
  return
1651
1697
  if kind == "dynamic":
1652
1698
  name = target.name
1653
- if name in self.locals and getattr(
1654
- self.locals[name].type, "pointee", None
1655
- ) == _DynValue:
1699
+ if (
1700
+ name in self.locals
1701
+ and getattr(self.locals[name].type, "pointee", None) == _DynValue
1702
+ ):
1656
1703
  zero = ir.Constant(_DynValue, ir.Undefined)
1657
- zero = self.builder.insert_value(
1658
- zero, ir.Constant(_i32, _DYN_NONE), 0
1659
- )
1704
+ zero = self.builder.insert_value(zero, ir.Constant(_i32, _DYN_NONE), 0)
1660
1705
  zero = self.builder.insert_value(zero, ir.Constant(_i64, 0), 1)
1661
1706
  self.builder.store(zero, self.locals[name])
1662
1707
  else:
@@ -2075,18 +2120,14 @@ class LLVM:
2075
2120
  index_fields = [Field("@vptr", "void*", token=node._token)] + all_fields
2076
2121
  field_tys = [self.llvm_type(f.type_expr) for f in index_fields]
2077
2122
  llvm_struct = self.structs.get(node.name)
2078
- if llvm_struct is None or not isinstance(
2079
- llvm_struct, ir.IdentifiedStructType
2080
- ):
2123
+ if llvm_struct is None or not isinstance(llvm_struct, ir.IdentifiedStructType):
2081
2124
  # Pre-register an opaque identified struct BEFORE resolving field
2082
2125
  # types so self-referential fields resolve to a pointer to this
2083
2126
  # type instead of silently degrading to `i32`. get_identified_type
2084
2127
  # registers into the module's context so the definition is
2085
2128
  # serialized into the IR. Fieldless sealed classes still register
2086
2129
  # (as an empty struct) so `new T` can allocate them.
2087
- llvm_struct = self.module.context.get_identified_type(
2088
- f"class.{node.name}"
2089
- )
2130
+ llvm_struct = self.module.context.get_identified_type(f"class.{node.name}")
2090
2131
  self.structs[node.name] = llvm_struct
2091
2132
  if field_tys or index_fields:
2092
2133
  if llvm_struct.is_opaque:
@@ -2141,9 +2182,7 @@ class LLVM:
2141
2182
  wb.ret(ret)
2142
2183
  self.functions[child_name] = wrapper
2143
2184
 
2144
- def _declare_class_method(
2145
- self, class_node: ClassDef, method: FuncDef
2146
- ):
2185
+ def _declare_class_method(self, class_node: ClassDef, method: FuncDef):
2147
2186
  """Create the `Class.method` header function if it doesn't exist yet."""
2148
2187
  class_ty = self.structs.get(class_node.name)
2149
2188
  if class_ty is None:
@@ -2197,17 +2236,13 @@ class LLVM:
2197
2236
  slots = self._class_slot_list(cls)
2198
2237
  vt_ty = ir.LiteralStructType(
2199
2238
  [_i64]
2200
- + [
2201
- self.functions[f"{d}.{n}"].function_type.as_pointer()
2202
- for n, d in slots
2203
- ]
2239
+ + [self.functions[f"{d}.{n}"].function_type.as_pointer() for n, d in slots]
2204
2240
  )
2205
2241
  cid = self._class_ids.setdefault(cls, len(self._class_ids))
2206
2242
  cv = ir.GlobalVariable(self.module, vt_ty, name=f"vt.{cls}")
2207
2243
  cv.initializer = ir.Constant(
2208
2244
  vt_ty,
2209
- [ir.Constant(_i64, cid)]
2210
- + [self.functions[f"{d}.{n}"] for n, d in slots],
2245
+ [ir.Constant(_i64, cid)] + [self.functions[f"{d}.{n}"] for n, d in slots],
2211
2246
  )
2212
2247
  self._vt_types[cls] = vt_ty
2213
2248
  self._vt_globals[cls] = cv
@@ -2356,7 +2391,9 @@ class LLVM:
2356
2391
  for i, arg in enumerate(node.args or []):
2357
2392
  init_args.append(
2358
2393
  self._coerce_call_arg(
2359
- self.emit(arg), ptypes[i + 1], getattr(arg, "inferred_type", None)
2394
+ self.emit(arg),
2395
+ ptypes[i + 1],
2396
+ getattr(arg, "inferred_type", None),
2360
2397
  )
2361
2398
  )
2362
2399
  self.builder.call(init_fn, init_args)
@@ -2694,7 +2731,9 @@ class LLVM:
2694
2731
  obj_decl = obj_decl or getattr(node.obj, "inferred_type", None)
2695
2732
  if not (obj_decl and str(obj_decl).endswith("[]")):
2696
2733
  call = Call(
2697
- Attr(node.obj, "__getitem__", token=getattr(node.obj, "_token", None)),
2734
+ Attr(
2735
+ node.obj, "__getitem__", token=getattr(node.obj, "_token", None)
2736
+ ),
2698
2737
  [node.index],
2699
2738
  token=node._token,
2700
2739
  )
@@ -4629,7 +4668,9 @@ class LLVM:
4629
4668
  typed class pointers and integer/fp literals."""
4630
4669
  if isinstance(expected, ir.PointerType):
4631
4670
  if isinstance(val.type, ir.PointerType):
4632
- return self.builder.bitcast(val, expected) if val.type != expected else val
4671
+ return (
4672
+ self.builder.bitcast(val, expected) if val.type != expected else val
4673
+ )
4633
4674
  if isinstance(val.type, ir.IntType):
4634
4675
  return self.builder.inttoptr(val, expected)
4635
4676
  return val
@@ -4640,7 +4681,9 @@ class LLVM:
4640
4681
  return self.builder.sext(val, expected)
4641
4682
  return self.builder.trunc(val, expected)
4642
4683
  return val
4643
- if isinstance(val.type, ir.DoubleType) or isinstance(val.type, ir.FloatType):
4684
+ if isinstance(val.type, ir.DoubleType) or isinstance(
4685
+ val.type, ir.FloatType
4686
+ ):
4644
4687
  return self.builder.fptosi(val, expected)
4645
4688
  if isinstance(val.type, ir.PointerType) and expected.width >= 32:
4646
4689
  return self.builder.ptrtoint(val, expected)
@@ -5731,7 +5774,8 @@ class LLVM:
5731
5774
  for arg in node.args:
5732
5775
  arg_val = self.emit(arg)
5733
5776
  arg_val = self._coerce_call_arg(
5734
- arg_val, slot_fnty.args[len(args)],
5777
+ arg_val,
5778
+ slot_fnty.args[len(args)],
5735
5779
  getattr(arg, "inferred_type", None),
5736
5780
  )
5737
5781
  args.append(arg_val)
@@ -5760,7 +5804,8 @@ class LLVM:
5760
5804
  for arg in node.args:
5761
5805
  arg_val = self.emit(arg)
5762
5806
  arg_val = self._coerce_call_arg(
5763
- arg_val, func.function_type.args[len(args)],
5807
+ arg_val,
5808
+ func.function_type.args[len(args)],
5764
5809
  getattr(arg, "inferred_type", None),
5765
5810
  )
5766
5811
  args.append(arg_val)
@@ -5859,10 +5904,7 @@ class LLVM:
5859
5904
  dunder_cls = node.callee._dunder_via
5860
5905
  if dunder_cls is None:
5861
5906
  dunder_cls = self._static_obj_class(node.callee)
5862
- if (
5863
- dunder_cls
5864
- and self._dunder_fn_name(dunder_cls, "__call__") is not None
5865
- ):
5907
+ if dunder_cls and self._dunder_fn_name(dunder_cls, "__call__") is not None:
5866
5908
  call = Call(
5867
5909
  Attr(node.callee, "__call__", token=node.callee._token),
5868
5910
  node.args,
@@ -6238,9 +6280,7 @@ class LLVM:
6238
6280
  for f in fields:
6239
6281
  fname = self._string_const(f.name)
6240
6282
  cmp_res = self.builder.call(self._strcmp_fn, [name_val, fname])
6241
- match = self.builder.icmp_signed(
6242
- "==", cmp_res, ir.Constant(_i32, 0)
6243
- )
6283
+ match = self.builder.icmp_signed("==", cmp_res, ir.Constant(_i32, 0))
6244
6284
  result = self.builder.or_(result, match)
6245
6285
  return result
6246
6286
  if kind == "get_attr_lit":
@@ -6259,12 +6299,8 @@ class LLVM:
6259
6299
  for i, f in enumerate(fields):
6260
6300
  fname = self._string_const(f.name)
6261
6301
  cmp_res = self.builder.call(self._strcmp_fn, [name_val, fname])
6262
- match = self.builder.icmp_signed(
6263
- "==", cmp_res, ir.Constant(_i32, 0)
6264
- )
6265
- idx = self.builder.select(
6266
- match, ir.Constant(_i32, i), idx
6267
- )
6302
+ match = self.builder.icmp_signed("==", cmp_res, ir.Constant(_i32, 0))
6303
+ idx = self.builder.select(match, ir.Constant(_i32, i), idx)
6268
6304
  struct_start = self.builder.gep(
6269
6305
  obj_ptr,
6270
6306
  [ir.Constant(_i32, 0), ir.Constant(_i32, 0)],
@@ -6289,9 +6325,7 @@ class LLVM:
6289
6325
  if getattr(self, "_ancestor_grid_emitted", False):
6290
6326
  return
6291
6327
  self._ancestor_grid_emitted = True
6292
- classes = [
6293
- c for c, n in self._struct_nodes.items() if isinstance(n, ClassDef)
6294
- ]
6328
+ classes = [c for c, n in self._struct_nodes.items() if isinstance(n, ClassDef)]
6295
6329
  chains = {}
6296
6330
  for c in classes:
6297
6331
  cur = self._struct_nodes.get(c)
@@ -6359,13 +6393,9 @@ class LLVM:
6359
6393
  self.builder.mul(rcid, ir.Constant(_i64, stride)),
6360
6394
  ir.Constant(_i64, depth_t),
6361
6395
  )
6362
- row = self.builder.gep(
6363
- grid, [ir.Constant(_i32, 0), off], inbounds=True
6364
- )
6396
+ row = self.builder.gep(grid, [ir.Constant(_i32, 0), off], inbounds=True)
6365
6397
  anc = self.builder.load(row)
6366
- return self.builder.icmp_signed(
6367
- "==", anc, ir.Constant(_i64, id_t)
6368
- )
6398
+ return self.builder.icmp_signed("==", anc, ir.Constant(_i64, id_t))
6369
6399
 
6370
6400
  def _emit_builtin_append(self, node):
6371
6401
  """append(arr, x) — grow a registered array by one element.
@@ -6941,7 +6971,6 @@ class LLVM:
6941
6971
  self._const_prop_f = saved_f
6942
6972
 
6943
6973
  def _emit_for_worker(self, node, var_name, iterable, body, iter_type, var_type):
6944
-
6945
6974
  # ---- 3. Bounded loop unrolling for constant-size list literals ----
6946
6975
  if (
6947
6976
  isinstance(iterable, ListLit)
@@ -1677,9 +1677,14 @@ def _map_process_libc(engine, mod):
1677
1677
  pass
1678
1678
  break
1679
1679
  if os.environ.get("CPYTE_JIT_DEBUG"):
1680
- unset = [f.name for f in mod.functions if f.is_declaration
1681
- and not f.name.startswith("llvm.") and f.name not in mapped
1682
- and f.name not in _explicit_mapped]
1680
+ unset = [
1681
+ f.name
1682
+ for f in mod.functions
1683
+ if f.is_declaration
1684
+ and not f.name.startswith("llvm.")
1685
+ and f.name not in mapped
1686
+ and f.name not in _explicit_mapped
1687
+ ]
1683
1688
  if unset:
1684
1689
  print("[jit] unresolved externs: %s" % sorted(unset), file=sys.stderr)
1685
1690
 
@@ -1817,7 +1822,9 @@ def _scorpion_tool(name):
1817
1822
  if os.path.isfile(bundled):
1818
1823
  return bundled
1819
1824
  sibling = os.path.join(
1820
- os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(_RUNTIME_SCORPION_C)))),
1825
+ os.path.dirname(
1826
+ os.path.dirname(os.path.dirname(os.path.dirname(_RUNTIME_SCORPION_C)))
1827
+ ),
1821
1828
  "WEW-scorpion",
1822
1829
  "tools" if name == "elf2sef.py" else "user",
1823
1830
  name,
@@ -1833,6 +1840,11 @@ def run_scorpion(
1833
1840
  pic=False,
1834
1841
  exports=None,
1835
1842
  auto_exports=None,
1843
+ requires=None,
1844
+ scopes=None,
1845
+ weaks=None,
1846
+ lazy=None,
1847
+ versym=None,
1836
1848
  ):
1837
1849
  """Compile a Cpyte module for Scorpion (RV32 bare-metal) producing a SEF file.
1838
1850
  Please use it good! :):)
@@ -1840,11 +1852,20 @@ def run_scorpion(
1840
1852
  With pic=True the module is compiled with -fPIC and the final ELF is linked
1841
1853
  with --emit-relocs so it can be converted to a dynamic (SEF v2) image via
1842
1854
  WEW-scorpion/tools/elf2sef.py. `exports` names the symbols to mark as
1843
- exported for dynamic linking (libraries). `auto_exports` are additional
1844
- candidates discovered automatically (top-level `public` functions); only
1845
- those that actually resolve to a defined symbol in the emitted module are
1846
- added (elf2sef refuses unknown names). Returns (elf_file, [final exports])
1847
- so callers can report exactly what was published.
1855
+ exported for dynamic linking (libraries); an entry is either `NAME` or
1856
+ elf2sef's `SYMBOL=WIRE_NAME` form to publish SYMBOL under a different wire
1857
+ name. `auto_exports` are additional candidates discovered automatically
1858
+ (top-level `public` functions); only those that actually resolve to a
1859
+ defined symbol in the emitted module are added (elf2sef refuses unknown
1860
+ names).
1861
+
1862
+ `requires` (`NAME=VER`), `scopes`, `weaks`, `lazy` and `versym` are the
1863
+ remaining elf2sef dynamic-linking controls, forwarded verbatim; see
1864
+ WEW-scorpion/docs/dynamic-linking.md. `lazy`/`versym` are tri-state (None
1865
+ = leave elf2sef's default). They only apply to the pic path.
1866
+
1867
+ Returns (elf_file, [final exports]) so callers can report exactly what was
1868
+ published.
1848
1869
  """
1849
1870
  from llvmlite import binding
1850
1871
 
@@ -1858,9 +1879,7 @@ def run_scorpion(
1858
1879
  optimize(mod, opt_level)
1859
1880
  mod.verify()
1860
1881
 
1861
- defined = {
1862
- f.name for f in mod.functions if not getattr(f, "is_declaration", True)
1863
- }
1882
+ defined = {f.name for f in mod.functions if not getattr(f, "is_declaration", True)}
1864
1883
  final_exports = list(exports or [])
1865
1884
  seen = set(final_exports)
1866
1885
  if auto_exports:
@@ -1941,6 +1960,28 @@ def run_scorpion(
1941
1960
  if final_exports:
1942
1961
  for name in final_exports:
1943
1962
  cmd += ["--export", name]
1963
+ # elf2sef rejects --no-versym together with --lazy (a v2.0 import record
1964
+ # has no plt field, so a lazy site would point into an unresolvable
1965
+ # hole). Surface that here instead of as a subprocess error.
1966
+ if versym is False and lazy:
1967
+ print_err(
1968
+ "error: --no-versym cannot be combined with --lazy "
1969
+ "(a v2.0 import record has no plt field)"
1970
+ )
1971
+ raise SystemExit(1)
1972
+ for req in requires or ():
1973
+ cmd += ["--require", req]
1974
+ for scope in scopes or ():
1975
+ cmd += ["--scope", scope]
1976
+ for weak in weaks or ():
1977
+ cmd += ["--weak", weak]
1978
+ if lazy is not None:
1979
+ cmd.append("--lazy" if lazy else "--no-lazy")
1980
+ # elf2sef has no `--versym`: wide (v2.1) records are the default and
1981
+ # only the downgrade is selectable. So `--versym` is an explicit
1982
+ # restatement of the default and forwards nothing.
1983
+ if versym is False:
1984
+ cmd.append("--no-versym")
1944
1985
  r = subprocess.run(cmd, capture_output=True, text=True)
1945
1986
  if r.returncode != 0:
1946
1987
  print_err(f"error converting to SEF: {format_cc_diag(r.stderr)}")
@@ -11,14 +11,12 @@ from pygls.cli import start_server
11
11
  from pygls.lsp.server import LanguageServer
12
12
 
13
13
  from .astparse import (
14
- Assign,
15
14
  Call,
16
15
  ClassDef,
17
16
  EnumDef,
18
17
  FuncDef,
19
18
  If,
20
19
  Import,
21
- NewExpr,
22
20
  ParseError,
23
21
  StructDef,
24
22
  Switch,
@@ -31,6 +29,7 @@ from .astparse import (
31
29
  from .formatter import format_source
32
30
  from .lexar import Lexer, LexerError, TokenType
33
31
  from .semantic_analasis import SemanticAnalyzer
32
+ from . import scorpion_abi
34
33
  from .clib import _BUILTIN_LIB_HEADERS
35
34
 
36
35
  logging.basicConfig(level=logging.INFO, format="%(message)s")
@@ -282,7 +281,10 @@ def _analyze(source, filepath=None, workspace_root=None):
282
281
  return tokens, parsed, analyzer, error
283
282
  try:
284
283
  analyzer = SemanticAnalyzer(
285
- source, filepath=filepath, workspace_root=workspace_root
284
+ source,
285
+ filepath=filepath,
286
+ workspace_root=workspace_root,
287
+ scorpion=scorpion_abi.detect(source)[1],
286
288
  )
287
289
  analyzer.analyze(parsed)
288
290
  except Exception as e: