LlvmGenerator.py 73.9 KB
Newer Older
Maxime Perrotin's avatar
Maxime Perrotin committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/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

15
    Designed and implemented by Diego Barbera 
Maxime Perrotin's avatar
Maxime Perrotin committed
16
17
18
19
20

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

from llvm import core as lc
from llvm import ee as le
26
from llvm import passes
Maxime Perrotin's avatar
Maxime Perrotin committed
27
28

import ogAST
Maxime Perrotin's avatar
Maxime Perrotin committed
29
import Helper
Maxime Perrotin's avatar
Maxime Perrotin committed
30
31
32

LOG = logging.getLogger(__name__)

Maxime Perrotin's avatar
Maxime Perrotin committed
33
34
__all__ = ['generate']

Maxime Perrotin's avatar
Maxime Perrotin committed
35

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

44
        self.procedures = process.procedures
45

dbarbera's avatar
dbarbera committed
46
        self.scope = Scope(self)
47
        self.global_scope = self.scope
48
        self.states = {}
dbarbera's avatar
dbarbera committed
49
        self.enums = {}
dbarbera's avatar
dbarbera committed
50
        self.structs = {}
dbarbera's avatar
dbarbera committed
51
        self.unions = {}
52
        self.strings = {}
dbarbera's avatar
dbarbera committed
53
        self.funcs = {}
54
        self.lltypes = {}
55
        self.basic_asn1types = {}
56
57

        # Initialize built-in types
dbarbera's avatar
dbarbera committed
58
59
60
61
62
63
64
65
66
67
68
        self.i1 = lc.Type.int(1)
        self.i8 = lc.Type.int(8)
        self.i32 = lc.Type.int(32)
        self.i64 = lc.Type.int(64)
        self.void = lc.Type.void()
        self.double = lc.Type.double()
        self.i1_ptr = lc.Type.pointer(self.i1)
        self.i8_ptr = lc.Type.pointer(self.i8)
        self.i32_ptr = lc.Type.pointer(self.i32)
        self.i64_ptr = lc.Type.pointer(self.i64)
        self.double_ptr = lc.Type.pointer(self.double)
69

dbarbera's avatar
dbarbera committed
70
        # Initialize common constants
dbarbera's avatar
dbarbera committed
71
72
        self.zero = lc.Constant.int(self.i32, 0)
        self.one = lc.Constant.int(self.i32, 1)
dbarbera's avatar
dbarbera committed
73

74
        # Intialize built-in functions
dbarbera's avatar
dbarbera committed
75
        ty = lc.Type.function(self.void, [self.i8_ptr], True)
dbarbera's avatar
dbarbera committed
76
        self.funcs['printf'] = self.module.add_function(ty, 'printf')
77

dbarbera's avatar
dbarbera committed
78
        ty = lc.Type.function(self.double, [self.double])
dbarbera's avatar
dbarbera committed
79
80
        self.funcs['round'] = self.module.add_function(ty, 'round')

dbarbera's avatar
dbarbera committed
81
        self.funcs['memcpy'] = lc.Function.intrinsic(
82
            self.module,
dbarbera's avatar
dbarbera committed
83
            lc.INTR_MEMCPY,
84
85
            [self.i8_ptr, self.i8_ptr, self.i64]
        )
Maxime Perrotin's avatar
Maxime Perrotin committed
86

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

dbarbera's avatar
dbarbera committed
93
        self.funcs['ceil'] = lc.Function.intrinsic(
dbarbera's avatar
dbarbera committed
94
            self.module,
dbarbera's avatar
dbarbera committed
95
            lc.INTR_CEIL,
dbarbera's avatar
dbarbera committed
96
97
98
            [self.double]
        )

dbarbera's avatar
dbarbera committed
99
        self.funcs['cos'] = lc.Function.intrinsic(
dbarbera's avatar
dbarbera committed
100
            self.module,
dbarbera's avatar
dbarbera committed
101
            lc.INTR_COS,
dbarbera's avatar
dbarbera committed
102
103
104
            [self.double]
        )

dbarbera's avatar
dbarbera committed
105
        self.funcs['fabs'] = lc.Function.intrinsic(
dbarbera's avatar
dbarbera committed
106
            self.module,
dbarbera's avatar
dbarbera committed
107
            lc.INTR_FABS,
dbarbera's avatar
dbarbera committed
108
109
110
            [self.double]
        )

dbarbera's avatar
dbarbera committed
111
        self.funcs['floor'] = lc.Function.intrinsic(
dbarbera's avatar
dbarbera committed
112
            self.module,
dbarbera's avatar
dbarbera committed
113
            lc.INTR_FLOOR,
dbarbera's avatar
dbarbera committed
114
115
116
            [self.double]
        )

dbarbera's avatar
dbarbera committed
117
        self.funcs['sin'] = lc.Function.intrinsic(
dbarbera's avatar
dbarbera committed
118
            self.module,
dbarbera's avatar
dbarbera committed
119
            lc.INTR_SIN,
dbarbera's avatar
dbarbera committed
120
121
122
            [self.double]
        )

dbarbera's avatar
dbarbera committed
123
        self.funcs['sqrt'] = lc.Function.intrinsic(
dbarbera's avatar
dbarbera committed
124
            self.module,
dbarbera's avatar
dbarbera committed
125
            lc.INTR_SQRT,
dbarbera's avatar
dbarbera committed
126
127
128
            [self.double]
        )

dbarbera's avatar
dbarbera committed
129
        self.funcs['trunc'] = lc.Function.intrinsic(
dbarbera's avatar
dbarbera committed
130
            self.module,
dbarbera's avatar
dbarbera committed
131
            lc.INTR_TRUNC,
dbarbera's avatar
dbarbera committed
132
133
134
            [self.double]
        )

135
    def open_scope(self):
dbarbera's avatar
dbarbera committed
136
        ''' Open a scope '''
dbarbera's avatar
dbarbera committed
137
        self.scope = Scope(self, self.scope)
138
139

    def close_scope(self):
dbarbera's avatar
dbarbera committed
140
        ''' Close the current scope '''
141
142
        self.scope = self.scope.parent

143
    def basic_asn1type_of(self, asn1ty):
144
        ''' Return the ASN.1 basic type of a type '''
dbarbera's avatar
dbarbera committed
145
146
147
148
149
150
        if asn1ty.kind != 'ReferenceType':
            return asn1ty

        asn1ty_name = asn1ty.ReferencedTypeName.lower()

        # return the basic type if its cached
151
152
        if asn1ty_name in self.basic_asn1types:
            return self.basic_asn1types[asn1ty_name]
dbarbera's avatar
dbarbera committed
153

154
155
        basic_asn1ty = asn1ty
        while basic_asn1ty.kind == 'ReferenceType':
156
            for typename in self.dataview.viewkeys():
157
158
                if typename.lower() == basic_asn1ty.ReferencedTypeName.lower():
                    basic_asn1ty = self.dataview[typename].type
159
                    break
dbarbera's avatar
dbarbera committed
160
161

        # cache the basic type
162
        self.basic_asn1types[asn1ty_name] = basic_asn1ty
dbarbera's avatar
dbarbera committed
163

164
        return basic_asn1ty
165

166
    def lltype_of(self, asn1ty):
167
168
169
170
171
172
173
174
175
        ''' 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]

176
        basic_asn1ty = self.basic_asn1type_of(asn1ty)
177

dbarbera's avatar
dbarbera committed
178
        if basic_asn1ty.kind == 'IntegerType':
dbarbera's avatar
dbarbera committed
179
            llty = self.i64
dbarbera's avatar
dbarbera committed
180
        elif basic_asn1ty.kind == 'Integer32Type':
dbarbera's avatar
dbarbera committed
181
            llty = self.i32
182
        elif basic_asn1ty.kind == 'BooleanType':
dbarbera's avatar
dbarbera committed
183
            llty = self.i1
184
        elif basic_asn1ty.kind == 'RealType':
dbarbera's avatar
dbarbera committed
185
            llty = self.double
186
        elif basic_asn1ty.kind == 'SequenceOfType':
187
            llty = self._lltype_of_sequenceof(name, basic_asn1ty)
188
        elif basic_asn1ty.kind == 'SequenceType':
189
            llty = self._lltype_of_sequence(name, basic_asn1ty)
190
        elif basic_asn1ty.kind == 'EnumeratedType':
dbarbera's avatar
dbarbera committed
191
            llty = self.i32
192
        elif basic_asn1ty.kind == 'ChoiceType':
193
            llty = self._lltype_of_choice(name, basic_asn1ty)
194
        elif basic_asn1ty.kind == 'OctetStringType':
195
            llty = self._lltype_of_octetstring(name, basic_asn1ty)
dbarbera's avatar
dbarbera committed
196
        elif basic_asn1ty.kind in ('StringType', 'StandardStringType'):
dbarbera's avatar
dbarbera committed
197
            llty = self.i8_ptr
198
        else:
199
            raise CompileError('Unknown basic ASN.1 type "%s"' % basic_asn1ty.kind)
200
201
202
203
204
205

        if name:
            self.lltypes[name] = llty

        return llty

206
    def _lltype_of_sequenceof(self, name, asn1ty):
207
        ''' Return the LL type of a SequenceOf ASN.1 type '''
208
209
        min_size = int(asn1ty.Min)
        max_size = int(asn1ty.Max)
210
211
        is_variable_size = min_size != max_size

212
        elem_llty = self.lltype_of(asn1ty.type)
dbarbera's avatar
dbarbera committed
213
        array_llty = lc.Type.array(elem_llty, max_size)
214
215

        if is_variable_size:
216
            struct = self.decl_struct(['nCount', 'arr'], [self.i32, array_llty], name)
217
        else:
218
            struct = self.decl_struct(['arr'], [array_llty], name)
219

dbarbera's avatar
dbarbera committed
220
        struct_ptr = lc.Type.pointer(struct.llty)
221
        self.decl_func("asn1Scc%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])
222

223
        return struct.llty
224

225
    def _lltype_of_sequence(self, name, asn1ty):
226
227
        ''' Return the LL type of a Sequence ASN.1 type '''
        field_names = []
228
        field_lltys = []
229

230
        for field_name in Helper.sorted_fields(asn1ty):
231
            field_names.append(field_name.replace('-', '_'))
232
            field_lltys.append(self.lltype_of(asn1ty.Children[field_name].type))
233

234
        struct = self.decl_struct(field_names, field_lltys, name)
235

dbarbera's avatar
dbarbera committed
236
        struct_ptr = lc.Type.pointer(struct.llty)
237
        self.decl_func("asn1Scc%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])
238

239
        return struct.llty
240

241
    def _lltype_of_choice(self, name, asn1ty):
242
243
        ''' Return the equivalent LL type of a Choice ASN.1 type '''
        field_names = []
244
        field_lltys = []
dbarbera's avatar
dbarbera committed
245

246
        for idx, field_name in enumerate(Helper.sorted_fields(asn1ty)):
dbarbera's avatar
dbarbera committed
247
            # enum values used in choice determinant/present
dbarbera's avatar
dbarbera committed
248
            self.enums[field_name.replace('-', '_')] = lc.Constant.int(self.i32, idx)
dbarbera's avatar
dbarbera committed
249

dbarbera's avatar
dbarbera committed
250
            field_names.append(field_name.replace('-', '_'))
251
            field_lltys.append(self.lltype_of(asn1ty.Children[field_name].type))
252

253
        union = self.decl_union(field_names, field_lltys, name)
254

dbarbera's avatar
dbarbera committed
255
        union_ptr = lc.Type.pointer(union.llty)
256
        self.decl_func("asn1Scc%s_Equal" % name, self.i1, [union_ptr, union_ptr])
257

258
        return union.llty
259

260
    def _lltype_of_octetstring(self, name, asn1ty):
dbarbera's avatar
dbarbera committed
261
        ''' Return the equivalent LL type of a OctetString ASN.1 type '''
262
263
        min_size = int(asn1ty.Min)
        max_size = int(asn1ty.Max)
264
265
        is_variable_size = min_size != max_size

dbarbera's avatar
dbarbera committed
266
        array_llty = lc.Type.array(self.i8, max_size)
267
268

        if is_variable_size:
269
            struct = self.decl_struct(['nCount', 'arr'], [self.i32, array_llty], name)
270
        else:
271
            struct = self.decl_struct(['arr'], [array_llty], name)
272

dbarbera's avatar
dbarbera committed
273
        struct_ptr = lc.Type.pointer(struct.llty)
274
        self.decl_func("asn1Scc%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])
275

276
        return struct.llty
277

278
279
280
281
282
    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])

dbarbera's avatar
dbarbera committed
283
        str_val = lc.Constant.stringz(str)
284
285
286
287
288
289
        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])

290
    def decl_func(self, name, return_llty, param_lltys, extern=False):
291
        ''' Declare a function '''
dbarbera's avatar
dbarbera committed
292
        func_llty = lc.Type.function(return_llty, param_lltys)
293
        func_name = ("%s_RI_%s" % (self.name, name)) if extern else name
dbarbera's avatar
dbarbera committed
294
        func = lc.Function.new(self.module, func_llty, func_name)
295
296
297
        self.funcs[name.lower()] = func
        return func

298
    def decl_struct(self, field_names, field_lltys, name=None):
299
300
        ''' Declare a struct '''
        name = name if name else "struct.%s" % len(self.structs)
dbarbera's avatar
dbarbera committed
301
        name = name.replace('-', '_')
302
        struct = StructType(name, field_names, field_lltys)
303
304
305
        self.structs[name] = struct
        return struct

dbarbera's avatar
dbarbera committed
306
307
308
309
    def resolve_struct(self, name):
        ''' Return the struct associated to a name '''
        return self.structs[name.replace('-', '_')]

310
    def decl_union(self, field_names, field_lltys, name=None):
311
        name = name if name else "union.%s" % len(self.structs)
dbarbera's avatar
dbarbera committed
312
        name = name.replace('-', '_')
313
        union = UnionType(name, field_names, field_lltys, self)
314
315
316
        self.unions[name] = union
        return union

dbarbera's avatar
dbarbera committed
317
318
319
320
    def resolve_union(self, name):
        ''' Return the union associated to a name '''
        return self.unions[name.replace('-', '_')]

dbarbera's avatar
dbarbera committed
321

322
class StructType():
323
    def __init__(self, name, field_names, field_lltys):
dbarbera's avatar
dbarbera committed
324
        self.name = name
325
        self.field_names = field_names
dbarbera's avatar
dbarbera committed
326
        self.llty = lc.Type.struct(field_lltys, self.name)
327
328
329

    def idx(self, field_name):
        return self.field_names.index(field_name)
dbarbera's avatar
dbarbera committed
330
331


dbarbera's avatar
dbarbera committed
332
class UnionType():
333
    def __init__(self, name, field_names, field_lltys, ctx):
dbarbera's avatar
dbarbera committed
334
335
        self.name = name
        self.field_names = field_names
336
        self.field_lltys = field_lltys
dbarbera's avatar
dbarbera committed
337
338
        # 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
339
        self.size = max([ctx.target_data.size(ty) for ty in field_lltys])
dbarbera's avatar
dbarbera committed
340
        self.llty = lc.Type.struct([ctx.i32, lc.Type.array(ctx.i8, self.size)], name)
dbarbera's avatar
dbarbera committed
341
342
343

    def kind(self, name):
        idx = self.field_names.index(name)
344
        return (idx, self.field_lltys[idx])
dbarbera's avatar
dbarbera committed
345
346


347
class Scope:
dbarbera's avatar
dbarbera committed
348
349
    def __init__(self, ctx, parent=None):
        self.ctx = ctx
350
        self.vars = {}
dbarbera's avatar
dbarbera committed
351
        self.labels = {}
352
353
354
        self.parent = parent

    def define(self, name, var):
dbarbera's avatar
dbarbera committed
355
        self.vars[name.lower().replace('-', '_')] = var
356
357

    def resolve(self, name):
dbarbera's avatar
dbarbera committed
358
        var = self.vars.get(name.lower().replace('-', '_'))
359
360
361
362
363
        if var:
            return var
        if self.parent:
            return self.parent.resolve(name)
        else:
dbarbera's avatar
dbarbera committed
364
            raise NameError("name '%s' is not defined" % name)
365

dbarbera's avatar
dbarbera committed
366
367
368
369
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
370
            func = self.ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
371
            label_block = func.append_basic_block('label:%s' % name)
dbarbera's avatar
dbarbera committed
372
373
374
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
375

376
377
378
379
class CompileError(Exception):
    pass


Maxime Perrotin's avatar
Maxime Perrotin committed
380
@singledispatch
381
def generate(ast, ctx=None, options=None):
dbarbera's avatar
dbarbera committed
382
    ''' Generate the IR for an AST node '''
383
    raise CompileError('Unsupported AST construct "%s"' % ast.__class__.__name__)
Maxime Perrotin's avatar
Maxime Perrotin committed
384

dbarbera's avatar
dbarbera committed
385

Maxime Perrotin's avatar
Maxime Perrotin committed
386
387
# Processing of the AST
@generate.register(ogAST.Process)
388
def _process(process, ctx=None, options=None):
dbarbera's avatar
dbarbera committed
389
    ''' Generate the IR for a process '''
dbarbera's avatar
dbarbera committed
390
391
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
392

dbarbera's avatar
dbarbera committed
393
    ctx = Context(process)
394

dbarbera's avatar
dbarbera committed
395
    # In case model has nested states, flatten everything
396
    Helper.flatten(process, '.')
dbarbera's avatar
dbarbera committed
397
398
399
400

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

402
403
    # Initialize states
    for name, val in process.mapping.viewitems():
404
        if not name.endswith('START'):
dbarbera's avatar
dbarbera committed
405
            cons_val = lc.Constant.int(ctx.i32, len(ctx.states))
dbarbera's avatar
dbarbera committed
406
            ctx.states[name.lower()] = cons_val
407
        elif name != 'START':
dbarbera's avatar
dbarbera committed
408
            cons_val = lc.Constant.int(ctx.i32, val)
dbarbera's avatar
dbarbera committed
409
            ctx.states[name.lower()] = cons_val
410

411
    # Generate state var
412
    state_cons = ctx.module.add_global_variable(ctx.i32, '.state')
dbarbera's avatar
dbarbera committed
413
    state_cons.initializer = lc.Constant.int(ctx.i32, -1)
414
    ctx.scope.define('.state', state_cons)
415

dbarbera's avatar
dbarbera committed
416
417
418
    # Generate ASN.1 constants
    for name, t in process.dv.variables.viewitems():
        var_llty = ctx.lltype_of(t.type)
419
420
421
        name = str(name).replace('-', '_').lower()
        global_var = ctx.module.add_global_variable(var_llty, name)
        ctx.scope.define(name, global_var)
dbarbera's avatar
dbarbera committed
422

423
    # Generare process-level vars
424
425
426
    for name, (asn1ty, expr) in process.variables.viewitems():
        var_llty = ctx.lltype_of(asn1ty)
        global_var = ctx.module.add_global_variable(var_llty, str(name))
dbarbera's avatar
dbarbera committed
427
        global_var.initializer = lc.Constant.null(var_llty)
dbarbera's avatar
dbarbera committed
428
        ctx.scope.define(str(name).lower(), global_var)
429

dbarbera's avatar
dbarbera committed
430
    # Declare set/reset timer functions
dbarbera's avatar
dbarbera committed
431
432
    for timer in process.timers:
        # TODO: Should be uint?
dbarbera's avatar
dbarbera committed
433
        ctx.decl_func("set_%s" % str(timer), ctx.void, [ctx.i64_ptr], True)
434
        ctx.decl_func("reset_%s" % str(timer), ctx.void, [], True)
dbarbera's avatar
dbarbera committed
435

436
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
437
    for signal in process.output_signals:
438
        if 'type' in signal:
dbarbera's avatar
dbarbera committed
439
            param_lltys = [lc.Type.pointer(ctx.lltype_of(signal['type']))]
440
        else:
441
442
            param_lltys = []
        ctx.decl_func(str(signal['name']), ctx.void, param_lltys, True)
dbarbera's avatar
dbarbera committed
443

444
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
445
    for proc in [proc for proc in process.procedures if proc.external]:
dbarbera's avatar
dbarbera committed
446
        param_lltys = [lc.Type.pointer(ctx.lltype_of(p['type'])) for p in proc.fpar]
447
        ctx.decl_func(str(proc.inputString), ctx.void, param_lltys, True)
dbarbera's avatar
dbarbera committed
448

449
450
    # Generate internal procedures
    for proc in process.content.inner_procedures:
dbarbera's avatar
dbarbera committed
451
        generate(proc, ctx)
452

453
    # Generate process functions
dbarbera's avatar
dbarbera committed
454
455
    generate_runtr_func(process, ctx)
    generate_startup_func(process, ctx)
456

457
458
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
459
        generate_input_signal(signal, mapping[signal['name']], ctx)
460

dbarbera's avatar
dbarbera committed
461
462
    # Generate timer signal
    for timer in process.timers:
dbarbera's avatar
dbarbera committed
463
        generate_input_signal({'name': timer.lower()}, mapping[timer], ctx)
dbarbera's avatar
dbarbera committed
464

dbarbera's avatar
dbarbera committed
465
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
466

467
468
469
470
471
472
473
474
475
476
477
    if options and options.optimization:
        LOG.info('Optimizing generated LLVM IR code for process %s at level %d'
            % (ctx.name, options.optimization))
        pm = passes.PassManager.new()

        pmb = passes.PassManagerBuilder.new()
        pmb.opt_level = options.optimization
        pmb.populate(pm)

        pm.run(ctx.module)

dbarbera's avatar
dbarbera committed
478
479
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
480
481


dbarbera's avatar
dbarbera committed
482
def generate_runtr_func(process, ctx):
dbarbera's avatar
dbarbera committed
483
    ''' Generate the IR for the run_transition function '''
484
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
485

486
    ctx.open_scope()
487

dbarbera's avatar
dbarbera committed
488
489
490
491
    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')
492

dbarbera's avatar
dbarbera committed
493
    ctx.builder = lc.Builder.new(entry_block)
494
495

    # entry
dbarbera's avatar
dbarbera committed
496
497
498
499
    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)
500
501

    # cond
dbarbera's avatar
dbarbera committed
502
    ctx.builder.position_at_end(cond_block)
dbarbera's avatar
dbarbera committed
503
    no_tr_cons = lc.Constant.int(ctx.i32, -1)
dbarbera's avatar
dbarbera committed
504
    id_val = ctx.builder.load(id_ptr)
dbarbera's avatar
dbarbera committed
505
    cond_val = ctx.builder.icmp(lc.ICMP_NE, id_val, no_tr_cons, 'cond')
dbarbera's avatar
dbarbera committed
506
    ctx.builder.cbranch(cond_val, body_block, exit_block)
507
508

    # body
dbarbera's avatar
dbarbera committed
509
    ctx.builder.position_at_end(body_block)
510
    switch = ctx.builder.switch(id_val, exit_block)
511
512
513

    # transitions
    for idx, tr in enumerate(process.transitions):
dbarbera's avatar
dbarbera committed
514
        tr_block = func.append_basic_block('runtr:tr%d' % idx)
dbarbera's avatar
dbarbera committed
515
        const = lc.Constant.int(ctx.i32, idx)
516
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
517
        ctx.builder.position_at_end(tr_block)
dbarbera's avatar
dbarbera committed
518
        generate(tr, ctx)
dbarbera's avatar
dbarbera committed
519
520
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
521
522

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

dbarbera's avatar
dbarbera committed
526
527
    Helper.inner_labels_to_floating(process)
    for label in process.content.floating_labels:
dbarbera's avatar
dbarbera committed
528
        generate(label, ctx)
dbarbera's avatar
dbarbera committed
529

dbarbera's avatar
dbarbera committed
530
531
532
    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
533

534
    ctx.close_scope()
535

536
537
538
539
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
540
def generate_startup_func(process, ctx):
dbarbera's avatar
dbarbera committed
541
    ''' Generate the IR for the startup function '''
542
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
543

544
    ctx.open_scope()
545

dbarbera's avatar
dbarbera committed
546
    entry_block = func.append_basic_block('startup:entry')
dbarbera's avatar
dbarbera committed
547
    ctx.builder = lc.Builder.new(entry_block)
548

549
550
551
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
552
            global_var = ctx.scope.resolve(str(name))
553
            sdl_assign(global_var, expression(expr, ctx), ctx)
554

dbarbera's avatar
dbarbera committed
555
    sdl_call('run_transition', [lc.Constant.int(ctx.i32, 0)], ctx)
dbarbera's avatar
dbarbera committed
556
    ctx.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
557

558
    ctx.close_scope()
559

560
561
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
562
563


dbarbera's avatar
dbarbera committed
564
def generate_input_signal(signal, inputs, ctx):
dbarbera's avatar
dbarbera committed
565
    ''' Generate the IR for an input signal '''
dbarbera's avatar
dbarbera committed
566
    func_name = ctx.name + "_" + str(signal['name'])
567
    param_lltys = []
568
    if 'type' in signal:
dbarbera's avatar
dbarbera committed
569
        param_lltys.append(lc.Type.pointer(ctx.lltype_of(signal['type'])))
570

571
    func = ctx.decl_func(func_name, ctx.void, param_lltys)
572

573
    ctx.open_scope()
574

dbarbera's avatar
dbarbera committed
575
576
    entry_block = func.append_basic_block('input:entry')
    exit_block = func.append_basic_block('input:exit')
dbarbera's avatar
dbarbera committed
577
    ctx.builder = lc.Builder.new(entry_block)
578

579
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('.state'))
dbarbera's avatar
dbarbera committed
580
    switch = ctx.builder.switch(g_state_val, exit_block)
581

dbarbera's avatar
dbarbera committed
582
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
583
        if state_name.endswith('start'):
dbarbera's avatar
dbarbera committed
584
            continue
585

dbarbera's avatar
dbarbera committed
586
        state_block = func.append_basic_block('input:state_%s' % str(state_name))
587
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
588
        ctx.builder.position_at_end(state_block)
589

590
591
592
593
594
595
596
597
598
599
600
        state_input = inputs.get(state_name)
        if not state_input:
            ctx.builder.ret_void()
            continue

        trans = state_input.transition

        for exit_func_name in exit_list(state_name, ctx.process):
            if trans and all(exit_func_name.startswith(trans_st)
                             for trans_st in trans.possible_states):
                sdl_call(exit_func_name, [], ctx)
601

602
603
604
605
606
607
608
609
        for var_name in state_input.parameters:
            var_ptr = ctx.scope.resolve(str(var_name))
            if is_struct_ptr(var_ptr) or is_array_ptr(var_ptr):
                sdl_assign(var_ptr, func.args[0], ctx)
            else:
                sdl_assign(var_ptr, ctx.builder.load(func.args[0]), ctx)

        if trans:
dbarbera's avatar
dbarbera committed
610
            id_val = lc.Constant.int(ctx.i32, state_input.transition_id)
611
            sdl_call('run_transition', [id_val], ctx)
612

dbarbera's avatar
dbarbera committed
613
        ctx.builder.ret_void()
614

dbarbera's avatar
dbarbera committed
615
616
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
617

618
    ctx.close_scope()
619

620
621
622
    func.verify()


623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
def exit_list(state_name, process):
    ''' Calculate the exit call list of a state '''
    context = process
    exitlist = []
    current = ''
    state_tree = state_name.split('.')

    while state_tree:
        current = current + state_tree.pop(0)
        for comp in context.composite_states:
            if current.lower() == comp.statename.lower():
                if comp.exit_procedure:
                    exitlist.append(current + '.exit')
                context = comp
                current = current + '.'
                break

    return reversed(exitlist)


Maxime Perrotin's avatar
Maxime Perrotin committed
643
@generate.register(ogAST.Output)
644
645
def _output(output, ctx):
    ''' Generate the IR for an output '''
dbarbera's avatar
dbarbera committed
646
647
    for out in output.output:
        name = out['outputName'].lower()
648
        args = out.get('params', [])
dbarbera's avatar
dbarbera committed
649

650
651
        arg_vals = []
        for arg in args:
dbarbera's avatar
dbarbera committed
652
            arg_val = expression(arg, ctx)
653
            # Pass by reference
dbarbera's avatar
dbarbera committed
654
            if arg_val.type.kind != lc.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
655
656
                arg_var = ctx.builder.alloca(arg_val.type, None)
                ctx.builder.store(arg_val, arg_var)
657
                arg_vals.append(arg_var)
658
            else:
659
                arg_vals.append(arg_val)
dbarbera's avatar
dbarbera committed
660

dbarbera's avatar
dbarbera committed
661
        sdl_call(str(name).lower(), arg_vals, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
662
663


664
665
666
667
668
669
670
671
672
@generate.register(ogAST.ProcedureCall)
def _proc_call(proc_call, ctx):
    ''' Generate the IR for a procedure call '''
    output = proc_call.output[0]

    name = output['outputName'].lower()
    args = output.get('params', [])

    if name == 'write' or name == 'writeln':
673
        def flatten_append(arg):
674
            ''' Transform "write(a//b//...)" to "write(a, b, ...)" '''
675
676
677
678
679
680
681
682
683
684
            if isinstance(arg, ogAST.ExprAppend):
                res = flatten_append(arg.left)
                res.extend(flatten_append(arg.right))
                return res
            return [arg]
        flat_args = []
        for each in args:
            flat_args.extend(flatten_append(each))
        arg_vals = [expression(a, ctx) for a in flat_args]
        arg_asn1tys = [a.exprType for a in flat_args]
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
        sdl_write(arg_vals, arg_asn1tys, ctx, name == 'writeln')
        return
    elif name == 'reset_timer':
        sdl_reset_timer(args[0].value[0], ctx)
        return
    elif name == 'set_timer':
        timer_expr, timer_id = args
        sdl_set_timer(timer_id.value[0], expression(timer_expr, ctx), ctx)
        return

    proc = None
    for p in ctx.procedures:
        if p.inputString.lower() == name:
            proc = p
            break
    else:
        raise CompileError('Procedure "%s" not found' % name)

    arg_vals = []
    for arg, param in zip(args, proc.fpar):
        if param['direction'] == 'out':
            arg_vals.append(reference(arg, ctx))
        else:
            arg_val = expression(arg, ctx)
            # Pass by reference
dbarbera's avatar
dbarbera committed
710
            if arg_val.type.kind != lc.TYPE_POINTER:
711
712
713
714
715
716
717
718
719
                arg_var = ctx.builder.alloca(arg_val.type, None)
                ctx.builder.store(arg_val, arg_var)
                arg_vals.append(arg_var)
            else:
                arg_vals.append(arg_val)

    sdl_call(str(name).lower(), arg_vals, ctx)


Maxime Perrotin's avatar
Maxime Perrotin committed
720
@generate.register(ogAST.TaskAssign)
dbarbera's avatar
dbarbera committed
721
def _task_assign(task, ctx):
dbarbera's avatar
dbarbera committed
722
    ''' Generate the IR for a list of assignments '''
723
    for expr in task.elems:
dbarbera's avatar
dbarbera committed
724
        expression(expr, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
725
726
727


@generate.register(ogAST.TaskInformalText)
dbarbera's avatar
dbarbera committed
728
def _task_informal_text(task, ctx):
Maxime Perrotin's avatar
Maxime Perrotin committed
729
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
730
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
731
732
733


@generate.register(ogAST.TaskForLoop)
dbarbera's avatar
dbarbera committed
734
def _task_forloop(task, ctx):
dbarbera's avatar
dbarbera committed
735
    ''' Generate the IRfor a for loop '''
dbarbera's avatar
dbarbera committed
736
737
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
738
            generate_for_range(loop, ctx)
dbarbera's avatar
dbarbera committed
739
        else:
dbarbera's avatar
dbarbera committed
740
            generate_for_iterable(loop, ctx)
dbarbera's avatar
dbarbera committed
741
742


dbarbera's avatar
dbarbera committed
743
def generate_for_range(loop, ctx):
dbarbera's avatar
dbarbera committed
744
    ''' Generate the IR for a for x in range loop '''
dbarbera's avatar
dbarbera committed
745
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
746
747
748
    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
749
    end_block = func.append_basic_block('for:end')
dbarbera's avatar
dbarbera committed
750

751
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
752

dbarbera's avatar
dbarbera committed
753
    loop_var = ctx.builder.alloca(ctx.i64, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
754
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
755
756

    if loop['range']['start']:
dbarbera's avatar
dbarbera committed
757
        start_val = expression(loop['range']['start'], ctx)
dbarbera's avatar
dbarbera committed
758
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
759
    else:
dbarbera's avatar
dbarbera committed
760
        ctx.builder.store(lc.Constant.int(ctx.i64, 0), loop_var)
dbarbera's avatar
dbarbera committed
761

dbarbera's avatar
dbarbera committed
762
    stop_val = expression(loop['range']['stop'], ctx)
dbarbera's avatar
dbarbera committed
763
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
764

dbarbera's avatar
dbarbera committed
765
766
    ctx.builder.position_at_end(cond_block)
    loop_val = ctx.