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
        # 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
    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:
146
            struct = self.decl_struct(['nCount', 'arr'], [ctx.i32, array_ty], name)
147
        else:
148
            struct = self.decl_struct(['arr'], [array_ty], name)
149
150
151
152
153
154
155
156
157
158

        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))
159
        struct = self.decl_struct(field_names, field_types, name)
160
161
162
163
164
165
166

        return struct.ty

    def _type_of_choice(self, name, choice_ty):
        ''' Return the equivalent LL type of a Choice ASN.1 type '''
        field_names = []
        field_types = []
dbarbera's avatar
dbarbera committed
167
168
169
        for field_name, field_ty in choice_ty.Children.viewitems():
            field_names.append(field_name)
            field_types.append(self.type_of(field_ty.type))
170

171
        union = self.decl_union(field_names, field_types, name)
172
173
174
175
176
177
        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)
178
        struct = self.decl_struct(['nCount', 'arr'], [ctx.i32, arr_ty], name)
179
180
        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])

193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
    def decl_func(self, name, return_ty, param_tys, extern=False):
        ''' Declare a function '''
        func_ty = core.Type.function(return_ty, param_tys)
        func_name = ("%s_RI_%s" % (self.name, name)) if extern else name
        func = core.Function.new(self.module, func_ty, func_name)
        self.funcs[name.lower()] = func
        return func

    def decl_struct(self, field_names, field_types, name=None):
        ''' Declare a struct '''
        name = name if name else "struct.%s" % len(self.structs)
        struct = StructType(name, field_names, field_types)
        self.structs[name] = struct
        return struct

    def decl_union(self, field_names, field_types, name=None):
        name = name if name else "union.%s" % len(self.structs)
        union = UnionType(name, field_names, field_types)
        self.unions[name] = union
        return union

dbarbera's avatar
dbarbera committed
214

215
216
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
217
        self.name = name
218
219
220
221
222
        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
223
224


dbarbera's avatar
dbarbera committed
225
226
227
228
229
230
231
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
232
233
        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
234
235
236
237
238
239

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


240
241
242
class Scope:
    def __init__(self, parent=None):
        self.vars = {}
dbarbera's avatar
dbarbera committed
243
        self.labels = {}
244
245
246
247
248
249
250
251
252
253
254
255
        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
256
            raise NameError("name '%s' is not defined" % name)
257

dbarbera's avatar
dbarbera committed
258
259
260
261
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
262
            func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
263
264
265
266
            label_block = func.append_basic_block(name)
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
267

Maxime Perrotin's avatar
Maxime Perrotin committed
268
269
270
271
@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
272

dbarbera's avatar
dbarbera committed
273

Maxime Perrotin's avatar
Maxime Perrotin committed
274
275
276
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
277
    ''' Generate LLVM IR code '''
dbarbera's avatar
dbarbera committed
278
279
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
280

dbarbera's avatar
dbarbera committed
281
282
    global ctx
    ctx = Context(process)
283

dbarbera's avatar
dbarbera committed
284
285
286
287
288
289
    # 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
290

291
292
    # Initialize states
    for name, val in process.mapping.viewitems():
293
        if not name.endswith('START'):
dbarbera's avatar
dbarbera committed
294
295
            cons_val = core.Constant.int(ctx.i32, len(ctx.states))
            ctx.states[name] = cons_val
296
        elif name != 'START':
dbarbera's avatar
dbarbera committed
297
298
            cons_val = core.Constant.int(ctx.i32, val)
            ctx.states[name] = cons_val
299

300
    # Generate state var
dbarbera's avatar
dbarbera committed
301
302
303
    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)
304

305
306
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
307
        var_ty = ctx.type_of(ty)
dbarbera's avatar
dbarbera committed
308
        global_var = ctx.module.add_global_variable(var_ty, str(name))
309
        global_var.initializer = core.Constant.null(var_ty)
dbarbera's avatar
dbarbera committed
310
        ctx.scope.define(str(name).lower(), global_var)
311

dbarbera's avatar
dbarbera committed
312
313
314
    # Declare timer set/reset functions
    for timer in process.timers:
        # TODO: Should be uint?
315
316
        ctx.decl_func("set_%s" % str(timer), ctx.void, [ctx.i32_ptr], True)
        ctx.decl_func("reset_%s" % str(timer), ctx.void, [], True)
dbarbera's avatar
dbarbera committed
317

318
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
319
    for signal in process.output_signals:
320
        if 'type' in signal:
321
            param_tys = [core.Type.pointer(ctx.type_of(signal['type']))]
322
323
        else:
            param_tys = []
324
        ctx.decl_func(str(signal['name']), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
325

326
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
327
    for proc in [proc for proc in process.procedures if proc.external]:
328
        param_tys = [core.Type.pointer(ctx.type_of(p['type'])) for p in proc.fpar]
329
        ctx.decl_func(str(proc.inputString), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
330

331
332
    # Generate internal procedures
    for proc in process.content.inner_procedures:
333
        generate(proc)
334

335
    # Generate process functions
dbarbera's avatar
dbarbera committed
336
337
    generate_runtr_func(process)
    generate_startup_func(process)
338

339
340
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
341
        generate_input_signal(signal, mapping[signal['name']])
342

dbarbera's avatar
dbarbera committed
343
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
344

dbarbera's avatar
dbarbera committed
345
346
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
347
348


dbarbera's avatar
dbarbera committed
349
def generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
350
    ''' Generate code for the run_transition function '''
351
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
352

353
    ctx.open_scope()
354

355
356
357
358
359
    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
360
    ctx.builder = core.Builder.new(entry_block)
361
362

    # entry
dbarbera's avatar
dbarbera committed
363
364
365
366
    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)
367
368

    # cond
dbarbera's avatar
dbarbera committed
369
370
371
372
373
    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)
374
375

    # body
dbarbera's avatar
dbarbera committed
376
377
    ctx.builder.position_at_end(body_block)
    switch = ctx.builder.switch(func.args[0], exit_block)
378
379
380
381

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
dbarbera's avatar
dbarbera committed
382
        const = core.Constant.int(ctx.i32, idx)
383
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
384
        ctx.builder.position_at_end(tr_block)
385
        generate(tr)
dbarbera's avatar
dbarbera committed
386
387
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
388
389

    # exit
dbarbera's avatar
dbarbera committed
390
391
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
392

dbarbera's avatar
dbarbera committed
393
394
395
396
397
    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
398
399
400
    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
401

402
    ctx.close_scope()
403

404
405
406
407
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
408
def generate_startup_func(process):
dbarbera's avatar
dbarbera committed
409
    ''' Generate code for the startup function '''
410
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
411

412
    ctx.open_scope()
413

414
    entry_block = func.append_basic_block('entry')
dbarbera's avatar
dbarbera committed
415
    ctx.builder = core.Builder.new(entry_block)
416

417
418
419
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
420
            global_var = ctx.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
421
            generate_assign(global_var, expression(expr))
422

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

426
    ctx.close_scope()
427

428
429
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
430
431


dbarbera's avatar
dbarbera committed
432
def generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
433
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
434
    func_name = ctx.name + "_" + str(signal['name'])
435
436
    param_tys = []
    if 'type' in signal:
437
        param_tys.append(core.Type.pointer(ctx.type_of(signal['type'])))
438

439
    func = ctx.decl_func(func_name, ctx.void, param_tys)
440

441
    ctx.open_scope()
442

443
444
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
dbarbera's avatar
dbarbera committed
445
    ctx.builder = core.Builder.new(entry_block)
446

dbarbera's avatar
dbarbera committed
447
448
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('state'))
    switch = ctx.builder.switch(g_state_val, exit_block)
449

dbarbera's avatar
dbarbera committed
450
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
451
452
        if state_name.endswith('START'):
            continue
453
454
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
455
        ctx.builder.position_at_end(state_block)
456
457
458

        # TODO: Nested states

459
460
461
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
462
                var_ptr = ctx.scope.resolve(str(var_name))
463
464
465
                if is_struct_ptr(var_ptr):
                    generate_assign(var_ptr, func.args[0])
                else:
dbarbera's avatar
dbarbera committed
466
                    generate_assign(var_ptr, ctx.builder.load(func.args[0]))
467
            if input.transition:
dbarbera's avatar
dbarbera committed
468
469
                id_val = core.Constant.int(ctx.i32, input.transition_id)
                ctx.builder.call(ctx.funcs['run_transition'], [id_val])
470

dbarbera's avatar
dbarbera committed
471
        ctx.builder.ret_void()
472

dbarbera's avatar
dbarbera committed
473
474
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
475

476
    ctx.close_scope()
477

478
479
480
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
481
482
483
484
@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
485
486
487
    for out in output.output:
        name = out['outputName'].lower()

488
        if name == 'write':
dbarbera's avatar
dbarbera committed
489
            generate_write(out['params'])
dbarbera's avatar
dbarbera committed
490
            continue
491
        elif name == 'writeln':
dbarbera's avatar
dbarbera committed
492
            generate_writeln(out['params'])
493
            continue
dbarbera's avatar
dbarbera committed
494
        elif name == 'reset_timer':
dbarbera's avatar
dbarbera committed
495
            generate_reset_timer(out['params'])
dbarbera's avatar
dbarbera committed
496
497
            continue
        elif name == 'set_timer':
dbarbera's avatar
dbarbera committed
498
            generate_set_timer(out['params'])
dbarbera's avatar
dbarbera committed
499
500
            continue

dbarbera's avatar
dbarbera committed
501
        func = ctx.funcs[str(name).lower()]
502
503
504
505
506
507

        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
508
509
                p_var = ctx.builder.alloca(p_val.type, None)
                ctx.builder.store(p_val, p_var)
510
511
512
513
                params.append(p_var)
            else:
                params.append(p_val)

dbarbera's avatar
dbarbera committed
514
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
515
516


dbarbera's avatar
dbarbera committed
517
def generate_write(params):
dbarbera's avatar
dbarbera committed
518
    ''' Generate the code for the write operator '''
519
520
521
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
522

523
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
524
525
            fmt_str_ptr = ctx.string_ptr('% d')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
526
        elif basic_ty.kind == 'RealType':
527
528
            fmt_str_ptr = ctx.string_ptr('% .14E')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
529
        elif basic_ty.kind == 'BooleanType':
530
531
            true_str_ptr = ctx.string_ptr('TRUE')
            false_str_ptr = ctx.string_ptr('FALSE')
dbarbera's avatar
dbarbera committed
532
533
            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
534
        elif basic_ty.kind in ['StringType', 'OctetStringType']:
535
            fmt_str_ptr = ctx.string_ptr('%s')
dbarbera's avatar
dbarbera committed
536
            arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.one])
537
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, arr_ptr])
538
539
540
541
        else:
            raise NotImplementedError


dbarbera's avatar
dbarbera committed
542
def generate_writeln(params):
543
    ''' Generate the code for the writeln operator '''
dbarbera's avatar
dbarbera committed
544
    generate_write(params)
545

546
    str_ptr = ctx.string_ptr('\n')
dbarbera's avatar
dbarbera committed
547
    ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
548
549


dbarbera's avatar
dbarbera committed
550
def generate_reset_timer(params):
dbarbera's avatar
dbarbera committed
551
    ''' Generate the code for the reset timer operator '''
dbarbera's avatar
dbarbera committed
552
    timer_id = params[0]
553
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
554
    reset_func = ctx.funcs[reset_func_name.lower()]
dbarbera's avatar
dbarbera committed
555

dbarbera's avatar
dbarbera committed
556
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
557
558


dbarbera's avatar
dbarbera committed
559
def generate_set_timer(params):
dbarbera's avatar
dbarbera committed
560
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
561
    timer_expr, timer_id = params
562
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
563
    set_func = ctx.funcs[set_func_name.lower()]
dbarbera's avatar
dbarbera committed
564
565
566

    expr_val = expression(timer_expr)

dbarbera's avatar
dbarbera committed
567
568
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
569

dbarbera's avatar
dbarbera committed
570
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
571
572
573
574


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
575
    ''' Generate the code of a list of assignments '''
576
577
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
578
579
580
581
582


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
583
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
584
585
586
587


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
588
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
589
590
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
591
            generate_for_range(loop)
dbarbera's avatar
dbarbera committed
592
        else:
dbarbera's avatar
dbarbera committed
593
            generate_for_iterable(loop)
dbarbera's avatar
dbarbera committed
594
595


dbarbera's avatar
dbarbera committed
596
def generate_for_range(loop):
dbarbera's avatar
dbarbera committed
597
    ''' Generate the code for a for x in range loop '''
dbarbera's avatar
dbarbera committed
598
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
599
600
601
602
603
    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('')

604
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
605

dbarbera's avatar
dbarbera committed
606
607
    loop_var = ctx.builder.alloca(ctx.i32, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
608
609
610

    if loop['range']['start']:
        start_val = expression(loop['range']['start'])
dbarbera's avatar
dbarbera committed
611
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
612
    else:
dbarbera's avatar
dbarbera committed
613
        ctx.builder.store(core.Constant.int(ctx.i32, 0), loop_var)
dbarbera's avatar
dbarbera committed
614
615

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

dbarbera's avatar
dbarbera committed
618
619
620
621
    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
622

dbarbera's avatar
dbarbera committed
623
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
624
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
625
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
626

dbarbera's avatar
dbarbera committed
627
628
629
630
631
632
    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
633

dbarbera's avatar
dbarbera committed
634
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
635

636
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
637
638


dbarbera's avatar
dbarbera committed
639
def generate_for_iterable(loop):
dbarbera's avatar
dbarbera committed
640
    ''' Generate the code for a for x in iterable loop'''
dbarbera's avatar
dbarbera committed
641
    seqof_asn1ty = find_basic_type(loop['list'].exprType)
642
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
643

dbarbera's avatar
dbarbera committed
644
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
645
646
647
648
649
650
651
652
653
654

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

655
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
656

dbarbera's avatar
dbarbera committed
657
658
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
659
    seqof_struct_ptr = expression(loop['list'])
660
661
662

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

dbarbera's avatar
dbarbera committed
667
    element_typ = array_ptr.type.pointee.element
668
669
670

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

dbarbera's avatar
dbarbera committed
675
676
    var_ptr = ctx.builder.alloca(element_typ, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
677

dbarbera's avatar
dbarbera committed
678
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
679
680

    # load block
dbarbera's avatar
dbarbera committed
681
682
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
683
    if element_typ.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
684
        generate_assign(var_ptr, ctx.builder.gep(array_ptr, [ctx.zero, idx_var]))
685
    else:
dbarbera's avatar
dbarbera committed
686
687
        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
688
689

    # body block
dbarbera's avatar
dbarbera committed
690
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
691
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
692
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
693
694

    # cond block
dbarbera's avatar
dbarbera committed
695
696
697
698
699
    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
700

dbarbera's avatar
dbarbera committed
701
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
702

703
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
704

dbarbera's avatar
dbarbera committed
705

dbarbera's avatar
dbarbera committed
706
707
708
@singledispatch
def reference(prim):
    ''' Generate a variable reference '''
dbarbera's avatar
flake8    
dbarbera committed
709
    raise TypeError('Unsupported reference: ' + str(prim))
dbarbera's avatar
dbarbera committed
710
711
712
713
714


@reference.register(ogAST.PrimVariable)
def _prim_var_reference(prim):
    ''' Generate a primary variable reference '''
dbarbera's avatar
dbarbera committed
715
    return ctx.scope.resolve(str(prim.value[0]))
dbarbera's avatar
dbarbera committed
716
717
718
719
720


@reference.register(ogAST.PrimPath)
def _prim_path_reference(prim):
    ''' Generate a primary path reference '''
721
    var_name = prim.value[0].lower()
dbarbera's avatar
dbarbera committed
722
    var_ptr = ctx.scope.resolve(str(var_name))
dbarbera's avatar
dbarbera committed
723
724
725
726

    if not prim.value:
        return var_ptr

dbarbera's avatar
dbarbera committed
727
728
729
730
    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
731
                array_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.zero])
732
                # TODO: Refactor this
dbarbera's avatar
dbarbera committed
733
734
                if array_ptr.type.pointee.kind != core.TYPE_ARRAY:
                    # If is not an array this is a pointer to a variable size SeqOf
735
                    # The array is in the second field of the struct
dbarbera's avatar
dbarbera committed
736
                    var_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.one, idx_val])
737
                else:
dbarbera's avatar
dbarbera committed
738
                    var_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.zero, idx_val])
dbarbera's avatar
dbarbera committed
739
740
741
742
            else:
                raise NotImplementedError
        else:
            var_ty = var_ptr.type
dbarbera's avatar
dbarbera committed
743
744
745
746
            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
747
                var_ptr = field_ptr
dbarbera's avatar
dbarbera committed
748
749
            elif var_ty.pointee.name in ctx.unions:
                union = ctx.unions[var_ty.pointee.name]
dbarbera's avatar
dbarbera committed
750
                _, field_ty = union.kind(elem)
dbarbera's avatar
dbarbera committed
751
752
                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
753
754
            else:
                raise NotImplementedError
dbarbera's avatar
dbarbera committed
755
756
    return var_ptr

Maxime Perrotin's avatar
Maxime Perrotin committed
757
758
759

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
760
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
761
762
763
764
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
765
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
766
767
    ''' Generate the code for a variable expression '''
    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


Maxime Perrotin's avatar
Maxime Perrotin committed
771
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
772
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
773
    ''' Generate the code for an of path expression '''
774
775
776
777
778
779
780
781
782
    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
783
784
785
786
787
788
789
    name = prim.value[0].lower()
    if name in specops_generators:
        generator = specops_generators[name]
        return generator(prim.value[1]['procParams'])

    return generate_access(prim)

790

dbarbera's avatar
dbarbera committed
791
792
def generate_access(prim):
    ''' Generate the code for an access '''
dbarbera's avatar
dbarbera committed
793
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
794
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
795
796


797
def generate_length(params):
dbarbera's avatar
dbarbera committed
798
    ''' Generate the code for the built-in length operation'''
dbarbera's avatar
dbarbera committed
799
    seq_ptr = reference(params[0])
dbarbera's avatar
dbarbera committed
800
    arr_ty = seq_ptr.type.pointee.elements[0]
dbarbera's avatar
dbarbera committed
801
    return core.Constant.int(ctx.i32, arr_ty.count)
802
803
804


def generate_present(params):
dbarbera's avatar
dbarbera committed
805
    ''' Generate the code for the built-in present operation'''
806
807
808
809
    raise NotImplementedError


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

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


def generate_fix(params):
dbarbera's avatar
dbarbera committed
822
    ''' Generate the code for the built-in fix operation'''
dbarbera's avatar
dbarbera committed
823
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
824
    return ctx.builder.fptosi(expr_val, ctx.i32)
825
826
827


def generate_float(params):
dbarbera's avatar
dbarbera committed
828
    ''' Generate the code for the built-in float operation'''
dbarbera's avatar
dbarbera committed
829
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
830
    return ctx.builder.sitofp(expr_val, ctx.double)
831
832
833


def generate_power(params):
dbarbera's avatar
dbarbera committed
834
    ''' Generate the code for the built-in power operation'''
835
836
837
838
    left_val = expression(params[0])
    right_val = expression(params[1])

    if left_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
839
840
841
        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)
842
    else:
dbarbera's avatar
dbarbera committed
843
        return ctx.builder.call(ctx.funcs['powi'], [left_val, right_val])
844
845


Maxime Perrotin's avatar
Maxime Perrotin committed
846
847
848
849
850
851
852
853
854
855
856
857
@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
858
859
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
860
861
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
862

863
864
    if lefttmp.type.kind == core.TYPE_INTEGER:
        if expr.operand == '+':
dbarbera's avatar
dbarbera committed
865
            return ctx.builder.add(lefttmp, righttmp, 'addtmp')
866
        elif expr.operand == '-':
dbarbera's avatar
dbarbera committed
867
            return ctx.builder.sub(lefttmp, righttmp, 'subtmp')
868
        elif expr.operand == '*':
dbarbera's avatar
dbarbera committed
869
            return ctx.builder.mul(lefttmp, righttmp, 'multmp')
870
        elif expr.operand == '/':
dbarbera's avatar
dbarbera committed
871
            return ctx.builder.sdiv(lefttmp, righttmp, 'divtmp')
872
        elif expr.operand == 'mod':
873
            # l mod r == (((l rem r) + r) rem r)
dbarbera's avatar
dbarbera committed
874
875
876
            remtmp = ctx.builder.srem(lefttmp, righttmp)
            addtmp = ctx.builder.add(remtmp, righttmp)
            return ctx.builder.srem(addtmp, righttmp, 'modtmp')
877
        elif expr.operand == 'rem':
dbarbera's avatar
dbarbera committed
878
            return ctx.builder.srem(lefttmp, righttmp, 'remtmp')
879
        elif expr.operand == '<':
dbarbera's avatar
dbarbera committed
880
            return ctx.builder.icmp(core.ICMP_SLT, lefttmp, righttmp, 'lttmp')
881
        elif expr.operand == '<=':
dbarbera's avatar
dbarbera committed
882
            return ctx.builder.icmp(core.ICMP_SLE, lefttmp, righttmp, 'letmp')
883
        elif expr.operand == '=':
dbarbera's avatar
dbarbera committed
884
            return ctx.builder.icmp(core.ICMP_EQ, lefttmp, righttmp, 'eqtmp')
dbarbera's avatar