LlvmGenerator.py 64.1 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

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

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


587
def generate_write(args, ctx, newline=False):
dbarbera's avatar
dbarbera committed
588
    ''' Generate the IR for the write operator '''
589
590
591
    fmt = ""
    arg_values = []

dbarbera's avatar
dbarbera committed
592
593
    for arg in args:
        basic_ty = ctx.basic_type_of(arg.exprType)
594
        arg_val = expression(arg, ctx)
595

596
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
597
598
599
            fmt += '% d'
            arg_values.append(arg_val)

600
        elif basic_ty.kind == 'RealType':
601
602
603
            fmt += '% .14E'
            arg_values.append(arg_val)

604
        elif basic_ty.kind == 'BooleanType':
605
606
            fmt += '%s'

607
608
            true_str_ptr = ctx.string_ptr('TRUE')
            false_str_ptr = ctx.string_ptr('FALSE')
609
610
611
612
            str_ptr = ctx.builder.select(arg_val, true_str_ptr, false_str_ptr)

            arg_values.append(str_ptr)

dbarbera's avatar
dbarbera committed
613
        elif basic_ty.kind in ('StringType', 'StandardStringType'):
614
615
616
            fmt += '%s'
            arg_values.append(arg_val)

617
        elif basic_ty.kind == 'OctetStringType':
618
619
            fmt += '%.*s'

620
            if basic_ty.Min == basic_ty.Max:
621
                arr_ptr = ctx.builder.gep(arg_val, [ctx.zero, ctx.zero])
622
623
                count_val = core.Constant.int(ctx.i32, arr_ptr.type.pointee.count)
            else:
624
625
626
627
628
629
                count_val = ctx.builder.load(ctx.builder.gep(arg_val, [ctx.zero, ctx.zero]))
                arr_ptr = ctx.builder.gep(arg_val, [ctx.zero, ctx.one])

            arg_values.append(count_val)
            arg_values.append(arr_ptr)

630
        else:
631
632
633
634
635
636
637
            raise CompileError('Type "%s" not supported in write/writeln operators')

    if newline:
        fmt += '\n'

    arg_values.insert(0, ctx.string_ptr(fmt))
    ctx.builder.call(ctx.funcs['printf'], arg_values)
638
639


dbarbera's avatar
dbarbera committed
640
def generate_writeln(args, ctx):
dbarbera's avatar
dbarbera committed
641
    ''' Generate the IR for the writeln operator '''
642
    generate_write(args, ctx, True)
dbarbera's avatar
dbarbera committed
643
644


dbarbera's avatar
dbarbera committed
645
def generate_reset_timer(args, ctx):
dbarbera's avatar
dbarbera committed
646
    ''' Generate the IR for the reset timer operator '''
dbarbera's avatar
dbarbera committed
647
    timer_id = args[0]
648
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
649
    reset_func = ctx.funcs[reset_func_name.lower()]
dbarbera's avatar
dbarbera committed
650

dbarbera's avatar
dbarbera committed
651
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
652
653


dbarbera's avatar
dbarbera committed
654
def generate_set_timer(args, ctx):
dbarbera's avatar
dbarbera committed
655
    ''' Generate the IR for the set timer operator '''
dbarbera's avatar
dbarbera committed
656
    timer_expr, timer_id = args
657
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
658
    set_func = ctx.funcs[set_func_name.lower()]
dbarbera's avatar
dbarbera committed
659

dbarbera's avatar
dbarbera committed
660
    expr_val = expression(timer_expr, ctx)
dbarbera's avatar
dbarbera committed
661

dbarbera's avatar
dbarbera committed
662
663
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
664

dbarbera's avatar
dbarbera committed
665
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
666
667
668


@generate.register(ogAST.TaskAssign)
dbarbera's avatar
dbarbera committed
669
def _task_assign(task, ctx):
dbarbera's avatar
dbarbera committed
670
    ''' Generate the IR for a list of assignments '''
671
    for expr in task.elems:
dbarbera's avatar
dbarbera committed
672
        expression(expr, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
673
674
675


@generate.register(ogAST.TaskInformalText)
dbarbera's avatar
dbarbera committed
676
def _task_informal_text(task, ctx):
Maxime Perrotin's avatar
Maxime Perrotin committed
677
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
678
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
679
680
681


@generate.register(ogAST.TaskForLoop)
dbarbera's avatar
dbarbera committed
682
def _task_forloop(task, ctx):
dbarbera's avatar
dbarbera committed
683
    ''' Generate the IRfor a for loop '''
dbarbera's avatar
dbarbera committed
684
685
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
686
            generate_for_range(loop, ctx)
dbarbera's avatar
dbarbera committed
687
        else:
dbarbera's avatar
dbarbera committed
688
            generate_for_iterable(loop, ctx)
dbarbera's avatar
dbarbera committed
689
690


dbarbera's avatar
dbarbera committed
691
def generate_for_range(loop, ctx):
dbarbera's avatar
dbarbera committed
692
    ''' Generate the IR for a for x in range loop '''
dbarbera's avatar
dbarbera committed
693
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
694
695
696
    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
697
    end_block = func.append_basic_block('for:end')
dbarbera's avatar
dbarbera committed
698

699
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
700

dbarbera's avatar
dbarbera committed
701
    loop_var = ctx.builder.alloca(ctx.i64, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
702
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
703
704

    if loop['range']['start']:
dbarbera's avatar
dbarbera committed
705
        start_val = expression(loop['range']['start'], ctx)
dbarbera's avatar
dbarbera committed
706
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
707
    else:
dbarbera's avatar
dbarbera committed
708
        ctx.builder.store(core.Constant.int(ctx.i64, 0), loop_var)
dbarbera's avatar
dbarbera committed
709

dbarbera's avatar
dbarbera committed
710
    stop_val = expression(loop['range']['stop'], ctx)
dbarbera's avatar
dbarbera committed
711
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
712

dbarbera's avatar
dbarbera committed
713
714
715
716
    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
717

dbarbera's avatar
dbarbera committed
718
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
719
    generate(loop['transition'], ctx)
dbarbera's avatar
dbarbera committed
720
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
721

dbarbera's avatar
dbarbera committed
722
    ctx.builder.position_at_end(inc_block)
dbarbera's avatar
dbarbera committed
723
    step_val = core.Constant.int(ctx.i64, loop['range']['step'])
dbarbera's avatar
dbarbera committed
724
725
726
727
    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
728

dbarbera's avatar
dbarbera committed
729
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
730

731
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
732
733


dbarbera's avatar
dbarbera committed
734
def generate_for_iterable(loop, ctx):
dbarbera's avatar
dbarbera committed
735
    ''' Generate the IR for a for x in iterable loop '''
736
    seqof_asn1ty = ctx.basic_type_of(loop['list'].exprType)
737
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
738

dbarbera's avatar
dbarbera committed
739
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
740
741
742
743
744
745
746
747

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

750
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
751

dbarbera's avatar
dbarbera committed
752
753
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
754
    seqof_struct_ptr = expression(loop['list'], ctx)
755
756
757

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

dbarbera's avatar
dbarbera committed
762
    element_typ = array_ptr.type.pointee.element
763
764
765

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

dbarbera's avatar
dbarbera committed
770
771
    var_ptr = ctx.builder.alloca(element_typ, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
772

dbarbera's avatar
dbarbera committed
773
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
774
775

    # load block
dbarbera's avatar
dbarbera committed
776
777
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
778
    if element_typ.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
779
780
        elem_ptr = ctx.builder.gep(array_ptr, [ctx.zero, idx_var])
        generate_assign(var_ptr, elem_ptr, ctx)
781
    else:
dbarbera's avatar
dbarbera committed
782
783
        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
784
    ctx.builder.branch(body_block)
dbarbera's avatar
dbarbera committed
785
786

    # body block
dbarbera's avatar
dbarbera committed
787
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
788
    generate(loop['transition'], ctx)
dbarbera's avatar
dbarbera committed
789
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
790
791

    # cond block
dbarbera's avatar
dbarbera committed
792
793
794
795
796
    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
797

dbarbera's avatar
dbarbera committed
798
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
799

800
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
801

dbarbera's avatar
dbarbera committed