LlvmGenerator.py 68.6 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
24
25

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

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

LOG = logging.getLogger(__name__)

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

Maxime Perrotin's avatar
Maxime Perrotin committed
34

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

dbarbera's avatar
dbarbera committed
44
        self.scope = Scope(self)
45
        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
        self.basic_asn1types = {}
54
55

        # Initialize built-in types
dbarbera's avatar
dbarbera committed
56
57
58
59
60
61
62
63
64
65
66
        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)
67

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        asn1ty_name = asn1ty.ReferencedTypeName.lower()

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

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

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

162
        return basic_asn1ty
163

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

174
        basic_asn1ty = self.basic_asn1type_of(asn1ty)
175

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

        if name:
            self.lltypes[name] = llty

        return llty

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

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

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

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

221
        return struct.llty
222

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

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

232
        struct = self.decl_struct(field_names, field_lltys, name)
233

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

237
        return struct.llty
238

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

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

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

251
        union = self.decl_union(field_names, field_lltys, name)
252

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

256
        return union.llty
257

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

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

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

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

274
        return struct.llty
275

276
277
278
279
280
    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
281
        str_val = lc.Constant.stringz(str)
282
283
284
285
286
287
        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])

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

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

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

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

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

dbarbera's avatar
dbarbera committed
319

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

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


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

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


345
class Scope:
dbarbera's avatar
dbarbera committed
346
347
    def __init__(self, ctx, parent=None):
        self.ctx = ctx
348
        self.vars = {}
dbarbera's avatar
dbarbera committed
349
        self.labels = {}
350
351
352
353
354
355
356
357
358
359
360
361
        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
362
            raise NameError("name '%s' is not defined" % name)
363

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

dbarbera's avatar
dbarbera committed
373

374
375
376
377
class CompileError(Exception):
    pass


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

dbarbera's avatar
dbarbera committed
383

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

dbarbera's avatar
dbarbera committed
391
    ctx = Context(process)
392

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

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

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

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

414
    # Generare process-level vars
415
416
417
    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
418
        global_var.initializer = lc.Constant.null(var_llty)
dbarbera's avatar
dbarbera committed
419
        ctx.scope.define(str(name).lower(), global_var)
420

dbarbera's avatar
dbarbera committed
421
    # Declare set/reset timer functions
dbarbera's avatar
dbarbera committed
422
423
    for timer in process.timers:
        # TODO: Should be uint?
dbarbera's avatar
dbarbera committed
424
        ctx.decl_func("set_%s" % str(timer), ctx.void, [ctx.i64_ptr], True)
425
        ctx.decl_func("reset_%s" % str(timer), ctx.void, [], True)
dbarbera's avatar
dbarbera committed
426

427
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
428
    for signal in process.output_signals:
429
        if 'type' in signal:
dbarbera's avatar
dbarbera committed
430
            param_lltys = [lc.Type.pointer(ctx.lltype_of(signal['type']))]
431
        else:
432
433
            param_lltys = []
        ctx.decl_func(str(signal['name']), ctx.void, param_lltys, True)
dbarbera's avatar
dbarbera committed
434

435
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
436
    for proc in [proc for proc in process.procedures if proc.external]:
dbarbera's avatar
dbarbera committed
437
        param_lltys = [lc.Type.pointer(ctx.lltype_of(p['type'])) for p in proc.fpar]
438
        ctx.decl_func(str(proc.inputString), ctx.void, param_lltys, True)
dbarbera's avatar
dbarbera committed
439

440
441
    # Generate internal procedures
    for proc in process.content.inner_procedures:
dbarbera's avatar
dbarbera committed
442
        generate(proc, ctx)
443

444
    # Generate process functions
dbarbera's avatar
dbarbera committed
445
446
    generate_runtr_func(process, ctx)
    generate_startup_func(process, ctx)
447

448
449
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
450
        generate_input_signal(signal, mapping[signal['name']], ctx)
451

dbarbera's avatar
dbarbera committed
452
453
    # Generate timer signal
    for timer in process.timers:
dbarbera's avatar
dbarbera committed
454
        generate_input_signal({'name': timer.lower()}, mapping[timer], ctx)
dbarbera's avatar
dbarbera committed
455

dbarbera's avatar
dbarbera committed
456
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
457

dbarbera's avatar
dbarbera committed
458
459
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
460
461


dbarbera's avatar
dbarbera committed
462
def generate_runtr_func(process, ctx):
dbarbera's avatar
dbarbera committed
463
    ''' Generate the IR for the run_transition function '''
464
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
465

466
    ctx.open_scope()
467

dbarbera's avatar
dbarbera committed
468
469
470
471
    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')
472

dbarbera's avatar
dbarbera committed
473
    ctx.builder = lc.Builder.new(entry_block)
474
475

    # entry
dbarbera's avatar
dbarbera committed
476
477
478
479
    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)
480
481

    # cond
dbarbera's avatar
dbarbera committed
482
    ctx.builder.position_at_end(cond_block)
dbarbera's avatar
dbarbera committed
483
    no_tr_cons = lc.Constant.int(ctx.i32, -1)
dbarbera's avatar
dbarbera committed
484
    id_val = ctx.builder.load(id_ptr)
dbarbera's avatar
dbarbera committed
485
    cond_val = ctx.builder.icmp(lc.ICMP_NE, id_val, no_tr_cons, 'cond')
dbarbera's avatar
dbarbera committed
486
    ctx.builder.cbranch(cond_val, body_block, exit_block)
487
488

    # body
dbarbera's avatar
dbarbera committed
489
    ctx.builder.position_at_end(body_block)
490
    switch = ctx.builder.switch(id_val, exit_block)
491
492
493

    # transitions
    for idx, tr in enumerate(process.transitions):
dbarbera's avatar
dbarbera committed
494
        tr_block = func.append_basic_block('runtr:tr%d' % idx)
dbarbera's avatar
dbarbera committed
495
        const = lc.Constant.int(ctx.i32, idx)
496
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
497
        ctx.builder.position_at_end(tr_block)
dbarbera's avatar
dbarbera committed
498
        generate(tr, ctx)
dbarbera's avatar
dbarbera committed
499
500
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
501
502

    # exit
dbarbera's avatar
dbarbera committed
503
504
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
505

dbarbera's avatar
dbarbera committed
506
507
    Helper.inner_labels_to_floating(process)
    for label in process.content.floating_labels:
dbarbera's avatar
dbarbera committed
508
        generate(label, ctx)
dbarbera's avatar
dbarbera committed
509

dbarbera's avatar
dbarbera committed
510
511
512
    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
513

514
    ctx.close_scope()
515

516
517
518
519
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
520
def generate_startup_func(process, ctx):
dbarbera's avatar
dbarbera committed
521
    ''' Generate the IR for the startup function '''
522
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
523

524
    ctx.open_scope()
525

dbarbera's avatar
dbarbera committed
526
    entry_block = func.append_basic_block('startup:entry')
dbarbera's avatar
dbarbera committed
527
    ctx.builder = lc.Builder.new(entry_block)
528

529
530
531
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
532
            global_var = ctx.scope.resolve(str(name))
533
            sdl_assign(global_var, expression(expr, ctx), ctx)
534

dbarbera's avatar
dbarbera committed
535
    sdl_call('run_transition', [lc.Constant.int(ctx.i32, 0)], ctx)
dbarbera's avatar
dbarbera committed
536
    ctx.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
537

538
    ctx.close_scope()
539

540
541
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
542
543


dbarbera's avatar
dbarbera committed
544
def generate_input_signal(signal, inputs, ctx):
dbarbera's avatar
dbarbera committed
545
    ''' Generate the IR for an input signal '''
dbarbera's avatar
dbarbera committed
546
    func_name = ctx.name + "_" + str(signal['name'])
547
    param_lltys = []
548
    if 'type' in signal:
dbarbera's avatar
dbarbera committed
549
        param_lltys.append(lc.Type.pointer(ctx.lltype_of(signal['type'])))
550

551
    func = ctx.decl_func(func_name, ctx.void, param_lltys)
552

553
    ctx.open_scope()
554

dbarbera's avatar
dbarbera committed
555
556
    entry_block = func.append_basic_block('input:entry')
    exit_block = func.append_basic_block('input:exit')
dbarbera's avatar
dbarbera committed
557
    ctx.builder = lc.Builder.new(entry_block)
558

559
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('.state'))
dbarbera's avatar
dbarbera committed
560
    switch = ctx.builder.switch(g_state_val, exit_block)
561

dbarbera's avatar
dbarbera committed
562
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
563
        if state_name.endswith('start'):
dbarbera's avatar
dbarbera committed
564
            continue
565

dbarbera's avatar
dbarbera committed
566
        state_block = func.append_basic_block('input:state_%s' % str(state_name))
567
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
568
        ctx.builder.position_at_end(state_block)
569

570
571
572
573
574
575
576
577
578
579
580
        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)
581

582
583
584
585
586
587
588
589
        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
590
            id_val = lc.Constant.int(ctx.i32, state_input.transition_id)
591
            sdl_call('run_transition', [id_val], ctx)
592

dbarbera's avatar
dbarbera committed
593
        ctx.builder.ret_void()
594

dbarbera's avatar
dbarbera committed
595
596
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
597

598
    ctx.close_scope()
599

600
601
602
    func.verify()


603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
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
623
@generate.register(ogAST.Output)
624
625
def _output(output, ctx):
    ''' Generate the IR for an output '''
dbarbera's avatar
dbarbera committed
626
627
    for out in output.output:
        name = out['outputName'].lower()
628
        args = out.get('params', [])
dbarbera's avatar
dbarbera committed
629

630
631
        arg_vals = []
        for arg in args:
dbarbera's avatar
dbarbera committed
632
            arg_val = expression(arg, ctx)
633
            # Pass by reference
dbarbera's avatar
dbarbera committed
634
            if arg_val.type.kind != lc.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
635
636
                arg_var = ctx.builder.alloca(arg_val.type, None)
                ctx.builder.store(arg_val, arg_var)
637
                arg_vals.append(arg_var)
638
            else:
639
                arg_vals.append(arg_val)
dbarbera's avatar
dbarbera committed
640

dbarbera's avatar
dbarbera committed
641
        sdl_call(str(name).lower(), arg_vals, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
642
643


644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
@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':
        arg_vals = [expression(a, ctx) for a in args]
        arg_asn1tys = [a.exprType for a in args]
        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
680
            if arg_val.type.kind != lc.TYPE_POINTER:
681
682
683
684
685
686
687
688
689
                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
690
@generate.register(ogAST.TaskAssign)
dbarbera's avatar
dbarbera committed
691
def _task_assign(task, ctx):
dbarbera's avatar
dbarbera committed
692
    ''' Generate the IR for a list of assignments '''
693
    for expr in task.elems:
dbarbera's avatar
dbarbera committed
694
        expression(expr, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
695
696
697


@generate.register(ogAST.TaskInformalText)
dbarbera's avatar
dbarbera committed
698
def _task_informal_text(task, ctx):
Maxime Perrotin's avatar
Maxime Perrotin committed
699
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
700
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
701
702
703


@generate.register(ogAST.TaskForLoop)
dbarbera's avatar
dbarbera committed
704
def _task_forloop(task, ctx):
dbarbera's avatar
dbarbera committed
705
    ''' Generate the IRfor a for loop '''
dbarbera's avatar
dbarbera committed
706
707
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
708
            generate_for_range(loop, ctx)
dbarbera's avatar
dbarbera committed
709
        else:
dbarbera's avatar
dbarbera committed
710
            generate_for_iterable(loop, ctx)
dbarbera's avatar
dbarbera committed
711
712


dbarbera's avatar
dbarbera committed
713
def generate_for_range(loop, ctx):
dbarbera's avatar
dbarbera committed
714
    ''' Generate the IR for a for x in range loop '''
dbarbera's avatar
dbarbera committed
715
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
716
717
718
    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
719
    end_block = func.append_basic_block('for:end')
dbarbera's avatar
dbarbera committed
720

721
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
722

dbarbera's avatar
dbarbera committed
723
    loop_var = ctx.builder.alloca(ctx.i64, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
724
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
725
726

    if loop['range']['start']:
dbarbera's avatar
dbarbera committed
727
        start_val = expression(loop['range']['start'], ctx)
dbarbera's avatar
dbarbera committed
728
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
729
    else:
dbarbera's avatar
dbarbera committed
730
        ctx.builder.store(lc.Constant.int(ctx.i64, 0), loop_var)
dbarbera's avatar
dbarbera committed
731

dbarbera's avatar
dbarbera committed
732
    stop_val = expression(loop['range']['stop'], ctx)
dbarbera's avatar
dbarbera committed
733
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
734

dbarbera's avatar
dbarbera committed
735
736
    ctx.builder.position_at_end(cond_block)
    loop_val = ctx.builder.load(loop_var)
dbarbera's avatar
dbarbera committed
737
    cond_val = ctx.builder.icmp(lc.ICMP_SLT, loop_val, stop_val)
dbarbera's avatar
dbarbera committed
738
    ctx.builder.cbranch(cond_val, body_block, end_block)
dbarbera's avatar
dbarbera committed
739

dbarbera's avatar
dbarbera committed
740
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
741
    generate(loop['transition'], ctx)
dbarbera's avatar
dbarbera committed
742
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
743

dbarbera's avatar
dbarbera committed
744
    ctx.builder.position_at_end(inc_block)
dbarbera's avatar
dbarbera committed
745
    step_val = lc.Constant.int(ctx.i64, loop['range']['step'])
dbarbera's avatar
dbarbera committed
746
747
748
749
    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
750

dbarbera's avatar
dbarbera committed
751
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
752

753
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
754
755


dbarbera's avatar
dbarbera committed
756
def generate_for_iterable(loop, ctx):
dbarbera's avatar
dbarbera committed
757
    ''' Generate the IR for a for x in iterable loop '''
758
    seqof_asn1ty = ctx.basic_asn1type_of(loop['list'].exprType)