LlvmGenerator.py 60 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)
dbarbera's avatar
dbarbera committed
131
        elif basic_asn1ty.kind in ('StringType', 'StandardStringType'):
132
            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
            cons_val = core.Constant.int(ctx.i32, len(ctx.states))
dbarbera's avatar
dbarbera committed
337
            ctx.states[name.lower()] = cons_val
338
        elif name != 'START':
dbarbera's avatar
dbarbera committed
339
            cons_val = core.Constant.int(ctx.i32, val)
dbarbera's avatar
dbarbera committed
340
            ctx.states[name.lower()] = cons_val
341

342
    # Generate state var
343
    state_cons = ctx.module.add_global_variable(ctx.i32, '.state')
dbarbera's avatar
dbarbera committed
344
    state_cons.initializer = core.Constant.int(ctx.i32, -1)
345
    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
    # Declare set/reset timer functions
dbarbera's avatar
dbarbera committed
355
356
    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
386
387
388
    # Generate timer signal
    for timer in process.timers:
        generate_input_signal({'name': timer.lower()}, mapping[timer])

dbarbera's avatar
dbarbera committed
389
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
390

dbarbera's avatar
dbarbera committed
391
392
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
393
394


dbarbera's avatar
dbarbera committed
395
def generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
396
    ''' Generate code for the run_transition function '''
397
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
398

399
    ctx.open_scope()
400

401
402
403
404
405
    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
406
    ctx.builder = core.Builder.new(entry_block)
407
408

    # entry
dbarbera's avatar
dbarbera committed
409
410
411
412
    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)
413
414

    # cond
dbarbera's avatar
dbarbera committed
415
416
417
418
419
    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)
420
421

    # body
dbarbera's avatar
dbarbera committed
422
    ctx.builder.position_at_end(body_block)
423
    switch = ctx.builder.switch(id_val, exit_block)
424
425
426
427

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

    # exit
dbarbera's avatar
dbarbera committed
436
437
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
438

dbarbera's avatar
dbarbera committed
439
440
441
442
443
    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
444
445
446
    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
447

448
    ctx.close_scope()
449

450
451
452
453
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
454
def generate_startup_func(process):
dbarbera's avatar
dbarbera committed
455
    ''' Generate code for the startup function '''
456
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
457

458
    ctx.open_scope()
459

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

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

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

472
    ctx.close_scope()
473

474
475
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
476
477


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

485
    func = ctx.decl_func(func_name, ctx.void, param_tys)
486

487
    ctx.open_scope()
488

489
490
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
dbarbera's avatar
dbarbera committed
491
    ctx.builder = core.Builder.new(entry_block)
492

493
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('.state'))
dbarbera's avatar
dbarbera committed
494
    switch = ctx.builder.switch(g_state_val, exit_block)
495

dbarbera's avatar
dbarbera committed
496
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
497
        if state_name.endswith('start'):
dbarbera's avatar
dbarbera committed
498
            continue
499
500
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
501
        ctx.builder.position_at_end(state_block)
502
503
504

        # TODO: Nested states

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

dbarbera's avatar
dbarbera committed
517
        ctx.builder.ret_void()
518

dbarbera's avatar
dbarbera committed
519
520
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
521

522
    ctx.close_scope()
523

524
525
526
    func.verify()


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

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

dbarbera's avatar
dbarbera committed
547
        func = ctx.funcs[str(name).lower()]
548
549
550
551
552
553

        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
554
555
                p_var = ctx.builder.alloca(p_val.type, None)
                ctx.builder.store(p_val, p_var)
556
557
558
559
                params.append(p_var)
            else:
                params.append(p_val)

dbarbera's avatar
dbarbera committed
560
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
561
562


dbarbera's avatar
dbarbera committed
563
def generate_write(params):
dbarbera's avatar
dbarbera committed
564
    ''' Generate the code for the write operator '''
565
566
567
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
568

569
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
570
571
            fmt_str_ptr = ctx.string_ptr('% d')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
572
        elif basic_ty.kind == 'RealType':
573
574
            fmt_str_ptr = ctx.string_ptr('% .14E')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
575
        elif basic_ty.kind == 'BooleanType':
576
577
            true_str_ptr = ctx.string_ptr('TRUE')
            false_str_ptr = ctx.string_ptr('FALSE')
dbarbera's avatar
dbarbera committed
578
579
            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
580
        elif basic_ty.kind in ('StringType', 'StandardStringType'):
581
582
583
            fmt_str_ptr = ctx.string_ptr('%s')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
        elif basic_ty.kind == 'OctetStringType':
584
            fmt_str_ptr = ctx.string_ptr('%.*s')
585
586
587
588
589
590
            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])
591
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, count_val, arr_ptr])
592
593
594
595
        else:
            raise NotImplementedError


dbarbera's avatar
dbarbera committed
596
def generate_writeln(params):
597
    ''' Generate the code for the writeln operator '''
dbarbera's avatar
dbarbera committed
598
    generate_write(params)
599

600
    str_ptr = ctx.string_ptr('\n')
dbarbera's avatar
dbarbera committed
601
    ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
602
603


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

dbarbera's avatar
dbarbera committed
610
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
611
612


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

    expr_val = expression(timer_expr)

dbarbera's avatar
dbarbera committed
621
622
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
623

dbarbera's avatar
dbarbera committed
624
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
625
626
627
628


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


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
637
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
638
639
640
641


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


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

658
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
659

dbarbera's avatar
dbarbera committed
660
    loop_var = ctx.builder.alloca(ctx.i64, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
661
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
662
663
664

    if loop['range']['start']:
        start_val = expression(loop['range']['start'])
dbarbera's avatar
dbarbera committed
665
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
666
    else:
dbarbera's avatar
dbarbera committed
667
        ctx.builder.store(core.Constant.int(ctx.i64, 0), loop_var)
dbarbera's avatar
dbarbera committed
668
669

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

dbarbera's avatar
dbarbera committed
672
673
674
675
    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
676

dbarbera's avatar
dbarbera committed
677
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
678
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
679
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
680

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

dbarbera's avatar
dbarbera committed
688
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
689

690
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
691
692


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

dbarbera's avatar
dbarbera committed
698
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
699
700
701
702
703
704
705
706
707
708

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

709
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
710

dbarbera's avatar
dbarbera committed
711
712
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
713
    seqof_struct_ptr = expression(loop['list'])
714
715
716

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

dbarbera's avatar
dbarbera committed
721
    element_typ = array_ptr.type.pointee.element
722
723
724

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

dbarbera's avatar
dbarbera committed
729
730
    var_ptr = ctx.builder.alloca(element_typ, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
731

dbarbera's avatar
dbarbera committed
732
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
733
734

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

    # body block
dbarbera's avatar
dbarbera committed
744
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
745
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
746
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
747
748

    # cond block
dbarbera's avatar
dbarbera committed
749
750
751
752
753
    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
754

dbarbera's avatar
dbarbera committed
755
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
756

757
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
758

dbarbera's avatar
dbarbera committed
759

dbarbera's avatar
dbarbera committed
760
761
@singledispatch
def reference(prim):
762
    ''' Generate a reference '''
dbarbera's avatar
flake8    
dbarbera committed
763
    raise TypeError('Unsupported reference: ' + str(prim))
dbarbera's avatar
dbarbera committed
764
765
766
767


@reference.register(ogAST.PrimVariable)
def _prim_var_reference(prim):
768
    ''' Generate a variable reference '''
dbarbera's avatar
dbarbera committed
769
    return ctx.scope.resolve(str(prim.value[0]))
dbarbera's avatar
dbarbera committed
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
801
802
803
804
@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
805

Maxime Perrotin's avatar
Maxime Perrotin committed
806
807
808

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
809
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
810
811
812
813
    raise TypeError('Unsupported expression: ' + str(expr))


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


820
821
822
823
824
@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
825

826

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


834
835
836
@expression.register(ogAST.PrimSubstring)
def _primary_substring(prim):
    ''' Generate the code for a Substring expression '''
dbarbera's avatar
dbarbera committed
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
865
866
867
868
    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
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888


@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
889
890
    elif name == 'num':
        return generate_num(args)
891
892


893
def generate_length(params):
dbarbera's avatar
dbarbera committed
894
    ''' Generate the code for the built-in length operation'''
dbarbera's avatar
dbarbera committed
895
    seq_ptr = reference(params[0])
896
897
898
899
900
901
902
903

    bty = find_basic_type(params[0].exprType)
    if bty.Min != bty.Max:
        len_ptr = ctx.builder.gep(seq_ptr, [ctx.zero, ctx.zero])
        return ctx.builder.zext(ctx.builder.load(len_ptr), ctx.i64)
    else:
        arr_ty = seq_ptr.type.pointee.elements[0]
        return core.Constant.int(ctx.i64, arr_ty.count)
904
905
906


def generate_present(params):
dbarbera's avatar
dbarbera committed
907
    ''' Generate the code for the built-in present operation'''
908
909
910
    expr_val = expression(params[0])
    kind_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.zero])
    return ctx.builder.load(kind_ptr)
911
912
913


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

dbarbera's avatar
dbarbera committed
917
    if expr_val.type.kind == core.TYPE_INTEGER: