LlvmGenerator.py 66.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
91
92
93
94
95
96
97
98
99
100
101
102
        self.funcs['ceil'] = core.Function.intrinsic(
            self.module,
            core.INTR_CEIL,
            [self.double]
        )

        self.funcs['floor'] = core.Function.intrinsic(
            self.module,
            core.INTR_FLOOR,
            [self.double]
        )

        self.funcs['trunc'] = core.Function.intrinsic(
            self.module,
            core.INTR_TRUNC,
            [self.double]
        )

dbarbera's avatar
dbarbera committed
103
104
105
106
107
108
        self.funcs['fabs'] = core.Function.intrinsic(
            self.module,
            core.INTR_FABS,
            [self.double]
        )

109
    def open_scope(self):
dbarbera's avatar
dbarbera committed
110
        ''' Open a scope '''
dbarbera's avatar
dbarbera committed
111
        self.scope = Scope(self, self.scope)
112
113

    def close_scope(self):
dbarbera's avatar
dbarbera committed
114
        ''' Close the current scope '''
115
116
        self.scope = self.scope.parent

117
    def basic_asn1type_of(self, asn1ty):
118
        ''' Return the ASN.1 basic type of a type '''
dbarbera's avatar
dbarbera committed
119
120
121
122
123
124
        if asn1ty.kind != 'ReferenceType':
            return asn1ty

        asn1ty_name = asn1ty.ReferencedTypeName.lower()

        # return the basic type if its cached
125
126
        if asn1ty_name in self.basic_asn1types:
            return self.basic_asn1types[asn1ty_name]
dbarbera's avatar
dbarbera committed
127

128
129
        basic_asn1ty = asn1ty
        while basic_asn1ty.kind == 'ReferenceType':
130
            for typename in self.dataview.viewkeys():
131
132
                if typename.lower() == basic_asn1ty.ReferencedTypeName.lower():
                    basic_asn1ty = self.dataview[typename].type
133
                    break
dbarbera's avatar
dbarbera committed
134
135

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

138
        return basic_asn1ty
139

140
    def lltype_of(self, asn1ty):
141
142
143
144
145
146
147
148
149
        ''' 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]

150
        basic_asn1ty = self.basic_asn1type_of(asn1ty)
151

dbarbera's avatar
dbarbera committed
152
        if basic_asn1ty.kind == 'IntegerType':
dbarbera's avatar
dbarbera committed
153
            llty = self.i64
dbarbera's avatar
dbarbera committed
154
        elif basic_asn1ty.kind == 'Integer32Type':
dbarbera's avatar
dbarbera committed
155
            llty = self.i32
156
        elif basic_asn1ty.kind == 'BooleanType':
dbarbera's avatar
dbarbera committed
157
            llty = self.i1
158
        elif basic_asn1ty.kind == 'RealType':
dbarbera's avatar
dbarbera committed
159
            llty = self.double
160
        elif basic_asn1ty.kind == 'SequenceOfType':
161
            llty = self._lltype_of_sequenceof(name, basic_asn1ty)
162
        elif basic_asn1ty.kind == 'SequenceType':
163
            llty = self._lltype_of_sequence(name, basic_asn1ty)
164
        elif basic_asn1ty.kind == 'EnumeratedType':
dbarbera's avatar
dbarbera committed
165
            llty = self.i32
166
        elif basic_asn1ty.kind == 'ChoiceType':
167
            llty = self._lltype_of_choice(name, basic_asn1ty)
168
        elif basic_asn1ty.kind == 'OctetStringType':
169
            llty = self._lltype_of_octetstring(name, basic_asn1ty)
dbarbera's avatar
dbarbera committed
170
        elif basic_asn1ty.kind in ('StringType', 'StandardStringType'):
dbarbera's avatar
dbarbera committed
171
            llty = self.i8_ptr
172
        else:
173
            raise CompileError('Unknown basic ASN.1 type "%s"' % basic_asn1ty.kind)
174
175
176
177
178
179

        if name:
            self.lltypes[name] = llty

        return llty

180
    def _lltype_of_sequenceof(self, name, asn1ty):
181
        ''' Return the LL type of a SequenceOf ASN.1 type '''
182
183
        min_size = int(asn1ty.Min)
        max_size = int(asn1ty.Max)
184
185
        is_variable_size = min_size != max_size

186
187
        elem_llty = self.lltype_of(asn1ty.type)
        array_llty = core.Type.array(elem_llty, max_size)
188
189

        if is_variable_size:
190
            struct = self.decl_struct(['nCount', 'arr'], [self.i32, array_llty], name)
191
        else:
192
            struct = self.decl_struct(['arr'], [array_llty], name)
193

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

197
        return struct.llty
198

199
    def _lltype_of_sequence(self, name, asn1ty):
200
201
        ''' Return the LL type of a Sequence ASN.1 type '''
        field_names = []
202
        field_lltys = []
203

204
        for field_name in Helper.sorted_fields(asn1ty):
205
            field_names.append(field_name.replace('-', '_'))
206
            field_lltys.append(self.lltype_of(asn1ty.Children[field_name].type))
207

208
        struct = self.decl_struct(field_names, field_lltys, name)
209

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

213
        return struct.llty
214

215
    def _lltype_of_choice(self, name, asn1ty):
216
217
        ''' Return the equivalent LL type of a Choice ASN.1 type '''
        field_names = []
218
        field_lltys = []
dbarbera's avatar
dbarbera committed
219

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

dbarbera's avatar
dbarbera committed
224
            field_names.append(field_name.replace('-', '_'))
225
            field_lltys.append(self.lltype_of(asn1ty.Children[field_name].type))
226

227
        union = self.decl_union(field_names, field_lltys, name)
228

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

232
        return union.llty
233

234
    def _lltype_of_octetstring(self, name, asn1ty):
dbarbera's avatar
dbarbera committed
235
        ''' Return the equivalent LL type of a OctetString ASN.1 type '''
236
237
        min_size = int(asn1ty.Min)
        max_size = int(asn1ty.Max)
238
239
        is_variable_size = min_size != max_size

240
        array_llty = core.Type.array(self.i8, max_size)
241
242

        if is_variable_size:
243
            struct = self.decl_struct(['nCount', 'arr'], [self.i32, array_llty], name)
244
        else:
245
            struct = self.decl_struct(['arr'], [array_llty], name)
246

247
        struct_ptr = core.Type.pointer(struct.llty)
248
        self.decl_func("asn1Scc%s_Equal" % name, self.i1, [struct_ptr, struct_ptr])
249

250
        return struct.llty
251

252
253
254
255
256
257
258
259
260
261
262
263
    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])

264
    def decl_func(self, name, return_llty, param_lltys, extern=False):
265
        ''' Declare a function '''
266
        func_llty = core.Type.function(return_llty, param_lltys)
267
        func_name = ("%s_RI_%s" % (self.name, name)) if extern else name
268
        func = core.Function.new(self.module, func_llty, func_name)
269
270
271
        self.funcs[name.lower()] = func
        return func

272
    def decl_struct(self, field_names, field_lltys, name=None):
273
274
        ''' Declare a struct '''
        name = name if name else "struct.%s" % len(self.structs)
dbarbera's avatar
dbarbera committed
275
        name = name.replace('-', '_')
276
        struct = StructType(name, field_names, field_lltys)
277
278
279
        self.structs[name] = struct
        return struct

dbarbera's avatar
dbarbera committed
280
281
282
283
    def resolve_struct(self, name):
        ''' Return the struct associated to a name '''
        return self.structs[name.replace('-', '_')]

284
    def decl_union(self, field_names, field_lltys, name=None):
285
        name = name if name else "union.%s" % len(self.structs)
dbarbera's avatar
dbarbera committed
286
        name = name.replace('-', '_')
287
        union = UnionType(name, field_names, field_lltys, self)
288
289
290
        self.unions[name] = union
        return union

dbarbera's avatar
dbarbera committed
291
292
293
294
    def resolve_union(self, name):
        ''' Return the union associated to a name '''
        return self.unions[name.replace('-', '_')]

dbarbera's avatar
dbarbera committed
295

296
class StructType():
297
    def __init__(self, name, field_names, field_lltys):
dbarbera's avatar
dbarbera committed
298
        self.name = name
299
        self.field_names = field_names
300
        self.llty = core.Type.struct(field_lltys, self.name)
301
302
303

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


dbarbera's avatar
dbarbera committed
306
class UnionType():
307
    def __init__(self, name, field_names, field_lltys, ctx):
dbarbera's avatar
dbarbera committed
308
309
        self.name = name
        self.field_names = field_names
310
        self.field_lltys = field_lltys
dbarbera's avatar
dbarbera committed
311
312
        # 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
313
314
        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
315
316
317

    def kind(self, name):
        idx = self.field_names.index(name)
318
        return (idx, self.field_lltys[idx])
dbarbera's avatar
dbarbera committed
319
320


321
class Scope:
dbarbera's avatar
dbarbera committed
322
323
    def __init__(self, ctx, parent=None):
        self.ctx = ctx
324
        self.vars = {}
dbarbera's avatar
dbarbera committed
325
        self.labels = {}
326
327
328
329
330
331
332
333
334
335
336
337
        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
338
            raise NameError("name '%s' is not defined" % name)
339

dbarbera's avatar
dbarbera committed
340
341
342
343
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
344
            func = self.ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
345
            label_block = func.append_basic_block('label:%s' % name)
dbarbera's avatar
dbarbera committed
346
347
348
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
349

350
351
352
353
class CompileError(Exception):
    pass


Maxime Perrotin's avatar
Maxime Perrotin committed
354
@singledispatch
dbarbera's avatar
dbarbera committed
355
def generate(ast, ctx=None):
dbarbera's avatar
dbarbera committed
356
    ''' Generate the IR for an AST node '''
357
    raise CompileError('Unsupported AST construct "%s"' % ast.__class__.__name__)
Maxime Perrotin's avatar
Maxime Perrotin committed
358

dbarbera's avatar
dbarbera committed
359

Maxime Perrotin's avatar
Maxime Perrotin committed
360
361
# Processing of the AST
@generate.register(ogAST.Process)
dbarbera's avatar
dbarbera committed
362
def _process(process, ctx=None):
dbarbera's avatar
dbarbera committed
363
    ''' Generate the IR for a process '''
dbarbera's avatar
dbarbera committed
364
365
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
366

dbarbera's avatar
dbarbera committed
367
    ctx = Context(process)
368

dbarbera's avatar
dbarbera committed
369
370
371
372
373
374
    # 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
375

376
377
    # Initialize states
    for name, val in process.mapping.viewitems():
378
        if not name.endswith('START'):
dbarbera's avatar
dbarbera committed
379
            cons_val = core.Constant.int(ctx.i32, len(ctx.states))
dbarbera's avatar
dbarbera committed
380
            ctx.states[name.lower()] = cons_val
381
        elif name != 'START':
dbarbera's avatar
dbarbera committed
382
            cons_val = core.Constant.int(ctx.i32, val)
dbarbera's avatar
dbarbera committed
383
            ctx.states[name.lower()] = cons_val
384

385
    # Generate state var
386
    state_cons = ctx.module.add_global_variable(ctx.i32, '.state')
dbarbera's avatar
dbarbera committed
387
    state_cons.initializer = core.Constant.int(ctx.i32, -1)
388
    ctx.scope.define('.state', state_cons)
389

390
    # Generare process-level vars
391
392
393
394
    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
395
        ctx.scope.define(str(name).lower(), global_var)
396

dbarbera's avatar
dbarbera committed
397
    # Declare set/reset timer functions
dbarbera's avatar
dbarbera committed
398
399
    for timer in process.timers:
        # TODO: Should be uint?
dbarbera's avatar
dbarbera committed
400
        ctx.decl_func("set_%s" % str(timer), ctx.void, [ctx.i64_ptr], True)
401
        ctx.decl_func("reset_%s" % str(timer), ctx.void, [], True)
dbarbera's avatar
dbarbera committed
402

403
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
404
    for signal in process.output_signals:
405
        if 'type' in signal:
406
            param_lltys = [core.Type.pointer(ctx.lltype_of(signal['type']))]
407
        else:
408
409
            param_lltys = []
        ctx.decl_func(str(signal['name']), ctx.void, param_lltys, True)
dbarbera's avatar
dbarbera committed
410

411
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
412
    for proc in [proc for proc in process.procedures if proc.external]:
413
414
        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
415

416
417
    # Generate internal procedures
    for proc in process.content.inner_procedures:
dbarbera's avatar
dbarbera committed
418
        generate(proc, ctx)
419

420
    # Generate process functions
dbarbera's avatar
dbarbera committed
421
422
    generate_runtr_func(process, ctx)
    generate_startup_func(process, ctx)
423

424
425
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
426
        generate_input_signal(signal, mapping[signal['name']], ctx)
427

dbarbera's avatar
dbarbera committed
428
429
    # Generate timer signal
    for timer in process.timers:
dbarbera's avatar
dbarbera committed
430
        generate_input_signal({'name': timer.lower()}, mapping[timer], ctx)
dbarbera's avatar
dbarbera committed
431

dbarbera's avatar
dbarbera committed
432
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
433

dbarbera's avatar
dbarbera committed
434
435
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
436
437


dbarbera's avatar
dbarbera committed
438
def generate_runtr_func(process, ctx):
dbarbera's avatar
dbarbera committed
439
    ''' Generate the IR for the run_transition function '''
440
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
441

442
    ctx.open_scope()
443

dbarbera's avatar
dbarbera committed
444
445
446
447
    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')
448

dbarbera's avatar
dbarbera committed
449
    ctx.builder = core.Builder.new(entry_block)
450
451

    # entry
dbarbera's avatar
dbarbera committed
452
453
454
455
    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)
456
457

    # cond
dbarbera's avatar
dbarbera committed
458
459
460
461
462
    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)
463
464

    # body
dbarbera's avatar
dbarbera committed
465
    ctx.builder.position_at_end(body_block)
466
    switch = ctx.builder.switch(id_val, exit_block)
467
468
469

    # transitions
    for idx, tr in enumerate(process.transitions):
dbarbera's avatar
dbarbera committed
470
        tr_block = func.append_basic_block('runtr:tr%d' % idx)
dbarbera's avatar
dbarbera committed
471
        const = core.Constant.int(ctx.i32, idx)
472
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
473
        ctx.builder.position_at_end(tr_block)
dbarbera's avatar
dbarbera committed
474
        generate(tr, ctx)
dbarbera's avatar
dbarbera committed
475
476
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
477
478

    # exit
dbarbera's avatar
dbarbera committed
479
480
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
481

dbarbera's avatar
dbarbera committed
482
483
    Helper.inner_labels_to_floating(process)
    for label in process.content.floating_labels:
dbarbera's avatar
dbarbera committed
484
        generate(label, ctx)
dbarbera's avatar
dbarbera committed
485

dbarbera's avatar
dbarbera committed
486
487
488
    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
489

490
    ctx.close_scope()
491

492
493
494
495
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
496
def generate_startup_func(process, ctx):
dbarbera's avatar
dbarbera committed
497
    ''' Generate the IR for the startup function '''
498
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
499

500
    ctx.open_scope()
501

dbarbera's avatar
dbarbera committed
502
    entry_block = func.append_basic_block('startup:entry')
dbarbera's avatar
dbarbera committed
503
    ctx.builder = core.Builder.new(entry_block)
504

505
506
507
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
508
            global_var = ctx.scope.resolve(str(name))
509
            sdl_assign(global_var, expression(expr, ctx), ctx)
510

dbarbera's avatar
dbarbera committed
511
    sdl_call('run_transition', [core.Constant.int(ctx.i32, 0)], ctx)
dbarbera's avatar
dbarbera committed
512
    ctx.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
513

514
    ctx.close_scope()
515

516
517
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
518
519


dbarbera's avatar
dbarbera committed
520
def generate_input_signal(signal, inputs, ctx):
dbarbera's avatar
dbarbera committed
521
    ''' Generate the IR for an input signal '''
dbarbera's avatar
dbarbera committed
522
    func_name = ctx.name + "_" + str(signal['name'])
523
    param_lltys = []
524
    if 'type' in signal:
525
        param_lltys.append(core.Type.pointer(ctx.lltype_of(signal['type'])))
526

527
    func = ctx.decl_func(func_name, ctx.void, param_lltys)
528

529
    ctx.open_scope()
530

dbarbera's avatar
dbarbera committed
531
532
    entry_block = func.append_basic_block('input:entry')
    exit_block = func.append_basic_block('input:exit')
dbarbera's avatar
dbarbera committed
533
    ctx.builder = core.Builder.new(entry_block)
534

535
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('.state'))
dbarbera's avatar
dbarbera committed
536
    switch = ctx.builder.switch(g_state_val, exit_block)
537

dbarbera's avatar
dbarbera committed
538
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
539
        if state_name.endswith('start'):
dbarbera's avatar
dbarbera committed
540
            continue
dbarbera's avatar
dbarbera committed
541
        state_block = func.append_basic_block('input:state_%s' % str(state_name))
542
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
543
        ctx.builder.position_at_end(state_block)
544
545
546

        # TODO: Nested states

547
548
549
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
550
                var_ptr = ctx.scope.resolve(str(var_name))
dbarbera's avatar
dbarbera committed
551
                if is_struct_ptr(var_ptr) or is_array_ptr(var_ptr):
552
                    sdl_assign(var_ptr, func.args[0], ctx)
553
                else:
554
                    sdl_assign(var_ptr, ctx.builder.load(func.args[0]), ctx)
555
            if input.transition:
dbarbera's avatar
dbarbera committed
556
                id_val = core.Constant.int(ctx.i32, input.transition_id)
dbarbera's avatar
dbarbera committed
557
                sdl_call('run_transition', [id_val], ctx)
558

dbarbera's avatar
dbarbera committed
559
        ctx.builder.ret_void()
560

dbarbera's avatar
dbarbera committed
561
562
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
563

564
    ctx.close_scope()
565

566
567
568
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
569
@generate.register(ogAST.Output)
570
571
def _output(output, ctx):
    ''' Generate the IR for an output '''
dbarbera's avatar
dbarbera committed
572
573
    for out in output.output:
        name = out['outputName'].lower()
574
        args = out.get('params', [])
dbarbera's avatar
dbarbera committed
575

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

dbarbera's avatar
dbarbera committed
587
        sdl_call(str(name).lower(), arg_vals, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
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
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
@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
636
@generate.register(ogAST.TaskAssign)
dbarbera's avatar
dbarbera committed
637
def _task_assign(task, ctx):
dbarbera's avatar
dbarbera committed
638
    ''' Generate the IR for a list of assignments '''
639
    for expr in task.elems:
dbarbera's avatar
dbarbera committed
640
        expression(expr, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
641
642
643


@generate.register(ogAST.TaskInformalText)
dbarbera's avatar
dbarbera committed
644
def _task_informal_text(task, ctx):
Maxime Perrotin's avatar
Maxime Perrotin committed
645
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
646
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
647
648
649


@generate.register(ogAST.TaskForLoop)
dbarbera's avatar
dbarbera committed
650
def _task_forloop(task, ctx):
dbarbera's avatar
dbarbera committed
651
    ''' Generate the IRfor a for loop '''
dbarbera's avatar
dbarbera committed
652
653
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
654
            generate_for_range(loop, ctx)
dbarbera's avatar
dbarbera committed
655
        else:
dbarbera's avatar
dbarbera committed
656
            generate_for_iterable(loop, ctx)
dbarbera's avatar
dbarbera committed
657
658


dbarbera's avatar
dbarbera committed
659
def generate_for_range(loop, ctx):
dbarbera's avatar
dbarbera committed
660
    ''' Generate the IR for a for x in range loop '''
dbarbera's avatar
dbarbera committed
661
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
662
663
664
    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
665
    end_block = func.append_basic_block('for:end')
dbarbera's avatar
dbarbera committed
666

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

dbarbera's avatar
dbarbera committed
669
    loop_var = ctx.builder.alloca(ctx.i64, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
670
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
671
672

    if loop['range']['start']:
dbarbera's avatar
dbarbera committed
673
        start_val = expression(loop['range']['start'], ctx)
dbarbera's avatar
dbarbera committed
674
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
675
    else:
dbarbera's avatar
dbarbera committed
676
        ctx.builder.store(core.Constant.int(ctx.i64, 0), loop_var)
dbarbera's avatar
dbarbera committed
677

dbarbera's avatar
dbarbera committed
678
    stop_val = expression(loop['range']['stop'], ctx)
dbarbera's avatar
dbarbera committed
679
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
680

dbarbera's avatar
dbarbera committed
681
682
683
684
    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
685

dbarbera's avatar
dbarbera committed
686
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
687
    generate(loop['transition'], ctx)
dbarbera's avatar
dbarbera committed
688
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
689

dbarbera's avatar
dbarbera committed
690
    ctx.builder.position_at_end(inc_block)
dbarbera's avatar
dbarbera committed
691
    step_val = core.Constant.int(ctx.i64, loop['range']['step'])
dbarbera's avatar
dbarbera committed
692
693
694
695
    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
696

dbarbera's avatar
dbarbera committed
697
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
698

699
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
700
701


dbarbera's avatar
dbarbera committed
702
def generate_for_iterable(loop, ctx):
dbarbera's avatar
dbarbera committed
703
    ''' Generate the IR for a for x in iterable loop '''
704
    seqof_asn1ty = ctx.basic_asn1type_of(loop['list'].exprType)
705
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
706

dbarbera's avatar
dbarbera committed
707
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
708
709
710
711
712
713
714
715

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

718
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
719

dbarbera's avatar
dbarbera committed
720
721
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
722
    seqof_val = expression(loop['list'], ctx)
723

724
725
726
727
    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])
728
    else:
729
        array_ptr = ctx.builder.gep(seqof_val, [ctx.zero, ctx.zero])
730

731
    elem_llty = array_ptr.type.pointee.element
732

733
734
735
    if isinstance(seqof_val, SDLSubstringValue):
        end_idx = seqof_val.count_val
    elif is_variable_size:
736
        # load the current number of elements that is on the first field
737
        end_idx = ctx.builder.load(ctx.builder.gep(seqof_val, [ctx.zero, ctx.zero]))
738
    else:
dbarbera's avatar
dbarbera committed
739
        end_idx = core.Constant.int(ctx.i32, array_ptr.type.pointee.count)
dbarbera's avatar
dbarbera committed
740

741
    var_ptr = ctx.builder.alloca(elem_llty, None, str(loop['var']))
dbarbera's avatar
dbarbera committed
742
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
743

dbarbera's avatar
dbarbera committed
744
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
745
746

    # load block
dbarbera's avatar
dbarbera committed
747
748
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
749
    if elem_llty.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
750
        elem_ptr = ctx.builder.gep(array_ptr, [ctx.zero, idx_var])
751
        sdl_assign(var_ptr, elem_ptr, ctx)
752
    else:
dbarbera's avatar
dbarbera committed
753
        elem_val = ctx.builder.load(ctx.builder.gep(array_ptr, [ctx.zero, idx_var]))
754
        sdl_assign(var_ptr, elem_val, ctx)
dbarbera's avatar
dbarbera committed
755
    ctx.builder.branch(body_block)
dbarbera's avatar
dbarbera committed
756
757

    # body block
dbarbera's avatar
dbarbera committed
758
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
759
    generate(loop['transition'], ctx)
dbarbera's avatar
dbarbera committed
760
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
761
762

    # cond block
dbarbera's avatar
dbarbera committed
763
764
765
766
767
    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
768

dbarbera's avatar
dbarbera committed
769
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
770