LlvmGenerator.py 48 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
71
        # Intialize built-in functions
        ty = core.Type.function(self.void, [core.Type.pointer(self.i8)], 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
93
94
95
96
97
    def open_scope(self):
        self.scope = Scope(self.scope)

    def close_scope(self):
        self.scope = self.scope.parent

98
99
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
    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

dbarbera's avatar
dbarbera committed
179

180
181
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
182
        self.name = name
183
184
185
186
187
        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
188
189


dbarbera's avatar
dbarbera committed
190
191
192
193
194
195
196
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
197
198
        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
199
200
201
202
203
204

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


205
206
207
class Scope:
    def __init__(self, parent=None):
        self.vars = {}
dbarbera's avatar
dbarbera committed
208
        self.labels = {}
209
210
211
212
213
214
215
216
217
218
219
220
        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
221
            raise NameError("name '%s' is not defined" % name)
222

dbarbera's avatar
dbarbera committed
223
224
225
226
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
227
            func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
228
229
230
231
            label_block = func.append_basic_block(name)
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
232

Maxime Perrotin's avatar
Maxime Perrotin committed
233
234
235
236
@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
237

dbarbera's avatar
dbarbera committed
238

Maxime Perrotin's avatar
Maxime Perrotin committed
239
240
241
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
242
    ''' Generate LLVM IR code '''
dbarbera's avatar
dbarbera committed
243
244
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
245

dbarbera's avatar
dbarbera committed
246
247
    global ctx
    ctx = Context(process)
248

dbarbera's avatar
dbarbera committed
249
250
251
252
253
254
    # 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
255

256
257
    # Initialize states
    for name, val in process.mapping.viewitems():
258
        if not name.endswith('START'):
dbarbera's avatar
dbarbera committed
259
260
            cons_val = core.Constant.int(ctx.i32, len(ctx.states))
            ctx.states[name] = cons_val
261
        elif name != 'START':
dbarbera's avatar
dbarbera committed
262
263
            cons_val = core.Constant.int(ctx.i32, val)
            ctx.states[name] = cons_val
264

265
    # Generate state var
dbarbera's avatar
dbarbera committed
266
267
268
    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)
269

270
271
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
272
        var_ty = ctx.type_of(ty)
dbarbera's avatar
dbarbera committed
273
        global_var = ctx.module.add_global_variable(var_ty, str(name))
274
        global_var.initializer = core.Constant.null(var_ty)
dbarbera's avatar
dbarbera committed
275
        ctx.scope.define(str(name).lower(), global_var)
276

dbarbera's avatar
dbarbera committed
277
278
279
    # Declare timer set/reset functions
    for timer in process.timers:
        # TODO: Should be uint?
dbarbera's avatar
dbarbera committed
280
281
        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
282

283
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
284
    for signal in process.output_signals:
285
        if 'type' in signal:
286
            param_tys = [core.Type.pointer(ctx.type_of(signal['type']))]
287
288
        else:
            param_tys = []
dbarbera's avatar
dbarbera committed
289
        decl_func(str(signal['name']), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
290

291
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
292
    for proc in [proc for proc in process.procedures if proc.external]:
293
        param_tys = [core.Type.pointer(ctx.type_of(p['type'])) for p in proc.fpar]
dbarbera's avatar
dbarbera committed
294
        decl_func(str(proc.inputString), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
295

296
297
    # Generate internal procedures
    for proc in process.content.inner_procedures:
298
        generate(proc)
299

300
    # Generate process functions
dbarbera's avatar
dbarbera committed
301
302
    generate_runtr_func(process)
    generate_startup_func(process)
303

304
305
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
306
        generate_input_signal(signal, mapping[signal['name']])
307

dbarbera's avatar
dbarbera committed
308
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
309

dbarbera's avatar
dbarbera committed
310
311
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
312
313


dbarbera's avatar
dbarbera committed
314
def generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
315
    ''' Generate code for the run_transition function '''
dbarbera's avatar
dbarbera committed
316
    func = decl_func('run_transition', ctx.void, [ctx.i32])
317

318
    ctx.open_scope()
319

320
321
322
323
324
    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
325
    ctx.builder = core.Builder.new(entry_block)
326
327

    # entry
dbarbera's avatar
dbarbera committed
328
329
330
331
    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)
332
333

    # cond
dbarbera's avatar
dbarbera committed
334
335
336
337
338
    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)
339
340

    # body
dbarbera's avatar
dbarbera committed
341
342
    ctx.builder.position_at_end(body_block)
    switch = ctx.builder.switch(func.args[0], exit_block)
343
344
345
346

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
dbarbera's avatar
dbarbera committed
347
        const = core.Constant.int(ctx.i32, idx)
348
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
349
        ctx.builder.position_at_end(tr_block)
350
        generate(tr)
dbarbera's avatar
dbarbera committed
351
352
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
353
354

    # exit
dbarbera's avatar
dbarbera committed
355
356
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
357

dbarbera's avatar
dbarbera committed
358
359
360
361
362
    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
363
364
365
    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
366

367
    ctx.close_scope()
368

369
370
371
372
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
373
def generate_startup_func(process):
dbarbera's avatar
dbarbera committed
374
    ''' Generate code for the startup function '''
dbarbera's avatar
dbarbera committed
375
    func = decl_func(ctx.name + '_startup', ctx.void, [])
376

377
    ctx.open_scope()
378

379
    entry_block = func.append_basic_block('entry')
dbarbera's avatar
dbarbera committed
380
    ctx.builder = core.Builder.new(entry_block)
381

382
383
384
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
385
            global_var = ctx.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
386
            generate_assign(global_var, expression(expr))
387

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

391
    ctx.close_scope()
392

393
394
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
395
396


dbarbera's avatar
dbarbera committed
397
def generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
398
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
399
    func_name = ctx.name + "_" + str(signal['name'])
400
401
    param_tys = []
    if 'type' in signal:
402
        param_tys.append(core.Type.pointer(ctx.type_of(signal['type'])))
403

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

406
    ctx.open_scope()
407

408
409
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
dbarbera's avatar
dbarbera committed
410
    ctx.builder = core.Builder.new(entry_block)
411

dbarbera's avatar
dbarbera committed
412
413
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('state'))
    switch = ctx.builder.switch(g_state_val, exit_block)
414

dbarbera's avatar
dbarbera committed
415
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
416
417
        if state_name.endswith('START'):
            continue
418
419
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
420
        ctx.builder.position_at_end(state_block)
421
422
423

        # TODO: Nested states

424
425
426
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
427
                var_ptr = ctx.scope.resolve(str(var_name))
428
429
430
                if is_struct_ptr(var_ptr):
                    generate_assign(var_ptr, func.args[0])
                else:
dbarbera's avatar
dbarbera committed
431
                    generate_assign(var_ptr, ctx.builder.load(func.args[0]))
432
            if input.transition:
dbarbera's avatar
dbarbera committed
433
434
                id_val = core.Constant.int(ctx.i32, input.transition_id)
                ctx.builder.call(ctx.funcs['run_transition'], [id_val])
435

dbarbera's avatar
dbarbera committed
436
        ctx.builder.ret_void()
437

dbarbera's avatar
dbarbera committed
438
439
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
440

441
    ctx.close_scope()
442

443
444
445
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
446
447
448
449
@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
450
451
452
    for out in output.output:
        name = out['outputName'].lower()

453
        if name == 'write':
dbarbera's avatar
dbarbera committed
454
            generate_write(out['params'])
dbarbera's avatar
dbarbera committed
455
            continue
456
        elif name == 'writeln':
dbarbera's avatar
dbarbera committed
457
            generate_writeln(out['params'])
458
            continue
dbarbera's avatar
dbarbera committed
459
        elif name == 'reset_timer':
dbarbera's avatar
dbarbera committed
460
            generate_reset_timer(out['params'])
dbarbera's avatar
dbarbera committed
461
462
            continue
        elif name == 'set_timer':
dbarbera's avatar
dbarbera committed
463
            generate_set_timer(out['params'])
dbarbera's avatar
dbarbera committed
464
465
            continue

dbarbera's avatar
dbarbera committed
466
        func = ctx.funcs[str(name).lower()]
467
468
469
470
471
472

        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
473
474
                p_var = ctx.builder.alloca(p_val.type, None)
                ctx.builder.store(p_val, p_var)
475
476
477
478
                params.append(p_var)
            else:
                params.append(p_val)

dbarbera's avatar
dbarbera committed
479
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
480
481


dbarbera's avatar
dbarbera committed
482
def generate_write(params):
dbarbera's avatar
dbarbera committed
483
    ''' Generate the code for the write operator '''
484
485
486
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
487

488
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
dbarbera's avatar
dbarbera committed
489
            fmt_val = get_string_cons('% d')
dbarbera's avatar
dbarbera committed
490
491
            fmt_ptr = ctx.builder.gep(fmt_val, [ctx.zero, ctx.zero])
            ctx.builder.call(ctx.funcs['printf'], [fmt_ptr, expr_val])
492
        elif basic_ty.kind == 'RealType':
dbarbera's avatar
dbarbera committed
493
            fmt_val = get_string_cons('% .14E')
dbarbera's avatar
dbarbera committed
494
495
            fmt_ptr = ctx.builder.gep(fmt_val, [ctx.zero, ctx.zero])
            ctx.builder.call(ctx.funcs['printf'], [fmt_ptr, expr_val])
496
        elif basic_ty.kind == 'BooleanType':
dbarbera's avatar
dbarbera committed
497
            true_str_val = get_string_cons('TRUE')
dbarbera's avatar
dbarbera committed
498
            true_str_ptr = ctx.builder.gep(true_str_val, [ctx.zero, ctx.zero])
dbarbera's avatar
dbarbera committed
499
            false_str_val = get_string_cons('FALSE')
dbarbera's avatar
dbarbera committed
500
501
502
            false_str_ptr = ctx.builder.gep(false_str_val, [ctx.zero, ctx.zero])
            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
503
504
        elif basic_ty.kind in ['StringType', 'OctetStringType']:
            fmt_val = get_string_cons('%s')
dbarbera's avatar
dbarbera committed
505
506
507
            fmt_ptr = ctx.builder.gep(fmt_val, [ctx.zero, ctx.zero])
            arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.one])
            ctx.builder.call(ctx.funcs['printf'], [fmt_ptr, arr_ptr])
508
509
510
511
        else:
            raise NotImplementedError


dbarbera's avatar
dbarbera committed
512
def generate_writeln(params):
513
    ''' Generate the code for the writeln operator '''
dbarbera's avatar
dbarbera committed
514
    generate_write(params)
515

dbarbera's avatar
dbarbera committed
516
    zero = core.Constant.int(ctx.i32, 0)
dbarbera's avatar
dbarbera committed
517
    str_cons = get_string_cons('\n')
dbarbera's avatar
dbarbera committed
518
519
    str_ptr = ctx.builder.gep(str_cons, [zero, zero])
    ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
520
521


dbarbera's avatar
dbarbera committed
522
def generate_reset_timer(params):
dbarbera's avatar
dbarbera committed
523
    ''' Generate the code for the reset timer operator '''
dbarbera's avatar
dbarbera committed
524
    timer_id = params[0]
525
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
526
    reset_func = ctx.funcs[reset_func_name.lower()]
dbarbera's avatar
dbarbera committed
527

dbarbera's avatar
dbarbera committed
528
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
529
530


dbarbera's avatar
dbarbera committed
531
def generate_set_timer(params):
dbarbera's avatar
dbarbera committed
532
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
533
    timer_expr, timer_id = params
534
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
535
    set_func = ctx.funcs[set_func_name.lower()]
dbarbera's avatar
dbarbera committed
536
537
538

    expr_val = expression(timer_expr)

dbarbera's avatar
dbarbera committed
539
540
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
541

dbarbera's avatar
dbarbera committed
542
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
543
544
545
546


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
547
    ''' Generate the code of a list of assignments '''
548
549
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
550
551
552
553
554


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
555
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
556
557
558
559


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
560
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
561
562
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
563
            generate_for_range(loop)
dbarbera's avatar
dbarbera committed
564
        else:
dbarbera's avatar
dbarbera committed
565
            generate_for_iterable(loop)
dbarbera's avatar
dbarbera committed
566
567


dbarbera's avatar
dbarbera committed
568
def generate_for_range(loop):
dbarbera's avatar
dbarbera committed
569
    ''' Generate the code for a for x in range loop '''
dbarbera's avatar
dbarbera committed
570
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
571
572
573
574
575
    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('')

576
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
577

dbarbera's avatar
dbarbera committed
578
579
    loop_var = ctx.builder.alloca(ctx.i32, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
580
581
582

    if loop['range']['start']:
        start_val = expression(loop['range']['start'])
dbarbera's avatar
dbarbera committed
583
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
584
    else:
dbarbera's avatar
dbarbera committed
585
        ctx.builder.store(core.Constant.int(ctx.i32, 0), loop_var)
dbarbera's avatar
dbarbera committed
586
587

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

dbarbera's avatar
dbarbera committed
590
591
592
593
    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
594

dbarbera's avatar
dbarbera committed
595
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
596
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
597
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
598

dbarbera's avatar
dbarbera committed
599
600
601
602
603
604
    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
605

dbarbera's avatar
dbarbera committed
606
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
607

608
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
609
610


dbarbera's avatar
dbarbera committed
611
def generate_for_iterable(loop):
dbarbera's avatar
dbarbera committed
612
    ''' Generate the code for a for x in iterable loop'''
dbarbera's avatar
dbarbera committed
613
    seqof_asn1ty = find_basic_type(loop['list'].exprType)
614
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
615

dbarbera's avatar
dbarbera committed
616
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
617
618
619
620
621
622
623
624
625
626

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

627
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
628

dbarbera's avatar
dbarbera committed
629
630
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
631
    seqof_struct_ptr = expression(loop['list'])
632
633
634

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

dbarbera's avatar
dbarbera committed
639
    element_typ = array_ptr.type.pointee.element
640
641
642

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

dbarbera's avatar
dbarbera committed
647
648
    var_ptr = ctx.builder.alloca(element_typ, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
649

dbarbera's avatar
dbarbera committed
650
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
651
652

    # load block
dbarbera's avatar
dbarbera committed
653
654
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
655
    if element_typ.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
656
        generate_assign(var_ptr, ctx.builder.gep(array_ptr, [ctx.zero, idx_var]))
657
    else:
dbarbera's avatar
dbarbera committed
658
659
        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
660
661

    # body block
dbarbera's avatar
dbarbera committed
662
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
663
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
664
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
665
666

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

dbarbera's avatar
dbarbera committed
673
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
674

675
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
676

dbarbera's avatar
dbarbera committed
677

dbarbera's avatar
dbarbera committed
678
679
680
@singledispatch
def reference(prim):
    ''' Generate a variable reference '''
dbarbera's avatar
flake8    
dbarbera committed
681
    raise TypeError('Unsupported reference: ' + str(prim))
dbarbera's avatar
dbarbera committed
682
683
684
685
686


@reference.register(ogAST.PrimVariable)
def _prim_var_reference(prim):
    ''' Generate a primary variable reference '''
dbarbera's avatar
dbarbera committed
687
    return ctx.scope.resolve(str(prim.value[0]))
dbarbera's avatar
dbarbera committed
688
689
690
691
692


@reference.register(ogAST.PrimPath)
def _prim_path_reference(prim):
    ''' Generate a primary path reference '''
693
    var_name = prim.value[0].lower()
dbarbera's avatar
dbarbera committed
694
    var_ptr = ctx.scope.resolve(str(var_name))
dbarbera's avatar
dbarbera committed
695
696
697
698

    if not prim.value:
        return var_ptr

dbarbera's avatar
dbarbera committed
699
700
701
702
    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
703
                array_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.zero])
704
                # TODO: Refactor this
dbarbera's avatar
dbarbera committed
705
706
                if array_ptr.type.pointee.kind != core.TYPE_ARRAY:
                    # If is not an array this is a pointer to a variable size SeqOf
707
                    # The array is in the second field of the struct
dbarbera's avatar
dbarbera committed
708
                    var_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.one, idx_val])
709
                else:
dbarbera's avatar
dbarbera committed
710
                    var_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.zero, idx_val])
dbarbera's avatar
dbarbera committed
711
712
713
714
            else:
                raise NotImplementedError
        else:
            var_ty = var_ptr.type
dbarbera's avatar
dbarbera committed
715
716
717
718
            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
719
                var_ptr = field_ptr
dbarbera's avatar
dbarbera committed
720
721
            elif var_ty.pointee.name in ctx.unions:
                union = ctx.unions[var_ty.pointee.name]
dbarbera's avatar
dbarbera committed
722
                _, field_ty = union.kind(elem)
dbarbera's avatar
dbarbera committed
723
724
                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
725
726
            else:
                raise NotImplementedError
dbarbera's avatar
dbarbera committed
727
728
    return var_ptr

Maxime Perrotin's avatar
Maxime Perrotin committed
729
730
731

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
732
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
733
734
735
736
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
737
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
738
739
    ''' Generate the code for a variable expression '''
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
740
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
741
742


Maxime Perrotin's avatar
Maxime Perrotin committed
743
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
744
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
745
    ''' Generate the code for an of path expression '''
746
747
748
749
750
751
752
753
754
    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
755
756
757
758
759
760
761
    name = prim.value[0].lower()
    if name in specops_generators:
        generator = specops_generators[name]
        return generator(prim.value[1]['procParams'])

    return generate_access(prim)

762

dbarbera's avatar
dbarbera committed
763
764
def generate_access(prim):
    ''' Generate the code for an access '''
dbarbera's avatar
dbarbera committed
765
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
766
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
767
768


769
def generate_length(params):
dbarbera's avatar
dbarbera committed
770
    ''' Generate the code for the built-in length operation'''
dbarbera's avatar
dbarbera committed
771
    seq_ptr = reference(params[0])
dbarbera's avatar
dbarbera committed
772
    arr_ty = seq_ptr.type.pointee.elements[0]
dbarbera's avatar
dbarbera committed
773
    return core.Constant.int(ctx.i32, arr_ty.count)
774
775
776


def generate_present(params):
dbarbera's avatar
dbarbera committed
777
    ''' Generate the code for the built-in present operation'''
778
779
780
781
    raise NotImplementedError


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

dbarbera's avatar
dbarbera committed
785
    if expr_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
786
787
788
        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
789
    else:
dbarbera's avatar
dbarbera committed
790
        return ctx.builder.call(ctx.funcs['fabs'], [expr_val])
791
792
793


def generate_fix(params):
dbarbera's avatar
dbarbera committed
794
    ''' Generate the code for the built-in fix operation'''
dbarbera's avatar
dbarbera committed
795
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
796
    return ctx.builder.fptosi(expr_val, ctx.i32)
797
798
799


def generate_float(params):
dbarbera's avatar
dbarbera committed
800
    ''' Generate the code for the built-in float operation'''
dbarbera's avatar
dbarbera committed
801
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
802
    return ctx.builder.sitofp(expr_val, ctx.double)
803
804
805


def generate_power(params):
dbarbera's avatar
dbarbera committed
806
    ''' Generate the code for the built-in power operation'''
807
808
809
810
    left_val = expression(params[0])
    right_val = expression(params[1])

    if left_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
811
812
813
        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)
814
    else:
dbarbera's avatar
dbarbera committed
815
        return ctx.builder.call(ctx.funcs['powi'], [left_val, right_val])
816
817


Maxime Perrotin's avatar
Maxime Perrotin committed
818
819
820
821
822
823
824
825
826
827
828
829
@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
830
831
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
832
833
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
834

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