LlvmGenerator.py 63.3 KB
Newer Older
Maxime Perrotin's avatar
Maxime Perrotin committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    OpenGEODE - A tiny SDL Editor for TASTE

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

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

    Copyright (c) 2012-2013 European Space Agency

    Designed and implemented by Maxime Perrotin

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

import logging
21

Maxime Perrotin's avatar
Maxime Perrotin committed
22
from singledispatch import singledispatch
dbarbera's avatar
dbarbera committed
23
from llvm import core, ee
Maxime Perrotin's avatar
Maxime Perrotin committed
24
25

import ogAST
Maxime Perrotin's avatar
Maxime Perrotin committed
26
import Helper
Maxime Perrotin's avatar
Maxime Perrotin committed
27
28
29

LOG = logging.getLogger(__name__)

Maxime Perrotin's avatar
Maxime Perrotin committed
30
31
__all__ = ['generate']

Maxime Perrotin's avatar
Maxime Perrotin committed
32

dbarbera's avatar
dbarbera committed
33
class Context():
34
    def __init__(self, process):
dbarbera's avatar
dbarbera committed
35
36
        self.name = str(process.processName)
        self.module = core.Module.new(self.name)
dbarbera's avatar
dbarbera committed
37
        self.target_data = ee.TargetData.new(self.module.data_layout)
38
39
        self.dataview = process.dataview

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

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

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

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

dbarbera's avatar
dbarbera committed
72
        self.funcs['memcpy'] = core.Function.intrinsic(
73
74
            self.module,
            core.INTR_MEMCPY,
75
76
            [self.i8_ptr, self.i8_ptr, self.i64]
        )
Maxime Perrotin's avatar
Maxime Perrotin committed
77

78
79
80
81
82
83
        self.funcs['powi'] = core.Function.intrinsic(
            self.module,
            core.INTR_POWI,
            [self.double]
        )

dbarbera's avatar
dbarbera committed
84
85
86
87
88
89
        self.funcs['fabs'] = core.Function.intrinsic(
            self.module,
            core.INTR_FABS,
            [self.double]
        )

90
    def open_scope(self):
dbarbera's avatar
dbarbera committed
91
        ''' Open a scope '''
dbarbera's avatar
dbarbera committed
92
        self.scope = Scope(self, self.scope)
93
94

    def close_scope(self):
dbarbera's avatar
dbarbera committed
95
        ''' Close the current scope '''
96
97
        self.scope = self.scope.parent

98
99
    def basic_type_of(self, asn1ty):
        ''' Return the ASN.1 basic type of a type '''
dbarbera's avatar
dbarbera committed
100
101
102
103
104
105
106
107
108
        if asn1ty.kind != 'ReferenceType':
            return asn1ty

        asn1ty_name = asn1ty.ReferencedTypeName.lower()

        # return the basic type if its cached
        if asn1ty_name in self.basic_types:
            return self.basic_types[asn1ty_name]

109
110
111
112
113
114
        basic_type = asn1ty
        while basic_type.kind == 'ReferenceType':
            for typename in self.dataview.viewkeys():
                if typename.lower() == basic_type.ReferencedTypeName.lower():
                    basic_type = self.dataview[typename].type
                    break
dbarbera's avatar
dbarbera committed
115
116
117
118

        # cache the basic type
        self.basic_types[asn1ty_name] = basic_type

119
120
        return basic_type

121
122
123
124
125
126
127
128
129
130
    def type_of(self, asn1ty):
        ''' 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]

131
        basic_asn1ty = self.basic_type_of(asn1ty)
132

dbarbera's avatar
dbarbera committed
133
        if basic_asn1ty.kind == 'IntegerType':
dbarbera's avatar
dbarbera committed
134
            llty = self.i64
dbarbera's avatar
dbarbera committed
135
        elif basic_asn1ty.kind == 'Integer32Type':
dbarbera's avatar
dbarbera committed
136
            llty = self.i32
137
        elif basic_asn1ty.kind == 'BooleanType':
dbarbera's avatar
dbarbera committed
138
            llty = self.i1
139
        elif basic_asn1ty.kind == 'RealType':
dbarbera's avatar
dbarbera committed
140
            llty = self.double
141
142
143
144
145
        elif basic_asn1ty.kind == 'SequenceOfType':
            llty = self._type_of_sequenceof(name, basic_asn1ty)
        elif basic_asn1ty.kind == 'SequenceType':
            llty = self._type_of_sequence(name, basic_asn1ty)
        elif basic_asn1ty.kind == 'EnumeratedType':
dbarbera's avatar
dbarbera committed
146
            llty = self.i32
147
148
149
150
        elif basic_asn1ty.kind == 'ChoiceType':
            llty = self._type_of_choice(name, basic_asn1ty)
        elif basic_asn1ty.kind == 'OctetStringType':
            llty = self._type_of_octetstring(name, basic_asn1ty)
dbarbera's avatar
dbarbera committed
151
        elif basic_asn1ty.kind in ('StringType', 'StandardStringType'):
dbarbera's avatar
dbarbera committed
152
            llty = self.i8_ptr
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
        else:
            raise NotImplementedError

        if name:
            self.lltypes[name] = llty

        return llty

    def _type_of_sequenceof(self, name, sequenceof_ty):
        ''' Return the LL type of a SequenceOf ASN.1 type '''
        min_size = int(sequenceof_ty.Min)
        max_size = int(sequenceof_ty.Max)
        is_variable_size = min_size != max_size

        elem_ty = self.type_of(sequenceof_ty.type)
        array_ty = core.Type.array(elem_ty, max_size)

        if is_variable_size:
dbarbera's avatar
dbarbera committed
171
            struct = self.decl_struct(['nCount', 'arr'], [self.i32, array_ty], name)
172
        else:
173
            struct = self.decl_struct(['arr'], [array_ty], name)
174

175
        struct_ptr = core.Type.pointer(struct.ty)
176
        self.decl_func("asn1Scc%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])
177

178
179
180
181
182
183
        return struct.ty

    def _type_of_sequence(self, name, sequence_ty):
        ''' Return the LL type of a Sequence ASN.1 type '''
        field_names = []
        field_types = []
184

185
186
187
        for field_name in Helper.sorted_fields(sequence_ty):
            field_names.append(field_name.replace('-', '_'))
            field_types.append(self.type_of(sequence_ty.Children[field_name].type))
188

189
        struct = self.decl_struct(field_names, field_types, name)
190

191
        struct_ptr = core.Type.pointer(struct.ty)
192
        self.decl_func("asn1Scc%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])
193

194
195
196
197
198
199
        return struct.ty

    def _type_of_choice(self, name, choice_ty):
        ''' Return the equivalent LL type of a Choice ASN.1 type '''
        field_names = []
        field_types = []
dbarbera's avatar
dbarbera committed
200
201
202

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

dbarbera's avatar
dbarbera committed
205
            field_names.append(field_name.replace('-', '_'))
dbarbera's avatar
dbarbera committed
206
            field_types.append(self.type_of(choice_ty.Children[field_name].type))
207

208
        union = self.decl_union(field_names, field_types, name)
209
210

        union_ptr = core.Type.pointer(union.ty)
211
        self.decl_func("asn1Scc%s_Equal" % name, self.i1, [union_ptr, union_ptr])
212

213
214
215
        return union.ty

    def _type_of_octetstring(self, name, octetstring_ty):
dbarbera's avatar
dbarbera committed
216
        ''' Return the equivalent LL type of a OctetString ASN.1 type '''
217
        min_size = int(octetstring_ty.Min)
218
        max_size = int(octetstring_ty.Max)
219
220
        is_variable_size = min_size != max_size

dbarbera's avatar
dbarbera committed
221
        array_ty = core.Type.array(self.i8, max_size)
222
223

        if is_variable_size:
dbarbera's avatar
dbarbera committed
224
            struct = self.decl_struct(['nCount', 'arr'], [self.i32, array_ty], name)
225
226
        else:
            struct = self.decl_struct(['arr'], [array_ty], name)
227
228

        struct_ptr = core.Type.pointer(struct.ty)
229
        self.decl_func("asn1Scc%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])
230

231
232
        return struct.ty

233
234
235
236
237
238
239
240
241
242
243
244
    def string_ptr(self, str):
        ''' Returns a pointer to a global string with the given value '''
        if str in self.strings:
            return self.strings[str].gep([self.zero, self.zero])

        str_val = core.Constant.stringz(str)
        var_name = '.str%s' % len(self.strings)
        var_ptr = self.module.add_global_variable(str_val.type, var_name)
        var_ptr.initializer = str_val
        self.strings[str] = var_ptr
        return var_ptr.gep([self.zero, self.zero])

245
246
247
248
249
250
251
252
253
254
255
    def decl_func(self, name, return_ty, param_tys, extern=False):
        ''' Declare a function '''
        func_ty = core.Type.function(return_ty, param_tys)
        func_name = ("%s_RI_%s" % (self.name, name)) if extern else name
        func = core.Function.new(self.module, func_ty, func_name)
        self.funcs[name.lower()] = func
        return func

    def decl_struct(self, field_names, field_types, name=None):
        ''' Declare a struct '''
        name = name if name else "struct.%s" % len(self.structs)
dbarbera's avatar
dbarbera committed
256
        name = name.replace('-', '_')
257
258
259
260
        struct = StructType(name, field_names, field_types)
        self.structs[name] = struct
        return struct

dbarbera's avatar
dbarbera committed
261
262
263
264
    def resolve_struct(self, name):
        ''' Return the struct associated to a name '''
        return self.structs[name.replace('-', '_')]

265
266
    def decl_union(self, field_names, field_types, name=None):
        name = name if name else "union.%s" % len(self.structs)
dbarbera's avatar
dbarbera committed
267
        name = name.replace('-', '_')
dbarbera's avatar
dbarbera committed
268
        union = UnionType(name, field_names, field_types, self)
269
270
271
        self.unions[name] = union
        return union

dbarbera's avatar
dbarbera committed
272
273
274
275
    def resolve_union(self, name):
        ''' Return the union associated to a name '''
        return self.unions[name.replace('-', '_')]

dbarbera's avatar
dbarbera committed
276

277
278
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
279
        self.name = name
280
281
282
283
284
        self.field_names = field_names
        self.ty = core.Type.struct(field_types, self.name)

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


dbarbera's avatar
dbarbera committed
287
class UnionType():
dbarbera's avatar
dbarbera committed
288
    def __init__(self, name, field_names, field_types, ctx):
dbarbera's avatar
dbarbera committed
289
290
291
292
293
        self.name = name
        self.field_names = field_names
        self.field_types = field_types
        # 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
dbarbera's avatar
dbarbera committed
294
295
        self.size = max([ctx.target_data.size(ty) for ty in field_types])
        self.ty = core.Type.struct([ctx.i32, core.Type.array(ctx.i8, self.size)], name)
dbarbera's avatar
dbarbera committed
296
297
298
299
300
301

    def kind(self, name):
        idx = self.field_names.index(name)
        return (idx, self.field_types[idx])


302
class Scope:
dbarbera's avatar
dbarbera committed
303
304
    def __init__(self, ctx, parent=None):
        self.ctx = ctx
305
        self.vars = {}
dbarbera's avatar
dbarbera committed
306
        self.labels = {}
307
308
309
310
311
312
313
314
315
316
317
318
        self.parent = parent

    def define(self, name, var):
        self.vars[name.lower()] = var

    def resolve(self, name):
        var = self.vars.get(name.lower())
        if var:
            return var
        if self.parent:
            return self.parent.resolve(name)
        else:
dbarbera's avatar
dbarbera committed
319
            raise NameError("name '%s' is not defined" % name)
320

dbarbera's avatar
dbarbera committed
321
322
323
324
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
325
            func = self.ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
326
            label_block = func.append_basic_block('label:%s' % name)
dbarbera's avatar
dbarbera committed
327
328
329
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
330

331
332
333
334
class CompileError(Exception):
    pass


Maxime Perrotin's avatar
Maxime Perrotin committed
335
@singledispatch
dbarbera's avatar
dbarbera committed
336
def generate(ast, ctx=None):
dbarbera's avatar
dbarbera committed
337
    ''' Generate the IR for an AST node '''
338
    raise CompileError('Unsupported AST construct "%s"' % ast.__class__.__name__)
Maxime Perrotin's avatar
Maxime Perrotin committed
339

dbarbera's avatar
dbarbera committed
340

Maxime Perrotin's avatar
Maxime Perrotin committed
341
342
# Processing of the AST
@generate.register(ogAST.Process)
dbarbera's avatar
dbarbera committed
343
def _process(process, ctx=None):
dbarbera's avatar
dbarbera committed
344
    ''' Generate the IR for a process '''
dbarbera's avatar
dbarbera committed
345
346
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
347

dbarbera's avatar
dbarbera committed
348
    ctx = Context(process)
349

dbarbera's avatar
dbarbera committed
350
351
352
353
354
355
    # In case model has nested states, flatten everything
    Helper.flatten(process)

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

357
358
    # Initialize states
    for name, val in process.mapping.viewitems():
359
        if not name.endswith('START'):
dbarbera's avatar
dbarbera committed
360
            cons_val = core.Constant.int(ctx.i32, len(ctx.states))
dbarbera's avatar
dbarbera committed
361
            ctx.states[name.lower()] = cons_val
362
        elif name != 'START':
dbarbera's avatar
dbarbera committed
363
            cons_val = core.Constant.int(ctx.i32, val)
dbarbera's avatar
dbarbera committed
364
            ctx.states[name.lower()] = cons_val
365

366
    # Generate state var
367
    state_cons = ctx.module.add_global_variable(ctx.i32, '.state')
dbarbera's avatar
dbarbera committed
368
    state_cons.initializer = core.Constant.int(ctx.i32, -1)
369
    ctx.scope.define('.state', state_cons)
370

371
372
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
373
        var_ty = ctx.type_of(ty)
dbarbera's avatar
dbarbera committed
374
        global_var = ctx.module.add_global_variable(var_ty, str(name))
375
        global_var.initializer = core.Constant.null(var_ty)
dbarbera's avatar
dbarbera committed
376
        ctx.scope.define(str(name).lower(), global_var)
377

dbarbera's avatar
dbarbera committed
378
    # Declare set/reset timer functions
dbarbera's avatar
dbarbera committed
379
380
    for timer in process.timers:
        # TODO: Should be uint?
dbarbera's avatar
dbarbera committed
381
        ctx.decl_func("set_%s" % str(timer), ctx.void, [ctx.i64_ptr], True)
382
        ctx.decl_func("reset_%s" % str(timer), ctx.void, [], True)
dbarbera's avatar
dbarbera committed
383

384
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
385
    for signal in process.output_signals:
386
        if 'type' in signal:
387
            param_tys = [core.Type.pointer(ctx.type_of(signal['type']))]
388
389
        else:
            param_tys = []
390
        ctx.decl_func(str(signal['name']), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
391

392
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
393
    for proc in [proc for proc in process.procedures if proc.external]:
394
        param_tys = [core.Type.pointer(ctx.type_of(p['type'])) for p in proc.fpar]
395
        ctx.decl_func(str(proc.inputString), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
396

397
398
    # Generate internal procedures
    for proc in process.content.inner_procedures:
dbarbera's avatar
dbarbera committed
399
        generate(proc, ctx)
400

401
    # Generate process functions
dbarbera's avatar
dbarbera committed
402
403
    generate_runtr_func(process, ctx)
    generate_startup_func(process, ctx)
404

405
406
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
407
        generate_input_signal(signal, mapping[signal['name']], ctx)
408

dbarbera's avatar
dbarbera committed
409
410
    # Generate timer signal
    for timer in process.timers:
dbarbera's avatar
dbarbera committed
411
        generate_input_signal({'name': timer.lower()}, mapping[timer], ctx)
dbarbera's avatar
dbarbera committed
412

dbarbera's avatar
dbarbera committed
413
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
414

dbarbera's avatar
dbarbera committed
415
416
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
417
418


dbarbera's avatar
dbarbera committed
419
def generate_runtr_func(process, ctx):
dbarbera's avatar
dbarbera committed
420
    ''' Generate the IR for the run_transition function '''
421
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
422

423
    ctx.open_scope()
424

dbarbera's avatar
dbarbera committed
425
426
427
428
    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')
429

dbarbera's avatar
dbarbera committed
430
    ctx.builder = core.Builder.new(entry_block)
431
432

    # entry
dbarbera's avatar
dbarbera committed
433
434
435
436
    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)
437
438

    # cond
dbarbera's avatar
dbarbera committed
439
440
441
442
443
    ctx.builder.position_at_end(cond_block)
    no_tr_cons = core.Constant.int(ctx.i32, -1)
    id_val = ctx.builder.load(id_ptr)
    cond_val = ctx.builder.icmp(core.ICMP_NE, id_val, no_tr_cons, 'cond')
    ctx.builder.cbranch(cond_val, body_block, exit_block)
444
445

    # body
dbarbera's avatar
dbarbera committed
446
    ctx.builder.position_at_end(body_block)
447
    switch = ctx.builder.switch(id_val, exit_block)
448
449
450

    # transitions
    for idx, tr in enumerate(process.transitions):
dbarbera's avatar
dbarbera committed
451
        tr_block = func.append_basic_block('runtr:tr%d' % idx)
dbarbera's avatar
dbarbera committed
452
        const = core.Constant.int(ctx.i32, idx)
453
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
454
        ctx.builder.position_at_end(tr_block)
dbarbera's avatar
dbarbera committed
455
        generate(tr, ctx)
dbarbera's avatar
dbarbera committed
456
457
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
458
459

    # exit
dbarbera's avatar
dbarbera committed
460
461
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
462

dbarbera's avatar
dbarbera committed
463
464
    Helper.inner_labels_to_floating(process)
    for label in process.content.floating_labels:
dbarbera's avatar
dbarbera committed
465
        generate(label, ctx)
dbarbera's avatar
dbarbera committed
466
467

    # TODO: Use defined cond_block instead?
dbarbera's avatar
dbarbera committed
468
469
470
    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
471

472
    ctx.close_scope()
473

474
475
476
477
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
478
def generate_startup_func(process, ctx):
dbarbera's avatar
dbarbera committed
479
    ''' Generate the IR for the startup function '''
480
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
481

482
    ctx.open_scope()
483

dbarbera's avatar
dbarbera committed
484
    entry_block = func.append_basic_block('startup:entry')
dbarbera's avatar
dbarbera committed
485
    ctx.builder = core.Builder.new(entry_block)
486

487
488
489
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
490
            global_var = ctx.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
491
            generate_assign(global_var, expression(expr, ctx), ctx)
492

dbarbera's avatar
dbarbera committed
493
494
    ctx.builder.call(ctx.funcs['run_transition'], [core.Constant.int(ctx.i32, 0)])
    ctx.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
495

496
    ctx.close_scope()
497

498
499
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
500
501


dbarbera's avatar
dbarbera committed
502
def generate_input_signal(signal, inputs, ctx):
dbarbera's avatar
dbarbera committed
503
    ''' Generate the IR for an input signal '''
dbarbera's avatar
dbarbera committed
504
    func_name = ctx.name + "_" + str(signal['name'])
505
506
    param_tys = []
    if 'type' in signal:
507
        param_tys.append(core.Type.pointer(ctx.type_of(signal['type'])))
508

509
    func = ctx.decl_func(func_name, ctx.void, param_tys)
510

511
    ctx.open_scope()
512

dbarbera's avatar
dbarbera committed
513
514
    entry_block = func.append_basic_block('input:entry')
    exit_block = func.append_basic_block('input:exit')
dbarbera's avatar
dbarbera committed
515
    ctx.builder = core.Builder.new(entry_block)
516

517
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('.state'))
dbarbera's avatar
dbarbera committed
518
    switch = ctx.builder.switch(g_state_val, exit_block)
519

dbarbera's avatar
dbarbera committed
520
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
521
        if state_name.endswith('start'):
dbarbera's avatar
dbarbera committed
522
            continue
dbarbera's avatar
dbarbera committed
523
        state_block = func.append_basic_block('input:state_%s' % str(state_name))
524
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
525
        ctx.builder.position_at_end(state_block)
526
527
528

        # TODO: Nested states

529
530
531
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
532
                var_ptr = ctx.scope.resolve(str(var_name))
dbarbera's avatar
dbarbera committed
533
                if is_struct_ptr(var_ptr) or is_array_ptr(var_ptr):
dbarbera's avatar
dbarbera committed
534
                    generate_assign(var_ptr, func.args[0], ctx)
535
                else:
dbarbera's avatar
dbarbera committed
536
                    generate_assign(var_ptr, ctx.builder.load(func.args[0]), ctx)
537
            if input.transition:
dbarbera's avatar
dbarbera committed
538
539
                id_val = core.Constant.int(ctx.i32, input.transition_id)
                ctx.builder.call(ctx.funcs['run_transition'], [id_val])
540

dbarbera's avatar
dbarbera committed
541
        ctx.builder.ret_void()
542

dbarbera's avatar
dbarbera committed
543
544
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
545

546
    ctx.close_scope()
547

548
549
550
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
551
552
@generate.register(ogAST.Output)
@generate.register(ogAST.ProcedureCall)
dbarbera's avatar
dbarbera committed
553
def _call_external_function(output, ctx):
dbarbera's avatar
dbarbera committed
554
    ''' Generate the IR for an output or procedure call '''
dbarbera's avatar
dbarbera committed
555
556
557
    for out in output.output:
        name = out['outputName'].lower()

558
        if name == 'write':
dbarbera's avatar
dbarbera committed
559
            generate_write(out['params'], ctx)
dbarbera's avatar
dbarbera committed
560
            continue
561
        elif name == 'writeln':
dbarbera's avatar
dbarbera committed
562
            generate_writeln(out['params'], ctx)
563
            continue
dbarbera's avatar
dbarbera committed
564
        elif name == 'reset_timer':
dbarbera's avatar
dbarbera committed
565
            generate_reset_timer(out['params'], ctx)
dbarbera's avatar
dbarbera committed
566
567
            continue
        elif name == 'set_timer':
dbarbera's avatar
dbarbera committed
568
            generate_set_timer(out['params'], ctx)
dbarbera's avatar
dbarbera committed
569
570
            continue

dbarbera's avatar
dbarbera committed
571
        func = ctx.funcs[str(name).lower()]
572
573
574

        params = []
        for p in out.get('params', []):
dbarbera's avatar
dbarbera committed
575
            p_val = expression(p, ctx)
576
577
            # Pass by reference
            if p_val.type.kind != core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
578
579
                p_var = ctx.builder.alloca(p_val.type, None)
                ctx.builder.store(p_val, p_var)
580
581
582
583
                params.append(p_var)
            else:
                params.append(p_val)

dbarbera's avatar
dbarbera committed
584
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
585
586


dbarbera's avatar
dbarbera committed
587
def generate_write(params, ctx):
dbarbera's avatar
dbarbera committed
588
    ''' Generate the IR for the write operator '''
589
    for param in params:
590
        basic_ty = ctx.basic_type_of(param.exprType)
dbarbera's avatar
dbarbera committed
591
        expr_val = expression(param, ctx)
592

593
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
594
595
            fmt_str_ptr = ctx.string_ptr('% d')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
596
        elif basic_ty.kind == 'RealType':
597
598
            fmt_str_ptr = ctx.string_ptr('% .14E')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
599
        elif basic_ty.kind == 'BooleanType':
600
601
            true_str_ptr = ctx.string_ptr('TRUE')
            false_str_ptr = ctx.string_ptr('FALSE')
dbarbera's avatar
dbarbera committed
602
603
            str_ptr = ctx.builder.select(expr_val, true_str_ptr, false_str_ptr)
            ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
604
        elif basic_ty.kind in ('StringType', 'StandardStringType'):
605
606
607
            fmt_str_ptr = ctx.string_ptr('%s')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
        elif basic_ty.kind == 'OctetStringType':
608
            fmt_str_ptr = ctx.string_ptr('%.*s')
609
610
611
612
613
614
            if basic_ty.Min == basic_ty.Max:
                arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.zero])
                count_val = core.Constant.int(ctx.i32, arr_ptr.type.pointee.count)
            else:
                count_val = ctx.builder.load(ctx.builder.gep(expr_val, [ctx.zero, ctx.zero]))
                arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.one])
615
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, count_val, arr_ptr])
616
617
618
619
        else:
            raise NotImplementedError


dbarbera's avatar
dbarbera committed
620
def generate_writeln(params, ctx):
dbarbera's avatar
dbarbera committed
621
    ''' Generate the IR for the writeln operator '''
dbarbera's avatar
dbarbera committed
622
    generate_write(params, ctx)
623

624
    str_ptr = ctx.string_ptr('\n')
dbarbera's avatar
dbarbera committed
625
    ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
626
627


dbarbera's avatar
dbarbera committed
628
def generate_reset_timer(params, ctx):
dbarbera's avatar
dbarbera committed
629
    ''' Generate the IR for the reset timer operator '''
dbarbera's avatar
dbarbera committed
630
    timer_id = params[0]
631
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
632
    reset_func = ctx.funcs[reset_func_name.lower()]
dbarbera's avatar
dbarbera committed
633

dbarbera's avatar
dbarbera committed
634
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
635
636


dbarbera's avatar
dbarbera committed
637
def generate_set_timer(params, ctx):
dbarbera's avatar
dbarbera committed
638
    ''' Generate the IR for the set timer operator '''
dbarbera's avatar
dbarbera committed
639
    timer_expr, timer_id = params
640
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
641
    set_func = ctx.funcs[set_func_name.lower()]
dbarbera's avatar
dbarbera committed
642

dbarbera's avatar
dbarbera committed
643
    expr_val = expression(timer_expr, ctx)
dbarbera's avatar
dbarbera committed
644

dbarbera's avatar
dbarbera committed
645
646
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
647

dbarbera's avatar
dbarbera committed
648
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
649
650
651


@generate.register(ogAST.TaskAssign)
dbarbera's avatar
dbarbera committed
652
def _task_assign(task, ctx):
dbarbera's avatar
dbarbera committed
653
    ''' Generate the IR for a list of assignments '''
654
    for expr in task.elems:
dbarbera's avatar
dbarbera committed
655
        expression(expr, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
656
657
658


@generate.register(ogAST.TaskInformalText)
dbarbera's avatar
dbarbera committed
659
def _task_informal_text(task, ctx):
Maxime Perrotin's avatar
Maxime Perrotin committed
660
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
661
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
662
663
664


@generate.register(ogAST.TaskForLoop)
dbarbera's avatar
dbarbera committed
665
def _task_forloop(task, ctx):
dbarbera's avatar
dbarbera committed
666
    ''' Generate the IRfor a for loop '''
dbarbera's avatar
dbarbera committed
667
668
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
669
            generate_for_range(loop, ctx)
dbarbera's avatar
dbarbera committed
670
        else:
dbarbera's avatar
dbarbera committed
671
            generate_for_iterable(loop, ctx)
dbarbera's avatar
dbarbera committed
672
673


dbarbera's avatar
dbarbera committed
674
def generate_for_range(loop, ctx):
dbarbera's avatar
dbarbera committed
675
    ''' Generate the IR for a for x in range loop '''
dbarbera's avatar
dbarbera committed
676
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
677
678
679
    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
680
    end_block = func.append_basic_block('for:end')
dbarbera's avatar
dbarbera committed
681

682
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
683

dbarbera's avatar
dbarbera committed
684
    loop_var = ctx.builder.alloca(ctx.i64, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
685
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
686
687

    if loop['range']['start']:
dbarbera's avatar
dbarbera committed
688
        start_val = expression(loop['range']['start'], ctx)
dbarbera's avatar
dbarbera committed
689
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
690
    else:
dbarbera's avatar
dbarbera committed
691
        ctx.builder.store(core.Constant.int(ctx.i64, 0), loop_var)
dbarbera's avatar
dbarbera committed
692

dbarbera's avatar
dbarbera committed
693
    stop_val = expression(loop['range']['stop'], ctx)
dbarbera's avatar
dbarbera committed
694
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
695

dbarbera's avatar
dbarbera committed
696
697
698
699
    ctx.builder.position_at_end(cond_block)
    loop_val = ctx.builder.load(loop_var)
    cond_val = ctx.builder.icmp(core.ICMP_SLT, loop_val, stop_val)
    ctx.builder.cbranch(cond_val, body_block, end_block)
dbarbera's avatar
dbarbera committed
700

dbarbera's avatar
dbarbera committed
701
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
702
    generate(loop['transition'], ctx)
dbarbera's avatar
dbarbera committed
703
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
704

dbarbera's avatar
dbarbera committed
705
    ctx.builder.position_at_end(inc_block)
dbarbera's avatar
dbarbera committed
706
    step_val = core.Constant.int(ctx.i64, loop['range']['step'])
dbarbera's avatar
dbarbera committed
707
708
709
710
    loop_val = ctx.builder.load(loop_var)
    temp_val = ctx.builder.add(loop_val, step_val)
    ctx.builder.store(temp_val, loop_var)
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
711

dbarbera's avatar
dbarbera committed
712
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
713

714
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
715
716


dbarbera's avatar
dbarbera committed
717
def generate_for_iterable(loop, ctx):
dbarbera's avatar
dbarbera committed
718
    ''' Generate the IR for a for x in iterable loop '''
719
    seqof_asn1ty = ctx.basic_type_of(loop['list'].exprType)
720
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
721

dbarbera's avatar
dbarbera committed
722
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
723
724
725
726
727
728
729
730

    # block for loading the value from the secuence
    # at the current index, incrementing the index afterwards
    load_block = func.append_basic_block('forin:load')
    # block for the body of the loop
    body_block = func.append_basic_block('forin:body')
    # block for checking if should loop again or terminate
    cond_block = func.append_basic_block('forin:cond')
dbarbera's avatar
dbarbera committed
731
    end_block = func.append_basic_block('forin:end')
dbarbera's avatar
dbarbera committed
732

733
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
734

dbarbera's avatar
dbarbera committed
735
736
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
737
    seqof_struct_ptr = expression(loop['list'], ctx)
738
739
740

    if is_variable_size:
        # In variable size SequenceOfs the array values are in the second field
dbarbera's avatar
dbarbera committed
741
        array_ptr = ctx.builder.gep(seqof_struct_ptr, [ctx.zero, ctx.one])
742
    else:
dbarbera's avatar
dbarbera committed
743
        array_ptr = ctx.builder.gep(seqof_struct_ptr, [ctx.zero, ctx.zero])
744

dbarbera's avatar
dbarbera committed
745
    element_typ = array_ptr.type.pointee.element
746
747
748

    if is_variable_size:
        # load the current number of elements that is on the first field
dbarbera's avatar
dbarbera committed
749
        end_idx = ctx.builder.load(ctx.builder.gep(seqof_struct_ptr, [ctx.zero, ctx.zero]))
750
    else:
dbarbera's avatar
dbarbera committed
751
        end_idx = core.Constant.int(ctx.i32, array_ptr.type.pointee.count)
dbarbera's avatar
dbarbera committed
752

dbarbera's avatar
dbarbera committed
753
754
    var_ptr = ctx.builder.alloca(element_typ, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
755

dbarbera's avatar
dbarbera committed
756
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
757
758

    # load block
dbarbera's avatar
dbarbera committed
759
760
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
761
    if element_typ.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
762
763
        elem_ptr = ctx.builder.gep(array_ptr, [ctx.zero, idx_var])
        generate_assign(var_ptr, elem_ptr, ctx)
764
    else:
dbarbera's avatar
dbarbera committed
765
766
        elem_val = ctx.builder.load(ctx.builder.gep(array_ptr, [ctx.zero, idx_var]))
        generate_assign(var_ptr, elem_val, ctx)
dbarbera's avatar
dbarbera committed
767
    ctx.builder.branch(body_block)
dbarbera's avatar
dbarbera committed
768
769

    # body block
dbarbera's avatar
dbarbera committed
770
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
771
    generate(loop['transition'], ctx)
dbarbera's avatar
dbarbera committed
772
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
773
774

    # cond block
dbarbera's avatar
dbarbera committed
775
776
777
778
779
    ctx.builder.position_at_end(cond_block)
    tmp_val = ctx.builder.add(idx_var, ctx.one)
    ctx.builder.store(tmp_val, idx_ptr)
    cond_val = ctx.builder.icmp(core.ICMP_SLT, tmp_val, end_idx)
    ctx.builder.cbranch(cond_val, load_block, end_block)
dbarbera's avatar
dbarbera committed
780

dbarbera's avatar
dbarbera committed
781
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
782

783
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
784

dbarbera's avatar
dbarbera committed
785

dbarbera's avatar
dbarbera committed
786
@singledispatch
dbarbera's avatar
dbarbera committed
787
def reference(prim, ctx):
dbarbera's avatar
dbarbera committed
788
    ''' Generate the IR for a reference '''
789
    raise CompileError('Unsupported reference "%s"' % prim.__class__.__name__)
dbarbera's avatar
dbarbera committed
790
791
792


@reference.register(ogAST.PrimVariable)