LlvmGenerator.py 63 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
50
51
52
53
54
55
56
57
58
59
60
61
62

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

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

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

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

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

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

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

97
98
99
100
101
102
103
104
105
106
107
    def basic_type_of(self, asn1ty):
        ''' Return the ASN.1 basic type of a type '''
        basic_type = asn1ty
        while basic_type.kind == 'ReferenceType':
            # Find type with proper case in the data view
            for typename in self.dataview.viewkeys():
                if typename.lower() == basic_type.ReferencedTypeName.lower():
                    basic_type = self.dataview[typename].type
                    break
        return basic_type

108
109
110
111
112
113
114
115
116
117
    def type_of(self, asn1ty):
        ''' Return the LL type of a ASN.1 type '''
        try:
            name = asn1ty.ReferencedTypeName.replace('-', '_')
        except AttributeError:
            name = None

        if name and name in self.lltypes:
            return self.lltypes[name]

118
        basic_asn1ty = self.basic_type_of(asn1ty)
119

dbarbera's avatar
dbarbera committed
120
        if basic_asn1ty.kind == 'IntegerType':
dbarbera's avatar
dbarbera committed
121
            llty = self.i64
dbarbera's avatar
dbarbera committed
122
        elif basic_asn1ty.kind == 'Integer32Type':
dbarbera's avatar
dbarbera committed
123
            llty = self.i32
124
        elif basic_asn1ty.kind == 'BooleanType':
dbarbera's avatar
dbarbera committed
125
            llty = self.i1
126
        elif basic_asn1ty.kind == 'RealType':
dbarbera's avatar
dbarbera committed
127
            llty = self.double
128
129
130
131
132
        elif basic_asn1ty.kind == 'SequenceOfType':
            llty = self._type_of_sequenceof(name, basic_asn1ty)
        elif basic_asn1ty.kind == 'SequenceType':
            llty = self._type_of_sequence(name, basic_asn1ty)
        elif basic_asn1ty.kind == 'EnumeratedType':
dbarbera's avatar
dbarbera committed
133
            llty = self.i32
134
135
136
137
        elif basic_asn1ty.kind == 'ChoiceType':
            llty = self._type_of_choice(name, basic_asn1ty)
        elif basic_asn1ty.kind == 'OctetStringType':
            llty = self._type_of_octetstring(name, basic_asn1ty)
dbarbera's avatar
dbarbera committed
138
        elif basic_asn1ty.kind in ('StringType', 'StandardStringType'):
dbarbera's avatar
dbarbera committed
139
            llty = self.i8_ptr
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
        else:
            raise NotImplementedError

        if name:
            self.lltypes[name] = llty

        return llty

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

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

        if is_variable_size:
dbarbera's avatar
dbarbera committed
158
            struct = self.decl_struct(['nCount', 'arr'], [self.i32, array_ty], name)
159
        else:
160
            struct = self.decl_struct(['arr'], [array_ty], name)
161

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

165
166
167
168
169
170
        return struct.ty

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

172
173
174
        for field_name in Helper.sorted_fields(sequence_ty):
            field_names.append(field_name.replace('-', '_'))
            field_types.append(self.type_of(sequence_ty.Children[field_name].type))
175

176
        struct = self.decl_struct(field_names, field_types, name)
177

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

181
182
183
184
185
186
        return struct.ty

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

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

dbarbera's avatar
dbarbera committed
192
            field_names.append(field_name.replace('-', '_'))
dbarbera's avatar
dbarbera committed
193
            field_types.append(self.type_of(choice_ty.Children[field_name].type))
194

195
        union = self.decl_union(field_names, field_types, name)
196
197

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

200
201
202
        return union.ty

    def _type_of_octetstring(self, name, octetstring_ty):
dbarbera's avatar
dbarbera committed
203
        ''' Return the equivalent LL type of a OctetString ASN.1 type '''
204
        min_size = int(octetstring_ty.Min)
205
        max_size = int(octetstring_ty.Max)
206
207
        is_variable_size = min_size != max_size

dbarbera's avatar
dbarbera committed
208
        array_ty = core.Type.array(self.i8, max_size)
209
210

        if is_variable_size:
dbarbera's avatar
dbarbera committed
211
            struct = self.decl_struct(['nCount', 'arr'], [self.i32, array_ty], name)
212
213
        else:
            struct = self.decl_struct(['arr'], [array_ty], name)
214
215

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

218
219
        return struct.ty

220
221
222
223
224
225
226
227
228
229
230
231
    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])

232
233
234
235
236
237
238
239
240
241
242
    def decl_func(self, name, return_ty, param_tys, extern=False):
        ''' Declare a function '''
        func_ty = core.Type.function(return_ty, param_tys)
        func_name = ("%s_RI_%s" % (self.name, name)) if extern else name
        func = core.Function.new(self.module, func_ty, func_name)
        self.funcs[name.lower()] = func
        return func

    def decl_struct(self, field_names, field_types, name=None):
        ''' Declare a struct '''
        name = name if name else "struct.%s" % len(self.structs)
dbarbera's avatar
dbarbera committed
243
        name = name.replace('-', '_')
244
245
246
247
        struct = StructType(name, field_names, field_types)
        self.structs[name] = struct
        return struct

dbarbera's avatar
dbarbera committed
248
249
250
251
    def resolve_struct(self, name):
        ''' Return the struct associated to a name '''
        return self.structs[name.replace('-', '_')]

252
253
    def decl_union(self, field_names, field_types, name=None):
        name = name if name else "union.%s" % len(self.structs)
dbarbera's avatar
dbarbera committed
254
        name = name.replace('-', '_')
dbarbera's avatar
dbarbera committed
255
        union = UnionType(name, field_names, field_types, self)
256
257
258
        self.unions[name] = union
        return union

dbarbera's avatar
dbarbera committed
259
260
261
262
    def resolve_union(self, name):
        ''' Return the union associated to a name '''
        return self.unions[name.replace('-', '_')]

dbarbera's avatar
dbarbera committed
263

264
265
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
266
        self.name = name
267
268
269
270
271
        self.field_names = field_names
        self.ty = core.Type.struct(field_types, self.name)

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


dbarbera's avatar
dbarbera committed
274
class UnionType():
dbarbera's avatar
dbarbera committed
275
    def __init__(self, name, field_names, field_types, ctx):
dbarbera's avatar
dbarbera committed
276
277
278
279
280
        self.name = name
        self.field_names = field_names
        self.field_types = field_types
        # Unions are represented a struct with a field indicating the index of its type
        # and a byte array with the size of the biggest type in the union
dbarbera's avatar
dbarbera committed
281
282
        self.size = max([ctx.target_data.size(ty) for ty in field_types])
        self.ty = core.Type.struct([ctx.i32, core.Type.array(ctx.i8, self.size)], name)
dbarbera's avatar
dbarbera committed
283
284
285
286
287
288

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


289
class Scope:
dbarbera's avatar
dbarbera committed
290
291
    def __init__(self, ctx, parent=None):
        self.ctx = ctx
292
        self.vars = {}
dbarbera's avatar
dbarbera committed
293
        self.labels = {}
294
295
296
297
298
299
300
301
302
303
304
305
        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
306
            raise NameError("name '%s' is not defined" % name)
307

dbarbera's avatar
dbarbera committed
308
309
310
311
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
312
            func = self.ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
313
            label_block = func.append_basic_block('label:%s' % name)
dbarbera's avatar
dbarbera committed
314
315
316
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
317

318
319
320
321
class CompileError(Exception):
    pass


Maxime Perrotin's avatar
Maxime Perrotin committed
322
@singledispatch
dbarbera's avatar
dbarbera committed
323
def generate(ast, ctx=None):
dbarbera's avatar
dbarbera committed
324
    ''' Generate the IR for an AST node '''
325
    raise CompileError('Unsupported AST construct "%s"' % ast.__class__.__name__)
Maxime Perrotin's avatar
Maxime Perrotin committed
326

dbarbera's avatar
dbarbera committed
327

Maxime Perrotin's avatar
Maxime Perrotin committed
328
329
# Processing of the AST
@generate.register(ogAST.Process)
dbarbera's avatar
dbarbera committed
330
def _process(process, ctx=None):
dbarbera's avatar
dbarbera committed
331
    ''' Generate the IR for a process '''
dbarbera's avatar
dbarbera committed
332
333
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
334

dbarbera's avatar
dbarbera committed
335
    ctx = Context(process)
336

dbarbera's avatar
dbarbera committed
337
338
339
340
341
342
    # 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
343

344
345
    # Initialize states
    for name, val in process.mapping.viewitems():
346
        if not name.endswith('START'):
dbarbera's avatar
dbarbera committed
347
            cons_val = core.Constant.int(ctx.i32, len(ctx.states))
dbarbera's avatar
dbarbera committed
348
            ctx.states[name.lower()] = cons_val
349
        elif name != 'START':
dbarbera's avatar
dbarbera committed
350
            cons_val = core.Constant.int(ctx.i32, val)
dbarbera's avatar
dbarbera committed
351
            ctx.states[name.lower()] = cons_val
352

353
    # Generate state var
354
    state_cons = ctx.module.add_global_variable(ctx.i32, '.state')
dbarbera's avatar
dbarbera committed
355
    state_cons.initializer = core.Constant.int(ctx.i32, -1)
356
    ctx.scope.define('.state', state_cons)
357

358
359
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
360
        var_ty = ctx.type_of(ty)
dbarbera's avatar
dbarbera committed
361
        global_var = ctx.module.add_global_variable(var_ty, str(name))
362
        global_var.initializer = core.Constant.null(var_ty)
dbarbera's avatar
dbarbera committed
363
        ctx.scope.define(str(name).lower(), global_var)
364

dbarbera's avatar
dbarbera committed
365
    # Declare set/reset timer functions
dbarbera's avatar
dbarbera committed
366
367
    for timer in process.timers:
        # TODO: Should be uint?
dbarbera's avatar
dbarbera committed
368
        ctx.decl_func("set_%s" % str(timer), ctx.void, [ctx.i64_ptr], True)
369
        ctx.decl_func("reset_%s" % str(timer), ctx.void, [], True)
dbarbera's avatar
dbarbera committed
370

371
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
372
    for signal in process.output_signals:
373
        if 'type' in signal:
374
            param_tys = [core.Type.pointer(ctx.type_of(signal['type']))]
375
376
        else:
            param_tys = []
377
        ctx.decl_func(str(signal['name']), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
378

379
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
380
    for proc in [proc for proc in process.procedures if proc.external]:
381
        param_tys = [core.Type.pointer(ctx.type_of(p['type'])) for p in proc.fpar]
382
        ctx.decl_func(str(proc.inputString), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
383

384
385
    # Generate internal procedures
    for proc in process.content.inner_procedures:
dbarbera's avatar
dbarbera committed
386
        generate(proc, ctx)
387

388
    # Generate process functions
dbarbera's avatar
dbarbera committed
389
390
    generate_runtr_func(process, ctx)
    generate_startup_func(process, ctx)
391

392
393
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
394
        generate_input_signal(signal, mapping[signal['name']], ctx)
395

dbarbera's avatar
dbarbera committed
396
397
    # Generate timer signal
    for timer in process.timers:
dbarbera's avatar
dbarbera committed
398
        generate_input_signal({'name': timer.lower()}, mapping[timer], ctx)
dbarbera's avatar
dbarbera committed
399

dbarbera's avatar
dbarbera committed
400
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
401

dbarbera's avatar
dbarbera committed
402
403
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
404
405


dbarbera's avatar
dbarbera committed
406
def generate_runtr_func(process, ctx):
dbarbera's avatar
dbarbera committed
407
    ''' Generate the IR for the run_transition function '''
408
    func = ctx.decl_func('run_transition', ctx.void, [ctx.i32])
409

410
    ctx.open_scope()
411

dbarbera's avatar
dbarbera committed
412
413
414
415
    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')
416

dbarbera's avatar
dbarbera committed
417
    ctx.builder = core.Builder.new(entry_block)
418
419

    # entry
dbarbera's avatar
dbarbera committed
420
421
422
423
    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)
424
425

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

    # body
dbarbera's avatar
dbarbera committed
433
    ctx.builder.position_at_end(body_block)
434
    switch = ctx.builder.switch(id_val, exit_block)
435
436
437

    # transitions
    for idx, tr in enumerate(process.transitions):
dbarbera's avatar
dbarbera committed
438
        tr_block = func.append_basic_block('runtr:tr%d' % idx)
dbarbera's avatar
dbarbera committed
439
        const = core.Constant.int(ctx.i32, idx)
440
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
441
        ctx.builder.position_at_end(tr_block)
dbarbera's avatar
dbarbera committed
442
        generate(tr, ctx)
dbarbera's avatar
dbarbera committed
443
444
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
445
446

    # exit
dbarbera's avatar
dbarbera committed
447
448
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
449

dbarbera's avatar
dbarbera committed
450
451
    Helper.inner_labels_to_floating(process)
    for label in process.content.floating_labels:
dbarbera's avatar
dbarbera committed
452
        generate(label, ctx)
dbarbera's avatar
dbarbera committed
453
454

    # TODO: Use defined cond_block instead?
dbarbera's avatar
dbarbera committed
455
456
457
    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
458

459
    ctx.close_scope()
460

461
462
463
464
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
465
def generate_startup_func(process, ctx):
dbarbera's avatar
dbarbera committed
466
    ''' Generate the IR for the startup function '''
467
    func = ctx.decl_func(ctx.name + '_startup', ctx.void, [])
468

469
    ctx.open_scope()
470

dbarbera's avatar
dbarbera committed
471
    entry_block = func.append_basic_block('startup:entry')
dbarbera's avatar
dbarbera committed
472
    ctx.builder = core.Builder.new(entry_block)
473

474
475
476
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
477
            global_var = ctx.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
478
            generate_assign(global_var, expression(expr, ctx), ctx)
479

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

483
    ctx.close_scope()
484

485
486
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
487
488


dbarbera's avatar
dbarbera committed
489
def generate_input_signal(signal, inputs, ctx):
dbarbera's avatar
dbarbera committed
490
    ''' Generate the IR for an input signal '''
dbarbera's avatar
dbarbera committed
491
    func_name = ctx.name + "_" + str(signal['name'])
492
493
    param_tys = []
    if 'type' in signal:
494
        param_tys.append(core.Type.pointer(ctx.type_of(signal['type'])))
495

496
    func = ctx.decl_func(func_name, ctx.void, param_tys)
497

498
    ctx.open_scope()
499

dbarbera's avatar
dbarbera committed
500
501
    entry_block = func.append_basic_block('input:entry')
    exit_block = func.append_basic_block('input:exit')
dbarbera's avatar
dbarbera committed
502
    ctx.builder = core.Builder.new(entry_block)
503

504
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('.state'))
dbarbera's avatar
dbarbera committed
505
    switch = ctx.builder.switch(g_state_val, exit_block)
506

dbarbera's avatar
dbarbera committed
507
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
508
        if state_name.endswith('start'):
dbarbera's avatar
dbarbera committed
509
            continue
dbarbera's avatar
dbarbera committed
510
        state_block = func.append_basic_block('input:state_%s' % str(state_name))
511
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
512
        ctx.builder.position_at_end(state_block)
513
514
515

        # TODO: Nested states

516
517
518
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
519
                var_ptr = ctx.scope.resolve(str(var_name))
dbarbera's avatar
dbarbera committed
520
                if is_struct_ptr(var_ptr) or is_array_ptr(var_ptr):
dbarbera's avatar
dbarbera committed
521
                    generate_assign(var_ptr, func.args[0], ctx)
522
                else:
dbarbera's avatar
dbarbera committed
523
                    generate_assign(var_ptr, ctx.builder.load(func.args[0]), ctx)
524
            if input.transition:
dbarbera's avatar
dbarbera committed
525
526
                id_val = core.Constant.int(ctx.i32, input.transition_id)
                ctx.builder.call(ctx.funcs['run_transition'], [id_val])
527

dbarbera's avatar
dbarbera committed
528
        ctx.builder.ret_void()
529

dbarbera's avatar
dbarbera committed
530
531
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
532

533
    ctx.close_scope()
534

535
536
537
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
538
539
@generate.register(ogAST.Output)
@generate.register(ogAST.ProcedureCall)
dbarbera's avatar
dbarbera committed
540
def _call_external_function(output, ctx):
dbarbera's avatar
dbarbera committed
541
    ''' Generate the IR for an output or procedure call '''
dbarbera's avatar
dbarbera committed
542
543
544
    for out in output.output:
        name = out['outputName'].lower()

545
        if name == 'write':
dbarbera's avatar
dbarbera committed
546
            generate_write(out['params'], ctx)
dbarbera's avatar
dbarbera committed
547
            continue
548
        elif name == 'writeln':
dbarbera's avatar
dbarbera committed
549
            generate_writeln(out['params'], ctx)
550
            continue
dbarbera's avatar
dbarbera committed
551
        elif name == 'reset_timer':
dbarbera's avatar
dbarbera committed
552
            generate_reset_timer(out['params'], ctx)
dbarbera's avatar
dbarbera committed
553
554
            continue
        elif name == 'set_timer':
dbarbera's avatar
dbarbera committed
555
            generate_set_timer(out['params'], ctx)
dbarbera's avatar
dbarbera committed
556
557
            continue

dbarbera's avatar
dbarbera committed
558
        func = ctx.funcs[str(name).lower()]
559
560
561

        params = []
        for p in out.get('params', []):
dbarbera's avatar
dbarbera committed
562
            p_val = expression(p, ctx)
563
564
            # Pass by reference
            if p_val.type.kind != core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
565
566
                p_var = ctx.builder.alloca(p_val.type, None)
                ctx.builder.store(p_val, p_var)
567
568
569
570
                params.append(p_var)
            else:
                params.append(p_val)

dbarbera's avatar
dbarbera committed
571
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
572
573


dbarbera's avatar
dbarbera committed
574
def generate_write(params, ctx):
dbarbera's avatar
dbarbera committed
575
    ''' Generate the IR for the write operator '''
576
    for param in params:
577
        basic_ty = ctx.basic_type_of(param.exprType)
dbarbera's avatar
dbarbera committed
578
        expr_val = expression(param, ctx)
579

580
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
581
582
            fmt_str_ptr = ctx.string_ptr('% d')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
583
        elif basic_ty.kind == 'RealType':
584
585
            fmt_str_ptr = ctx.string_ptr('% .14E')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
586
        elif basic_ty.kind == 'BooleanType':
587
588
            true_str_ptr = ctx.string_ptr('TRUE')
            false_str_ptr = ctx.string_ptr('FALSE')
dbarbera's avatar
dbarbera committed
589
590
            str_ptr = ctx.builder.select(expr_val, true_str_ptr, false_str_ptr)
            ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
591
        elif basic_ty.kind in ('StringType', 'StandardStringType'):
592
593
594
            fmt_str_ptr = ctx.string_ptr('%s')
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, expr_val])
        elif basic_ty.kind == 'OctetStringType':
595
            fmt_str_ptr = ctx.string_ptr('%.*s')
596
597
598
599
600
601
            if basic_ty.Min == basic_ty.Max:
                arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.zero])
                count_val = core.Constant.int(ctx.i32, arr_ptr.type.pointee.count)
            else:
                count_val = ctx.builder.load(ctx.builder.gep(expr_val, [ctx.zero, ctx.zero]))
                arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.one])
602
            ctx.builder.call(ctx.funcs['printf'], [fmt_str_ptr, count_val, arr_ptr])
603
604
605
606
        else:
            raise NotImplementedError


dbarbera's avatar
dbarbera committed
607
def generate_writeln(params, ctx):
dbarbera's avatar
dbarbera committed
608
    ''' Generate the IR for the writeln operator '''
dbarbera's avatar
dbarbera committed
609
    generate_write(params, ctx)
610

611
    str_ptr = ctx.string_ptr('\n')
dbarbera's avatar
dbarbera committed
612
    ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
613
614


dbarbera's avatar
dbarbera committed
615
def generate_reset_timer(params, ctx):
dbarbera's avatar
dbarbera committed
616
    ''' Generate the IR for the reset timer operator '''
dbarbera's avatar
dbarbera committed
617
    timer_id = params[0]
618
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
619
    reset_func = ctx.funcs[reset_func_name.lower()]
dbarbera's avatar
dbarbera committed
620

dbarbera's avatar
dbarbera committed
621
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
622
623


dbarbera's avatar
dbarbera committed
624
def generate_set_timer(params, ctx):
dbarbera's avatar
dbarbera committed
625
    ''' Generate the IR for the set timer operator '''
dbarbera's avatar
dbarbera committed
626
    timer_expr, timer_id = params
627
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
628
    set_func = ctx.funcs[set_func_name.lower()]
dbarbera's avatar
dbarbera committed
629

dbarbera's avatar
dbarbera committed
630
    expr_val = expression(timer_expr, ctx)
dbarbera's avatar
dbarbera committed
631

dbarbera's avatar
dbarbera committed
632
633
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
634

dbarbera's avatar
dbarbera committed
635
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
636
637
638


@generate.register(ogAST.TaskAssign)
dbarbera's avatar
dbarbera committed
639
def _task_assign(task, ctx):
dbarbera's avatar
dbarbera committed
640
    ''' Generate the IR for a list of assignments '''
641
    for expr in task.elems:
dbarbera's avatar
dbarbera committed
642
        expression(expr, ctx)
Maxime Perrotin's avatar
Maxime Perrotin committed
643
644
645


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


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


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

669
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
670

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

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

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

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

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

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

dbarbera's avatar
dbarbera committed
699
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
700

701
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
702
703


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

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

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

720
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
721

dbarbera's avatar
dbarbera committed
722
723
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
724
    seqof_struct_ptr = expression(loop['list'], ctx)
725
726
727

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

dbarbera's avatar
dbarbera committed
732
    element_typ = array_ptr.type.pointee.element
733
734
735

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

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

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

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

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

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

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

770
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
771

dbarbera's avatar
dbarbera committed
772

dbarbera's avatar
dbarbera committed
773
@singledispatch
dbarbera's avatar
dbarbera committed
774
def reference(prim, ctx):
dbarbera's avatar
dbarbera committed
775
    ''' Generate the IR for a reference '''
776
    raise CompileError('Unsupported reference "%s"' % prim.__class__.__name__)
dbarbera's avatar
dbarbera committed
777
778
779


@reference.register(ogAST.PrimVariable)
dbarbera's avatar
dbarbera committed
780
def _prim_var_reference(prim, ctx):
dbarbera's avatar
dbarbera committed
781
    ''' Generate the IR for a variable reference '''
dbarbera's avatar
dbarbera committed
782
    return ctx.scope.resolve(str(prim.value[0]))
dbarbera's avatar
dbarbera committed
783
784


785
@reference.register(ogAST.PrimSelector)
dbarbera's avatar
dbarbera committed
786
def _prim_selector_reference(prim, ctx):