LlvmGenerator.py 64.8 KB
Newer Older
Maxime Perrotin's avatar
Maxime Perrotin committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 = {}
49
        self.basic_asn1types = {}
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
    def basic_asn1type_of(self, asn1ty):
99
        ''' Return the ASN.1 basic type of a type '''
dbarbera's avatar
dbarbera committed
100
101
102
103
104
105
        if asn1ty.kind != 'ReferenceType':
            return asn1ty

        asn1ty_name = asn1ty.ReferencedTypeName.lower()

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

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

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

119
        return basic_asn1ty
120

121
    def lltype_of(self, asn1ty):
122
123
124
125
126
127
128
129
130
        ''' 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_asn1type_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
        elif basic_asn1ty.kind == 'SequenceOfType':
142
            llty = self._lltype_of_sequenceof(name, basic_asn1ty)
143
        elif basic_asn1ty.kind == 'SequenceType':
144
            llty = self._lltype_of_sequence(name, basic_asn1ty)
145
        elif basic_asn1ty.kind == 'EnumeratedType':
dbarbera's avatar
dbarbera committed
146
            llty = self.i32
147
        elif basic_asn1ty.kind == 'ChoiceType':
148
            llty = self._lltype_of_choice(name, basic_asn1ty)
149
        elif basic_asn1ty.kind == 'OctetStringType':
150
            llty = self._lltype_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
        else:
154
            raise CompileError('Unknown basic ASN.1 type "%s"' % basic_asn1ty.kind)
155
156
157
158
159
160

        if name:
            self.lltypes[name] = llty

        return llty

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

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

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

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

178
        return struct.llty
179

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

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

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

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

194
        return struct.llty
195

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

201
        for idx, field_name in enumerate(Helper.sorted_fields(asn1ty)):
dbarbera's avatar
dbarbera committed
202
            # 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('-', '_'))
206
            field_lltys.append(self.lltype_of(asn1ty.Children[field_name].type))
207

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

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

213
        return union.llty
214

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

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

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

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

231
        return struct.llty
232

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

253
    def decl_struct(self, field_names, field_lltys, name=None):
254
255
        ''' Declare a struct '''
        name = name if name else "struct.%s" % len(self.structs)
dbarbera's avatar
dbarbera committed
256
        name = name.replace('-', '_')
257
        struct = StructType(name, field_names, field_lltys)
258
259
260
        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
    def decl_union(self, field_names, field_lltys, name=None):
266
        name = name if name else "union.%s" % len(self.structs)
dbarbera's avatar
dbarbera committed
267
        name = name.replace('-', '_')
268
        union = UnionType(name, field_names, field_lltys, 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
class StructType():
278
    def __init__(self, name, field_names, field_lltys):
dbarbera's avatar
dbarbera committed
279
        self.name = name
280
        self.field_names = field_names
281
        self.llty = core.Type.struct(field_lltys, self.name)
282
283
284

    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():
288
    def __init__(self, name, field_names, field_lltys, ctx):
dbarbera's avatar
dbarbera committed
289
290
        self.name = name
        self.field_names = field_names
291
        self.field_lltys = field_lltys
dbarbera's avatar
dbarbera committed
292
293
        # 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
294
295
        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
296
297
298

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


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
    # Generare process-level vars
372
373
374
375
    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
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_lltys = [core.Type.pointer(ctx.lltype_of(signal['type']))]
388
        else:
389
390
            param_lltys = []
        ctx.decl_func(str(signal['name']), ctx.void, param_lltys, 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
395
        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
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

dbarbera's avatar
dbarbera committed
467
468
469
    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
470

471
    ctx.close_scope()
472

473
474
475
476
    func.verify()
    return func


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

481
    ctx.open_scope()
482

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

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

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

495
    ctx.close_scope()
496

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


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

508
    func = ctx.decl_func(func_name, ctx.void, param_lltys)
509

510
    ctx.open_scope()
511

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

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

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

        # TODO: Nested states

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

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

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

545
    ctx.close_scope()
546

547
548
549
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
550
551
@generate.register(ogAST.Output)
@generate.register(ogAST.ProcedureCall)
dbarbera's avatar
dbarbera committed
552
def _call_external_function(output, ctx):
dbarbera's avatar
dbarbera committed
553
    ''' Generate the IR for an output or procedure call '''
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
        if name == 'write' or name == 'writeln':
            arg_vals = [expression(a, ctx) for a in args]
560
561
            arg_asn1tys = [a.exprType for a in args]
            sdl_write(arg_vals, arg_asn1tys, ctx, name == 'writeln')
562
            continue
dbarbera's avatar
dbarbera committed
563
        elif name == 'reset_timer':
564
            sdl_reset_timer(args[0].value[0], ctx)
dbarbera's avatar
dbarbera committed
565
566
            continue
        elif name == 'set_timer':
567
568
            timer_expr, timer_id = args
            sdl_set_timer(timer_id.value[0], expression(timer_expr, ctx), ctx)
dbarbera's avatar
dbarbera committed
569
570
            continue

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

dbarbera's avatar
dbarbera committed
582
        sdl_call(str(name).lower(), arg_vals, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
583
584
585


@generate.register(ogAST.TaskAssign)
dbarbera's avatar
dbarbera committed
586
def _task_assign(task, ctx):
dbarbera's avatar
dbarbera committed
587
    ''' Generate the IR for a list of assignments '''
588
    for expr in task.elems:
dbarbera's avatar
dbarbera committed
589
        expression(expr, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
590
591
592


@generate.register(ogAST.TaskInformalText)
dbarbera's avatar
dbarbera committed
593
def _task_informal_text(task, ctx):
Maxime Perrotin's avatar
Maxime Perrotin committed
594
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
595
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
596
597
598


@generate.register(ogAST.TaskForLoop)
dbarbera's avatar
dbarbera committed
599
def _task_forloop(task, ctx):
dbarbera's avatar
dbarbera committed
600
    ''' Generate the IRfor a for loop '''
dbarbera's avatar
dbarbera committed
601
602
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
603
            generate_for_range(loop, ctx)
dbarbera's avatar
dbarbera committed
604
        else:
dbarbera's avatar
dbarbera committed
605
            generate_for_iterable(loop, ctx)
dbarbera's avatar
dbarbera committed
606
607


dbarbera's avatar
dbarbera committed
608
def generate_for_range(loop, ctx):
dbarbera's avatar
dbarbera committed
609
    ''' Generate the IR for a for x in range loop '''
dbarbera's avatar
dbarbera committed
610
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
611
612
613
    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
614
    end_block = func.append_basic_block('for:end')
dbarbera's avatar
dbarbera committed
615

616
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
617

dbarbera's avatar
dbarbera committed
618
    loop_var = ctx.builder.alloca(ctx.i64, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
619
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
620
621

    if loop['range']['start']:
dbarbera's avatar
dbarbera committed
622
        start_val = expression(loop['range']['start'], ctx)
dbarbera's avatar
dbarbera committed
623
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
624
    else:
dbarbera's avatar
dbarbera committed
625
        ctx.builder.store(core.Constant.int(ctx.i64, 0), loop_var)
dbarbera's avatar
dbarbera committed
626

dbarbera's avatar
dbarbera committed
627
    stop_val = expression(loop['range']['stop'], ctx)
dbarbera's avatar
dbarbera committed
628
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
629

dbarbera's avatar
dbarbera committed
630
631
632
633
    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
634

dbarbera's avatar
dbarbera committed
635
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
636
    generate(loop['transition'], ctx)
dbarbera's avatar
dbarbera committed
637
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
638

dbarbera's avatar
dbarbera committed
639
    ctx.builder.position_at_end(inc_block)
dbarbera's avatar
dbarbera committed
640
    step_val = core.Constant.int(ctx.i64, loop['range']['step'])
dbarbera's avatar
dbarbera committed
641
642
643
644
    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
645

dbarbera's avatar
dbarbera committed
646
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
647

648
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
649
650


dbarbera's avatar
dbarbera committed
651
def generate_for_iterable(loop, ctx):
dbarbera's avatar
dbarbera committed
652
    ''' Generate the IR for a for x in iterable loop '''
653
    seqof_asn1ty = ctx.basic_asn1type_of(loop['list'].exprType)
654
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
655

dbarbera's avatar
dbarbera committed
656
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
657
658
659
660
661
662
663
664

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

667
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
668

dbarbera's avatar
dbarbera committed
669
670
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
671
    seqof_val = expression(loop['list'], ctx)
672

673
674
675
676
    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])
677
    else:
678
        array_ptr = ctx.builder.gep(seqof_val, [ctx.zero, ctx.zero])
679

680
    elem_llty = array_ptr.type.pointee.element
681

682
683
684
    if isinstance(seqof_val, SDLSubstringValue):
        end_idx = seqof_val.count_val
    elif is_variable_size:
685
        # load the current number of elements that is on the first field
686
        end_idx = ctx.builder.load(ctx.builder.gep(seqof_val, [ctx.zero, ctx.zero]))
687
    else:
dbarbera's avatar
dbarbera committed
688
        end_idx = core.Constant.int(ctx.i32, array_ptr.type.pointee.count)
dbarbera's avatar
dbarbera committed
689

690
    var_ptr = ctx.builder.alloca(elem_llty, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
691
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
692

dbarbera's avatar
dbarbera committed
693
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
694
695

    # load block
dbarbera's avatar
dbarbera committed
696
697
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
698
    if elem_llty.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
699
        elem_ptr = ctx.builder.gep(array_ptr, [ctx.zero, idx_var])
700
        sdl_assign(var_ptr, elem_ptr, ctx)
701
    else:
dbarbera's avatar
dbarbera committed
702
        elem_val = ctx.builder.load(ctx.builder.gep(array_ptr, [ctx.zero, idx_var]))
703
        sdl_assign(var_ptr, elem_val, ctx)
dbarbera's avatar
dbarbera committed
704
    ctx.builder.branch(body_block)
dbarbera's avatar
dbarbera committed
705
706

    # body block
dbarbera's avatar
dbarbera committed
707
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
708
    generate(loop['transition'], ctx)
dbarbera's avatar
dbarbera committed
709
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
710
711

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

dbarbera's avatar
dbarbera committed
718
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
719

720
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
721

dbarbera's avatar
dbarbera committed
722

dbarbera's avatar
dbarbera committed
723
@singledispatch
dbarbera's avatar
dbarbera committed
724
def reference(prim, ctx):
dbarbera's avatar
dbarbera committed
725
    ''' Generate the IR for a reference '''
726
    raise CompileError('Unsupp