rltk3 3.0.2
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/AUTHORS +1 -0
- data/LICENSE +27 -0
- data/README.md +852 -0
- data/Rakefile +197 -0
- data/lib/rltk/ast.rb +573 -0
- data/lib/rltk/cfg.rb +683 -0
- data/lib/rltk/cg/basic_block.rb +157 -0
- data/lib/rltk/cg/bindings.rb +151 -0
- data/lib/rltk/cg/builder.rb +1127 -0
- data/lib/rltk/cg/context.rb +48 -0
- data/lib/rltk/cg/contractor.rb +51 -0
- data/lib/rltk/cg/execution_engine.rb +194 -0
- data/lib/rltk/cg/function.rb +237 -0
- data/lib/rltk/cg/generated_bindings.rb +8118 -0
- data/lib/rltk/cg/generic_value.rb +95 -0
- data/lib/rltk/cg/instruction.rb +519 -0
- data/lib/rltk/cg/llvm.rb +150 -0
- data/lib/rltk/cg/memory_buffer.rb +75 -0
- data/lib/rltk/cg/module.rb +451 -0
- data/lib/rltk/cg/pass_manager.rb +252 -0
- data/lib/rltk/cg/support.rb +29 -0
- data/lib/rltk/cg/target.rb +230 -0
- data/lib/rltk/cg/triple.rb +58 -0
- data/lib/rltk/cg/type.rb +554 -0
- data/lib/rltk/cg/value.rb +1272 -0
- data/lib/rltk/cg.rb +32 -0
- data/lib/rltk/lexer.rb +372 -0
- data/lib/rltk/lexers/calculator.rb +44 -0
- data/lib/rltk/lexers/ebnf.rb +38 -0
- data/lib/rltk/parser.rb +1702 -0
- data/lib/rltk/parsers/infix_calc.rb +43 -0
- data/lib/rltk/parsers/postfix_calc.rb +34 -0
- data/lib/rltk/parsers/prefix_calc.rb +34 -0
- data/lib/rltk/token.rb +90 -0
- data/lib/rltk/version.rb +11 -0
- data/lib/rltk.rb +16 -0
- data/test/cg/tc_basic_block.rb +83 -0
- data/test/cg/tc_control_flow.rb +191 -0
- data/test/cg/tc_function.rb +54 -0
- data/test/cg/tc_generic_value.rb +33 -0
- data/test/cg/tc_instruction.rb +256 -0
- data/test/cg/tc_llvm.rb +25 -0
- data/test/cg/tc_math.rb +88 -0
- data/test/cg/tc_module.rb +89 -0
- data/test/cg/tc_transforms.rb +68 -0
- data/test/cg/tc_type.rb +69 -0
- data/test/cg/tc_value.rb +151 -0
- data/test/cg/ts_cg.rb +23 -0
- data/test/tc_ast.rb +332 -0
- data/test/tc_cfg.rb +164 -0
- data/test/tc_lexer.rb +216 -0
- data/test/tc_parser.rb +711 -0
- data/test/tc_token.rb +34 -0
- data/test/ts_rltk.rb +47 -0
- metadata +317 -0
data/lib/rltk/cg/llvm.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
# Author: Chris Wailes <chris.wailes@gmail.com>
|
2
|
+
# Project: Ruby Language Toolkit
|
3
|
+
# Date: 2012/03/15
|
4
|
+
# Description: This file defines the LLVM module.
|
5
|
+
|
6
|
+
############
|
7
|
+
# Requires #
|
8
|
+
############
|
9
|
+
|
10
|
+
# Ruby Language Toolkit
|
11
|
+
require 'rltk/version'
|
12
|
+
require 'rltk/cg/bindings'
|
13
|
+
|
14
|
+
#######################
|
15
|
+
# Classes and Modules #
|
16
|
+
#######################
|
17
|
+
|
18
|
+
module RLTK::CG
|
19
|
+
|
20
|
+
# This module contains global operations on the LLVM compiler infrastructure.
|
21
|
+
module LLVM
|
22
|
+
|
23
|
+
# Enable LLVM's built-in stack trace code. This intercepts the OS's
|
24
|
+
# crash signals and prints which component of LLVM you were in at the
|
25
|
+
# time if the crash.
|
26
|
+
#
|
27
|
+
# @return [void]
|
28
|
+
def self.enable_pretty_stack_trace
|
29
|
+
Bindings.enable_pretty_stack_trace
|
30
|
+
end
|
31
|
+
|
32
|
+
# Initialize LLVM to generate code for a given architecture. You may
|
33
|
+
# also specify :all to initialize all targets or :native to
|
34
|
+
# initialize the host target.
|
35
|
+
#
|
36
|
+
# @see Bindings::ARCHS
|
37
|
+
#
|
38
|
+
# @param [Symbol] arch Architecture to initialize LLVM for.
|
39
|
+
#
|
40
|
+
# @raise [ArgumentError] An error is raised if an unsupported architecture is specified.
|
41
|
+
#
|
42
|
+
# @return [void]
|
43
|
+
def self.init(arch)
|
44
|
+
if arch == :all
|
45
|
+
Bindings.initialize_all_targets
|
46
|
+
|
47
|
+
elsif arch == :native
|
48
|
+
Bindings.initialize_native_target
|
49
|
+
|
50
|
+
elsif Bindings::ARCHS.include?(arch) or Bindings::ARCHS.map { |sym| sym.to_s.downcase.to_sym }.include?(arch)
|
51
|
+
arch = Bindings.get_bname(arch)
|
52
|
+
|
53
|
+
Bindings.send("initialize_#{arch}_target".to_sym)
|
54
|
+
Bindings.send("initialize_#{arch}_target_info".to_sym)
|
55
|
+
Bindings.send("initialize_#{arch}_target_mc".to_sym)
|
56
|
+
|
57
|
+
else
|
58
|
+
raise ArgumentError, "Unsupported architecture specified: #{arch}."
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Initialize access to all available target MC that LLVM is
|
63
|
+
# configured to support.
|
64
|
+
#
|
65
|
+
# @return [void]
|
66
|
+
def self.initialize_all_target_mcs
|
67
|
+
Bindings.initialize_all_target_mcs
|
68
|
+
end
|
69
|
+
|
70
|
+
# Initialize a given ASM parser inside LLVM. You may also specify
|
71
|
+
# :all to initialize all ASM parsers.
|
72
|
+
#
|
73
|
+
# @see Bindings::ASM_PARSERS
|
74
|
+
#
|
75
|
+
# @param [Symbol] asm Assembly language type to initialize parser for.
|
76
|
+
#
|
77
|
+
# @raise [ArgumentError] An error is raised if an unsupported assembler parser is specified.
|
78
|
+
#
|
79
|
+
# @return [void]
|
80
|
+
def self.init_asm_parser(asm)
|
81
|
+
if asm == :all
|
82
|
+
Bindings.initialize_all_asm_parsers
|
83
|
+
|
84
|
+
elsif Bindings::ASM_PARSERS.include?(asm) or Bindings::ASM_PARSERS.map { |sym| sym.to_s.downcase.to_sym }.include?(asm)
|
85
|
+
asm = Bindings.get_bname(asm)
|
86
|
+
|
87
|
+
Bindings.send("initialize_#{asm}_asm_parser".to_sym)
|
88
|
+
|
89
|
+
else
|
90
|
+
raise ArgumentError, "Unsupported assembler type specified: #{asm}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Initialize a given ASM printer inside LLVM. You may also specify
|
95
|
+
# :all to initialize all ASM printers or :native to initialize the
|
96
|
+
# printer for the host machine's assembly language.
|
97
|
+
#
|
98
|
+
# @see Bindings::ASM_PRINTERS
|
99
|
+
#
|
100
|
+
# @param [Symbol] asm Assembly language type to initialize printer for.
|
101
|
+
#
|
102
|
+
# @raise [ArgumentError] An error is raised if an unsupported assembler printer is specified.
|
103
|
+
#
|
104
|
+
# @return [void]
|
105
|
+
def self.init_asm_printer(asm)
|
106
|
+
if asm == :all
|
107
|
+
Bindings.initialize_all_asm_printers
|
108
|
+
|
109
|
+
elsif asm == :native
|
110
|
+
Bindings.initialize_native_asm_printer
|
111
|
+
|
112
|
+
elsif Bindings::ASM_PRINTERS.include?(asm) or Bindings::ASM_PRINTERS.map { |sym| sym.to_s.downcase.to_sym }.include?(asm)
|
113
|
+
asm = Bindings.get_bname(asm)
|
114
|
+
|
115
|
+
Bindings.send("initialize_#{asm}_asm_printer".to_sym)
|
116
|
+
|
117
|
+
else
|
118
|
+
raise ArgumentError, "Unsupported assembler type specified: #{asm}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.multithreaded?
|
123
|
+
Bindings.is_multithreaded.to_bool
|
124
|
+
end
|
125
|
+
|
126
|
+
# Deallocate and destroy all ManagedStatic variables.
|
127
|
+
#
|
128
|
+
# @return [void]
|
129
|
+
def self.shutdown
|
130
|
+
Bindings.shutdown
|
131
|
+
end
|
132
|
+
|
133
|
+
# Initialize LLVM's multithreaded infrestructure.
|
134
|
+
#
|
135
|
+
# @return [void]
|
136
|
+
def self.start_multithreaded
|
137
|
+
Bindings.start_multithreaded
|
138
|
+
end
|
139
|
+
|
140
|
+
# Shutdown and cleanup LLVM's multithreaded infrastructure.
|
141
|
+
def self.stop_multithreaded
|
142
|
+
Bindings.stop_multithreaded
|
143
|
+
end
|
144
|
+
|
145
|
+
# @return [String] String representing the version of LLVM targeted by these bindings.
|
146
|
+
def self.version
|
147
|
+
RLTK::LLVM_TARGET_VERSION
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Author: Chris Wailes <chris.wailes@gmail.com>
|
2
|
+
# Project: Ruby Language Toolkit
|
3
|
+
# Date: 2012/04/16
|
4
|
+
# Description: This file defines the MemoryBuffer class.
|
5
|
+
|
6
|
+
############
|
7
|
+
# Requires #
|
8
|
+
############
|
9
|
+
|
10
|
+
# Ruby Language Toolkit
|
11
|
+
require 'rltk/cg/bindings'
|
12
|
+
|
13
|
+
#######################
|
14
|
+
# Classes and Modules #
|
15
|
+
#######################
|
16
|
+
|
17
|
+
module RLTK::CG
|
18
|
+
|
19
|
+
# This class is used by the {Module} class to dump and load LLVM bitcode.
|
20
|
+
class MemoryBuffer
|
21
|
+
include BindingClass
|
22
|
+
|
23
|
+
# The Proc object called by the garbage collector to free resources used by LLVM.
|
24
|
+
CLASS_FINALIZER = Proc.new { |id| Bindings.dispose_memory_buffer(ptr) if ptr = ObjectSpace._id2ref(id).ptr }
|
25
|
+
|
26
|
+
# Create a new memory buffer.
|
27
|
+
#
|
28
|
+
# @param [FFI::Pointer, String, nil] overloaded This parameter may be either a pointer to an existing memory
|
29
|
+
# buffer, the name of a file containing LLVM bitcode or IR, or nil. If it is nil the memory buffer will read
|
30
|
+
# from standard in.
|
31
|
+
def initialize(overloaded = nil)
|
32
|
+
@ptr =
|
33
|
+
case overloaded
|
34
|
+
when FFI::Pointer
|
35
|
+
overloaded
|
36
|
+
else
|
37
|
+
buf_ptr = FFI::MemoryPointer.new(:pointer)
|
38
|
+
msg_ptr = FFI::MemoryPointer.new(:pointer)
|
39
|
+
|
40
|
+
status =
|
41
|
+
case overloaded
|
42
|
+
when String
|
43
|
+
Bindings.create_memory_buffer_with_contents_of_file(overloaded, buf_ptr, msg_ptr)
|
44
|
+
else
|
45
|
+
Bindings.create_memory_buffer_with_stdin(buf_ptr, msg_ptr)
|
46
|
+
end
|
47
|
+
|
48
|
+
if status.zero?
|
49
|
+
buf_ptr.get_pointer(0)
|
50
|
+
else
|
51
|
+
raise msg_ptr.get_pointer(0).get_string(0)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Define a finalizer to free the memory used by LLVM for this
|
56
|
+
# memory buffer.
|
57
|
+
ObjectSpace.define_finalizer(self, CLASS_FINALIZER)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Get the size of the memory buffer.
|
61
|
+
#
|
62
|
+
# @return [Integer] Size of memory buffer
|
63
|
+
def size
|
64
|
+
Bindings.get_buffer_size(@ptr)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Get a copy of the memory buffer, from the beginning, as a sequence
|
68
|
+
# of characters.
|
69
|
+
#
|
70
|
+
# @return [String]
|
71
|
+
def start
|
72
|
+
Bindings.get_buffer_start(@ptr)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,451 @@
|
|
1
|
+
# Author: Chris Wailes <chris.wailes@gmail.com>
|
2
|
+
# Project: Ruby Language Toolkit
|
3
|
+
# Date: 2012/03/20
|
4
|
+
# Description: This file defines the Module class.
|
5
|
+
|
6
|
+
############
|
7
|
+
# Requires #
|
8
|
+
############
|
9
|
+
|
10
|
+
# Ruby Language Toolkit
|
11
|
+
require 'rltk/cg/bindings'
|
12
|
+
require 'rltk/cg/context'
|
13
|
+
|
14
|
+
#######################
|
15
|
+
# Classes and Modules #
|
16
|
+
#######################
|
17
|
+
|
18
|
+
module RLTK::CG
|
19
|
+
|
20
|
+
# This class represents a collection of functions, constants, and global
|
21
|
+
# variables.
|
22
|
+
class Module
|
23
|
+
include BindingClass
|
24
|
+
|
25
|
+
# The Proc object called by the garbage collector to free resources used by LLVM.
|
26
|
+
CLASS_FINALIZER = Proc.new { |id| Bindings.dispose_module(ptr) if ptr = ObjectSpace._id2ref(id).ptr }
|
27
|
+
|
28
|
+
# @!attribute [rw] engine
|
29
|
+
# @return [ExecutionEngine, nil] Execution engine associated with this module.
|
30
|
+
attr_accessor :engine
|
31
|
+
|
32
|
+
# Load a module from LLVM bitcode.
|
33
|
+
#
|
34
|
+
# @param [MemoryBuffer, String] overloaded Where to read the bitecode from
|
35
|
+
# @param [Context, nil] context Context in which to parse bitcode
|
36
|
+
#
|
37
|
+
# @return [Module]
|
38
|
+
def self.read_bitcode(overloaded, context = nil)
|
39
|
+
buffer = overloaded.is_a?(MemoryBuffer) ? overloaded : MemoryBuffer.new(overloaded)
|
40
|
+
|
41
|
+
mod_ptr = FFI::MemoryPointer.new(:pointer)
|
42
|
+
msg_ptr = FFI::MemoryPointer.new(:pointer)
|
43
|
+
|
44
|
+
status =
|
45
|
+
if context
|
46
|
+
Bindings.parse_bitcode_in_context(context, buffer, mod_ptr, msg_ptr)
|
47
|
+
else
|
48
|
+
Bindings.parse_bitcode(buffer, mod_ptr, msg_ptr)
|
49
|
+
end
|
50
|
+
|
51
|
+
if status.zero?
|
52
|
+
Module.new(mod_ptr.get_pointer(0))
|
53
|
+
else
|
54
|
+
raise msg_ptr.get_pointer(0).get_string(0)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Load a Module form an LLVM IR.
|
59
|
+
#
|
60
|
+
# @param [MemoryBuffer, String] overloaded Where to read the IR from
|
61
|
+
# @param [Context] context Context in which to parse IR
|
62
|
+
#
|
63
|
+
# @return [Module]
|
64
|
+
def self.read_ir(overloaded, context = Context.global)
|
65
|
+
buffer = overloaded.is_a?(MemoryBuffer) ? overloaded : MemoryBuffer.new(overloaded)
|
66
|
+
|
67
|
+
mod_ptr = FFI::MemoryPointer.new(:pointer)
|
68
|
+
msg_ptr = FFI::MemoryPointer.new(:pointer)
|
69
|
+
|
70
|
+
status = Bindings.parse_ir_in_context(context, buffer, mod_ptr, msg_ptr)
|
71
|
+
|
72
|
+
if status.zero?
|
73
|
+
Module.new(mod_ptr.get_pointer(0))
|
74
|
+
else
|
75
|
+
raise msg_ptr.get_pointer(0).get_string(0)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Create a new LLVM module.
|
80
|
+
#
|
81
|
+
# @param [FFI::Pointer, String] overloaded Pointer to existing module or name of new module.
|
82
|
+
# @param [Context, nil] context Optional context in which to create the module.
|
83
|
+
# @param [Proc] block Block to be executed inside the context of the module.
|
84
|
+
def initialize(overloaded, context = nil, &block)
|
85
|
+
@ptr =
|
86
|
+
case overloaded
|
87
|
+
when FFI::Pointer
|
88
|
+
overloaded
|
89
|
+
|
90
|
+
when String
|
91
|
+
if context
|
92
|
+
Bindings.module_create_with_name_in_context(overloaded, check_type(context, Context, 'context'))
|
93
|
+
else
|
94
|
+
Bindings.module_create_with_name(overloaded)
|
95
|
+
end
|
96
|
+
|
97
|
+
else
|
98
|
+
raise 'Argument `overloaded` must be a FFI::Pointer of String.'
|
99
|
+
end
|
100
|
+
|
101
|
+
# Define a finalizer to free the memory used by LLVM for this
|
102
|
+
# module.
|
103
|
+
ObjectSpace.define_finalizer(self, CLASS_FINALIZER)
|
104
|
+
|
105
|
+
self.instance_exec(&block) if block
|
106
|
+
end
|
107
|
+
|
108
|
+
# Compile this module to an assembly or object file.
|
109
|
+
#
|
110
|
+
# @param [String] file_name File to emit code to
|
111
|
+
# @param [:assembly, :object] emit_type Type of code to emit
|
112
|
+
# @param [TargetMachine] machine TargetMachine used to generate code
|
113
|
+
#
|
114
|
+
# @return [void]
|
115
|
+
#
|
116
|
+
# @raise LLVM error message if unable to emit code for module
|
117
|
+
def compile(file_name, emit_type = :object, machine = TargetMachine.host)
|
118
|
+
machine.emite_module(self, file_name, emit_type)
|
119
|
+
end
|
120
|
+
|
121
|
+
# @return [Context] Context in which this module exists.
|
122
|
+
def context
|
123
|
+
Context.new(Bindings.get_module_context(@ptr))
|
124
|
+
end
|
125
|
+
|
126
|
+
# Print the LLVM IR representation of this value to standard error.
|
127
|
+
# This function is the debugging version of the more general purpose
|
128
|
+
# {#print} method.
|
129
|
+
#
|
130
|
+
# @see #print
|
131
|
+
#
|
132
|
+
# @return [void]
|
133
|
+
def dump
|
134
|
+
Bindings.dump_module(@ptr)
|
135
|
+
end
|
136
|
+
|
137
|
+
# @return [FunctionPassManager] Function pass manager for this module.
|
138
|
+
def function_pass_manager
|
139
|
+
@function_pass_manager ||= FunctionPassManager.new(self)
|
140
|
+
end
|
141
|
+
alias :fpm :function_pass_manager
|
142
|
+
|
143
|
+
# Link another module into this one, taking ownership of it. You may
|
144
|
+
# not access the other module again once linking it.
|
145
|
+
#
|
146
|
+
# @param [Module] other Module to be linked
|
147
|
+
#
|
148
|
+
# @raise Errors encountered during linking
|
149
|
+
def link(other)
|
150
|
+
error = FFI::MemoryPointer.new(:pointer)
|
151
|
+
status = Bindings.link_modules(@ptr, other, :linker_destroy_source, error)
|
152
|
+
|
153
|
+
if not status.zero?
|
154
|
+
errorp = error.read_pointer
|
155
|
+
message = errorp.null? ? 'Unknown' : errorp.read_string
|
156
|
+
|
157
|
+
error.autorelease = false
|
158
|
+
|
159
|
+
Bindings.dispose_message(error)
|
160
|
+
|
161
|
+
raise "Error linking modules: #{message}"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# @return [PassManager] Pass manager for this module.
|
166
|
+
def pass_manager
|
167
|
+
@pass_manager ||= PassManager.new(self)
|
168
|
+
end
|
169
|
+
alias :pm :pass_manager
|
170
|
+
|
171
|
+
# Print the LLVM IR representation of this module to a file.
|
172
|
+
#
|
173
|
+
# @param [String] file_name Name of file to print to
|
174
|
+
#
|
175
|
+
# @return [void]
|
176
|
+
def print(file_name)
|
177
|
+
error = FFI::MemoryPointer.new(:pointer)
|
178
|
+
status = Bindings.print_module_to_file(@ptr, file_name, error)
|
179
|
+
|
180
|
+
if not status.zero?
|
181
|
+
errorp = error.read_pointer
|
182
|
+
message = errorp.null? ? 'Unknown' : errorp.read_string
|
183
|
+
|
184
|
+
error.autorelease = false
|
185
|
+
|
186
|
+
Bindings.dispose_message(error)
|
187
|
+
|
188
|
+
raise "Error printing module: #{message}"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# @return [FunctionCollection] Proxy object for inspecting this module's functions.
|
193
|
+
def functions
|
194
|
+
@functions ||= FunctionCollection.new(self)
|
195
|
+
end
|
196
|
+
alias :funs :functions
|
197
|
+
|
198
|
+
# @return [GlobalCollection] Proxy object for inspecting this module's global values and variables.
|
199
|
+
def globals
|
200
|
+
@globals ||= GlobalCollection.new(self)
|
201
|
+
end
|
202
|
+
|
203
|
+
# Set the module's target triple.
|
204
|
+
#
|
205
|
+
# @param [String] triple Triple value to set.
|
206
|
+
#
|
207
|
+
# @return [void]
|
208
|
+
def target=(triple)
|
209
|
+
Bindings.set_target(@ptr, triple)
|
210
|
+
end
|
211
|
+
|
212
|
+
# Get the module's target triple.
|
213
|
+
#
|
214
|
+
# @return [String]
|
215
|
+
def target
|
216
|
+
Bindings.get_target(@ptr)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Return a LLVM IR representation of this file as a string.
|
220
|
+
#
|
221
|
+
# @return [String]
|
222
|
+
def to_s
|
223
|
+
Bindings.print_module_to_string(@ptr)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Write the module as LLVM bitcode to a file.
|
227
|
+
#
|
228
|
+
# @param [#path, #fileno, Integer, String] overloaded Where to write the bitcode.
|
229
|
+
#
|
230
|
+
# @return [Boolean] If the write was successful.
|
231
|
+
def write_bitcode(overloaded)
|
232
|
+
0 ==
|
233
|
+
if overloaded.respond_to?(:path)
|
234
|
+
Bindings.write_bitcode_to_file(@ptr, overloaded.path)
|
235
|
+
|
236
|
+
elsif overloaded.respond_to?(:fileno)
|
237
|
+
Bindings.write_bitcode_to_fd(@ptr, overloaded.fileno, 0, 1)
|
238
|
+
|
239
|
+
elsif overloaded.is_a?(Integer)
|
240
|
+
Bindings.write_bitcode_to_fd(@ptr, overloaded, 0, 1)
|
241
|
+
|
242
|
+
elsif overloaded.is_a?(String)
|
243
|
+
Bindings.write_bitcode_to_file(@ptr, overloaded)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Verify that the module is valid LLVM IR.
|
248
|
+
#
|
249
|
+
# @return [nil, String] Human-readable description of any invalid constructs if invalid.
|
250
|
+
def verify
|
251
|
+
do_verification(:return_status)
|
252
|
+
end
|
253
|
+
|
254
|
+
# Verify that a module is valid LLVM IR and abort the process if it isn't.
|
255
|
+
#
|
256
|
+
# @return [nil]
|
257
|
+
def verify!
|
258
|
+
do_verification(:abort_process)
|
259
|
+
end
|
260
|
+
|
261
|
+
# Helper function for {#verify} and {#verify!}
|
262
|
+
def do_verification(action)
|
263
|
+
str_ptr = FFI::MemoryPointer.new(:pointer)
|
264
|
+
status = Bindings.verify_module(@ptr, action, str_ptr)
|
265
|
+
|
266
|
+
status == 1 ? str_ptr.read_string : nil
|
267
|
+
end
|
268
|
+
private :do_verification
|
269
|
+
|
270
|
+
# This class is used to access a module's {Function Functions}.
|
271
|
+
class FunctionCollection
|
272
|
+
include Enumerable
|
273
|
+
|
274
|
+
# @param [Module] mod Module for which this is a proxy.
|
275
|
+
def initialize(mod)
|
276
|
+
@module = mod
|
277
|
+
end
|
278
|
+
|
279
|
+
# Retreive a Function object.
|
280
|
+
#
|
281
|
+
# @param [String, Symbol, Integer] key Function identifier. Either the name of the function or its index.
|
282
|
+
#
|
283
|
+
# @return [Function]
|
284
|
+
def [](key)
|
285
|
+
case key
|
286
|
+
when String, Symbol
|
287
|
+
self.named(key)
|
288
|
+
|
289
|
+
when Integer
|
290
|
+
(1...key).inject(self.first) { |fun| if fun then self.next(fun) else break end }
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# Add a Function to this module.
|
295
|
+
#
|
296
|
+
# @param [String] name Name of the module in LLVM IR.
|
297
|
+
# @param [FunctionType, Array(Type, Array<Type>)] type_info FunctionType or Values that will be passed to {FunctionType#initialize}.
|
298
|
+
# @param [Proc] block Block to be executed inside the context of the function.
|
299
|
+
#
|
300
|
+
# @return [Function]
|
301
|
+
def add(name, *type_info, &block)
|
302
|
+
Function.new(@module, name, *type_info, &block)
|
303
|
+
end
|
304
|
+
|
305
|
+
# Remove a function from the module.
|
306
|
+
#
|
307
|
+
# @param [Function] fun Function to remove.
|
308
|
+
#
|
309
|
+
# @return [void]
|
310
|
+
def delete(fun)
|
311
|
+
Bindings.delete_function(fun)
|
312
|
+
end
|
313
|
+
|
314
|
+
# An iterator for each function inside this collection.
|
315
|
+
#
|
316
|
+
# @yieldparam fun [Function]
|
317
|
+
#
|
318
|
+
# @return [Enumerator] Returns an Enumerator if no block is given.
|
319
|
+
def each
|
320
|
+
return to_enum(:each) unless block_given?
|
321
|
+
|
322
|
+
fun = self.first
|
323
|
+
|
324
|
+
while fun
|
325
|
+
yield fun
|
326
|
+
fun = self.next(fun)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
# @return [Function, nil] The module's first function if one has been added.
|
331
|
+
def first
|
332
|
+
if (ptr = Bindings.get_first_function(@module)).null? then nil else Function.new(ptr) end
|
333
|
+
end
|
334
|
+
|
335
|
+
# @return [Function, nil] The module's last function if one has been added.
|
336
|
+
def last
|
337
|
+
if (ptr = Bindings.get_last_function(@module)).null? then nil else Function.new(ptr) end
|
338
|
+
end
|
339
|
+
|
340
|
+
# @param [String, Symbol] name Name of the desired function.
|
341
|
+
#
|
342
|
+
# @return [Function, nil] The function with the given name.
|
343
|
+
def named(name)
|
344
|
+
if (ptr = Bindings.get_named_function(@module, name)).null? then nil else Function.new(ptr) end
|
345
|
+
end
|
346
|
+
|
347
|
+
# @param [Function] fun Function you want the successor for.
|
348
|
+
#
|
349
|
+
# @return [Function, nil] Next function in the collection.
|
350
|
+
def next(fun)
|
351
|
+
if (ptr = Bindings.get_next_function(fun)).null? then nil else Function.new(ptr) end
|
352
|
+
end
|
353
|
+
|
354
|
+
# @param [Function] fun Function you want the predecessor for.
|
355
|
+
#
|
356
|
+
# @return [Function, nil] Previous function in the collection.
|
357
|
+
def previous(fun)
|
358
|
+
if (ptr = Bindings.get_previous_function(fun)).null? then nil else Function.new(ptr) end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
# This class is used to access a module's global variables.
|
363
|
+
class GlobalCollection
|
364
|
+
include Enumerable
|
365
|
+
|
366
|
+
# @param [Module] mod Module for which this is a proxy.
|
367
|
+
def initialize(mod)
|
368
|
+
@module = mod
|
369
|
+
end
|
370
|
+
|
371
|
+
# Retreive a GlobalVariable object.
|
372
|
+
#
|
373
|
+
# @param [String, Symbol, Integer] key Global variable identifier. Either the name of the variable or its index.
|
374
|
+
#
|
375
|
+
# @return [GlobalVariable]
|
376
|
+
def [](key)
|
377
|
+
case key
|
378
|
+
when String, Symbol
|
379
|
+
self.named(key)
|
380
|
+
|
381
|
+
when Integer
|
382
|
+
(1...key).inject(self.first) { |global| if global then self.next(global) else break end }
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
# Add a global variable to a module.
|
387
|
+
#
|
388
|
+
# @param [Type] type Type of the global variable.
|
389
|
+
# @param [String] name Name of the global variable in LLVM IR.
|
390
|
+
def add(type, name)
|
391
|
+
GlobalVariable.new(Bindings.add_global(@module, type, name))
|
392
|
+
end
|
393
|
+
|
394
|
+
# Remove a global variable from the module.
|
395
|
+
#
|
396
|
+
# @param [GlobalVariable] global Global variable to remove.
|
397
|
+
#
|
398
|
+
# @return [void]
|
399
|
+
def delete(global)
|
400
|
+
Bindings.delete_global(global)
|
401
|
+
end
|
402
|
+
|
403
|
+
# An iterator for each global variable inside this collection.
|
404
|
+
#
|
405
|
+
# @yieldparam fun [GlobalVariable]
|
406
|
+
#
|
407
|
+
# @return [Enumerator] Returns an Enumerator if no block is given.
|
408
|
+
def each
|
409
|
+
return to_enum(:each) unless block_given?
|
410
|
+
|
411
|
+
global = self.first
|
412
|
+
|
413
|
+
while global
|
414
|
+
yield global
|
415
|
+
global = self.next(global)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
# @return [GlobalVariable, nil] The module's first global variable if one has been added.
|
420
|
+
def first
|
421
|
+
if (ptr = Bindings.get_first_global(@module)).null? then nil else GlobalValue.new(ptr) end
|
422
|
+
end
|
423
|
+
|
424
|
+
# @return [GlobalVariable, nil] The module's last global variable if one has been added.
|
425
|
+
def last
|
426
|
+
if (ptr = Bindings.get_last_global(@module)).null? then nil else GlobalValue.new(ptr) end
|
427
|
+
end
|
428
|
+
|
429
|
+
# @param [String, Symbol] name Name of the desired global variable.
|
430
|
+
#
|
431
|
+
# @return [GlobalVariable, nil] The global variable with the given name.
|
432
|
+
def named(name)
|
433
|
+
if (ptr = Bindings.get_named_global(@module, name)).null? then nil else GlobalValue.new(ptr) end
|
434
|
+
end
|
435
|
+
|
436
|
+
# @param [GlobalVariable] global Global variable you want the successor for.
|
437
|
+
#
|
438
|
+
# @return [GlobalVariable, nil] global Next global variable in the collection.
|
439
|
+
def next(global)
|
440
|
+
if (ptr = Bindings.get_next_global(global)).null? then nil else GlobalValue.new(ptr) end
|
441
|
+
end
|
442
|
+
|
443
|
+
# @param [GlobalVariable] global Global variable you want the predecessor for.
|
444
|
+
#
|
445
|
+
# @return [GlobalVariable, nil] Previous global variable in the collection.
|
446
|
+
def previous(global)
|
447
|
+
if (ptr = Bindings.get_previous_global(global)).null? then nil else GlobalValue.new(ptr) end
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|