konpeito 0.1.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 +7 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +75 -0
- data/CONTRIBUTING.md +123 -0
- data/LICENSE +21 -0
- data/README.md +257 -0
- data/Rakefile +11 -0
- data/bin/konpeito +6 -0
- data/konpeito.gemspec +43 -0
- data/lib/konpeito/ast/typed_ast.rb +620 -0
- data/lib/konpeito/ast/visitor.rb +78 -0
- data/lib/konpeito/cache/cache_manager.rb +230 -0
- data/lib/konpeito/cache/dependency_graph.rb +192 -0
- data/lib/konpeito/cache.rb +8 -0
- data/lib/konpeito/cli/base_command.rb +187 -0
- data/lib/konpeito/cli/build_command.rb +220 -0
- data/lib/konpeito/cli/check_command.rb +104 -0
- data/lib/konpeito/cli/config.rb +231 -0
- data/lib/konpeito/cli/deps_command.rb +128 -0
- data/lib/konpeito/cli/doctor_command.rb +340 -0
- data/lib/konpeito/cli/fmt_command.rb +199 -0
- data/lib/konpeito/cli/init_command.rb +312 -0
- data/lib/konpeito/cli/lsp_command.rb +40 -0
- data/lib/konpeito/cli/run_command.rb +150 -0
- data/lib/konpeito/cli/test_command.rb +248 -0
- data/lib/konpeito/cli/watch_command.rb +212 -0
- data/lib/konpeito/cli.rb +301 -0
- data/lib/konpeito/codegen/builtin_methods.rb +229 -0
- data/lib/konpeito/codegen/cruby_backend.rb +1090 -0
- data/lib/konpeito/codegen/debug_info.rb +352 -0
- data/lib/konpeito/codegen/inliner.rb +486 -0
- data/lib/konpeito/codegen/jvm_backend.rb +197 -0
- data/lib/konpeito/codegen/jvm_generator.rb +13412 -0
- data/lib/konpeito/codegen/llvm_generator.rb +13191 -0
- data/lib/konpeito/codegen/loop_optimizer.rb +363 -0
- data/lib/konpeito/codegen/monomorphizer.rb +359 -0
- data/lib/konpeito/codegen/profile_runtime.c +341 -0
- data/lib/konpeito/codegen/profiler.rb +99 -0
- data/lib/konpeito/compiler.rb +592 -0
- data/lib/konpeito/dependency_resolver.rb +296 -0
- data/lib/konpeito/diagnostics/collector.rb +127 -0
- data/lib/konpeito/diagnostics/diagnostic.rb +237 -0
- data/lib/konpeito/diagnostics/renderer.rb +144 -0
- data/lib/konpeito/formatter/formatter.rb +1214 -0
- data/lib/konpeito/hir/builder.rb +7167 -0
- data/lib/konpeito/hir/nodes.rb +2465 -0
- data/lib/konpeito/lsp/document_manager.rb +820 -0
- data/lib/konpeito/lsp/server.rb +183 -0
- data/lib/konpeito/lsp/transport.rb +38 -0
- data/lib/konpeito/parser/prism_adapter.rb +65 -0
- data/lib/konpeito/platform.rb +103 -0
- data/lib/konpeito/profile/report.rb +136 -0
- data/lib/konpeito/rbs_inline/preprocessor.rb +199 -0
- data/lib/konpeito/stdlib/compression/compression.rb +72 -0
- data/lib/konpeito/stdlib/compression/compression.rbs +60 -0
- data/lib/konpeito/stdlib/compression/compression_native.c +415 -0
- data/lib/konpeito/stdlib/compression/extconf.rb +19 -0
- data/lib/konpeito/stdlib/crypto/crypto.rb +85 -0
- data/lib/konpeito/stdlib/crypto/crypto.rbs +74 -0
- data/lib/konpeito/stdlib/crypto/crypto_native.c +312 -0
- data/lib/konpeito/stdlib/crypto/extconf.rb +40 -0
- data/lib/konpeito/stdlib/http/extconf.rb +19 -0
- data/lib/konpeito/stdlib/http/http.rb +125 -0
- data/lib/konpeito/stdlib/http/http.rbs +57 -0
- data/lib/konpeito/stdlib/http/http_native.c +440 -0
- data/lib/konpeito/stdlib/json/extconf.rb +17 -0
- data/lib/konpeito/stdlib/json/json.rb +44 -0
- data/lib/konpeito/stdlib/json/json.rbs +33 -0
- data/lib/konpeito/stdlib/json/json_native.c +286 -0
- data/lib/konpeito/stdlib/ui/extconf.rb +216 -0
- data/lib/konpeito/stdlib/ui/konpeito_ui_native.cpp +1625 -0
- data/lib/konpeito/stdlib/ui/konpeito_ui_native.h +162 -0
- data/lib/konpeito/stdlib/ui/ui.rb +318 -0
- data/lib/konpeito/stdlib/ui/ui.rbs +247 -0
- data/lib/konpeito/type_checker/annotation_parser.rb +67 -0
- data/lib/konpeito/type_checker/hm_inferrer.rb +2565 -0
- data/lib/konpeito/type_checker/inferrer.rb +565 -0
- data/lib/konpeito/type_checker/rbs_loader.rb +1621 -0
- data/lib/konpeito/type_checker/type_resolver.rb +276 -0
- data/lib/konpeito/type_checker/types.rb +1434 -0
- data/lib/konpeito/type_checker/unification.rb +323 -0
- data/lib/konpeito/ui/animation/animated_state.rb +80 -0
- data/lib/konpeito/ui/animation/easing.rb +59 -0
- data/lib/konpeito/ui/animation/value_tween.rb +66 -0
- data/lib/konpeito/ui/app.rb +379 -0
- data/lib/konpeito/ui/box.rb +38 -0
- data/lib/konpeito/ui/castella.rb +70 -0
- data/lib/konpeito/ui/castella_native.rb +76 -0
- data/lib/konpeito/ui/chart/area_chart.rb +305 -0
- data/lib/konpeito/ui/chart/bar_chart.rb +288 -0
- data/lib/konpeito/ui/chart/base_chart.rb +210 -0
- data/lib/konpeito/ui/chart/chart_helpers.rb +79 -0
- data/lib/konpeito/ui/chart/gauge_chart.rb +171 -0
- data/lib/konpeito/ui/chart/heatmap_chart.rb +222 -0
- data/lib/konpeito/ui/chart/line_chart.rb +289 -0
- data/lib/konpeito/ui/chart/pie_chart.rb +219 -0
- data/lib/konpeito/ui/chart/scales.rb +77 -0
- data/lib/konpeito/ui/chart/scatter_chart.rb +303 -0
- data/lib/konpeito/ui/chart/stacked_bar_chart.rb +276 -0
- data/lib/konpeito/ui/column.rb +271 -0
- data/lib/konpeito/ui/core.rb +2199 -0
- data/lib/konpeito/ui/dsl.rb +443 -0
- data/lib/konpeito/ui/frame.rb +171 -0
- data/lib/konpeito/ui/frame_native.rb +494 -0
- data/lib/konpeito/ui/markdown/ast.rb +124 -0
- data/lib/konpeito/ui/markdown/mermaid/layout.rb +387 -0
- data/lib/konpeito/ui/markdown/mermaid/models.rb +232 -0
- data/lib/konpeito/ui/markdown/mermaid/parser.rb +519 -0
- data/lib/konpeito/ui/markdown/mermaid/renderer.rb +336 -0
- data/lib/konpeito/ui/markdown/parser.rb +805 -0
- data/lib/konpeito/ui/markdown/renderer.rb +639 -0
- data/lib/konpeito/ui/markdown/theme.rb +165 -0
- data/lib/konpeito/ui/render_node.rb +260 -0
- data/lib/konpeito/ui/row.rb +207 -0
- data/lib/konpeito/ui/spacer.rb +18 -0
- data/lib/konpeito/ui/style.rb +799 -0
- data/lib/konpeito/ui/theme.rb +563 -0
- data/lib/konpeito/ui/themes/material.rb +35 -0
- data/lib/konpeito/ui/themes/tokyo_night.rb +6 -0
- data/lib/konpeito/ui/widgets/button.rb +103 -0
- data/lib/konpeito/ui/widgets/calendar.rb +1034 -0
- data/lib/konpeito/ui/widgets/checkbox.rb +119 -0
- data/lib/konpeito/ui/widgets/container.rb +91 -0
- data/lib/konpeito/ui/widgets/data_table.rb +667 -0
- data/lib/konpeito/ui/widgets/divider.rb +29 -0
- data/lib/konpeito/ui/widgets/image.rb +105 -0
- data/lib/konpeito/ui/widgets/input.rb +485 -0
- data/lib/konpeito/ui/widgets/markdown.rb +57 -0
- data/lib/konpeito/ui/widgets/modal.rb +163 -0
- data/lib/konpeito/ui/widgets/multiline_input.rb +968 -0
- data/lib/konpeito/ui/widgets/multiline_text.rb +180 -0
- data/lib/konpeito/ui/widgets/net_image.rb +100 -0
- data/lib/konpeito/ui/widgets/progress_bar.rb +70 -0
- data/lib/konpeito/ui/widgets/radio_buttons.rb +93 -0
- data/lib/konpeito/ui/widgets/slider.rb +133 -0
- data/lib/konpeito/ui/widgets/switch.rb +84 -0
- data/lib/konpeito/ui/widgets/tabs.rb +157 -0
- data/lib/konpeito/ui/widgets/text.rb +110 -0
- data/lib/konpeito/ui/widgets/tree.rb +426 -0
- data/lib/konpeito/version.rb +5 -0
- data/lib/konpeito.rb +109 -0
- data/test_native_array.rb +172 -0
- data/test_native_array_class.rb +197 -0
- data/test_native_class.rb +151 -0
- data/tools/konpeito-asm/build.sh +65 -0
- data/tools/konpeito-asm/lib/asm-9.7.1.jar +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
- data/tools/konpeito-asm/src/ClassIntrospector.java +312 -0
- data/tools/konpeito-asm/src/KonpeitoAssembler.java +659 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +390 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KCompression.java +168 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +48 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KCrypto.java +151 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KFile.java +100 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHTTP.java +113 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +228 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KJSON.java +405 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KMath.java +54 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRactor.java +244 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java +53 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +49 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KThread.java +49 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KTime.java +53 -0
- data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +416 -0
- metadata +267 -0
|
@@ -0,0 +1,1090 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "rbconfig"
|
|
5
|
+
require "set"
|
|
6
|
+
|
|
7
|
+
module Konpeito
|
|
8
|
+
module Codegen
|
|
9
|
+
# Generates CRuby extension (.so/.bundle) from LLVM module
|
|
10
|
+
class CRubyBackend
|
|
11
|
+
attr_reader :llvm_generator, :output_file, :module_name, :rbs_loader, :stdlib_requires, :debug, :profile
|
|
12
|
+
|
|
13
|
+
def initialize(llvm_generator, output_file:, module_name: nil, rbs_loader: nil, stdlib_requires: [], runtime_native_extensions: [], debug: false, profile: false, uses_json_parse_as: false)
|
|
14
|
+
@llvm_generator = llvm_generator
|
|
15
|
+
@output_file = output_file
|
|
16
|
+
@module_name = module_name || derive_module_name(output_file)
|
|
17
|
+
@rbs_loader = rbs_loader
|
|
18
|
+
@stdlib_requires = stdlib_requires
|
|
19
|
+
@runtime_native_extensions = runtime_native_extensions
|
|
20
|
+
@debug = debug
|
|
21
|
+
@profile = profile
|
|
22
|
+
@uses_json_parse_as = uses_json_parse_as
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def generate
|
|
26
|
+
ir_file = "#{output_base}.ll"
|
|
27
|
+
obj_file = "#{output_base}.o"
|
|
28
|
+
init_c_file = "#{output_base}_init.c"
|
|
29
|
+
init_obj_file = "#{output_base}_init.o"
|
|
30
|
+
profile_c_file = nil
|
|
31
|
+
profile_obj_file = nil
|
|
32
|
+
|
|
33
|
+
begin
|
|
34
|
+
# Write LLVM IR to file
|
|
35
|
+
File.write(ir_file, llvm_generator.to_ir)
|
|
36
|
+
|
|
37
|
+
# Generate C wrapper for Init function
|
|
38
|
+
File.write(init_c_file, generate_init_c_code)
|
|
39
|
+
|
|
40
|
+
# Compile IR to object file
|
|
41
|
+
compile_ir_to_object(ir_file, obj_file)
|
|
42
|
+
|
|
43
|
+
# Compile C init wrapper
|
|
44
|
+
compile_c_to_object(init_c_file, init_obj_file)
|
|
45
|
+
|
|
46
|
+
obj_files = [obj_file, init_obj_file]
|
|
47
|
+
|
|
48
|
+
# Compile profile runtime if profiling enabled
|
|
49
|
+
if @profile
|
|
50
|
+
profile_c_file = "#{output_base}_profile_runtime.c"
|
|
51
|
+
profile_obj_file = "#{output_base}_profile_runtime.o"
|
|
52
|
+
|
|
53
|
+
# Write embedded C runtime
|
|
54
|
+
File.write(profile_c_file, profile_runtime_c_code)
|
|
55
|
+
compile_c_to_object(profile_c_file, profile_obj_file)
|
|
56
|
+
|
|
57
|
+
obj_files << profile_obj_file
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Link all object files to shared library
|
|
61
|
+
link_to_shared_library(obj_files, output_file)
|
|
62
|
+
ensure
|
|
63
|
+
# Cleanup temporary files (keep .ll for debugging)
|
|
64
|
+
# FileUtils.rm_f(ir_file)
|
|
65
|
+
unless @debug
|
|
66
|
+
FileUtils.rm_f(obj_file)
|
|
67
|
+
end
|
|
68
|
+
FileUtils.rm_f(init_c_file)
|
|
69
|
+
FileUtils.rm_f(init_obj_file)
|
|
70
|
+
FileUtils.rm_f(profile_c_file) if profile_c_file
|
|
71
|
+
FileUtils.rm_f(profile_obj_file) if profile_obj_file
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
output_file
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def output_base
|
|
80
|
+
output_file.sub(/\.(so|bundle|dll)$/, "")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def derive_module_name(path)
|
|
84
|
+
File.basename(path).sub(/\.(so|bundle|dll)$/, "").gsub(/[^a-zA-Z0-9_]/, "_")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def generate_init_c_code
|
|
88
|
+
hir = llvm_generator.hir_program
|
|
89
|
+
lines = []
|
|
90
|
+
|
|
91
|
+
lines << "#include <ruby.h>"
|
|
92
|
+
lines << "#include <stddef.h>"
|
|
93
|
+
lines << "#include <string.h>"
|
|
94
|
+
lines << ""
|
|
95
|
+
|
|
96
|
+
# Collect NativeClasses from RBS loader
|
|
97
|
+
native_classes = @rbs_loader&.native_classes || {}
|
|
98
|
+
|
|
99
|
+
# Sort native classes by dependency order (embedded types first)
|
|
100
|
+
sorted_classes = topological_sort_native_classes(native_classes)
|
|
101
|
+
|
|
102
|
+
# Forward declare structs for vtable function signatures
|
|
103
|
+
sorted_classes.each do |class_name|
|
|
104
|
+
lines << "typedef struct Native_#{class_name}_s Native_#{class_name};"
|
|
105
|
+
end
|
|
106
|
+
lines << ""
|
|
107
|
+
|
|
108
|
+
# Generate struct definitions and TypedData for NativeClasses
|
|
109
|
+
sorted_classes.each do |class_name|
|
|
110
|
+
class_type = native_classes[class_name]
|
|
111
|
+
lines.concat(generate_native_class_struct_body(class_name, class_type))
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Declare external native functions from LLVM module (for NativeClass methods)
|
|
115
|
+
native_classes.each do |class_name, class_type|
|
|
116
|
+
class_type.methods.each do |method_name, method_sig|
|
|
117
|
+
lines.concat(generate_native_func_declaration(class_name, class_type, method_name, method_sig))
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Generate vtables for vtable classes (after function declarations)
|
|
122
|
+
sorted_classes.each do |class_name|
|
|
123
|
+
class_type = native_classes[class_name]
|
|
124
|
+
if class_type.uses_vtable?(native_classes)
|
|
125
|
+
lines.concat(generate_vtable(class_name, class_type, native_classes))
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
lines << ""
|
|
130
|
+
|
|
131
|
+
# Generate wrapper functions for NativeClass methods
|
|
132
|
+
native_classes.each do |class_name, class_type|
|
|
133
|
+
class_type.methods.each do |method_name, method_sig|
|
|
134
|
+
lines.concat(generate_native_method_wrapper(class_name, class_type, method_name, method_sig))
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Declare external functions from LLVM module (non-native classes)
|
|
139
|
+
hir.classes.each do |class_def|
|
|
140
|
+
next if native_classes.key?(class_def.name.to_sym)
|
|
141
|
+
|
|
142
|
+
(class_def.method_names + class_def.singleton_methods).each do |method_name|
|
|
143
|
+
mangled_name = mangle_method_name(class_def.name, method_name)
|
|
144
|
+
func = llvm_generator.mod.functions[mangled_name]
|
|
145
|
+
next unless func
|
|
146
|
+
|
|
147
|
+
# Use variadic signature for functions with **kwargs or *args
|
|
148
|
+
if llvm_generator.variadic_functions[mangled_name]
|
|
149
|
+
lines << "extern VALUE #{mangled_name}(int argc, VALUE *argv, VALUE self);"
|
|
150
|
+
else
|
|
151
|
+
arity = func.params.size - 1
|
|
152
|
+
lines << "extern VALUE #{mangled_name}(#{(['VALUE'] * (arity + 1)).join(', ')});"
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Declare external functions from LLVM module (modules)
|
|
158
|
+
hir.modules.each do |module_def|
|
|
159
|
+
(module_def.methods + module_def.singleton_methods).each do |method_name|
|
|
160
|
+
mangled_name = mangle_method_name(module_def.name, method_name)
|
|
161
|
+
func = llvm_generator.mod.functions[mangled_name]
|
|
162
|
+
next unless func
|
|
163
|
+
|
|
164
|
+
# Use variadic signature for functions with **kwargs or *args
|
|
165
|
+
if llvm_generator.variadic_functions[mangled_name]
|
|
166
|
+
lines << "extern VALUE #{mangled_name}(int argc, VALUE *argv, VALUE self);"
|
|
167
|
+
else
|
|
168
|
+
arity = func.params.size - 1
|
|
169
|
+
lines << "extern VALUE #{mangled_name}(#{(['VALUE'] * (arity + 1)).join(', ')});"
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Declare top-level functions
|
|
175
|
+
hir.functions.each do |func_def|
|
|
176
|
+
next if func_def.owner_class
|
|
177
|
+
|
|
178
|
+
mangled_name = "rn_#{func_def.name}".gsub(/[^a-zA-Z0-9_]/, "_")
|
|
179
|
+
func = llvm_generator.mod.functions[mangled_name]
|
|
180
|
+
next unless func
|
|
181
|
+
|
|
182
|
+
# Use variadic signature for functions with **kwargs or *args
|
|
183
|
+
if llvm_generator.variadic_functions[mangled_name]
|
|
184
|
+
lines << "extern VALUE #{mangled_name}(int argc, VALUE *argv, VALUE self);"
|
|
185
|
+
else
|
|
186
|
+
arity = func.params.size - 1
|
|
187
|
+
lines << "extern VALUE #{mangled_name}(#{(['VALUE'] * (arity + 1)).join(', ')});"
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Declare external C functions used by @cfunc annotations
|
|
192
|
+
cfunc_methods = @rbs_loader&.cfunc_methods || {}
|
|
193
|
+
unless cfunc_methods.empty?
|
|
194
|
+
lines << ""
|
|
195
|
+
lines << "/* External C functions (@cfunc) */"
|
|
196
|
+
declared_cfuncs = Set.new
|
|
197
|
+
cfunc_methods.each_value do |cfunc_type|
|
|
198
|
+
next if declared_cfuncs.include?(cfunc_type.c_func_name)
|
|
199
|
+
|
|
200
|
+
declared_cfuncs << cfunc_type.c_func_name
|
|
201
|
+
lines.concat(generate_cfunc_extern_declaration(cfunc_type))
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Profile runtime function declarations
|
|
206
|
+
if @profile
|
|
207
|
+
lines << "/* Profiling runtime functions */"
|
|
208
|
+
lines << "extern void konpeito_profile_init(int num_functions, const char* output_path);"
|
|
209
|
+
lines << "extern void konpeito_profile_finalize(void);"
|
|
210
|
+
lines << ""
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
lines << ""
|
|
214
|
+
lines << "void Init_#{module_name}(void) {"
|
|
215
|
+
|
|
216
|
+
# Initialize profiling if enabled
|
|
217
|
+
if @profile
|
|
218
|
+
num_funcs = llvm_generator.profiler&.num_functions || 0
|
|
219
|
+
profile_output = "#{module_name}_profile.json"
|
|
220
|
+
lines << " /* Initialize profiling */"
|
|
221
|
+
lines << " konpeito_profile_init(#{num_funcs}, \"#{profile_output}\");"
|
|
222
|
+
lines << ""
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Load stdlib dependencies first
|
|
226
|
+
unless @stdlib_requires.empty?
|
|
227
|
+
lines << " /* Load stdlib dependencies */"
|
|
228
|
+
@stdlib_requires.each do |lib_name|
|
|
229
|
+
lines << " rb_require(\"#{lib_name}\");"
|
|
230
|
+
end
|
|
231
|
+
lines << ""
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Define modules first (before classes that may include them)
|
|
235
|
+
hir.modules.each do |module_def|
|
|
236
|
+
module_var = "m#{module_def.name}"
|
|
237
|
+
lines << " VALUE #{module_var} = rb_define_module(\"#{module_def.name}\");"
|
|
238
|
+
|
|
239
|
+
# Register instance methods (for include/extend)
|
|
240
|
+
module_def.methods.each do |method_name|
|
|
241
|
+
# Skip @cfunc methods - they are direct C calls, not Ruby methods
|
|
242
|
+
next if @rbs_loader&.cfunc_method?(module_def.name, method_name, singleton: false)
|
|
243
|
+
|
|
244
|
+
mangled_name = mangle_method_name(module_def.name, method_name)
|
|
245
|
+
func = llvm_generator.mod.functions[mangled_name]
|
|
246
|
+
next unless func
|
|
247
|
+
|
|
248
|
+
# Use -1 arity for variadic functions
|
|
249
|
+
arity = llvm_generator.variadic_functions[mangled_name] ? -1 : func.params.size - 1
|
|
250
|
+
lines << " rb_define_method(#{module_var}, \"#{method_name}\", #{mangled_name}, #{arity});"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Register singleton methods (def self.method)
|
|
254
|
+
module_def.singleton_methods.each do |method_name|
|
|
255
|
+
# Skip @cfunc methods - they are direct C calls, not Ruby methods
|
|
256
|
+
next if @rbs_loader&.cfunc_method?(module_def.name, method_name, singleton: true)
|
|
257
|
+
|
|
258
|
+
mangled_name = mangle_method_name(module_def.name, method_name)
|
|
259
|
+
func = llvm_generator.mod.functions[mangled_name]
|
|
260
|
+
next unless func
|
|
261
|
+
|
|
262
|
+
# Use -1 arity for variadic functions
|
|
263
|
+
arity = llvm_generator.variadic_functions[mangled_name] ? -1 : func.params.size - 1
|
|
264
|
+
lines << " rb_define_singleton_method(#{module_var}, \"#{method_name}\", #{mangled_name}, #{arity});"
|
|
265
|
+
end
|
|
266
|
+
|
|
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 */"
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Define NativeClasses with TypedData allocator
|
|
277
|
+
native_classes.each do |class_name, class_type|
|
|
278
|
+
lines.concat(generate_native_class_init(class_name, class_type))
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Define non-native classes (including @boxed classes)
|
|
282
|
+
# Native-first: Classes without field definitions or marked @boxed
|
|
283
|
+
# use standard VALUE-based class definition
|
|
284
|
+
# Sort classes topologically so superclasses are defined first
|
|
285
|
+
non_native_classes = hir.classes.reject { |cd| native_classes.key?(cd.name.to_sym) }
|
|
286
|
+
sorted_non_native = topological_sort_non_native_classes(non_native_classes)
|
|
287
|
+
|
|
288
|
+
sorted_non_native.each do |class_def|
|
|
289
|
+
class_var = "c#{class_def.name}"
|
|
290
|
+
if class_def.reopened
|
|
291
|
+
# Reopened class - get existing class instead of defining new one
|
|
292
|
+
lines << " VALUE #{class_var} = rb_const_get(rb_cObject, rb_intern(\"#{class_def.name}\"));"
|
|
293
|
+
else
|
|
294
|
+
superclass_expr = resolve_superclass_c_expr(class_def.superclass, non_native_classes)
|
|
295
|
+
lines << " VALUE #{class_var} = rb_define_class(\"#{class_def.name}\", #{superclass_expr});"
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Prepend modules (must come before include to maintain proper method resolution order)
|
|
299
|
+
class_def.prepended_modules.each do |module_name|
|
|
300
|
+
lines << " rb_prepend_module(#{class_var}, m#{module_name});"
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Include modules
|
|
304
|
+
class_def.included_modules.each do |module_name|
|
|
305
|
+
lines << " rb_include_module(#{class_var}, m#{module_name});"
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Extend modules (adds module methods as singleton methods on the class)
|
|
309
|
+
class_def.extended_modules.each do |module_name|
|
|
310
|
+
lines << " rb_extend_object(#{class_var}, m#{module_name});"
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
class_def.method_names.each do |method_name|
|
|
314
|
+
mangled_name = mangle_method_name(class_def.name, method_name)
|
|
315
|
+
func = llvm_generator.mod.functions[mangled_name]
|
|
316
|
+
next unless func
|
|
317
|
+
|
|
318
|
+
# Use -1 arity for variadic functions
|
|
319
|
+
arity = llvm_generator.variadic_functions[mangled_name] ? -1 : func.params.size - 1
|
|
320
|
+
# Method visibility
|
|
321
|
+
define_func = if class_def.private_methods.include?(method_name)
|
|
322
|
+
"rb_define_private_method"
|
|
323
|
+
elsif class_def.protected_methods.include?(method_name)
|
|
324
|
+
"rb_define_protected_method"
|
|
325
|
+
else
|
|
326
|
+
"rb_define_method"
|
|
327
|
+
end
|
|
328
|
+
lines << " #{define_func}(#{class_var}, \"#{method_name}\", #{mangled_name}, #{arity});"
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Register singleton methods (class << self / def self.xxx)
|
|
332
|
+
class_def.singleton_methods.each do |method_name|
|
|
333
|
+
# Skip @cfunc methods - they are direct C calls, not Ruby methods
|
|
334
|
+
next if @rbs_loader&.cfunc_method?(class_def.name, method_name, singleton: true)
|
|
335
|
+
|
|
336
|
+
mangled_name = mangle_method_name(class_def.name, method_name)
|
|
337
|
+
func = llvm_generator.mod.functions[mangled_name]
|
|
338
|
+
next unless func
|
|
339
|
+
|
|
340
|
+
arity = llvm_generator.variadic_functions[mangled_name] ? -1 : func.params.size - 1
|
|
341
|
+
lines << " rb_define_singleton_method(#{class_var}, \"#{method_name}\", #{mangled_name}, #{arity});"
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Register aliases
|
|
345
|
+
class_def.aliases.each do |new_name, old_name|
|
|
346
|
+
lines << " rb_define_alias(#{class_var}, \"#{new_name}\", \"#{old_name}\");"
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# Define top-level methods on Object
|
|
351
|
+
hir.functions.each do |func_def|
|
|
352
|
+
next if func_def.owner_class
|
|
353
|
+
next if func_def.owner_module
|
|
354
|
+
next if func_def.name == "__main__"
|
|
355
|
+
|
|
356
|
+
mangled_name = "rn_#{func_def.name}".gsub(/[^a-zA-Z0-9_]/, "_")
|
|
357
|
+
func = llvm_generator.mod.functions[mangled_name]
|
|
358
|
+
next unless func
|
|
359
|
+
|
|
360
|
+
# Use -1 arity for variadic functions (**kwargs, *args)
|
|
361
|
+
if llvm_generator.variadic_functions[mangled_name]
|
|
362
|
+
arity = -1
|
|
363
|
+
else
|
|
364
|
+
arity = func.params.size - 1
|
|
365
|
+
end
|
|
366
|
+
lines << " rb_define_private_method(rb_cObject, \"#{func_def.name}\", #{mangled_name}, #{arity});"
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
lines << "}"
|
|
370
|
+
lines << ""
|
|
371
|
+
|
|
372
|
+
lines.join("\n")
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# Generate C struct definition body and TypedData for a NativeClass
|
|
376
|
+
# Uses forward-declared struct name (Native_ClassName_s) to allow self-referential types
|
|
377
|
+
def generate_native_class_struct_body(class_name, class_type)
|
|
378
|
+
lines = []
|
|
379
|
+
struct_name = "Native_#{class_name}"
|
|
380
|
+
native_classes = @rbs_loader&.native_classes || {}
|
|
381
|
+
uses_vtable = class_type.uses_vtable?(native_classes)
|
|
382
|
+
|
|
383
|
+
# Struct definition with tag (for forward declaration)
|
|
384
|
+
lines << "struct Native_#{class_name}_s {"
|
|
385
|
+
|
|
386
|
+
# For vtable classes, add vptr as first field
|
|
387
|
+
if uses_vtable
|
|
388
|
+
lines << " void **vptr; /* Pointer to vtable */"
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
class_type.fields.each do |field_name, field_type|
|
|
392
|
+
c_type = case field_type
|
|
393
|
+
when :Int64 then "int64_t"
|
|
394
|
+
when :Float64 then "double"
|
|
395
|
+
when :Bool then "int8_t"
|
|
396
|
+
when :String, :Object, :Array, :Hash then "VALUE"
|
|
397
|
+
when Hash
|
|
398
|
+
# Reference to NativeClass - stored as VALUE (can be nil)
|
|
399
|
+
"VALUE"
|
|
400
|
+
else
|
|
401
|
+
# Embedded NativeClass - use the struct type
|
|
402
|
+
if @rbs_loader&.native_class?(field_type)
|
|
403
|
+
"Native_#{field_type}"
|
|
404
|
+
else
|
|
405
|
+
"VALUE"
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
lines << " #{c_type} #{field_name};"
|
|
409
|
+
end
|
|
410
|
+
lines << "};"
|
|
411
|
+
lines << ""
|
|
412
|
+
|
|
413
|
+
# GC mark function (if class has Ruby object fields)
|
|
414
|
+
ruby_fields = class_type.ruby_object_field_names(@rbs_loader&.native_classes || {})
|
|
415
|
+
unless ruby_fields.empty?
|
|
416
|
+
lines << "static void #{class_name}_mark(void *ptr) {"
|
|
417
|
+
lines << " #{struct_name} *obj = (#{struct_name} *)ptr;"
|
|
418
|
+
ruby_fields.each do |field_name|
|
|
419
|
+
lines << " rb_gc_mark(obj->#{field_name});"
|
|
420
|
+
end
|
|
421
|
+
lines << "}"
|
|
422
|
+
lines << ""
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# TypedData type definition
|
|
426
|
+
ruby_fields = class_type.ruby_object_field_names(@rbs_loader&.native_classes || {})
|
|
427
|
+
dmark_func = ruby_fields.empty? ? "NULL" : "#{class_name}_mark"
|
|
428
|
+
|
|
429
|
+
lines << "static const rb_data_type_t #{class_name}_type = {"
|
|
430
|
+
lines << " .wrap_struct_name = \"#{class_name}\","
|
|
431
|
+
lines << " .function = {"
|
|
432
|
+
lines << " .dmark = #{dmark_func},"
|
|
433
|
+
lines << " .dfree = RUBY_DEFAULT_FREE,"
|
|
434
|
+
lines << " .dsize = NULL,"
|
|
435
|
+
lines << " },"
|
|
436
|
+
lines << " .flags = RUBY_TYPED_FREE_IMMEDIATELY,"
|
|
437
|
+
lines << "};"
|
|
438
|
+
lines << ""
|
|
439
|
+
|
|
440
|
+
# Allocator function
|
|
441
|
+
lines << "static VALUE #{class_name}_alloc(VALUE klass) {"
|
|
442
|
+
lines << " #{struct_name} *ptr;"
|
|
443
|
+
lines << " VALUE obj = TypedData_Make_Struct(klass, #{struct_name}, &#{class_name}_type, ptr);"
|
|
444
|
+
|
|
445
|
+
# Initialize vptr if using vtable
|
|
446
|
+
if uses_vtable
|
|
447
|
+
lines << " ptr->vptr = vtable_#{class_name};"
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
class_type.fields.each do |field_name, field_type|
|
|
451
|
+
case field_type
|
|
452
|
+
when :Int64
|
|
453
|
+
lines << " ptr->#{field_name} = 0;"
|
|
454
|
+
when :Float64
|
|
455
|
+
lines << " ptr->#{field_name} = 0.0;"
|
|
456
|
+
when :Bool
|
|
457
|
+
lines << " ptr->#{field_name} = 0;"
|
|
458
|
+
when :String, :Object, :Array, :Hash
|
|
459
|
+
lines << " ptr->#{field_name} = Qnil;"
|
|
460
|
+
when ::Hash
|
|
461
|
+
# Reference to NativeClass - initialize to nil
|
|
462
|
+
lines << " ptr->#{field_name} = Qnil;"
|
|
463
|
+
else
|
|
464
|
+
# Embedded NativeClass - initialize with memset or field-by-field
|
|
465
|
+
if @rbs_loader&.native_class?(field_type)
|
|
466
|
+
lines << " memset(&ptr->#{field_name}, 0, sizeof(Native_#{field_type}));"
|
|
467
|
+
else
|
|
468
|
+
lines << " ptr->#{field_name} = Qnil;"
|
|
469
|
+
end
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
lines << " return obj;"
|
|
473
|
+
lines << "}"
|
|
474
|
+
lines << ""
|
|
475
|
+
|
|
476
|
+
# Field accessor wrappers (getter)
|
|
477
|
+
class_type.fields.each do |field_name, field_type|
|
|
478
|
+
lines << "static VALUE #{class_name}_get_#{field_name}(VALUE self) {"
|
|
479
|
+
lines << " #{struct_name} *ptr;"
|
|
480
|
+
lines << " TypedData_Get_Struct(self, #{struct_name}, &#{class_name}_type, ptr);"
|
|
481
|
+
case field_type
|
|
482
|
+
when :Int64
|
|
483
|
+
lines << " return rb_int2inum(ptr->#{field_name});"
|
|
484
|
+
when :Float64
|
|
485
|
+
lines << " return rb_float_new(ptr->#{field_name});"
|
|
486
|
+
when :Bool
|
|
487
|
+
lines << " return ptr->#{field_name} ? Qtrue : Qfalse;"
|
|
488
|
+
when :String, :Object, :Array, :Hash
|
|
489
|
+
lines << " return ptr->#{field_name};"
|
|
490
|
+
when ::Hash
|
|
491
|
+
# Reference to NativeClass - return VALUE directly (can be nil)
|
|
492
|
+
lines << " return ptr->#{field_name};"
|
|
493
|
+
else
|
|
494
|
+
# Embedded NativeClass - return a new Ruby object with a copy
|
|
495
|
+
if @rbs_loader&.native_class?(field_type)
|
|
496
|
+
embedded_struct = "Native_#{field_type}"
|
|
497
|
+
lines << " VALUE klass = rb_const_get(rb_cObject, rb_intern(\"#{field_type}\"));"
|
|
498
|
+
lines << " VALUE result = #{field_type}_alloc(klass);"
|
|
499
|
+
lines << " #{embedded_struct} *result_ptr;"
|
|
500
|
+
lines << " TypedData_Get_Struct(result, #{embedded_struct}, &#{field_type}_type, result_ptr);"
|
|
501
|
+
lines << " *result_ptr = ptr->#{field_name};"
|
|
502
|
+
lines << " return result;"
|
|
503
|
+
else
|
|
504
|
+
lines << " return ptr->#{field_name};"
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
lines << "}"
|
|
508
|
+
lines << ""
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
# Field accessor wrappers (setter)
|
|
512
|
+
class_type.fields.each do |field_name, field_type|
|
|
513
|
+
lines << "static VALUE #{class_name}_set_#{field_name}(VALUE self, VALUE val) {"
|
|
514
|
+
lines << " #{struct_name} *ptr;"
|
|
515
|
+
lines << " TypedData_Get_Struct(self, #{struct_name}, &#{class_name}_type, ptr);"
|
|
516
|
+
case field_type
|
|
517
|
+
when :Int64
|
|
518
|
+
lines << " ptr->#{field_name} = NUM2LONG(val);"
|
|
519
|
+
when :Float64
|
|
520
|
+
lines << " ptr->#{field_name} = NUM2DBL(val);"
|
|
521
|
+
when :Bool
|
|
522
|
+
lines << " ptr->#{field_name} = RTEST(val) ? 1 : 0;"
|
|
523
|
+
when :String, :Object, :Array, :Hash
|
|
524
|
+
lines << " ptr->#{field_name} = val;"
|
|
525
|
+
when ::Hash
|
|
526
|
+
# Reference to NativeClass - store VALUE directly
|
|
527
|
+
lines << " ptr->#{field_name} = val;"
|
|
528
|
+
else
|
|
529
|
+
# Embedded NativeClass - copy from the passed object
|
|
530
|
+
if @rbs_loader&.native_class?(field_type)
|
|
531
|
+
embedded_struct = "Native_#{field_type}"
|
|
532
|
+
lines << " #{embedded_struct} *val_ptr;"
|
|
533
|
+
lines << " TypedData_Get_Struct(val, #{embedded_struct}, &#{field_type}_type, val_ptr);"
|
|
534
|
+
lines << " ptr->#{field_name} = *val_ptr;"
|
|
535
|
+
else
|
|
536
|
+
lines << " ptr->#{field_name} = val;"
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
lines << " return val;"
|
|
540
|
+
lines << "}"
|
|
541
|
+
lines << ""
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
lines
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
# Generate extern declaration for a native function
|
|
548
|
+
def generate_native_func_declaration(class_name, class_type, method_name, method_sig)
|
|
549
|
+
lines = []
|
|
550
|
+
struct_name = "Native_#{class_name}"
|
|
551
|
+
func_name = mangle_method_name(class_name, method_name)
|
|
552
|
+
|
|
553
|
+
# Build parameter list: first is struct pointer for self
|
|
554
|
+
params = ["#{struct_name}* self"]
|
|
555
|
+
|
|
556
|
+
method_sig.param_types.each_with_index do |param_type, i|
|
|
557
|
+
param_name = method_sig.param_names[i] || "arg#{i}"
|
|
558
|
+
c_type = native_type_to_c(param_type, class_name)
|
|
559
|
+
params << "#{c_type} #{param_name}"
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
# Return type
|
|
563
|
+
return_c_type = native_return_type_to_c(method_sig.return_type, class_name)
|
|
564
|
+
|
|
565
|
+
lines << "extern #{return_c_type} #{func_name}(#{params.join(', ')});"
|
|
566
|
+
lines
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
# Generate wrapper function for a NativeClass method
|
|
570
|
+
def generate_native_method_wrapper(class_name, class_type, method_name, method_sig)
|
|
571
|
+
lines = []
|
|
572
|
+
struct_name = "Native_#{class_name}"
|
|
573
|
+
native_func = mangle_method_name(class_name, method_name)
|
|
574
|
+
sanitized_method = sanitize_c_name(method_name.to_s)
|
|
575
|
+
wrapper_name = "rn_wrap_#{class_name}_#{sanitized_method}"
|
|
576
|
+
|
|
577
|
+
arity = method_sig.param_types.size
|
|
578
|
+
|
|
579
|
+
# Build parameter list for wrapper
|
|
580
|
+
params = ["VALUE self"]
|
|
581
|
+
arity.times { |i| params << "VALUE arg#{i}" }
|
|
582
|
+
|
|
583
|
+
lines << "static VALUE #{wrapper_name}(#{params.join(', ')}) {"
|
|
584
|
+
lines << " #{struct_name} *ptr;"
|
|
585
|
+
lines << " TypedData_Get_Struct(self, #{struct_name}, &#{class_name}_type, ptr);"
|
|
586
|
+
|
|
587
|
+
# Convert Ruby arguments to native types
|
|
588
|
+
call_args = ["ptr"]
|
|
589
|
+
method_sig.param_types.each_with_index do |param_type, i|
|
|
590
|
+
arg_name = "native_arg#{i}"
|
|
591
|
+
lines.concat(convert_ruby_to_native("arg#{i}", arg_name, param_type, class_name, class_type))
|
|
592
|
+
call_args << arg_name
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
# Call native function
|
|
596
|
+
return_type = method_sig.return_type
|
|
597
|
+
if return_type == :Void
|
|
598
|
+
lines << " #{native_func}(#{call_args.join(', ')});"
|
|
599
|
+
lines << " return Qnil;"
|
|
600
|
+
elsif return_type == :Self || @rbs_loader&.native_class?(return_type)
|
|
601
|
+
# Struct returned by value - allocate Ruby object and copy into it
|
|
602
|
+
result_class = return_type == :Self ? class_name : return_type
|
|
603
|
+
result_struct = "Native_#{result_class}"
|
|
604
|
+
lines << " #{result_struct} result = #{native_func}(#{call_args.join(', ')});"
|
|
605
|
+
lines << " VALUE result_klass = rb_const_get(rb_cObject, rb_intern(\"#{result_class}\"));"
|
|
606
|
+
lines << " VALUE result_obj = #{result_class}_alloc(result_klass);"
|
|
607
|
+
lines << " #{result_struct} *result_ptr;"
|
|
608
|
+
lines << " TypedData_Get_Struct(result_obj, #{result_struct}, &#{result_class}_type, result_ptr);"
|
|
609
|
+
lines << " *result_ptr = result;"
|
|
610
|
+
lines << " return result_obj;"
|
|
611
|
+
elsif return_type == :Int64
|
|
612
|
+
lines << " int64_t result = #{native_func}(#{call_args.join(', ')});"
|
|
613
|
+
lines << " return rb_int2inum(result);"
|
|
614
|
+
elsif return_type == :Float64
|
|
615
|
+
lines << " double result = #{native_func}(#{call_args.join(', ')});"
|
|
616
|
+
lines << " return rb_float_new(result);"
|
|
617
|
+
else
|
|
618
|
+
lines << " double result = #{native_func}(#{call_args.join(', ')});"
|
|
619
|
+
lines << " return rb_float_new(result);"
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
lines << "}"
|
|
623
|
+
lines << ""
|
|
624
|
+
lines
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
# Generate vtable for a NativeClass
|
|
628
|
+
def generate_vtable(class_name, class_type, native_classes)
|
|
629
|
+
lines = []
|
|
630
|
+
|
|
631
|
+
vtable_methods = class_type.vtable_methods(native_classes)
|
|
632
|
+
return lines if vtable_methods.empty?
|
|
633
|
+
|
|
634
|
+
# Generate vtable as array of function pointers
|
|
635
|
+
lines << "/* Vtable for #{class_name} */"
|
|
636
|
+
lines << "static void *vtable_#{class_name}[] = {"
|
|
637
|
+
|
|
638
|
+
vtable_methods.each_with_index do |(method_name, _method_sig, owner_name), idx|
|
|
639
|
+
# Each entry points to the implementing class's function
|
|
640
|
+
func_name = mangle_method_name(owner_name, method_name)
|
|
641
|
+
comma = idx < vtable_methods.size - 1 ? "," : ""
|
|
642
|
+
lines << " (void *)#{func_name}#{comma} /* #{idx}: #{method_name} */"
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
lines << "};"
|
|
646
|
+
lines << ""
|
|
647
|
+
lines
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
# Generate Init code for a NativeClass
|
|
651
|
+
def generate_native_class_init(class_name, class_type)
|
|
652
|
+
lines = []
|
|
653
|
+
class_var = "c#{class_name}"
|
|
654
|
+
|
|
655
|
+
# Define class with superclass
|
|
656
|
+
superclass = class_type.superclass
|
|
657
|
+
if superclass && @rbs_loader&.native_class?(superclass)
|
|
658
|
+
lines << " VALUE #{class_var} = rb_define_class(\"#{class_name}\", c#{superclass});"
|
|
659
|
+
else
|
|
660
|
+
lines << " VALUE #{class_var} = rb_define_class(\"#{class_name}\", rb_cObject);"
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
# Register allocator
|
|
664
|
+
lines << " rb_define_alloc_func(#{class_var}, #{class_name}_alloc);"
|
|
665
|
+
|
|
666
|
+
# Register field accessors
|
|
667
|
+
class_type.fields.each do |field_name, _field_type|
|
|
668
|
+
lines << " rb_define_method(#{class_var}, \"#{field_name}\", #{class_name}_get_#{field_name}, 0);"
|
|
669
|
+
lines << " rb_define_method(#{class_var}, \"#{field_name}=\", #{class_name}_set_#{field_name}, 1);"
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
# Register native methods
|
|
673
|
+
class_type.methods.each do |method_name, method_sig|
|
|
674
|
+
sanitized_method = sanitize_c_name(method_name.to_s)
|
|
675
|
+
wrapper_name = "rn_wrap_#{class_name}_#{sanitized_method}"
|
|
676
|
+
arity = method_sig.param_types.size
|
|
677
|
+
lines << " rb_define_method(#{class_var}, \"#{method_name}\", #{wrapper_name}, #{arity});"
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
lines
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
# Convert native type symbol to C type string
|
|
684
|
+
def native_type_to_c(type_sym, current_class)
|
|
685
|
+
case type_sym
|
|
686
|
+
when :Int64 then "int64_t"
|
|
687
|
+
when :Float64 then "double"
|
|
688
|
+
when :Self then "Native_#{current_class}*"
|
|
689
|
+
else
|
|
690
|
+
# Another NativeClass - use pointer
|
|
691
|
+
"Native_#{type_sym}*"
|
|
692
|
+
end
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
# Convert native return type to C type (structs are returned by value)
|
|
696
|
+
def native_return_type_to_c(type_sym, current_class)
|
|
697
|
+
case type_sym
|
|
698
|
+
when :Int64 then "int64_t"
|
|
699
|
+
when :Float64 then "double"
|
|
700
|
+
when :Void then "void"
|
|
701
|
+
when :Self then "Native_#{current_class}" # Return by value
|
|
702
|
+
else
|
|
703
|
+
# Another NativeClass - return by value
|
|
704
|
+
"Native_#{type_sym}"
|
|
705
|
+
end
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
# Generate code to convert Ruby VALUE to native type
|
|
709
|
+
def convert_ruby_to_native(ruby_var, native_var, type_sym, current_class, class_type)
|
|
710
|
+
lines = []
|
|
711
|
+
case type_sym
|
|
712
|
+
when :Int64
|
|
713
|
+
lines << " int64_t #{native_var} = NUM2LONG(#{ruby_var});"
|
|
714
|
+
when :Float64
|
|
715
|
+
lines << " double #{native_var} = NUM2DBL(#{ruby_var});"
|
|
716
|
+
when :Self
|
|
717
|
+
struct_name = "Native_#{current_class}"
|
|
718
|
+
lines << " #{struct_name} *#{native_var};"
|
|
719
|
+
lines << " TypedData_Get_Struct(#{ruby_var}, #{struct_name}, &#{current_class}_type, #{native_var});"
|
|
720
|
+
else
|
|
721
|
+
# Another NativeClass
|
|
722
|
+
struct_name = "Native_#{type_sym}"
|
|
723
|
+
lines << " #{struct_name} *#{native_var};"
|
|
724
|
+
lines << " TypedData_Get_Struct(#{ruby_var}, #{struct_name}, &#{type_sym}_type, #{native_var});"
|
|
725
|
+
end
|
|
726
|
+
lines
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
def mangle_method_name(class_name, method_name)
|
|
730
|
+
owner = class_name.to_s.gsub(/[^a-zA-Z0-9_]/, "_")
|
|
731
|
+
name = sanitize_c_name(method_name.to_s)
|
|
732
|
+
"rn_#{owner}_#{name}"
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
# Sanitize a Ruby method name to a valid C identifier
|
|
736
|
+
# Operators get distinctive names to avoid collisions
|
|
737
|
+
OPERATOR_NAME_MAP = {
|
|
738
|
+
"+" => "op_plus", "-" => "op_minus", "*" => "op_mul", "/" => "op_div",
|
|
739
|
+
"%" => "op_mod", "**" => "op_pow", "==" => "op_eq", "!=" => "op_neq",
|
|
740
|
+
"<" => "op_lt", ">" => "op_gt", "<=" => "op_le", ">=" => "op_ge",
|
|
741
|
+
"<=>" => "op_cmp", "<<" => "op_lshift", ">>" => "op_rshift",
|
|
742
|
+
"&" => "op_and", "|" => "op_or", "^" => "op_xor", "~" => "op_not",
|
|
743
|
+
"[]" => "op_aref", "[]=" => "op_aset", "+@" => "op_uplus", "-@" => "op_uminus",
|
|
744
|
+
}.freeze
|
|
745
|
+
|
|
746
|
+
def sanitize_c_name(name)
|
|
747
|
+
return OPERATOR_NAME_MAP[name] if OPERATOR_NAME_MAP.key?(name)
|
|
748
|
+
name.gsub(/[^a-zA-Z0-9_]/, "_")
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
def compile_ir_to_object(ir_file, obj_file)
|
|
752
|
+
llc = find_llvm_tool("llc")
|
|
753
|
+
optimized_ir = nil
|
|
754
|
+
|
|
755
|
+
# Run opt passes before llc for better optimization
|
|
756
|
+
# In debug mode, skip opt to preserve debug info
|
|
757
|
+
unless @debug
|
|
758
|
+
opt = find_llvm_tool("opt")
|
|
759
|
+
if opt
|
|
760
|
+
optimized_ir = "#{ir_file}.opt.ll"
|
|
761
|
+
opt_cmd = [
|
|
762
|
+
opt,
|
|
763
|
+
"--passes=default<O2>",
|
|
764
|
+
"-S", # Output as text IR (not bitcode)
|
|
765
|
+
"-o", optimized_ir,
|
|
766
|
+
ir_file
|
|
767
|
+
]
|
|
768
|
+
if system(*opt_cmd)
|
|
769
|
+
ir_file = optimized_ir
|
|
770
|
+
else
|
|
771
|
+
optimized_ir = nil # Don't clean up if opt failed
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
# Use llc to compile IR to object file
|
|
777
|
+
# -O2 enables optimization passes including mem2reg which converts
|
|
778
|
+
# allocas to proper SSA form with Phi nodes for loop variables
|
|
779
|
+
# In debug mode, use -O0 to preserve debug info
|
|
780
|
+
opt_level = @debug ? "-O0" : "-O2"
|
|
781
|
+
|
|
782
|
+
cmd = [
|
|
783
|
+
llc,
|
|
784
|
+
opt_level,
|
|
785
|
+
"-filetype=obj",
|
|
786
|
+
"-relocation-model=pic" # Required for shared libraries
|
|
787
|
+
]
|
|
788
|
+
|
|
789
|
+
# Add debug-specific options
|
|
790
|
+
if @debug
|
|
791
|
+
cmd << "--debugger-tune=#{Platform.debugger_tune}"
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
cmd += ["-o", obj_file, ir_file]
|
|
795
|
+
|
|
796
|
+
system(*cmd) or raise CodegenError, "Failed to compile LLVM IR to object file"
|
|
797
|
+
ensure
|
|
798
|
+
FileUtils.rm_f(optimized_ir) if optimized_ir
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
def compile_c_to_object(c_file, obj_file)
|
|
802
|
+
cc = find_llvm_tool("clang") || "cc"
|
|
803
|
+
|
|
804
|
+
cmd = [
|
|
805
|
+
cc,
|
|
806
|
+
"-c",
|
|
807
|
+
"-fPIC",
|
|
808
|
+
"-I#{RbConfig::CONFIG['rubyhdrdir']}",
|
|
809
|
+
"-I#{RbConfig::CONFIG['rubyarchhdrdir']}",
|
|
810
|
+
"-o", obj_file,
|
|
811
|
+
c_file
|
|
812
|
+
]
|
|
813
|
+
|
|
814
|
+
system(*cmd) or raise CodegenError, "Failed to compile C init wrapper"
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
def link_to_shared_library(obj_files, output_file)
|
|
818
|
+
obj_files = Array(obj_files)
|
|
819
|
+
|
|
820
|
+
# Use clang or system linker
|
|
821
|
+
clang = find_llvm_tool("clang") || "cc"
|
|
822
|
+
|
|
823
|
+
# Get Ruby's library flags
|
|
824
|
+
ruby_libs = ruby_link_flags
|
|
825
|
+
|
|
826
|
+
# Get FFI library flags from RBS annotations
|
|
827
|
+
ffi_libs = ffi_link_flags
|
|
828
|
+
|
|
829
|
+
cmd = [
|
|
830
|
+
clang,
|
|
831
|
+
"-shared",
|
|
832
|
+
"-fPIC"
|
|
833
|
+
]
|
|
834
|
+
|
|
835
|
+
# Add debug flag to preserve debug info during linking
|
|
836
|
+
if @debug
|
|
837
|
+
cmd << "-g"
|
|
838
|
+
end
|
|
839
|
+
|
|
840
|
+
cmd += [
|
|
841
|
+
"-o", output_file,
|
|
842
|
+
*obj_files,
|
|
843
|
+
*ruby_libs,
|
|
844
|
+
*ffi_libs
|
|
845
|
+
]
|
|
846
|
+
|
|
847
|
+
# Add platform-specific flags
|
|
848
|
+
case RbConfig::CONFIG["host_os"]
|
|
849
|
+
when /darwin/
|
|
850
|
+
cmd << "-undefined"
|
|
851
|
+
cmd << "dynamic_lookup"
|
|
852
|
+
when /mingw|mswin|cygwin/
|
|
853
|
+
cmd << "-Wl,--export-all-symbols"
|
|
854
|
+
when /linux/
|
|
855
|
+
# Don't use --no-undefined for extensions
|
|
856
|
+
end
|
|
857
|
+
|
|
858
|
+
system(*cmd) or raise CodegenError, "Failed to link shared library"
|
|
859
|
+
|
|
860
|
+
# On macOS, generate dSYM bundle for debug info
|
|
861
|
+
if @debug && RbConfig::CONFIG["host_os"] =~ /darwin/
|
|
862
|
+
dsymutil = "dsymutil"
|
|
863
|
+
dsym_cmd = [dsymutil, output_file]
|
|
864
|
+
system(*dsym_cmd) # Don't fail if dsymutil is not available
|
|
865
|
+
end
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
# Get link flags for @ffi annotated libraries
|
|
869
|
+
def ffi_link_flags
|
|
870
|
+
flags = []
|
|
871
|
+
|
|
872
|
+
# Add yyjson object files if JSON parse_as is used
|
|
873
|
+
if @uses_json_parse_as
|
|
874
|
+
yyjson_objs = ensure_yyjson_compiled
|
|
875
|
+
flags.concat(yyjson_objs)
|
|
876
|
+
end
|
|
877
|
+
|
|
878
|
+
if @rbs_loader
|
|
879
|
+
@rbs_loader.all_ffi_libraries.each do |lib_name|
|
|
880
|
+
# Convert library name to linker flag
|
|
881
|
+
# "libm" -> "-lm", "libfoo" -> "-lfoo", "foo" -> "-lfoo"
|
|
882
|
+
link_name = lib_name.sub(/^lib/, "")
|
|
883
|
+
flags << "-l#{link_name}"
|
|
884
|
+
end
|
|
885
|
+
end
|
|
886
|
+
|
|
887
|
+
flags
|
|
888
|
+
end
|
|
889
|
+
|
|
890
|
+
# Compile yyjson.c and wrapper to object files if needed
|
|
891
|
+
# Returns array of object file paths
|
|
892
|
+
def ensure_yyjson_compiled
|
|
893
|
+
yyjson_dir = File.expand_path("../../../vendor/yyjson", __dir__)
|
|
894
|
+
yyjson_c = File.join(yyjson_dir, "yyjson.c")
|
|
895
|
+
yyjson_obj = File.join(yyjson_dir, "yyjson.o")
|
|
896
|
+
wrapper_c = File.join(yyjson_dir, "yyjson_wrapper.c")
|
|
897
|
+
wrapper_obj = File.join(yyjson_dir, "yyjson_wrapper.o")
|
|
898
|
+
|
|
899
|
+
return [] unless File.exist?(yyjson_c) && File.exist?(wrapper_c)
|
|
900
|
+
|
|
901
|
+
cc = find_llvm_tool("clang") || "cc"
|
|
902
|
+
|
|
903
|
+
# Compile yyjson.c
|
|
904
|
+
unless File.exist?(yyjson_obj) && File.mtime(yyjson_obj) > File.mtime(yyjson_c)
|
|
905
|
+
cmd = [cc, "-c", "-O3", "-fPIC", "-o", yyjson_obj, yyjson_c]
|
|
906
|
+
system(*cmd) or return []
|
|
907
|
+
end
|
|
908
|
+
|
|
909
|
+
# Compile wrapper
|
|
910
|
+
unless File.exist?(wrapper_obj) && File.mtime(wrapper_obj) > File.mtime(wrapper_c)
|
|
911
|
+
cmd = [cc, "-c", "-O3", "-fPIC", "-I#{yyjson_dir}", "-o", wrapper_obj, wrapper_c]
|
|
912
|
+
system(*cmd) or return []
|
|
913
|
+
end
|
|
914
|
+
|
|
915
|
+
[yyjson_obj, wrapper_obj]
|
|
916
|
+
end
|
|
917
|
+
|
|
918
|
+
def ruby_link_flags
|
|
919
|
+
# Get Ruby's linker flags for extensions
|
|
920
|
+
[
|
|
921
|
+
"-L#{RbConfig::CONFIG['libdir']}",
|
|
922
|
+
# Don't link against libruby for extensions - they're loaded by Ruby
|
|
923
|
+
]
|
|
924
|
+
end
|
|
925
|
+
|
|
926
|
+
# Sort native classes in dependency order (embedded types first)
|
|
927
|
+
def topological_sort_native_classes(native_classes)
|
|
928
|
+
sorted = []
|
|
929
|
+
visited = {}
|
|
930
|
+
temp_mark = {}
|
|
931
|
+
|
|
932
|
+
visit = lambda do |name|
|
|
933
|
+
return if visited[name]
|
|
934
|
+
raise "Circular dependency detected in NativeClass #{name}" if temp_mark[name]
|
|
935
|
+
|
|
936
|
+
temp_mark[name] = true
|
|
937
|
+
|
|
938
|
+
class_type = native_classes[name]
|
|
939
|
+
if class_type
|
|
940
|
+
# Visit dependencies (embedded NativeClass fields only, not references)
|
|
941
|
+
class_type.fields.each_value do |field_type|
|
|
942
|
+
next if TypeChecker::Types::NativeClassType::ALLOWED_PRIMITIVE_TYPES.include?(field_type)
|
|
943
|
+
next if TypeChecker::Types::NativeClassType::RUBY_OBJECT_TYPES.include?(field_type)
|
|
944
|
+
next if field_type.is_a?(Hash) # Skip references (stored as VALUE)
|
|
945
|
+
visit.call(field_type) if native_classes.key?(field_type)
|
|
946
|
+
end
|
|
947
|
+
|
|
948
|
+
# Also visit superclass
|
|
949
|
+
visit.call(class_type.superclass) if class_type.superclass && native_classes.key?(class_type.superclass)
|
|
950
|
+
end
|
|
951
|
+
|
|
952
|
+
temp_mark.delete(name)
|
|
953
|
+
visited[name] = true
|
|
954
|
+
sorted << name
|
|
955
|
+
end
|
|
956
|
+
|
|
957
|
+
native_classes.each_key { |name| visit.call(name) }
|
|
958
|
+
sorted
|
|
959
|
+
end
|
|
960
|
+
|
|
961
|
+
# Topological sort for non-native classes (superclass before subclass)
|
|
962
|
+
def topological_sort_non_native_classes(class_defs)
|
|
963
|
+
by_name = class_defs.each_with_object({}) { |cd, h| h[cd.name] = cd }
|
|
964
|
+
sorted = []
|
|
965
|
+
visited = {}
|
|
966
|
+
|
|
967
|
+
visit = lambda do |cd|
|
|
968
|
+
return if visited[cd.name]
|
|
969
|
+
|
|
970
|
+
visited[cd.name] = true
|
|
971
|
+
if cd.superclass && by_name[cd.superclass]
|
|
972
|
+
visit.call(by_name[cd.superclass])
|
|
973
|
+
end
|
|
974
|
+
sorted << cd
|
|
975
|
+
end
|
|
976
|
+
|
|
977
|
+
class_defs.each { |cd| visit.call(cd) }
|
|
978
|
+
sorted
|
|
979
|
+
end
|
|
980
|
+
|
|
981
|
+
# Resolve superclass to C expression for rb_define_class
|
|
982
|
+
EXCEPTION_CLASS_MAP = {
|
|
983
|
+
"StandardError" => "rb_eStandardError",
|
|
984
|
+
"RuntimeError" => "rb_eRuntimeError",
|
|
985
|
+
"TypeError" => "rb_eTypeError",
|
|
986
|
+
"ArgumentError" => "rb_eArgError",
|
|
987
|
+
"NameError" => "rb_eNameError",
|
|
988
|
+
"NoMethodError" => "rb_eNoMethodError",
|
|
989
|
+
"RangeError" => "rb_eRangeError",
|
|
990
|
+
"IOError" => "rb_eIOError",
|
|
991
|
+
"EOFError" => "rb_eEOFError",
|
|
992
|
+
"IndexError" => "rb_eIndexError",
|
|
993
|
+
"KeyError" => "rb_eKeyError",
|
|
994
|
+
"StopIteration" => "rb_eStopIteration",
|
|
995
|
+
"ZeroDivisionError" => "rb_eZeroDivError",
|
|
996
|
+
"NotImplementedError" => "rb_eNotImpError",
|
|
997
|
+
"LoadError" => "rb_eLoadError",
|
|
998
|
+
"ScriptError" => "rb_eScriptError",
|
|
999
|
+
"SyntaxError" => "rb_eSyntaxError",
|
|
1000
|
+
"SecurityError" => "rb_eSecurityError",
|
|
1001
|
+
"RegexpError" => "rb_eRegexpError",
|
|
1002
|
+
"EncodingError" => "rb_eEncError",
|
|
1003
|
+
"Errno::ENOENT" => "rb_eSystemCallError",
|
|
1004
|
+
"Exception" => "rb_eException",
|
|
1005
|
+
}.freeze
|
|
1006
|
+
|
|
1007
|
+
KNOWN_SUPERCLASS_MAP = {
|
|
1008
|
+
"Numeric" => "rb_cNumeric",
|
|
1009
|
+
"Integer" => "rb_cInteger",
|
|
1010
|
+
"Float" => "rb_cFloat",
|
|
1011
|
+
"String" => "rb_cString",
|
|
1012
|
+
"Array" => "rb_cArray",
|
|
1013
|
+
"Hash" => "rb_cHash",
|
|
1014
|
+
"IO" => "rb_cIO",
|
|
1015
|
+
"Struct" => "rb_cStruct",
|
|
1016
|
+
"Comparable" => "rb_mComparable",
|
|
1017
|
+
}.freeze
|
|
1018
|
+
|
|
1019
|
+
def resolve_superclass_c_expr(superclass_name, non_native_classes)
|
|
1020
|
+
return "rb_cObject" unless superclass_name
|
|
1021
|
+
|
|
1022
|
+
# Check known exception classes
|
|
1023
|
+
if EXCEPTION_CLASS_MAP[superclass_name]
|
|
1024
|
+
return EXCEPTION_CLASS_MAP[superclass_name]
|
|
1025
|
+
end
|
|
1026
|
+
|
|
1027
|
+
# Check known standard classes
|
|
1028
|
+
if KNOWN_SUPERCLASS_MAP[superclass_name]
|
|
1029
|
+
return KNOWN_SUPERCLASS_MAP[superclass_name]
|
|
1030
|
+
end
|
|
1031
|
+
|
|
1032
|
+
# Check if it's a user-defined class in the same compilation unit
|
|
1033
|
+
if non_native_classes.any? { |cd| cd.name == superclass_name }
|
|
1034
|
+
return "c#{superclass_name}"
|
|
1035
|
+
end
|
|
1036
|
+
|
|
1037
|
+
# Fallback: runtime constant lookup
|
|
1038
|
+
"rb_const_get(rb_cObject, rb_intern(\"#{superclass_name}\"))"
|
|
1039
|
+
end
|
|
1040
|
+
|
|
1041
|
+
# Generate extern declaration for a @cfunc C function
|
|
1042
|
+
def generate_cfunc_extern_declaration(cfunc_type)
|
|
1043
|
+
lines = []
|
|
1044
|
+
|
|
1045
|
+
c_params = cfunc_type.param_types.map do |type|
|
|
1046
|
+
cfunc_type_to_c(type)
|
|
1047
|
+
end.join(", ")
|
|
1048
|
+
c_params = "void" if c_params.empty?
|
|
1049
|
+
|
|
1050
|
+
c_return = cfunc_type_to_c(cfunc_type.return_type)
|
|
1051
|
+
|
|
1052
|
+
lines << "extern #{c_return} #{cfunc_type.c_func_name}(#{c_params});"
|
|
1053
|
+
lines
|
|
1054
|
+
end
|
|
1055
|
+
|
|
1056
|
+
# Convert CFuncType type symbol to C type string
|
|
1057
|
+
def cfunc_type_to_c(type_sym)
|
|
1058
|
+
case type_sym
|
|
1059
|
+
when :Float then "double"
|
|
1060
|
+
when :Integer then "int64_t"
|
|
1061
|
+
when :String then "VALUE"
|
|
1062
|
+
when :Bool then "int"
|
|
1063
|
+
when :void then "void"
|
|
1064
|
+
else "VALUE"
|
|
1065
|
+
end
|
|
1066
|
+
end
|
|
1067
|
+
|
|
1068
|
+
def find_llvm_tool(name)
|
|
1069
|
+
path = Platform.find_llvm_tool(name)
|
|
1070
|
+
return path if path
|
|
1071
|
+
|
|
1072
|
+
raise CodegenError, "Could not find LLVM tool: #{name}. #{Platform.llvm_install_hint}"
|
|
1073
|
+
end
|
|
1074
|
+
|
|
1075
|
+
def ptr_type
|
|
1076
|
+
LLVM::Pointer(LLVM::Int8)
|
|
1077
|
+
end
|
|
1078
|
+
|
|
1079
|
+
def profile_runtime_c_code
|
|
1080
|
+
# Read the profile runtime C code from the installed location
|
|
1081
|
+
runtime_path = File.join(__dir__, "profile_runtime.c")
|
|
1082
|
+
if File.exist?(runtime_path)
|
|
1083
|
+
File.read(runtime_path)
|
|
1084
|
+
else
|
|
1085
|
+
raise CodegenError, "Profile runtime not found at #{runtime_path}"
|
|
1086
|
+
end
|
|
1087
|
+
end
|
|
1088
|
+
end
|
|
1089
|
+
end
|
|
1090
|
+
end
|