konpeito 0.4.1 → 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 +32 -0
- data/Rakefile +33 -1
- data/THIRD_PARTY_LICENSES.md +26 -0
- data/lib/konpeito/cli/run_command.rb +1 -1
- data/lib/konpeito/codegen/cruby_backend.rb +26 -0
- data/lib/konpeito/codegen/inliner.rb +10 -0
- data/lib/konpeito/codegen/llvm_generator.rb +109 -53
- data/lib/konpeito/codegen/mruby_backend.rb +65 -0
- data/lib/konpeito/compiler.rb +27 -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,38 @@ 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
|
+
|
|
24
|
+
## [0.4.2] - 2026-03-10
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
- **Linux symbol collision**: use `internal` linkage for LLVM callback functions to prevent flat namespace collisions on Linux
|
|
28
|
+
- **NativeClass ptr→VALUE**: add missing `ptr2int` conversion for NativeClass objects passed to CRuby APIs
|
|
29
|
+
- **JSON codegen tests**: skip when vendored yyjson source is unavailable (CI environments)
|
|
30
|
+
- **CI stabilization**: run codegen tests per-file in separate processes to prevent `.so` accumulation crashes
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- **macOS ARM CI job**: unit tests and codegen tests on `macos-latest` (ARM)
|
|
34
|
+
- **mruby CI job**: build and run verification with `konpeito run --target mruby`
|
|
35
|
+
- **Japanese tutorial**: add mruby backend section (5.5) matching English tutorial
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
- Update `actions/checkout` v4 → v6, `actions/setup-java` v4 → v5
|
|
39
|
+
|
|
8
40
|
## [0.4.1] - 2026-03-09
|
|
9
41
|
|
|
10
42
|
### Added
|
data/Rakefile
CHANGED
|
@@ -5,7 +5,34 @@ require "rake/testtask"
|
|
|
5
5
|
Rake::TestTask.new(:test) do |t|
|
|
6
6
|
t.libs << "test"
|
|
7
7
|
t.libs << "lib"
|
|
8
|
-
t.test_files = FileList["test/**/*_test.rb"]
|
|
8
|
+
t.test_files = FileList["test/**/*_test.rb"].exclude("test/codegen/**/*_test.rb")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
desc "Run codegen tests (each file in a separate process to avoid .so accumulation crashes)"
|
|
12
|
+
task "test:codegen" do
|
|
13
|
+
test_files = FileList["test/codegen/**/*_test.rb"].sort
|
|
14
|
+
failed = []
|
|
15
|
+
test_files.each do |f|
|
|
16
|
+
print "#{File.basename(f, '.rb')} "
|
|
17
|
+
unless system("bundle", "exec", "ruby", "-Ilib:test", f)
|
|
18
|
+
failed << f
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
puts
|
|
22
|
+
unless failed.empty?
|
|
23
|
+
abort "#{failed.size}/#{test_files.size} codegen test files failed:\n #{failed.join("\n ")}"
|
|
24
|
+
end
|
|
25
|
+
puts "All #{test_files.size} codegen test files passed."
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc "Run all tests (non-codegen + codegen in separate processes)"
|
|
29
|
+
task "test:all" => [:test] do
|
|
30
|
+
# Run codegen tests in a separate process so a crash doesn't kill non-codegen results
|
|
31
|
+
sh "bundle exec rake test:codegen" do |ok, _status|
|
|
32
|
+
unless ok
|
|
33
|
+
warn "Codegen tests failed (possibly due to native extension crash on ruby-head)"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
9
36
|
end
|
|
10
37
|
|
|
11
38
|
desc "Run conformance tests against Ruby/Native/JVM backends"
|
|
@@ -23,4 +50,9 @@ task "conformance:jvm" do
|
|
|
23
50
|
ruby "spec/conformance/runner.rb", "--jvm-only"
|
|
24
51
|
end
|
|
25
52
|
|
|
53
|
+
desc "Run CI compilation diagnostics"
|
|
54
|
+
task "test:diagnose" do
|
|
55
|
+
ruby "test/ci_diagnostic.rb"
|
|
56
|
+
end
|
|
57
|
+
|
|
26
58
|
task default: :test
|
|
@@ -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.
|
|
@@ -277,7 +277,7 @@ module Konpeito
|
|
|
277
277
|
|
|
278
278
|
# Include extra C files in cache key (they affect the binary)
|
|
279
279
|
source_dir = File.dirname(File.expand_path(source_file))
|
|
280
|
-
extra_c_files = Dir.glob(File.join(source_dir, "*.c"))
|
|
280
|
+
extra_c_files = Dir.glob(File.join(source_dir, "*.c"))
|
|
281
281
|
|
|
282
282
|
options_hash = {
|
|
283
283
|
"inline_rbs" => options[:inline_rbs].to_s,
|
|
@@ -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
|
|
@@ -2985,6 +2990,9 @@ module Konpeito
|
|
|
2985
2990
|
# Convert bool to Ruby true/false
|
|
2986
2991
|
is_true = @builder.icmp(:ne, value, LLVM::Int8.from_i(0))
|
|
2987
2992
|
@builder.select(is_true, qtrue, qfalse)
|
|
2993
|
+
when [:native_class, :value]
|
|
2994
|
+
# NativeClass struct pointer to VALUE (i64)
|
|
2995
|
+
@builder.ptr2int(value, LLVM::Int64)
|
|
2988
2996
|
when [:i64, :double]
|
|
2989
2997
|
@builder.si2fp(value, LLVM::Double)
|
|
2990
2998
|
when [:double, :i64]
|
|
@@ -4826,6 +4834,7 @@ module Konpeito
|
|
|
4826
4834
|
callback_func = @mod.functions.add(callback_name,
|
|
4827
4835
|
[value_type, value_type, LLVM::Int32, LLVM::Pointer(value_type), value_type],
|
|
4828
4836
|
value_type)
|
|
4837
|
+
callback_func.linkage = :internal
|
|
4829
4838
|
|
|
4830
4839
|
# Save current builder state
|
|
4831
4840
|
saved_block = @builder.insert_block
|
|
@@ -5221,6 +5230,7 @@ module Konpeito
|
|
|
5221
5230
|
# --- Shared lambda_true helper (created once per module) ---
|
|
5222
5231
|
@lambda_true_func ||= begin
|
|
5223
5232
|
f = @mod.functions.add("__konpeito_lambda_true", [value_type], value_type)
|
|
5233
|
+
f.linkage = :internal
|
|
5224
5234
|
bb = f.basic_blocks.append("entry")
|
|
5225
5235
|
@builder.position_at_end(bb)
|
|
5226
5236
|
@builder.ret(qtrue)
|
|
@@ -5231,6 +5241,7 @@ module Konpeito
|
|
|
5231
5241
|
arity_func_name = "__konpeito_proc_arity_#{param_count}"
|
|
5232
5242
|
arity_func = @mod.functions[arity_func_name] || begin
|
|
5233
5243
|
f = @mod.functions.add(arity_func_name, [value_type], value_type)
|
|
5244
|
+
f.linkage = :internal
|
|
5234
5245
|
bb = f.basic_blocks.append("entry")
|
|
5235
5246
|
@builder.position_at_end(bb)
|
|
5236
5247
|
arity_ruby = @builder.call(@rb_int2inum, LLVM::Int64.from_i(param_count), "arity_val")
|
|
@@ -5560,6 +5571,7 @@ module Konpeito
|
|
|
5560
5571
|
callback_func = @mod.functions.add(callback_name,
|
|
5561
5572
|
[LLVM::Pointer(LLVM::Int8)],
|
|
5562
5573
|
value_type)
|
|
5574
|
+
callback_func.linkage = :internal
|
|
5563
5575
|
|
|
5564
5576
|
# Save current builder state
|
|
5565
5577
|
saved_block = @builder.insert_block
|
|
@@ -5893,6 +5905,7 @@ module Konpeito
|
|
|
5893
5905
|
# VALUE callback(VALUE data) - data is pointer to captures array
|
|
5894
5906
|
callback_type = LLVM::Type.function([value_type], value_type)
|
|
5895
5907
|
callback_func = @mod.functions.add(callback_name, [value_type], value_type)
|
|
5908
|
+
callback_func.linkage = :internal
|
|
5896
5909
|
|
|
5897
5910
|
# Save current builder state
|
|
5898
5911
|
saved_block = @builder.insert_block
|
|
@@ -5988,6 +6001,7 @@ module Konpeito
|
|
|
5988
6001
|
|
|
5989
6002
|
# VALUE callback(VALUE mutex) - mutex is passed as data2
|
|
5990
6003
|
callback_func = @mod.functions.add(callback_name, [value_type], value_type)
|
|
6004
|
+
callback_func.linkage = :internal
|
|
5991
6005
|
|
|
5992
6006
|
# Save current builder state
|
|
5993
6007
|
saved_block = @builder.insert_block
|
|
@@ -8108,6 +8122,7 @@ module Konpeito
|
|
|
8108
8122
|
|
|
8109
8123
|
# VALUE func(VALUE data) — data (params[0]) = self or escape array
|
|
8110
8124
|
callback_func = @mod.functions.add(callback_name, [value_type], value_type)
|
|
8125
|
+
callback_func.linkage = :internal
|
|
8111
8126
|
|
|
8112
8127
|
# Save current builder state
|
|
8113
8128
|
saved_block = @builder.insert_block
|
|
@@ -8227,6 +8242,7 @@ module Konpeito
|
|
|
8227
8242
|
|
|
8228
8243
|
# VALUE func(VALUE data2, VALUE exception)
|
|
8229
8244
|
callback_func = @mod.functions.add(callback_name, [value_type, value_type], value_type)
|
|
8245
|
+
callback_func.linkage = :internal
|
|
8230
8246
|
|
|
8231
8247
|
# Save current builder state
|
|
8232
8248
|
saved_block = @builder.insert_block
|
|
@@ -8387,6 +8403,7 @@ module Konpeito
|
|
|
8387
8403
|
callback_name = "rescue_handler_gflag_#{counter}"
|
|
8388
8404
|
# VALUE func(VALUE data2, VALUE exception) — data2 (params[0]) = self or escape array
|
|
8389
8405
|
callback_func = @mod.functions.add(callback_name, [value_type, value_type], value_type)
|
|
8406
|
+
callback_func.linkage = :internal
|
|
8390
8407
|
|
|
8391
8408
|
saved_block = @builder.insert_block
|
|
8392
8409
|
saved_vars = @variables.dup
|
|
@@ -8524,6 +8541,7 @@ module Konpeito
|
|
|
8524
8541
|
def generate_rescue_handler_with_flag_callback(rescue_clauses, counter)
|
|
8525
8542
|
callback_name = "rescue_handler_flag_#{counter}"
|
|
8526
8543
|
callback_func = @mod.functions.add(callback_name, [value_type, value_type], value_type)
|
|
8544
|
+
callback_func.linkage = :internal
|
|
8527
8545
|
|
|
8528
8546
|
saved_block = @builder.insert_block
|
|
8529
8547
|
saved_vars = @variables.dup
|
|
@@ -9828,8 +9846,8 @@ module Konpeito
|
|
|
9828
9846
|
# Normalize negative indices
|
|
9829
9847
|
index_i64 = normalize_native_index(index_i64, inst.receiver)
|
|
9830
9848
|
|
|
9831
|
-
# GEP to get element pointer
|
|
9832
|
-
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")
|
|
9833
9851
|
|
|
9834
9852
|
# Load element
|
|
9835
9853
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
@@ -9859,8 +9877,9 @@ module Konpeito
|
|
|
9859
9877
|
target_type = element_type == :Int64 ? :i64 : :double
|
|
9860
9878
|
converted_value = convert_value(store_value, value_type, target_type)
|
|
9861
9879
|
|
|
9862
|
-
# GEP to get element pointer
|
|
9863
|
-
|
|
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")
|
|
9864
9883
|
|
|
9865
9884
|
# Store element
|
|
9866
9885
|
@builder.store(converted_value, elem_ptr)
|
|
@@ -9880,46 +9899,19 @@ module Konpeito
|
|
|
9880
9899
|
len_value = @variables["#{receiver_var}_len"]
|
|
9881
9900
|
return index_i64 unless len_value
|
|
9882
9901
|
|
|
9883
|
-
|
|
9902
|
+
# Use branchless select instead of conditional branches to avoid
|
|
9903
|
+
# creating new basic blocks that break surrounding PHI node predecessors.
|
|
9884
9904
|
is_negative = @builder.icmp(:slt, index_i64, LLVM::Int64.from_i(0))
|
|
9885
|
-
|
|
9886
|
-
neg_bb = func.basic_blocks.append("idx_neg")
|
|
9887
|
-
pos_bb = func.basic_blocks.append("idx_pos")
|
|
9888
|
-
merge_bb = func.basic_blocks.append("idx_merge")
|
|
9889
|
-
|
|
9890
|
-
@builder.cond(is_negative, neg_bb, pos_bb)
|
|
9891
|
-
|
|
9892
|
-
@builder.position_at_end(neg_bb)
|
|
9893
9905
|
normalized = @builder.add(len_value, index_i64, "neg_idx")
|
|
9894
|
-
@builder.
|
|
9895
|
-
|
|
9896
|
-
@builder.position_at_end(pos_bb)
|
|
9897
|
-
@builder.br(merge_bb)
|
|
9898
|
-
|
|
9899
|
-
@builder.position_at_end(merge_bb)
|
|
9900
|
-
@builder.phi(LLVM::Int64, { neg_bb => normalized, pos_bb => index_i64 })
|
|
9906
|
+
@builder.select(is_negative, normalized, index_i64, "norm_idx")
|
|
9901
9907
|
end
|
|
9902
9908
|
|
|
9903
9909
|
# Normalize negative index for StaticArray (compile-time known size)
|
|
9904
9910
|
def normalize_static_index(index_i64, size)
|
|
9905
|
-
|
|
9911
|
+
# Use branchless select to avoid creating new basic blocks
|
|
9906
9912
|
is_negative = @builder.icmp(:slt, index_i64, LLVM::Int64.from_i(0))
|
|
9907
|
-
|
|
9908
|
-
neg_bb = func.basic_blocks.append("sidx_neg")
|
|
9909
|
-
pos_bb = func.basic_blocks.append("sidx_pos")
|
|
9910
|
-
merge_bb = func.basic_blocks.append("sidx_merge")
|
|
9911
|
-
|
|
9912
|
-
@builder.cond(is_negative, neg_bb, pos_bb)
|
|
9913
|
-
|
|
9914
|
-
@builder.position_at_end(neg_bb)
|
|
9915
9913
|
normalized = @builder.add(LLVM::Int64.from_i(size), index_i64, "neg_sidx")
|
|
9916
|
-
@builder.
|
|
9917
|
-
|
|
9918
|
-
@builder.position_at_end(pos_bb)
|
|
9919
|
-
@builder.br(merge_bb)
|
|
9920
|
-
|
|
9921
|
-
@builder.position_at_end(merge_bb)
|
|
9922
|
-
@builder.phi(LLVM::Int64, { neg_bb => normalized, pos_bb => index_i64 })
|
|
9914
|
+
@builder.select(is_negative, normalized, index_i64, "norm_sidx")
|
|
9923
9915
|
end
|
|
9924
9916
|
|
|
9925
9917
|
# Generate NativeArray length access: arr.length
|
|
@@ -9977,7 +9969,7 @@ module Konpeito
|
|
|
9977
9969
|
@builder.position_at_end(loop_body)
|
|
9978
9970
|
|
|
9979
9971
|
# Get element via GEP + load (no rb_ary_entry!)
|
|
9980
|
-
elem_ptr = @builder.
|
|
9972
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
9981
9973
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
9982
9974
|
|
|
9983
9975
|
# Set up block parameter (unboxed!)
|
|
@@ -10027,7 +10019,7 @@ module Konpeito
|
|
|
10027
10019
|
convert_value(val, val_type, type_tag)
|
|
10028
10020
|
else
|
|
10029
10021
|
# Use first element as initial, start from index 1
|
|
10030
|
-
elem_ptr = @builder.
|
|
10022
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [LLVM::Int64.from_i(0)], "first_ptr")
|
|
10031
10023
|
@builder.load2(llvm_elem_type, elem_ptr, "first_elem")
|
|
10032
10024
|
end
|
|
10033
10025
|
|
|
@@ -10059,7 +10051,7 @@ module Konpeito
|
|
|
10059
10051
|
|
|
10060
10052
|
# Load accumulator and element (both unboxed)
|
|
10061
10053
|
acc_value = @builder.load2(llvm_elem_type, acc_alloca, "acc")
|
|
10062
|
-
elem_ptr = @builder.
|
|
10054
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
10063
10055
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
10064
10056
|
|
|
10065
10057
|
# Set up block parameters
|
|
@@ -10159,7 +10151,7 @@ module Konpeito
|
|
|
10159
10151
|
@builder.position_at_end(loop_body)
|
|
10160
10152
|
|
|
10161
10153
|
# Load element (unboxed)
|
|
10162
|
-
elem_ptr = @builder.
|
|
10154
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
10163
10155
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
10164
10156
|
|
|
10165
10157
|
# Set up block parameter
|
|
@@ -10242,7 +10234,7 @@ module Konpeito
|
|
|
10242
10234
|
@builder.position_at_end(loop_body)
|
|
10243
10235
|
|
|
10244
10236
|
# Load element (unboxed)
|
|
10245
|
-
elem_ptr = @builder.
|
|
10237
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
10246
10238
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
10247
10239
|
|
|
10248
10240
|
# Set up block parameter
|
|
@@ -10340,7 +10332,7 @@ module Konpeito
|
|
|
10340
10332
|
@builder.position_at_end(loop_body)
|
|
10341
10333
|
|
|
10342
10334
|
# Load element and store to alloca for later use
|
|
10343
|
-
elem_ptr = @builder.
|
|
10335
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
10344
10336
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
10345
10337
|
@builder.store(elem_value, elem_alloca)
|
|
10346
10338
|
|
|
@@ -10432,7 +10424,7 @@ module Konpeito
|
|
|
10432
10424
|
@builder.position_at_end(loop_body)
|
|
10433
10425
|
|
|
10434
10426
|
# Load element
|
|
10435
|
-
elem_ptr = @builder.
|
|
10427
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
10436
10428
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
10437
10429
|
|
|
10438
10430
|
# Set up block parameter
|
|
@@ -10540,7 +10532,7 @@ module Konpeito
|
|
|
10540
10532
|
|
|
10541
10533
|
# Load accumulator and element
|
|
10542
10534
|
acc_value = @builder.load2(llvm_elem_type, acc_alloca, "acc")
|
|
10543
|
-
elem_ptr = @builder.
|
|
10535
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
10544
10536
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
10545
10537
|
|
|
10546
10538
|
# Add (unboxed)
|
|
@@ -10601,7 +10593,7 @@ module Konpeito
|
|
|
10601
10593
|
# Non-empty: initialize with first element
|
|
10602
10594
|
@builder.position_at_end(non_empty_block)
|
|
10603
10595
|
result_alloca = @builder.alloca(llvm_elem_type, "na_minmax_result")
|
|
10604
|
-
first_ptr = @builder.
|
|
10596
|
+
first_ptr = @builder.gep2(llvm_elem_type, array_ptr, [LLVM::Int64.from_i(0)], "first_ptr")
|
|
10605
10597
|
first_elem = @builder.load2(llvm_elem_type, first_ptr, "first")
|
|
10606
10598
|
@builder.store(first_elem, result_alloca)
|
|
10607
10599
|
|
|
@@ -10621,7 +10613,7 @@ module Konpeito
|
|
|
10621
10613
|
|
|
10622
10614
|
# Load current result and element
|
|
10623
10615
|
current_result = @builder.load2(llvm_elem_type, result_alloca, "current")
|
|
10624
|
-
elem_ptr = @builder.
|
|
10616
|
+
elem_ptr = @builder.gep2(llvm_elem_type, array_ptr, [current_idx], "elem_ptr")
|
|
10625
10617
|
elem_value = @builder.load2(llvm_elem_type, elem_ptr, "elem")
|
|
10626
10618
|
|
|
10627
10619
|
# Compare and select
|
|
@@ -10691,11 +10683,16 @@ module Konpeito
|
|
|
10691
10683
|
# Get the array pointer from a receiver HIR value
|
|
10692
10684
|
def get_native_array_ptr(receiver)
|
|
10693
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")
|
|
10694
10691
|
when HIR::Instruction
|
|
10695
10692
|
if receiver.result_var
|
|
10696
10693
|
@variables[receiver.result_var]
|
|
10697
10694
|
else
|
|
10698
|
-
raise "NativeArray receiver has no result_var"
|
|
10695
|
+
raise "NativeArray receiver has no result_var: #{receiver.class}"
|
|
10699
10696
|
end
|
|
10700
10697
|
when String
|
|
10701
10698
|
@variables[receiver]
|
|
@@ -10707,6 +10704,8 @@ module Konpeito
|
|
|
10707
10704
|
# Get the variable name from a receiver for length lookup
|
|
10708
10705
|
def get_receiver_var_name(receiver)
|
|
10709
10706
|
case receiver
|
|
10707
|
+
when HIR::LoadLocal
|
|
10708
|
+
receiver.var.name
|
|
10710
10709
|
when HIR::Instruction
|
|
10711
10710
|
receiver.result_var
|
|
10712
10711
|
when String
|
|
@@ -10716,6 +10715,62 @@ module Konpeito
|
|
|
10716
10715
|
end
|
|
10717
10716
|
end
|
|
10718
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
|
+
|
|
10719
10774
|
# Allocate a NativeArray on the stack
|
|
10720
10775
|
# Returns a pointer to contiguous memory + stores length
|
|
10721
10776
|
def generate_native_array_alloc(inst)
|
|
@@ -10765,8 +10820,8 @@ module Konpeito
|
|
|
10765
10820
|
index_value, index_type = get_value_with_type(inst.index)
|
|
10766
10821
|
index_i64 = index_type == :i64 ? index_value : @builder.call(@rb_num2long, index_value)
|
|
10767
10822
|
|
|
10768
|
-
# GEP to get element pointer
|
|
10769
|
-
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")
|
|
10770
10825
|
|
|
10771
10826
|
if native_array_has_class_element?(element_type)
|
|
10772
10827
|
# For NativeClass elements, return pointer to struct (for field access)
|
|
@@ -10807,8 +10862,9 @@ module Konpeito
|
|
|
10807
10862
|
target_type = element_type == :Int64 ? :i64 : :double
|
|
10808
10863
|
converted_value = convert_value(store_value, value_type, target_type)
|
|
10809
10864
|
|
|
10810
|
-
# GEP to get element pointer
|
|
10811
|
-
|
|
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")
|
|
10812
10868
|
|
|
10813
10869
|
# Store element
|
|
10814
10870
|
@builder.store(converted_value, elem_ptr)
|
|
@@ -12906,8 +12962,8 @@ module Konpeito
|
|
|
12906
12962
|
# Get JSON element at index
|
|
12907
12963
|
elem_val = @builder.call(@yyjson_arr_get, root, idx, "json_elem")
|
|
12908
12964
|
|
|
12909
|
-
# Get pointer to NativeClass struct in the array
|
|
12910
|
-
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")
|
|
12911
12967
|
|
|
12912
12968
|
# Parse fields from JSON object into struct
|
|
12913
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")
|