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

"""
    OpenGEODE - A tiny SDL Editor for TASTE

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

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

    Copyright (c) 2012-2013 European Space Agency

    Designed and implemented by Maxime Perrotin

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

import logging
21

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

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

LOG = logging.getLogger(__name__)

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

Maxime Perrotin's avatar
Maxime Perrotin committed
32

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


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

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

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

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

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

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

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

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

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

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

101
102
103
104
105
106
107
108
109
110
111
112
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
    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:
147
            struct = self.decl_struct(['nCount', 'arr'], [ctx.i32, array_ty], name)
148
        else:
149
            struct = self.decl_struct(['arr'], [array_ty], name)
150

151
152
153
        struct_ptr = core.Type.pointer(struct.ty)
        self.decl_func("%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])

154
155
156
157
158
159
        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 = []
160

161
162
163
        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))
164

165
        struct = self.decl_struct(field_names, field_types, name)
166

167
168
169
        struct_ptr = core.Type.pointer(struct.ty)
        self.decl_func("%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])

170
171
172
173
174
175
        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
176
177
178

        for idx, field_name in enumerate(Helper.sorted_fields(choice_ty)):
            # enum values used in choice determinant/present
dbarbera's avatar
dbarbera committed
179
            self.enums[field_name.replace('-', '_')] = core.Constant.int(self.i32, idx)
dbarbera's avatar
dbarbera committed
180

dbarbera's avatar
dbarbera committed
181
            field_names.append(field_name.replace('-', '_'))
dbarbera's avatar
dbarbera committed
182
            field_types.append(self.type_of(choice_ty.Children[field_name].type))
183

184
        union = self.decl_union(field_names, field_types, name)
185
186
187
188

        union_ptr = core.Type.pointer(union.ty)
        self.decl_func("%s_Equal" % name, self.i1, [union_ptr, union_ptr])

189
190
191
192
193
194
        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)
195
        struct = self.decl_struct(['nCount', 'arr'], [ctx.i32, arr_ty], name)
196
197
198
199

        struct_ptr = core.Type.pointer(struct.ty)
        self.decl_func("%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])

200
201
        return struct.ty

202
203
204
205
206
207
208
209
210
211
212
213
    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])

214
215
216
217
218
219
220
221
222
223
224
    def decl_func(self, name, return_ty, param_tys, extern=False):
        ''' Declare a function '''
        func_ty = core.Type.function(return_ty, param_tys)
        func_name = ("%s_RI_%s" % (self.name, name)) if extern else name
        func = core.Function.new(self.module, func_ty, func_name)
        self.funcs[name.lower()] = func
        return func

    def decl_struct(self, field_names, field_types, name=None):
        ''' Declare a struct '''
        name = name if name else "struct.%s" % len(self.structs)
dbarbera's avatar
dbarbera committed
225
        name = name.replace('-', '_')
226
227
228
229
        struct = StructType(name, field_names, field_types)
        self.structs[name] = struct
        return struct

dbarbera's avatar
dbarbera committed
230
231
232
233
    def resolve_struct(self, name):
        ''' Return the struct associated to a name '''
        return self.structs[name.replace('-', '_')]

234
235
    def decl_union(self, field_names, field_types, name=None):
        name = name if name else "union.%s" % len(self.structs)
dbarbera's avatar
dbarbera committed
236
        name = name.replace('-', '_')
237
238
239
240
        union = UnionType(name, field_names, field_types)
        self.unions[name] = union
        return union

dbarbera's avatar
dbarbera committed
241
242
243
244
    def resolve_union(self, name):
        ''' Return the union associated to a name '''
        return self.unions[name.replace('-', '_')]

dbarbera's avatar
dbarbera committed
245

246
247
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
248
        self.name = name
249
250
251
252
253
        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
254
255


dbarbera's avatar
dbarbera committed
256
257
258
259
260
261
262
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
263
264
        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
265
266
267
268
269
270

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


271
272
273
class Scope:
    def __init__(self, parent=None):
        self.vars = {}
dbarbera's avatar
dbarbera committed
274
        self.labels = {}
275
276
277
278
279
280
281
282
283
284
285
286
        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
287
            raise NameError("name '%s' is not defined" % name)
288

dbarbera's avatar
dbarbera committed
289
290
291
292
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
293
            func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
294
295
296
297
            label_block = func.append_basic_block(name)
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
298

Maxime Perrotin's avatar
Maxime Perrotin committed
299
300
301
302
@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
303

dbarbera's avatar
dbarbera committed
304

Maxime Perrotin's avatar
Maxime Perrotin committed
305
306
307
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
308
    ''' Generate LLVM IR code '''
dbarbera's avatar
dbarbera committed
309
310
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
311

dbarbera's avatar
dbarbera committed
312
313
    global ctx
    ctx = Context(process)
314

dbarbera's avatar
dbarbera committed
315
316
317
318
319
320
    # 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
321

322
323
    # Initialize states
    for name, val in process.mapping.viewitems():
324
        if not name.endswith('START'):
dbarbera's avatar
dbarbera committed
325
326
            cons_val = core.Constant.int(ctx.i32, len(ctx.states))
            ctx.states[name] = cons_val
327
        elif name != 'START':
dbarbera's avatar
dbarbera committed
328
329
            cons_val = core.Constant.int(ctx.i32, val)
            ctx.states[name] = cons_val
330

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

336
337
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
338
        var_ty = ctx.type_of(ty)
dbarbera's avatar
dbarbera committed
339
        global_var = ctx.module.add_global_variable(var_ty, str(name))
340
        global_var.initializer = core.Constant.null(var_ty)
dbarbera's avatar
dbarbera committed
341
        ctx.scope.define(str(name).lower(), global_var)
342

dbarbera's avatar
dbarbera committed
343
344
345
    # Declare timer set/reset functions
    for timer in process.timers:
        # TODO: Should be uint?
346
347
        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
348

349
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
350
    for signal in process.output_signals:
351
        if 'type' in signal:
352
            param_tys = [core.Type.pointer(ctx.type_of(signal['type']))]
353
354
        else:
            param_tys = []
355
        ctx.decl_func(str(signal['name']), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
356

357
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
358
    for proc in [proc for proc in process.procedures if proc.external]:
359
        param_tys = [core.Type.pointer(ctx.type_of(p['type'])) for p in proc.fpar]
360
        ctx.decl_func(str(proc.inputString), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
361

362
363
    # Generate internal procedures
    for proc in process.content.inner_procedures:
364
        generate(proc)
365

366
    # Generate process functions
dbarbera's avatar
dbarbera committed
367
368
    generate_runtr_func(process)
    generate_startup_func(process)
369

370
371
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
372
        generate_input_signal(signal, mapping[signal['name']])
373

dbarbera's avatar
dbarbera committed
374
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
375

dbarbera's avatar
dbarbera committed
376
377
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
378
379


dbarbera's avatar
dbarbera committed
380
def generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
381
    ''' Generate code for the run_transition function '''
382
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
383

384
    ctx.open_scope()
385

386
387
388
389
390
    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
391
    ctx.builder = core.Builder.new(entry_block)
392
393

    # entry
dbarbera's avatar
dbarbera committed
394
395
396
397
    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)
398
399

    # cond
dbarbera's avatar
dbarbera committed
400
401
402
403
404
    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)
405
406

    # body
dbarbera's avatar
dbarbera committed
407
408
    ctx.builder.position_at_end(body_block)
    switch = ctx.builder.switch(func.args[0], exit_block)
409
410
411
412

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
dbarbera's avatar
dbarbera committed
413
        const = core.Constant.int(ctx.i32, idx)
414
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
415
        ctx.builder.position_at_end(tr_block)
416
        generate(tr)
dbarbera's avatar
dbarbera committed
417
418
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
419
420

    # exit
dbarbera's avatar
dbarbera committed
421
422
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
423

dbarbera's avatar
dbarbera committed
424
425
426
427
428
    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
429
430
431
    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
432

433
    ctx.close_scope()
434

435
436
437
438
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
439
def generate_startup_func(process):
dbarbera's avatar
dbarbera committed
440
    ''' Generate code for the startup function '''
441
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
442

443
    ctx.open_scope()
444

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

448
449
450
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
451
            global_var = ctx.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
452
            generate_assign(global_var, expression(expr))
453

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

457
    ctx.close_scope()
458

459
460
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
461
462


dbarbera's avatar
dbarbera committed
463
def generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
464
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
465
    func_name = ctx.name + "_" + str(signal['name'])
466
467
    param_tys = []
    if 'type' in signal:
468
        param_tys.append(core.Type.pointer(ctx.type_of(signal['type'])))
469

470
    func = ctx.decl_func(func_name, ctx.void, param_tys)
471

472
    ctx.open_scope()
473

474
475
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
dbarbera's avatar
dbarbera committed
476
    ctx.builder = core.Builder.new(entry_block)
477

dbarbera's avatar
dbarbera committed
478
479
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('state'))
    switch = ctx.builder.switch(g_state_val, exit_block)
480

dbarbera's avatar
dbarbera committed
481
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
482
483
        if state_name.endswith('START'):
            continue
484
485
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
486
        ctx.builder.position_at_end(state_block)
487
488
489

        # TODO: Nested states

490
491
492
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
493
                var_ptr = ctx.scope.resolve(str(var_name))
494
495
496
                if is_struct_ptr(var_ptr):
                    generate_assign(var_ptr, func.args[0])
                else:
dbarbera's avatar
dbarbera committed
497
                    generate_assign(var_ptr, ctx.builder.load(func.args[0]))
498
            if input.transition:
dbarbera's avatar
dbarbera committed
499
500
                id_val = core.Constant.int(ctx.i32, input.transition_id)
                ctx.builder.call(ctx.funcs['run_transition'], [id_val])
501

dbarbera's avatar
dbarbera committed
502
        ctx.builder.ret_void()
503

dbarbera's avatar
dbarbera committed
504
505
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
506

507
    ctx.close_scope()
508

509
510
511
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
512
513
514
515
@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
516
517
518
    for out in output.output:
        name = out['outputName'].lower()

519
        if name == 'write':
dbarbera's avatar
dbarbera committed
520
            generate_write(out['params'])
dbarbera's avatar
dbarbera committed
521
            continue
522
        elif name == 'writeln':
dbarbera's avatar
dbarbera committed
523
            generate_writeln(out['params'])
524
            continue
dbarbera's avatar
dbarbera committed
525
        elif name == 'reset_timer':
dbarbera's avatar
dbarbera committed
526
            generate_reset_timer(out['params'])
dbarbera's avatar
dbarbera committed
527
528
            continue
        elif name == 'set_timer':
dbarbera's avatar
dbarbera committed
529
            generate_set_timer(out['params'])
dbarbera's avatar
dbarbera committed
530
531
            continue

dbarbera's avatar
dbarbera committed
532
        func = ctx.funcs[str(name).lower()]
533
534
535
536
537
538

        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
539
540
                p_var = ctx.builder.alloca(p_val.type, None)
                ctx.builder.store(p_val, p_var)
541
542
543
544
                params.append(p_var)
            else:
                params.append(p_val)

dbarbera's avatar
dbarbera committed
545
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
546
547


dbarbera's avatar
dbarbera committed
548
def generate_write(params):
dbarbera's avatar
dbarbera committed
549
    ''' Generate the code for the write operator '''
550
551
552
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
553

554
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
555
556
            fmt_str_ptr = ctx.string_ptr('% d')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
557
        elif basic_ty.kind == 'RealType':
558
559
            fmt_str_ptr = ctx.string_ptr('% .14E')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
560
        elif basic_ty.kind == 'BooleanType':
561
562
            true_str_ptr = ctx.string_ptr('TRUE')
            false_str_ptr = ctx.string_ptr('FALSE')
dbarbera's avatar
dbarbera committed
563
564
            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
565
        elif basic_ty.kind in ['StringType', 'OctetStringType']:
566
            fmt_str_ptr = ctx.string_ptr('%s')
dbarbera's avatar
dbarbera committed
567
            arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.one])
568
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, arr_ptr])
569
570
571
572
        else:
            raise NotImplementedError


dbarbera's avatar
dbarbera committed
573
def generate_writeln(params):
574
    ''' Generate the code for the writeln operator '''
dbarbera's avatar
dbarbera committed
575
    generate_write(params)
576

577
    str_ptr = ctx.string_ptr('\n')
dbarbera's avatar
dbarbera committed
578
    ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
579
580


dbarbera's avatar
dbarbera committed
581
def generate_reset_timer(params):
dbarbera's avatar
dbarbera committed
582
    ''' Generate the code for the reset timer operator '''
dbarbera's avatar
dbarbera committed
583
    timer_id = params[0]
584
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
585
    reset_func = ctx.funcs[reset_func_name.lower()]
dbarbera's avatar
dbarbera committed
586

dbarbera's avatar
dbarbera committed
587
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
588
589


dbarbera's avatar
dbarbera committed
590
def generate_set_timer(params):
dbarbera's avatar
dbarbera committed
591
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
592
    timer_expr, timer_id = params
593
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
594
    set_func = ctx.funcs[set_func_name.lower()]
dbarbera's avatar
dbarbera committed
595
596
597

    expr_val = expression(timer_expr)

dbarbera's avatar
dbarbera committed
598
599
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
600

dbarbera's avatar
dbarbera committed
601
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
602
603
604
605


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
606
    ''' Generate the code of a list of assignments '''
607
608
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
609
610
611
612
613


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
614
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
615
616
617
618


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
619
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
620
621
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
622
            generate_for_range(loop)
dbarbera's avatar
dbarbera committed
623
        else:
dbarbera's avatar
dbarbera committed
624
            generate_for_iterable(loop)
dbarbera's avatar
dbarbera committed
625
626


dbarbera's avatar
dbarbera committed
627
def generate_for_range(loop):
dbarbera's avatar
dbarbera committed
628
    ''' Generate the code for a for x in range loop '''
dbarbera's avatar
dbarbera committed
629
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
630
631
632
633
634
    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('')

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

dbarbera's avatar
dbarbera committed
637
638
    loop_var = ctx.builder.alloca(ctx.i32, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
639
640
641

    if loop['range']['start']:
        start_val = expression(loop['range']['start'])
dbarbera's avatar
dbarbera committed
642
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
643
    else:
dbarbera's avatar
dbarbera committed
644
        ctx.builder.store(core.Constant.int(ctx.i32, 0), loop_var)
dbarbera's avatar
dbarbera committed
645
646

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

dbarbera's avatar
dbarbera committed
649
650
651
652
    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
653

dbarbera's avatar
dbarbera committed
654
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
655
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
656
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
657

dbarbera's avatar
dbarbera committed
658
659
660
661
662
663
    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
664

dbarbera's avatar
dbarbera committed
665
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
666

667
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
668
669


dbarbera's avatar
dbarbera committed
670
def generate_for_iterable(loop):
dbarbera's avatar
dbarbera committed
671
    ''' Generate the code for a for x in iterable loop'''
dbarbera's avatar
dbarbera committed
672
    seqof_asn1ty = find_basic_type(loop['list'].exprType)
673
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
674

dbarbera's avatar
dbarbera committed
675
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
676
677
678
679
680
681
682
683
684
685

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

686
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
687

dbarbera's avatar
dbarbera committed
688
689
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
690
    seqof_struct_ptr = expression(loop['list'])
691
692
693

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

dbarbera's avatar
dbarbera committed
698
    element_typ = array_ptr.type.pointee.element
699
700
701

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

dbarbera's avatar
dbarbera committed
706
707
    var_ptr = ctx.builder.alloca(element_typ, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
708

dbarbera's avatar
dbarbera committed
709
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
710
711

    # load block
dbarbera's avatar
dbarbera committed
712
713
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
714
    if element_typ.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
715
        generate_assign(var_ptr, ctx.builder.gep(array_ptr, [ctx.zero, idx_var]))
716
    else:
dbarbera's avatar
dbarbera committed
717
718
        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
719
720

    # body block
dbarbera's avatar
dbarbera committed
721
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
722
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
723
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
724
725

    # cond block
dbarbera's avatar
dbarbera committed
726
727
728
729
730
    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
731

dbarbera's avatar
dbarbera committed
732
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
733

734
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
735

dbarbera's avatar
dbarbera committed
736

dbarbera's avatar
dbarbera committed
737
738
739
@singledispatch
def reference(prim):
    ''' Generate a variable reference '''
dbarbera's avatar
flake8    
dbarbera committed
740
    raise TypeError('Unsupported reference: ' + str(prim))
dbarbera's avatar
dbarbera committed
741
742
743
744
745


@reference.register(ogAST.PrimVariable)
def _prim_var_reference(prim):
    ''' Generate a primary variable reference '''
dbarbera's avatar
dbarbera committed
746
    return ctx.scope.resolve(str(prim.value[0]))
dbarbera's avatar
dbarbera committed
747
748
749
750
751


@reference.register(ogAST.PrimPath)
def _prim_path_reference(prim):
    ''' Generate a primary path reference '''
752
    var_name = prim.value[0].lower()
dbarbera's avatar
dbarbera committed
753
    var_ptr = ctx.scope.resolve(str(var_name))
dbarbera's avatar
dbarbera committed
754
755
756
757

    if not prim.value:
        return var_ptr

dbarbera's avatar
dbarbera committed
758
759
760
761
    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
762
                array_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.zero])
763
                # TODO: Refactor this
dbarbera's avatar
dbarbera committed
764
765
                if array_ptr.type.pointee.kind != core.TYPE_ARRAY:
                    # If is not an array this is a pointer to a variable size SeqOf
766
                    # The array is in the second field of the struct
dbarbera's avatar
dbarbera committed
767
                    var_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.one, idx_val])
768
                else:
dbarbera's avatar
dbarbera committed
769
                    var_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.zero, idx_val])
dbarbera's avatar
dbarbera committed
770
771
772
773
            else:
                raise NotImplementedError
        else:
            var_ty = var_ptr.type
dbarbera's avatar
dbarbera committed
774
775
776
777
            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
778
                var_ptr = field_ptr
dbarbera's avatar
dbarbera committed
779
780
            elif var_ty.pointee.name in ctx.unions:
                union = ctx.unions[var_ty.pointee.name]
dbarbera's avatar
dbarbera committed
781
                _, field_ty = union.kind(elem)
dbarbera's avatar
dbarbera committed
782
783
                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
784
785
            else:
                raise NotImplementedError
dbarbera's avatar
dbarbera committed
786
787
    return var_ptr

Maxime Perrotin's avatar
Maxime Perrotin committed
788
789
790

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
791
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
792
793
794
795
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
796
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
797
798
    ''' Generate the code for a variable expression '''
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
799
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
800
801


Maxime Perrotin's avatar
Maxime Perrotin committed
802
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
803
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
804
    ''' Generate the code for an of path expression '''
805
806
807
808
809
810
811
812
813
    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
814
815
816
817
818
819
820
    name = prim.value[0].lower()
    if name in specops_generators:
        generator = specops_generators[name]
        return generator(prim.value[1]['procParams'])

    return generate_access(prim)

821

dbarbera's avatar
dbarbera committed
822
823
def generate_access(prim):
    ''' Generate the code for an access '''
dbarbera's avatar
dbarbera committed
824
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
825
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
826
827


828
def generate_length(params):
dbarbera's avatar
dbarbera committed
829
    ''' Generate the code for the built-in length operation'''
dbarbera's avatar
dbarbera committed
830
    seq_ptr = reference(params[0])
dbarbera's avatar
dbarbera committed
831
    arr_ty = seq_ptr.type.pointee.elements[0]
832
833
834
835
    if arr_ty.kind != core.TYPE_ARRAY:
        # If is not an array this is a pointer to a variable size SeqOf
        # The array is in the second field of the struct
        arr_ty = seq_ptr.type.pointee.elements[1]
dbarbera's avatar
dbarbera committed
836
    return core.Constant.int(ctx.i32, arr_ty.count)
837
838
839


def generate_present(params):
dbarbera's avatar
dbarbera committed
840
    ''' Generate the code for the built-in present operation'''
841
842
843
    expr_val = expression(params[0])
    kind_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.zero])
    return ctx.builder.load(kind_ptr)
844
845
846


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

dbarbera's avatar
dbarbera committed
850
    if expr_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
851
852
853
        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
854
    else:
dbarbera's avatar
dbarbera committed
855
        return ctx.builder.call(ctx.funcs['fabs'], [expr_val])
856
857
858


def generate_fix(params):
dbarbera's avatar
dbarbera committed
859
    ''' Generate the code for the built-in fix operation'''
dbarbera's avatar
dbarbera committed
860
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
861
    return ctx.builder.fptosi(expr_val, ctx.i32)
862
863
864


def generate_float(params):
dbarbera's avatar
dbarbera committed
865
    ''' Generate the code for the built-in float operation'''
dbarbera's avatar
dbarbera committed
866
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
867
    return ctx.builder.sitofp(expr_val, ctx.double)
868
869
870


def generate_power(params):
dbarbera's avatar
dbarbera committed
871
    ''' Generate the code for the built-in power operation'''
872
873
874
875
    left_val = expression(params[0])
    right_val = expression(params[1])

    if left_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
876
877
878
        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)
879
    else:
dbarbera's avatar
dbarbera committed
880
        return ctx.builder.call(ctx.funcs['powi'], [left_val, right_val])
881
882


Maxime Perrotin's avatar
Maxime Perrotin committed
883
884
885
886
887
888
889
890
891