Class: Citrus::Generator

Inherits:
Object
  • Object
show all
Defined in:
lib/citrus/compiler/generator.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(mod, function, parent = nil) ⇒ Generator

Returns a new instance of Generator.



10
11
12
13
14
15
16
17
18
# File 'lib/citrus/compiler/generator.rb', line 10

def initialize(mod, function, parent=nil)
  @module = mod
  @locals = {}
  @parent = parent
  @function = function
  @basic_block = @function.basic_blocks.append
  @builder = LLVM::Builder.create
  @builder.position_at_end(@basic_block)
end

Instance Attribute Details

#basic_blockObject (readonly)

Returns the value of attribute basic_block.



4
5
6
# File 'lib/citrus/compiler/generator.rb', line 4

def basic_block
  @basic_block
end

#builderObject (readonly)

Returns the value of attribute builder.



6
7
8
# File 'lib/citrus/compiler/generator.rb', line 6

def builder
  @builder
end

#localsObject (readonly)

Returns the value of attribute locals.



7
8
9
# File 'lib/citrus/compiler/generator.rb', line 7

def locals
  @locals
end

#moduleObject (readonly)

Returns the value of attribute module.



5
6
7
# File 'lib/citrus/compiler/generator.rb', line 5

def module
  @module
end

#parentObject (readonly)

Returns the value of attribute parent.



8
9
10
# File 'lib/citrus/compiler/generator.rb', line 8

def parent
  @parent
end

Instance Method Details

#array(values) ⇒ Object



20
21
22
# File 'lib/citrus/compiler/generator.rb', line 20

def array(values)
  return Array.create(values, @builder)
end

#assign(name, value) ⇒ Object



107
108
109
110
111
112
113
# File 'lib/citrus/compiler/generator.rb', line 107

def assign(name, value)
  unless @locals.has_key?(name)
    @locals[name] = Variable.new(value, @builder) 
  else
    @locals[name].assign(value, @builder)
  end
end

#assign_global(name, value) ⇒ Object



115
116
117
118
119
120
121
# File 'lib/citrus/compiler/generator.rb', line 115

def assign_global(name, value)
  unless GlobalVariables.named(name)
    GlobalVariables.add(name, value, @builder)
  else
    GlobalVariables.named(name).assign(value, @builder)
  end
end

#assign_index(name, index, value) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/citrus/compiler/generator.rb', line 123

def assign_index(name, index, value)
  ary = self.load(name)
  length = @builder.alloca(INT)
  @builder.store(ary.length, length)
  tb = self.block do |gb|
    gb.builder.store(gb.equate(:+, index, gb.number(1)), length)
  end
  cond = gb.compare(:<=, ary.length, index)
  self.condition(cond, tb.bb, self.block.bb)
  ary.length = @builder.load(length)
  ptr = @builder.gep(ary.pointer, [INT.from_i(0), index])
  @builder.store(value, ptr)
end

#begin(rblock, elblock, enblock) ⇒ Object



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/citrus/compiler/generator.rb', line 249

def begin(rblock, elblock, enblock)
  cond = self.compare(:==, self.load_global(STS_GLOBAL), self.number(1))
  @builder.cond(cond, rblock.bb, elblock.bb)
  @basic_block = self.block.bb
  @builder.position_before(rblock.bb.instructions.first)
  self.assign_global(STS_GLOBAL, self.number(0))
  @builder.position_at_end(rblock.bb)
  @builder.br(enblock.bb)
  @builder.position_at_end(elblock.bb)
  @builder.br(enblock.bb)
  @builder.position_before(enblock.bb.instructions.first)
  self.resolve_conflicts(rblock, elblock)
  @builder.position_at_end(enblock.bb)
  @builder.br(@basic_block)
  @builder.position_at_end(@basic_block)
  @locals.merge!(enblock.locals)
end

#blockObject



206
207
208
# File 'lib/citrus/compiler/generator.rb', line 206

def block
  return Block.new(@module, @function, self) { |g| yield g if block_given? }
end

#bool(value) ⇒ Object



56
57
58
# File 'lib/citrus/compiler/generator.rb', line 56

def bool(value)
  BOOL.from_i(value ? 1 : 0)
end

#break(block) ⇒ Object



318
319
320
321
322
# File 'lib/citrus/compiler/generator.rb', line 318

def break(block)
  unless @finished
    @builder.br(block)
  end
end

#call(func, *args) ⇒ Object



68
69
70
71
72
73
74
75
76
# File 'lib/citrus/compiler/generator.rb', line 68

def call(func, *args)
  begin
    GlobalFunctions.named(func).call(args, @builder)
  rescue NoMethodError # for C methods
    f = @module.functions.named(func)
    raise NoMethodError if f.nil?
    @builder.call(f, *args)
  end
end

#case(val, cases, elseblock) ⇒ Object



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/citrus/compiler/generator.rb', line 232

def case(val, cases, elseblock)
  ncases = {}
  for pair in cases
    ncases[pair[0]] = pair[1].bb
  end
  switch = @builder.switch(val, elseblock.bb, ncases)
  @basic_block = self.block.bb
  @builder.position_at_end(elseblock.bb)
  @builder.br(@basic_block)
  ncases.each_value do |c|
    @builder.position_at_end(c[1])
    @builder.br(@basic_block)
  end
  @builder.position_at_end(@basic_block)
  self.resolve_conflicts(elseblock, *cases.keys)
end

#compare(op, lval, rval) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/citrus/compiler/generator.rb', line 137

def compare(op, lval, rval)
  case op.to_s
  when *CMP_MAPPING.keys
    if LLVM::Type(lval) == FLOAT.type || LLVM::Type(rval) == FLOAT.type
      symbol = "o#{CMP_MAPPING[op.to_s].to_s}".to_sym
      if LLVM::Type(lval) != FLOAT.type
        lval = @builder.ui2fp(lval, FLOAT.type)
      elsif LLVM::Type(rval) != FLOAT.type
        rval = @builder.ui2fp(rval, FLOAT.type)
      end
      @builder.fcmp(symbol, lval, rval)
    else
      symbol = CMP_MAPPING[op.to_s]
      symbol = "s#{symbol.to_s}".to_sym unless symbol == :eq || symbol == :ne
      @builder.icmp(symbol, lval, rval)
    end
  when "&&", "and"
    @builder.and(lval, rval)
  when "||", "or"
    @builder.or(lval, rval)
  end
end

#condition(cond, thenblock, elseblock, elsifs = []) ⇒ Object



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/citrus/compiler/generator.rb', line 210

def condition(cond, thenblock, elseblock, elsifs=[])
  @basic_block = self.block.bb
  eb = elsifs.empty? ? elseblock : self.block
  efbs = []
  @builder.cond(cond, thenblock.bb, eb.bb)
  for i in 0...elsifs.length
    efbs += eb
    @builder.position_at_end(eb.bb)
    neb = i+1 == elsifs.length ? elseblock : self.block
    @builder.cond(elsifs[i][0], elsifs[i][1].bb, eb.bb)
    @builder.position_at_end(elsifs[i][1].bb)
    @builder.br(@basic_block)    
    eb = neb
  end
  @builder.position_at_end(thenblock.bb)
  @builder.br(@basic_block)
  @builder.position_at_end(elseblock.bb)
  @builder.br(@basic_block)
  @builder.position_at_end(@basic_block)
  self.resolve_conflicts(thenblock, elseblock, *efbs)
end

#declare(name, args, ret, varargs = false) ⇒ Object



200
201
202
203
204
# File 'lib/citrus/compiler/generator.rb', line 200

def declare(name, args, ret, varargs = false)
  rtype = DEC_MAPPING[ret.to_sym]
  atypes = args.map{|arg| DEC_MAPPING[arg.to_sym]}
  @module.functions.add(name.to_s, LLVM::Type.function(atypes, rtype, :varargs => varargs))
end

#equate(op, lval, rval) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/citrus/compiler/generator.rb', line 160

def equate(op, lval, rval)
  if LLVM::Type(lval) == FLOAT.type || LLVM::Type(rval) == FLOAT.type
    symbol = "f#{EQU_MAPPING[op.to_s].to_s}".to_sym
    if LLVM::Type(lval) != FLOAT.type
      lval = @builder.ui2fp(lval, FLOAT.type)
    elsif LLVM::Type(rval) != FLOAT.type
      rval = @builder.ui2fp(rval, FLOAT.type)
    end
    @builder.send(symbol, lval, rval)
  else
    symbol = EQU_MAPPING[op.to_s]
    symbol = :sdiv if symbol == :div
    @builder.send(symbol, lval, rval)
  end
end

#finishObject



324
325
326
# File 'lib/citrus/compiler/generator.rb', line 324

def finish
  @builder.dispose
end

#float(value) ⇒ Object



48
49
50
# File 'lib/citrus/compiler/generator.rb', line 48

def float(value)
  FLOAT.from_f(value)
end

#for(var, indices) {|generator| ... } ⇒ Object

Yields:

  • (generator)


296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/citrus/compiler/generator.rb', line 296

def for(var, indices)
  generator = Generator.new(@module, @function, self)
  generator.assign(var, generator.load_index(indices, generator.load("for")))
  yield generator
  generator.assign("for", generator.equate(:+, generator.load("for"), generator.number(1)))
  generator.break(@basic_block)
  generator.finish
  @builder.position_at_end(@basic_block)
  self.resolve_conflict("for", generator, self)
  @basic_block = self.block.bb
  cond = self.compare(:<, self.load("for"), indices.length)
  @builder.cond(cond, generator.basic_block, @basic_block)
  @builder.position_at_end(@basic_block)
end

#function(name, args) ⇒ Object



196
197
198
# File 'lib/citrus/compiler/generator.rb', line 196

def function(name, args)
  return GlobalFunctions.add(name, args) { |g| yield g }
end

#load(name) ⇒ Object



180
181
182
183
184
185
186
# File 'lib/citrus/compiler/generator.rb', line 180

def load(name)
  if @locals.has_key?(name)
    @locals[name].value(@builder)
  else
    self.vars[name].value(@builder)
  end
end

#load_global(name) ⇒ Object



188
189
190
# File 'lib/citrus/compiler/generator.rb', line 188

def load_global(name)
  GlobalVariables.named(name).value(@builder)
end

#load_index(ary, index) ⇒ Object



192
193
194
# File 'lib/citrus/compiler/generator.rb', line 192

def load_index(ary, index)
  @builder.load(@builder.gep(ary.pointer, [INT.from_i(0), index]))
end

#negate(value) ⇒ Object



60
61
62
# File 'lib/citrus/compiler/generator.rb', line 60

def negate(value)
  @builder.neg(value)
end

#not(value) ⇒ Object



64
65
66
# File 'lib/citrus/compiler/generator.rb', line 64

def not(value)
  @builder.not(value)
end

#number(value) ⇒ Object



52
53
54
# File 'lib/citrus/compiler/generator.rb', line 52

def number(value)
  INT.from_i(value)
end

#preploop(looptype = nil, *args) ⇒ Object

Needs to be called before running any loop command (specifically before calculating the conditions for the loop)



273
274
275
276
277
278
279
280
281
# File 'lib/citrus/compiler/generator.rb', line 273

def preploop(looptype=nil, *args)
  if looptype == :for
    self.assign("for", INT.from_i(0))
    self.assign(args[0], self.load_index(args[1], self.number(0)))
  end
  @basic_block = self.block.bb
  @builder.br(@basic_block)
  @builder.position_at_end(@basic_block)
end

#range(first, last, full) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/citrus/compiler/generator.rb', line 24

def range(first, last, full)
  ival = nil
  ary = @builder.alloca(LLVM::Array(INT, 0))
  iteration = @builder.alloca(INT)
  @builder.store(first, iteration)
  index = @builder.alloca(INT)
  @builder.store(INT.from_i(0), index)
  self.preploop(:while)
  self.while(self.compare(full ? :<= : :<, @builder.load(iteration), last)) do |gw|
    ival = gw.builder.load(index)
    val = gw.builder.load(iteration)
    ptr = gw.builder.gep(ary, [INT.from_i(0), ival])
    gw.builder.store(val, ptr)
    gw.builder.store(gw.equate(:+, val, gw.number(1)), iteration)
    gw.builder.store(gw.equate(:+, ival, gw.number(1)), index)
  end
  ival = @builder.load(index)
  return Array.new(ary, ival) 
end

#resolve_conflict(name, *gens) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/citrus/compiler/generator.rb', line 87

def resolve_conflict(name, *gens)
  ty = INT
  nodes = {}
  gens.select{|g| g.locals.has_key?(name)}.each do |g|
    bb = g.basic_block
    var = g.locals[name]
    builder = LLVM::Builder.create
    if bb == @builder.insert_block && !nodes.has_key?(bb.previous)
      builder.position_before(bb.previous.instructions.last)
      nodes[bb.previous] = var.value(builder)
    else
      builder.position_before(bb.instructions.last)
      nodes[bb] = var.value(builder)
    end
    builder.dispose
  end
  ptr = @builder.phi(ty, nodes)
  self.assign(name, ptr)
end

#resolve_conflicts(*gens) ⇒ Object



78
79
80
81
82
83
84
85
# File 'lib/citrus/compiler/generator.rb', line 78

def resolve_conflicts(*gens)
  locals = []
  gens.each{|g| locals += g.locals.keys}
  stat = locals.inject(Hash.new(0)){|h, e| h[e]+=1; h}
  stat.select{|k, v| v > 1}.collect{|a| a[0]}.each do |k|
    resolve_conflict(k, *gens)
  end
end

#return(value = self.number(0)) ⇒ Object



311
312
313
314
315
316
# File 'lib/citrus/compiler/generator.rb', line 311

def return(value=self.number(0))
  unless @finished
    @function.return(value, @builder)
    @finished = true
  end
end

#string(value) ⇒ Object



44
45
46
# File 'lib/citrus/compiler/generator.rb', line 44

def string(value)
  ptr = GlobalStrings.pointer(value)
end

#unwindObject



267
268
269
# File 'lib/citrus/compiler/generator.rb', line 267

def unwind
  @builder.unwind
end

#varsObject



176
177
178
# File 'lib/citrus/compiler/generator.rb', line 176

def vars
  return @parent.nil? ? @locals : @locals.merge(@parent.vars) 
end

#while(cond) {|generator| ... } ⇒ Object

Yields:

  • (generator)


283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/citrus/compiler/generator.rb', line 283

def while(cond)
  @builder.position_before(@basic_block.instructions.first)
  generator = Generator.new(@module, @function, self)
  yield generator
  generator.break(@basic_block)
  generator.finish
  self.resolve_conflicts(generator, self)
  @builder.position_at_end(@basic_block)
  @basic_block = self.block.bb
  @builder.cond(cond, generator.basic_block, @basic_block)
  @builder.position_at_end(@basic_block)
end