LlvmGenerator.py 31.6 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
70
            self.module, core.INTR_MEMCPY,
            [self.i8_ptr, self.i8_ptr, self.i64]
        )
Maxime Perrotin's avatar
Maxime Perrotin committed
71

dbarbera's avatar
dbarbera committed
72

73
74
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
75
        self.name = name
76
77
78
79
80
        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
81
82


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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
98
            print name
99
100
            raise NameError

dbarbera's avatar
dbarbera committed
101

Maxime Perrotin's avatar
Maxime Perrotin committed
102
103
104
105
@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
106

dbarbera's avatar
dbarbera committed
107

Maxime Perrotin's avatar
Maxime Perrotin committed
108
109
110
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
111
    ''' Generate LLVM IR code '''
dbarbera's avatar
dbarbera committed
112
113
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
114

115
116
    global g
    g = GlobalState(process)
117

dbarbera's avatar
dbarbera committed
118
119
120
121
122
123
    # 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
124

125
126
127
    # Initialize states enum
    for name in process.mapping.iterkeys():
        if not name.endswith('START'):
128
129
            cons = core.Constant.int(g.i32, len(g.states))
            g.states[name] = cons
130

131
    # Generate state var
132
133
    state_cons = g.module.add_global_variable(g.i32, 'state')
    state_cons.initializer = core.Constant.int(g.i32, -1)
134
    g.scope.define('state', state_cons)
135

136
137
138
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
        var_ty = _generate_type(ty)
139
        global_var = g.module.add_global_variable(var_ty, str(name))
140
        global_var.initializer = core.Constant.null(var_ty)
141
        g.scope.define(str(name).lower(), global_var)
142

dbarbera's avatar
dbarbera committed
143
144
145
146
147
148
149
150
    # Declare timer set/reset functions
    for timer in process.timers:
        func_name = '%s_RI_set_%s' % (process_name, str(timer))
        # TODO: Should be uint?
        decl_func(func_name, g.void, [g.i32_ptr])
        func_name = '%s_RI_reset_%s' % (process_name, str(timer))
        decl_func(func_name, g.void, [])

dbarbera's avatar
dbarbera committed
151
152
    # Initialize output signals
    for signal in process.output_signals:
153
154
155
156
        if 'type' in signal:
            param_tys = [core.Type.pointer(_generate_type(signal['type']))]
        else:
            param_tys = []
157
        func_ty = core.Type.function(g.void, param_tys)
dbarbera's avatar
dbarbera committed
158
159
160
        func_name = str(signal['name'])
        func = core.Function.new(g.module, func_ty, func_name)
        g.funcs[func_name.lower()] = func
dbarbera's avatar
dbarbera committed
161
162
163
164

    # Initialize external procedures
    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]
165
        func_ty = core.Type.function(g.void, param_tys)
dbarbera's avatar
dbarbera committed
166
167
168
        func_name = str(proc.inputString)
        func = core.Function.new(g.module, func_ty, func_name)
        g.funcs[func_name.lower()] = func
dbarbera's avatar
dbarbera committed
169

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

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

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

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

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


def _generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
189
    ''' Generate code for the run_transition function '''
190
    func_name = 'run_transition'
191
192
    func_type = core.Type.function(g.void, [g.i32])
    func = core.Function.new(g.module, func_type, func_name)
193
    g.funcs[func_name] = func
194

195
196
    _push_scope()

197
198
199
200
201
    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
202
    g.builder = core.Builder.new(entry_block)
203
204

    # entry
dbarbera's avatar
dbarbera committed
205
    id_ptr = g.builder.alloca(g.i32, None, 'id')
206
    g.scope.define('id', id_ptr)
dbarbera's avatar
dbarbera committed
207
208
    g.builder.store(func.args[0], id_ptr)
    g.builder.branch(cond_block)
209
210

    # cond
dbarbera's avatar
dbarbera committed
211
    g.builder.position_at_end(cond_block)
212
    no_tr_cons = core.Constant.int(g.i32, -1)
213
214
    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
215
    g.builder.cbranch(cond_val, body_block, exit_block)
216
217

    # body
dbarbera's avatar
dbarbera committed
218
219
    g.builder.position_at_end(body_block)
    switch = g.builder.switch(func.args[0], exit_block)
220
221
222
223

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

    # exit
dbarbera's avatar
dbarbera committed
231
232
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
233

234
235
    _pop_scope()

236
237
238
239
    func.verify()
    return func


240
def _generate_startup_func(process):
dbarbera's avatar
dbarbera committed
241
    ''' Generate code for the startup function '''
dbarbera's avatar
dbarbera committed
242
    func_name = g.name + '_startup'
243
244
    func_type = core.Type.function(g.void, [])
    func = core.Function.new(g.module, func_type, func_name)
245
    g.funcs[func_name] = func
246

247
248
    _push_scope()

249
    entry_block = func.append_basic_block('entry')
250
    g.builder = core.Builder.new(entry_block)
251

252
253
254
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
255
            global_var = _resolve(str(name))
256
257
            _generate_assign(global_var, expression(expr))

258
259
    g.builder.call(g.funcs['run_transition'], [core.Constant.int(g.i32, 0)])
    g.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
260

261
262
    _pop_scope()

263
264
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
265
266


267
def _generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
268
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
269
    func_name = g.name + "_" + str(signal['name'])
270
271
272
    param_tys = []
    if 'type' in signal:
        param_tys.append(core.Type.pointer(_generate_type(signal['type'])))
273
274
    func_type = core.Type.function(g.void, param_tys)
    func = core.Function.new(g.module, func_type, func_name)
dbarbera's avatar
dbarbera committed
275
    g.funcs[func_name.lower()] = func
276

277
278
    _push_scope()

279
280
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
281
    g.builder = core.Builder.new(entry_block)
282

283
    g_state_val = g.builder.load(g.global_scope.resolve('state'))
284
    switch = g.builder.switch(g_state_val, exit_block)
285

286
    for state_name, state_id in g.states.iteritems():
287
288
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
289
        g.builder.position_at_end(state_block)
290
291
292

        # TODO: Nested states

293
294
295
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
296
                var_val = g.scope.resolve(str(var_name))
297
298
                _generate_assign(var_val, func.args[0])
            if input.transition:
299
                id_val = core.Constant.int(g.i32, input.transition_id)
300
                g.builder.call(g.funcs['run_transition'], [id_val])
301

302
        g.builder.ret_void()
303

304
305
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
306

307
308
    _pop_scope()

309
310
311
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
312
313
314
315
@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 '''
316
    zero = core.Constant.int(g.i32, 0)
dbarbera's avatar
dbarbera committed
317
318
319
    for out in output.output:
        name = out['outputName'].lower()

320
        if name == 'write':
dbarbera's avatar
dbarbera committed
321
322
            _generate_write(out['params'])
            continue
323
324
325
        elif name == 'writeln':
            _generate_writeln(out['params'])
            continue
dbarbera's avatar
dbarbera committed
326
327
328
329
330
331
332
        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
333
        func = g.funcs[str(name).lower()]
334
335
336
337
338
339
340
341
342
343
344
345
346

        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
347
348
349
350


def _generate_write(params):
    ''' Generate the code for the write operator '''
351
    zero = core.Constant.int(g.i32, 0)
352
353
354
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
355
356
357
358

        if basic_ty.kind != 'StringType' and expr_val.type.kind == core.TYPE_POINTER:
            expr_val = g.builder.load(expr_val)

359
        if basic_ty.kind == 'IntegerType':
360
            fmt_val = _get_string_cons('% d')
361
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
dbarbera's avatar
dbarbera committed
362
            g.builder.call(g.funcs['printf'], [fmt_ptr, expr_val])
363
        elif basic_ty.kind == 'RealType':
364
            fmt_val = _get_string_cons('% .14E')
365
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
dbarbera's avatar
dbarbera committed
366
            g.builder.call(g.funcs['printf'], [fmt_ptr, expr_val])
367
        elif basic_ty.kind == 'BooleanType':
368
            true_str_val = _get_string_cons('TRUE')
369
            true_str_ptr = g.builder.gep(true_str_val, [zero, zero])
370
            false_str_val = _get_string_cons('FALSE')
371
372
            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
373
            g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
374
375
        elif basic_ty.kind == 'StringType':
            expr_ptr = g.builder.gep(expr_val, [zero, zero])
dbarbera's avatar
dbarbera committed
376
            g.builder.call(g.funcs['printf'], [expr_ptr])
377
378
379
380
381
382
383
        else:
            raise NotImplementedError


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
384
385

    zero = core.Constant.int(g.i32, 0)
386
    str_cons = _get_string_cons('\n')
387
    str_ptr = g.builder.gep(str_cons, [zero, zero])
dbarbera's avatar
dbarbera committed
388
    g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
389
390
391
392


def _generate_reset_timer(params):
    ''' Generate the code for the reset timer operator '''
dbarbera's avatar
dbarbera committed
393
394
395
396
397
    timer_id = params[0]
    reset_func_name = '%s_RI_reset_%s' % (g.name, timer_id.value[0])
    reset_func = g.funcs[reset_func_name.lower()]

    g.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
398
399
400
401


def _generate_set_timer(params):
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
402
403
404
405
406
407
408
409
410
411
412
413
414
    timer_expr, timer_id = params
    set_func_name = '%s_RI_set_%s' % (g.name, timer_id.value[0])
    set_func = g.funcs[set_func_name.lower()]

    expr_val = expression(timer_expr)

    if type(timer_expr) in [ogAST.PrimPath, ogAST.PrimVariable]:
        expr_val = g.builder.load(expr_val)

    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
415
416
417
418


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
419
    ''' Generate the code of a list of assignments '''
420
421
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
422
423
424
425
426


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
427
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
428
429
430
431


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
432
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
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
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
    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'])
        if type(loop['range']['start']) in [ogAST.PrimPath, ogAST.PrimVariable]:
            start_val = g.builder.load(start_val)
        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'])
    if type(loop['range']['stop']) in [ogAST.PrimPath, ogAST.PrimVariable]:
        stop_val = g.builder.load(stop_val)
    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'''
489
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
490

dbarbera's avatar
dbarbera committed
491

Maxime Perrotin's avatar
Maxime Perrotin committed
492
493
494
495
# ------ expressions --------

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
496
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
497
498
499
500
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
501
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
502
    ''' Generate the code for a single variable reference '''
503
    return g.scope.resolve(str(prim.value[0]))
Maxime Perrotin's avatar
Maxime Perrotin committed
504
505


Maxime Perrotin's avatar
Maxime Perrotin committed
506
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
507
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
508
    ''' Generate the code for an of an element list (path) '''
dbarbera's avatar
dbarbera committed
509

510
    var_ptr = g.scope.resolve(str(prim.value.pop(0)))
dbarbera's avatar
dbarbera committed
511
512

    if not prim.value:
513
        return var_ptr
dbarbera's avatar
dbarbera committed
514
515
516
517
518
519
520
521
522
523
524
525
526

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

    for field_name in prim.value:
        var_ty = var_ptr.type
        if var_ty.kind == core.TYPE_POINTER and var_ty.pointee.kind == core.TYPE_STRUCT:
            struct = g.structs[var_ty.pointee.name]
            field_idx_cons = core.Constant.int(g.i32, struct.idx(field_name))
            field_ptr = g.builder.gep(var_ptr, [zero_cons, field_idx_cons])
            var_ptr = field_ptr
        else:
            raise NotImplementedError

527
    return var_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
528
529


Maxime Perrotin's avatar
Maxime Perrotin committed
530
531
532
533
534
535
536
537
538
539
540
541
@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
542
543
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
544
545
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
546
547

    # load the value of the expression if it is a pointer
548
    if lefttmp.type.kind == core.TYPE_POINTER:
549
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
550
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
551
        righttmp = g.builder.load(righttmp, 'righttmp')
552
553
554
555
556
557

    if lefttmp.type.kind != righttmp.type.kind:
        raise NotImplementedError

    if lefttmp.type.kind == core.TYPE_INTEGER:
        if expr.operand == '+':
558
            return g.builder.add(lefttmp, righttmp, 'addtmp')
559
        elif expr.operand == '-':
560
            return g.builder.sub(lefttmp, righttmp, 'subtmp')
561
        elif expr.operand == '*':
562
            return g.builder.mul(lefttmp, righttmp, 'multmp')
563
        elif expr.operand == '/':
564
            return g.builder.sdiv(lefttmp, righttmp, 'divtmp')
565
        elif expr.operand == 'mod':
566
            # l mod r == (((l rem r) + r) rem r)
567
568
569
            remtmp = g.builder.srem(lefttmp, righttmp)
            addtmp = g.builder.add(remtmp, righttmp)
            return g.builder.srem(addtmp, righttmp, 'modtmp')
570
        elif expr.operand == 'rem':
571
            return g.builder.srem(lefttmp, righttmp, 'remtmp')
572
        elif expr.operand == '<':
573
            return g.builder.icmp(core.ICMP_SLT, lefttmp, righttmp, 'lttmp')
574
        elif expr.operand == '<=':
575
            return g.builder.icmp(core.ICMP_SLE, lefttmp, righttmp, 'letmp')
576
        elif expr.operand == '=':
577
            return g.builder.icmp(core.ICMP_EQ, lefttmp, righttmp, 'eqtmp')
578
        elif expr.operand == '/=':
579
            return g.builder.icmp(core.ICMP_NE, lefttmp, righttmp, 'netmp')
580
        elif expr.operand == '>=':
581
            return g.builder.icmp(core.ICMP_SGE, lefttmp, righttmp, 'getmp')
582
        elif expr.operand == '>':
583
            return g.builder.icmp(core.ICMP_SGT, lefttmp, righttmp, 'gttmp')
584
585
586
587
        else:
            raise NotImplementedError
    elif lefttmp.type.kind == core.TYPE_DOUBLE:
        if expr.operand == '+':
588
            return g.builder.fadd(lefttmp, righttmp, 'addtmp')
589
        elif expr.operand == '-':
590
            return g.builder.fsub(lefttmp, righttmp, 'subtmp')
591
        elif expr.operand == '*':
592
            return g.builder.fmul(lefttmp, righttmp, 'multmp')
593
        elif expr.operand == '/':
594
            return g.builder.fdiv(lefttmp, righttmp, 'divtmp')
595
        elif expr.operand == '<':
596
            return g.builder.fcmp(core.FCMP_OLT, lefttmp, righttmp, 'lttmp')
597
        elif expr.operand == '<=':
598
            return g.builder.fcmp(core.FCMP_OLE, lefttmp, righttmp, 'letmp')
599
        elif expr.operand == '=':
600
            return g.builder.fcmp(core.FCMP_OEQ, lefttmp, righttmp, 'eqtmp')
601
        elif expr.operand == '/=':
602
            return g.builder.fcmp(core.FCMP_ONE, lefttmp, righttmp, 'netmp')
603
        elif expr.operand == '>=':
604
            return g.builder.fcmp(core.FCMP_OGE, lefttmp, righttmp, 'getmp')
605
        elif expr.operand == '>':
606
            return g.builder.fcmp(core.FCMP_OGT, lefttmp, righttmp, 'gttmp')
607
608
        else:
            raise NotImplementedError
609
    else:
610
        raise NotImplementedError
611

Maxime Perrotin's avatar
Maxime Perrotin committed
612

613
614
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
615
    ''' Generate the code for an assign expression '''
616
617
    left = expression(expr.left)
    right = expression(expr.right)
618

619
620
    _generate_assign(left, right)
    return left
621

622
623
624
625
626

def _generate_assign(left, right):
    ''' Generate code for an assign from two LLVM values'''
    # This is extracted as an standalone function because is used by
    # multiple generation rules
627
    if left.type.pointee.kind == core.TYPE_STRUCT:
628
629
        size = core.Constant.sizeof(left.type.pointee)
        align = core.Constant.int(g.i32, 0)
630
        volatile = core.Constant.int(g.i1, 0)
631

632
633
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
634

dbarbera's avatar
dbarbera committed
635
        g.builder.call(g.funcs['memcpy'], [left_ptr, right_ptr, size, align, volatile])
636
    else:
637
638
        if right.type.kind == core.TYPE_POINTER:
            right = g.builder.load(right)
639
        g.builder.store(right, left)
dbarbera's avatar
dbarbera committed
640

Maxime Perrotin's avatar
Maxime Perrotin committed
641

Maxime Perrotin's avatar
Maxime Perrotin committed
642
643
644
@expression.register(ogAST.ExprOr)
@expression.register(ogAST.ExprAnd)
@expression.register(ogAST.ExprXor)
dbarbera's avatar
dbarbera committed
645
646
def _logical(expr):
    ''' Generate the code for a logical expression '''
dbarbera's avatar
dbarbera committed
647
648
649
650
651
652
653
654
655
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)

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

    # load the value of the expression if it is a pointer
    if lefttmp.type.kind == core.TYPE_POINTER:
656
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
dbarbera's avatar
dbarbera committed
657
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
658
        righttmp = g.builder.load(righttmp, 'righttmp')
dbarbera's avatar
dbarbera committed
659

dbarbera's avatar
dbarbera committed
660
661
662
    if expr.operand == 'and':
        return g.builder.and_(lefttmp, righttmp, 'andtmp')
    elif expr.operand == 'or':
663
        return g.builder.or_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
664
    else:
665
        return g.builder.xor(lefttmp, righttmp, 'xortmp')
dbarbera's avatar
dbarbera committed
666

Maxime Perrotin's avatar
Maxime Perrotin committed
667

Maxime Perrotin's avatar
Maxime Perrotin committed
668
@expression.register(ogAST.ExprAppend)
Maxime Perrotin's avatar
Maxime Perrotin committed
669
670
def _append(expr):
    ''' Generate code for the APPEND construct: a // b '''
671
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
672
673


Maxime Perrotin's avatar
Maxime Perrotin committed
674
@expression.register(ogAST.ExprIn)
Maxime Perrotin's avatar
Maxime Perrotin committed
675
def _expr_in(expr):
dbarbera's avatar
dbarbera committed
676
    ''' Generate the code for an in expression '''
677
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
678
679


Maxime Perrotin's avatar
Maxime Perrotin committed
680
@expression.register(ogAST.PrimEnumeratedValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
681
682
def _enumerated_value(primary):
    ''' Generate code for an enumerated value '''
dbarbera's avatar
dbarbera committed
683
684
685
    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
686
687


Maxime Perrotin's avatar
Maxime Perrotin committed
688
@expression.register(ogAST.PrimChoiceDeterminant)
Maxime Perrotin's avatar
Maxime Perrotin committed
689
690
def _choice_determinant(primary):
    ''' Generate code for a choice determinant (enumerated) '''
691
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
692
693


Maxime Perrotin's avatar
Maxime Perrotin committed
694
@expression.register(ogAST.PrimInteger)
695
696
def _integer(primary):
    ''' Generate code for a raw integer value  '''
697
    return core.Constant.int(g.i32, primary.value[0])
698
699


Maxime Perrotin's avatar
Maxime Perrotin committed
700
@expression.register(ogAST.PrimReal)
701
702
def _real(primary):
    ''' Generate code for a raw real value  '''
703
    return core.Constant.real(g.double, primary.value[0])
704
705


Maxime Perrotin's avatar
Maxime Perrotin committed
706
@expression.register(ogAST.PrimBoolean)
707
708
def _boolean(primary):
    ''' Generate code for a raw boolean value  '''
dbarbera's avatar
dbarbera committed
709
    if primary.value[0].lower() == 'true':
710
        return core.Constant.int(g.i1, 1)
dbarbera's avatar
dbarbera committed
711
    else:
712
        return core.Constant.int(g.i1, 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
713
714


Maxime Perrotin's avatar
Maxime Perrotin committed
715
@expression.register(ogAST.PrimEmptyString)
Maxime Perrotin's avatar
Maxime Perrotin committed
716
717
def _empty_string(primary):
    ''' Generate code for an empty SEQUENCE OF: {} '''
718
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
719
720


Maxime Perrotin's avatar
Maxime Perrotin committed
721
@expression.register(ogAST.PrimStringLiteral)
Maxime Perrotin's avatar
Maxime Perrotin committed
722
723
def _string_literal(primary):
    ''' Generate code for a string (Octet String) '''
dbarbera's avatar
dbarbera committed
724
    return _get_string_cons(str(primary.value[1:-1]))
Maxime Perrotin's avatar
Maxime Perrotin committed
725
726


Maxime Perrotin's avatar
Maxime Perrotin committed
727
@expression.register(ogAST.PrimConstant)
Maxime Perrotin's avatar
Maxime Perrotin committed
728
729
def _constant(primary):
    ''' Generate code for a reference to an ASN.1 constant '''
730
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
731
732


Maxime Perrotin's avatar
Maxime Perrotin committed
733
@expression.register(ogAST.PrimMantissaBaseExp)
Maxime Perrotin's avatar
Maxime Perrotin committed
734
735
def _mantissa_base_exp(primary):
    ''' Generate code for a Real with Mantissa-base-Exponent representation '''
736
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
737
738


Maxime Perrotin's avatar
Maxime Perrotin committed
739
@expression.register(ogAST.PrimIfThenElse)
dbarbera's avatar
dbarbera committed
740
def _if_then_else(ifthen):
dbarbera's avatar
dbarbera committed
741
    ''' Generate the code for ternary operator '''
742
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
743
744


Maxime Perrotin's avatar
Maxime Perrotin committed
745
@expression.register(ogAST.PrimSequence)
Maxime Perrotin's avatar
Maxime Perrotin committed
746
def _sequence(seq):
dbarbera's avatar
dbarbera committed
747
    ''' Generate the code for an ASN.1 SEQUENCE '''
dbarbera's avatar
dbarbera committed
748
749
750
751
752
753
754
755
756
757
758
    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
759
760


Maxime Perrotin's avatar
Maxime Perrotin committed
761
@expression.register(ogAST.PrimSequenceOf)
Maxime Perrotin's avatar
Maxime Perrotin committed
762
def _sequence_of(seqof):
dbarbera's avatar
dbarbera committed
763
    ''' Generate the code for an ASN.1 SEQUENCE OF '''
764
    ty = _generate_type(seqof.exprType)
765
766
767
    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])
768
769

    for idx, expr in enumerate(seqof.value):
770
        idx_cons = core.Constant.int(g.i32, idx)
771
        expr_val = expression(expr)
772
773
        pos_ptr = g.builder.gep(array_ptr, [zero_cons, idx_cons])
        g.builder.store(expr_val, pos_ptr)
774
775

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
776
777


Maxime Perrotin's avatar
Maxime Perrotin committed
778
@expression.register(ogAST.PrimChoiceItem)
Maxime Perrotin's avatar
Maxime Perrotin committed
779
def _choiceitem(choice):
dbarbera's avatar
dbarbera committed
780
    ''' Generate the code for a CHOICE expression '''
781
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
782
783
784
785


@generate.register(ogAST.Decision)
def _decision(dec):
dbarbera's avatar
dbarbera committed
786
    ''' Generate the code for a decision '''
787
    func = g.builder.basic_block.function
dbarbera's avatar
dbarbera committed
788
789
790
791

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

792
    g.builder.branch(ans_cond_blocks[0])
dbarbera's avatar
dbarbera committed
793
794
795
796
797

    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')
798
        g.builder.position_at_end(ans_cond_block)
dbarbera's avatar
dbarbera committed
799
800

        if ans.kind == 'constant':
dbarbera's avatar
dbarbera committed
801
            next_block = ans_cond_blocks[idx+1] if idx < len(ans_cond_blocks)-1 else end_block
dbarbera's avatar
dbarbera committed
802
803
804
805
806
807

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

808
809
810
            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
811
812
        elif ans.kind == 'else':
            if ans.transition:
813
                g.builder.branch(ans_tr_block)
dbarbera's avatar
dbarbera committed
814
            else:
815
                g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
816
817
818
819
        else:
            raise NotImplementedError

        if ans.transition:
820
            g.builder.position_at_end(ans_tr_block)
dbarbera's avatar
dbarbera committed
821
            generate(ans.transition)
822
            g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
823

824
    g.builder.position_at_end(end_block)
Maxime Perrotin's avatar
Maxime Perrotin committed
825
826
827
828


@generate.register(ogAST.Label)
def _label(tr):
dbarbera's avatar
dbarbera committed
829
    ''' TGenerate the code for a Label '''
830
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
831
832
833
834


@generate.register(ogAST.Transition)
def _transition(tr):
dbarbera's avatar
dbarbera committed
835
    ''' Generate the code for a transition '''
836
837
838
839
840
841
    for action in tr.actions:
        generate(action)
        if isinstance(action, ogAST.Label):
            return
    if tr.terminator:
        _generate_terminator(tr.terminator)
842
843


844
def _generate_terminator(term):
dbarbera's avatar
dbarbera committed
845
    ''' Generate the code for a transition termiantor '''
846
    if term.label:
847
        raise NotImplementedError
848
849
850
    if term.kind == 'next_state':
        state = term.inputString.lower()
        if state.strip() != '-':
851
            next_id_cons = core.Constant.int(g.i32, term.next_id)
852
            g.builder.store(next_id_cons, g.scope.resolve('id'))
853
            if term.next_id == -1:
854
                state_ptr = g.global_scope.resolve('state')
855
856
                state_id_cons = g.states[state]
                g.builder.store(state_id_cons, state_ptr)
857
        else:
858
            raise NotImplementedError
859
    elif term.kind == 'join':
860
        raise NotImplementedError
861
    elif term.kind == 'stop':
862
        raise NotImplementedError
863
    elif term.kind == 'return':
864
865
866
867
868
869
870
871
        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
872
873
874
875


@generate.register(ogAST.Floating_label)
def _floating_label(label):
dbarbera's avatar
dbarbera committed
876
    ''' Generate the code for a floating label '''
877
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
878
879
880
881
882


@generate.register(ogAST.Procedure)
def _inner_procedure(proc):
    ''' Generate the code for a procedure '''
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
    func_name = str(proc.inputString)
    param_tys = [core.Type.pointer(_generate_type(p['type'])) for p in proc.fpar]
    func_ty = core.Type.function(g.void, param_tys)
    func = core.Function.new(g.module, func_ty, func_name)
    g.funcs[func_name] = func

    _push_scope()

    for arg, param in zip(func.args, proc.fpar):
        g.scope.define(str(param['name']), arg)

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

    for name, (ty, expr) in proc.variables.viewitems():
        raise NotImplementedError

    generate(proc.content.start.transition)

    for label in proc.content.floating_labels:
        raise NotImplementedError

    _pop_scope()

    func.verify()
dbarbera's avatar
dbarbera committed
908
909
910


def _generate_type(ty):
911
    ''' Generate the equivalent LLVM type of a ASN.1 type '''