LlvmGenerator.py 47.6 KB
Newer Older
Maxime Perrotin's avatar
Maxime Perrotin committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    OpenGEODE - A tiny SDL Editor for TASTE

    This module generates LLVM IR code from SDL process models, allowing
    generation of a binary application without an intermediate language.
    LLVM also allows for various code verification, analysis, and optimization.

    The design is based on the Ada code generator. Check it for details.

    Copyright (c) 2012-2013 European Space Agency

    Designed and implemented by Maxime Perrotin

    Contact: maxime.perrotin@esa.int
"""

import logging
21

Maxime Perrotin's avatar
Maxime Perrotin committed
22
from singledispatch import singledispatch
dbarbera's avatar
dbarbera committed
23
from llvm import core, ee
Maxime Perrotin's avatar
Maxime Perrotin committed
24
25

import ogAST
Maxime Perrotin's avatar
Maxime Perrotin committed
26
import Helper
Maxime Perrotin's avatar
Maxime Perrotin committed
27
28
29

LOG = logging.getLogger(__name__)

Maxime Perrotin's avatar
Maxime Perrotin committed
30
31
__all__ = ['generate']

Maxime Perrotin's avatar
Maxime Perrotin committed
32

dbarbera's avatar
dbarbera committed
33
# Global context
dbarbera's avatar
dbarbera committed
34
ctx = None
35
36


dbarbera's avatar
dbarbera committed
37
class Context():
38
    def __init__(self, process):
dbarbera's avatar
dbarbera committed
39
40
        self.name = str(process.processName)
        self.module = core.Module.new(self.name)
dbarbera's avatar
dbarbera committed
41
        self.target_data = ee.TargetData.new(self.module.data_layout)
42
43
        self.dataview = process.dataview

44
45
        self.scope = Scope()
        self.global_scope = self.scope
46
        self.states = {}
dbarbera's avatar
dbarbera committed
47
        self.structs = {}
dbarbera's avatar
dbarbera committed
48
        self.unions = {}
49
        self.strings = {}
dbarbera's avatar
dbarbera committed
50
        self.funcs = {}
51
        self.lltypes = {}
52
53
54
55
56
57
58
59
60
61
62
63
64
65

        # Initialize built-in types
        self.i1 = core.Type.int(1)
        self.i8 = core.Type.int(8)
        self.i32 = core.Type.int(32)
        self.i64 = core.Type.int(64)
        self.void = core.Type.void()
        self.double = core.Type.double()
        self.i1_ptr = core.Type.pointer(self.i1)
        self.i8_ptr = core.Type.pointer(self.i8)
        self.i32_ptr = core.Type.pointer(self.i32)
        self.i64_ptr = core.Type.pointer(self.i64)
        self.double_ptr = core.Type.pointer(self.double)

dbarbera's avatar
dbarbera committed
66
67
68
69
        # Initialize common constants
        self.zero = core.Constant.int(self.i32, 0)
        self.one = core.Constant.int(self.i32, 1)

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

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

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

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

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

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

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
    def type_of(self, asn1ty):
        ''' Return the LL type of a ASN.1 type '''
        try:
            name = asn1ty.ReferencedTypeName.replace('-', '_')
        except AttributeError:
            name = None

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

        basic_asn1ty = find_basic_type(asn1ty)

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

        if name:
            self.lltypes[name] = llty

        return llty

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

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

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

        return struct.ty

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

        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 = []
        for name, t in choice_ty.Children.viewitems():
            field_names.append(name)
            field_types.append(self.type_of(t.type))

        union = decl_union(field_names, field_types, name)
        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)
        struct = decl_struct(['nCount', 'arr'], [ctx.i32, arr_ty], name)
        return struct.ty

181
182
183
184
185
186
187
188
189
190
191
192
    def string_ptr(self, str):
        ''' Returns a pointer to a global string with the given value '''
        if str in self.strings:
            return self.strings[str].gep([self.zero, self.zero])

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

dbarbera's avatar
dbarbera committed
193

194
195
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
196
        self.name = name
197
198
199
200
201
        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
202
203


dbarbera's avatar
dbarbera committed
204
205
206
207
208
209
210
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
211
212
        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
213
214
215
216
217
218

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


219
220
221
class Scope:
    def __init__(self, parent=None):
        self.vars = {}
dbarbera's avatar
dbarbera committed
222
        self.labels = {}
223
224
225
226
227
228
229
230
231
232
233
234
        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
235
            raise NameError("name '%s' is not defined" % name)
236

dbarbera's avatar
dbarbera committed
237
238
239
240
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
241
            func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
242
243
244
245
            label_block = func.append_basic_block(name)
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
246

Maxime Perrotin's avatar
Maxime Perrotin committed
247
248
249
250
@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
251

dbarbera's avatar
dbarbera committed
252

Maxime Perrotin's avatar
Maxime Perrotin committed
253
254
255
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
256
    ''' Generate LLVM IR code '''
dbarbera's avatar
dbarbera committed
257
258
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
259

dbarbera's avatar
dbarbera committed
260
261
    global ctx
    ctx = Context(process)
262

dbarbera's avatar
dbarbera committed
263
264
265
266
267
268
    # 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
269

270
271
    # Initialize states
    for name, val in process.mapping.viewitems():
272
        if not name.endswith('START'):
dbarbera's avatar
dbarbera committed
273
274
            cons_val = core.Constant.int(ctx.i32, len(ctx.states))
            ctx.states[name] = cons_val
275
        elif name != 'START':
dbarbera's avatar
dbarbera committed
276
277
            cons_val = core.Constant.int(ctx.i32, val)
            ctx.states[name] = cons_val
278

279
    # Generate state var
dbarbera's avatar
dbarbera committed
280
281
282
    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)
283

284
285
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
286
        var_ty = ctx.type_of(ty)
dbarbera's avatar
dbarbera committed
287
        global_var = ctx.module.add_global_variable(var_ty, str(name))
288
        global_var.initializer = core.Constant.null(var_ty)
dbarbera's avatar
dbarbera committed
289
        ctx.scope.define(str(name).lower(), global_var)
290

dbarbera's avatar
dbarbera committed
291
292
293
    # Declare timer set/reset functions
    for timer in process.timers:
        # TODO: Should be uint?
dbarbera's avatar
dbarbera committed
294
295
        decl_func("set_%s" % str(timer), ctx.void, [ctx.i32_ptr], True)
        decl_func("reset_%s" % str(timer), ctx.void, [], True)
dbarbera's avatar
dbarbera committed
296

297
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
298
    for signal in process.output_signals:
299
        if 'type' in signal:
300
            param_tys = [core.Type.pointer(ctx.type_of(signal['type']))]
301
302
        else:
            param_tys = []
dbarbera's avatar
dbarbera committed
303
        decl_func(str(signal['name']), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
304

305
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
306
    for proc in [proc for proc in process.procedures if proc.external]:
307
        param_tys = [core.Type.pointer(ctx.type_of(p['type'])) for p in proc.fpar]
dbarbera's avatar
dbarbera committed
308
        decl_func(str(proc.inputString), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
309

310
311
    # Generate internal procedures
    for proc in process.content.inner_procedures:
312
        generate(proc)
313

314
    # Generate process functions
dbarbera's avatar
dbarbera committed
315
316
    generate_runtr_func(process)
    generate_startup_func(process)
317

318
319
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
320
        generate_input_signal(signal, mapping[signal['name']])
321

dbarbera's avatar
dbarbera committed
322
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
323

dbarbera's avatar
dbarbera committed
324
325
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
326
327


dbarbera's avatar
dbarbera committed
328
def generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
329
    ''' Generate code for the run_transition function '''
dbarbera's avatar
dbarbera committed
330
    func = decl_func('run_transition', ctx.void, [ctx.i32])
331

332
    ctx.open_scope()
333

334
335
336
337
338
    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
339
    ctx.builder = core.Builder.new(entry_block)
340
341

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

    # cond
dbarbera's avatar
dbarbera committed
348
349
350
351
352
    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)
353
354

    # body
dbarbera's avatar
dbarbera committed
355
356
    ctx.builder.position_at_end(body_block)
    switch = ctx.builder.switch(func.args[0], exit_block)
357
358
359
360

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
dbarbera's avatar
dbarbera committed
361
        const = core.Constant.int(ctx.i32, idx)
362
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
363
        ctx.builder.position_at_end(tr_block)
364
        generate(tr)
dbarbera's avatar
dbarbera committed
365
366
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
367
368

    # exit
dbarbera's avatar
dbarbera committed
369
370
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
371

dbarbera's avatar
dbarbera committed
372
373
374
375
376
    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
377
378
379
    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
380

381
    ctx.close_scope()
382

383
384
385
386
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
387
def generate_startup_func(process):
dbarbera's avatar
dbarbera committed
388
    ''' Generate code for the startup function '''
dbarbera's avatar
dbarbera committed
389
    func = decl_func(ctx.name + '_startup', ctx.void, [])
390

391
    ctx.open_scope()
392

393
    entry_block = func.append_basic_block('entry')
dbarbera's avatar
dbarbera committed
394
    ctx.builder = core.Builder.new(entry_block)
395

396
397
398
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
399
            global_var = ctx.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
400
            generate_assign(global_var, expression(expr))
401

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

405
    ctx.close_scope()
406

407
408
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
409
410


dbarbera's avatar
dbarbera committed
411
def generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
412
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
413
    func_name = ctx.name + "_" + str(signal['name'])
414
415
    param_tys = []
    if 'type' in signal:
416
        param_tys.append(core.Type.pointer(ctx.type_of(signal['type'])))
417

dbarbera's avatar
dbarbera committed
418
    func = decl_func(func_name, ctx.void, param_tys)
419

420
    ctx.open_scope()
421

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

dbarbera's avatar
dbarbera committed
426
427
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('state'))
    switch = ctx.builder.switch(g_state_val, exit_block)
428

dbarbera's avatar
dbarbera committed
429
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
430
431
        if state_name.endswith('START'):
            continue
432
433
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
434
        ctx.builder.position_at_end(state_block)
435
436
437

        # TODO: Nested states

438
439
440
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
441
                var_ptr = ctx.scope.resolve(str(var_name))
442
443
444
                if is_struct_ptr(var_ptr):
                    generate_assign(var_ptr, func.args[0])
                else:
dbarbera's avatar
dbarbera committed
445
                    generate_assign(var_ptr, ctx.builder.load(func.args[0]))
446
            if input.transition:
dbarbera's avatar
dbarbera committed
447
448
                id_val = core.Constant.int(ctx.i32, input.transition_id)
                ctx.builder.call(ctx.funcs['run_transition'], [id_val])
449

dbarbera's avatar
dbarbera committed
450
        ctx.builder.ret_void()
451

dbarbera's avatar
dbarbera committed
452
453
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
454

455
    ctx.close_scope()
456

457
458
459
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
460
461
462
463
@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
464
465
466
    for out in output.output:
        name = out['outputName'].lower()

467
        if name == 'write':
dbarbera's avatar
dbarbera committed
468
            generate_write(out['params'])
dbarbera's avatar
dbarbera committed
469
            continue
470
        elif name == 'writeln':
dbarbera's avatar
dbarbera committed
471
            generate_writeln(out['params'])
472
            continue
dbarbera's avatar
dbarbera committed
473
        elif name == 'reset_timer':
dbarbera's avatar
dbarbera committed
474
            generate_reset_timer(out['params'])
dbarbera's avatar
dbarbera committed
475
476
            continue
        elif name == 'set_timer':
dbarbera's avatar
dbarbera committed
477
            generate_set_timer(out['params'])
dbarbera's avatar
dbarbera committed
478
479
            continue

dbarbera's avatar
dbarbera committed
480
        func = ctx.funcs[str(name).lower()]
481
482
483
484
485
486

        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
487
488
                p_var = ctx.builder.alloca(p_val.type, None)
                ctx.builder.store(p_val, p_var)
489
490
491
492
                params.append(p_var)
            else:
                params.append(p_val)

dbarbera's avatar
dbarbera committed
493
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
494
495


dbarbera's avatar
dbarbera committed
496
def generate_write(params):
dbarbera's avatar
dbarbera committed
497
    ''' Generate the code for the write operator '''
498
499
500
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
501

502
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
503
504
            fmt_str_ptr = ctx.string_ptr('% d')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
505
        elif basic_ty.kind == 'RealType':
506
507
            fmt_str_ptr = ctx.string_ptr('% .14E')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
508
        elif basic_ty.kind == 'BooleanType':
509
510
            true_str_ptr = ctx.string_ptr('TRUE')
            false_str_ptr = ctx.string_ptr('FALSE')
dbarbera's avatar
dbarbera committed
511
512
            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
513
        elif basic_ty.kind in ['StringType', 'OctetStringType']:
514
            fmt_str_ptr = ctx.string_ptr('%s')
dbarbera's avatar
dbarbera committed
515
            arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.one])
516
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, arr_ptr])
517
518
519
520
        else:
            raise NotImplementedError


dbarbera's avatar
dbarbera committed
521
def generate_writeln(params):
522
    ''' Generate the code for the writeln operator '''
dbarbera's avatar
dbarbera committed
523
    generate_write(params)
524

525
    str_ptr = ctx.string_ptr('\n')
dbarbera's avatar
dbarbera committed
526
    ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
527
528


dbarbera's avatar
dbarbera committed
529
def generate_reset_timer(params):
dbarbera's avatar
dbarbera committed
530
    ''' Generate the code for the reset timer operator '''
dbarbera's avatar
dbarbera committed
531
    timer_id = params[0]
532
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
533
    reset_func = ctx.funcs[reset_func_name.lower()]
dbarbera's avatar
dbarbera committed
534

dbarbera's avatar
dbarbera committed
535
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
536
537


dbarbera's avatar
dbarbera committed
538
def generate_set_timer(params):
dbarbera's avatar
dbarbera committed
539
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
540
    timer_expr, timer_id = params
541
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
542
    set_func = ctx.funcs[set_func_name.lower()]
dbarbera's avatar
dbarbera committed
543
544
545

    expr_val = expression(timer_expr)

dbarbera's avatar
dbarbera committed
546
547
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
548

dbarbera's avatar
dbarbera committed
549
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
550
551
552
553


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
554
    ''' Generate the code of a list of assignments '''
555
556
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
557
558
559
560
561


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
562
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
563
564
565
566


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
567
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
568
569
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
570
            generate_for_range(loop)
dbarbera's avatar
dbarbera committed
571
        else:
dbarbera's avatar
dbarbera committed
572
            generate_for_iterable(loop)
dbarbera's avatar
dbarbera committed
573
574


dbarbera's avatar
dbarbera committed
575
def generate_for_range(loop):
dbarbera's avatar
dbarbera committed
576
    ''' Generate the code for a for x in range loop '''
dbarbera's avatar
dbarbera committed
577
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
578
579
580
581
582
    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('')

583
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
584

dbarbera's avatar
dbarbera committed
585
586
    loop_var = ctx.builder.alloca(ctx.i32, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
587
588
589

    if loop['range']['start']:
        start_val = expression(loop['range']['start'])
dbarbera's avatar
dbarbera committed
590
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
591
    else:
dbarbera's avatar
dbarbera committed
592
        ctx.builder.store(core.Constant.int(ctx.i32, 0), loop_var)
dbarbera's avatar
dbarbera committed
593
594

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

dbarbera's avatar
dbarbera committed
597
598
599
600
    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
601

dbarbera's avatar
dbarbera committed
602
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
603
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
604
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
605

dbarbera's avatar
dbarbera committed
606
607
608
609
610
611
    ctx.builder.position_at_end(inc_block)
    step_val = core.Constant.int(ctx.i32, loop['range']['step'])
    loop_val = ctx.builder.load(loop_var)
    temp_val = ctx.builder.add(loop_val, step_val)
    ctx.builder.store(temp_val, loop_var)
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
612

dbarbera's avatar
dbarbera committed
613
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
614

615
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
616
617


dbarbera's avatar
dbarbera committed
618
def generate_for_iterable(loop):
dbarbera's avatar
dbarbera committed
619
    ''' Generate the code for a for x in iterable loop'''
dbarbera's avatar
dbarbera committed
620
    seqof_asn1ty = find_basic_type(loop['list'].exprType)
621
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
622

dbarbera's avatar
dbarbera committed
623
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
624
625
626
627
628
629
630
631
632
633

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

634
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
635

dbarbera's avatar
dbarbera committed
636
637
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
638
    seqof_struct_ptr = expression(loop['list'])
639
640
641

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

dbarbera's avatar
dbarbera committed
646
    element_typ = array_ptr.type.pointee.element
647
648
649

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

dbarbera's avatar
dbarbera committed
654
655
    var_ptr = ctx.builder.alloca(element_typ, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
656

dbarbera's avatar
dbarbera committed
657
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
658
659

    # load block
dbarbera's avatar
dbarbera committed
660
661
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
662
    if element_typ.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
663
        generate_assign(var_ptr, ctx.builder.gep(array_ptr, [ctx.zero, idx_var]))
664
    else:
dbarbera's avatar
dbarbera committed
665
666
        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
667
668

    # body block
dbarbera's avatar
dbarbera committed
669
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
670
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
671
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
672
673

    # cond block
dbarbera's avatar
dbarbera committed
674
675
676
677
678
    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
679

dbarbera's avatar
dbarbera committed
680
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
681

682
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
683

dbarbera's avatar
dbarbera committed
684

dbarbera's avatar
dbarbera committed
685
686
687
@singledispatch
def reference(prim):
    ''' Generate a variable reference '''
dbarbera's avatar
flake8    
dbarbera committed
688
    raise TypeError('Unsupported reference: ' + str(prim))
dbarbera's avatar
dbarbera committed
689
690
691
692
693


@reference.register(ogAST.PrimVariable)
def _prim_var_reference(prim):
    ''' Generate a primary variable reference '''
dbarbera's avatar
dbarbera committed
694
    return ctx.scope.resolve(str(prim.value[0]))
dbarbera's avatar
dbarbera committed
695
696
697
698
699


@reference.register(ogAST.PrimPath)
def _prim_path_reference(prim):
    ''' Generate a primary path reference '''
700
    var_name = prim.value[0].lower()
dbarbera's avatar
dbarbera committed
701
    var_ptr = ctx.scope.resolve(str(var_name))
dbarbera's avatar
dbarbera committed
702
703
704
705

    if not prim.value:
        return var_ptr

dbarbera's avatar
dbarbera committed
706
707
708
709
    for elem in prim.value[1:]:
        if type(elem) == dict:
            if 'index' in elem:
                idx_val = expression(elem['index'][0])
dbarbera's avatar
dbarbera committed
710
                array_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.zero])
711
                # TODO: Refactor this
dbarbera's avatar
dbarbera committed
712
713
                if array_ptr.type.pointee.kind != core.TYPE_ARRAY:
                    # If is not an array this is a pointer to a variable size SeqOf
714
                    # The array is in the second field of the struct
dbarbera's avatar
dbarbera committed
715
                    var_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.one, idx_val])
716
                else:
dbarbera's avatar
dbarbera committed
717
                    var_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.zero, idx_val])
dbarbera's avatar
dbarbera committed
718
719
720
721
            else:
                raise NotImplementedError
        else:
            var_ty = var_ptr.type
dbarbera's avatar
dbarbera committed
722
723
724
725
            if var_ty.pointee.name in ctx.structs:
                struct = ctx.structs[var_ty.pointee.name]
                field_idx_cons = core.Constant.int(ctx.i32, struct.idx(elem))
                field_ptr = ctx.builder.gep(var_ptr, [ctx.zero, field_idx_cons])
dbarbera's avatar
dbarbera committed
726
                var_ptr = field_ptr
dbarbera's avatar
dbarbera committed
727
728
            elif var_ty.pointee.name in ctx.unions:
                union = ctx.unions[var_ty.pointee.name]
dbarbera's avatar
dbarbera committed
729
                _, field_ty = union.kind(elem)
dbarbera's avatar
dbarbera committed
730
731
                field_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.one])
                var_ptr = ctx.builder.bitcast(field_ptr, core.Type.pointer(field_ty))
dbarbera's avatar
dbarbera committed
732
733
            else:
                raise NotImplementedError
dbarbera's avatar
dbarbera committed
734
735
    return var_ptr

Maxime Perrotin's avatar
Maxime Perrotin committed
736
737
738

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
739
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
740
741
742
743
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
744
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
745
746
    ''' Generate the code for a variable expression '''
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
747
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
748
749


Maxime Perrotin's avatar
Maxime Perrotin committed
750
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
751
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
752
    ''' Generate the code for an of path expression '''
753
754
755
756
757
758
759
760
761
    specops_generators = {
        'length': generate_length,
        'present': generate_present,
        'abs': generate_abs,
        'fix': generate_fix,
        'float': generate_float,
        'power': generate_power
    }

dbarbera's avatar
dbarbera committed
762
763
764
765
766
767
768
    name = prim.value[0].lower()
    if name in specops_generators:
        generator = specops_generators[name]
        return generator(prim.value[1]['procParams'])

    return generate_access(prim)

769

dbarbera's avatar
dbarbera committed
770
771
def generate_access(prim):
    ''' Generate the code for an access '''
dbarbera's avatar
dbarbera committed
772
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
773
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
774
775


776
def generate_length(params):
dbarbera's avatar
dbarbera committed
777
    ''' Generate the code for the built-in length operation'''
dbarbera's avatar
dbarbera committed
778
    seq_ptr = reference(params[0])
dbarbera's avatar
dbarbera committed
779
    arr_ty = seq_ptr.type.pointee.elements[0]
dbarbera's avatar
dbarbera committed
780
    return core.Constant.int(ctx.i32, arr_ty.count)
781
782
783


def generate_present(params):
dbarbera's avatar
dbarbera committed
784
    ''' Generate the code for the built-in present operation'''
785
786
787
788
    raise NotImplementedError


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

dbarbera's avatar
dbarbera committed
792
    if expr_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
793
794
795
        expr_conv = ctx.builder.sitofp(expr_val, ctx.double)
        res_val = ctx.builder.call(ctx.funcs['fabs'], [expr_conv])
        return ctx.builder.fptosi(res_val, ctx.i32)
dbarbera's avatar
dbarbera committed
796
    else:
dbarbera's avatar
dbarbera committed
797
        return ctx.builder.call(ctx.funcs['fabs'], [expr_val])
798
799
800


def generate_fix(params):
dbarbera's avatar
dbarbera committed
801
    ''' Generate the code for the built-in fix operation'''
dbarbera's avatar
dbarbera committed
802
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
803
    return ctx.builder.fptosi(expr_val, ctx.i32)
804
805
806


def generate_float(params):
dbarbera's avatar
dbarbera committed
807
    ''' Generate the code for the built-in float operation'''
dbarbera's avatar
dbarbera committed
808
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
809
    return ctx.builder.sitofp(expr_val, ctx.double)
810
811
812


def generate_power(params):
dbarbera's avatar
dbarbera committed
813
    ''' Generate the code for the built-in power operation'''
814
815
816
817
    left_val = expression(params[0])
    right_val = expression(params[1])

    if left_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
818
819
820
        left_conv = ctx.builder.sitofp(left_val, ctx.double)
        res_val = ctx.builder.call(ctx.funcs['powi'], [left_conv, right_val])
        return ctx.builder.fptosi(res_val, ctx.i32)
821
    else:
dbarbera's avatar
dbarbera committed
822
        return ctx.builder.call(ctx.funcs['powi'], [left_val, right_val])
823
824


Maxime Perrotin's avatar
Maxime Perrotin committed
825
826
827
828
829
830
831
832
833
834
835
836
@expression.register(ogAST.ExprPlus)
@expression.register(ogAST.ExprMul)
@expression.register(ogAST.ExprMinus)
@expression.register(ogAST.ExprEq)
@expression.register(ogAST.ExprNeq)
@expression.register(ogAST.ExprGt)
@expression.register(ogAST.ExprGe)
@expression.register(ogAST.ExprLt)
@expression.register(ogAST.ExprLe)
@expression.register(ogAST.ExprDiv)
@expression.register(ogAST.ExprMod)
@expression.register(ogAST.ExprRem)
dbarbera's avatar
dbarbera committed
837
838
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
839
840
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
841

842
843
    if lefttmp.type.kind == core.TYPE_INTEGER:
        if expr.operand == '+':
dbarbera's avatar
dbarbera committed
844
            return ctx.builder.add(lefttmp, righttmp, 'addtmp')
845
        elif expr.operand == '-':
dbarbera's avatar
dbarbera committed
846
            return ctx.builder.sub(lefttmp, righttmp, 'subtmp')
847
        elif expr.operand == '*':
dbarbera's avatar
dbarbera committed
848
            return ctx.builder.mul(lefttmp, righttmp, 'multmp')
849
        elif expr.operand == '/':
dbarbera's avatar
dbarbera committed
850
            return ctx.builder.sdiv(lefttmp, righttmp, 'divtmp')
851
        elif expr.operand == 'mod':
852
            # l mod r == (((l rem r) + r) rem r)
dbarbera's avatar
dbarbera committed
853
854
855
            remtmp = ctx.builder.srem(lefttmp, righttmp)
            addtmp = ctx.builder.add(remtmp, righttmp)
            return ctx.builder.srem(addtmp, righttmp, 'modtmp')
856
        elif expr.operand == 'rem':
dbarbera's avatar
dbarbera committed
857
            return ctx.builder.srem(lefttmp, righttmp, 'remtmp')
858
        elif expr.operand == '<':
dbarbera's avatar
dbarbera committed
859
            return ctx.builder.icmp(core.ICMP_SLT, lefttmp, righttmp, 'lttmp')
860
        elif expr.operand == '<=':
dbarbera's avatar
dbarbera committed
861
            return ctx.builder.icmp(core.ICMP_SLE, lefttmp, righttmp, 'letmp')
862
        elif expr.operand == '=':
dbarbera's avatar
dbarbera committed
863
            return ctx.builder.icmp(core.ICMP_EQ, lefttmp, righttmp, 'eqtmp')
864
        elif expr.operand == '/=':
dbarbera's avatar
dbarbera committed
865
            return ctx.builder.icmp(core.ICMP_NE, lefttmp, righttmp, 'netmp')
866
        elif expr.operand == '>=':
dbarbera's avatar
dbarbera committed
867
            return ctx.builder.icmp(core.ICMP_SGE, lefttmp, righttmp, 'getmp')
868
        elif expr.operand == '>':
dbarbera's avatar
dbarbera committed
869
            return ctx.builder.icmp(core.ICMP_SGT, lefttmp, righttmp, 'gttmp')
870
871
872
873
        else:
            raise NotImplementedError
    elif lefttmp.type.kind == core.TYPE_DOUBLE:
        if expr.operand == '+':
dbarbera's avatar
dbarbera committed
874
            return ctx.builder.fadd(lefttmp, righttmp, 'addtmp')
875
        elif expr.operand == '-':
dbarbera's avatar
dbarbera committed
876
            return ctx.builder.fsub(lefttmp, righttmp, 'subtmp')
877
        elif expr.operand == '*':
dbarbera's avatar
dbarbera committed
878
            return ctx.builder.fmul(lefttmp, righttmp, 'multmp')
879
        elif expr.operand == '/':
dbarbera's avatar
dbarbera committed
880
            return ctx.builder.fdiv(lefttmp, righttmp, 'divtmp')