-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcompiler.rb
140 lines (113 loc) · 2.79 KB
/
compiler.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
require "parser"
require "nodes"
require "llvm/core"
require "llvm/execution_engine"
require "llvm/transforms/scalar"
require "llvm/transforms/ipo"
LLVM.init_jit
class Compiler
attr_reader :locals
PCHAR = LLVM::Type.pointer(LLVM::Int8)
INT = LLVM::Int
def initialize mod=nil, function=nil
@module = mod || LLVM::Module.new("bio")
@locals = {}
@function = function || @module.functions.named("main") || @module.functions.add("main", [], LLVM.Void)
@builder = LLVM::Builder.new
@builder.position_at_end(@function.basic_blocks.append)
@engine = LLVM::JITCompiler.new @module
end
def preamble
define_external_functions
end
def finish
@builder.ret_void
end
def new_string value
@builder.global_string_pointer(value)
end
def new_number value
LLVM::Int value
end
def call func, args=[]
f = @module.functions.named func
@builder.call f, *args
end
def assign name, value
ptr = @builder.alloca value.type
@builder.store value, ptr
@locals[name] = ptr
end
def load name
@builder.load @locals[name]
end
def function name
func = @module.functions.add name, [], LLVM.Void
generator = Compiler.new @module, func
yield generator
generator.finish
end
def optimize
@module.verify!
pass_manager = LLVM::PassManager.new @engine
pass_manager.simplifycfg!
pass_manager.mem2reg!
pass_manager.gdce!
end
def run
@engine.run_function @function
end
def dump
@module.dump
end
private
def define_external_functions
fun = @module.functions.add("printf", [PCHAR], INT, { :varargs => true })
fun.linkage = :external
fun = @module.functions.add("puts", [PCHAR], INT)
fun.linkage = :external
fun = @module.functions.add("read", [INT, PCHAR, INT], INT)
fun.linkage = :external
fun = @module.functions.add("exit", [INT], INT)
fun.linkage = :external
end
end
class Nodes
def compile compiler
nodes.map { |node| node.compile(compiler) }.last
end
end
class NumberNode
def compile compiler
compiler.new_number value
end
end
class StringNode
def compile compiler
compiler.new_string value
end
end
class CallNode
def compile compiler
raise "Receiver not supported for compilation" if receiver
if receiver.nil? and arguments.empty? and compiler.locals[method]
compiler.load(method)
else
compiled_arguments = arguments.map { |arg| arg.compile(compiler) }
compiler.call(method, compiled_arguments)
end
end
end
class SetLocalNode
def compile compiler
compiler.assign name, value.compile(compiler)
end
end
class DefNode
def compile compiler
raise "Parameters not supported for compilation" unless params.empty?
compiler.function(name) do |function|
body.compile(function)
end
end
end