LlvmGenerator.py 75.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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    OpenGEODE - A tiny SDL Editor for TASTE

    This module generates LLVM IR code from SDL process models, allowing
    generation of a binary application without an intermediate language.
    LLVM also allows for various code verification, analysis, and optimization.

    The design is based on the Ada code generator. Check it for details.

    Copyright (c) 2012-2013 European Space Agency

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

    Contact: maxime.perrotin@esa.int
"""

import logging
21

Maxime Perrotin's avatar
Maxime Perrotin committed
22
from singledispatch import singledispatch
dbarbera's avatar
dbarbera committed
23
24
25

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

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

LOG = logging.getLogger(__name__)

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

Maxime Perrotin's avatar
Maxime Perrotin committed
36

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

45
        self.procedures = process.procedures
46

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        asn1ty_name = asn1ty.ReferencedTypeName.lower()

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

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

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

165
        return basic_asn1ty
166

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

177
        basic_asn1ty = self.basic_asn1type_of(asn1ty)
178

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

        if name:
            self.lltypes[name] = llty

        return llty

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

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

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

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

224
        return struct.llty
225

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

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

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

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

240
        return struct.llty
241

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

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

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

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

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

259
        return union.llty
260

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

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

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

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

277
        return struct.llty
278

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

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

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

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

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

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

dbarbera's avatar
dbarbera committed
322

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

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


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

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


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

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

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

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

dbarbera's avatar
dbarbera committed
376

377
378
379
380
class CompileError(Exception):
    pass


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

dbarbera's avatar
dbarbera committed
386

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

466
467
468
469
    try:
        ctx.module.verify()
    except LLVMException as err:
        LOG.error(str(err))
dbarbera's avatar
dbarbera committed
470

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

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

        pm.run(ctx.module)

dbarbera's avatar
dbarbera committed
482
483
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
484
485


dbarbera's avatar
dbarbera committed
486
def generate_runtr_func(process, ctx):
dbarbera's avatar
dbarbera committed
487
    ''' Generate the IR for the run_transition function '''
488
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
489

490
    ctx.open_scope()
491

dbarbera's avatar
dbarbera committed
492
493
494
495
    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')
496

dbarbera's avatar
dbarbera committed
497
    ctx.builder = lc.Builder.new(entry_block)
498
499

    # entry
dbarbera's avatar
dbarbera committed
500
501
502
503
    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)
504
505

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

    # body
dbarbera's avatar
dbarbera committed
513
    ctx.builder.position_at_end(body_block)
514
    switch = ctx.builder.switch(id_val, exit_block)
515
516
517

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

    # exit
dbarbera's avatar
dbarbera committed
527
528
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
529

dbarbera's avatar
dbarbera committed
530
531
    Helper.inner_labels_to_floating(process)
    for label in process.content.floating_labels:
dbarbera's avatar
dbarbera committed
532
        generate(label, ctx)
dbarbera's avatar
dbarbera committed
533

dbarbera's avatar
dbarbera committed
534
535
536
    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
537

538
    ctx.close_scope()
539

540
541
542
543
    try:
        func.verify()
    except LLVMException as err:
        LOG.error(str(err))
544
545
546
    return func


dbarbera's avatar
dbarbera committed
547
def generate_startup_func(process, ctx):
dbarbera's avatar
dbarbera committed
548
    ''' Generate the IR for the startup function '''
549
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
550

551
    ctx.open_scope()
552

dbarbera's avatar
dbarbera committed
553
    entry_block = func.append_basic_block('startup:entry')
dbarbera's avatar
dbarbera committed
554
    ctx.builder = lc.Builder.new(entry_block)
555

556
557
558
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
559
            global_var = ctx.scope.resolve(str(name))
560
561
562
563
564
            right = expression(expr, ctx)
            if isinstance(right, SDLStringLiteral):
                # Assigning string literal - make sure the left type is known
                right.typeof = ty
            sdl_assign(global_var, right, ctx)
565

dbarbera's avatar
dbarbera committed
566
    sdl_call('run_transition', [lc.Constant.int(ctx.i32, 0)], ctx)
dbarbera's avatar
dbarbera committed
567
    ctx.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
568

569
    ctx.close_scope()
570

571
572
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
573
574


dbarbera's avatar
dbarbera committed
575
def generate_input_signal(signal, inputs, ctx):
dbarbera's avatar
dbarbera committed
576
    ''' Generate the IR for an input signal '''
dbarbera's avatar
dbarbera committed
577
    func_name = ctx.name + "_" + str(signal['name'])
578
    param_lltys = []
579
    if 'type' in signal:
dbarbera's avatar
dbarbera committed
580
        param_lltys.append(lc.Type.pointer(ctx.lltype_of(signal['type'])))
581

582
    func = ctx.decl_func(func_name, ctx.void, param_lltys)
583

584
    ctx.open_scope()
585

dbarbera's avatar
dbarbera committed
586
587
    entry_block = func.append_basic_block('input:entry')
    exit_block = func.append_basic_block('input:exit')
dbarbera's avatar
dbarbera committed
588
    ctx.builder = lc.Builder.new(entry_block)
589

590
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('.state'))
dbarbera's avatar
dbarbera committed
591
    switch = ctx.builder.switch(g_state_val, exit_block)
592

dbarbera's avatar
dbarbera committed
593
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
594
        if state_name.endswith('start'):
dbarbera's avatar
dbarbera committed
595
            continue
596

dbarbera's avatar
dbarbera committed
597
        state_block = func.append_basic_block('input:state_%s' % str(state_name))
598
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
599
        ctx.builder.position_at_end(state_block)
600

601
602
603
604
605
606
607
608
609
610
611
        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)
612

613
614
615
616
617
618
619
620
        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
621
            id_val = lc.Constant.int(ctx.i32, state_input.transition_id)
622
            sdl_call('run_transition', [id_val], ctx)
623

dbarbera's avatar
dbarbera committed
624
        ctx.builder.ret_void()
625

dbarbera's avatar
dbarbera committed
626
627
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
628

629
    ctx.close_scope()
630

631
632
633
    func.verify()


634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
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
654
@generate.register(ogAST.Output)
655
656
def _output(output, ctx):
    ''' Generate the IR for an output '''
dbarbera's avatar
dbarbera committed
657
658
    for out in output.output:
        name = out['outputName'].lower()
659
        args = out.get('params', [])
dbarbera's avatar
dbarbera committed
660

661
662
        arg_vals = []
        for arg in args:
dbarbera's avatar
dbarbera committed
663
            arg_val = expression(arg, ctx)
664
            # Pass by reference
dbarbera's avatar
dbarbera committed
665
            if arg_val.type.kind != lc.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
666
667
                arg_var = ctx.builder.alloca(arg_val.type, None)
                ctx.builder.store(arg_val, arg_var)
668
                arg_vals.append(arg_var)
669
            else:
670
                arg_vals.append(arg_val)
dbarbera's avatar
dbarbera committed
671

dbarbera's avatar
dbarbera committed
672
        sdl_call(str(name).lower(), arg_vals, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
673
674


675
676
677
678
679
680
681
682
683
@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':
684
        def flatten_append(arg):
685
            ''' Transform "write(a//b//...)" to "write(a, b, ...)" '''
686
687
688
689
690
691
692
693
694
695
            if isinstance(arg, ogAST.ExprAppend):
                res = flatten_append(arg.left)
                res.extend(flatten_append(arg.right))
                return res
            return [arg]
        flat_args = []
        for each in args:
            flat_args.extend(flatten_append(each))
        arg_vals = [expression(a, ctx) for a in flat_args]
        arg_asn1tys = [a.exprType for a in flat_args]
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
        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
721
            if arg_val.type.kind != lc.TYPE_POINTER:
722
723
724
725
726
727
728
729
730
                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
731
@generate.register(ogAST.TaskAssign)
dbarbera's avatar
dbarbera committed
732
def _task_assign(task, ctx):
dbarbera's avatar
dbarbera committed
733
    ''' Generate the IR for a list of assignments '''
734
    for expr in task.elems:
dbarbera's avatar
dbarbera committed
735
        expression(expr, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
736
737
738


@generate.register(ogAST.TaskInformalText)
dbarbera's avatar
dbarbera committed
739
def _task_informal_text(task, ctx):
Maxime Perrotin's avatar
Maxime Perrotin committed
740
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
741
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
742
743
744


@generate.register(ogAST.TaskForLoop)
dbarbera's avatar
dbarbera committed
745
def _task_forloop(task, ctx):
dbarbera's avatar
dbarbera committed
746
    ''' Generate the IRfor a for loop '''
dbarbera's avatar
dbarbera committed
747
748
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
749
            generate_for_range(loop, ctx)
dbarbera's avatar
dbarbera committed
750
        else:
dbarbera's avatar
dbarbera committed
751
            generate_for_iterable(loop, ctx)
dbarbera's avatar
dbarbera committed
752
753


dbarbera's avatar
dbarbera committed
754
def generate_for_range(loop, ctx):
dbarbera's avatar
dbarbera committed
755
    ''' Generate the IR for a for x in range loop '''
dbarbera's avatar
dbarbera committed
756
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
757
758
759
    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
760
    end_block = func.append_basic_block('for:end')
dbarbera's avatar
dbarbera committed
761

762
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
763

dbarbera's avatar
dbarbera committed
764
    loop_var = ctx.builder.alloca(ctx.i64, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
765
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
766
767

    if loop['range']['start']:
dbarbera's avatar
dbarbera committed
768
        start_val = expression(loop['range']['start'], ctx)
dbarbera's avatar
dbarbera committed
769
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
770
    else:
dbarbera's avatar
dbarbera committed
771
        ctx.builder.store(lc.Constant.int(ctx.i64, 0), loop_var)
dbarbera's avatar
dbarbera committed
772