LlvmGenerator.py 31.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
23
from llvm import core
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

33
34
35
36
37
38
# Global state
g = None


class GlobalState():
    def __init__(self, process):
dbarbera's avatar
dbarbera committed
39
40
        self.name = str(process.processName)
        self.module = core.Module.new(self.name)
41
42
        self.dataview = process.dataview

43
44
        self.scope = Scope()
        self.global_scope = self.scope
45
        self.states = {}
dbarbera's avatar
dbarbera committed
46
        self.structs = {}
47
        self.strings = {}
dbarbera's avatar
dbarbera committed
48
        self.funcs = {}
49
50
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)

        # Intialize built-in functions
        ty = core.Type.function(self.void, [core.Type.pointer(self.i8)], True)
dbarbera's avatar
dbarbera committed
65
        self.funcs['printf'] = self.module.add_function(ty, 'printf')
66

dbarbera's avatar
dbarbera committed
67
        self.funcs['memcpy'] = core.Function.intrinsic(
68
69
            self.module,
            core.INTR_MEMCPY,
70
71
            [self.i8_ptr, self.i8_ptr, self.i64]
        )
Maxime Perrotin's avatar
Maxime Perrotin committed
72

73
74
75
76
77
78
        self.funcs['powi'] = core.Function.intrinsic(
            self.module,
            core.INTR_POWI,
            [self.double]
        )

dbarbera's avatar
dbarbera committed
79

80
81
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
82
        self.name = name
83
84
85
86
87
        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
88
89


90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
class Scope:
    def __init__(self, parent=None):
        self.vars = {}
        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
105
            print name
106
107
            raise NameError

dbarbera's avatar
dbarbera committed
108

Maxime Perrotin's avatar
Maxime Perrotin committed
109
110
111
112
@singledispatch
def generate(ast):
    ''' Generate the code for an item of the AST '''
    raise TypeError('[Backend] Unsupported AST construct')
Maxime Perrotin's avatar
Maxime Perrotin committed
113

dbarbera's avatar
dbarbera committed
114

Maxime Perrotin's avatar
Maxime Perrotin committed
115
116
117
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
118
    ''' Generate LLVM IR code '''
dbarbera's avatar
dbarbera committed
119
120
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
121

122
123
    global g
    g = GlobalState(process)
124

dbarbera's avatar
dbarbera committed
125
126
127
128
129
130
    # 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
131

132
133
134
    # Initialize states enum
    for name in process.mapping.iterkeys():
        if not name.endswith('START'):
135
136
            cons = core.Constant.int(g.i32, len(g.states))
            g.states[name] = cons
137

138
    # Generate state var
139
140
    state_cons = g.module.add_global_variable(g.i32, 'state')
    state_cons.initializer = core.Constant.int(g.i32, -1)
141
    g.scope.define('state', state_cons)
142

143
144
145
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
        var_ty = _generate_type(ty)
146
        global_var = g.module.add_global_variable(var_ty, str(name))
147
        global_var.initializer = core.Constant.null(var_ty)
148
        g.scope.define(str(name).lower(), global_var)
149

dbarbera's avatar
dbarbera committed
150
151
152
    # Declare timer set/reset functions
    for timer in process.timers:
        # TODO: Should be uint?
153
154
        decl_func("set_%s" % str(timer), g.void, [g.i32_ptr], True)
        decl_func("reset_%s" % str(timer), g.void, [], True)
dbarbera's avatar
dbarbera committed
155

156
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
157
    for signal in process.output_signals:
158
159
160
161
        if 'type' in signal:
            param_tys = [core.Type.pointer(_generate_type(signal['type']))]
        else:
            param_tys = []
162
        decl_func(str(signal['name']), g.void, param_tys, True)
dbarbera's avatar
dbarbera committed
163

164
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
165
166
    for proc in [proc for proc in process.procedures if proc.external]:
        param_tys = [core.Type.pointer(_generate_type(p['type'])) for p in proc.fpar]
167
        decl_func(str(proc.inputString), g.void, param_tys, True)
dbarbera's avatar
dbarbera committed
168

169
170
    # Generate internal procedures
    for proc in process.content.inner_procedures:
171
        generate(proc)
172

173
    # Generate process functions
174
    _generate_runtr_func(process)
175
    _generate_startup_func(process)
176

177
178
179
180
    # Generate input signals
    for signal in process.input_signals:
        _generate_input_signal(signal, mapping[signal['name']])

dbarbera's avatar
dbarbera committed
181
182
    g.module.verify()

dbarbera's avatar
dbarbera committed
183
    with open(g.name + '.ll', 'w') as ll_file:
dbarbera's avatar
dbarbera committed
184
        ll_file.write(str(g.module))
185
186
187


def _generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
188
    ''' Generate code for the run_transition function '''
189
    func = decl_func('run_transition', g.void, [g.i32])
190

191
192
    _push_scope()

193
194
195
196
197
    entry_block = func.append_basic_block('entry')
    cond_block = func.append_basic_block('cond')
    body_block = func.append_basic_block('body')
    exit_block = func.append_basic_block('exit')

dbarbera's avatar
dbarbera committed
198
    g.builder = core.Builder.new(entry_block)
199
200

    # entry
dbarbera's avatar
dbarbera committed
201
    id_ptr = g.builder.alloca(g.i32, None, 'id')
202
    g.scope.define('id', id_ptr)
dbarbera's avatar
dbarbera committed
203
204
    g.builder.store(func.args[0], id_ptr)
    g.builder.branch(cond_block)
205
206

    # cond
dbarbera's avatar
dbarbera committed
207
    g.builder.position_at_end(cond_block)
208
    no_tr_cons = core.Constant.int(g.i32, -1)
209
210
    id_val = g.builder.load(id_ptr)
    cond_val = g.builder.icmp(core.ICMP_NE, id_val, no_tr_cons, 'cond')
dbarbera's avatar
dbarbera committed
211
    g.builder.cbranch(cond_val, body_block, exit_block)
212
213

    # body
dbarbera's avatar
dbarbera committed
214
215
    g.builder.position_at_end(body_block)
    switch = g.builder.switch(func.args[0], exit_block)
216
217
218
219

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
220
        const = core.Constant.int(g.i32, idx)
221
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
222
        g.builder.position_at_end(tr_block)
223
        generate(tr)
dbarbera's avatar
dbarbera committed
224
        g.builder.branch(cond_block)
225
226

    # exit
dbarbera's avatar
dbarbera committed
227
228
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
229

230
231
    _pop_scope()

232
233
234
235
    func.verify()
    return func


236
def _generate_startup_func(process):
dbarbera's avatar
dbarbera committed
237
    ''' Generate code for the startup function '''
238
    func = decl_func(g.name + '_startup', g.void, [])
239

240
241
    _push_scope()

242
    entry_block = func.append_basic_block('entry')
243
    g.builder = core.Builder.new(entry_block)
244

245
246
247
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
248
            global_var = g.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
249
            generate_assign(global_var, expression(expr))
250

251
252
    g.builder.call(g.funcs['run_transition'], [core.Constant.int(g.i32, 0)])
    g.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
253

254
255
    _pop_scope()

256
257
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
258
259


260
def _generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
261
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
262
    func_name = g.name + "_" + str(signal['name'])
263
264
265
    param_tys = []
    if 'type' in signal:
        param_tys.append(core.Type.pointer(_generate_type(signal['type'])))
266
267

    func = decl_func(func_name, g.void, param_tys)
268

269
270
    _push_scope()

271
272
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
273
    g.builder = core.Builder.new(entry_block)
274

275
    g_state_val = g.builder.load(g.global_scope.resolve('state'))
276
    switch = g.builder.switch(g_state_val, exit_block)
277

278
    for state_name, state_id in g.states.iteritems():
279
280
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
281
        g.builder.position_at_end(state_block)
282
283
284

        # TODO: Nested states

285
286
287
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
288
289
                var_ptr = g.scope.resolve(str(var_name))
                generate_assign(var_ptr, func.args[0])
290
            if input.transition:
291
                id_val = core.Constant.int(g.i32, input.transition_id)
292
                g.builder.call(g.funcs['run_transition'], [id_val])
293

294
        g.builder.ret_void()
295

296
297
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
298

299
300
    _pop_scope()

301
302
303
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
304
305
306
307
@generate.register(ogAST.Output)
@generate.register(ogAST.ProcedureCall)
def _call_external_function(output):
    ''' Generate the code of a set of output or procedure call statement '''
dbarbera's avatar
dbarbera committed
308
309
310
    for out in output.output:
        name = out['outputName'].lower()

311
        if name == 'write':
dbarbera's avatar
dbarbera committed
312
313
            _generate_write(out['params'])
            continue
314
315
316
        elif name == 'writeln':
            _generate_writeln(out['params'])
            continue
dbarbera's avatar
dbarbera committed
317
318
319
320
321
322
323
        elif name == 'reset_timer':
            _generate_reset_timer(out['params'])
            continue
        elif name == 'set_timer':
            _generate_set_timer(out['params'])
            continue

dbarbera's avatar
dbarbera committed
324
        func = g.funcs[str(name).lower()]
325
326
327
328
329
330
331
332
333
334
335
336
337

        params = []
        for p in out.get('params', []):
            p_val = expression(p)
            # Pass by reference
            if p_val.type.kind != core.TYPE_POINTER:
                p_var = g.builder.alloca(p_val.type, None)
                g.builder.store(p_val, p_var)
                params.append(p_var)
            else:
                params.append(p_val)

        g.builder.call(func, params)
dbarbera's avatar
dbarbera committed
338
339
340
341


def _generate_write(params):
    ''' Generate the code for the write operator '''
342
    zero = core.Constant.int(g.i32, 0)
343
344
345
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
346

347
        if basic_ty.kind == 'IntegerType':
348
            fmt_val = _get_string_cons('% d')
349
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
dbarbera's avatar
dbarbera committed
350
            g.builder.call(g.funcs['printf'], [fmt_ptr, expr_val])
351
        elif basic_ty.kind == 'RealType':
352
            fmt_val = _get_string_cons('% .14E')
353
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
dbarbera's avatar
dbarbera committed
354
            g.builder.call(g.funcs['printf'], [fmt_ptr, expr_val])
355
        elif basic_ty.kind == 'BooleanType':
356
            true_str_val = _get_string_cons('TRUE')
357
            true_str_ptr = g.builder.gep(true_str_val, [zero, zero])
358
            false_str_val = _get_string_cons('FALSE')
359
360
            false_str_ptr = g.builder.gep(false_str_val, [zero, zero])
            str_ptr = g.builder.select(expr_val, true_str_ptr, false_str_ptr)
dbarbera's avatar
dbarbera committed
361
            g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
362
363
        elif basic_ty.kind == 'StringType':
            expr_ptr = g.builder.gep(expr_val, [zero, zero])
dbarbera's avatar
dbarbera committed
364
            g.builder.call(g.funcs['printf'], [expr_ptr])
365
366
367
368
369
370
371
        else:
            raise NotImplementedError


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
372
373

    zero = core.Constant.int(g.i32, 0)
374
    str_cons = _get_string_cons('\n')
375
    str_ptr = g.builder.gep(str_cons, [zero, zero])
dbarbera's avatar
dbarbera committed
376
    g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
377
378
379
380


def _generate_reset_timer(params):
    ''' Generate the code for the reset timer operator '''
dbarbera's avatar
dbarbera committed
381
    timer_id = params[0]
382
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
383
384
385
    reset_func = g.funcs[reset_func_name.lower()]

    g.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
386
387
388
389


def _generate_set_timer(params):
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
390
    timer_expr, timer_id = params
391
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
392
393
394
395
396
397
398
399
    set_func = g.funcs[set_func_name.lower()]

    expr_val = expression(timer_expr)

    tmp_ptr = g.builder.alloca(expr_val.type)
    g.builder.store(expr_val, tmp_ptr)

    g.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
400
401
402
403


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
404
    ''' Generate the code of a list of assignments '''
405
406
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
407
408
409
410
411


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
412
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
413
414
415
416


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
417
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
    for loop in task.elems:
        if loop['range']:
            _generate_for_range(loop)
        else:
            _generate_for_iterable(loop)


def _generate_for_range(loop):
    ''' Generate the code for a for x in range loop '''
    func = g.builder.basic_block.function
    cond_block = func.append_basic_block('for:cond')
    body_block = func.append_basic_block('for:body')
    inc_block = func.append_basic_block('for:inc')
    end_block = func.append_basic_block('')

    _push_scope()

    loop_var = g.builder.alloca(g.i32, None, str(loop['var']))
    g.scope.define(str(loop['var']), loop_var)

    if loop['range']['start']:
        start_val = expression(loop['range']['start'])
        g.builder.store(start_val, loop_var)
    else:
        g.builder.store(core.Constant.int(g.i32, 0), loop_var)

    stop_val = expression(loop['range']['stop'])
    g.builder.branch(cond_block)

    g.builder.position_at_end(cond_block)
    loop_val = g.builder.load(loop_var)
    cond_val = g.builder.icmp(core.ICMP_SLT, loop_val, stop_val)
    g.builder.cbranch(cond_val, body_block, end_block)

    g.builder.position_at_end(body_block)
    generate(loop['transition'])
    g.builder.branch(inc_block)

    g.builder.position_at_end(inc_block)
    step_val = core.Constant.int(g.i32, loop['range']['step'])
    loop_val = g.builder.load(loop_var)
    temp_val = g.builder.add(loop_val, step_val)
    g.builder.store(temp_val, loop_var)
    g.builder.branch(cond_block)

    g.builder.position_at_end(end_block)

    _pop_scope()


def _generate_for_iterable(loop):
    ''' Generate the code for a for x in iterable loop'''
470
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
471

dbarbera's avatar
dbarbera committed
472

dbarbera's avatar
dbarbera committed
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
@singledispatch
def reference(prim):
    ''' Generate a variable reference '''
    raise TypeError('Unsupported reference: ' + str(expr))


@reference.register(ogAST.PrimVariable)
def _prim_var_reference(prim):
    ''' Generate a primary variable reference '''
    return g.scope.resolve(str(prim.value[0]))


@reference.register(ogAST.PrimPath)
def _prim_path_reference(prim):
    ''' Generate a primary path reference '''
    var_name = prim.value.pop(0).lower()
    var_ptr = g.scope.resolve(str(var_name))

    if not prim.value:
        return var_ptr

    zero_cons = core.Constant.int(g.i32, 0)

    for field_name in prim.value:
        var_ty = var_ptr.type
        struct = g.structs[var_ty.pointee.name]
        field_idx_cons = core.Constant.int(g.i32, struct.idx(field_name.lower()))
        field_ptr = g.builder.gep(var_ptr, [zero_cons, field_idx_cons])
        var_ptr = field_ptr

    return var_ptr

Maxime Perrotin's avatar
Maxime Perrotin committed
505
506
507

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
508
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
509
510
511
512
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
513
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
514
515
516
    ''' Generate the code for a variable expression '''
    var_ptr = reference(prim)
    return var_ptr if is_struct_ptr(var_ptr) else g.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
517
518


Maxime Perrotin's avatar
Maxime Perrotin committed
519
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
520
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
521
    ''' Generate the code for an of path expression '''
522
523
524
525
526
527
528
529
530
531
532
533
534
    specops_generators = {
        'length': generate_length,
        'present': generate_present,
        'abs': generate_abs,
        'fix': generate_fix,
        'float': generate_float,
        'power': generate_power
    }

    specop_generator = specops_generators.get(prim.value[0].lower())
    if specop_generator:
        return specop_generator(prim.value[1]['procParams'])

dbarbera's avatar
dbarbera committed
535
536
    var_ptr = reference(prim)
    return var_ptr if is_struct_ptr(var_ptr) else g.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
537
538


539
def generate_length(params):
dbarbera's avatar
dbarbera committed
540
    ''' Generate the code for the built-in length operation'''
541
542
543
544
    raise NotImplementedError


def generate_present(params):
dbarbera's avatar
dbarbera committed
545
    ''' Generate the code for the built-in present operation'''
546
547
548
549
    raise NotImplementedError


def generate_abs(params):
dbarbera's avatar
dbarbera committed
550
    ''' Generate the code for the built-in abs operation'''
551
552
553
554
    raise NotImplementedError


def generate_fix(params):
dbarbera's avatar
dbarbera committed
555
    ''' Generate the code for the built-in fix operation'''
556
557
558
559
    raise NotImplementedError


def generate_float(params):
dbarbera's avatar
dbarbera committed
560
    ''' Generate the code for the built-in float operation'''
561
562
563
564
    raise NotImplementedError


def generate_power(params):
dbarbera's avatar
dbarbera committed
565
    ''' Generate the code for the built-in power operation'''
566
567
568
569
570
571
572
573
574
575
576
    left_val = expression(params[0])
    right_val = expression(params[1])

    if left_val.type.kind == core.TYPE_INTEGER:
        left_conv = g.builder.sitofp(left_val, g.double)
        res_val = g.builder.call(g.funcs['powi'], [left_conv, right_val])
        return g.builder.fptosi(res_val, g.i32)
    else:
        return g.builder.call(g.funcs['powi'], [left_val, right_val])


Maxime Perrotin's avatar
Maxime Perrotin committed
577
578
579
580
581
582
583
584
585
586
587
588
@expression.register(ogAST.ExprPlus)
@expression.register(ogAST.ExprMul)
@expression.register(ogAST.ExprMinus)
@expression.register(ogAST.ExprEq)
@expression.register(ogAST.ExprNeq)
@expression.register(ogAST.ExprGt)
@expression.register(ogAST.ExprGe)
@expression.register(ogAST.ExprLt)
@expression.register(ogAST.ExprLe)
@expression.register(ogAST.ExprDiv)
@expression.register(ogAST.ExprMod)
@expression.register(ogAST.ExprRem)
dbarbera's avatar
dbarbera committed
589
590
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
591
592
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
593

594
595
    if lefttmp.type.kind == core.TYPE_INTEGER:
        if expr.operand == '+':
596
            return g.builder.add(lefttmp, righttmp, 'addtmp')
597
        elif expr.operand == '-':
598
            return g.builder.sub(lefttmp, righttmp, 'subtmp')
599
        elif expr.operand == '*':
600
            return g.builder.mul(lefttmp, righttmp, 'multmp')
601
        elif expr.operand == '/':
602
            return g.builder.sdiv(lefttmp, righttmp, 'divtmp')
603
        elif expr.operand == 'mod':
604
            # l mod r == (((l rem r) + r) rem r)
605
606
607
            remtmp = g.builder.srem(lefttmp, righttmp)
            addtmp = g.builder.add(remtmp, righttmp)
            return g.builder.srem(addtmp, righttmp, 'modtmp')
608
        elif expr.operand == 'rem':
609
            return g.builder.srem(lefttmp, righttmp, 'remtmp')
610
        elif expr.operand == '<':
611
            return g.builder.icmp(core.ICMP_SLT, lefttmp, righttmp, 'lttmp')
612
        elif expr.operand == '<=':
613
            return g.builder.icmp(core.ICMP_SLE, lefttmp, righttmp, 'letmp')
614
        elif expr.operand == '=':
615
            return g.builder.icmp(core.ICMP_EQ, lefttmp, righttmp, 'eqtmp')
616
        elif expr.operand == '/=':
617
            return g.builder.icmp(core.ICMP_NE, lefttmp, righttmp, 'netmp')
618
        elif expr.operand == '>=':
619
            return g.builder.icmp(core.ICMP_SGE, lefttmp, righttmp, 'getmp')
620
        elif expr.operand == '>':
621
            return g.builder.icmp(core.ICMP_SGT, lefttmp, righttmp, 'gttmp')
622
623
624
625
        else:
            raise NotImplementedError
    elif lefttmp.type.kind == core.TYPE_DOUBLE:
        if expr.operand == '+':
626
            return g.builder.fadd(lefttmp, righttmp, 'addtmp')
627
        elif expr.operand == '-':
628
            return g.builder.fsub(lefttmp, righttmp, 'subtmp')
629
        elif expr.operand == '*':
630
            return g.builder.fmul(lefttmp, righttmp, 'multmp')
631
        elif expr.operand == '/':
632
            return g.builder.fdiv(lefttmp, righttmp, 'divtmp')
633
        elif expr.operand == '<':
634
            return g.builder.fcmp(core.FCMP_OLT, lefttmp, righttmp, 'lttmp')
635
        elif expr.operand == '<=':
636
            return g.builder.fcmp(core.FCMP_OLE, lefttmp, righttmp, 'letmp')
637
        elif expr.operand == '=':
638
            return g.builder.fcmp(core.FCMP_OEQ, lefttmp, righttmp, 'eqtmp')
639
        elif expr.operand == '/=':
640
            return g.builder.fcmp(core.FCMP_ONE, lefttmp, righttmp, 'netmp')
641
        elif expr.operand == '>=':
642
            return g.builder.fcmp(core.FCMP_OGE, lefttmp, righttmp, 'getmp')
643
        elif expr.operand == '>':
644
            return g.builder.fcmp(core.FCMP_OGT, lefttmp, righttmp, 'gttmp')
645
646
        else:
            raise NotImplementedError
647
    else:
648
        raise NotImplementedError
649

Maxime Perrotin's avatar
Maxime Perrotin committed
650

651
652
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
653
    ''' Generate the code for an assign expression '''
dbarbera's avatar
dbarbera committed
654
    generate_assign(reference(expr.left), expression(expr.right))
655

656

dbarbera's avatar
dbarbera committed
657
def generate_assign(left, right):
658
659
660
    ''' Generate code for an assign from two LLVM values'''
    # This is extracted as an standalone function because is used by
    # multiple generation rules
dbarbera's avatar
dbarbera committed
661
    if is_struct_ptr(left):
662
663
        size = core.Constant.sizeof(left.type.pointee)
        align = core.Constant.int(g.i32, 0)
664
        volatile = core.Constant.int(g.i1, 0)
665

666
667
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
668

dbarbera's avatar
dbarbera committed
669
        g.builder.call(g.funcs['memcpy'], [left_ptr, right_ptr, size, align, volatile])
670
    else:
671
        g.builder.store(right, left)
dbarbera's avatar
dbarbera committed
672

Maxime Perrotin's avatar
Maxime Perrotin committed
673

Maxime Perrotin's avatar
Maxime Perrotin committed
674
675
676
@expression.register(ogAST.ExprOr)
@expression.register(ogAST.ExprAnd)
@expression.register(ogAST.ExprXor)
dbarbera's avatar
dbarbera committed
677
678
def _logical(expr):
    ''' Generate the code for a logical expression '''
dbarbera's avatar
dbarbera committed
679
680
681
682
683
684
685
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)

    ty = find_basic_type(expr.exprType)
    if ty.kind != 'BooleanType':
        raise NotImplementedError

dbarbera's avatar
dbarbera committed
686
687
688
    if expr.operand == 'and':
        return g.builder.and_(lefttmp, righttmp, 'andtmp')
    elif expr.operand == 'or':
689
        return g.builder.or_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
690
    else:
691
        return g.builder.xor(lefttmp, righttmp, 'xortmp')
dbarbera's avatar
dbarbera committed
692

Maxime Perrotin's avatar
Maxime Perrotin committed
693

Maxime Perrotin's avatar
Maxime Perrotin committed
694
@expression.register(ogAST.ExprAppend)
Maxime Perrotin's avatar
Maxime Perrotin committed
695
696
def _append(expr):
    ''' Generate code for the APPEND construct: a // b '''
697
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
698
699


Maxime Perrotin's avatar
Maxime Perrotin committed
700
@expression.register(ogAST.ExprIn)
Maxime Perrotin's avatar
Maxime Perrotin committed
701
def _expr_in(expr):
dbarbera's avatar
dbarbera committed
702
    ''' Generate the code for an in expression '''
703
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
704
705


Maxime Perrotin's avatar
Maxime Perrotin committed
706
@expression.register(ogAST.PrimEnumeratedValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
707
708
def _enumerated_value(primary):
    ''' Generate code for an enumerated value '''
dbarbera's avatar
dbarbera committed
709
710
711
    enumerant = primary.value[0].replace('_', '-')
    basic_ty = find_basic_type(primary.exprType)
    return core.Constant.int(g.i32, basic_ty.EnumValues[enumerant].IntValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
712
713


Maxime Perrotin's avatar
Maxime Perrotin committed
714
@expression.register(ogAST.PrimChoiceDeterminant)
Maxime Perrotin's avatar
Maxime Perrotin committed
715
716
def _choice_determinant(primary):
    ''' Generate code for a choice determinant (enumerated) '''
717
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
718
719


Maxime Perrotin's avatar
Maxime Perrotin committed
720
@expression.register(ogAST.PrimInteger)
721
722
def _integer(primary):
    ''' Generate code for a raw integer value  '''
723
    return core.Constant.int(g.i32, primary.value[0])
724
725


Maxime Perrotin's avatar
Maxime Perrotin committed
726
@expression.register(ogAST.PrimReal)
727
728
def _real(primary):
    ''' Generate code for a raw real value  '''
729
    return core.Constant.real(g.double, primary.value[0])
730
731


Maxime Perrotin's avatar
Maxime Perrotin committed
732
@expression.register(ogAST.PrimBoolean)
733
734
def _boolean(primary):
    ''' Generate code for a raw boolean value  '''
dbarbera's avatar
dbarbera committed
735
    if primary.value[0].lower() == 'true':
736
        return core.Constant.int(g.i1, 1)
dbarbera's avatar
dbarbera committed
737
    else:
738
        return core.Constant.int(g.i1, 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
739
740


Maxime Perrotin's avatar
Maxime Perrotin committed
741
@expression.register(ogAST.PrimEmptyString)
Maxime Perrotin's avatar
Maxime Perrotin committed
742
743
def _empty_string(primary):
    ''' Generate code for an empty SEQUENCE OF: {} '''
744
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
745
746


Maxime Perrotin's avatar
Maxime Perrotin committed
747
@expression.register(ogAST.PrimStringLiteral)
Maxime Perrotin's avatar
Maxime Perrotin committed
748
749
def _string_literal(primary):
    ''' Generate code for a string (Octet String) '''
dbarbera's avatar
dbarbera committed
750
    return _get_string_cons(str(primary.value[1:-1]))
Maxime Perrotin's avatar
Maxime Perrotin committed
751
752


Maxime Perrotin's avatar
Maxime Perrotin committed
753
@expression.register(ogAST.PrimConstant)
Maxime Perrotin's avatar
Maxime Perrotin committed
754
755
def _constant(primary):
    ''' Generate code for a reference to an ASN.1 constant '''
756
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
757
758


Maxime Perrotin's avatar
Maxime Perrotin committed
759
@expression.register(ogAST.PrimMantissaBaseExp)
Maxime Perrotin's avatar
Maxime Perrotin committed
760
761
def _mantissa_base_exp(primary):
    ''' Generate code for a Real with Mantissa-base-Exponent representation '''
762
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
763
764


Maxime Perrotin's avatar
Maxime Perrotin committed
765
@expression.register(ogAST.PrimIfThenElse)
dbarbera's avatar
dbarbera committed
766
def _if_then_else(ifthen):
dbarbera's avatar
dbarbera committed
767
    ''' Generate the code for ternary operator '''
768
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
769
770


Maxime Perrotin's avatar
Maxime Perrotin committed
771
@expression.register(ogAST.PrimSequence)
Maxime Perrotin's avatar
Maxime Perrotin committed
772
def _sequence(seq):
dbarbera's avatar
dbarbera committed
773
    ''' Generate the code for an ASN.1 SEQUENCE '''
dbarbera's avatar
dbarbera committed
774
775
776
777
778
779
780
781
782
783
784
    struct = g.structs[seq.exprType.ReferencedTypeName]
    struct_ptr = g.builder.alloca(struct.ty)
    zero_cons = core.Constant.int(g.i32, 0)

    for field_name, field_expr in seq.value.viewitems():
        field_val = expression(field_expr)
        field_idx_cons = core.Constant.int(g.i32, struct.idx(field_name))
        field_ptr = g.builder.gep(struct_ptr, [zero_cons, field_idx_cons])
        g.builder.store(field_val, field_ptr)

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
785
786


Maxime Perrotin's avatar
Maxime Perrotin committed
787
@expression.register(ogAST.PrimSequenceOf)
Maxime Perrotin's avatar
Maxime Perrotin committed
788
def _sequence_of(seqof):
dbarbera's avatar
dbarbera committed
789
    ''' Generate the code for an ASN.1 SEQUENCE OF '''
790
    ty = _generate_type(seqof.exprType)
791
792
793
    struct_ptr = g.builder.alloca(ty)
    zero_cons = core.Constant.int(g.i32, 0)
    array_ptr = g.builder.gep(struct_ptr, [zero_cons, zero_cons])
794
795

    for idx, expr in enumerate(seqof.value):
796
        idx_cons = core.Constant.int(g.i32, idx)
797
        expr_val = expression(expr)
798
799
        pos_ptr = g.builder.gep(array_ptr, [zero_cons, idx_cons])
        g.builder.store(expr_val, pos_ptr)
800
801

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
802
803


Maxime Perrotin's avatar
Maxime Perrotin committed
804
@expression.register(ogAST.PrimChoiceItem)
Maxime Perrotin's avatar
Maxime Perrotin committed
805
def _choiceitem(choice):
dbarbera's avatar
dbarbera committed
806
    ''' Generate the code for a CHOICE expression '''
807
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
808
809
810
811


@generate.register(ogAST.Decision)
def _decision(dec):
dbarbera's avatar
dbarbera committed
812
    ''' Generate the code for a decision '''
813
    func = g.builder.basic_block.function
dbarbera's avatar
dbarbera committed
814
815
816
817

    ans_cond_blocks = [func.append_basic_block('ans_cond') for ans in dec.answers]
    end_block = func.append_basic_block('end')

818
    g.builder.branch(ans_cond_blocks[0])
dbarbera's avatar
dbarbera committed
819
820
821
822
823

    for idx, ans in enumerate(dec.answers):
        ans_cond_block = ans_cond_blocks[idx]
        if ans.transition:
            ans_tr_block = func.append_basic_block('ans_tr')
824
        g.builder.position_at_end(ans_cond_block)
dbarbera's avatar
dbarbera committed
825
826

        if ans.kind == 'constant':
dbarbera's avatar
dbarbera committed
827
            next_block = ans_cond_blocks[idx+1] if idx < len(ans_cond_blocks)-1 else end_block
dbarbera's avatar
dbarbera committed
828
829
830
831
832
833

            expr = ans.openRangeOp()
            expr.left = dec.question
            expr.right = ans.constant
            expr_val = expression(expr)

834
835
836
            true_cons = core.Constant.int(g.i1, 1)
            cond_val = g.builder.icmp(core.ICMP_EQ, expr_val, true_cons)
            g.builder.cbranch(cond_val, ans_tr_block if ans.transition else end_block, next_block)
dbarbera's avatar
dbarbera committed
837
838
        elif ans.kind == 'else':
            if ans.transition:
839
                g.builder.branch(ans_tr_block)
dbarbera's avatar
dbarbera committed
840
            else:
841
                g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
842
843
844
845
        else:
            raise NotImplementedError

        if ans.transition:
846
            g.builder.position_at_end(ans_tr_block)
dbarbera's avatar
dbarbera committed
847
            generate(ans.transition)
848
            g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
849

850
    g.builder.position_at_end(end_block)
Maxime Perrotin's avatar
Maxime Perrotin committed
851
852
853
854


@generate.register(ogAST.Label)
def _label(tr):
dbarbera's avatar
dbarbera committed
855
    ''' TGenerate the code for a Label '''
856
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
857
858
859
860


@generate.register(ogAST.Transition)
def _transition(tr):
dbarbera's avatar
dbarbera committed
861
    ''' Generate the code for a transition '''
862
863
864
865
866
867
    for action in tr.actions:
        generate(action)
        if isinstance(action, ogAST.Label):
            return
    if tr.terminator:
        _generate_terminator(tr.terminator)
868
869


870
def _generate_terminator(term):
dbarbera's avatar
dbarbera committed
871
    ''' Generate the code for a transition termiantor '''
872
    if term.label:
873
        raise NotImplementedError
874
875
876
    if term.kind == 'next_state':
        state = term.inputString.lower()
        if state.strip() != '-':
877
            next_id_cons = core.Constant.int(g.i32, term.next_id)
878
            g.builder.store(next_id_cons, g.scope.resolve('id'))
879
            if term.next_id == -1:
880
                state_ptr = g.global_scope.resolve('state')
881
882
                state_id_cons = g.states[state]
                g.builder.store(state_id_cons, state_ptr)
883
        else:
884
            raise NotImplementedError
885
    elif term.kind == 'join':
886
        raise NotImplementedError
887
    elif term.kind == 'stop':
888
        raise NotImplementedError
889
    elif term.kind == 'return':
890
891
892
893
894
895
896
897
        if term.next_id == -1 and term.return_expr:
            g.builder.ret(expression(term.return_expr))
        elif term.next_id == -1:
            g.builder.ret_void()
        else:
            next_id_cons = core.Constant.int(g.i32, term.next_id)
            g.builder.store(next_id_cons, g.scope.resolve('id'))
            g.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
898
899
900
901


@generate.register(ogAST.Floating_label)
def _floating_label(label):
dbarbera's avatar
dbarbera committed
902
    ''' Generate the code for a floating label '''
903
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
904
905
906
907
908


@generate.register(ogAST.Procedure)
def _inner_procedure(proc):
    ''' Generate the code for a procedure '''
909
    param_tys = [core.Type.pointer(_generate_type(p['type'])) for p in proc.fpar]
910
    func = decl_func(str(proc.inputString), g.void, param_tys)
911
912
913
914
915
916
917
918
919
920
921