LlvmGenerator.py 65.7 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
        self.dataview = process.dataview
39
        self.procedures = process.procedures
40

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

        # 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
65
66
67
68
        # Initialize common constants
        self.zero = core.Constant.int(self.i32, 0)
        self.one = core.Constant.int(self.i32, 1)

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

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

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

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

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

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

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

        asn1ty_name = asn1ty.ReferencedTypeName.lower()

        # return the basic type if its cached
107
108
        if asn1ty_name in self.basic_asn1types:
            return self.basic_asn1types[asn1ty_name]
dbarbera's avatar
dbarbera committed
109

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

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

120
        return basic_asn1ty
121

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

132
        basic_asn1ty = self.basic_asn1type_of(asn1ty)
133

dbarbera's avatar
dbarbera committed
134
        if basic_asn1ty.kind == 'IntegerType':
dbarbera's avatar
dbarbera committed
135
            llty = self.i64
dbarbera's avatar
dbarbera committed
136
        elif basic_asn1ty.kind == 'Integer32Type':
dbarbera's avatar
dbarbera committed
137
            llty = self.i32
138
        elif basic_asn1ty.kind == 'BooleanType':
dbarbera's avatar
dbarbera committed
139
            llty = self.i1
140
        elif basic_asn1ty.kind == 'RealType':
dbarbera's avatar
dbarbera committed
141
            llty = self.double
142
        elif basic_asn1ty.kind == 'SequenceOfType':
143
            llty = self._lltype_of_sequenceof(name, basic_asn1ty)
144
        elif basic_asn1ty.kind == 'SequenceType':
145
            llty = self._lltype_of_sequence(name, basic_asn1ty)
146
        elif basic_asn1ty.kind == 'EnumeratedType':
dbarbera's avatar
dbarbera committed
147
            llty = self.i32
148
        elif basic_asn1ty.kind == 'ChoiceType':
149
            llty = self._lltype_of_choice(name, basic_asn1ty)
150
        elif basic_asn1ty.kind == 'OctetStringType':
151
            llty = self._lltype_of_octetstring(name, basic_asn1ty)
dbarbera's avatar
dbarbera committed
152
        elif basic_asn1ty.kind in ('StringType', 'StandardStringType'):
dbarbera's avatar
dbarbera committed
153
            llty = self.i8_ptr
154
        else:
155
            raise CompileError('Unknown basic ASN.1 type "%s"' % basic_asn1ty.kind)
156
157
158
159
160
161

        if name:
            self.lltypes[name] = llty

        return llty

162
    def _lltype_of_sequenceof(self, name, asn1ty):
163
        ''' Return the LL type of a SequenceOf ASN.1 type '''
164
165
        min_size = int(asn1ty.Min)
        max_size = int(asn1ty.Max)
166
167
        is_variable_size = min_size != max_size

168
169
        elem_llty = self.lltype_of(asn1ty.type)
        array_llty = core.Type.array(elem_llty, max_size)
170
171

        if is_variable_size:
172
            struct = self.decl_struct(['nCount', 'arr'], [self.i32, array_llty], name)
173
        else:
174
            struct = self.decl_struct(['arr'], [array_llty], name)
175

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

179
        return struct.llty
180

181
    def _lltype_of_sequence(self, name, asn1ty):
182
183
        ''' Return the LL type of a Sequence ASN.1 type '''
        field_names = []
184
        field_lltys = []
185

186
        for field_name in Helper.sorted_fields(asn1ty):
187
            field_names.append(field_name.replace('-', '_'))
188
            field_lltys.append(self.lltype_of(asn1ty.Children[field_name].type))
189

190
        struct = self.decl_struct(field_names, field_lltys, name)
191

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

195
        return struct.llty
196

197
    def _lltype_of_choice(self, name, asn1ty):
198
199
        ''' Return the equivalent LL type of a Choice ASN.1 type '''
        field_names = []
200
        field_lltys = []
dbarbera's avatar
dbarbera committed
201

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

dbarbera's avatar
dbarbera committed
206
            field_names.append(field_name.replace('-', '_'))
207
            field_lltys.append(self.lltype_of(asn1ty.Children[field_name].type))
208

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

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

214
        return union.llty
215

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

222
        array_llty = core.Type.array(self.i8, max_size)
223
224

        if is_variable_size:
225
            struct = self.decl_struct(['nCount', 'arr'], [self.i32, array_llty], name)
226
        else:
227
            struct = self.decl_struct(['arr'], [array_llty], name)
228

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

232
        return struct.llty
233

234
235
236
237
238
239
240
241
242
243
244
245
    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])

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

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

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

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

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

dbarbera's avatar
dbarbera committed
277

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

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


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

    def kind(self, name):
        idx = self.field_names.index(name)
300
        return (idx, self.field_lltys[idx])
dbarbera's avatar
dbarbera committed
301
302


303
class Scope:
dbarbera's avatar
dbarbera committed
304
305
    def __init__(self, ctx, parent=None):
        self.ctx = ctx
306
        self.vars = {}
dbarbera's avatar
dbarbera committed
307
        self.labels = {}
308
309
310
311
312
313
314
315
316
317
318
319
        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
320
            raise NameError("name '%s' is not defined" % name)
321

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

dbarbera's avatar
dbarbera committed
331

332
333
334
335
class CompileError(Exception):
    pass


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

dbarbera's avatar
dbarbera committed
341

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

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

dbarbera's avatar
dbarbera committed
351
352
353
354
355
356
    # 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
357

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

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

372
    # Generare process-level vars
373
374
375
376
    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))
        global_var.initializer = core.Constant.null(var_llty)
dbarbera's avatar
dbarbera committed
377
        ctx.scope.define(str(name).lower(), global_var)
378

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

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

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

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

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

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

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

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

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


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

424
    ctx.open_scope()
425

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

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

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

    # cond
dbarbera's avatar
dbarbera committed
440
441
442
443
444
    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)
445
446

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

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

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

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

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))
491
            sdl_assign(global_var, expression(expr, ctx), ctx)
492

dbarbera's avatar
dbarbera committed
493
    sdl_call('run_transition', [core.Constant.int(ctx.i32, 0)], ctx)
dbarbera's avatar
dbarbera committed
494
    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
    param_lltys = []
506
    if 'type' in signal:
507
        param_lltys.append(core.Type.pointer(ctx.lltype_of(signal['type'])))
508

509
    func = ctx.decl_func(func_name, ctx.void, param_lltys)
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):
534
                    sdl_assign(var_ptr, func.args[0], ctx)
535
                else:
536
                    sdl_assign(var_ptr, ctx.builder.load(func.args[0]), ctx)
537
            if input.transition:
dbarbera's avatar
dbarbera committed
538
                id_val = core.Constant.int(ctx.i32, input.transition_id)
dbarbera's avatar
dbarbera committed
539
                sdl_call('run_transition', [id_val], ctx)
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
@generate.register(ogAST.Output)
552
553
def _output(output, ctx):
    ''' Generate the IR for an output '''
dbarbera's avatar
dbarbera committed
554
555
    for out in output.output:
        name = out['outputName'].lower()
556
        args = out.get('params', [])
dbarbera's avatar
dbarbera committed
557

558
559
        arg_vals = []
        for arg in args:
dbarbera's avatar
dbarbera committed
560
            arg_val = expression(arg, ctx)
561
            # Pass by reference
dbarbera's avatar
dbarbera committed
562
563
564
            if arg_val.type.kind != core.TYPE_POINTER:
                arg_var = ctx.builder.alloca(arg_val.type, None)
                ctx.builder.store(arg_val, arg_var)
565
                arg_vals.append(arg_var)
566
            else:
567
                arg_vals.append(arg_val)
dbarbera's avatar
dbarbera committed
568

dbarbera's avatar
dbarbera committed
569
        sdl_call(str(name).lower(), arg_vals, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
570
571


572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
@generate.register(ogAST.ProcedureCall)
def _proc_call(proc_call, ctx):
    ''' Generate the IR for a procedure call '''
    output = proc_call.output[0]

    name = output['outputName'].lower()
    args = output.get('params', [])

    if name == 'write' or name == 'writeln':
        arg_vals = [expression(a, ctx) for a in args]
        arg_asn1tys = [a.exprType for a in args]
        sdl_write(arg_vals, arg_asn1tys, ctx, name == 'writeln')
        return
    elif name == 'reset_timer':
        sdl_reset_timer(args[0].value[0], ctx)
        return
    elif name == 'set_timer':
        timer_expr, timer_id = args
        sdl_set_timer(timer_id.value[0], expression(timer_expr, ctx), ctx)
        return

    proc = None
    for p in ctx.procedures:
        if p.inputString.lower() == name:
            proc = p
            break
    else:
        raise CompileError('Procedure "%s" not found' % name)

    arg_vals = []
    for arg, param in zip(args, proc.fpar):
        if param['direction'] == 'out':
            arg_vals.append(reference(arg, ctx))
        else:
            arg_val = expression(arg, ctx)
            # Pass by reference
            if arg_val.type.kind != core.TYPE_POINTER:
                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
618
@generate.register(ogAST.TaskAssign)
dbarbera's avatar
dbarbera committed
619
def _task_assign(task, ctx):
dbarbera's avatar
dbarbera committed
620
    ''' Generate the IR for a list of assignments '''
621
    for expr in task.elems:
dbarbera's avatar
dbarbera committed
622
        expression(expr, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
623
624
625


@generate.register(ogAST.TaskInformalText)
dbarbera's avatar
dbarbera committed
626
def _task_informal_text(task, ctx):
Maxime Perrotin's avatar
Maxime Perrotin committed
627
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
628
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
629
630
631


@generate.register(ogAST.TaskForLoop)
dbarbera's avatar
dbarbera committed
632
def _task_forloop(task, ctx):
dbarbera's avatar
dbarbera committed
633
    ''' Generate the IRfor a for loop '''
dbarbera's avatar
dbarbera committed
634
635
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
636
            generate_for_range(loop, ctx)
dbarbera's avatar
dbarbera committed
637
        else:
dbarbera's avatar
dbarbera committed
638
            generate_for_iterable(loop, ctx)
dbarbera's avatar
dbarbera committed
639
640


dbarbera's avatar
dbarbera committed
641
def generate_for_range(loop, ctx):
dbarbera's avatar
dbarbera committed
642
    ''' Generate the IR for a for x in range loop '''
dbarbera's avatar
dbarbera committed
643
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
644
645
646
    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
647
    end_block = func.append_basic_block('for:end')
dbarbera's avatar
dbarbera committed
648

649
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
650

dbarbera's avatar
dbarbera committed
651
    loop_var = ctx.builder.alloca(ctx.i64, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
652
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
653
654

    if loop['range']['start']:
dbarbera's avatar
dbarbera committed
655
        start_val = expression(loop['range']['start'], ctx)
dbarbera's avatar
dbarbera committed
656
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
657
    else:
dbarbera's avatar
dbarbera committed
658
        ctx.builder.store(core.Constant.int(ctx.i64, 0), loop_var)
dbarbera's avatar
dbarbera committed
659

dbarbera's avatar
dbarbera committed
660
    stop_val = expression(loop['range']['stop'], ctx)
dbarbera's avatar
dbarbera committed
661
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
662

dbarbera's avatar
dbarbera committed
663
664
665
666
    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
667

dbarbera's avatar
dbarbera committed
668
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
669
    generate(loop['transition'], ctx)
dbarbera's avatar
dbarbera committed
670
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
671

dbarbera's avatar
dbarbera committed
672
    ctx.builder.position_at_end(inc_block)
dbarbera's avatar
dbarbera committed
673
    step_val = core.Constant.int(ctx.i64, loop['range']['step'])
dbarbera's avatar
dbarbera committed
674
675
676
677
    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
678

dbarbera's avatar
dbarbera committed
679
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
680

681
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
682
683


dbarbera's avatar
dbarbera committed
684
def generate_for_iterable(loop, ctx):
dbarbera's avatar
dbarbera committed
685
    ''' Generate the IR for a for x in iterable loop '''
686
    seqof_asn1ty = ctx.basic_asn1type_of(loop['list'].exprType)
687
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
688

dbarbera's avatar
dbarbera committed
689
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
690
691
692
693
694
695
696
697

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

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

dbarbera's avatar
dbarbera committed
702
703
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
704
    seqof_val = expression(loop['list'], ctx)
705

706
707
708
709
    if isinstance(seqof_val, SDLSubstringValue):
        array_ptr = seqof_val.arr_ptr
    elif is_variable_size:
        array_ptr = ctx.builder.gep(seqof_val, [ctx.zero, ctx.one])
710
    else:
711
        array_ptr = ctx.builder.gep(seqof_val, [ctx.zero, ctx.zero])
712

713
    elem_llty = array_ptr.type.pointee.element
714

715
716
717
    if isinstance(seqof_val, SDLSubstringValue):
        end_idx = seqof_val.count_val
    elif is_variable_size:
718
        # load the current number of elements that is on the first field
719
        end_idx = ctx.builder.load(ctx.builder.gep(seqof_val, [ctx.zero, ctx.zero]))
720
    else:
dbarbera's avatar
dbarbera committed
721
        end_idx = core.Constant.int(ctx.i32, array_ptr.type.pointee.count)
dbarbera's avatar
dbarbera committed
722

723
    var_ptr = ctx.builder.alloca(elem_llty, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
724
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
725

dbarbera's avatar
dbarbera committed
726
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
727
728

    # load block
dbarbera's avatar
dbarbera committed
729
730
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
731
    if elem_llty.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
732
        elem_ptr = ctx.builder.gep(array_ptr, [ctx.zero, idx_var])
733
        sdl_assign(var_ptr, elem_ptr, ctx)
734
    else:
dbarbera's avatar
dbarbera committed
735
        elem_val = ctx.builder.load(ctx.builder.gep(array_ptr, [ctx.zero, idx_var]))
736
        sdl_assign(var_ptr, elem_val, ctx)
dbarbera's avatar
dbarbera committed
737
    ctx.builder.branch(body_block)
dbarbera's avatar
dbarbera committed
738
739

    # body block
dbarbera's avatar
dbarbera committed
740
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
741
    generate(loop['transition'], ctx)
dbarbera's avatar
dbarbera committed
742
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
743
744

    # cond block
dbarbera's avatar
dbarbera committed
745
746
747
748
749
    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
750

dbarbera's avatar
dbarbera committed
751
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
752

753
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
754

dbarbera's avatar
dbarbera committed
755

dbarbera's avatar
dbarbera committed
756
@singledispatch
dbarbera's avatar