LlvmGenerator.py 48.9 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.enums = {}
dbarbera's avatar
dbarbera committed
48
        self.structs = {}
dbarbera's avatar
dbarbera committed
49
        self.unions = {}
50
        self.strings = {}
dbarbera's avatar
dbarbera committed
51
        self.funcs = {}
52
        self.lltypes = {}
53
54
55
56
57
58
59
60
61
62
63
64
65
66

        # 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
67
68
69
70
        # Initialize common constants
        self.zero = core.Constant.int(self.i32, 0)
        self.one = core.Constant.int(self.i32, 1)

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

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

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

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

93
    def open_scope(self):
dbarbera's avatar
dbarbera committed
94
        ''' Open a scope '''
95
96
97
        self.scope = Scope(self.scope)

    def close_scope(self):
dbarbera's avatar
dbarbera committed
98
        ''' Close the current scope '''
99
100
        self.scope = self.scope.parent

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
141
142
143
144
145
146
    def type_of(self, asn1ty):
        ''' Return the LL type of a ASN.1 type '''
        try:
            name = asn1ty.ReferencedTypeName.replace('-', '_')
        except AttributeError:
            name = None

        if name and name in self.lltypes:
            return self.lltypes[name]

        basic_asn1ty = find_basic_type(asn1ty)

        if basic_asn1ty.kind in ['IntegerType', 'Integer32Type']:
            llty = ctx.i32
        elif basic_asn1ty.kind == 'BooleanType':
            llty = ctx.i1
        elif basic_asn1ty.kind == 'RealType':
            llty = ctx.double
        elif basic_asn1ty.kind == 'SequenceOfType':
            llty = self._type_of_sequenceof(name, basic_asn1ty)
        elif basic_asn1ty.kind == 'SequenceType':
            llty = self._type_of_sequence(name, basic_asn1ty)
        elif basic_asn1ty.kind == 'EnumeratedType':
            llty = ctx.i32
        elif basic_asn1ty.kind == 'ChoiceType':
            llty = self._type_of_choice(name, basic_asn1ty)
        elif basic_asn1ty.kind == 'OctetStringType':
            llty = self._type_of_octetstring(name, basic_asn1ty)
        else:
            raise NotImplementedError

        if name:
            self.lltypes[name] = llty

        return llty

    def _type_of_sequenceof(self, name, sequenceof_ty):
        ''' Return the LL type of a SequenceOf ASN.1 type '''
        min_size = int(sequenceof_ty.Min)
        max_size = int(sequenceof_ty.Max)
        is_variable_size = min_size != max_size

        elem_ty = self.type_of(sequenceof_ty.type)
        array_ty = core.Type.array(elem_ty, max_size)

        if is_variable_size:
147
            struct = self.decl_struct(['nCount', 'arr'], [ctx.i32, array_ty], name)
148
        else:
149
            struct = self.decl_struct(['arr'], [array_ty], name)
150
151
152
153
154
155
156
157
158
159

        return struct.ty

    def _type_of_sequence(self, name, sequence_ty):
        ''' Return the LL type of a Sequence ASN.1 type '''
        field_names = []
        field_types = []
        for field_name in Helper.sorted_fields(sequence_ty):
            field_names.append(field_name.replace('-', '_'))
            field_types.append(self.type_of(sequence_ty.Children[field_name].type))
160
        struct = self.decl_struct(field_names, field_types, name)
161
162
163
164
165
166
167

        return struct.ty

    def _type_of_choice(self, name, choice_ty):
        ''' Return the equivalent LL type of a Choice ASN.1 type '''
        field_names = []
        field_types = []
dbarbera's avatar
dbarbera committed
168
169
170

        for idx, field_name in enumerate(Helper.sorted_fields(choice_ty)):
            # enum values used in choice determinant/present
dbarbera's avatar
dbarbera committed
171
            self.enums[field_name.replace('-', '_')] = core.Constant.int(self.i32, idx)
dbarbera's avatar
dbarbera committed
172

dbarbera's avatar
dbarbera committed
173
            field_names.append(field_name.replace('-', '_'))
dbarbera's avatar
dbarbera committed
174
            field_types.append(self.type_of(choice_ty.Children[field_name].type))
175

176
        union = self.decl_union(field_names, field_types, name)
177
178
179
180
181
182
        return union.ty

    def _type_of_octetstring(self, name, octetstring_ty):
        ''' Return the equivalent LL type of a OcterString ASN.1 type '''
        max_size = int(octetstring_ty.Max)
        arr_ty = core.Type.array(ctx.i8, max_size)
183
        struct = self.decl_struct(['nCount', 'arr'], [ctx.i32, arr_ty], name)
184
185
        return struct.ty

186
187
188
189
190
191
192
193
194
195
196
197
    def string_ptr(self, str):
        ''' Returns a pointer to a global string with the given value '''
        if str in self.strings:
            return self.strings[str].gep([self.zero, self.zero])

        str_val = core.Constant.stringz(str)
        var_name = '.str%s' % len(self.strings)
        var_ptr = self.module.add_global_variable(str_val.type, var_name)
        var_ptr.initializer = str_val
        self.strings[str] = var_ptr
        return var_ptr.gep([self.zero, self.zero])

198
199
200
201
202
203
204
205
206
207
208
    def decl_func(self, name, return_ty, param_tys, extern=False):
        ''' Declare a function '''
        func_ty = core.Type.function(return_ty, param_tys)
        func_name = ("%s_RI_%s" % (self.name, name)) if extern else name
        func = core.Function.new(self.module, func_ty, func_name)
        self.funcs[name.lower()] = func
        return func

    def decl_struct(self, field_names, field_types, name=None):
        ''' Declare a struct '''
        name = name if name else "struct.%s" % len(self.structs)
dbarbera's avatar
dbarbera committed
209
        name = name.replace('-', '_')
210
211
212
213
        struct = StructType(name, field_names, field_types)
        self.structs[name] = struct
        return struct

dbarbera's avatar
dbarbera committed
214
215
216
217
    def resolve_struct(self, name):
        ''' Return the struct associated to a name '''
        return self.structs[name.replace('-', '_')]

218
219
    def decl_union(self, field_names, field_types, name=None):
        name = name if name else "union.%s" % len(self.structs)
dbarbera's avatar
dbarbera committed
220
        name = name.replace('-', '_')
221
222
223
224
        union = UnionType(name, field_names, field_types)
        self.unions[name] = union
        return union

dbarbera's avatar
dbarbera committed
225
226
227
228
    def resolve_union(self, name):
        ''' Return the union associated to a name '''
        return self.unions[name.replace('-', '_')]

dbarbera's avatar
dbarbera committed
229

230
231
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
232
        self.name = name
233
234
235
236
237
        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
238
239


dbarbera's avatar
dbarbera committed
240
241
242
243
244
245
246
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
247
248
        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
249
250
251
252
253
254

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


255
256
257
class Scope:
    def __init__(self, parent=None):
        self.vars = {}
dbarbera's avatar
dbarbera committed
258
        self.labels = {}
259
260
261
262
263
264
265
266
267
268
269
270
        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
271
            raise NameError("name '%s' is not defined" % name)
272

dbarbera's avatar
dbarbera committed
273
274
275
276
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
277
            func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
278
279
280
281
            label_block = func.append_basic_block(name)
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
282

Maxime Perrotin's avatar
Maxime Perrotin committed
283
284
285
286
@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
287

dbarbera's avatar
dbarbera committed
288

Maxime Perrotin's avatar
Maxime Perrotin committed
289
290
291
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
292
    ''' Generate LLVM IR code '''
dbarbera's avatar
dbarbera committed
293
294
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
295

dbarbera's avatar
dbarbera committed
296
297
    global ctx
    ctx = Context(process)
298

dbarbera's avatar
dbarbera committed
299
300
301
302
303
304
    # 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
305

306
307
    # Initialize states
    for name, val in process.mapping.viewitems():
308
        if not name.endswith('START'):
dbarbera's avatar
dbarbera committed
309
310
            cons_val = core.Constant.int(ctx.i32, len(ctx.states))
            ctx.states[name] = cons_val
311
        elif name != 'START':
dbarbera's avatar
dbarbera committed
312
313
            cons_val = core.Constant.int(ctx.i32, val)
            ctx.states[name] = cons_val
314

315
    # Generate state var
dbarbera's avatar
dbarbera committed
316
317
318
    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)
319

320
321
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
322
        var_ty = ctx.type_of(ty)
dbarbera's avatar
dbarbera committed
323
        global_var = ctx.module.add_global_variable(var_ty, str(name))
324
        global_var.initializer = core.Constant.null(var_ty)
dbarbera's avatar
dbarbera committed
325
        ctx.scope.define(str(name).lower(), global_var)
326

dbarbera's avatar
dbarbera committed
327
328
329
    # Declare timer set/reset functions
    for timer in process.timers:
        # TODO: Should be uint?
330
331
        ctx.decl_func("set_%s" % str(timer), ctx.void, [ctx.i32_ptr], True)
        ctx.decl_func("reset_%s" % str(timer), ctx.void, [], True)
dbarbera's avatar
dbarbera committed
332

333
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
334
    for signal in process.output_signals:
335
        if 'type' in signal:
336
            param_tys = [core.Type.pointer(ctx.type_of(signal['type']))]
337
338
        else:
            param_tys = []
339
        ctx.decl_func(str(signal['name']), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
340

341
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
342
    for proc in [proc for proc in process.procedures if proc.external]:
343
        param_tys = [core.Type.pointer(ctx.type_of(p['type'])) for p in proc.fpar]
344
        ctx.decl_func(str(proc.inputString), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
345

346
347
    # Generate internal procedures
    for proc in process.content.inner_procedures:
348
        generate(proc)
349

350
    # Generate process functions
dbarbera's avatar
dbarbera committed
351
352
    generate_runtr_func(process)
    generate_startup_func(process)
353

354
355
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
356
        generate_input_signal(signal, mapping[signal['name']])
357

dbarbera's avatar
dbarbera committed
358
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
359

dbarbera's avatar
dbarbera committed
360
361
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
362
363


dbarbera's avatar
dbarbera committed
364
def generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
365
    ''' Generate code for the run_transition function '''
366
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
367

368
    ctx.open_scope()
369

370
371
372
373
374
    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
375
    ctx.builder = core.Builder.new(entry_block)
376
377

    # entry
dbarbera's avatar
dbarbera committed
378
379
380
381
    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)
382
383

    # cond
dbarbera's avatar
dbarbera committed
384
385
386
387
388
    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)
389
390

    # body
dbarbera's avatar
dbarbera committed
391
392
    ctx.builder.position_at_end(body_block)
    switch = ctx.builder.switch(func.args[0], exit_block)
393
394
395
396

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
dbarbera's avatar
dbarbera committed
397
        const = core.Constant.int(ctx.i32, idx)
398
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
399
        ctx.builder.position_at_end(tr_block)
400
        generate(tr)
dbarbera's avatar
dbarbera committed
401
402
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
403
404

    # exit
dbarbera's avatar
dbarbera committed
405
406
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
407

dbarbera's avatar
dbarbera committed
408
409
410
411
412
    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
413
414
415
    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
416

417
    ctx.close_scope()
418

419
420
421
422
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
423
def generate_startup_func(process):
dbarbera's avatar
dbarbera committed
424
    ''' Generate code for the startup function '''
425
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
426

427
    ctx.open_scope()
428

429
    entry_block = func.append_basic_block('entry')
dbarbera's avatar
dbarbera committed
430
    ctx.builder = core.Builder.new(entry_block)
431

432
433
434
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
435
            global_var = ctx.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
436
            generate_assign(global_var, expression(expr))
437

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

441
    ctx.close_scope()
442

443
444
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
445
446


dbarbera's avatar
dbarbera committed
447
def generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
448
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
449
    func_name = ctx.name + "_" + str(signal['name'])
450
451
    param_tys = []
    if 'type' in signal:
452
        param_tys.append(core.Type.pointer(ctx.type_of(signal['type'])))
453

454
    func = ctx.decl_func(func_name, ctx.void, param_tys)
455

456
    ctx.open_scope()
457

458
459
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
dbarbera's avatar
dbarbera committed
460
    ctx.builder = core.Builder.new(entry_block)
461

dbarbera's avatar
dbarbera committed
462
463
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('state'))
    switch = ctx.builder.switch(g_state_val, exit_block)
464

dbarbera's avatar
dbarbera committed
465
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
466
467
        if state_name.endswith('START'):
            continue
468
469
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
470
        ctx.builder.position_at_end(state_block)
471
472
473

        # TODO: Nested states

474
475
476
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
477
                var_ptr = ctx.scope.resolve(str(var_name))
478
479
480
                if is_struct_ptr(var_ptr):
                    generate_assign(var_ptr, func.args[0])
                else:
dbarbera's avatar
dbarbera committed
481
                    generate_assign(var_ptr, ctx.builder.load(func.args[0]))
482
            if input.transition:
dbarbera's avatar
dbarbera committed
483
484
                id_val = core.Constant.int(ctx.i32, input.transition_id)
                ctx.builder.call(ctx.funcs['run_transition'], [id_val])
485

dbarbera's avatar
dbarbera committed
486
        ctx.builder.ret_void()
487

dbarbera's avatar
dbarbera committed
488
489
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
490

491
    ctx.close_scope()
492

493
494
495
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
496
497
498
499
@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
500
501
502
    for out in output.output:
        name = out['outputName'].lower()

503
        if name == 'write':
dbarbera's avatar
dbarbera committed
504
            generate_write(out['params'])
dbarbera's avatar
dbarbera committed
505
            continue
506
        elif name == 'writeln':
dbarbera's avatar
dbarbera committed
507
            generate_writeln(out['params'])
508
            continue
dbarbera's avatar
dbarbera committed
509
        elif name == 'reset_timer':
dbarbera's avatar
dbarbera committed
510
            generate_reset_timer(out['params'])
dbarbera's avatar
dbarbera committed
511
512
            continue
        elif name == 'set_timer':
dbarbera's avatar
dbarbera committed
513
            generate_set_timer(out['params'])
dbarbera's avatar
dbarbera committed
514
515
            continue

dbarbera's avatar
dbarbera committed
516
        func = ctx.funcs[str(name).lower()]
517
518
519
520
521
522

        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
523
524
                p_var = ctx.builder.alloca(p_val.type, None)
                ctx.builder.store(p_val, p_var)
525
526
527
528
                params.append(p_var)
            else:
                params.append(p_val)

dbarbera's avatar
dbarbera committed
529
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
530
531


dbarbera's avatar
dbarbera committed
532
def generate_write(params):
dbarbera's avatar
dbarbera committed
533
    ''' Generate the code for the write operator '''
534
535
536
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
537

538
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
539
540
            fmt_str_ptr = ctx.string_ptr('% d')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
541
        elif basic_ty.kind == 'RealType':
542
543
            fmt_str_ptr = ctx.string_ptr('% .14E')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
544
        elif basic_ty.kind == 'BooleanType':
545
546
            true_str_ptr = ctx.string_ptr('TRUE')
            false_str_ptr = ctx.string_ptr('FALSE')
dbarbera's avatar
dbarbera committed
547
548
            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
549
        elif basic_ty.kind in ['StringType', 'OctetStringType']:
550
            fmt_str_ptr = ctx.string_ptr('%s')
dbarbera's avatar
dbarbera committed
551
            arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.one])
552
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, arr_ptr])
553
554
555
556
        else:
            raise NotImplementedError


dbarbera's avatar
dbarbera committed
557
def generate_writeln(params):
558
    ''' Generate the code for the writeln operator '''
dbarbera's avatar
dbarbera committed
559
    generate_write(params)
560

561
    str_ptr = ctx.string_ptr('\n')
dbarbera's avatar
dbarbera committed
562
    ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
563
564


dbarbera's avatar
dbarbera committed
565
def generate_reset_timer(params):
dbarbera's avatar
dbarbera committed
566
    ''' Generate the code for the reset timer operator '''
dbarbera's avatar
dbarbera committed
567
    timer_id = params[0]
568
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
569
    reset_func = ctx.funcs[reset_func_name.lower()]
dbarbera's avatar
dbarbera committed
570

dbarbera's avatar
dbarbera committed
571
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
572
573


dbarbera's avatar
dbarbera committed
574
def generate_set_timer(params):
dbarbera's avatar
dbarbera committed
575
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
576
    timer_expr, timer_id = params
577
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
578
    set_func = ctx.funcs[set_func_name.lower()]
dbarbera's avatar
dbarbera committed
579
580
581

    expr_val = expression(timer_expr)

dbarbera's avatar
dbarbera committed
582
583
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
584

dbarbera's avatar
dbarbera committed
585
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
586
587
588
589


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
590
    ''' Generate the code of a list of assignments '''
591
592
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
593
594
595
596
597


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
598
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
599
600
601
602


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
603
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
604
605
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
606
            generate_for_range(loop)
dbarbera's avatar
dbarbera committed
607
        else:
dbarbera's avatar
dbarbera committed
608
            generate_for_iterable(loop)
dbarbera's avatar
dbarbera committed
609
610


dbarbera's avatar
dbarbera committed
611
def generate_for_range(loop):
dbarbera's avatar
dbarbera committed
612
    ''' Generate the code for a for x in range loop '''
dbarbera's avatar
dbarbera committed
613
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
614
615
616
617
618
    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('')

619
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
620

dbarbera's avatar
dbarbera committed
621
622
    loop_var = ctx.builder.alloca(ctx.i32, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
623
624
625

    if loop['range']['start']:
        start_val = expression(loop['range']['start'])
dbarbera's avatar
dbarbera committed
626
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
627
    else:
dbarbera's avatar
dbarbera committed
628
        ctx.builder.store(core.Constant.int(ctx.i32, 0), loop_var)
dbarbera's avatar
dbarbera committed
629
630

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

dbarbera's avatar
dbarbera committed
633
634
635
636
    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
637

dbarbera's avatar
dbarbera committed
638
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
639
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
640
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
641

dbarbera's avatar
dbarbera committed
642
643
644
645
646
647
    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
648

dbarbera's avatar
dbarbera committed
649
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
650

651
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
652
653


dbarbera's avatar
dbarbera committed
654
def generate_for_iterable(loop):
dbarbera's avatar
dbarbera committed
655
    ''' Generate the code for a for x in iterable loop'''
dbarbera's avatar
dbarbera committed
656
    seqof_asn1ty = find_basic_type(loop['list'].exprType)
657
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
658

dbarbera's avatar
dbarbera committed
659
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
660
661
662
663
664
665
666
667
668
669

    # 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('')

670
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
671

dbarbera's avatar
dbarbera committed
672
673
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
674
    seqof_struct_ptr = expression(loop['list'])
675
676
677

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

dbarbera's avatar
dbarbera committed
682
    element_typ = array_ptr.type.pointee.element
683
684
685

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

dbarbera's avatar
dbarbera committed
690
691
    var_ptr = ctx.builder.alloca(element_typ, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
692

dbarbera's avatar
dbarbera committed
693
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
694
695

    # load block
dbarbera's avatar
dbarbera committed
696
697
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
698
    if element_typ.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
699
        generate_assign(var_ptr, ctx.builder.gep(array_ptr, [ctx.zero, idx_var]))
700
    else:
dbarbera's avatar
dbarbera committed
701
702
        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
703
704

    # body block
dbarbera's avatar
dbarbera committed
705
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
706
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
707
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
708
709

    # cond block
dbarbera's avatar
dbarbera committed
710
711
712
713
714
    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
715

dbarbera's avatar
dbarbera committed
716
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
717

718
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
719

dbarbera's avatar
dbarbera committed
720

dbarbera's avatar
dbarbera committed
721
722
723
@singledispatch
def reference(prim):
    ''' Generate a variable reference '''
dbarbera's avatar
flake8    
dbarbera committed
724
    raise TypeError('Unsupported reference: ' + str(prim))
dbarbera's avatar
dbarbera committed
725
726
727
728
729


@reference.register(ogAST.PrimVariable)
def _prim_var_reference(prim):
    ''' Generate a primary variable reference '''
dbarbera's avatar
dbarbera committed
730
    return ctx.scope.resolve(str(prim.value[0]))
dbarbera's avatar
dbarbera committed
731
732
733
734
735


@reference.register(ogAST.PrimPath)
def _prim_path_reference(prim):
    ''' Generate a primary path reference '''
736
    var_name = prim.value[0].lower()
dbarbera's avatar
dbarbera committed
737
    var_ptr = ctx.scope.resolve(str(var_name))
dbarbera's avatar
dbarbera committed
738
739
740
741

    if not prim.value:
        return var_ptr

dbarbera's avatar
dbarbera committed
742
743
744
745
    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
746
                array_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.zero])
747
                # TODO: Refactor this
dbarbera's avatar
dbarbera committed
748
749
                if array_ptr.type.pointee.kind != core.TYPE_ARRAY:
                    # If is not an array this is a pointer to a variable size SeqOf
750
                    # The array is in the second field of the struct
dbarbera's avatar
dbarbera committed
751
                    var_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.one, idx_val])
752
                else:
dbarbera's avatar
dbarbera committed
753
                    var_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.zero, idx_val])
dbarbera's avatar
dbarbera committed
754
755
756
757
            else:
                raise NotImplementedError
        else:
            var_ty = var_ptr.type
dbarbera's avatar
dbarbera committed
758
759
760
761
            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
762
                var_ptr = field_ptr
dbarbera's avatar
dbarbera committed
763
764
            elif var_ty.pointee.name in ctx.unions:
                union = ctx.unions[var_ty.pointee.name]
dbarbera's avatar
dbarbera committed
765
                _, field_ty = union.kind(elem)
dbarbera's avatar
dbarbera committed
766
767
                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
768
769
            else:
                raise NotImplementedError
dbarbera's avatar
dbarbera committed
770
771
    return var_ptr

Maxime Perrotin's avatar
Maxime Perrotin committed
772
773
774

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
775
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
776
777
778
779
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
780
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
781
782
    ''' Generate the code for a variable expression '''
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
783
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
784
785


Maxime Perrotin's avatar
Maxime Perrotin committed
786
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
787
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
788
    ''' Generate the code for an of path expression '''
789
790
791
792
793
794
795
796
797
    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
798
799
800
801
802
803
804
    name = prim.value[0].lower()
    if name in specops_generators:
        generator = specops_generators[name]
        return generator(prim.value[1]['procParams'])

    return generate_access(prim)

805

dbarbera's avatar
dbarbera committed
806
807
def generate_access(prim):
    ''' Generate the code for an access '''
dbarbera's avatar
dbarbera committed
808
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
809
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
810
811


812
def generate_length(params):
dbarbera's avatar
dbarbera committed
813
    ''' Generate the code for the built-in length operation'''
dbarbera's avatar
dbarbera committed
814
    seq_ptr = reference(params[0])
dbarbera's avatar
dbarbera committed
815
    arr_ty = seq_ptr.type.pointee.elements[0]
816
817
818
819
    if arr_ty.kind != core.TYPE_ARRAY:
        # If is not an array this is a pointer to a variable size SeqOf
        # The array is in the second field of the struct
        arr_ty = seq_ptr.type.pointee.elements[1]
dbarbera's avatar
dbarbera committed
820
    return core.Constant.int(ctx.i32, arr_ty.count)
821
822
823


def generate_present(params):
dbarbera's avatar
dbarbera committed
824
    ''' Generate the code for the built-in present operation'''
825
826
827
    expr_val = expression(params[0])
    kind_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.zero])
    return ctx.builder.load(kind_ptr)
828
829
830


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

dbarbera's avatar
dbarbera committed
834
    if expr_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
835
836
837
        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
838
    else:
dbarbera's avatar
dbarbera committed
839
        return ctx.builder.call(ctx.funcs['fabs'], [expr_val])
840
841
842


def generate_fix(params):
dbarbera's avatar
dbarbera committed
843
    ''' Generate the code for the built-in fix operation'''
dbarbera's avatar
dbarbera committed
844
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
845
    return ctx.builder.fptosi(expr_val, ctx.i32)
846
847
848


def generate_float(params):
dbarbera's avatar
dbarbera committed
849
    ''' Generate the code for the built-in float operation'''
dbarbera's avatar
dbarbera committed
850
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
851
    return ctx.builder.sitofp(expr_val, ctx.double)
852
853
854


def generate_power(params):
dbarbera's avatar
dbarbera committed
855
    ''' Generate the code for the built-in power operation'''
856
857
858
859
    left_val = expression(params[0])
    right_val = expression(params[1])

    if left_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
860
861
862
        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)
863
    else:
dbarbera's avatar
dbarbera committed
864
        return ctx.builder.call(ctx.funcs['powi'], [left_val, right_val])
865
866


Maxime Perrotin's avatar
Maxime Perrotin committed
867
868
869
870
871
872
873
874
875
876
877
878
@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
879
880
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
881
882
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
883

884
885
    if lefttmp.type.kind == core.TYPE_INTEGER:
        if expr.operand == '+':
dbarbera's avatar
dbarbera committed
886
            return ctx.builder.add(lefttmp, righttmp, 'addtmp')
887
        elif expr.operand == '-':
dbarbera's avatar
dbarbera committed
888
            return ctx.builder.sub(lefttmp, righttmp, 'subtmp')