LlvmGenerator.py 62.1 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
    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)

dbarbera's avatar
dbarbera committed
113
114
115
        if basic_asn1ty.kind == 'IntegerType':
            llty = ctx.i64
        elif basic_asn1ty.kind == 'Integer32Type':
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
            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)
dbarbera's avatar
dbarbera committed
131
        elif basic_asn1ty.kind in ('StringType', 'StandardStringType'):
132
            llty = ctx.i8_ptr
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
        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:
151
            struct = self.decl_struct(['nCount', 'arr'], [ctx.i32, array_ty], name)
152
        else:
153
            struct = self.decl_struct(['arr'], [array_ty], name)
154

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

158
159
160
161
162
163
        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 = []
164

165
166
167
        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))
168

169
        struct = self.decl_struct(field_names, field_types, name)
170

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

174
175
176
177
178
179
        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
180
181
182

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

dbarbera's avatar
dbarbera committed
185
            field_names.append(field_name.replace('-', '_'))
dbarbera's avatar
dbarbera committed
186
            field_types.append(self.type_of(choice_ty.Children[field_name].type))
187

188
        union = self.decl_union(field_names, field_types, name)
189
190

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

193
194
195
        return union.ty

    def _type_of_octetstring(self, name, octetstring_ty):
dbarbera's avatar
dbarbera committed
196
        ''' Return the equivalent LL type of a OctetString ASN.1 type '''
197
        min_size = int(octetstring_ty.Min)
198
        max_size = int(octetstring_ty.Max)
199
200
201
202
203
204
205
206
        is_variable_size = min_size != max_size

        array_ty = core.Type.array(ctx.i8, max_size)

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

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

211
212
        return struct.ty

213
214
215
216
217
218
219
220
221
222
223
224
    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])

225
226
227
228
229
230
231
232
233
234
235
    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
236
        name = name.replace('-', '_')
237
238
239
240
        struct = StructType(name, field_names, field_types)
        self.structs[name] = struct
        return struct

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

245
246
    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
247
        name = name.replace('-', '_')
248
249
250
251
        union = UnionType(name, field_names, field_types)
        self.unions[name] = union
        return union

dbarbera's avatar
dbarbera committed
252
253
254
255
    def resolve_union(self, name):
        ''' Return the union associated to a name '''
        return self.unions[name.replace('-', '_')]

dbarbera's avatar
dbarbera committed
256

257
258
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
259
        self.name = name
260
261
262
263
264
        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
265
266


dbarbera's avatar
dbarbera committed
267
268
269
270
271
272
273
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
274
275
        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
276
277
278
279
280
281

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


282
283
284
class Scope:
    def __init__(self, parent=None):
        self.vars = {}
dbarbera's avatar
dbarbera committed
285
        self.labels = {}
286
287
288
289
290
291
292
293
294
295
296
297
        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
298
            raise NameError("name '%s' is not defined" % name)
299

dbarbera's avatar
dbarbera committed
300
301
302
303
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
304
            func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
305
            label_block = func.append_basic_block('label:%s' % name)
dbarbera's avatar
dbarbera committed
306
307
308
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
309

310
311
312
313
class CompileError(Exception):
    pass


Maxime Perrotin's avatar
Maxime Perrotin committed
314
315
@singledispatch
def generate(ast):
dbarbera's avatar
dbarbera committed
316
    ''' Generate the IR for an AST node '''
317
    raise CompileError('Unsupported AST construct "%s"' % ast.__class__.__name__)
Maxime Perrotin's avatar
Maxime Perrotin committed
318

dbarbera's avatar
dbarbera committed
319

Maxime Perrotin's avatar
Maxime Perrotin committed
320
321
322
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
323
    ''' Generate the IR for a process '''
dbarbera's avatar
dbarbera committed
324
325
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
326

dbarbera's avatar
dbarbera committed
327
328
    global ctx
    ctx = Context(process)
329

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

337
338
    # Initialize states
    for name, val in process.mapping.viewitems():
339
        if not name.endswith('START'):
dbarbera's avatar
dbarbera committed
340
            cons_val = core.Constant.int(ctx.i32, len(ctx.states))
dbarbera's avatar
dbarbera committed
341
            ctx.states[name.lower()] = cons_val
342
        elif name != 'START':
dbarbera's avatar
dbarbera committed
343
            cons_val = core.Constant.int(ctx.i32, val)
dbarbera's avatar
dbarbera committed
344
            ctx.states[name.lower()] = cons_val
345

346
    # Generate state var
347
    state_cons = ctx.module.add_global_variable(ctx.i32, '.state')
dbarbera's avatar
dbarbera committed
348
    state_cons.initializer = core.Constant.int(ctx.i32, -1)
349
    ctx.scope.define('.state', state_cons)
350

351
352
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
353
        var_ty = ctx.type_of(ty)
dbarbera's avatar
dbarbera committed
354
        global_var = ctx.module.add_global_variable(var_ty, str(name))
355
        global_var.initializer = core.Constant.null(var_ty)
dbarbera's avatar
dbarbera committed
356
        ctx.scope.define(str(name).lower(), global_var)
357

dbarbera's avatar
dbarbera committed
358
    # Declare set/reset timer functions
dbarbera's avatar
dbarbera committed
359
360
    for timer in process.timers:
        # TODO: Should be uint?
dbarbera's avatar
dbarbera committed
361
        ctx.decl_func("set_%s" % str(timer), ctx.void, [ctx.i64_ptr], True)
362
        ctx.decl_func("reset_%s" % str(timer), ctx.void, [], True)
dbarbera's avatar
dbarbera committed
363

364
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
365
    for signal in process.output_signals:
366
        if 'type' in signal:
367
            param_tys = [core.Type.pointer(ctx.type_of(signal['type']))]
368
369
        else:
            param_tys = []
370
        ctx.decl_func(str(signal['name']), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
371

372
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
373
    for proc in [proc for proc in process.procedures if proc.external]:
374
        param_tys = [core.Type.pointer(ctx.type_of(p['type'])) for p in proc.fpar]
375
        ctx.decl_func(str(proc.inputString), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
376

377
378
    # Generate internal procedures
    for proc in process.content.inner_procedures:
379
        generate(proc)
380

381
    # Generate process functions
dbarbera's avatar
dbarbera committed
382
383
    generate_runtr_func(process)
    generate_startup_func(process)
384

385
386
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
387
        generate_input_signal(signal, mapping[signal['name']])
388

dbarbera's avatar
dbarbera committed
389
390
391
392
    # Generate timer signal
    for timer in process.timers:
        generate_input_signal({'name': timer.lower()}, mapping[timer])

dbarbera's avatar
dbarbera committed
393
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
394

dbarbera's avatar
dbarbera committed
395
396
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
397
398


dbarbera's avatar
dbarbera committed
399
def generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
400
    ''' Generate the IR for the run_transition function '''
401
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
402

403
    ctx.open_scope()
404

dbarbera's avatar
dbarbera committed
405
406
407
408
    entry_block = func.append_basic_block('runtr:entry')
    cond_block = func.append_basic_block('runtr:cond')
    body_block = func.append_basic_block('runtr:body')
    exit_block = func.append_basic_block('runtr:exit')
409

dbarbera's avatar
dbarbera committed
410
    ctx.builder = core.Builder.new(entry_block)
411
412

    # entry
dbarbera's avatar
dbarbera committed
413
414
415
416
    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)
417
418

    # cond
dbarbera's avatar
dbarbera committed
419
420
421
422
423
    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)
424
425

    # body
dbarbera's avatar
dbarbera committed
426
    ctx.builder.position_at_end(body_block)
427
    switch = ctx.builder.switch(id_val, exit_block)
428
429
430

    # transitions
    for idx, tr in enumerate(process.transitions):
dbarbera's avatar
dbarbera committed
431
        tr_block = func.append_basic_block('runtr:tr%d' % idx)
dbarbera's avatar
dbarbera committed
432
        const = core.Constant.int(ctx.i32, idx)
433
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
434
        ctx.builder.position_at_end(tr_block)
435
        generate(tr)
dbarbera's avatar
dbarbera committed
436
437
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
438
439

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

dbarbera's avatar
dbarbera committed
443
444
445
446
447
    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
448
449
450
    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
451

452
    ctx.close_scope()
453

454
455
456
457
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
458
def generate_startup_func(process):
dbarbera's avatar
dbarbera committed
459
    ''' Generate the IR for the startup function '''
460
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
461

462
    ctx.open_scope()
463

dbarbera's avatar
dbarbera committed
464
    entry_block = func.append_basic_block('startup:entry')
dbarbera's avatar
dbarbera committed
465
    ctx.builder = core.Builder.new(entry_block)
466

467
468
469
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
470
            global_var = ctx.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
471
            generate_assign(global_var, expression(expr))
472

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

476
    ctx.close_scope()
477

478
479
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
480
481


dbarbera's avatar
dbarbera committed
482
def generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
483
    ''' Generate the IR for an input signal '''
dbarbera's avatar
dbarbera committed
484
    func_name = ctx.name + "_" + str(signal['name'])
485
486
    param_tys = []
    if 'type' in signal:
487
        param_tys.append(core.Type.pointer(ctx.type_of(signal['type'])))
488

489
    func = ctx.decl_func(func_name, ctx.void, param_tys)
490

491
    ctx.open_scope()
492

dbarbera's avatar
dbarbera committed
493
494
    entry_block = func.append_basic_block('input:entry')
    exit_block = func.append_basic_block('input:exit')
dbarbera's avatar
dbarbera committed
495
    ctx.builder = core.Builder.new(entry_block)
496

497
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('.state'))
dbarbera's avatar
dbarbera committed
498
    switch = ctx.builder.switch(g_state_val, exit_block)
499

dbarbera's avatar
dbarbera committed
500
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
501
        if state_name.endswith('start'):
dbarbera's avatar
dbarbera committed
502
            continue
dbarbera's avatar
dbarbera committed
503
        state_block = func.append_basic_block('input:state_%s' % str(state_name))
504
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
505
        ctx.builder.position_at_end(state_block)
506
507
508

        # TODO: Nested states

509
510
511
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
512
                var_ptr = ctx.scope.resolve(str(var_name))
dbarbera's avatar
dbarbera committed
513
                if is_struct_ptr(var_ptr) or is_array_ptr(var_ptr):
514
515
                    generate_assign(var_ptr, func.args[0])
                else:
dbarbera's avatar
dbarbera committed
516
                    generate_assign(var_ptr, ctx.builder.load(func.args[0]))
517
            if input.transition:
dbarbera's avatar
dbarbera committed
518
519
                id_val = core.Constant.int(ctx.i32, input.transition_id)
                ctx.builder.call(ctx.funcs['run_transition'], [id_val])
520

dbarbera's avatar
dbarbera committed
521
        ctx.builder.ret_void()
522

dbarbera's avatar
dbarbera committed
523
524
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
525

526
    ctx.close_scope()
527

528
529
530
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
531
532
533
@generate.register(ogAST.Output)
@generate.register(ogAST.ProcedureCall)
def _call_external_function(output):
dbarbera's avatar
dbarbera committed
534
    ''' Generate the IR for an output or procedure call '''
dbarbera's avatar
dbarbera committed
535
536
537
    for out in output.output:
        name = out['outputName'].lower()

538
        if name == 'write':
dbarbera's avatar
dbarbera committed
539
            generate_write(out['params'])
dbarbera's avatar
dbarbera committed
540
            continue
541
        elif name == 'writeln':
dbarbera's avatar
dbarbera committed
542
            generate_writeln(out['params'])
543
            continue
dbarbera's avatar
dbarbera committed
544
        elif name == 'reset_timer':
dbarbera's avatar
dbarbera committed
545
            generate_reset_timer(out['params'])
dbarbera's avatar
dbarbera committed
546
547
            continue
        elif name == 'set_timer':
dbarbera's avatar
dbarbera committed
548
            generate_set_timer(out['params'])
dbarbera's avatar
dbarbera committed
549
550
            continue

dbarbera's avatar
dbarbera committed
551
        func = ctx.funcs[str(name).lower()]
552
553
554
555
556
557

        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
558
559
                p_var = ctx.builder.alloca(p_val.type, None)
                ctx.builder.store(p_val, p_var)
560
561
562
563
                params.append(p_var)
            else:
                params.append(p_val)

dbarbera's avatar
dbarbera committed
564
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
565
566


dbarbera's avatar
dbarbera committed
567
def generate_write(params):
dbarbera's avatar
dbarbera committed
568
    ''' Generate the IR for the write operator '''
569
570
571
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
572

573
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
574
575
            fmt_str_ptr = ctx.string_ptr('% d')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
576
        elif basic_ty.kind == 'RealType':
577
578
            fmt_str_ptr = ctx.string_ptr('% .14E')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
579
        elif basic_ty.kind == 'BooleanType':
580
581
            true_str_ptr = ctx.string_ptr('TRUE')
            false_str_ptr = ctx.string_ptr('FALSE')
dbarbera's avatar
dbarbera committed
582
583
            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
584
        elif basic_ty.kind in ('StringType', 'StandardStringType'):
585
586
587
            fmt_str_ptr = ctx.string_ptr('%s')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
        elif basic_ty.kind == 'OctetStringType':
588
            fmt_str_ptr = ctx.string_ptr('%.*s')
589
590
591
592
593
594
            if basic_ty.Min == basic_ty.Max:
                arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.zero])
                count_val = core.Constant.int(ctx.i32, arr_ptr.type.pointee.count)
            else:
                count_val = ctx.builder.load(ctx.builder.gep(expr_val, [ctx.zero, ctx.zero]))
                arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.one])
595
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, count_val, arr_ptr])
596
597
598
599
        else:
            raise NotImplementedError


dbarbera's avatar
dbarbera committed
600
def generate_writeln(params):
dbarbera's avatar
dbarbera committed
601
    ''' Generate the IR for the writeln operator '''
dbarbera's avatar
dbarbera committed
602
    generate_write(params)
603

604
    str_ptr = ctx.string_ptr('\n')
dbarbera's avatar
dbarbera committed
605
    ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
606
607


dbarbera's avatar
dbarbera committed
608
def generate_reset_timer(params):
dbarbera's avatar
dbarbera committed
609
    ''' Generate the IR for the reset timer operator '''
dbarbera's avatar
dbarbera committed
610
    timer_id = params[0]
611
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
612
    reset_func = ctx.funcs[reset_func_name.lower()]
dbarbera's avatar
dbarbera committed
613

dbarbera's avatar
dbarbera committed
614
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
615
616


dbarbera's avatar
dbarbera committed
617
def generate_set_timer(params):
dbarbera's avatar
dbarbera committed
618
    ''' Generate the IR for the set timer operator '''
dbarbera's avatar
dbarbera committed
619
    timer_expr, timer_id = params
620
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
621
    set_func = ctx.funcs[set_func_name.lower()]
dbarbera's avatar
dbarbera committed
622
623
624

    expr_val = expression(timer_expr)

dbarbera's avatar
dbarbera committed
625
626
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
627

dbarbera's avatar
dbarbera committed
628
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
629
630
631
632


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
633
    ''' Generate the IR for a list of assignments '''
634
635
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
636
637
638
639
640


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
641
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
642
643
644
645


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
646
    ''' Generate the IRfor a for loop '''
dbarbera's avatar
dbarbera committed
647
648
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
649
            generate_for_range(loop)
dbarbera's avatar
dbarbera committed
650
        else:
dbarbera's avatar
dbarbera committed
651
            generate_for_iterable(loop)
dbarbera's avatar
dbarbera committed
652
653


dbarbera's avatar
dbarbera committed
654
def generate_for_range(loop):
dbarbera's avatar
dbarbera committed
655
    ''' Generate the IR for a for x in range loop '''
dbarbera's avatar
dbarbera committed
656
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
657
658
659
    cond_block = func.append_basic_block('for:cond')
    body_block = func.append_basic_block('for:body')
    inc_block = func.append_basic_block('for:inc')
dbarbera's avatar
dbarbera committed
660
    end_block = func.append_basic_block('for:end')
dbarbera's avatar
dbarbera committed
661

662
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
663

dbarbera's avatar
dbarbera committed
664
    loop_var = ctx.builder.alloca(ctx.i64, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
665
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
666
667
668

    if loop['range']['start']:
        start_val = expression(loop['range']['start'])
dbarbera's avatar
dbarbera committed
669
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
670
    else:
dbarbera's avatar
dbarbera committed
671
        ctx.builder.store(core.Constant.int(ctx.i64, 0), loop_var)
dbarbera's avatar
dbarbera committed
672
673

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

dbarbera's avatar
dbarbera committed
676
677
678
679
    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
680

dbarbera's avatar
dbarbera committed
681
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
682
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
683
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
684

dbarbera's avatar
dbarbera committed
685
    ctx.builder.position_at_end(inc_block)
dbarbera's avatar
dbarbera committed
686
    step_val = core.Constant.int(ctx.i64, loop['range']['step'])
dbarbera's avatar
dbarbera committed
687
688
689
690
    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
691

dbarbera's avatar
dbarbera committed
692
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
693

694
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
695
696


dbarbera's avatar
dbarbera committed
697
def generate_for_iterable(loop):
dbarbera's avatar
dbarbera committed
698
    ''' Generate the IR for a for x in iterable loop '''
dbarbera's avatar
dbarbera committed
699
    seqof_asn1ty = find_basic_type(loop['list'].exprType)
700
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
701

dbarbera's avatar
dbarbera committed
702
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
703
704
705
706
707
708
709
710

    # 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')
dbarbera's avatar
dbarbera committed
711
    end_block = func.append_basic_block('forin:end')
dbarbera's avatar
dbarbera committed
712

713
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
714

dbarbera's avatar
dbarbera committed
715
716
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
717
    seqof_struct_ptr = expression(loop['list'])
718
719
720

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

dbarbera's avatar
dbarbera committed
725
    element_typ = array_ptr.type.pointee.element
726
727
728

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

dbarbera's avatar
dbarbera committed
733
734
    var_ptr = ctx.builder.alloca(element_typ, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
735

dbarbera's avatar
dbarbera committed
736
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
737
738

    # load block
dbarbera's avatar
dbarbera committed
739
740
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
741
    if element_typ.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
742
        generate_assign(var_ptr, ctx.builder.gep(array_ptr, [ctx.zero, idx_var]))
743
    else:
dbarbera's avatar
dbarbera committed
744
745
        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
746
747

    # body block
dbarbera's avatar
dbarbera committed
748
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
749
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
750
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
751
752

    # cond block
dbarbera's avatar
dbarbera committed
753
754
755
756
757
    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
758

dbarbera's avatar
dbarbera committed
759
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
760

761
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
762

dbarbera's avatar
dbarbera committed
763

dbarbera's avatar
dbarbera committed
764
765
@singledispatch
def reference(prim):
dbarbera's avatar
dbarbera committed
766
    ''' Generate the IR for a reference '''
767
    raise CompileError('Unsupported reference "%s"' % prim.__class__.__name__)
dbarbera's avatar
dbarbera committed
768
769
770
771


@reference.register(ogAST.PrimVariable)
def _prim_var_reference(prim):
dbarbera's avatar
dbarbera committed
772
    ''' Generate the IR for a variable reference '''
dbarbera's avatar
dbarbera committed
773
    return ctx.scope.resolve(str(prim.value[0]))
dbarbera's avatar
dbarbera committed
774
775


776
777
@reference.register(ogAST.PrimSelector)
def _prim_selector_reference(prim):
dbarbera's avatar
dbarbera committed
778
    ''' Generate the IR for a field selector referece '''
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
    receiver_ptr = reference(prim.value[0])
    field_name = prim.value[1]

    if receiver_ptr.type.pointee.name in ctx.structs:
        struct = ctx.structs[receiver_ptr.type.pointee.name]
        field_idx_cons = core.Constant.int(ctx.i32, struct.idx(field_name))
        return ctx.builder.gep(receiver_ptr, [ctx.zero, field_idx_cons])

    else:
        union = ctx.unions[receiver_ptr.type.pointee.name]
        _, field_ty = union.kind(field_name)
        field_ptr = ctx.builder.gep(receiver_ptr, [ctx.zero, ctx.one])
        return ctx.builder.bitcast(field_ptr, core.Type.pointer(field_ty))


@reference.register(ogAST.PrimIndex)
def _prim_index_reference(prim):
dbarbera's avatar
dbarbera committed
796
    ''' Generate the IR for an index reference '''
797
798
799
800
801
802
803
804
805
806
807
808
    receiver_ptr = reference(prim.value[0])
    idx_val = expression(prim.value[1]['index'][0])

    array_ptr = ctx.builder.gep(receiver_ptr, [ctx.zero, ctx.zero])

    # TODO: Refactor this
    if array_ptr.type.pointee.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
        return ctx.builder.gep(receiver_ptr, [ctx.zero, ctx.one, idx_val])
    else:
        return ctx.builder.gep(receiver_ptr, [ctx.zero, ctx.zero, idx_val])
dbarbera's avatar
dbarbera committed
809

Maxime Perrotin's avatar
Maxime Perrotin committed
810
811
812

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
813
    ''' Generate the IR for an expression node '''
814
    raise CompileError('Unsupported expression "%s"' % expr.__class__.__name__)
Maxime Perrotin's avatar
Maxime Perrotin committed
815
816
817
818
819
820
821
822


@expression.register(ogAST.ExprPlus)
@expression.register(ogAST.ExprMul)
@expression.register(ogAST.ExprMinus)
@expression.register(ogAST.ExprDiv)
@expression.register(ogAST.ExprMod)
@expression.register(ogAST.ExprRem)
823
def _expr_arith(expr):
dbarbera's avatar
dbarbera committed
824
    ''' Generate the IR for an arithmetic expression '''
825
826
    left_val = expression(expr.left)
    right_val = expression(expr.right)
827

828
829
830
    expr_bty = find_basic_type(expr.exprType)

    if expr_bty.kind in ('IntegerType', 'Integer32Type'):
831
        if expr.operand == '+':
832
            return ctx.builder.add(left_val, right_val)
833
        elif expr.operand == '-':
834
            return ctx.builder.sub(left_val, right_val)
835
        elif expr.operand == '*':
836
            return ctx.builder.mul(left_val, right_val)
837
        elif expr.operand == '/':
838
            return ctx.builder.sdiv(left_val, right_val)
839
        elif expr.operand == 'mod':
840
            # l mod r == (((l rem r) + r) rem r)
841
842
843
            rem_val = ctx.builder.srem(left_val, right_val)
            add_val = ctx.builder.add(rem_val, right_val)
            return ctx.builder.srem(add_val, right_val)
844
        elif expr.operand == 'rem':
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
            return ctx.builder.srem(left_val, right_val)
        raise CompileError(
            'Operator "%s" not supported for Integer types' % expr.