LlvmGenerator.py 81 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
Maxime Perrotin's avatar
Maxime Perrotin committed
249
            self.enums[field_name.replace('-', '_').lower()] = 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
    try:
        func.verify()
    except LLVMException as err:
Maxime Perrotin's avatar
Maxime Perrotin committed
543
        LOG.error('LLVM Verify: {}'.format(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
            right = expression(expr, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
561
562
563
            if isinstance(right, (SDLStringLiteral, SDLSequenceOf,
                                  SDLAppendValue, SDLSubstringValue)):
                # Propagate left type before processing the right side
564
565
                right.typeof = ty
            sdl_assign(global_var, right, ctx)
566

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

570
    ctx.close_scope()
571

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


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

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

585
    ctx.open_scope()
586

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

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

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

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

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

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

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

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

630
    ctx.close_scope()
631

632
633
634
    func.verify()


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

662
663
        arg_vals = []
        for arg in args:
dbarbera's avatar
dbarbera committed
664
            arg_val = expression(arg, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
665
666
667
668
669
            if isinstance(arg_val, SDLStringLiteral):
                arg_val = sdl_stringliteral(arg_val.string, arg_val.typeof, ctx)
            elif isinstance(arg_val, SDLSequenceOf):
                arg_val = sdl_sequenceof(arg_val, ctx)
            # XXX Add SDLSubstring and SDLAppendValue, no?
670
            # Pass by reference
dbarbera's avatar
dbarbera committed
671
            if arg_val.type.kind != lc.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
672
673
                arg_var = ctx.builder.alloca(arg_val.type, None)
                ctx.builder.store(arg_val, arg_var)
674
                arg_vals.append(arg_var)
675
            else:
676
                arg_vals.append(arg_val)
dbarbera's avatar
dbarbera committed
677

dbarbera's avatar
dbarbera committed
678
        sdl_call(str(name).lower(), arg_vals, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
679
680


681
682
683
684
685
686
687
688
689
@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':
690
        def flatten_append(arg):
691
            ''' Transform "write(a//b//...)" to "write(a, b, ...)" '''
692
693
694
695
696
697
698
699
700
701
            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]
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
        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)
Maxime Perrotin's avatar
Maxime Perrotin committed
726
727
            if isinstance(arg_val, SDLStringLiteral):
                arg_val = sdl_stringliteral(arg_val.string, arg_val.typeof, ctx)
728
            # Pass by reference
dbarbera's avatar
dbarbera committed
729
            if arg_val.type.kind != lc.TYPE_POINTER:
730
731
732
733
734
735
736
737
738
                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
739
@generate.register(ogAST.TaskAssign)
dbarbera's avatar
dbarbera committed
740
def _task_assign(task, ctx):
dbarbera's avatar
dbarbera committed
741
    ''' Generate the IR for a list of assignments '''
742
    for expr in task.elems:
dbarbera's avatar
dbarbera committed
743
        expression(expr, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
744
745
746


@generate.register(ogAST.TaskInformalText)
dbarbera's avatar
dbarbera committed
747
def _task_informal_text(task, ctx):
Maxime Perrotin's avatar
Maxime Perrotin committed
748
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
749
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
750
751
752


@generate.register(ogAST.TaskForLoop)
dbarbera's avatar
dbarbera committed
753
def _task_forloop(task, ctx):
dbarbera's avatar
dbarbera committed
754
    ''' Generate the IRfor a for loop '''
dbarbera's avatar
dbarbera committed
755
756
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
757
            generate_for_range(loop, ctx)
dbarbera's avatar
dbarbera committed
758
        else:
dbarbera's avatar
dbarbera committed
759
            generate_for_iterable(loop, ctx)
dbarbera's avatar
dbarbera committed
760
761


dbarbera's avatar
dbarbera committed
762
def generate_for_range(loop, ctx):
dbarbera's avatar
dbarbera committed
763
    ''' Generate the IR for a for x in range loop '''
dbarbera's avatar
dbarbera committed
764
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
765
766
767
    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
768
    end_block = func.append_basic_block('for:end')
dbarbera's avatar
dbarbera committed
769

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

dbarbera's avatar
dbarbera committed
772
    loop_var = ctx.builder.alloca(ctx.i64, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
773
    ctx.scope.define(str(loop['var']), loop_var)