LlvmGenerator.py 58.1 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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
            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:
149
            struct = self.decl_struct(['nCount', 'arr'], [ctx.i32, array_ty], name)
150
        else:
151
            struct = self.decl_struct(['arr'], [array_ty], name)
152

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

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

163
164
165
        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))
166

167
        struct = self.decl_struct(field_names, field_types, name)
168

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

172
173
174
175
176
177
        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
178
179
180

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

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

186
        union = self.decl_union(field_names, field_types, name)
187
188
189
190

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

191
192
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 '''
        max_size = int(octetstring_ty.Max)
        arr_ty = core.Type.array(ctx.i8, max_size)
197
        struct = self.decl_struct(['nCount', 'arr'], [ctx.i32, arr_ty], name)
198
199
200
201

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

202
203
        return struct.ty

204
205
206
207
208
209
210
211
212
213
214
215
    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])

216
217
218
219
220
221
222
223
224
225
226
    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
227
        name = name.replace('-', '_')
228
229
230
231
        struct = StructType(name, field_names, field_types)
        self.structs[name] = struct
        return struct

dbarbera's avatar
dbarbera committed
232
233
234
235
    def resolve_struct(self, name):
        ''' Return the struct associated to a name '''
        return self.structs[name.replace('-', '_')]

236
237
    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
238
        name = name.replace('-', '_')
239
240
241
242
        union = UnionType(name, field_names, field_types)
        self.unions[name] = union
        return union

dbarbera's avatar
dbarbera committed
243
244
245
246
    def resolve_union(self, name):
        ''' Return the union associated to a name '''
        return self.unions[name.replace('-', '_')]

dbarbera's avatar
dbarbera committed
247

248
249
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
250
        self.name = name
251
252
253
254
255
        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
256
257


dbarbera's avatar
dbarbera committed
258
259
260
261
262
263
264
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
265
266
        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
267
268
269
270
271
272

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


273
274
275
class Scope:
    def __init__(self, parent=None):
        self.vars = {}
dbarbera's avatar
dbarbera committed
276
        self.labels = {}
277
278
279
280
281
282
283
284
285
286
287
288
        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
289
            raise NameError("name '%s' is not defined" % name)
290

dbarbera's avatar
dbarbera committed
291
292
293
294
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
295
            func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
296
297
298
299
            label_block = func.append_basic_block(name)
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
300

Maxime Perrotin's avatar
Maxime Perrotin committed
301
302
303
304
@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
305

dbarbera's avatar
dbarbera committed
306

Maxime Perrotin's avatar
Maxime Perrotin committed
307
308
309
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
310
    ''' Generate LLVM IR code '''
dbarbera's avatar
dbarbera committed
311
312
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
313

dbarbera's avatar
dbarbera committed
314
315
    global ctx
    ctx = Context(process)
316

dbarbera's avatar
dbarbera committed
317
318
319
320
321
322
    # 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
323

324
325
    # Initialize states
    for name, val in process.mapping.viewitems():
326
        if not name.endswith('START'):
dbarbera's avatar
dbarbera committed
327
328
            cons_val = core.Constant.int(ctx.i32, len(ctx.states))
            ctx.states[name] = cons_val
329
        elif name != 'START':
dbarbera's avatar
dbarbera committed
330
331
            cons_val = core.Constant.int(ctx.i32, val)
            ctx.states[name] = cons_val
332

333
    # Generate state var
dbarbera's avatar
dbarbera committed
334
335
336
    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)
337

338
339
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
340
        var_ty = ctx.type_of(ty)
dbarbera's avatar
dbarbera committed
341
        global_var = ctx.module.add_global_variable(var_ty, str(name))
342
        global_var.initializer = core.Constant.null(var_ty)
dbarbera's avatar
dbarbera committed
343
        ctx.scope.define(str(name).lower(), global_var)
344

dbarbera's avatar
dbarbera committed
345
346
347
    # Declare timer set/reset functions
    for timer in process.timers:
        # TODO: Should be uint?
dbarbera's avatar
dbarbera committed
348
        ctx.decl_func("set_%s" % str(timer), ctx.void, [ctx.i64_ptr], True)
349
        ctx.decl_func("reset_%s" % str(timer), ctx.void, [], True)
dbarbera's avatar
dbarbera committed
350

351
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
352
    for signal in process.output_signals:
353
        if 'type' in signal:
354
            param_tys = [core.Type.pointer(ctx.type_of(signal['type']))]
355
356
        else:
            param_tys = []
357
        ctx.decl_func(str(signal['name']), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
358

359
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
360
    for proc in [proc for proc in process.procedures if proc.external]:
361
        param_tys = [core.Type.pointer(ctx.type_of(p['type'])) for p in proc.fpar]
362
        ctx.decl_func(str(proc.inputString), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
363

364
365
    # Generate internal procedures
    for proc in process.content.inner_procedures:
366
        generate(proc)
367

368
    # Generate process functions
dbarbera's avatar
dbarbera committed
369
370
    generate_runtr_func(process)
    generate_startup_func(process)
371

372
373
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
374
        generate_input_signal(signal, mapping[signal['name']])
375

dbarbera's avatar
dbarbera committed
376
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
377

dbarbera's avatar
dbarbera committed
378
379
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
380
381


dbarbera's avatar
dbarbera committed
382
def generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
383
    ''' Generate code for the run_transition function '''
384
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
385

386
    ctx.open_scope()
387

388
389
390
391
392
    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
393
    ctx.builder = core.Builder.new(entry_block)
394
395

    # entry
dbarbera's avatar
dbarbera committed
396
397
398
399
    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)
400
401

    # cond
dbarbera's avatar
dbarbera committed
402
403
404
405
406
    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)
407
408

    # body
dbarbera's avatar
dbarbera committed
409
410
    ctx.builder.position_at_end(body_block)
    switch = ctx.builder.switch(func.args[0], exit_block)
411
412
413
414

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
dbarbera's avatar
dbarbera committed
415
        const = core.Constant.int(ctx.i32, idx)
416
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
417
        ctx.builder.position_at_end(tr_block)
418
        generate(tr)
dbarbera's avatar
dbarbera committed
419
420
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
421
422

    # exit
dbarbera's avatar
dbarbera committed
423
424
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
425

dbarbera's avatar
dbarbera committed
426
427
428
429
430
    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
431
432
433
    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
434

435
    ctx.close_scope()
436

437
438
439
440
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
441
def generate_startup_func(process):
dbarbera's avatar
dbarbera committed
442
    ''' Generate code for the startup function '''
443
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
444

445
    ctx.open_scope()
446

447
    entry_block = func.append_basic_block('entry')
dbarbera's avatar
dbarbera committed
448
    ctx.builder = core.Builder.new(entry_block)
449

450
451
452
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
453
            global_var = ctx.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
454
            generate_assign(global_var, expression(expr))
455

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

459
    ctx.close_scope()
460

461
462
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
463
464


dbarbera's avatar
dbarbera committed
465
def generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
466
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
467
    func_name = ctx.name + "_" + str(signal['name'])
468
469
    param_tys = []
    if 'type' in signal:
470
        param_tys.append(core.Type.pointer(ctx.type_of(signal['type'])))
471

472
    func = ctx.decl_func(func_name, ctx.void, param_tys)
473

474
    ctx.open_scope()
475

476
477
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
dbarbera's avatar
dbarbera committed
478
    ctx.builder = core.Builder.new(entry_block)
479

dbarbera's avatar
dbarbera committed
480
481
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('state'))
    switch = ctx.builder.switch(g_state_val, exit_block)
482

dbarbera's avatar
dbarbera committed
483
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
484
485
        if state_name.endswith('START'):
            continue
486
487
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
488
        ctx.builder.position_at_end(state_block)
489
490
491

        # TODO: Nested states

492
493
494
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
495
                var_ptr = ctx.scope.resolve(str(var_name))
496
497
498
                if is_struct_ptr(var_ptr):
                    generate_assign(var_ptr, func.args[0])
                else:
dbarbera's avatar
dbarbera committed
499
                    generate_assign(var_ptr, ctx.builder.load(func.args[0]))
500
            if input.transition:
dbarbera's avatar
dbarbera committed
501
502
                id_val = core.Constant.int(ctx.i32, input.transition_id)
                ctx.builder.call(ctx.funcs['run_transition'], [id_val])
503

dbarbera's avatar
dbarbera committed
504
        ctx.builder.ret_void()
505

dbarbera's avatar
dbarbera committed
506
507
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
508

509
    ctx.close_scope()
510

511
512
513
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
514
515
516
517
@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
518
519
520
    for out in output.output:
        name = out['outputName'].lower()

521
        if name == 'write':
dbarbera's avatar
dbarbera committed
522
            generate_write(out['params'])
dbarbera's avatar
dbarbera committed
523
            continue
524
        elif name == 'writeln':
dbarbera's avatar
dbarbera committed
525
            generate_writeln(out['params'])
526
            continue
dbarbera's avatar
dbarbera committed
527
        elif name == 'reset_timer':
dbarbera's avatar
dbarbera committed
528
            generate_reset_timer(out['params'])
dbarbera's avatar
dbarbera committed
529
530
            continue
        elif name == 'set_timer':
dbarbera's avatar
dbarbera committed
531
            generate_set_timer(out['params'])
dbarbera's avatar
dbarbera committed
532
533
            continue

dbarbera's avatar
dbarbera committed
534
        func = ctx.funcs[str(name).lower()]
535
536
537
538
539
540

        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
541
542
                p_var = ctx.builder.alloca(p_val.type, None)
                ctx.builder.store(p_val, p_var)
543
544
545
546
                params.append(p_var)
            else:
                params.append(p_val)

dbarbera's avatar
dbarbera committed
547
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
548
549


dbarbera's avatar
dbarbera committed
550
def generate_write(params):
dbarbera's avatar
dbarbera committed
551
    ''' Generate the code for the write operator '''
552
553
554
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
555

556
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
557
558
            fmt_str_ptr = ctx.string_ptr('% d')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
559
        elif basic_ty.kind == 'RealType':
560
561
            fmt_str_ptr = ctx.string_ptr('% .14E')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
562
        elif basic_ty.kind == 'BooleanType':
563
564
            true_str_ptr = ctx.string_ptr('TRUE')
            false_str_ptr = ctx.string_ptr('FALSE')
dbarbera's avatar
dbarbera committed
565
566
            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
567
        elif basic_ty.kind in ['StringType', 'OctetStringType']:
568
569
            fmt_str_ptr = ctx.string_ptr('%.*s')
            count_val = ctx.builder.load(ctx.builder.gep(expr_val, [ctx.zero, ctx.zero]))
dbarbera's avatar
dbarbera committed
570
            arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.one])
571
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, count_val, arr_ptr])
572
573
574
575
        else:
            raise NotImplementedError


dbarbera's avatar
dbarbera committed
576
def generate_writeln(params):
577
    ''' Generate the code for the writeln operator '''
dbarbera's avatar
dbarbera committed
578
    generate_write(params)
579

580
    str_ptr = ctx.string_ptr('\n')
dbarbera's avatar
dbarbera committed
581
    ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
582
583


dbarbera's avatar
dbarbera committed
584
def generate_reset_timer(params):
dbarbera's avatar
dbarbera committed
585
    ''' Generate the code for the reset timer operator '''
dbarbera's avatar
dbarbera committed
586
    timer_id = params[0]
587
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
588
    reset_func = ctx.funcs[reset_func_name.lower()]
dbarbera's avatar
dbarbera committed
589

dbarbera's avatar
dbarbera committed
590
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
591
592


dbarbera's avatar
dbarbera committed
593
def generate_set_timer(params):
dbarbera's avatar
dbarbera committed
594
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
595
    timer_expr, timer_id = params
596
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
597
    set_func = ctx.funcs[set_func_name.lower()]
dbarbera's avatar
dbarbera committed
598
599
600

    expr_val = expression(timer_expr)

dbarbera's avatar
dbarbera committed
601
602
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
603

dbarbera's avatar
dbarbera committed
604
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
605
606
607
608


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
609
    ''' Generate the code of a list of assignments '''
610
611
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
612
613
614
615
616


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
617
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
618
619
620
621


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
622
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
623
624
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
625
            generate_for_range(loop)
dbarbera's avatar
dbarbera committed
626
        else:
dbarbera's avatar
dbarbera committed
627
            generate_for_iterable(loop)
dbarbera's avatar
dbarbera committed
628
629


dbarbera's avatar
dbarbera committed
630
def generate_for_range(loop):
dbarbera's avatar
dbarbera committed
631
    ''' Generate the code for a for x in range loop '''
dbarbera's avatar
dbarbera committed
632
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
633
634
635
636
637
    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('')

638
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
639

dbarbera's avatar
dbarbera committed
640
    loop_var = ctx.builder.alloca(ctx.i64, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
641
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
642
643
644

    if loop['range']['start']:
        start_val = expression(loop['range']['start'])
dbarbera's avatar
dbarbera committed
645
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
646
    else:
dbarbera's avatar
dbarbera committed
647
        ctx.builder.store(core.Constant.int(ctx.i64, 0), loop_var)
dbarbera's avatar
dbarbera committed
648
649

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

dbarbera's avatar
dbarbera committed
652
653
654
655
    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
656

dbarbera's avatar
dbarbera committed
657
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
658
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
659
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
660

dbarbera's avatar
dbarbera committed
661
    ctx.builder.position_at_end(inc_block)
dbarbera's avatar
dbarbera committed
662
    step_val = core.Constant.int(ctx.i64, loop['range']['step'])
dbarbera's avatar
dbarbera committed
663
664
665
666
    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
667

dbarbera's avatar
dbarbera committed
668
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
669

670
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
671
672


dbarbera's avatar
dbarbera committed
673
def generate_for_iterable(loop):
dbarbera's avatar
dbarbera committed
674
    ''' Generate the code for a for x in iterable loop'''
dbarbera's avatar
dbarbera committed
675
    seqof_asn1ty = find_basic_type(loop['list'].exprType)
676
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
677

dbarbera's avatar
dbarbera committed
678
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
679
680
681
682
683
684
685
686
687
688

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

689
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
690

dbarbera's avatar
dbarbera committed
691
692
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
693
    seqof_struct_ptr = expression(loop['list'])
694
695
696

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

dbarbera's avatar
dbarbera committed
701
    element_typ = array_ptr.type.pointee.element
702
703
704

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

dbarbera's avatar
dbarbera committed
709
710
    var_ptr = ctx.builder.alloca(element_typ, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
711

dbarbera's avatar
dbarbera committed
712
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
713
714

    # load block
dbarbera's avatar
dbarbera committed
715
716
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
717
    if element_typ.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
718
        generate_assign(var_ptr, ctx.builder.gep(array_ptr, [ctx.zero, idx_var]))
719
    else:
dbarbera's avatar
dbarbera committed
720
721
        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
722
723

    # body block
dbarbera's avatar
dbarbera committed
724
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
725
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
726
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
727
728

    # cond block
dbarbera's avatar
dbarbera committed
729
730
731
732
733
    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
734

dbarbera's avatar
dbarbera committed
735
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
736

737
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
738

dbarbera's avatar
dbarbera committed
739

dbarbera's avatar
dbarbera committed
740
741
@singledispatch
def reference(prim):
742
    ''' Generate a reference '''
dbarbera's avatar
flake8    
dbarbera committed
743
    raise TypeError('Unsupported reference: ' + str(prim))
dbarbera's avatar
dbarbera committed
744
745
746
747


@reference.register(ogAST.PrimVariable)
def _prim_var_reference(prim):
748
    ''' Generate a variable reference '''
dbarbera's avatar
dbarbera committed
749
    return ctx.scope.resolve(str(prim.value[0]))
dbarbera's avatar
dbarbera committed
750
751


752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
@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
785

Maxime Perrotin's avatar
Maxime Perrotin committed
786
787
788

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
789
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
790
791
792
793
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
794
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
795
796
    ''' Generate the code for a variable expression '''
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
797
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
798
799


800
801
802
803
804
@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
805

806

807
808
809
@expression.register(ogAST.PrimIndex)
def _primary_index(prim):
    ''' Generate the code for an Index expression '''
dbarbera's avatar
dbarbera committed
810
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
811
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
812
813


814
815
816
@expression.register(ogAST.PrimSubstring)
def _primary_substring(prim):
    ''' Generate the code for a Substring expression '''
dbarbera's avatar
dbarbera committed
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
    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
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868


@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
869
870
    elif name == 'num':
        return generate_num(args)
871
872


873
def generate_length(params):
dbarbera's avatar
dbarbera committed
874
    ''' Generate the code for the built-in length operation'''
dbarbera's avatar
dbarbera committed
875
    seq_ptr = reference(params[0])
dbarbera's avatar
dbarbera committed
876
    arr_ty = seq_ptr.type.pointee.elements[0]
877
878
879
880
    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
881
    return core.Constant.int(ctx.i64, arr_ty.count)
882
883
884


def generate_present(params):
dbarbera's avatar
dbarbera committed
885
    ''' Generate the code for the built-in present operation'''
886
887
888
    expr_val = expression(params[0])
    kind_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.zero])
    return ctx.builder.load(kind_ptr)
889
890
891


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

dbarbera's avatar
dbarbera committed
895
    if expr_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
896
897
        expr_conv = ctx.builder.sitofp(expr_val, ctx.double)
        res_val = ctx.builder.call(ctx.funcs['fabs'], [expr_conv])
dbarbera's avatar
dbarbera committed
898
        return ctx.builder.fptosi(res_val, ctx.i64)
dbarbera's avatar
dbarbera committed
899
    else:
dbarbera's avatar
dbarbera committed
900
        return ctx.builder.call(ctx.funcs['fabs'], [expr_val])
901
902
903


def generate_fix(params):
dbarbera's avatar
dbarbera committed
904
    ''' Generate the code for the built-in fix operation'''
dbarbera's avatar
dbarbera committed
905
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
906
    return ctx.builder.fptosi(expr_val, ctx.i64)
907
908
909


def generate_float(params):
dbarbera's avatar
dbarbera committed
910
    ''' Generate the code for the built-in float operation'''
dbarbera's avatar
dbarbera committed
911
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
912
    return ctx.builder.sitofp(expr_val, ctx.double)
913
914
915


def generate_power(params):
dbarbera's avatar
dbarbera committed
916
    ''' Generate the code for the built-in power operation'''
917
918
    left_val = expression(params[0])
    right_val = expression(params[1])
dbarbera's avatar
dbarbera committed
919