carray-jit 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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +10 -0
  3. data/CHANGELOG.md +84 -0
  4. data/LICENSE +21 -0
  5. data/README.md +88 -0
  6. data/bin/carray-jit +194 -0
  7. data/carray-jit.gemspec +41 -0
  8. data/docs/00_Introduction.md +40 -0
  9. data/docs/01_GettingStarted.md +80 -0
  10. data/docs/02_KernelShapes.md +397 -0
  11. data/docs/03_SupportedFeatures.md +595 -0
  12. data/docs/04_Compiling.md +234 -0
  13. data/docs/05_DesignNotes.md +136 -0
  14. data/docs/06_Cheatsheet.md +177 -0
  15. data/examples/README.md +56 -0
  16. data/examples/applications/game_of_life.rb +161 -0
  17. data/examples/applications/heat_equation.rb +117 -0
  18. data/examples/applications/kepler.rb +178 -0
  19. data/examples/applications/mandelbrot.rb +151 -0
  20. data/examples/applications/moving_average.rb +124 -0
  21. data/examples/applications/partial_sums.rb +141 -0
  22. data/examples/applications/point_cloud.rb +110 -0
  23. data/examples/applications/quicksort.rb +118 -0
  24. data/examples/applications/recursion.rb +121 -0
  25. data/examples/applications/relaxation.rb +115 -0
  26. data/examples/applications/sensor_gaps.rb +118 -0
  27. data/examples/applications/sieve.rb +95 -0
  28. data/examples/applications/sobel_edges.rb +80 -0
  29. data/examples/features/01_element_wise.rb +69 -0
  30. data/examples/features/02_stencil.rb +40 -0
  31. data/examples/features/03_recurrence.rb +50 -0
  32. data/examples/features/04_thomas.rb +81 -0
  33. data/examples/features/05_reduction.rb +90 -0
  34. data/examples/features/06_jit_contract.rb +58 -0
  35. data/examples/features/07_masks.rb +55 -0
  36. data/examples/features/08_views.rb +46 -0
  37. data/examples/features/09_inspecting.rb +55 -0
  38. data/examples/features/10_complex.rb +107 -0
  39. data/examples/features/11_c_functions.rb +260 -0
  40. data/examples/features/12_sweep.rb +139 -0
  41. data/examples/features/13_cscalar.rb +80 -0
  42. data/examples/features/14_stencil_window.rb +106 -0
  43. data/examples/features/15_loops.rb +148 -0
  44. data/examples/features/16_raising.rb +69 -0
  45. data/ext/carray_jit_access/carray_jit_access.c +460 -0
  46. data/ext/carray_jit_access/extconf.rb +8 -0
  47. data/lib/carray/jit/analyzer.rb +1847 -0
  48. data/lib/carray/jit/block_reader.rb +139 -0
  49. data/lib/carray/jit/c_function.rb +777 -0
  50. data/lib/carray/jit/c_generator.rb +2305 -0
  51. data/lib/carray/jit/compiler.rb +468 -0
  52. data/lib/carray/jit/errors.rb +37 -0
  53. data/lib/carray/jit/expression.rb +202 -0
  54. data/lib/carray/jit/kernel.rb +509 -0
  55. data/lib/carray/jit/node.rb +573 -0
  56. data/lib/carray/jit/sweep.rb +97 -0
  57. data/lib/carray/jit/type_assignment.rb +811 -0
  58. data/lib/carray/jit/version.rb +5 -0
  59. data/lib/carray/jit.rb +1210 -0
  60. metadata +139 -0
@@ -0,0 +1,139 @@
1
+ require "prism"
2
+
3
+ class CArray
4
+ module JIT
5
+
6
+ # Recovers the source of a Proc so that a real block can be compiled.
7
+ #
8
+ # Ruby does not hand back a Proc's source, but it does say exactly where
9
+ # the block sits: RubyVM::InstructionSequence carries a :code_location of
10
+ # [first_line, first_column, last_line, last_column]. Columns are what
11
+ # make this usable -- Proc#source_location gives only a line, which cannot
12
+ # tell two blocks on one line apart.
13
+ #
14
+ # RubyVM::AbstractSyntaxTree.of would be the obvious route and does not
15
+ # work: since Ruby 3.4 the default parser is Prism, and it refuses with
16
+ # "cannot get AST for ISEQ compiled by prism".
17
+ module BlockReader
18
+
19
+ class << self
20
+
21
+ # Returns [Prism node for the block, its source text, where it was
22
+ # written]. The third is for the generated C to say what it came
23
+ # from: a hash names a kernel, a file and a line explain it.
24
+ def read (block)
25
+ unless defined?(RubyVM::InstructionSequence)
26
+ raise Unsupported,
27
+ "this Ruby has no RubyVM::InstructionSequence; " \
28
+ "pass the kernel as `source:` instead"
29
+ end
30
+
31
+ sequence = RubyVM::InstructionSequence.of(block)
32
+ unless sequence
33
+ raise Unsupported,
34
+ "the block has no instruction sequence (defined in C?); " \
35
+ "pass the kernel as `source:` instead"
36
+ end
37
+
38
+ location = code_location(sequence)
39
+ source = script_source(sequence)
40
+ node = locate(source, location)
41
+ [node, extract(source, location), origin(sequence, location)]
42
+ end
43
+
44
+ private
45
+
46
+ def origin (sequence, location)
47
+ path = begin
48
+ sequence.absolute_path || sequence.path
49
+ rescue NoMethodError
50
+ nil
51
+ end
52
+ path ? "#{path}:#{location[0]}" : nil
53
+ end
54
+
55
+ def code_location (sequence)
56
+ location = sequence.to_a[4][:code_location]
57
+ unless location
58
+ raise Unsupported,
59
+ "this Ruby does not report a code location for blocks; " \
60
+ "pass the kernel as `source:` instead"
61
+ end
62
+ location
63
+ end
64
+
65
+ # script_lines is present when the code was compiled with
66
+ # RubyVM.keep_script_lines set, which covers eval'd blocks; otherwise
67
+ # the file is read back.
68
+ def script_source (sequence)
69
+ lines = begin
70
+ sequence.script_lines
71
+ rescue NoMethodError
72
+ nil
73
+ end
74
+ return lines.join if lines
75
+
76
+ path = sequence.absolute_path
77
+ if path && File.file?(path)
78
+ return File.read(path)
79
+ end
80
+
81
+ raise Unsupported,
82
+ "the block's source is not available " \
83
+ "(defined in eval or in a console?); " \
84
+ "pass the kernel as `source:`, or set " \
85
+ "RubyVM.keep_script_lines = true before defining it"
86
+ end
87
+
88
+ def locate (source, location)
89
+ first_line, first_column, = location
90
+ result = Prism.parse(source)
91
+ unless result.success?
92
+ raise Unsupported, "the file holding the block does not parse"
93
+ end
94
+ node = find(result.value, first_line, first_column)
95
+ unless node
96
+ raise Unsupported,
97
+ "could not find the block at line #{first_line}, " \
98
+ "column #{first_column}; " \
99
+ "has the file changed since it was loaded?"
100
+ end
101
+ node
102
+ end
103
+
104
+ def find (node, line, column)
105
+ if block_like?(node) &&
106
+ node.location.start_line == line &&
107
+ node.location.start_column == column
108
+ return node
109
+ end
110
+ node.compact_child_nodes.each do |child|
111
+ found = find(child, line, column)
112
+ return found if found
113
+ end
114
+ nil
115
+ end
116
+
117
+ def block_like? (node)
118
+ node.is_a?(Prism::BlockNode) || node.is_a?(Prism::LambdaNode)
119
+ end
120
+
121
+ def extract (source, location)
122
+ first_line, first_column, last_line, last_column = location
123
+ lines = source.lines
124
+ if first_line == last_line
125
+ lines[first_line - 1][first_column...last_column]
126
+ else
127
+ body = lines[(first_line - 1)...last_line]
128
+ body[0] = body[0][first_column..]
129
+ body[-1] = body[-1][0...last_column]
130
+ body.join
131
+ end
132
+ end
133
+
134
+ end
135
+
136
+ end
137
+
138
+ end
139
+ end