LlvmGenerator.py 69 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
dbarbera's avatar
dbarbera committed
42

43
        self.procedures = process.procedures
44

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        asn1ty_name = asn1ty.ReferencedTypeName.lower()

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

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

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

163
        return basic_asn1ty
164

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

175
        basic_asn1ty = self.basic_asn1type_of(asn1ty)
176

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

        if name:
            self.lltypes[name] = llty

        return llty

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

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

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

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

222
        return struct.llty
223

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

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

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

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

238
        return struct.llty
239

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

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

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

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

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

257
        return union.llty
258

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

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

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

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

275
        return struct.llty
276

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

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

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

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

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

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

dbarbera's avatar
dbarbera committed
320

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

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


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

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


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

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

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

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

dbarbera's avatar
dbarbera committed
374

375
376
377
378
class CompileError(Exception):
    pass


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

dbarbera's avatar
dbarbera committed
384

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

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

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

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

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

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

dbarbera's avatar
dbarbera committed
415
416
417
    # Generate ASN.1 constants
    for name, t in process.dv.variables.viewitems():
        var_llty = ctx.lltype_of(t.type)
418
419
420
        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
421

422
    # Generare process-level vars
423
424
425
    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
426
        global_var.initializer = lc.Constant.null(var_llty)
dbarbera's avatar
dbarbera committed
427
        ctx.scope.define(str(name).lower(), global_var)
428

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

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

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

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

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

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

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

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

dbarbera's avatar
dbarbera committed
466
467
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
468
469


dbarbera's avatar
dbarbera committed
470
def generate_runtr_func(process, ctx):
dbarbera's avatar
dbarbera committed
471
    ''' Generate the IR for the run_transition function '''
472
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
473

474
    ctx.open_scope()
475

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

dbarbera's avatar
dbarbera committed
481
    ctx.builder = lc.Builder.new(entry_block)
482
483

    # entry
dbarbera's avatar
dbarbera committed
484
485
486
487
    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)
488
489

    # cond
dbarbera's avatar
dbarbera committed
490
    ctx.builder.position_at_end(cond_block)
dbarbera's avatar
dbarbera committed
491
    no_tr_cons = lc.Constant.int(ctx.i32, -1)
dbarbera's avatar
dbarbera committed
492
    id_val = ctx.builder.load(id_ptr)
dbarbera's avatar
dbarbera committed
493
    cond_val = ctx.builder.icmp(lc.ICMP_NE, id_val, no_tr_cons, 'cond')
dbarbera's avatar
dbarbera committed
494
    ctx.builder.cbranch(cond_val, body_block, exit_block)
495
496

    # body
dbarbera's avatar
dbarbera committed
497
    ctx.builder.position_at_end(body_block)
498
    switch = ctx.builder.switch(id_val, exit_block)
499
500
501

    # transitions
    for idx, tr in enumerate(process.transitions):
dbarbera's avatar
dbarbera committed
502
        tr_block = func.append_basic_block('runtr:tr%d' % idx)
dbarbera's avatar
dbarbera committed
503
        const = lc.Constant.int(ctx.i32, idx)
504
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
505
        ctx.builder.position_at_end(tr_block)
dbarbera's avatar
dbarbera committed
506
        generate(tr, ctx)
dbarbera's avatar
dbarbera committed
507
508
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
509
510

    # exit
dbarbera's avatar
dbarbera committed
511
512
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
513

dbarbera's avatar
dbarbera committed
514
515
    Helper.inner_labels_to_floating(process)
    for label in process.content.floating_labels:
dbarbera's avatar
dbarbera committed
516
        generate(label, ctx)
dbarbera's avatar
dbarbera committed
517

dbarbera's avatar
dbarbera committed
518
519
520
    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
521

522
    ctx.close_scope()
523

524
525
526
527
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
528
def generate_startup_func(process, ctx):
dbarbera's avatar
dbarbera committed
529
    ''' Generate the IR for the startup function '''
530
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
531

532
    ctx.open_scope()
533

dbarbera's avatar
dbarbera committed
534
    entry_block = func.append_basic_block('startup:entry')
dbarbera's avatar
dbarbera committed
535
    ctx.builder = lc.Builder.new(entry_block)
536

537
538
539
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
540
            global_var = ctx.scope.resolve(str(name))
541
            sdl_assign(global_var, expression(expr, ctx), ctx)
542

dbarbera's avatar
dbarbera committed
543
    sdl_call('run_transition', [lc.Constant.int(ctx.i32, 0)], ctx)
dbarbera's avatar
dbarbera committed
544
    ctx.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
545

546
    ctx.close_scope()
547

548
549
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
550
551


dbarbera's avatar
dbarbera committed
552
def generate_input_signal(signal, inputs, ctx):
dbarbera's avatar
dbarbera committed
553
    ''' Generate the IR for an input signal '''
dbarbera's avatar
dbarbera committed
554
    func_name = ctx.name + "_" + str(signal['name'])
555
    param_lltys = []
556
    if 'type' in signal:
dbarbera's avatar
dbarbera committed
557
        param_lltys.append(lc.Type.pointer(ctx.lltype_of(signal['type'])))
558

559
    func = ctx.decl_func(func_name, ctx.void, param_lltys)
560

561
    ctx.open_scope()
562

dbarbera's avatar
dbarbera committed
563
564
    entry_block = func.append_basic_block('input:entry')
    exit_block = func.append_basic_block('input:exit')
dbarbera's avatar
dbarbera committed
565
    ctx.builder = lc.Builder.new(entry_block)
566

567
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('.state'))
dbarbera's avatar
dbarbera committed
568
    switch = ctx.builder.switch(g_state_val, exit_block)
569

dbarbera's avatar
dbarbera committed
570
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
571
        if state_name.endswith('start'):
dbarbera's avatar
dbarbera committed
572
            continue
573

dbarbera's avatar
dbarbera committed
574
        state_block = func.append_basic_block('input:state_%s' % str(state_name))
575
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
576
        ctx.builder.position_at_end(state_block)
577

578
579
580
581
582
583
584
585
586
587
588
        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)
589

590
591
592
593
594
595
596
597
        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
598
            id_val = lc.Constant.int(ctx.i32, state_input.transition_id)
599
            sdl_call('run_transition', [id_val], ctx)
600

dbarbera's avatar
dbarbera committed
601
        ctx.builder.ret_void()
602

dbarbera's avatar
dbarbera committed
603
604
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
605

606
    ctx.close_scope()
607

608
609
610
    func.verify()


611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
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
631
@generate.register(ogAST.Output)
632
633
def _output(output, ctx):
    ''' Generate the IR for an output '''
dbarbera's avatar
dbarbera committed
634
635
    for out in output.output:
        name = out['outputName'].lower()
636
        args = out.get('params', [])
dbarbera's avatar
dbarbera committed
637

638
639
        arg_vals = []
        for arg in args:
dbarbera's avatar
dbarbera committed
640
            arg_val = expression(arg, ctx)
641
            # Pass by reference
dbarbera's avatar
dbarbera committed
642
            if arg_val.type.kind != lc.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
643
644
                arg_var = ctx.builder.alloca(arg_val.type, None)
                ctx.builder.store(arg_val, arg_var)
645
                arg_vals.append(arg_var)
646
            else:
647
                arg_vals.append(arg_val)
dbarbera's avatar
dbarbera committed
648

dbarbera's avatar
dbarbera committed
649
        sdl_call(str(name).lower(), arg_vals, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
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
dbarbera's avatar
dbarbera committed
688
            if arg_val.type.kind != lc.TYPE_POINTER:
689
690
691
692
693
694
695
696
697
                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
698
@generate.register(ogAST.TaskAssign)
dbarbera's avatar
dbarbera committed
699
def _task_assign(task, ctx):
dbarbera's avatar
dbarbera committed
700
    ''' Generate the IR for a list of assignments '''
701
    for expr in task.elems:
dbarbera's avatar
dbarbera committed
702
        expression(expr, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
703
704
705


@generate.register(ogAST.TaskInformalText)
dbarbera's avatar
dbarbera committed
706
def _task_informal_text(task, ctx):
Maxime Perrotin's avatar
Maxime Perrotin committed
707
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
708
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
709
710
711


@generate.register(ogAST.TaskForLoop)
dbarbera's avatar
dbarbera committed
712
def _task_forloop(task, ctx):
dbarbera's avatar
dbarbera committed
713
    ''' Generate the IRfor a for loop '''
dbarbera's avatar
dbarbera committed
714
715
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
716
            generate_for_range(loop, ctx)
dbarbera's avatar
dbarbera committed
717
        else:
dbarbera's avatar
dbarbera committed
718
            generate_for_iterable(loop, ctx)
dbarbera's avatar
dbarbera committed
719
720


dbarbera's avatar
dbarbera committed
721
def generate_for_range(loop, ctx):
dbarbera's avatar
dbarbera committed
722
    ''' Generate the IR for a for x in range loop '''
dbarbera's avatar
dbarbera committed
723
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
724
725
726
    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
727
    end_block = func.append_basic_block('for:end')
dbarbera's avatar
dbarbera committed
728

729
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
730

dbarbera's avatar
dbarbera committed
731
    loop_var = ctx.builder.alloca(ctx.i64, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
732
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
733
734

    if loop['range']['start']:
dbarbera's avatar
dbarbera committed
735
        start_val = expression(loop['range']['start'], ctx)
dbarbera's avatar
dbarbera committed
736
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
737
    else:
dbarbera's avatar
dbarbera committed
738
        ctx.builder.store(lc.Constant.int(ctx.i64, 0), loop_var)
dbarbera's avatar
dbarbera committed
739

dbarbera's avatar
dbarbera committed
740
    stop_val = expression(loop['range']['stop'], ctx)
dbarbera's avatar
dbarbera committed
741
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
742

dbarbera's avatar
dbarbera committed
743
744
    ctx.builder.position_at_end(cond_block)
    loop_val = ctx.builder.load(loop_var)
dbarbera's avatar
dbarbera committed
745
    cond_val = ctx.builder.icmp(lc.ICMP_SLT, loop_val, stop_val)
dbarbera's avatar
dbarbera committed
746
    ctx.builder.cbranch(cond_val, body_block, end_block)
dbarbera's avatar
dbarbera committed
747

dbarbera's avatar
dbarbera committed
748
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
749
    generate(loop['transition'], ctx)
dbarbera's avatar
dbarbera committed
750
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
751

dbarbera's avatar
dbarbera committed
752
    ctx.builder.position_at_end(inc_block)
dbarbera's avatar
dbarbera committed
753
    step_val = lc.Constant.int(ctx.i64, loop['range']['step'])
dbarbera's avatar
dbarbera committed
754
755
756
757
    loop_val = ctx.builder.load(loop_var)
    temp_val = ctx.builder.add(loop_val