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

dbarbera's avatar
dbarbera committed
181

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


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

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


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

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

dbarbera's avatar
dbarbera committed
234

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

dbarbera's avatar
dbarbera committed
240

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

dbarbera's avatar
dbarbera committed
248
249
    global ctx
    ctx = Context(process)
250

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

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

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

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

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

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

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

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

302
    # Generate process functions
dbarbera's avatar
dbarbera committed
303
304
    generate_runtr_func(process)
    generate_startup_func(process)
305

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

dbarbera's avatar
dbarbera committed
310
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
311

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


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

320
    ctx.open_scope()
321

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

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

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

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

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

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

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

369
    ctx.close_scope()
370

371
372
373
374
    func.verify()
    return func


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

379
    ctx.open_scope()
380

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

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

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

393
    ctx.close_scope()
394

395
396
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
397
398


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

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

408
    ctx.open_scope()
409

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

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

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

        # TODO: Nested states

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

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

dbarbera's avatar
dbarbera committed
440
441
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
442

443
    ctx.close_scope()
444

445
446
447
    func.verify()


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

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

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

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

dbarbera's avatar
dbarbera committed
481
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
482
483


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

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


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

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


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

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


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

    expr_val = expression(timer_expr)

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

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


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


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


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


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

578
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
579

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

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

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

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

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

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

dbarbera's avatar
dbarbera committed
608
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
609

610
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
611
612


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

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

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

629
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
630

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

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

dbarbera's avatar
dbarbera committed
641
    element_typ = array_ptr.type.pointee.element
642
643
644

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

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

dbarbera's avatar
dbarbera committed
652
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
653
654

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

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

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

dbarbera's avatar
dbarbera committed
675
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
676

677
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
678

dbarbera's avatar
dbarbera committed
679

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


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


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

    if not prim.value:
        return var_ptr

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

Maxime Perrotin's avatar
Maxime Perrotin committed
731
732
733

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


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


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

    return generate_access(prim)

764

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


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


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


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

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


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


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


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

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


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

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