konpeito 0.2.2 → 0.2.3

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/lib/konpeito/codegen/builtin_methods.rb +4 -2
  4. data/lib/konpeito/codegen/cruby_backend.rb +122 -10
  5. data/lib/konpeito/codegen/inliner.rb +9 -3
  6. data/lib/konpeito/codegen/jvm_generator.rb +3986 -919
  7. data/lib/konpeito/codegen/llvm_generator.rb +334 -45
  8. data/lib/konpeito/codegen/monomorphizer.rb +14 -2
  9. data/lib/konpeito/hir/builder.rb +150 -20
  10. data/lib/konpeito/hir/nodes.rb +16 -0
  11. data/lib/konpeito/type_checker/hm_inferrer.rb +100 -41
  12. data/lib/konpeito/type_checker/types.rb +6 -6
  13. data/lib/konpeito/type_checker/unification.rb +8 -1
  14. data/lib/konpeito/version.rb +1 -1
  15. data/tools/konpeito-asm/build.sh +1 -0
  16. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
  17. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
  18. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFiber.class +0 -0
  19. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
  20. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMatchData.class +0 -0
  21. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRubyException.class +0 -0
  22. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
  23. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
  24. data/tools/konpeito-asm/src/KonpeitoAssembler.java +17 -1
  25. data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +97 -4
  26. data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +20 -25
  27. data/tools/konpeito-asm/src/konpeito/runtime/KFiber.java +112 -0
  28. data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +67 -0
  29. data/tools/konpeito-asm/src/konpeito/runtime/KMatchData.java +55 -0
  30. data/tools/konpeito-asm/src/konpeito/runtime/KRubyException.java +79 -0
  31. data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +5 -0
  32. data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +285 -19
  33. metadata +7 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3daf2b84b1f0d775d0cc7ca3a3ed5937f82a44d44a068131b62a326049893f5d
4
- data.tar.gz: 0ebda1e25a79a24d8bbed4376d048945a595202060802315d69eebf23293be2c
3
+ metadata.gz: 968753a2cfabd2d2b237dd5125d6ae0818848a3f7243e7fdbb408a50dc802f48
4
+ data.tar.gz: 724dcd3309cb7e42fbd2cf7e3ffa0b6b1eac07840a8ba46affcad3c0bab6a21a
5
5
  SHA512:
6
- metadata.gz: 355daf3587c553762d23be9cf1620a08f87c13afc825114cb4b6413e64a33ca7173a5c569109c21745390732a603ade72d3cca8c67c5953b5418fbfe8b94fd18
7
- data.tar.gz: 50b4130ab80368df57e5466e4b0faadebdd40f385b018cdcc1a3f8b1da6557c0ae559b55b277b3f0619a47ff1edbf9a18b0ad153fc044a4707649f2c9c54f3f3
6
+ metadata.gz: e23cbc46176d84f51b1706e1125eae4f02a1ff31f6e9f64bc49856c79c8061a1aae6441cac1ee638c1a7eb8b0884cb235a40d11c509783e8e82b502d0b77e477
7
+ data.tar.gz: 3ead6d2c4b880aae4f687cfabf4f2c75c07f3b1939ed610a20ca0b1af301ff27153acfc25007720aaad772ae2581cb55c11558d7c6e9d9a16876012911081394
data/CHANGELOG.md CHANGED
@@ -5,6 +5,24 @@ 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
+ ## [0.2.3] - 2026-02-24
9
+
10
+ ### Added
11
+ - Conformance test suite expansion: 77 spec files with 1,095+ assertions
12
+ - JVM runtime: Fiber support, ConditionVariable deadlock fix, Math module (PI, E, sqrt)
13
+ - `&blk` block argument support at call sites (e.g., `arr.map(&blk)`)
14
+
15
+ ### Fixed
16
+ - Native backend: BUS error in `defined?(:method)` at top-level
17
+ - Native backend: inliner no longer inlines functions with `&blk` block params
18
+ - JVM backend: ConditionVariable deadlock — rewritten to release mutex during wait
19
+ - JVM backend: Math::PI, Math::E constants and Math.sqrt dispatch
20
+ - JVM backend: nested rescue, pattern matching, SizedQueue#empty?
21
+ - JVM backend: Integer()/Float() kernel methods, String#split limit, sleep, Symbol#frozen?
22
+ - JVM backend: hash iteration, KArray methods, splat args, user-defined ==, super(args)
23
+ - JVM backend: VerifyError for widened instance method parameters
24
+ - JVM backend: Fiber resume/yield, thread_mutex, runtime class identity
25
+
8
26
  ## [0.2.2] - 2026-02-21
9
27
 
10
28
  ### Added
@@ -69,8 +69,10 @@ module Konpeito
69
69
  :+ => { c_func: "rb_ary_plus", arity: 1, return_type: :Array, conv: :simple },
70
70
  concat: { c_func: "rb_ary_concat", arity: 1, return_type: :Array, conv: :simple },
71
71
 
72
- # Array transformation - exported
73
- reverse: { c_func: "rb_ary_reverse", arity: 0, return_type: :Array, conv: :simple },
72
+ # Array transformation
73
+ # Note: rb_ary_reverse is in-place mutation, so we use rb_funcallv fallback
74
+ # to get Ruby's Array#reverse behavior (returns new array without modifying original)
75
+ # reverse: removed from builtin_methods - falls back to rb_funcallv
74
76
  sort: { c_func: "rb_ary_sort", arity: 0, return_type: :Array, conv: :simple },
75
77
 
76
78
  # Array query with args - exported
@@ -152,6 +152,25 @@ module Konpeito
152
152
  lines << "extern VALUE #{mangled_name}(#{(['VALUE'] * (arity + 1)).join(', ')});"
153
153
  end
154
154
  end
155
+
156
+ # Declare extern for alias-renamed functions
157
+ (llvm_generator.alias_renamed_methods || {}).each do |key, renamed|
158
+ class_name, _method_name = key.split("#", 2)
159
+ next unless class_name == class_def.name.to_s
160
+
161
+ owner = class_name.gsub(/[^a-zA-Z0-9_]/, "_")
162
+ sanitized = renamed.gsub(/[^a-zA-Z0-9_]/, "_")
163
+ mangled = "rn_#{owner}_#{sanitized}"
164
+ func = llvm_generator.mod.functions[mangled]
165
+ next unless func
166
+
167
+ if llvm_generator.variadic_functions[mangled]
168
+ lines << "extern VALUE #{mangled}(int argc, VALUE *argv, VALUE self);"
169
+ else
170
+ arity = func.params.size - 1
171
+ lines << "extern VALUE #{mangled}(#{(['VALUE'] * (arity + 1)).join(', ')});"
172
+ end
173
+ end
155
174
  end
156
175
 
157
176
  # Declare external functions from LLVM module (modules)
@@ -264,12 +283,12 @@ module Konpeito
264
283
  lines << " rb_define_singleton_method(#{module_var}, \"#{method_name}\", #{mangled_name}, #{arity});"
265
284
  end
266
285
 
267
- # Register module constants
268
- module_def.constants.each do |const_name, _value|
269
- # Constants are typically registered at runtime via rb_const_set
270
- # For compile-time literals, we could use rb_define_const here
271
- # For now, mark them for runtime initialization
272
- lines << " /* Module constant #{module_def.name}::#{const_name} registered at runtime */"
286
+ # Register module constants (e.g., VERSION = "2.0")
287
+ module_def.constants.each do |const_name, value_node|
288
+ c_value = hir_literal_to_c_value(value_node)
289
+ next unless c_value
290
+
291
+ lines << " rb_const_set(#{module_var}, rb_intern(\"#{const_name}\"), #{c_value});"
273
292
  end
274
293
  end
275
294
 
@@ -297,17 +316,20 @@ module Konpeito
297
316
 
298
317
  # Prepend modules (must come before include to maintain proper method resolution order)
299
318
  class_def.prepended_modules.each do |module_name|
300
- lines << " rb_prepend_module(#{class_var}, m#{module_name});"
319
+ module_expr = resolve_module_c_expr(module_name)
320
+ lines << " rb_prepend_module(#{class_var}, #{module_expr});"
301
321
  end
302
322
 
303
323
  # Include modules
304
324
  class_def.included_modules.each do |module_name|
305
- lines << " rb_include_module(#{class_var}, m#{module_name});"
325
+ module_expr = resolve_module_c_expr(module_name)
326
+ lines << " rb_include_module(#{class_var}, #{module_expr});"
306
327
  end
307
328
 
308
329
  # Extend modules (adds module methods as singleton methods on the class)
309
330
  class_def.extended_modules.each do |module_name|
310
- lines << " rb_extend_object(#{class_var}, m#{module_name});"
331
+ module_expr = resolve_module_c_expr(module_name)
332
+ lines << " rb_extend_object(#{class_var}, #{module_expr});"
311
333
  end
312
334
 
313
335
  class_def.method_names.each do |method_name|
@@ -343,7 +365,41 @@ module Konpeito
343
365
 
344
366
  # Register aliases
345
367
  class_def.aliases.each do |new_name, old_name|
346
- lines << " rb_define_alias(#{class_var}, \"#{new_name}\", \"#{old_name}\");"
368
+ renamed_key = "#{class_def.name}##{old_name}"
369
+ if llvm_generator.alias_renamed_methods&.key?(renamed_key)
370
+ # Method was redefined after alias — point alias to renamed original
371
+ renamed = llvm_generator.alias_renamed_methods[renamed_key]
372
+ owner = class_def.name.to_s.gsub(/[^a-zA-Z0-9_]/, "_")
373
+ sanitized = renamed.gsub(/[^a-zA-Z0-9_]/, "_")
374
+ mangled = "rn_#{owner}_#{sanitized}"
375
+ llvm_func = llvm_generator.mod.functions[mangled]
376
+ if llvm_generator.variadic_functions[mangled]
377
+ arity = -1
378
+ elsif llvm_func
379
+ arity = llvm_func.params.size - 1
380
+ else
381
+ arity = 0
382
+ end
383
+ lines << " rb_define_method(#{class_var}, \"#{new_name}\", #{mangled}, #{arity});"
384
+ else
385
+ lines << " rb_define_alias(#{class_var}, \"#{new_name}\", \"#{old_name}\");"
386
+ end
387
+ end
388
+
389
+ # Register class body constants (e.g., PI = 3, VERSION = "1.0")
390
+ class_def.body_constants.each do |const_name, value_node|
391
+ c_value = hir_literal_to_c_value(value_node)
392
+ next unless c_value
393
+
394
+ lines << " rb_const_set(#{class_var}, rb_intern(\"#{const_name}\"), #{c_value});"
395
+ end
396
+
397
+ # Register class body class variables (e.g., @@count = 0)
398
+ class_def.body_class_vars.each do |cvar_name, value_node|
399
+ c_value = hir_literal_to_c_value(value_node)
400
+ next unless c_value
401
+
402
+ lines << " rb_cvar_set(#{class_var}, rb_intern(\"#{cvar_name}\"), #{c_value});"
347
403
  end
348
404
  end
349
405
 
@@ -1019,6 +1075,25 @@ module Konpeito
1019
1075
  "Comparable" => "rb_mComparable",
1020
1076
  }.freeze
1021
1077
 
1078
+ # Known Ruby stdlib modules that can be included/extended/prepended
1079
+ KNOWN_MODULE_MAP = {
1080
+ "Comparable" => "rb_mComparable",
1081
+ "Enumerable" => "rb_mEnumerable",
1082
+ "Kernel" => "rb_mKernel",
1083
+ "Math" => "rb_mMath",
1084
+ }.freeze
1085
+
1086
+ # Resolve a module name to its C expression for include/extend/prepend
1087
+ def resolve_module_c_expr(module_name)
1088
+ # Check known stdlib modules first
1089
+ if KNOWN_MODULE_MAP[module_name]
1090
+ return KNOWN_MODULE_MAP[module_name]
1091
+ end
1092
+
1093
+ # User-defined module (defined earlier in Init function as mModuleName)
1094
+ "m#{module_name}"
1095
+ end
1096
+
1022
1097
  def resolve_superclass_c_expr(superclass_name, non_native_classes)
1023
1098
  return "rb_cObject" unless superclass_name
1024
1099
 
@@ -1041,6 +1116,43 @@ module Konpeito
1041
1116
  "rb_const_get(rb_cObject, rb_intern(\"#{superclass_name}\"))"
1042
1117
  end
1043
1118
 
1119
+ # Convert an HIR literal node to a C VALUE expression for use in Init function.
1120
+ # Returns nil for non-literal nodes that cannot be statically initialized.
1121
+ def hir_literal_to_c_value(node)
1122
+ case node
1123
+ when HIR::IntegerLit
1124
+ "INT2FIX(#{node.value})"
1125
+ when HIR::FloatLit
1126
+ "DBL2NUM(#{node.value})"
1127
+ when HIR::StringLit
1128
+ escaped = node.value.to_s.gsub("\\", "\\\\\\\\").gsub('"', '\\"').gsub("\n", "\\n").gsub("\t", "\\t")
1129
+ "rb_str_new_cstr(\"#{escaped}\")"
1130
+ when HIR::SymbolLit
1131
+ "ID2SYM(rb_intern(\"#{node.value}\"))"
1132
+ when HIR::BoolLit
1133
+ node.value ? "Qtrue" : "Qfalse"
1134
+ when HIR::NilLit
1135
+ "Qnil"
1136
+ when HIR::Literal
1137
+ # Generic literal fallback
1138
+ case node.type
1139
+ when TypeChecker::Types::INTEGER
1140
+ "INT2FIX(#{node.value})"
1141
+ when TypeChecker::Types::FLOAT
1142
+ "DBL2NUM(#{node.value})"
1143
+ when TypeChecker::Types::STRING
1144
+ escaped = node.value.to_s.gsub("\\", "\\\\\\\\").gsub('"', '\\"').gsub("\n", "\\n").gsub("\t", "\\t")
1145
+ "rb_str_new_cstr(\"#{escaped}\")"
1146
+ when TypeChecker::Types::BOOL
1147
+ node.value ? "Qtrue" : "Qfalse"
1148
+ else
1149
+ nil
1150
+ end
1151
+ else
1152
+ nil
1153
+ end
1154
+ end
1155
+
1044
1156
  # Generate extern declaration for a @cfunc C function
1045
1157
  def generate_cfunc_extern_declaration(cfunc_type)
1046
1158
  lines = []
@@ -74,9 +74,9 @@ module Konpeito
74
74
  # Skip class methods for now
75
75
  return false if func.owner_class
76
76
 
77
- # Skip functions with rest params (*args) or keyword_rest (**kwargs)
78
- # The inliner doesn't support rest param array aggregation
79
- return false if func.params.any? { |p| p.rest || p.keyword_rest }
77
+ # Skip functions with rest params (*args), keyword_rest (**kwargs), or block params (&blk)
78
+ # The inliner doesn't support rest param array aggregation or block capture context
79
+ return false if func.params.any? { |p| p.rest || p.keyword_rest || p.block }
80
80
 
81
81
  # Skip functions with multiple blocks (contains if/while/etc.)
82
82
  # These have complex control flow that can't be simply inlined
@@ -91,6 +91,7 @@ module Konpeito
91
91
  # Skip functions containing block calls (captures need complex transformation)
92
92
  # Skip functions containing yield (need KBlock parameter handling)
93
93
  # Skip functions containing BeginRescue (exception handling has complex sub-block structure)
94
+ # Skip functions containing ThreadNew/FiberNew (callbacks reference specific allocas)
94
95
  func.body.each do |block|
95
96
  block.instructions.each do |inst|
96
97
  return false if inst.is_a?(HIR::Call) && inst.block
@@ -98,6 +99,8 @@ module Konpeito
98
99
  return false if inst.is_a?(HIR::BeginRescue)
99
100
  return false if inst.is_a?(HIR::CaseStatement)
100
101
  return false if inst.is_a?(HIR::CaseMatchStatement)
102
+ return false if inst.is_a?(HIR::ThreadNew)
103
+ return false if inst.is_a?(HIR::FiberNew)
101
104
  end
102
105
  end
103
106
 
@@ -158,6 +161,9 @@ module Konpeito
158
161
  def can_inline_call?(inst)
159
162
  return false unless self_call?(inst)
160
163
 
164
+ # Don't inline calls with splat arguments - they need rb_apply
165
+ return false if inst.args.any? { |a| a.is_a?(HIR::SplatArg) }
166
+
161
167
  callee_name = inst.method_name.to_s
162
168
  @inline_candidates[callee_name] == true
163
169
  end