LlvmGenerator.py 68.8 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
class Context():
34
    def __init__(self, process):
dbarbera's avatar
dbarbera committed
35
        self.name = str(process.processName)
36
        self.process = process
dbarbera's avatar
dbarbera committed
37
        self.module = core.Module.new(self.name)
dbarbera's avatar
dbarbera committed
38
        self.target_data = ee.TargetData.new(self.module.data_layout)
39
        self.dataview = process.dataview
40
        self.procedures = process.procedures
41

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

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

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

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

dbarbera's avatar
dbarbera committed
74
75
76
        ty = core.Type.function(self.double, [self.double])
        self.funcs['round'] = self.module.add_function(ty, 'round')

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

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

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

dbarbera's avatar
dbarbera committed
95
96
97
98
99
100
101
102
103
104
105
106
        self.funcs['cos'] = core.Function.intrinsic(
            self.module,
            core.INTR_COS,
            [self.double]
        )

        self.funcs['fabs'] = core.Function.intrinsic(
            self.module,
            core.INTR_FABS,
            [self.double]
        )

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

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

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

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

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

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

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

        asn1ty_name = asn1ty.ReferencedTypeName.lower()

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

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

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

160
        return basic_asn1ty
161

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

172
        basic_asn1ty = self.basic_asn1type_of(asn1ty)
173

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

        if name:
            self.lltypes[name] = llty

        return llty

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

208
209
        elem_llty = self.lltype_of(asn1ty.type)
        array_llty = core.Type.array(elem_llty, max_size)
210
211

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

216
        struct_ptr = core.Type.pointer(struct.llty)
217
        self.decl_func("asn1Scc%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])
218

219
        return struct.llty
220

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

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

230
        struct = self.decl_struct(field_names, field_lltys, name)
231

232
        struct_ptr = core.Type.pointer(struct.llty)
233
        self.decl_func("asn1Scc%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])
234

235
        return struct.llty
236

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

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

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

249
        union = self.decl_union(field_names, field_lltys, name)
250

251
        union_ptr = core.Type.pointer(union.llty)
252
        self.decl_func("asn1Scc%s_Equal" % name, self.i1, [union_ptr, union_ptr])
253

254
        return union.llty
255

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

262
        array_llty = core.Type.array(self.i8, max_size)
263
264

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

269
        struct_ptr = core.Type.pointer(struct.llty)
270
        self.decl_func("asn1Scc%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])
271

272
        return struct.llty
273

274
275
276
277
278
279
280
281
282
283
284
285
    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])

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

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

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

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

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

dbarbera's avatar
dbarbera committed
317

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

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


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

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


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

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

dbarbera's avatar
dbarbera committed
371

372
373
374
375
class CompileError(Exception):
    pass


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

dbarbera's avatar
dbarbera committed
381

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

dbarbera's avatar
dbarbera committed
389
    ctx = Context(process)
390

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

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

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

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

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

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

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

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

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

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

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

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

dbarbera's avatar
dbarbera committed
454
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
455

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


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

464
    ctx.open_scope()
465

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

dbarbera's avatar
dbarbera committed
471
    ctx.builder = core.Builder.new(entry_block)
472
473

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

    # cond
dbarbera's avatar
dbarbera committed
480
481
482
483
484
    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)
485
486

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

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

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

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

dbarbera's avatar
dbarbera committed
508
509
510
    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
511

512
    ctx.close_scope()
513

514
515
516
517
    func.verify()
    return func


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

522
    ctx.open_scope()
523

dbarbera's avatar
dbarbera committed
524
    entry_block = func.append_basic_block('startup:entry')
dbarbera's avatar
dbarbera committed
525
    ctx.builder = core.Builder.new(entry_block)
526

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

dbarbera's avatar
dbarbera committed
533
    sdl_call('run_transition', [core.Constant.int(ctx.i32, 0)], ctx)
dbarbera's avatar
dbarbera committed
534
    ctx.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
535

536
    ctx.close_scope()
537

538
539
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
540
541


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

549
    func = ctx.decl_func(func_name, ctx.void, param_lltys)
550

551
    ctx.open_scope()
552

dbarbera's avatar
dbarbera committed
553
554
    entry_block = func.append_basic_block('input:entry')
    exit_block = func.append_basic_block('input:exit')
dbarbera's avatar
dbarbera committed
555
    ctx.builder = core.Builder.new(entry_block)
556

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

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

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

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

580
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:
            id_val = core.Constant.int(ctx.i32, state_input.transition_id)
            sdl_call('run_transition', [id_val], ctx)
590

dbarbera's avatar
dbarbera committed
591
        ctx.builder.ret_void()
592

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

596
    ctx.close_scope()
597

598
599
600
    func.verify()


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

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

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


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
680
681
682
683
684
685
686
687
@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
            if arg_val.type.kind != core.TYPE_POINTER:
                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
688
@generate.register(ogAST.TaskAssign)
dbarbera's avatar
dbarbera committed
689
def _task_assign(task, ctx):
dbarbera's avatar
dbarbera committed
690
    ''' Generate the IR for a list of assignments '''
691
    for expr in task.elems:
dbarbera's avatar
dbarbera committed
692
        expression(expr, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
693
694
695


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


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


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

719
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
720

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

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

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

dbarbera's avatar
dbarbera committed
733
734
735
736
    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
737

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

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

dbarbera's avatar
dbarbera committed
749
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
750

751
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
752
753


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

dbarbera's avatar
dbarbera committed
759
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
760
761
762
763
764
765
766
767

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

770
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
771

dbarbera's avatar
dbarbera committed
772
773
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
774
    seqof_val = expression(loop['list'], ctx)
775

776
777
778
779
    if isinstance(seqof_val, SDLSubstringValue):
        array_ptr = seqof_val.arr_ptr
    elif is_variable_size:
        array_ptr = ctx.builder.gep(seqof_val, [ctx.zero, ctx.one])
780
    else:
781
        array_ptr = ctx.builder.gep(seqof_val, [ctx.zero, ctx.zero])
782

783
    elem_llty = array_ptr.type.pointee.element
784

785
786
787
    if isinstance(seqof_val, SDLSubstringValue):
        end_idx = seqof_val.count_val
    elif is_variable_size:
788
        # load the current number of elements that is on the first field
789
        end_idx = ctx.builder.load(ctx.builder.gep(seqof_val, [ctx.zero, ctx.zero]))
790
    else:
dbarbera's avatar
dbarbera committed
791
        end_idx = core.Constant.int(ctx.i32, array_ptr.type.pointee.count)
dbarbera's avatar
dbarbera committed
792

793
    var_ptr = ctx.builder.alloca(elem_llty, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
794
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
795

dbarbera's avatar
dbarbera committed
796
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
797
798

    # load block
dbarbera's avatar
dbarbera committed
799
800
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)