konpeito 0.4.2 → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/THIRD_PARTY_LICENSES.md +26 -0
- data/lib/konpeito/codegen/cruby_backend.rb +26 -0
- data/lib/konpeito/codegen/inliner.rb +10 -0
- data/lib/konpeito/codegen/llvm_generator.rb +96 -53
- data/lib/konpeito/codegen/mruby_backend.rb +65 -0
- data/lib/konpeito/compiler.rb +18 -1
- data/lib/konpeito/hir/builder.rb +71 -0
- data/lib/konpeito/hir/nodes.rb +17 -0
- data/lib/konpeito/rbs_inline/preprocessor.rb +47 -4
- data/lib/konpeito/stdlib/clay/clay.rb +66 -0
- data/lib/konpeito/stdlib/clay/clay.rbs +169 -0
- data/lib/konpeito/stdlib/clay/clay_native.c +471 -0
- data/lib/konpeito/stdlib/raylib/raylib.rb +6 -0
- data/lib/konpeito/stdlib/raylib/raylib.rbs +14 -0
- data/lib/konpeito/stdlib/raylib/raylib_native.c +7 -0
- data/lib/konpeito/type_checker/hm_inferrer.rb +11 -0
- data/lib/konpeito/type_checker/rbs_loader.rb +51 -5
- data/lib/konpeito/type_checker/types.rb +18 -5
- data/lib/konpeito/version.rb +1 -1
- data/vendor/clay/LICENSE +22 -0
- data/vendor/clay/clay.h +4454 -0
- data/vendor/clay/clay_impl.c +2 -0
- data/vendor/clay/clay_renderer_raylib.c +265 -0
- data/vendor/yyjson/yyjson.c +9568 -0
- data/vendor/yyjson/yyjson.h +7942 -0
- metadata +11 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 453b0bb32e4ef66b8b6020d262200e070caf01fb483cff4a0ebd6ceba46495b3
|
|
4
|
+
data.tar.gz: 7b38fb2807e1649150a0febae5defcb8f964967a715770db1de857d6409f88b3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: afa5fd10db773c8e1bb3c399a39cf140b1c7577271420750368551ee80cab864683d9455860ddba185b0d53cc221df42b5beb3a7334fbc67b56747e4a7bd6e0f
|
|
7
|
+
data.tar.gz: 7fbe956b09d489f9af313c0fa6600f81960cb02cb72d95f912a4d50d9f658b4b7bff35528d43b73c223d011d0aa5c35f6c63bf09ce5f1873c14605cd7274395e
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ All notable changes to Konpeito will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.5.0] - 2026-03-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Clay UI layout stdlib**: Flexbox-style UI layout library for the mruby backend. `module Clay` auto-detected like raylib — 40+ `%a{cfunc}` bindings covering lifecycle, element construction, text, borders, scrolling, floating elements, and bulk rendering via the official raylib renderer. Vendored Clay v0.14 under `vendor/clay/`.
|
|
14
|
+
- **Clay UI demo**: `examples/mruby_clay_ui/clay_demo.rb` — sidebar + main content layout with TTF fonts
|
|
15
|
+
- **Memory Match game**: `examples/mruby_clay_ui/memory_game.rb` — card matching game using Clay layout system with module NativeArray game state
|
|
16
|
+
- **Module NativeArray**: fixed-size global arrays shared across functions via RBS module instance variables (`@field: NativeArray[T, N]`). Compiles to LLVM global arrays — no C wrapper file needed. Available on LLVM (CRuby) and mruby backends.
|
|
17
|
+
- **Inline RBS module blocks**: `# @rbs module Foo ... # @rbs end` comment blocks are now extracted by the preprocessor and emitted as raw RBS, enabling module NativeArray declarations without separate `.rbs` files or empty `module Foo; end` stubs.
|
|
18
|
+
- **Space Invaders example rewrite**: `examples/mruby_space_invaders/` rewritten to use module NativeArray with inline RBS — single `.rb` file, no C wrapper or separate `.rbs` needed
|
|
19
|
+
- **Third-party license file**: `THIRD_PARTY_LICENSES.md` summarizing vendored library licenses (yyjson, Clay)
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- **NativeArray GEP stride**: fix `gep` → `gep2` for all NativeArray element access (14 call sites). With LLVM opaque pointers, `gep` used array type as stride instead of element type, causing memory corruption and SIGSEGV crashes.
|
|
23
|
+
|
|
8
24
|
## [0.4.2] - 2026-03-10
|
|
9
25
|
|
|
10
26
|
### Fixed
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Third-Party Licenses
|
|
2
|
+
|
|
3
|
+
Konpeito vendors the following third-party libraries. Each library's license
|
|
4
|
+
file is kept alongside its source under `vendor/`.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## yyjson
|
|
9
|
+
|
|
10
|
+
- **Location:** `vendor/yyjson/`
|
|
11
|
+
- **License:** MIT
|
|
12
|
+
- **Copyright:** (c) 2020 YaoYuan
|
|
13
|
+
- **URL:** https://github.com/ibireme/yyjson
|
|
14
|
+
|
|
15
|
+
See `vendor/yyjson/LICENSE` for the full license text.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Clay
|
|
20
|
+
|
|
21
|
+
- **Location:** `vendor/clay/`
|
|
22
|
+
- **License:** zlib/libpng
|
|
23
|
+
- **Copyright:** (c) 2024 Nic Barker
|
|
24
|
+
- **URL:** https://github.com/nicbarker/clay
|
|
25
|
+
|
|
26
|
+
See `vendor/clay/LICENSE` for the full license text.
|
|
@@ -996,6 +996,13 @@ module Konpeito
|
|
|
996
996
|
flags.concat(yyjson_objs)
|
|
997
997
|
end
|
|
998
998
|
|
|
999
|
+
# Add clay object files if Clay stdlib is used
|
|
1000
|
+
clay_used = @rbs_loader&.cfunc_methods&.any? { |k, _| k.start_with?("Clay.") }
|
|
1001
|
+
if clay_used
|
|
1002
|
+
clay_objs = ensure_clay_compiled
|
|
1003
|
+
flags.concat(clay_objs)
|
|
1004
|
+
end
|
|
1005
|
+
|
|
999
1006
|
if @rbs_loader
|
|
1000
1007
|
stdlib_ui_dir = File.expand_path("../stdlib/ui", __dir__)
|
|
1001
1008
|
|
|
@@ -1051,6 +1058,25 @@ module Konpeito
|
|
|
1051
1058
|
[yyjson_obj, wrapper_obj]
|
|
1052
1059
|
end
|
|
1053
1060
|
|
|
1061
|
+
# Compile vendored clay.h implementation if Clay stdlib is used
|
|
1062
|
+
# Returns array of object file paths
|
|
1063
|
+
def ensure_clay_compiled
|
|
1064
|
+
clay_dir = File.expand_path("../../../vendor/clay", __dir__)
|
|
1065
|
+
clay_impl_c = File.join(clay_dir, "clay_impl.c")
|
|
1066
|
+
clay_impl_obj = File.join(clay_dir, "clay_impl.o")
|
|
1067
|
+
|
|
1068
|
+
return [] unless File.exist?(clay_impl_c)
|
|
1069
|
+
|
|
1070
|
+
cc = find_llvm_tool("clang") || "cc"
|
|
1071
|
+
|
|
1072
|
+
unless File.exist?(clay_impl_obj) && File.mtime(clay_impl_obj) > File.mtime(clay_impl_c)
|
|
1073
|
+
cmd = [cc, "-c", "-O2", "-fPIC", "-o", clay_impl_obj, clay_impl_c]
|
|
1074
|
+
system(*cmd) or return []
|
|
1075
|
+
end
|
|
1076
|
+
|
|
1077
|
+
[clay_impl_obj]
|
|
1078
|
+
end
|
|
1079
|
+
|
|
1054
1080
|
def ruby_link_flags
|
|
1055
1081
|
# Get Ruby's linker flags for extensions
|
|
1056
1082
|
[
|
|
@@ -532,6 +532,16 @@ module Konpeito
|
|
|
532
532
|
new_array = transform_value(inst.array, prefix, param_map)
|
|
533
533
|
HIR::MultiWriteExtract.new(array: new_array, index: inst.index, type: inst.type, result_var: new_result)
|
|
534
534
|
|
|
535
|
+
when HIR::ModuleNativeArrayRef
|
|
536
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
537
|
+
HIR::ModuleNativeArrayRef.new(
|
|
538
|
+
module_name: inst.module_name,
|
|
539
|
+
field_name: inst.field_name,
|
|
540
|
+
element_type: inst.element_type,
|
|
541
|
+
size: inst.size,
|
|
542
|
+
result_var: new_result
|
|
543
|
+
)
|
|
544
|
+
|
|
535
545
|
else
|
|
536
546
|
# For other instructions, just return as-is with renamed result
|
|
537
547
|
inst
|
|
@@ -100,6 +100,9 @@ module Konpeito
|
|
|
100
100
|
# Scan HIR to register NativeClassTypes before code generation
|
|
101
101
|
scan_for_native_class_types(hir_program)
|
|
102
102
|
|
|
103
|
+
# Declare LLVM globals for module-level NativeArray fields
|
|
104
|
+
declare_module_native_arrays(hir_program)
|
|
105
|
+
|
|
103
106
|
# Pre-scan: detect parameters that can receive nil at call sites
|
|
104
107
|
@nil_possible_params = scan_nil_possible_params(hir_program)
|
|
105
108
|
|
|
@@ -2182,6 +2185,8 @@ module Konpeito
|
|
|
2182
2185
|
generate_sized_queue_push(inst)
|
|
2183
2186
|
when HIR::SizedQueuePop
|
|
2184
2187
|
generate_sized_queue_pop(inst)
|
|
2188
|
+
when HIR::ModuleNativeArrayRef
|
|
2189
|
+
generate_module_native_array_ref(inst)
|
|
2185
2190
|
when HIR::NativeArrayAlloc
|
|
2186
2191
|
generate_native_array_alloc(inst)
|
|
2187
2192
|
when HIR::NativeArrayGet
|
|
@@ -9841,8 +9846,8 @@ module Konpeito
|
|
|
9841
9846
|
# Normalize negative indices
|
|
9842
9847
|
index_i64 = normalize_native_index(index_i64, inst.receiver)
|
|
9843
9848
|
|
|
9844
|
-
# GEP to get element pointer
|
|
9845
|
-
elem_ptr = @builder.
|
|
9849
|
+
# GEP to get element pointer (use gep2 with element type for correct stride)
|
|
9850
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [index_i64], "elem_ptr")
|
|
9846
9851
|
|
|
9847
9852
|
# Load element
|
|
9848
9853
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
@@ -9872,8 +9877,9 @@ module Konpeito
|
|
|
9872
9877
|
target_type = element_type == :Int64 ? :i64 : :double
|
|
9873
9878
|
converted_value = convert_value(store_value, value_type, target_type)
|
|
9874
9879
|
|
|
9875
|
-
# GEP to get element pointer
|
|
9876
|
-
|
|
9880
|
+
# GEP to get element pointer (use gep2 with element type for correct stride)
|
|
9881
|
+
llvm_elem_type = native_array_element_llvm_type(element_type)
|
|
9882
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [index_i64], "elem_ptr")
|
|
9877
9883
|
|
|
9878
9884
|
# Store element
|
|
9879
9885
|
@builder.store(converted_value, elem_ptr)
|
|
@@ -9893,46 +9899,19 @@ module Konpeito
|
|
|
9893
9899
|
len_value = @variables["#{receiver_var}_len"]
|
|
9894
9900
|
return index_i64 unless len_value
|
|
9895
9901
|
|
|
9896
|
-
|
|
9902
|
+
# Use branchless select instead of conditional branches to avoid
|
|
9903
|
+
# creating new basic blocks that break surrounding PHI node predecessors.
|
|
9897
9904
|
is_negative = @builder.icmp(:slt, index_i64, LLVM::Int64.from_i(0))
|
|
9898
|
-
|
|
9899
|
-
neg_bb = func.basic_blocks.append("idx_neg")
|
|
9900
|
-
pos_bb = func.basic_blocks.append("idx_pos")
|
|
9901
|
-
merge_bb = func.basic_blocks.append("idx_merge")
|
|
9902
|
-
|
|
9903
|
-
@builder.cond(is_negative, neg_bb, pos_bb)
|
|
9904
|
-
|
|
9905
|
-
@builder.position_at_end(neg_bb)
|
|
9906
9905
|
normalized = @builder.add(len_value, index_i64, "neg_idx")
|
|
9907
|
-
@builder.
|
|
9908
|
-
|
|
9909
|
-
@builder.position_at_end(pos_bb)
|
|
9910
|
-
@builder.br(merge_bb)
|
|
9911
|
-
|
|
9912
|
-
@builder.position_at_end(merge_bb)
|
|
9913
|
-
@builder.phi(LLVM::Int64, { neg_bb => normalized, pos_bb => index_i64 })
|
|
9906
|
+
@builder.select(is_negative, normalized, index_i64, "norm_idx")
|
|
9914
9907
|
end
|
|
9915
9908
|
|
|
9916
9909
|
# Normalize negative index for StaticArray (compile-time known size)
|
|
9917
9910
|
def normalize_static_index(index_i64, size)
|
|
9918
|
-
|
|
9911
|
+
# Use branchless select to avoid creating new basic blocks
|
|
9919
9912
|
is_negative = @builder.icmp(:slt, index_i64, LLVM::Int64.from_i(0))
|
|
9920
|
-
|
|
9921
|
-
neg_bb = func.basic_blocks.append("sidx_neg")
|
|
9922
|
-
pos_bb = func.basic_blocks.append("sidx_pos")
|
|
9923
|
-
merge_bb = func.basic_blocks.append("sidx_merge")
|
|
9924
|
-
|
|
9925
|
-
@builder.cond(is_negative, neg_bb, pos_bb)
|
|
9926
|
-
|
|
9927
|
-
@builder.position_at_end(neg_bb)
|
|
9928
9913
|
normalized = @builder.add(LLVM::Int64.from_i(size), index_i64, "neg_sidx")
|
|
9929
|
-
@builder.
|
|
9930
|
-
|
|
9931
|
-
@builder.position_at_end(pos_bb)
|
|
9932
|
-
@builder.br(merge_bb)
|
|
9933
|
-
|
|
9934
|
-
@builder.position_at_end(merge_bb)
|
|
9935
|
-
@builder.phi(LLVM::Int64, { neg_bb => normalized, pos_bb => index_i64 })
|
|
9914
|
+
@builder.select(is_negative, normalized, index_i64, "norm_sidx")
|
|
9936
9915
|
end
|
|
9937
9916
|
|
|
9938
9917
|
# Generate NativeArray length access: arr.length
|
|
@@ -9990,7 +9969,7 @@ module Konpeito
|
|
|
9990
9969
|
@builder.position_at_end(loop_body)
|
|
9991
9970
|
|
|
9992
9971
|
# Get element via GEP + load (no rb_ary_entry!)
|
|
9993
|
-
elem_ptr = @builder.
|
|
9972
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
9994
9973
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
9995
9974
|
|
|
9996
9975
|
# Set up block parameter (unboxed!)
|
|
@@ -10040,7 +10019,7 @@ module Konpeito
|
|
|
10040
10019
|
convert_value(val, val_type, type_tag)
|
|
10041
10020
|
else
|
|
10042
10021
|
# Use first element as initial, start from index 1
|
|
10043
|
-
elem_ptr = @builder.
|
|
10022
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [LLVM::Int64.from_i(0)], "first_ptr")
|
|
10044
10023
|
@builder.load2(llvm_elem_type, elem_ptr, "first_elem")
|
|
10045
10024
|
end
|
|
10046
10025
|
|
|
@@ -10072,7 +10051,7 @@ module Konpeito
|
|
|
10072
10051
|
|
|
10073
10052
|
# Load accumulator and element (both unboxed)
|
|
10074
10053
|
acc_value = @builder.load2(llvm_elem_type, acc_alloca, "acc")
|
|
10075
|
-
elem_ptr = @builder.
|
|
10054
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
10076
10055
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
10077
10056
|
|
|
10078
10057
|
# Set up block parameters
|
|
@@ -10172,7 +10151,7 @@ module Konpeito
|
|
|
10172
10151
|
@builder.position_at_end(loop_body)
|
|
10173
10152
|
|
|
10174
10153
|
# Load element (unboxed)
|
|
10175
|
-
elem_ptr = @builder.
|
|
10154
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
10176
10155
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
10177
10156
|
|
|
10178
10157
|
# Set up block parameter
|
|
@@ -10255,7 +10234,7 @@ module Konpeito
|
|
|
10255
10234
|
@builder.position_at_end(loop_body)
|
|
10256
10235
|
|
|
10257
10236
|
# Load element (unboxed)
|
|
10258
|
-
elem_ptr = @builder.
|
|
10237
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
10259
10238
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
10260
10239
|
|
|
10261
10240
|
# Set up block parameter
|
|
@@ -10353,7 +10332,7 @@ module Konpeito
|
|
|
10353
10332
|
@builder.position_at_end(loop_body)
|
|
10354
10333
|
|
|
10355
10334
|
# Load element and store to alloca for later use
|
|
10356
|
-
elem_ptr = @builder.
|
|
10335
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
10357
10336
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
10358
10337
|
@builder.store(elem_value, elem_alloca)
|
|
10359
10338
|
|
|
@@ -10445,7 +10424,7 @@ module Konpeito
|
|
|
10445
10424
|
@builder.position_at_end(loop_body)
|
|
10446
10425
|
|
|
10447
10426
|
# Load element
|
|
10448
|
-
elem_ptr = @builder.
|
|
10427
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
10449
10428
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
10450
10429
|
|
|
10451
10430
|
# Set up block parameter
|
|
@@ -10553,7 +10532,7 @@ module Konpeito
|
|
|
10553
10532
|
|
|
10554
10533
|
# Load accumulator and element
|
|
10555
10534
|
acc_value = @builder.load2(llvm_elem_type, acc_alloca, "acc")
|
|
10556
|
-
elem_ptr = @builder.
|
|
10535
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
10557
10536
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
10558
10537
|
|
|
10559
10538
|
# Add (unboxed)
|
|
@@ -10614,7 +10593,7 @@ module Konpeito
|
|
|
10614
10593
|
# Non-empty: initialize with first element
|
|
10615
10594
|
@builder.position_at_end(non_empty_block)
|
|
10616
10595
|
result_alloca = @builder.alloca(llvm_elem_type, "na_minmax_result")
|
|
10617
|
-
first_ptr = @builder.
|
|
10596
|
+
first_ptr = @builder.gep2(llvm_elem_type, array_ptr, [LLVM::Int64.from_i(0)], "first_ptr")
|
|
10618
10597
|
first_elem = @builder.load2(llvm_elem_type, first_ptr, "first")
|
|
10619
10598
|
@builder.store(first_elem, result_alloca)
|
|
10620
10599
|
|
|
@@ -10634,7 +10613,7 @@ module Konpeito
|
|
|
10634
10613
|
|
|
10635
10614
|
# Load current result and element
|
|
10636
10615
|
current_result = @builder.load2(llvm_elem_type, result_alloca, "current")
|
|
10637
|
-
elem_ptr = @builder.
|
|
10616
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
10638
10617
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
10639
10618
|
|
|
10640
10619
|
# Compare and select
|
|
@@ -10704,11 +10683,16 @@ module Konpeito
|
|
|
10704
10683
|
# Get the array pointer from a receiver HIR value
|
|
10705
10684
|
def get_native_array_ptr(receiver)
|
|
10706
10685
|
case receiver
|
|
10686
|
+
when HIR::LoadLocal
|
|
10687
|
+
# LoadLocal may have result_var=nil (e.g., from inliner transform_value).
|
|
10688
|
+
# Use the variable name to look up the array pointer.
|
|
10689
|
+
@variables[receiver.var.name] || (receiver.result_var && @variables[receiver.result_var]) ||
|
|
10690
|
+
raise("NativeArray LoadLocal var '#{receiver.var.name}' not in @variables")
|
|
10707
10691
|
when HIR::Instruction
|
|
10708
10692
|
if receiver.result_var
|
|
10709
10693
|
@variables[receiver.result_var]
|
|
10710
10694
|
else
|
|
10711
|
-
raise "NativeArray receiver has no result_var"
|
|
10695
|
+
raise "NativeArray receiver has no result_var: #{receiver.class}"
|
|
10712
10696
|
end
|
|
10713
10697
|
when String
|
|
10714
10698
|
@variables[receiver]
|
|
@@ -10720,6 +10704,8 @@ module Konpeito
|
|
|
10720
10704
|
# Get the variable name from a receiver for length lookup
|
|
10721
10705
|
def get_receiver_var_name(receiver)
|
|
10722
10706
|
case receiver
|
|
10707
|
+
when HIR::LoadLocal
|
|
10708
|
+
receiver.var.name
|
|
10723
10709
|
when HIR::Instruction
|
|
10724
10710
|
receiver.result_var
|
|
10725
10711
|
when String
|
|
@@ -10729,6 +10715,62 @@ module Konpeito
|
|
|
10729
10715
|
end
|
|
10730
10716
|
end
|
|
10731
10717
|
|
|
10718
|
+
# ========================================
|
|
10719
|
+
# Module-level NativeArray (LLVM global variables)
|
|
10720
|
+
# ========================================
|
|
10721
|
+
|
|
10722
|
+
# Declare LLVM global arrays for all modules with NativeArray fields
|
|
10723
|
+
def declare_module_native_arrays(hir)
|
|
10724
|
+
@module_native_arrays ||= {}
|
|
10725
|
+
|
|
10726
|
+
hir.modules.each do |module_def|
|
|
10727
|
+
next unless module_def.native_array_fields&.any?
|
|
10728
|
+
|
|
10729
|
+
module_def.native_array_fields.each do |field_name, info|
|
|
10730
|
+
elem_type = info[:element_type]
|
|
10731
|
+
size = info[:size]
|
|
10732
|
+
llvm_elem_type = elem_type == :Int64 ? LLVM::Int64 : LLVM::Double
|
|
10733
|
+
|
|
10734
|
+
global_name = "konpeito_#{module_def.name}_#{field_name}"
|
|
10735
|
+
array_type = LLVM::Type.array(llvm_elem_type, size)
|
|
10736
|
+
|
|
10737
|
+
global = @mod.globals.add(array_type, global_name)
|
|
10738
|
+
global.initializer = LLVM::Constant.null(array_type)
|
|
10739
|
+
global.linkage = :internal
|
|
10740
|
+
|
|
10741
|
+
@module_native_arrays["#{module_def.name}_#{field_name}"] = {
|
|
10742
|
+
global: global,
|
|
10743
|
+
element_type: elem_type,
|
|
10744
|
+
size: size,
|
|
10745
|
+
llvm_elem_type: llvm_elem_type,
|
|
10746
|
+
array_type: array_type
|
|
10747
|
+
}
|
|
10748
|
+
end
|
|
10749
|
+
end
|
|
10750
|
+
end
|
|
10751
|
+
|
|
10752
|
+
# Generate a reference to a module-level NativeArray global
|
|
10753
|
+
def generate_module_native_array_ref(inst)
|
|
10754
|
+
key = "#{inst.module_name}_#{inst.field_name}"
|
|
10755
|
+
info = @module_native_arrays[key]
|
|
10756
|
+
raise "Unknown module NativeArray: #{key}" unless info
|
|
10757
|
+
|
|
10758
|
+
# GEP to get pointer to first element of global array
|
|
10759
|
+
array_ptr = @builder.gep2(
|
|
10760
|
+
info[:array_type], info[:global],
|
|
10761
|
+
[LLVM::Int64.from_i(0), LLVM::Int64.from_i(0)],
|
|
10762
|
+
"#{inst.field_name}_ptr"
|
|
10763
|
+
)
|
|
10764
|
+
|
|
10765
|
+
if inst.result_var
|
|
10766
|
+
@variables[inst.result_var] = array_ptr
|
|
10767
|
+
@variable_types[inst.result_var] = :native_array
|
|
10768
|
+
@variables["#{inst.result_var}_len"] = LLVM::Int64.from_i(inst.size)
|
|
10769
|
+
end
|
|
10770
|
+
|
|
10771
|
+
array_ptr
|
|
10772
|
+
end
|
|
10773
|
+
|
|
10732
10774
|
# Allocate a NativeArray on the stack
|
|
10733
10775
|
# Returns a pointer to contiguous memory + stores length
|
|
10734
10776
|
def generate_native_array_alloc(inst)
|
|
@@ -10778,8 +10820,8 @@ module Konpeito
|
|
|
10778
10820
|
index_value, index_type = get_value_with_type(inst.index)
|
|
10779
10821
|
index_i64 = index_type == :i64 ? index_value : @builder.call(@rb_num2long, index_value)
|
|
10780
10822
|
|
|
10781
|
-
# GEP to get element pointer
|
|
10782
|
-
elem_ptr = @builder.
|
|
10823
|
+
# GEP to get element pointer (use gep2 with element type for correct stride)
|
|
10824
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [index_i64], "elem_ptr")
|
|
10783
10825
|
|
|
10784
10826
|
if native_array_has_class_element?(element_type)
|
|
10785
10827
|
# For NativeClass elements, return pointer to struct (for field access)
|
|
@@ -10820,8 +10862,9 @@ module Konpeito
|
|
|
10820
10862
|
target_type = element_type == :Int64 ? :i64 : :double
|
|
10821
10863
|
converted_value = convert_value(store_value, value_type, target_type)
|
|
10822
10864
|
|
|
10823
|
-
# GEP to get element pointer
|
|
10824
|
-
|
|
10865
|
+
# GEP to get element pointer (use gep2 with element type for correct stride)
|
|
10866
|
+
llvm_elem_type = native_array_element_llvm_type(element_type)
|
|
10867
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [index_i64], "elem_ptr")
|
|
10825
10868
|
|
|
10826
10869
|
# Store element
|
|
10827
10870
|
@builder.store(converted_value, elem_ptr)
|
|
@@ -12919,8 +12962,8 @@ module Konpeito
|
|
|
12919
12962
|
# Get JSON element at index
|
|
12920
12963
|
elem_val = @builder.call(@yyjson_arr_get, root, idx, "json_elem")
|
|
12921
12964
|
|
|
12922
|
-
# Get pointer to NativeClass struct in the array
|
|
12923
|
-
struct_ptr = @builder.
|
|
12965
|
+
# Get pointer to NativeClass struct in the array (use gep2 for correct stride)
|
|
12966
|
+
struct_ptr = @builder.gep2(llvm_elem_type, array_ptr, [idx], "elem_struct_ptr")
|
|
12924
12967
|
|
|
12925
12968
|
# Parse fields from JSON object into struct
|
|
12926
12969
|
fields = element_class.fields
|
|
@@ -58,6 +58,10 @@ module Konpeito
|
|
|
58
58
|
extra_obj_files << extra_obj
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
+
# Compile vendored Clay library if used
|
|
62
|
+
clay_objs = ensure_clay_compiled
|
|
63
|
+
extra_obj_files.concat(clay_objs)
|
|
64
|
+
|
|
61
65
|
obj_files = [obj_file, init_obj_file, helpers_obj_file] + extra_obj_files
|
|
62
66
|
|
|
63
67
|
# Link into standalone executable
|
|
@@ -901,6 +905,34 @@ module Konpeito
|
|
|
901
905
|
)
|
|
902
906
|
end
|
|
903
907
|
|
|
908
|
+
# Include Clay if used
|
|
909
|
+
clay_used = @extra_c_files.any? { |f| File.basename(f).include?("clay") }
|
|
910
|
+
if clay_used
|
|
911
|
+
sections << license_section(
|
|
912
|
+
"Clay",
|
|
913
|
+
"zlib/libpng",
|
|
914
|
+
"Copyright (c) 2024 Nic Barker",
|
|
915
|
+
"https://github.com/nicbarker/clay",
|
|
916
|
+
<<~ZLIB
|
|
917
|
+
This software is provided 'as-is', without any express or implied warranty.
|
|
918
|
+
In no event will the authors be held liable for any damages arising from the
|
|
919
|
+
use of this software.
|
|
920
|
+
|
|
921
|
+
Permission is granted to anyone to use this software for any purpose,
|
|
922
|
+
including commercial applications, and to alter it and redistribute it freely,
|
|
923
|
+
subject to the following restrictions:
|
|
924
|
+
|
|
925
|
+
1. The origin of this software must not be misrepresented; you must not claim
|
|
926
|
+
that you wrote the original software. If you use this software in a product,
|
|
927
|
+
an acknowledgment in the product documentation would be appreciated but is
|
|
928
|
+
not required.
|
|
929
|
+
2. Altered source versions must be plainly marked as such, and must not be
|
|
930
|
+
misrepresented as being the original software.
|
|
931
|
+
3. This notice may not be removed or altered from any source distribution.
|
|
932
|
+
ZLIB
|
|
933
|
+
)
|
|
934
|
+
end
|
|
935
|
+
|
|
904
936
|
# Include raylib if linked
|
|
905
937
|
ffi_libs = @rbs_loader&.all_ffi_libraries || []
|
|
906
938
|
if ffi_libs.any? { |lib| lib.to_s.include?("raylib") }
|
|
@@ -1160,8 +1192,41 @@ module Konpeito
|
|
|
1160
1192
|
end
|
|
1161
1193
|
end
|
|
1162
1194
|
|
|
1195
|
+
# Compile vendored clay.h implementation if Clay stdlib is used
|
|
1196
|
+
# Returns array of object file paths
|
|
1197
|
+
def ensure_clay_compiled
|
|
1198
|
+
clay_used = @extra_c_files.any? { |f| File.basename(f).include?("clay") }
|
|
1199
|
+
return [] unless clay_used
|
|
1200
|
+
|
|
1201
|
+
clay_dir = File.expand_path("../../../vendor/clay", __dir__)
|
|
1202
|
+
clay_impl_c = File.join(clay_dir, "clay_impl.c")
|
|
1203
|
+
clay_impl_obj = File.join(clay_dir, "clay_impl.o")
|
|
1204
|
+
|
|
1205
|
+
return [] unless File.exist?(clay_impl_c)
|
|
1206
|
+
|
|
1207
|
+
cc, cc_flags = cross_cc_with_flags
|
|
1208
|
+
cflags = cross_compiling? ? Platform.cross_mruby_cflags(@cross_mruby_dir) : (Platform.mruby_cflags rescue "-O2")
|
|
1209
|
+
|
|
1210
|
+
# Compile clay_impl.c (only if stale)
|
|
1211
|
+
unless File.exist?(clay_impl_obj) && File.mtime(clay_impl_obj) > File.mtime(clay_impl_c)
|
|
1212
|
+
cmd = [*cc, "-c", "-O2"]
|
|
1213
|
+
cmd.concat(cc_flags)
|
|
1214
|
+
cmd += ["-o", clay_impl_obj, clay_impl_c]
|
|
1215
|
+
system(*cmd) or return []
|
|
1216
|
+
end
|
|
1217
|
+
|
|
1218
|
+
[clay_impl_obj]
|
|
1219
|
+
end
|
|
1220
|
+
|
|
1163
1221
|
def ffi_include_flags
|
|
1164
1222
|
flags = []
|
|
1223
|
+
|
|
1224
|
+
# Always add vendored Clay include path if Clay stdlib is used
|
|
1225
|
+
clay_dir = File.expand_path("../../../vendor/clay", __dir__)
|
|
1226
|
+
if @extra_c_files.any? { |f| File.basename(f).include?("clay") } && Dir.exist?(clay_dir)
|
|
1227
|
+
flags << "-I#{clay_dir}"
|
|
1228
|
+
end
|
|
1229
|
+
|
|
1165
1230
|
if cross_compiling?
|
|
1166
1231
|
# When cross-compiling, use cross library include paths
|
|
1167
1232
|
flags << "-I#{@cross_libs_dir}/../include" if @cross_libs_dir && Dir.exist?("#{@cross_libs_dir}/../include")
|
data/lib/konpeito/compiler.rb
CHANGED
|
@@ -652,7 +652,8 @@ module Konpeito
|
|
|
652
652
|
|
|
653
653
|
# Map of stdlib modules: module name pattern => stdlib directory name
|
|
654
654
|
STDLIB_MODULE_MAP = {
|
|
655
|
-
"Raylib" => "raylib"
|
|
655
|
+
"Raylib" => "raylib",
|
|
656
|
+
"Clay" => "clay"
|
|
656
657
|
}.freeze
|
|
657
658
|
|
|
658
659
|
# Scan AST for known stdlib module references and auto-add their RBS paths
|
|
@@ -694,6 +695,7 @@ module Konpeito
|
|
|
694
695
|
end
|
|
695
696
|
|
|
696
697
|
# Auto-include stdlib C files based on FFI libraries detected by RBS loader
|
|
698
|
+
# and vendored stdlibs detected by module reference
|
|
697
699
|
def inject_stdlib_c_files(extra_c_files)
|
|
698
700
|
return extra_c_files unless @rbs_loader
|
|
699
701
|
|
|
@@ -716,6 +718,21 @@ module Konpeito
|
|
|
716
718
|
end
|
|
717
719
|
end
|
|
718
720
|
|
|
721
|
+
# Also include vendored stdlib C files detected by module reference
|
|
722
|
+
# (for stdlibs without %a{ffi} that use vendored C libraries)
|
|
723
|
+
@rbs_paths.each do |rbs_path|
|
|
724
|
+
STDLIB_MODULE_MAP.each_value do |stdlib_name|
|
|
725
|
+
stdlib_rbs = File.expand_path("stdlib/#{stdlib_name}/#{stdlib_name}.rbs", __dir__)
|
|
726
|
+
next unless rbs_path == stdlib_rbs
|
|
727
|
+
|
|
728
|
+
stdlib_c = File.expand_path("stdlib/#{stdlib_name}/#{stdlib_name}_native.c", __dir__)
|
|
729
|
+
if File.exist?(stdlib_c) && !extra_c_files.include?(stdlib_c)
|
|
730
|
+
extra_c_files << stdlib_c
|
|
731
|
+
log "Auto-including stdlib C wrapper: #{stdlib_name}" if verbose
|
|
732
|
+
end
|
|
733
|
+
end
|
|
734
|
+
end
|
|
735
|
+
|
|
719
736
|
extra_c_files
|
|
720
737
|
end
|
|
721
738
|
end
|
data/lib/konpeito/hir/builder.rb
CHANGED
|
@@ -43,11 +43,34 @@ module Konpeito
|
|
|
43
43
|
|
|
44
44
|
def build(typed_ast)
|
|
45
45
|
visit(typed_ast)
|
|
46
|
+
ensure_module_native_array_defs
|
|
46
47
|
@program
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
private
|
|
50
51
|
|
|
52
|
+
# Ensure ModuleDefs exist for all modules with NativeArray fields from RBS.
|
|
53
|
+
# When using inline RBS (rbs_inline), the Ruby source may not contain
|
|
54
|
+
# `module Foo; end`, so visit_module is never called and no ModuleDef
|
|
55
|
+
# is created. This method fills in the missing ModuleDefs.
|
|
56
|
+
def ensure_module_native_array_defs
|
|
57
|
+
return unless @rbs_loader
|
|
58
|
+
|
|
59
|
+
@rbs_loader.each_native_module do |module_name, mod_type|
|
|
60
|
+
next unless mod_type.native_array_fields&.any?
|
|
61
|
+
next if @program.modules.any? { |m| m.name == module_name.to_s }
|
|
62
|
+
|
|
63
|
+
module_def = ModuleDef.new(
|
|
64
|
+
name: module_name.to_s,
|
|
65
|
+
methods: [],
|
|
66
|
+
singleton_methods: [],
|
|
67
|
+
constants: {}
|
|
68
|
+
)
|
|
69
|
+
module_def.native_array_fields = mod_type.native_array_fields
|
|
70
|
+
@program.modules << module_def
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
51
74
|
def visit(typed_node)
|
|
52
75
|
return nil unless typed_node
|
|
53
76
|
|
|
@@ -884,6 +907,13 @@ module Konpeito
|
|
|
884
907
|
|
|
885
908
|
@current_module = old_module
|
|
886
909
|
|
|
910
|
+
# Get NativeArray fields from RBS if available
|
|
911
|
+
mod_native_array_fields = {}
|
|
912
|
+
if @rbs_loader
|
|
913
|
+
mod_type = @rbs_loader.native_module_type(name)
|
|
914
|
+
mod_native_array_fields = mod_type.native_array_fields if mod_type
|
|
915
|
+
end
|
|
916
|
+
|
|
887
917
|
# Merge into existing ModuleDef if this module was already opened (multi-file projects)
|
|
888
918
|
existing_module_def = @program.modules.find { |m| m.name == name }
|
|
889
919
|
if existing_module_def
|
|
@@ -892,6 +922,7 @@ module Konpeito
|
|
|
892
922
|
existing_module_def.module_function_methods.concat(module_function_method_names)
|
|
893
923
|
existing_module_def.private_methods.merge(module_private_method_names)
|
|
894
924
|
existing_module_def.constants.merge!(module_constants)
|
|
925
|
+
existing_module_def.native_array_fields.merge!(mod_native_array_fields)
|
|
895
926
|
else
|
|
896
927
|
module_def = ModuleDef.new(
|
|
897
928
|
name: name,
|
|
@@ -901,6 +932,7 @@ module Konpeito
|
|
|
901
932
|
)
|
|
902
933
|
module_def.module_function_methods.concat(module_function_method_names)
|
|
903
934
|
module_def.private_methods.merge(module_private_method_names)
|
|
935
|
+
module_def.native_array_fields = mod_native_array_fields
|
|
904
936
|
@program.modules << module_def
|
|
905
937
|
end
|
|
906
938
|
NilLit.new
|
|
@@ -1798,6 +1830,11 @@ module Konpeito
|
|
|
1798
1830
|
def visit_call(typed_node)
|
|
1799
1831
|
node = typed_node.node
|
|
1800
1832
|
|
|
1833
|
+
# Check for module-level NativeArray field access (e.g., Inv.gs)
|
|
1834
|
+
if (mod_arr_info = module_native_array_access?(typed_node))
|
|
1835
|
+
return visit_module_native_array_ref(mod_arr_info)
|
|
1836
|
+
end
|
|
1837
|
+
|
|
1801
1838
|
# Check for NativeArray.new(size) pattern
|
|
1802
1839
|
if native_array_new_call?(typed_node)
|
|
1803
1840
|
return visit_native_array_new(typed_node)
|
|
@@ -2366,6 +2403,40 @@ module Konpeito
|
|
|
2366
2403
|
receiver_child.node.name.to_s == "NativeArray"
|
|
2367
2404
|
end
|
|
2368
2405
|
|
|
2406
|
+
# Check if this is a module-level NativeArray field access (e.g., Inv.gs)
|
|
2407
|
+
# Returns field info hash or nil
|
|
2408
|
+
def module_native_array_access?(typed_node)
|
|
2409
|
+
return nil unless @rbs_loader
|
|
2410
|
+
|
|
2411
|
+
# Receiver must be a constant read (e.g., Inv)
|
|
2412
|
+
receiver_child = typed_node.children.first
|
|
2413
|
+
return nil unless receiver_child&.node_type == :constant_read
|
|
2414
|
+
|
|
2415
|
+
module_name = receiver_child.node.name.to_s
|
|
2416
|
+
mod_type = @rbs_loader.native_module_type(module_name)
|
|
2417
|
+
return nil unless mod_type
|
|
2418
|
+
|
|
2419
|
+
field_name = typed_node.node.name.to_s.to_sym
|
|
2420
|
+
field_info = mod_type.lookup_native_array_field(field_name)
|
|
2421
|
+
return nil unless field_info
|
|
2422
|
+
|
|
2423
|
+
{ module_name: module_name, field_name: field_name, **field_info }
|
|
2424
|
+
end
|
|
2425
|
+
|
|
2426
|
+
# Emit a ModuleNativeArrayRef for module-level NativeArray access
|
|
2427
|
+
def visit_module_native_array_ref(info)
|
|
2428
|
+
result_var = new_temp_var
|
|
2429
|
+
inst = ModuleNativeArrayRef.new(
|
|
2430
|
+
module_name: info[:module_name],
|
|
2431
|
+
field_name: info[:field_name],
|
|
2432
|
+
element_type: info[:element_type],
|
|
2433
|
+
size: info[:size],
|
|
2434
|
+
result_var: result_var
|
|
2435
|
+
)
|
|
2436
|
+
emit(inst)
|
|
2437
|
+
inst
|
|
2438
|
+
end
|
|
2439
|
+
|
|
2369
2440
|
# Check if this is a NativeArray element access: arr[i] where arr is NativeArray[NativeClass]
|
|
2370
2441
|
def native_array_element_access?(typed_node)
|
|
2371
2442
|
method_name = typed_node.node.name.to_s
|