LlvmGenerator.py 48.7 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
        self.lltypes = {}
52
53
54
55
56
57
58
59
60
61
62
63
64
65

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

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

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

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

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

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

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

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
    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:
146
            struct = self.decl_struct(['nCount', 'arr'], [ctx.i32, array_ty], name)
147
        else:
148
            struct = self.decl_struct(['arr'], [array_ty], name)
149
150
151
152
153
154
155
156
157
158

        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))
159
        struct = self.decl_struct(field_names, field_types, name)
160
161
162
163
164
165
166

        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
167
168
169
        for field_name, field_ty in choice_ty.Children.viewitems():
            field_names.append(field_name)
            field_types.append(self.type_of(field_ty.type))
170

171
        union = self.decl_union(field_names, field_types, name)
172
173
174
175
176
177
        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)
178
        struct = self.decl_struct(['nCount', 'arr'], [ctx.i32, arr_ty], name)
179
180
        return struct.ty

181
182
183
184
185
186
187
188
189
190
191
192
    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])

193
194
195
196
197
198
199
200
201
202
203
    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
204
        name = name.replace('-', '_')
205
206
207
208
        struct = StructType(name, field_names, field_types)
        self.structs[name] = struct
        return struct

dbarbera's avatar
dbarbera committed
209
210
211
212
    def resolve_struct(self, name):
        ''' Return the struct associated to a name '''
        return self.structs[name.replace('-', '_')]

213
214
    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
215
        name = name.replace('-', '_')
216
217
218
219
        union = UnionType(name, field_names, field_types)
        self.unions[name] = union
        return union

dbarbera's avatar
dbarbera committed
220
221
222
223
    def resolve_union(self, name):
        ''' Return the union associated to a name '''
        return self.unions[name.replace('-', '_')]

dbarbera's avatar
dbarbera committed
224

225
226
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
227
        self.name = name
228
229
230
231
232
        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
233
234


dbarbera's avatar
dbarbera committed
235
236
237
238
239
240
241
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
242
243
        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
244
245
246
247
248
249

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


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

dbarbera's avatar
dbarbera committed
268
269
270
271
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
272
            func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
273
274
275
276
            label_block = func.append_basic_block(name)
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
277

Maxime Perrotin's avatar
Maxime Perrotin committed
278
279
280
281
@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
282

dbarbera's avatar
dbarbera committed
283

Maxime Perrotin's avatar
Maxime Perrotin committed
284
285
286
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
287
    ''' Generate LLVM IR code '''
dbarbera's avatar
dbarbera committed
288
289
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
290

dbarbera's avatar
dbarbera committed
291
292
    global ctx
    ctx = Context(process)
293

dbarbera's avatar
dbarbera committed
294
295
296
297
298
299
    # 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
300

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

310
    # Generate state var
dbarbera's avatar
dbarbera committed
311
312
313
    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)
314

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

dbarbera's avatar
dbarbera committed
322
323
324
    # Declare timer set/reset functions
    for timer in process.timers:
        # TODO: Should be uint?
325
326
        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
327

328
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
329
    for signal in process.output_signals:
330
        if 'type' in signal:
331
            param_tys = [core.Type.pointer(ctx.type_of(signal['type']))]
332
333
        else:
            param_tys = []
334
        ctx.decl_func(str(signal['name']), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
335

336
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
337
    for proc in [proc for proc in process.procedures if proc.external]:
338
        param_tys = [core.Type.pointer(ctx.type_of(p['type'])) for p in proc.fpar]
339
        ctx.decl_func(str(proc.inputString), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
340

341
342
    # Generate internal procedures
    for proc in process.content.inner_procedures:
343
        generate(proc)
344

345
    # Generate process functions
dbarbera's avatar
dbarbera committed
346
347
    generate_runtr_func(process)
    generate_startup_func(process)
348

349
350
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
351
        generate_input_signal(signal, mapping[signal['name']])
352

dbarbera's avatar
dbarbera committed
353
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
354

dbarbera's avatar
dbarbera committed
355
356
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
357
358


dbarbera's avatar
dbarbera committed
359
def generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
360
    ''' Generate code for the run_transition function '''
361
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
362

363
    ctx.open_scope()
364

365
366
367
368
369
    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
370
    ctx.builder = core.Builder.new(entry_block)
371
372

    # entry
dbarbera's avatar
dbarbera committed
373
374
375
376
    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)
377
378

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

    # body
dbarbera's avatar
dbarbera committed
386
387
    ctx.builder.position_at_end(body_block)
    switch = ctx.builder.switch(func.args[0], exit_block)
388
389
390
391

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

    # exit
dbarbera's avatar
dbarbera committed
400
401
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
402

dbarbera's avatar
dbarbera committed
403
404
405
406
407
    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
408
409
410
    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
411

412
    ctx.close_scope()
413

414
415
416
417
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
418
def generate_startup_func(process):
dbarbera's avatar
dbarbera committed
419
    ''' Generate code for the startup function '''
420
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
421

422
    ctx.open_scope()
423

424
    entry_block = func.append_basic_block('entry')
dbarbera's avatar
dbarbera committed
425
    ctx.builder = core.Builder.new(entry_block)
426

427
428
429
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
430
            global_var = ctx.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
431
            generate_assign(global_var, expression(expr))
432

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

436
    ctx.close_scope()
437

438
439
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
440
441


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

449
    func = ctx.decl_func(func_name, ctx.void, param_tys)
450

451
    ctx.open_scope()
452

453
454
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
dbarbera's avatar
dbarbera committed
455
    ctx.builder = core.Builder.new(entry_block)
456

dbarbera's avatar
dbarbera committed
457
458
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('state'))
    switch = ctx.builder.switch(g_state_val, exit_block)
459

dbarbera's avatar
dbarbera committed
460
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
461
462
        if state_name.endswith('START'):
            continue
463
464
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
465
        ctx.builder.position_at_end(state_block)
466
467
468

        # TODO: Nested states

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

dbarbera's avatar
dbarbera committed
481
        ctx.builder.ret_void()
482

dbarbera's avatar
dbarbera committed
483
484
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
485

486
    ctx.close_scope()
487

488
489
490
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
491
492
493
494
@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
495
496
497
    for out in output.output:
        name = out['outputName'].lower()

498
        if name == 'write':
dbarbera's avatar
dbarbera committed
499
            generate_write(out['params'])
dbarbera's avatar
dbarbera committed
500
            continue
501
        elif name == 'writeln':
dbarbera's avatar
dbarbera committed
502
            generate_writeln(out['params'])
503
            continue
dbarbera's avatar
dbarbera committed
504
        elif name == 'reset_timer':
dbarbera's avatar
dbarbera committed
505
            generate_reset_timer(out['params'])
dbarbera's avatar
dbarbera committed
506
507
            continue
        elif name == 'set_timer':
dbarbera's avatar
dbarbera committed
508
            generate_set_timer(out['params'])
dbarbera's avatar
dbarbera committed
509
510
            continue

dbarbera's avatar
dbarbera committed
511
        func = ctx.funcs[str(name).lower()]
512
513
514
515
516
517

        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
518
519
                p_var = ctx.builder.alloca(p_val.type, None)
                ctx.builder.store(p_val, p_var)
520
521
522
523
                params.append(p_var)
            else:
                params.append(p_val)

dbarbera's avatar
dbarbera committed
524
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
525
526


dbarbera's avatar
dbarbera committed
527
def generate_write(params):
dbarbera's avatar
dbarbera committed
528
    ''' Generate the code for the write operator '''
529
530
531
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
532

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


dbarbera's avatar
dbarbera committed
552
def generate_writeln(params):
553
    ''' Generate the code for the writeln operator '''
dbarbera's avatar
dbarbera committed
554
    generate_write(params)
555

556
    str_ptr = ctx.string_ptr('\n')
dbarbera's avatar
dbarbera committed
557
    ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
558
559


dbarbera's avatar
dbarbera committed
560
def generate_reset_timer(params):
dbarbera's avatar
dbarbera committed
561
    ''' Generate the code for the reset timer operator '''
dbarbera's avatar
dbarbera committed
562
    timer_id = params[0]
563
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
564
    reset_func = ctx.funcs[reset_func_name.lower()]
dbarbera's avatar
dbarbera committed
565

dbarbera's avatar
dbarbera committed
566
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
567
568


dbarbera's avatar
dbarbera committed
569
def generate_set_timer(params):
dbarbera's avatar
dbarbera committed
570
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
571
    timer_expr, timer_id = params
572
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
573
    set_func = ctx.funcs[set_func_name.lower()]
dbarbera's avatar
dbarbera committed
574
575
576

    expr_val = expression(timer_expr)

dbarbera's avatar
dbarbera committed
577
578
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
579

dbarbera's avatar
dbarbera committed
580
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
581
582
583
584


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
585
    ''' Generate the code of a list of assignments '''
586
587
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
588
589
590
591
592


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
593
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
594
595
596
597


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
598
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
599
600
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
601
            generate_for_range(loop)
dbarbera's avatar
dbarbera committed
602
        else:
dbarbera's avatar
dbarbera committed
603
            generate_for_iterable(loop)
dbarbera's avatar
dbarbera committed
604
605


dbarbera's avatar
dbarbera committed
606
def generate_for_range(loop):
dbarbera's avatar
dbarbera committed
607
    ''' Generate the code for a for x in range loop '''
dbarbera's avatar
dbarbera committed
608
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
609
610
611
612
613
    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('')

614
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
615

dbarbera's avatar
dbarbera committed
616
617
    loop_var = ctx.builder.alloca(ctx.i32, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
618
619
620

    if loop['range']['start']:
        start_val = expression(loop['range']['start'])
dbarbera's avatar
dbarbera committed
621
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
622
    else:
dbarbera's avatar
dbarbera committed
623
        ctx.builder.store(core.Constant.int(ctx.i32, 0), loop_var)
dbarbera's avatar
dbarbera committed
624
625

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

dbarbera's avatar
dbarbera committed
628
629
630
631
    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
632

dbarbera's avatar
dbarbera committed
633
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
634
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
635
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
636

dbarbera's avatar
dbarbera committed
637
638
639
640
641
642
    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
643

dbarbera's avatar
dbarbera committed
644
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
645

646
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
647
648


dbarbera's avatar
dbarbera committed
649
def generate_for_iterable(loop):
dbarbera's avatar
dbarbera committed
650
    ''' Generate the code for a for x in iterable loop'''
dbarbera's avatar
dbarbera committed
651
    seqof_asn1ty = find_basic_type(loop['list'].exprType)
652
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
653

dbarbera's avatar
dbarbera committed
654
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
655
656
657
658
659
660
661
662
663
664

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

665
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
666

dbarbera's avatar
dbarbera committed
667
668
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
669
    seqof_struct_ptr = expression(loop['list'])
670
671
672

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

dbarbera's avatar
dbarbera committed
677
    element_typ = array_ptr.type.pointee.element
678
679
680

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

dbarbera's avatar
dbarbera committed
685
686
    var_ptr = ctx.builder.alloca(element_typ, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
687

dbarbera's avatar
dbarbera committed
688
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
689
690

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

    # body block
dbarbera's avatar
dbarbera committed
700
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
701
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
702
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
703
704

    # cond block
dbarbera's avatar
dbarbera committed
705
706
707
708
709
    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
710

dbarbera's avatar
dbarbera committed
711
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
712

713
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
714

dbarbera's avatar
dbarbera committed
715

dbarbera's avatar
dbarbera committed
716
717
718
@singledispatch
def reference(prim):
    ''' Generate a variable reference '''
dbarbera's avatar
flake8    
dbarbera committed
719
    raise TypeError('Unsupported reference: ' + str(prim))
dbarbera's avatar
dbarbera committed
720
721
722
723
724


@reference.register(ogAST.PrimVariable)
def _prim_var_reference(prim):
    ''' Generate a primary variable reference '''
dbarbera's avatar
dbarbera committed
725
    return ctx.scope.resolve(str(prim.value[0]))
dbarbera's avatar
dbarbera committed
726
727
728
729
730


@reference.register(ogAST.PrimPath)
def _prim_path_reference(prim):
    ''' Generate a primary path reference '''
731
    var_name = prim.value[0].lower()
dbarbera's avatar
dbarbera committed
732
    var_ptr = ctx.scope.resolve(str(var_name))
dbarbera's avatar
dbarbera committed
733
734
735
736

    if not prim.value:
        return var_ptr

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

Maxime Perrotin's avatar
Maxime Perrotin committed
767
768
769

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
770
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
771
772
773
774
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
775
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
776
777
    ''' Generate the code for a variable expression '''
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
778
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
779
780


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

    return generate_access(prim)

800

dbarbera's avatar
dbarbera committed
801
802
def generate_access(prim):
    ''' Generate the code for an access '''
dbarbera's avatar
dbarbera committed
803
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
804
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
805
806


807
def generate_length(params):
dbarbera's avatar
dbarbera committed
808
    ''' Generate the code for the built-in length operation'''
dbarbera's avatar
dbarbera committed
809
    seq_ptr = reference(params[0])
dbarbera's avatar
dbarbera committed
810
    arr_ty = seq_ptr.type.pointee.elements[0]
811
812
813
814
    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
815
    return core.Constant.int(ctx.i32, arr_ty.count)
816
817
818


def generate_present(params):
dbarbera's avatar
dbarbera committed
819
    ''' Generate the code for the built-in present operation'''
820
821
822
    expr_val = expression(params[0])
    kind_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.zero])
    return ctx.builder.load(kind_ptr)
823
824
825


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

dbarbera's avatar
dbarbera committed
829
    if expr_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
830
831
832
        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
833
    else:
dbarbera's avatar
dbarbera committed
834
        return ctx.builder.call(ctx.funcs['fabs'], [expr_val])
835
836
837


def generate_fix(params):
dbarbera's avatar
dbarbera committed
838
    ''' Generate the code for the built-in fix operation'''
dbarbera's avatar
dbarbera committed
839
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
840
    return ctx.builder.fptosi(expr_val, ctx.i32)
841
842
843


def generate_float(params):
dbarbera's avatar
dbarbera committed
844
    ''' Generate the code for the built-in float operation'''
dbarbera's avatar
dbarbera committed
845
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
846
    return ctx.builder.sitofp(expr_val, ctx.double)
847
848
849


def generate_power(params):
dbarbera's avatar
dbarbera committed
850
    ''' Generate the code for the built-in power operation'''
851
852
853
854
    left_val = expression(params[0])
    right_val = expression(params[1])

    if left_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
855
856
857
        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)
858
    else:
dbarbera's avatar
dbarbera committed
859
        return ctx.builder.call(ctx.funcs['powi'], [left_val, right_val])
860
861


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

879
880
    if lefttmp.type.kind == core.TYPE_INTEGER:
        if expr.operand == '+':
dbarbera's avatar
dbarbera committed
881
            return ctx.builder.add(lefttmp, righttmp, 'addtmp')
882
        elif expr.operand == '-':
dbarbera's avatar
dbarbera committed
883
            return ctx.builder.sub(lefttmp, righttmp, 'subtmp')
884
        elif expr.operand == '*':
dbarbera's avatar
dbarbera committed
885
            return ctx.builder.mul(lefttmp, righttmp, 'multmp')
886
        elif expr.operand == '/':
dbarbera's avatar
dbarbera committed
887
            return ctx.builder.sdiv(lefttmp, righttmp, 'divtmp')
888
        elif expr.operand == 'mod':