LlvmGenerator.py 47.8 KB
Newer Older
Maxime Perrotin's avatar
Maxime Perrotin committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    OpenGEODE - A tiny SDL Editor for TASTE

    This module generates LLVM IR code from SDL process models, allowing
    generation of a binary application without an intermediate language.
    LLVM also allows for various code verification, analysis, and optimization.

    The design is based on the Ada code generator. Check it for details.

    Copyright (c) 2012-2013 European Space Agency

    Designed and implemented by Maxime Perrotin

    Contact: maxime.perrotin@esa.int
"""

import logging
21

Maxime Perrotin's avatar
Maxime Perrotin committed
22
from singledispatch import singledispatch
dbarbera's avatar
dbarbera committed
23
from llvm import core, ee
Maxime Perrotin's avatar
Maxime Perrotin committed
24
25

import ogAST
Maxime Perrotin's avatar
Maxime Perrotin committed
26
import Helper
Maxime Perrotin's avatar
Maxime Perrotin committed
27
28
29

LOG = logging.getLogger(__name__)

Maxime Perrotin's avatar
Maxime Perrotin committed
30
31
__all__ = ['generate']

Maxime Perrotin's avatar
Maxime Perrotin committed
32

dbarbera's avatar
dbarbera committed
33
# Global context
dbarbera's avatar
dbarbera committed
34
ctx = None
35
36


dbarbera's avatar
dbarbera committed
37
class Context():
38
    def __init__(self, process):
dbarbera's avatar
dbarbera committed
39
40
        self.name = str(process.processName)
        self.module = core.Module.new(self.name)
dbarbera's avatar
dbarbera committed
41
        self.target_data = ee.TargetData.new(self.module.data_layout)
42
43
        self.dataview = process.dataview

44
45
        self.scope = Scope()
        self.global_scope = self.scope
46
        self.states = {}
dbarbera's avatar
dbarbera committed
47
        self.structs = {}
dbarbera's avatar
dbarbera committed
48
        self.unions = {}
49
        self.strings = {}
dbarbera's avatar
dbarbera committed
50
        self.funcs = {}
51
52
53
54
55
56
57
58
59
60
61
62
63
64

        # Initialize built-in types
        self.i1 = core.Type.int(1)
        self.i8 = core.Type.int(8)
        self.i32 = core.Type.int(32)
        self.i64 = core.Type.int(64)
        self.void = core.Type.void()
        self.double = core.Type.double()
        self.i1_ptr = core.Type.pointer(self.i1)
        self.i8_ptr = core.Type.pointer(self.i8)
        self.i32_ptr = core.Type.pointer(self.i32)
        self.i64_ptr = core.Type.pointer(self.i64)
        self.double_ptr = core.Type.pointer(self.double)

dbarbera's avatar
dbarbera committed
65
66
67
68
        # Initialize common constants
        self.zero = core.Constant.int(self.i32, 0)
        self.one = core.Constant.int(self.i32, 1)

69
70
        # Intialize built-in functions
        ty = core.Type.function(self.void, [core.Type.pointer(self.i8)], True)
dbarbera's avatar
dbarbera committed
71
        self.funcs['printf'] = self.module.add_function(ty, 'printf')
72

dbarbera's avatar
dbarbera committed
73
        self.funcs['memcpy'] = core.Function.intrinsic(
74
75
            self.module,
            core.INTR_MEMCPY,
76
77
            [self.i8_ptr, self.i8_ptr, self.i64]
        )
Maxime Perrotin's avatar
Maxime Perrotin committed
78

79
80
81
82
83
84
        self.funcs['powi'] = core.Function.intrinsic(
            self.module,
            core.INTR_POWI,
            [self.double]
        )

dbarbera's avatar
dbarbera committed
85
86
87
88
89
90
        self.funcs['fabs'] = core.Function.intrinsic(
            self.module,
            core.INTR_FABS,
            [self.double]
        )

dbarbera's avatar
dbarbera committed
91

92
93
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
94
        self.name = name
95
96
97
98
99
        self.field_names = field_names
        self.ty = core.Type.struct(field_types, self.name)

    def idx(self, field_name):
        return self.field_names.index(field_name)
dbarbera's avatar
dbarbera committed
100
101


dbarbera's avatar
dbarbera committed
102
103
104
105
106
107
108
class UnionType():
    def __init__(self, name, field_names, field_types):
        self.name = name
        self.field_names = field_names
        self.field_types = field_types
        # Unions are represented a struct with a field indicating the index of its type
        # and a byte array with the size of the biggest type in the union
dbarbera's avatar
dbarbera committed
109
110
        self.size = max([ctx.target_data.size(ty) for ty in field_types])
        self.ty = core.Type.struct([ctx.i32, core.Type.array(ctx.i8, self.size)], name)
dbarbera's avatar
dbarbera committed
111
112
113
114
115
116

    def kind(self, name):
        idx = self.field_names.index(name)
        return (idx, self.field_types[idx])


117
118
119
class Scope:
    def __init__(self, parent=None):
        self.vars = {}
dbarbera's avatar
dbarbera committed
120
        self.labels = {}
121
122
123
124
125
126
127
128
129
130
131
132
        self.parent = parent

    def define(self, name, var):
        self.vars[name.lower()] = var

    def resolve(self, name):
        var = self.vars.get(name.lower())
        if var:
            return var
        if self.parent:
            return self.parent.resolve(name)
        else:
dbarbera's avatar
dbarbera committed
133
            raise NameError("name '%s' is not defined" % name)
134

dbarbera's avatar
dbarbera committed
135
136
137
138
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
139
            func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
140
141
142
143
            label_block = func.append_basic_block(name)
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
144

Maxime Perrotin's avatar
Maxime Perrotin committed
145
146
147
148
@singledispatch
def generate(ast):
    ''' Generate the code for an item of the AST '''
    raise TypeError('[Backend] Unsupported AST construct')
Maxime Perrotin's avatar
Maxime Perrotin committed
149

dbarbera's avatar
dbarbera committed
150

Maxime Perrotin's avatar
Maxime Perrotin committed
151
152
153
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
154
    ''' Generate LLVM IR code '''
dbarbera's avatar
dbarbera committed
155
156
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
157

dbarbera's avatar
dbarbera committed
158
159
    global ctx
    ctx = Context(process)
160

dbarbera's avatar
dbarbera committed
161
162
163
164
165
166
    # In case model has nested states, flatten everything
    Helper.flatten(process)

    # Make an maping {input: {state: transition...}} in order to easily
    # generate the lookup tables for the state machine runtime
    mapping = Helper.map_input_state(process)
Maxime Perrotin's avatar
Maxime Perrotin committed
167

168
169
    # Initialize states
    for name, val in process.mapping.viewitems():
170
        if not name.endswith('START'):
dbarbera's avatar
dbarbera committed
171
172
            cons_val = core.Constant.int(ctx.i32, len(ctx.states))
            ctx.states[name] = cons_val
173
        elif name != 'START':
dbarbera's avatar
dbarbera committed
174
175
            cons_val = core.Constant.int(ctx.i32, val)
            ctx.states[name] = cons_val
176

177
    # Generate state var
dbarbera's avatar
dbarbera committed
178
179
180
    state_cons = ctx.module.add_global_variable(ctx.i32, 'state')
    state_cons.initializer = core.Constant.int(ctx.i32, -1)
    ctx.scope.define('state', state_cons)
181

182
183
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
dbarbera's avatar
dbarbera committed
184
        var_ty = generate_type(ty)
dbarbera's avatar
dbarbera committed
185
        global_var = ctx.module.add_global_variable(var_ty, str(name))
186
        global_var.initializer = core.Constant.null(var_ty)
dbarbera's avatar
dbarbera committed
187
        ctx.scope.define(str(name).lower(), global_var)
188

dbarbera's avatar
dbarbera committed
189
190
191
    # Declare timer set/reset functions
    for timer in process.timers:
        # TODO: Should be uint?
dbarbera's avatar
dbarbera committed
192
193
        decl_func("set_%s" % str(timer), ctx.void, [ctx.i32_ptr], True)
        decl_func("reset_%s" % str(timer), ctx.void, [], True)
dbarbera's avatar
dbarbera committed
194

195
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
196
    for signal in process.output_signals:
197
        if 'type' in signal:
dbarbera's avatar
dbarbera committed
198
            param_tys = [core.Type.pointer(generate_type(signal['type']))]
199
200
        else:
            param_tys = []
dbarbera's avatar
dbarbera committed
201
        decl_func(str(signal['name']), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
202

203
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
204
    for proc in [proc for proc in process.procedures if proc.external]:
dbarbera's avatar
dbarbera committed
205
        param_tys = [core.Type.pointer(generate_type(p['type'])) for p in proc.fpar]
dbarbera's avatar
dbarbera committed
206
        decl_func(str(proc.inputString), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
207

208
209
    # Generate internal procedures
    for proc in process.content.inner_procedures:
210
        generate(proc)
211

212
    # Generate process functions
dbarbera's avatar
dbarbera committed
213
214
    generate_runtr_func(process)
    generate_startup_func(process)
215

216
217
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
218
        generate_input_signal(signal, mapping[signal['name']])
219

dbarbera's avatar
dbarbera committed
220
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
221

dbarbera's avatar
dbarbera committed
222
223
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
224
225


dbarbera's avatar
dbarbera committed
226
def generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
227
    ''' Generate code for the run_transition function '''
dbarbera's avatar
dbarbera committed
228
    func = decl_func('run_transition', ctx.void, [ctx.i32])
229

dbarbera's avatar
dbarbera committed
230
    open_scope()
231

232
233
234
235
236
    entry_block = func.append_basic_block('entry')
    cond_block = func.append_basic_block('cond')
    body_block = func.append_basic_block('body')
    exit_block = func.append_basic_block('exit')

dbarbera's avatar
dbarbera committed
237
    ctx.builder = core.Builder.new(entry_block)
238
239

    # entry
dbarbera's avatar
dbarbera committed
240
241
242
243
    id_ptr = ctx.builder.alloca(ctx.i32, None, 'id')
    ctx.scope.define('id', id_ptr)
    ctx.builder.store(func.args[0], id_ptr)
    ctx.builder.branch(cond_block)
244
245

    # cond
dbarbera's avatar
dbarbera committed
246
247
248
249
250
    ctx.builder.position_at_end(cond_block)
    no_tr_cons = core.Constant.int(ctx.i32, -1)
    id_val = ctx.builder.load(id_ptr)
    cond_val = ctx.builder.icmp(core.ICMP_NE, id_val, no_tr_cons, 'cond')
    ctx.builder.cbranch(cond_val, body_block, exit_block)
251
252

    # body
dbarbera's avatar
dbarbera committed
253
254
    ctx.builder.position_at_end(body_block)
    switch = ctx.builder.switch(func.args[0], exit_block)
255
256
257
258

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
dbarbera's avatar
dbarbera committed
259
        const = core.Constant.int(ctx.i32, idx)
260
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
261
        ctx.builder.position_at_end(tr_block)
262
        generate(tr)
dbarbera's avatar
dbarbera committed
263
264
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
265
266

    # exit
dbarbera's avatar
dbarbera committed
267
268
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
269

dbarbera's avatar
dbarbera committed
270
271
272
273
274
    Helper.inner_labels_to_floating(process)
    for label in process.content.floating_labels:
        generate(label)

    # TODO: Use defined cond_block instead?
dbarbera's avatar
dbarbera committed
275
276
277
    next_tr_label_block = ctx.scope.label('next_transition')
    ctx.builder.position_at_end(next_tr_label_block)
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
278

dbarbera's avatar
dbarbera committed
279
    close_scope()
280

281
282
283
284
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
285
def generate_startup_func(process):
dbarbera's avatar
dbarbera committed
286
    ''' Generate code for the startup function '''
dbarbera's avatar
dbarbera committed
287
    func = decl_func(ctx.name + '_startup', ctx.void, [])
288

dbarbera's avatar
dbarbera committed
289
    open_scope()
290

291
    entry_block = func.append_basic_block('entry')
dbarbera's avatar
dbarbera committed
292
    ctx.builder = core.Builder.new(entry_block)
293

294
295
296
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
297
            global_var = ctx.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
298
            generate_assign(global_var, expression(expr))
299

dbarbera's avatar
dbarbera committed
300
301
    ctx.builder.call(ctx.funcs['run_transition'], [core.Constant.int(ctx.i32, 0)])
    ctx.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
302

dbarbera's avatar
dbarbera committed
303
    close_scope()
304

305
306
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
307
308


dbarbera's avatar
dbarbera committed
309
def generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
310
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
311
    func_name = ctx.name + "_" + str(signal['name'])
312
313
    param_tys = []
    if 'type' in signal:
dbarbera's avatar
dbarbera committed
314
        param_tys.append(core.Type.pointer(generate_type(signal['type'])))
315

dbarbera's avatar
dbarbera committed
316
    func = decl_func(func_name, ctx.void, param_tys)
317

dbarbera's avatar
dbarbera committed
318
    open_scope()
319

320
321
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
dbarbera's avatar
dbarbera committed
322
    ctx.builder = core.Builder.new(entry_block)
323

dbarbera's avatar
dbarbera committed
324
325
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('state'))
    switch = ctx.builder.switch(g_state_val, exit_block)
326

dbarbera's avatar
dbarbera committed
327
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
328
329
        if state_name.endswith('START'):
            continue
330
331
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
332
        ctx.builder.position_at_end(state_block)
333
334
335

        # TODO: Nested states

336
337
338
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
339
                var_ptr = ctx.scope.resolve(str(var_name))
340
341
342
                if is_struct_ptr(var_ptr):
                    generate_assign(var_ptr, func.args[0])
                else:
dbarbera's avatar
dbarbera committed
343
                    generate_assign(var_ptr, ctx.builder.load(func.args[0]))
344
            if input.transition:
dbarbera's avatar
dbarbera committed
345
346
                id_val = core.Constant.int(ctx.i32, input.transition_id)
                ctx.builder.call(ctx.funcs['run_transition'], [id_val])
347

dbarbera's avatar
dbarbera committed
348
        ctx.builder.ret_void()
349

dbarbera's avatar
dbarbera committed
350
351
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
352

dbarbera's avatar
dbarbera committed
353
    close_scope()
354

355
356
357
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
358
359
360
361
@generate.register(ogAST.Output)
@generate.register(ogAST.ProcedureCall)
def _call_external_function(output):
    ''' Generate the code of a set of output or procedure call statement '''
dbarbera's avatar
dbarbera committed
362
363
364
    for out in output.output:
        name = out['outputName'].lower()

365
        if name == 'write':
dbarbera's avatar
dbarbera committed
366
            generate_write(out['params'])
dbarbera's avatar
dbarbera committed
367
            continue
368
        elif name == 'writeln':
dbarbera's avatar
dbarbera committed
369
            generate_writeln(out['params'])
370
            continue
dbarbera's avatar
dbarbera committed
371
        elif name == 'reset_timer':
dbarbera's avatar
dbarbera committed
372
            generate_reset_timer(out['params'])
dbarbera's avatar
dbarbera committed
373
374
            continue
        elif name == 'set_timer':
dbarbera's avatar
dbarbera committed
375
            generate_set_timer(out['params'])
dbarbera's avatar
dbarbera committed
376
377
            continue

dbarbera's avatar
dbarbera committed
378
        func = ctx.funcs[str(name).lower()]
379
380
381
382
383
384

        params = []
        for p in out.get('params', []):
            p_val = expression(p)
            # Pass by reference
            if p_val.type.kind != core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
385
386
                p_var = ctx.builder.alloca(p_val.type, None)
                ctx.builder.store(p_val, p_var)
387
388
389
390
                params.append(p_var)
            else:
                params.append(p_val)

dbarbera's avatar
dbarbera committed
391
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
392
393


dbarbera's avatar
dbarbera committed
394
def generate_write(params):
dbarbera's avatar
dbarbera committed
395
    ''' Generate the code for the write operator '''
396
397
398
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
399

400
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
dbarbera's avatar
dbarbera committed
401
            fmt_val = get_string_cons('% d')
dbarbera's avatar
dbarbera committed
402
403
            fmt_ptr = ctx.builder.gep(fmt_val, [ctx.zero, ctx.zero])
            ctx.builder.call(ctx.funcs['printf'], [fmt_ptr, expr_val])
404
        elif basic_ty.kind == 'RealType':
dbarbera's avatar
dbarbera committed
405
            fmt_val = get_string_cons('% .14E')
dbarbera's avatar
dbarbera committed
406
407
            fmt_ptr = ctx.builder.gep(fmt_val, [ctx.zero, ctx.zero])
            ctx.builder.call(ctx.funcs['printf'], [fmt_ptr, expr_val])
408
        elif basic_ty.kind == 'BooleanType':
dbarbera's avatar
dbarbera committed
409
            true_str_val = get_string_cons('TRUE')
dbarbera's avatar
dbarbera committed
410
            true_str_ptr = ctx.builder.gep(true_str_val, [ctx.zero, ctx.zero])
dbarbera's avatar
dbarbera committed
411
            false_str_val = get_string_cons('FALSE')
dbarbera's avatar
dbarbera committed
412
413
414
            false_str_ptr = ctx.builder.gep(false_str_val, [ctx.zero, ctx.zero])
            str_ptr = ctx.builder.select(expr_val, true_str_ptr, false_str_ptr)
            ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
415
416
        elif basic_ty.kind in ['StringType', 'OctetStringType']:
            fmt_val = get_string_cons('%s')
dbarbera's avatar
dbarbera committed
417
418
419
            fmt_ptr = ctx.builder.gep(fmt_val, [ctx.zero, ctx.zero])
            arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.one])
            ctx.builder.call(ctx.funcs['printf'], [fmt_ptr, arr_ptr])
420
421
422
423
        else:
            raise NotImplementedError


dbarbera's avatar
dbarbera committed
424
def generate_writeln(params):
425
    ''' Generate the code for the writeln operator '''
dbarbera's avatar
dbarbera committed
426
    generate_write(params)
427

dbarbera's avatar
dbarbera committed
428
    zero = core.Constant.int(ctx.i32, 0)
dbarbera's avatar
dbarbera committed
429
    str_cons = get_string_cons('\n')
dbarbera's avatar
dbarbera committed
430
431
    str_ptr = ctx.builder.gep(str_cons, [zero, zero])
    ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
432
433


dbarbera's avatar
dbarbera committed
434
def generate_reset_timer(params):
dbarbera's avatar
dbarbera committed
435
    ''' Generate the code for the reset timer operator '''
dbarbera's avatar
dbarbera committed
436
    timer_id = params[0]
437
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
438
    reset_func = ctx.funcs[reset_func_name.lower()]
dbarbera's avatar
dbarbera committed
439

dbarbera's avatar
dbarbera committed
440
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
441
442


dbarbera's avatar
dbarbera committed
443
def generate_set_timer(params):
dbarbera's avatar
dbarbera committed
444
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
445
    timer_expr, timer_id = params
446
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
447
    set_func = ctx.funcs[set_func_name.lower()]
dbarbera's avatar
dbarbera committed
448
449
450

    expr_val = expression(timer_expr)

dbarbera's avatar
dbarbera committed
451
452
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
453

dbarbera's avatar
dbarbera committed
454
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
455
456
457
458


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
459
    ''' Generate the code of a list of assignments '''
460
461
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
462
463
464
465
466


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
467
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
468
469
470
471


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
472
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
473
474
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
475
            generate_for_range(loop)
dbarbera's avatar
dbarbera committed
476
        else:
dbarbera's avatar
dbarbera committed
477
            generate_for_iterable(loop)
dbarbera's avatar
dbarbera committed
478
479


dbarbera's avatar
dbarbera committed
480
def generate_for_range(loop):
dbarbera's avatar
dbarbera committed
481
    ''' Generate the code for a for x in range loop '''
dbarbera's avatar
dbarbera committed
482
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
483
484
485
486
487
    cond_block = func.append_basic_block('for:cond')
    body_block = func.append_basic_block('for:body')
    inc_block = func.append_basic_block('for:inc')
    end_block = func.append_basic_block('')

dbarbera's avatar
dbarbera committed
488
    open_scope()
dbarbera's avatar
dbarbera committed
489

dbarbera's avatar
dbarbera committed
490
491
    loop_var = ctx.builder.alloca(ctx.i32, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
492
493
494

    if loop['range']['start']:
        start_val = expression(loop['range']['start'])
dbarbera's avatar
dbarbera committed
495
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
496
    else:
dbarbera's avatar
dbarbera committed
497
        ctx.builder.store(core.Constant.int(ctx.i32, 0), loop_var)
dbarbera's avatar
dbarbera committed
498
499

    stop_val = expression(loop['range']['stop'])
dbarbera's avatar
dbarbera committed
500
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
501

dbarbera's avatar
dbarbera committed
502
503
504
505
    ctx.builder.position_at_end(cond_block)
    loop_val = ctx.builder.load(loop_var)
    cond_val = ctx.builder.icmp(core.ICMP_SLT, loop_val, stop_val)
    ctx.builder.cbranch(cond_val, body_block, end_block)
dbarbera's avatar
dbarbera committed
506

dbarbera's avatar
dbarbera committed
507
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
508
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
509
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
510

dbarbera's avatar
dbarbera committed
511
512
513
514
515
516
    ctx.builder.position_at_end(inc_block)
    step_val = core.Constant.int(ctx.i32, loop['range']['step'])
    loop_val = ctx.builder.load(loop_var)
    temp_val = ctx.builder.add(loop_val, step_val)
    ctx.builder.store(temp_val, loop_var)
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
517

dbarbera's avatar
dbarbera committed
518
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
519

dbarbera's avatar
dbarbera committed
520
    close_scope()
dbarbera's avatar
dbarbera committed
521
522


dbarbera's avatar
dbarbera committed
523
def generate_for_iterable(loop):
dbarbera's avatar
dbarbera committed
524
    ''' Generate the code for a for x in iterable loop'''
dbarbera's avatar
dbarbera committed
525
    seqof_asn1ty = find_basic_type(loop['list'].exprType)
526
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
527

dbarbera's avatar
dbarbera committed
528
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
529
530
531
532
533
534
535
536
537
538
539
540

    # block for loading the value from the secuence
    # at the current index, incrementing the index afterwards
    load_block = func.append_basic_block('forin:load')
    # block for the body of the loop
    body_block = func.append_basic_block('forin:body')
    # block for checking if should loop again or terminate
    cond_block = func.append_basic_block('forin:cond')
    end_block = func.append_basic_block('')

    open_scope()

dbarbera's avatar
dbarbera committed
541
542
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
543
    seqof_struct_ptr = expression(loop['list'])
544
545
546

    if is_variable_size:
        # In variable size SequenceOfs the array values are in the second field
dbarbera's avatar
dbarbera committed
547
        array_ptr = ctx.builder.gep(seqof_struct_ptr, [ctx.zero, ctx.one])
548
    else:
dbarbera's avatar
dbarbera committed
549
        array_ptr = ctx.builder.gep(seqof_struct_ptr, [ctx.zero, ctx.zero])
550

dbarbera's avatar
dbarbera committed
551
    element_typ = array_ptr.type.pointee.element
552
553
554

    if is_variable_size:
        # load the current number of elements that is on the first field
dbarbera's avatar
dbarbera committed
555
        end_idx = ctx.builder.load(ctx.builder.gep(seqof_struct_ptr, [ctx.zero, ctx.zero]))
556
    else:
dbarbera's avatar
dbarbera committed
557
        end_idx = core.Constant.int(ctx.i32, array_ptr.type.pointee.count)
dbarbera's avatar
dbarbera committed
558

dbarbera's avatar
dbarbera committed
559
560
    var_ptr = ctx.builder.alloca(element_typ, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
561

dbarbera's avatar
dbarbera committed
562
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
563
564

    # load block
dbarbera's avatar
dbarbera committed
565
566
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
567
    if element_typ.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
568
        generate_assign(var_ptr, ctx.builder.gep(array_ptr, [ctx.zero, idx_var]))
569
    else:
dbarbera's avatar
dbarbera committed
570
571
        generate_assign(var_ptr, ctx.builder.load(ctx.builder.gep(array_ptr, [ctx.zero, idx_var])))
    ctx.builder.branch(body_block)
dbarbera's avatar
dbarbera committed
572
573

    # body block
dbarbera's avatar
dbarbera committed
574
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
575
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
576
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
577
578

    # cond block
dbarbera's avatar
dbarbera committed
579
580
581
582
583
    ctx.builder.position_at_end(cond_block)
    tmp_val = ctx.builder.add(idx_var, ctx.one)
    ctx.builder.store(tmp_val, idx_ptr)
    cond_val = ctx.builder.icmp(core.ICMP_SLT, tmp_val, end_idx)
    ctx.builder.cbranch(cond_val, load_block, end_block)
dbarbera's avatar
dbarbera committed
584

dbarbera's avatar
dbarbera committed
585
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
586
587

    close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
588

dbarbera's avatar
dbarbera committed
589

dbarbera's avatar
dbarbera committed
590
591
592
@singledispatch
def reference(prim):
    ''' Generate a variable reference '''
dbarbera's avatar
flake8    
dbarbera committed
593
    raise TypeError('Unsupported reference: ' + str(prim))
dbarbera's avatar
dbarbera committed
594
595
596
597
598


@reference.register(ogAST.PrimVariable)
def _prim_var_reference(prim):
    ''' Generate a primary variable reference '''
dbarbera's avatar
dbarbera committed
599
    return ctx.scope.resolve(str(prim.value[0]))
dbarbera's avatar
dbarbera committed
600
601
602
603
604


@reference.register(ogAST.PrimPath)
def _prim_path_reference(prim):
    ''' Generate a primary path reference '''
605
    var_name = prim.value[0].lower()
dbarbera's avatar
dbarbera committed
606
    var_ptr = ctx.scope.resolve(str(var_name))
dbarbera's avatar
dbarbera committed
607
608
609
610

    if not prim.value:
        return var_ptr

dbarbera's avatar
dbarbera committed
611
612
613
614
    for elem in prim.value[1:]:
        if type(elem) == dict:
            if 'index' in elem:
                idx_val = expression(elem['index'][0])
dbarbera's avatar
dbarbera committed
615
                array_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.zero])
616
                # TODO: Refactor this
dbarbera's avatar
dbarbera committed
617
618
                if array_ptr.type.pointee.kind != core.TYPE_ARRAY:
                    # If is not an array this is a pointer to a variable size SeqOf
619
                    # The array is in the second field of the struct
dbarbera's avatar
dbarbera committed
620
                    var_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.one, idx_val])
621
                else:
dbarbera's avatar
dbarbera committed
622
                    var_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.zero, idx_val])
dbarbera's avatar
dbarbera committed
623
624
625
626
            else:
                raise NotImplementedError
        else:
            var_ty = var_ptr.type
dbarbera's avatar
dbarbera committed
627
628
629
630
            if var_ty.pointee.name in ctx.structs:
                struct = ctx.structs[var_ty.pointee.name]
                field_idx_cons = core.Constant.int(ctx.i32, struct.idx(elem))
                field_ptr = ctx.builder.gep(var_ptr, [ctx.zero, field_idx_cons])
dbarbera's avatar
dbarbera committed
631
                var_ptr = field_ptr
dbarbera's avatar
dbarbera committed
632
633
            elif var_ty.pointee.name in ctx.unions:
                union = ctx.unions[var_ty.pointee.name]
dbarbera's avatar
dbarbera committed
634
                _, field_ty = union.kind(elem)
dbarbera's avatar
dbarbera committed
635
636
                field_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.one])
                var_ptr = ctx.builder.bitcast(field_ptr, core.Type.pointer(field_ty))
dbarbera's avatar
dbarbera committed
637
638
            else:
                raise NotImplementedError
dbarbera's avatar
dbarbera committed
639
640
    return var_ptr

Maxime Perrotin's avatar
Maxime Perrotin committed
641
642
643

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
644
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
645
646
647
648
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
649
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
650
651
    ''' Generate the code for a variable expression '''
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
652
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
653
654


Maxime Perrotin's avatar
Maxime Perrotin committed
655
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
656
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
657
    ''' Generate the code for an of path expression '''
658
659
660
661
662
663
664
665
666
    specops_generators = {
        'length': generate_length,
        'present': generate_present,
        'abs': generate_abs,
        'fix': generate_fix,
        'float': generate_float,
        'power': generate_power
    }

dbarbera's avatar
dbarbera committed
667
668
669
670
671
672
673
    name = prim.value[0].lower()
    if name in specops_generators:
        generator = specops_generators[name]
        return generator(prim.value[1]['procParams'])

    return generate_access(prim)

674

dbarbera's avatar
dbarbera committed
675
676
def generate_access(prim):
    ''' Generate the code for an access '''
dbarbera's avatar
dbarbera committed
677
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
678
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
679
680


681
def generate_length(params):
dbarbera's avatar
dbarbera committed
682
    ''' Generate the code for the built-in length operation'''
dbarbera's avatar
dbarbera committed
683
    seq_ptr = reference(params[0])
dbarbera's avatar
dbarbera committed
684
    arr_ty = seq_ptr.type.pointee.elements[0]
dbarbera's avatar
dbarbera committed
685
    return core.Constant.int(ctx.i32, arr_ty.count)
686
687
688


def generate_present(params):
dbarbera's avatar
dbarbera committed
689
    ''' Generate the code for the built-in present operation'''
690
691
692
693
    raise NotImplementedError


def generate_abs(params):
dbarbera's avatar
dbarbera committed
694
    ''' Generate the code for the built-in abs operation'''
dbarbera's avatar
dbarbera committed
695
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
696

dbarbera's avatar
dbarbera committed
697
    if expr_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
698
699
700
        expr_conv = ctx.builder.sitofp(expr_val, ctx.double)
        res_val = ctx.builder.call(ctx.funcs['fabs'], [expr_conv])
        return ctx.builder.fptosi(res_val, ctx.i32)
dbarbera's avatar
dbarbera committed
701
    else:
dbarbera's avatar
dbarbera committed
702
        return ctx.builder.call(ctx.funcs['fabs'], [expr_val])
703
704
705


def generate_fix(params):
dbarbera's avatar
dbarbera committed
706
    ''' Generate the code for the built-in fix operation'''
dbarbera's avatar
dbarbera committed
707
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
708
    return ctx.builder.fptosi(expr_val, ctx.i32)
709
710
711


def generate_float(params):
dbarbera's avatar
dbarbera committed
712
    ''' Generate the code for the built-in float operation'''
dbarbera's avatar
dbarbera committed
713
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
714
    return ctx.builder.sitofp(expr_val, ctx.double)
715
716
717


def generate_power(params):
dbarbera's avatar
dbarbera committed
718
    ''' Generate the code for the built-in power operation'''
719
720
721
722
    left_val = expression(params[0])
    right_val = expression(params[1])

    if left_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
723
724
725
        left_conv = ctx.builder.sitofp(left_val, ctx.double)
        res_val = ctx.builder.call(ctx.funcs['powi'], [left_conv, right_val])
        return ctx.builder.fptosi(res_val, ctx.i32)
726
    else:
dbarbera's avatar
dbarbera committed
727
        return ctx.builder.call(ctx.funcs['powi'], [left_val, right_val])
728
729


Maxime Perrotin's avatar
Maxime Perrotin committed
730
731
732
733
734
735
736
737
738
739
740
741
@expression.register(ogAST.ExprPlus)
@expression.register(ogAST.ExprMul)
@expression.register(ogAST.ExprMinus)
@expression.register(ogAST.ExprEq)
@expression.register(ogAST.ExprNeq)
@expression.register(ogAST.ExprGt)
@expression.register(ogAST.ExprGe)
@expression.register(ogAST.ExprLt)
@expression.register(ogAST.ExprLe)
@expression.register(ogAST.ExprDiv)
@expression.register(ogAST.ExprMod)
@expression.register(ogAST.ExprRem)
dbarbera's avatar
dbarbera committed
742
743
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
744
745
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
746

747
748
    if lefttmp.type.kind == core.TYPE_INTEGER:
        if expr.operand == '+':
dbarbera's avatar
dbarbera committed
749
            return ctx.builder.add(lefttmp, righttmp, 'addtmp')
750
        elif expr.operand == '-':
dbarbera's avatar
dbarbera committed
751
            return ctx.builder.sub(lefttmp, righttmp, 'subtmp')
752
        elif expr.operand == '*':
dbarbera's avatar
dbarbera committed
753
            return ctx.builder.mul(lefttmp, righttmp, 'multmp')
754
        elif expr.operand == '/':
dbarbera's avatar
dbarbera committed
755
            return ctx.builder.sdiv(lefttmp, righttmp, 'divtmp')
756
        elif expr.operand == 'mod':
757
            # l mod r == (((l rem r) + r) rem r)
dbarbera's avatar
dbarbera committed
758
759
760
            remtmp = ctx.builder.srem(lefttmp, righttmp)
            addtmp = ctx.builder.add(remtmp, righttmp)
            return ctx.builder.srem(addtmp, righttmp, 'modtmp')
761
        elif expr.operand == 'rem':
dbarbera's avatar
dbarbera committed
762
            return ctx.builder.srem(lefttmp, righttmp, 'remtmp')
763
        elif expr.operand == '<':
dbarbera's avatar
dbarbera committed
764
            return ctx.builder.icmp(core.ICMP_SLT, lefttmp, righttmp, 'lttmp')
765
        elif expr.operand == '<=':
dbarbera's avatar
dbarbera committed
766
            return ctx.builder.icmp(core.ICMP_SLE, lefttmp, righttmp, 'letmp')
767
        elif expr.operand == '=':
dbarbera's avatar
dbarbera committed
768
            return ctx.builder.icmp(core.ICMP_EQ, lefttmp, righttmp, 'eqtmp')
769
        elif expr.operand == '/=':
dbarbera's avatar
dbarbera committed
770
            return ctx.builder.icmp(core.ICMP_NE, lefttmp, righttmp, 'netmp')
771
        elif expr.operand == '>=':
dbarbera's avatar
dbarbera committed
772
            return ctx.builder.icmp(core.ICMP_SGE, lefttmp, righttmp, 'getmp')
773
        elif expr.operand == '>':
dbarbera's avatar
dbarbera committed
774
            return ctx.builder.icmp(core.ICMP_SGT, lefttmp, righttmp, 'gttmp')
775
776
777
778
        else:
            raise NotImplementedError
    elif lefttmp.type.kind == core.TYPE_DOUBLE:
        if expr.operand == '+':
dbarbera's avatar
dbarbera committed
779
            return ctx.builder.fadd(lefttmp, righttmp, 'addtmp')
780
        elif expr.operand == '-':
dbarbera's avatar
dbarbera committed
781
            return ctx.builder.fsub(lefttmp, righttmp, 'subtmp')
782
        elif expr.operand == '*':
dbarbera's avatar
dbarbera committed
783
            return ctx.builder.fmul(lefttmp, righttmp, 'multmp')
784
        elif expr.operand == '/':
dbarbera's avatar
dbarbera committed
785
            return ctx.builder.fdiv(lefttmp, righttmp, 'divtmp')
786
        elif expr.operand == '<':
dbarbera's avatar
dbarbera committed
787
            return ctx.builder.fcmp(core.FCMP_OLT, lefttmp, righttmp, 'lttmp')
788
        elif expr.operand == '<=':
dbarbera's avatar
dbarbera committed
789
            return ctx.builder.fcmp(core.FCMP_OLE, lefttmp, righttmp, 'letmp')
790
        elif expr.operand == '=':
dbarbera's avatar
dbarbera committed
791
            return ctx.builder.fcmp(core.FCMP_OEQ, lefttmp, righttmp, 'eqtmp')
792
        elif expr.operand == '/=':
dbarbera's avatar
dbarbera committed
793
            return ctx.builder.fcmp(core.FCMP_ONE, lefttmp, righttmp, 'netmp')
794
        elif expr.operand == '>=':
dbarbera's avatar
dbarbera committed
795
            return ctx.builder.fcmp(core.FCMP_OGE, lefttmp, righttmp, 'getmp')
796
        elif expr.operand == '>':
dbarbera's avatar
dbarbera committed
797
            return ctx.builder.fcmp(core.FCMP_OGT, lefttmp, righttmp, 'gttmp')
798
799
        else:
            raise NotImplementedError
800
    else:
801
        raise NotImplementedError
802

Maxime Perrotin's avatar
Maxime Perrotin committed
803

804
805
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
806
    ''' Generate the code for an assign expression '''
dbarbera's avatar
dbarbera committed
807
    generate_assign(reference(expr.left), expression(expr.right))
808

809

dbarbera's avatar
dbarbera committed
810
def generate_assign(left, right):
811
812
813
    ''' Generate code for an assign from two LLVM values'''
    # This is extracted as an standalone function because is used by
    # multiple generation rules
dbarbera's avatar
dbarbera committed
814
    if is_struct_ptr(left):