LlvmGenerator.py 59.4 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
    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)

dbarbera's avatar
dbarbera committed
113
114
115
        if basic_asn1ty.kind == 'IntegerType':
            llty = ctx.i64
        elif basic_asn1ty.kind == 'Integer32Type':
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
            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)
131
132
        elif basic_asn1ty.kind == 'StringType':
            llty = ctx.i8_ptr
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
        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:
151
            struct = self.decl_struct(['nCount', 'arr'], [ctx.i32, array_ty], name)
152
        else:
153
            struct = self.decl_struct(['arr'], [array_ty], name)
154

155
156
157
        struct_ptr = core.Type.pointer(struct.ty)
        self.decl_func("%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])

158
159
160
161
162
163
        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 = []
164

165
166
167
        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))
168

169
        struct = self.decl_struct(field_names, field_types, name)
170

171
172
173
        struct_ptr = core.Type.pointer(struct.ty)
        self.decl_func("%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])

174
175
176
177
178
179
        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
180
181
182

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

dbarbera's avatar
dbarbera committed
185
            field_names.append(field_name.replace('-', '_'))
dbarbera's avatar
dbarbera committed
186
            field_types.append(self.type_of(choice_ty.Children[field_name].type))
187

188
        union = self.decl_union(field_names, field_types, name)
189
190
191
192

        union_ptr = core.Type.pointer(union.ty)
        self.decl_func("%s_Equal" % name, self.i1, [union_ptr, union_ptr])

193
194
195
196
        return union.ty

    def _type_of_octetstring(self, name, octetstring_ty):
        ''' Return the equivalent LL type of a OcterString ASN.1 type '''
197
        min_size = int(octetstring_ty.Min)
198
        max_size = int(octetstring_ty.Max)
199
200
201
202
203
204
205
206
        is_variable_size = min_size != max_size

        array_ty = core.Type.array(ctx.i8, max_size)

        if is_variable_size:
            struct = self.decl_struct(['nCount', 'arr'], [ctx.i32, array_ty], name)
        else:
            struct = self.decl_struct(['arr'], [array_ty], name)
207
208
209
210

        struct_ptr = core.Type.pointer(struct.ty)
        self.decl_func("%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])

211
212
        return struct.ty

213
214
215
216
217
218
219
220
221
222
223
224
    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])

225
226
227
228
229
230
231
232
233
234
235
    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
236
        name = name.replace('-', '_')
237
238
239
240
        struct = StructType(name, field_names, field_types)
        self.structs[name] = struct
        return struct

dbarbera's avatar
dbarbera committed
241
242
243
244
    def resolve_struct(self, name):
        ''' Return the struct associated to a name '''
        return self.structs[name.replace('-', '_')]

245
246
    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
247
        name = name.replace('-', '_')
248
249
250
251
        union = UnionType(name, field_names, field_types)
        self.unions[name] = union
        return union

dbarbera's avatar
dbarbera committed
252
253
254
255
    def resolve_union(self, name):
        ''' Return the union associated to a name '''
        return self.unions[name.replace('-', '_')]

dbarbera's avatar
dbarbera committed
256

257
258
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
259
        self.name = name
260
261
262
263
264
        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
265
266


dbarbera's avatar
dbarbera committed
267
268
269
270
271
272
273
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
274
275
        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
276
277
278
279
280
281

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


282
283
284
class Scope:
    def __init__(self, parent=None):
        self.vars = {}
dbarbera's avatar
dbarbera committed
285
        self.labels = {}
286
287
288
289
290
291
292
293
294
295
296
297
        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
298
            raise NameError("name '%s' is not defined" % name)
299

dbarbera's avatar
dbarbera committed
300
301
302
303
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
304
            func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
305
306
307
308
            label_block = func.append_basic_block(name)
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
309

Maxime Perrotin's avatar
Maxime Perrotin committed
310
311
312
313
@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
314

dbarbera's avatar
dbarbera committed
315

Maxime Perrotin's avatar
Maxime Perrotin committed
316
317
318
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
319
    ''' Generate LLVM IR code '''
dbarbera's avatar
dbarbera committed
320
321
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
322

dbarbera's avatar
dbarbera committed
323
324
    global ctx
    ctx = Context(process)
325

dbarbera's avatar
dbarbera committed
326
327
328
329
330
331
    # 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
332

333
334
    # Initialize states
    for name, val in process.mapping.viewitems():
335
        if not name.endswith('START'):
dbarbera's avatar
dbarbera committed
336
337
            cons_val = core.Constant.int(ctx.i32, len(ctx.states))
            ctx.states[name] = cons_val
338
        elif name != 'START':
dbarbera's avatar
dbarbera committed
339
340
            cons_val = core.Constant.int(ctx.i32, val)
            ctx.states[name] = cons_val
341

342
    # Generate state var
dbarbera's avatar
dbarbera committed
343
344
345
    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)
346

347
348
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
349
        var_ty = ctx.type_of(ty)
dbarbera's avatar
dbarbera committed
350
        global_var = ctx.module.add_global_variable(var_ty, str(name))
351
        global_var.initializer = core.Constant.null(var_ty)
dbarbera's avatar
dbarbera committed
352
        ctx.scope.define(str(name).lower(), global_var)
353

dbarbera's avatar
dbarbera committed
354
355
356
    # Declare timer set/reset functions
    for timer in process.timers:
        # TODO: Should be uint?
dbarbera's avatar
dbarbera committed
357
        ctx.decl_func("set_%s" % str(timer), ctx.void, [ctx.i64_ptr], True)
358
        ctx.decl_func("reset_%s" % str(timer), ctx.void, [], True)
dbarbera's avatar
dbarbera committed
359

360
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
361
    for signal in process.output_signals:
362
        if 'type' in signal:
363
            param_tys = [core.Type.pointer(ctx.type_of(signal['type']))]
364
365
        else:
            param_tys = []
366
        ctx.decl_func(str(signal['name']), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
367

368
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
369
    for proc in [proc for proc in process.procedures if proc.external]:
370
        param_tys = [core.Type.pointer(ctx.type_of(p['type'])) for p in proc.fpar]
371
        ctx.decl_func(str(proc.inputString), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
372

373
374
    # Generate internal procedures
    for proc in process.content.inner_procedures:
375
        generate(proc)
376

377
    # Generate process functions
dbarbera's avatar
dbarbera committed
378
379
    generate_runtr_func(process)
    generate_startup_func(process)
380

381
382
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
383
        generate_input_signal(signal, mapping[signal['name']])
384

dbarbera's avatar
dbarbera committed
385
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
386

dbarbera's avatar
dbarbera committed
387
388
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
389
390


dbarbera's avatar
dbarbera committed
391
def generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
392
    ''' Generate code for the run_transition function '''
393
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
394

395
    ctx.open_scope()
396

397
398
399
400
401
    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
402
    ctx.builder = core.Builder.new(entry_block)
403
404

    # entry
dbarbera's avatar
dbarbera committed
405
406
407
408
    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)
409
410

    # cond
dbarbera's avatar
dbarbera committed
411
412
413
414
415
    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)
416
417

    # body
dbarbera's avatar
dbarbera committed
418
419
    ctx.builder.position_at_end(body_block)
    switch = ctx.builder.switch(func.args[0], exit_block)
420
421
422
423

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
dbarbera's avatar
dbarbera committed
424
        const = core.Constant.int(ctx.i32, idx)
425
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
426
        ctx.builder.position_at_end(tr_block)
427
        generate(tr)
dbarbera's avatar
dbarbera committed
428
429
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
430
431

    # exit
dbarbera's avatar
dbarbera committed
432
433
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
434

dbarbera's avatar
dbarbera committed
435
436
437
438
439
    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
440
441
442
    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
443

444
    ctx.close_scope()
445

446
447
448
449
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
450
def generate_startup_func(process):
dbarbera's avatar
dbarbera committed
451
    ''' Generate code for the startup function '''
452
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
453

454
    ctx.open_scope()
455

456
    entry_block = func.append_basic_block('entry')
dbarbera's avatar
dbarbera committed
457
    ctx.builder = core.Builder.new(entry_block)
458

459
460
461
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
462
            global_var = ctx.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
463
            generate_assign(global_var, expression(expr))
464

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

468
    ctx.close_scope()
469

470
471
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
472
473


dbarbera's avatar
dbarbera committed
474
def generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
475
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
476
    func_name = ctx.name + "_" + str(signal['name'])
477
478
    param_tys = []
    if 'type' in signal:
479
        param_tys.append(core.Type.pointer(ctx.type_of(signal['type'])))
480

481
    func = ctx.decl_func(func_name, ctx.void, param_tys)
482

483
    ctx.open_scope()
484

485
486
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
dbarbera's avatar
dbarbera committed
487
    ctx.builder = core.Builder.new(entry_block)
488

dbarbera's avatar
dbarbera committed
489
490
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('state'))
    switch = ctx.builder.switch(g_state_val, exit_block)
491

dbarbera's avatar
dbarbera committed
492
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
493
494
        if state_name.endswith('START'):
            continue
495
496
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
497
        ctx.builder.position_at_end(state_block)
498
499
500

        # TODO: Nested states

501
502
503
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
504
                var_ptr = ctx.scope.resolve(str(var_name))
505
506
507
                if is_struct_ptr(var_ptr):
                    generate_assign(var_ptr, func.args[0])
                else:
dbarbera's avatar
dbarbera committed
508
                    generate_assign(var_ptr, ctx.builder.load(func.args[0]))
509
            if input.transition:
dbarbera's avatar
dbarbera committed
510
511
                id_val = core.Constant.int(ctx.i32, input.transition_id)
                ctx.builder.call(ctx.funcs['run_transition'], [id_val])
512

dbarbera's avatar
dbarbera committed
513
        ctx.builder.ret_void()
514

dbarbera's avatar
dbarbera committed
515
516
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
517

518
    ctx.close_scope()
519

520
521
522
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
523
524
525
526
@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
527
528
529
    for out in output.output:
        name = out['outputName'].lower()

530
        if name == 'write':
dbarbera's avatar
dbarbera committed
531
            generate_write(out['params'])
dbarbera's avatar
dbarbera committed
532
            continue
533
        elif name == 'writeln':
dbarbera's avatar
dbarbera committed
534
            generate_writeln(out['params'])
535
            continue
dbarbera's avatar
dbarbera committed
536
        elif name == 'reset_timer':
dbarbera's avatar
dbarbera committed
537
            generate_reset_timer(out['params'])
dbarbera's avatar
dbarbera committed
538
539
            continue
        elif name == 'set_timer':
dbarbera's avatar
dbarbera committed
540
            generate_set_timer(out['params'])
dbarbera's avatar
dbarbera committed
541
542
            continue

dbarbera's avatar
dbarbera committed
543
        func = ctx.funcs[str(name).lower()]
544
545
546
547
548
549

        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
550
551
                p_var = ctx.builder.alloca(p_val.type, None)
                ctx.builder.store(p_val, p_var)
552
553
554
555
                params.append(p_var)
            else:
                params.append(p_val)

dbarbera's avatar
dbarbera committed
556
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
557
558


dbarbera's avatar
dbarbera committed
559
def generate_write(params):
dbarbera's avatar
dbarbera committed
560
    ''' Generate the code for the write operator '''
561
562
563
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
564

565
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
566
567
            fmt_str_ptr = ctx.string_ptr('% d')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
568
        elif basic_ty.kind == 'RealType':
569
570
            fmt_str_ptr = ctx.string_ptr('% .14E')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
571
        elif basic_ty.kind == 'BooleanType':
572
573
            true_str_ptr = ctx.string_ptr('TRUE')
            false_str_ptr = ctx.string_ptr('FALSE')
dbarbera's avatar
dbarbera committed
574
575
            str_ptr = ctx.builder.select(expr_val, true_str_ptr, false_str_ptr)
            ctx.builder.call(ctx.funcs['printf'], [str_ptr])
576
577
578
579
        elif basic_ty.kind == 'StringType':
            fmt_str_ptr = ctx.string_ptr('%s')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
        elif basic_ty.kind == 'OctetStringType':
580
            fmt_str_ptr = ctx.string_ptr('%.*s')
581
582
583
584
585
586
            if basic_ty.Min == basic_ty.Max:
                arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.zero])
                count_val = core.Constant.int(ctx.i32, arr_ptr.type.pointee.count)
            else:
                count_val = ctx.builder.load(ctx.builder.gep(expr_val, [ctx.zero, ctx.zero]))
                arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.one])
587
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, count_val, arr_ptr])
588
589
590
591
        else:
            raise NotImplementedError


dbarbera's avatar
dbarbera committed
592
def generate_writeln(params):
593
    ''' Generate the code for the writeln operator '''
dbarbera's avatar
dbarbera committed
594
    generate_write(params)
595

596
    str_ptr = ctx.string_ptr('\n')
dbarbera's avatar
dbarbera committed
597
    ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
598
599


dbarbera's avatar
dbarbera committed
600
def generate_reset_timer(params):
dbarbera's avatar
dbarbera committed
601
    ''' Generate the code for the reset timer operator '''
dbarbera's avatar
dbarbera committed
602
    timer_id = params[0]
603
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
604
    reset_func = ctx.funcs[reset_func_name.lower()]
dbarbera's avatar
dbarbera committed
605

dbarbera's avatar
dbarbera committed
606
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
607
608


dbarbera's avatar
dbarbera committed
609
def generate_set_timer(params):
dbarbera's avatar
dbarbera committed
610
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
611
    timer_expr, timer_id = params
612
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
613
    set_func = ctx.funcs[set_func_name.lower()]
dbarbera's avatar
dbarbera committed
614
615
616

    expr_val = expression(timer_expr)

dbarbera's avatar
dbarbera committed
617
618
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
619

dbarbera's avatar
dbarbera committed
620
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
621
622
623
624


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
625
    ''' Generate the code of a list of assignments '''
626
627
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
628
629
630
631
632


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
633
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
634
635
636
637


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
638
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
639
640
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
641
            generate_for_range(loop)
dbarbera's avatar
dbarbera committed
642
        else:
dbarbera's avatar
dbarbera committed
643
            generate_for_iterable(loop)
dbarbera's avatar
dbarbera committed
644
645


dbarbera's avatar
dbarbera committed
646
def generate_for_range(loop):
dbarbera's avatar
dbarbera committed
647
    ''' Generate the code for a for x in range loop '''
dbarbera's avatar
dbarbera committed
648
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
649
650
651
652
653
    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('')

654
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
655

dbarbera's avatar
dbarbera committed
656
    loop_var = ctx.builder.alloca(ctx.i64, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
657
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
658
659
660

    if loop['range']['start']:
        start_val = expression(loop['range']['start'])
dbarbera's avatar
dbarbera committed
661
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
662
    else:
dbarbera's avatar
dbarbera committed
663
        ctx.builder.store(core.Constant.int(ctx.i64, 0), loop_var)
dbarbera's avatar
dbarbera committed
664
665

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

dbarbera's avatar
dbarbera committed
668
669
670
671
    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
672

dbarbera's avatar
dbarbera committed
673
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
674
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
675
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
676

dbarbera's avatar
dbarbera committed
677
    ctx.builder.position_at_end(inc_block)
dbarbera's avatar
dbarbera committed
678
    step_val = core.Constant.int(ctx.i64, loop['range']['step'])
dbarbera's avatar
dbarbera committed
679
680
681
682
    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
683

dbarbera's avatar
dbarbera committed
684
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
685

686
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
687
688


dbarbera's avatar
dbarbera committed
689
def generate_for_iterable(loop):
dbarbera's avatar
dbarbera committed
690
    ''' Generate the code for a for x in iterable loop'''
dbarbera's avatar
dbarbera committed
691
    seqof_asn1ty = find_basic_type(loop['list'].exprType)
692
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
693

dbarbera's avatar
dbarbera committed
694
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
695
696
697
698
699
700
701
702
703
704

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

705
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
706

dbarbera's avatar
dbarbera committed
707
708
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
709
    seqof_struct_ptr = expression(loop['list'])
710
711
712

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

dbarbera's avatar
dbarbera committed
717
    element_typ = array_ptr.type.pointee.element
718
719
720

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

dbarbera's avatar
dbarbera committed
725
726
    var_ptr = ctx.builder.alloca(element_typ, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
727

dbarbera's avatar
dbarbera committed
728
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
729
730

    # load block
dbarbera's avatar
dbarbera committed
731
732
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
733
    if element_typ.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
734
        generate_assign(var_ptr, ctx.builder.gep(array_ptr, [ctx.zero, idx_var]))
735
    else:
dbarbera's avatar
dbarbera committed
736
737
        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
738
739

    # body block
dbarbera's avatar
dbarbera committed
740
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
741
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
742
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
743
744

    # cond block
dbarbera's avatar
dbarbera committed
745
746
747
748
749
    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
750

dbarbera's avatar
dbarbera committed
751
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
752

753
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
754

dbarbera's avatar
dbarbera committed
755

dbarbera's avatar
dbarbera committed
756
757
@singledispatch
def reference(prim):
758
    ''' Generate a reference '''
dbarbera's avatar
flake8    
dbarbera committed
759
    raise TypeError('Unsupported reference: ' + str(prim))
dbarbera's avatar
dbarbera committed
760
761
762
763


@reference.register(ogAST.PrimVariable)
def _prim_var_reference(prim):
764
    ''' Generate a variable reference '''
dbarbera's avatar
dbarbera committed
765
    return ctx.scope.resolve(str(prim.value[0]))
dbarbera's avatar
dbarbera committed
766
767


768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
@reference.register(ogAST.PrimSelector)
def _prim_selector_reference(prim):
    ''' Generate a field selector referece '''
    receiver_ptr = reference(prim.value[0])
    field_name = prim.value[1]

    if receiver_ptr.type.pointee.name in ctx.structs:
        struct = ctx.structs[receiver_ptr.type.pointee.name]
        field_idx_cons = core.Constant.int(ctx.i32, struct.idx(field_name))
        return ctx.builder.gep(receiver_ptr, [ctx.zero, field_idx_cons])

    else:
        union = ctx.unions[receiver_ptr.type.pointee.name]
        _, field_ty = union.kind(field_name)
        field_ptr = ctx.builder.gep(receiver_ptr, [ctx.zero, ctx.one])
        return ctx.builder.bitcast(field_ptr, core.Type.pointer(field_ty))


@reference.register(ogAST.PrimIndex)
def _prim_index_reference(prim):
    ''' Generate an index reference '''
    receiver_ptr = reference(prim.value[0])
    idx_val = expression(prim.value[1]['index'][0])

    array_ptr = ctx.builder.gep(receiver_ptr, [ctx.zero, ctx.zero])

    # TODO: Refactor this
    if array_ptr.type.pointee.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
        return ctx.builder.gep(receiver_ptr, [ctx.zero, ctx.one, idx_val])
    else:
        return ctx.builder.gep(receiver_ptr, [ctx.zero, ctx.zero, idx_val])
dbarbera's avatar
dbarbera committed
801

Maxime Perrotin's avatar
Maxime Perrotin committed
802
803
804

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
805
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
806
807
808
809
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
810
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
811
812
    ''' Generate the code for a variable expression '''
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
813
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
814
815


816
817
818
819
820
@expression.register(ogAST.PrimSelector)
def _primary_selector(prim):
    ''' Generate the code for a Selector expression '''
    var_ptr = reference(prim)
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
dbarbera's avatar
dbarbera committed
821

822

823
824
825
@expression.register(ogAST.PrimIndex)
def _primary_index(prim):
    ''' Generate the code for an Index expression '''
dbarbera's avatar
dbarbera committed
826
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
827
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
828
829


830
831
832
@expression.register(ogAST.PrimSubstring)
def _primary_substring(prim):
    ''' Generate the code for a Substring expression '''
dbarbera's avatar
dbarbera committed
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
    bty = find_basic_type(prim.exprType)
    if bty.Min == bty.Max:
        raise NotImplementedError

    range_l_val = expression(prim.value[1]['substring'][0])
    range_r_val = expression(prim.value[1]['substring'][1])
    len_val = ctx.builder.sub(range_r_val, range_l_val)

    recvr_ptr = expression(prim.value[0])
    recvr_arr_ptr = ctx.builder.gep(recvr_ptr, [ctx.zero, ctx.one, range_l_val])

    recvr_ty = recvr_ptr.type.pointee
    elem_ty = recvr_ty.elements[1].element

    res_ptr = ctx.builder.alloca(recvr_ty)
    res_len_ptr = ctx.builder.gep(res_ptr, [ctx.zero, ctx.zero])
    res_arr_ptr = ctx.builder.gep(res_ptr, [ctx.zero, ctx.one])

    ctx.builder.store(ctx.builder.trunc(len_val, ctx.i32), res_len_ptr)

    elem_size_val = core.Constant.sizeof(elem_ty)

    size = ctx.builder.mul(elem_size_val, len_val)
    align = core.Constant.int(ctx.i32, 0)
    volatile = core.Constant.int(ctx.i1, 0)

    recvr_arr_ptr = ctx.builder.bitcast(recvr_arr_ptr, ctx.i8_ptr)
    res_arr_ptr = ctx.builder.bitcast(res_arr_ptr, ctx.i8_ptr)

    ctx.builder.call(ctx.funcs['memcpy'], [res_arr_ptr, recvr_arr_ptr, size, align, volatile])

    return res_ptr
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884


@expression.register(ogAST.PrimCall)
def _primary_call(prim):
    ''' Generate the code for a builtin call expression '''
    name = prim.value[0].lower()
    args = prim.value[1]['procParams']

    if name == 'length':
        return generate_length(args)
    elif name == 'present':
        return generate_present(args)
    elif name == 'abs':
        return generate_abs(args)
    elif name == 'fix':
        return generate_fix(args)
    elif name == 'float':
        return generate_float(args)
    elif name == 'power':
        return generate_power(args)
dbarbera's avatar
dbarbera committed
885
886
    elif name == 'num':
        return generate_num(args)
887
888


889
def generate_length(params):
dbarbera's avatar
dbarbera committed
890
    ''' Generate the code for the built-in length operation'''
dbarbera's avatar
dbarbera committed
891
    seq_ptr = reference(params[0])
dbarbera's avatar
dbarbera committed
892
    arr_ty = seq_ptr.type.pointee.elements[0]
893
894
895
896
    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
897
    return core.Constant.int(ctx.i64, arr_ty.count)
898
899
900


def generate_present(params):
dbarbera's avatar
dbarbera committed
901
    ''' Generate the code for the built-in present operation'''
902
903
904
    expr_val = expression(params[0])
    kind_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.zero])
    return ctx.builder.load(kind_ptr)
905
906
907


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

dbarbera's avatar
dbarbera committed
911
    if expr_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
912
913
        expr_conv = ctx.builder.sitofp(expr_val, ctx.double)
        res_val = ctx.builder.call(ctx.funcs['fabs'], [expr_conv])
dbarbera's avatar
dbarbera committed
914
        return ctx.builder.fptosi(res_val, ctx.i64)
dbarbera's avatar
dbarbera committed
915
    else:
dbarbera's avatar
dbarbera committed
916
        return ctx.builder.call(ctx.funcs['fabs'], [expr_val])
917
918
919


def generate_fix(params):
dbarbera's avatar
dbarbera committed
920
    ''' Generate the code for the built-in fix operation'''
dbarbera's avatar
dbarbera committed
921
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
922
    return ctx.builder.fptosi(expr_val, ctx.i64)
923
924
925


def generate_float(params):