LlvmGenerator.py 30.9 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
    # Declare timer set/reset functions
    for timer in process.timers:
        # TODO: Should be uint?
146
147
        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
148

149
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
150
    for signal in process.output_signals:
151
152
153
154
        if 'type' in signal:
            param_tys = [core.Type.pointer(_generate_type(signal['type']))]
        else:
            param_tys = []
155
        decl_func(str(signal['name']), g.void, param_tys, True)
dbarbera's avatar
dbarbera committed
156

157
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
158
159
    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]
160
        decl_func(str(proc.inputString), g.void, param_tys, True)
dbarbera's avatar
dbarbera committed
161

162
163
    # Generate internal procedures
    for proc in process.content.inner_procedures:
164
        generate(proc)
165

166
    # Generate process functions
167
    _generate_runtr_func(process)
168
    _generate_startup_func(process)
169

170
171
172
173
    # Generate input signals
    for signal in process.input_signals:
        _generate_input_signal(signal, mapping[signal['name']])

dbarbera's avatar
dbarbera committed
174
175
    g.module.verify()

dbarbera's avatar
dbarbera committed
176
    with open(g.name + '.ll', 'w') as ll_file:
dbarbera's avatar
dbarbera committed
177
        ll_file.write(str(g.module))
178
179
180


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

184
185
    _push_scope()

186
187
188
189
190
    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
191
    g.builder = core.Builder.new(entry_block)
192
193

    # entry
dbarbera's avatar
dbarbera committed
194
    id_ptr = g.builder.alloca(g.i32, None, 'id')
195
    g.scope.define('id', id_ptr)
dbarbera's avatar
dbarbera committed
196
197
    g.builder.store(func.args[0], id_ptr)
    g.builder.branch(cond_block)
198
199

    # cond
dbarbera's avatar
dbarbera committed
200
    g.builder.position_at_end(cond_block)
201
    no_tr_cons = core.Constant.int(g.i32, -1)
202
203
    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
204
    g.builder.cbranch(cond_val, body_block, exit_block)
205
206

    # body
dbarbera's avatar
dbarbera committed
207
208
    g.builder.position_at_end(body_block)
    switch = g.builder.switch(func.args[0], exit_block)
209
210
211
212

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
213
        const = core.Constant.int(g.i32, idx)
214
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
215
        g.builder.position_at_end(tr_block)
216
        generate(tr)
dbarbera's avatar
dbarbera committed
217
        g.builder.branch(cond_block)
218
219

    # exit
dbarbera's avatar
dbarbera committed
220
221
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
222

223
224
    _pop_scope()

225
226
227
228
    func.verify()
    return func


229
def _generate_startup_func(process):
dbarbera's avatar
dbarbera committed
230
    ''' Generate code for the startup function '''
231
    func = decl_func(g.name + '_startup', g.void, [])
232

233
234
    _push_scope()

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

238
239
240
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
241
            global_var = g.scope.resolve(str(name))
242
243
            _generate_assign(global_var, expression(expr))

244
245
    g.builder.call(g.funcs['run_transition'], [core.Constant.int(g.i32, 0)])
    g.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
246

247
248
    _pop_scope()

249
250
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
251
252


253
def _generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
254
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
255
    func_name = g.name + "_" + str(signal['name'])
256
257
258
    param_tys = []
    if 'type' in signal:
        param_tys.append(core.Type.pointer(_generate_type(signal['type'])))
259
260

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

262
263
    _push_scope()

264
265
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
266
    g.builder = core.Builder.new(entry_block)
267

268
    g_state_val = g.builder.load(g.global_scope.resolve('state'))
269
    switch = g.builder.switch(g_state_val, exit_block)
270

271
    for state_name, state_id in g.states.iteritems():
272
273
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
274
        g.builder.position_at_end(state_block)
275
276
277

        # TODO: Nested states

278
279
280
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
281
                var_val = g.scope.resolve(str(var_name))
282
283
                _generate_assign(var_val, func.args[0])
            if input.transition:
284
                id_val = core.Constant.int(g.i32, input.transition_id)
285
                g.builder.call(g.funcs['run_transition'], [id_val])
286

287
        g.builder.ret_void()
288

289
290
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
291

292
293
    _pop_scope()

294
295
296
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
297
298
299
300
@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
301
302
303
    for out in output.output:
        name = out['outputName'].lower()

304
        if name == 'write':
dbarbera's avatar
dbarbera committed
305
306
            _generate_write(out['params'])
            continue
307
308
309
        elif name == 'writeln':
            _generate_writeln(out['params'])
            continue
dbarbera's avatar
dbarbera committed
310
311
312
313
314
315
316
        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
317
        func = g.funcs[str(name).lower()]
318
319
320
321
322
323
324
325
326
327
328
329
330

        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
331
332
333
334


def _generate_write(params):
    ''' Generate the code for the write operator '''
335
    zero = core.Constant.int(g.i32, 0)
336
337
338
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
339
340
341
342

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

343
        if basic_ty.kind == 'IntegerType':
344
            fmt_val = _get_string_cons('% d')
345
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
dbarbera's avatar
dbarbera committed
346
            g.builder.call(g.funcs['printf'], [fmt_ptr, expr_val])
347
        elif basic_ty.kind == 'RealType':
348
            fmt_val = _get_string_cons('% .14E')
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 == 'BooleanType':
352
            true_str_val = _get_string_cons('TRUE')
353
            true_str_ptr = g.builder.gep(true_str_val, [zero, zero])
354
            false_str_val = _get_string_cons('FALSE')
355
356
            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
357
            g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
358
359
        elif basic_ty.kind == 'StringType':
            expr_ptr = g.builder.gep(expr_val, [zero, zero])
dbarbera's avatar
dbarbera committed
360
            g.builder.call(g.funcs['printf'], [expr_ptr])
361
362
363
364
365
366
367
        else:
            raise NotImplementedError


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
368
369

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


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

    g.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
382
383
384
385


def _generate_set_timer(params):
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
386
    timer_expr, timer_id = params
387
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
388
389
390
391
392
393
394
395
396
397
398
    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
399
400
401
402


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


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


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
416
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
417
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
470
471
472
    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'''
473
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
474

dbarbera's avatar
dbarbera committed
475

Maxime Perrotin's avatar
Maxime Perrotin committed
476
477
478
479
# ------ expressions --------

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
480
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
481
482
483
484
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
485
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
486
    ''' Generate the code for a single variable reference '''
487
    return g.scope.resolve(str(prim.value[0]))
Maxime Perrotin's avatar
Maxime Perrotin committed
488
489


Maxime Perrotin's avatar
Maxime Perrotin committed
490
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
491
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
492
    ''' Generate the code for an of an element list (path) '''
dbarbera's avatar
dbarbera committed
493

494
    var_ptr = g.scope.resolve(str(prim.value.pop(0)))
dbarbera's avatar
dbarbera committed
495
496

    if not prim.value:
497
        return var_ptr
dbarbera's avatar
dbarbera committed
498
499
500
501
502
503
504
505
506
507
508
509
510

    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

511
    return var_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
512
513


Maxime Perrotin's avatar
Maxime Perrotin committed
514
515
516
517
518
519
520
521
522
523
524
525
@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
526
527
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
528
529
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
530
531

    # load the value of the expression if it is a pointer
532
    if lefttmp.type.kind == core.TYPE_POINTER:
533
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
534
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
535
        righttmp = g.builder.load(righttmp, 'righttmp')
536
537
538
539
540
541

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

    if lefttmp.type.kind == core.TYPE_INTEGER:
        if expr.operand == '+':
542
            return g.builder.add(lefttmp, righttmp, 'addtmp')
543
        elif expr.operand == '-':
544
            return g.builder.sub(lefttmp, righttmp, 'subtmp')
545
        elif expr.operand == '*':
546
            return g.builder.mul(lefttmp, righttmp, 'multmp')
547
        elif expr.operand == '/':
548
            return g.builder.sdiv(lefttmp, righttmp, 'divtmp')
549
        elif expr.operand == 'mod':
550
            # l mod r == (((l rem r) + r) rem r)
551
552
553
            remtmp = g.builder.srem(lefttmp, righttmp)
            addtmp = g.builder.add(remtmp, righttmp)
            return g.builder.srem(addtmp, righttmp, 'modtmp')
554
        elif expr.operand == 'rem':
555
            return g.builder.srem(lefttmp, righttmp, 'remtmp')
556
        elif expr.operand == '<':
557
            return g.builder.icmp(core.ICMP_SLT, lefttmp, righttmp, 'lttmp')
558
        elif expr.operand == '<=':
559
            return g.builder.icmp(core.ICMP_SLE, lefttmp, righttmp, 'letmp')
560
        elif expr.operand == '=':
561
            return g.builder.icmp(core.ICMP_EQ, lefttmp, righttmp, 'eqtmp')
562
        elif expr.operand == '/=':
563
            return g.builder.icmp(core.ICMP_NE, lefttmp, righttmp, 'netmp')
564
        elif expr.operand == '>=':
565
            return g.builder.icmp(core.ICMP_SGE, lefttmp, righttmp, 'getmp')
566
        elif expr.operand == '>':
567
            return g.builder.icmp(core.ICMP_SGT, lefttmp, righttmp, 'gttmp')
568
569
570
571
        else:
            raise NotImplementedError
    elif lefttmp.type.kind == core.TYPE_DOUBLE:
        if expr.operand == '+':
572
            return g.builder.fadd(lefttmp, righttmp, 'addtmp')
573
        elif expr.operand == '-':
574
            return g.builder.fsub(lefttmp, righttmp, 'subtmp')
575
        elif expr.operand == '*':
576
            return g.builder.fmul(lefttmp, righttmp, 'multmp')
577
        elif expr.operand == '/':
578
            return g.builder.fdiv(lefttmp, righttmp, 'divtmp')
579
        elif expr.operand == '<':
580
            return g.builder.fcmp(core.FCMP_OLT, lefttmp, righttmp, 'lttmp')
581
        elif expr.operand == '<=':
582
            return g.builder.fcmp(core.FCMP_OLE, lefttmp, righttmp, 'letmp')
583
        elif expr.operand == '=':
584
            return g.builder.fcmp(core.FCMP_OEQ, lefttmp, righttmp, 'eqtmp')
585
        elif expr.operand == '/=':
586
            return g.builder.fcmp(core.FCMP_ONE, lefttmp, righttmp, 'netmp')
587
        elif expr.operand == '>=':
588
            return g.builder.fcmp(core.FCMP_OGE, lefttmp, righttmp, 'getmp')
589
        elif expr.operand == '>':
590
            return g.builder.fcmp(core.FCMP_OGT, lefttmp, righttmp, 'gttmp')
591
592
        else:
            raise NotImplementedError
593
    else:
594
        raise NotImplementedError
595

Maxime Perrotin's avatar
Maxime Perrotin committed
596

597
598
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
599
    ''' Generate the code for an assign expression '''
600
601
    left = expression(expr.left)
    right = expression(expr.right)
602

603
604
    _generate_assign(left, right)
    return left
605

606
607
608
609
610

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
611
    if left.type.pointee.kind == core.TYPE_STRUCT:
612
613
        size = core.Constant.sizeof(left.type.pointee)
        align = core.Constant.int(g.i32, 0)
614
        volatile = core.Constant.int(g.i1, 0)
615

616
617
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
618

dbarbera's avatar
dbarbera committed
619
        g.builder.call(g.funcs['memcpy'], [left_ptr, right_ptr, size, align, volatile])
620
    else:
621
622
        if right.type.kind == core.TYPE_POINTER:
            right = g.builder.load(right)
623
        g.builder.store(right, left)
dbarbera's avatar
dbarbera committed
624

Maxime Perrotin's avatar
Maxime Perrotin committed
625

Maxime Perrotin's avatar
Maxime Perrotin committed
626
627
628
@expression.register(ogAST.ExprOr)
@expression.register(ogAST.ExprAnd)
@expression.register(ogAST.ExprXor)
dbarbera's avatar
dbarbera committed
629
630
def _logical(expr):
    ''' Generate the code for a logical expression '''
dbarbera's avatar
dbarbera committed
631
632
633
634
635
636
637
638
639
    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:
640
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
dbarbera's avatar
dbarbera committed
641
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
642
        righttmp = g.builder.load(righttmp, 'righttmp')
dbarbera's avatar
dbarbera committed
643

dbarbera's avatar
dbarbera committed
644
645
646
    if expr.operand == 'and':
        return g.builder.and_(lefttmp, righttmp, 'andtmp')
    elif expr.operand == 'or':
647
        return g.builder.or_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
648
    else:
649
        return g.builder.xor(lefttmp, righttmp, 'xortmp')
dbarbera's avatar
dbarbera committed
650

Maxime Perrotin's avatar
Maxime Perrotin committed
651

Maxime Perrotin's avatar
Maxime Perrotin committed
652
@expression.register(ogAST.ExprAppend)
Maxime Perrotin's avatar
Maxime Perrotin committed
653
654
def _append(expr):
    ''' Generate code for the APPEND construct: a // b '''
655
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
656
657


Maxime Perrotin's avatar
Maxime Perrotin committed
658
@expression.register(ogAST.ExprIn)
Maxime Perrotin's avatar
Maxime Perrotin committed
659
def _expr_in(expr):
dbarbera's avatar
dbarbera committed
660
    ''' Generate the code for an in expression '''
661
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
662
663


Maxime Perrotin's avatar
Maxime Perrotin committed
664
@expression.register(ogAST.PrimEnumeratedValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
665
666
def _enumerated_value(primary):
    ''' Generate code for an enumerated value '''
dbarbera's avatar
dbarbera committed
667
668
669
    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
670
671


Maxime Perrotin's avatar
Maxime Perrotin committed
672
@expression.register(ogAST.PrimChoiceDeterminant)
Maxime Perrotin's avatar
Maxime Perrotin committed
673
674
def _choice_determinant(primary):
    ''' Generate code for a choice determinant (enumerated) '''
675
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
676
677


Maxime Perrotin's avatar
Maxime Perrotin committed
678
@expression.register(ogAST.PrimInteger)
679
680
def _integer(primary):
    ''' Generate code for a raw integer value  '''
681
    return core.Constant.int(g.i32, primary.value[0])
682
683


Maxime Perrotin's avatar
Maxime Perrotin committed
684
@expression.register(ogAST.PrimReal)
685
686
def _real(primary):
    ''' Generate code for a raw real value  '''
687
    return core.Constant.real(g.double, primary.value[0])
688
689


Maxime Perrotin's avatar
Maxime Perrotin committed
690
@expression.register(ogAST.PrimBoolean)
691
692
def _boolean(primary):
    ''' Generate code for a raw boolean value  '''
dbarbera's avatar
dbarbera committed
693
    if primary.value[0].lower() == 'true':
694
        return core.Constant.int(g.i1, 1)
dbarbera's avatar
dbarbera committed
695
    else:
696
        return core.Constant.int(g.i1, 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
697
698


Maxime Perrotin's avatar
Maxime Perrotin committed
699
@expression.register(ogAST.PrimEmptyString)
Maxime Perrotin's avatar
Maxime Perrotin committed
700
701
def _empty_string(primary):
    ''' Generate code for an empty SEQUENCE OF: {} '''
702
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
703
704


Maxime Perrotin's avatar
Maxime Perrotin committed
705
@expression.register(ogAST.PrimStringLiteral)
Maxime Perrotin's avatar
Maxime Perrotin committed
706
707
def _string_literal(primary):
    ''' Generate code for a string (Octet String) '''
dbarbera's avatar
dbarbera committed
708
    return _get_string_cons(str(primary.value[1:-1]))
Maxime Perrotin's avatar
Maxime Perrotin committed
709
710


Maxime Perrotin's avatar
Maxime Perrotin committed
711
@expression.register(ogAST.PrimConstant)
Maxime Perrotin's avatar
Maxime Perrotin committed
712
713
def _constant(primary):
    ''' Generate code for a reference to an ASN.1 constant '''
714
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
715
716


Maxime Perrotin's avatar
Maxime Perrotin committed
717
@expression.register(ogAST.PrimMantissaBaseExp)
Maxime Perrotin's avatar
Maxime Perrotin committed
718
719
def _mantissa_base_exp(primary):
    ''' Generate code for a Real with Mantissa-base-Exponent representation '''
720
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
721
722


Maxime Perrotin's avatar
Maxime Perrotin committed
723
@expression.register(ogAST.PrimIfThenElse)
dbarbera's avatar
dbarbera committed
724
def _if_then_else(ifthen):
dbarbera's avatar
dbarbera committed
725
    ''' Generate the code for ternary operator '''
726
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
727
728


Maxime Perrotin's avatar
Maxime Perrotin committed
729
@expression.register(ogAST.PrimSequence)
Maxime Perrotin's avatar
Maxime Perrotin committed
730
def _sequence(seq):
dbarbera's avatar
dbarbera committed
731
    ''' Generate the code for an ASN.1 SEQUENCE '''
dbarbera's avatar
dbarbera committed
732
733
734
735
736
737
738
739
740
741
742
    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
743
744


Maxime Perrotin's avatar
Maxime Perrotin committed
745
@expression.register(ogAST.PrimSequenceOf)
Maxime Perrotin's avatar
Maxime Perrotin committed
746
def _sequence_of(seqof):
dbarbera's avatar
dbarbera committed
747
    ''' Generate the code for an ASN.1 SEQUENCE OF '''
748
    ty = _generate_type(seqof.exprType)
749
750
751
    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])
752
753

    for idx, expr in enumerate(seqof.value):
754
        idx_cons = core.Constant.int(g.i32, idx)
755
        expr_val = expression(expr)
756
757
        pos_ptr = g.builder.gep(array_ptr, [zero_cons, idx_cons])
        g.builder.store(expr_val, pos_ptr)
758
759

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
760
761


Maxime Perrotin's avatar
Maxime Perrotin committed
762
@expression.register(ogAST.PrimChoiceItem)
Maxime Perrotin's avatar
Maxime Perrotin committed
763
def _choiceitem(choice):
dbarbera's avatar
dbarbera committed
764
    ''' Generate the code for a CHOICE expression '''
765
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
766
767
768
769


@generate.register(ogAST.Decision)
def _decision(dec):
dbarbera's avatar
dbarbera committed
770
    ''' Generate the code for a decision '''
771
    func = g.builder.basic_block.function
dbarbera's avatar
dbarbera committed
772
773
774
775

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

776
    g.builder.branch(ans_cond_blocks[0])
dbarbera's avatar
dbarbera committed
777
778
779
780
781

    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')
782
        g.builder.position_at_end(ans_cond_block)
dbarbera's avatar
dbarbera committed
783
784

        if ans.kind == 'constant':
dbarbera's avatar
dbarbera committed
785
            next_block = ans_cond_blocks[idx+1] if idx < len(ans_cond_blocks)-1 else end_block
dbarbera's avatar
dbarbera committed
786
787
788
789
790
791

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

792
793
794
            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
795
796
        elif ans.kind == 'else':
            if ans.transition:
797
                g.builder.branch(ans_tr_block)
dbarbera's avatar
dbarbera committed
798
            else:
799
                g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
800
801
802
803
        else:
            raise NotImplementedError

        if ans.transition:
804
            g.builder.position_at_end(ans_tr_block)
dbarbera's avatar
dbarbera committed
805
            generate(ans.transition)
806
            g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
807

808
    g.builder.position_at_end(end_block)
Maxime Perrotin's avatar
Maxime Perrotin committed
809
810
811
812


@generate.register(ogAST.Label)
def _label(tr):
dbarbera's avatar
dbarbera committed
813
    ''' TGenerate the code for a Label '''
814
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
815
816
817
818


@generate.register(ogAST.Transition)
def _transition(tr):
dbarbera's avatar
dbarbera committed
819
    ''' Generate the code for a transition '''
820
821
822
823
824
825
    for action in tr.actions:
        generate(action)
        if isinstance(action, ogAST.Label):
            return
    if tr.terminator:
        _generate_terminator(tr.terminator)
826
827


828
def _generate_terminator(term):
dbarbera's avatar
dbarbera committed
829
    ''' Generate the code for a transition termiantor '''
830
    if term.label:
831
        raise NotImplementedError
832
833
834
    if term.kind == 'next_state':
        state = term.inputString.lower()
        if state.strip() != '-':
835
            next_id_cons = core.Constant.int(g.i32, term.next_id)
836
            g.builder.store(next_id_cons, g.scope.resolve('id'))
837
            if term.next_id == -1:
838
                state_ptr = g.global_scope.resolve('state')
839
840
                state_id_cons = g.states[state]
                g.builder.store(state_id_cons, state_ptr)
841
        else:
842
            raise NotImplementedError
843
    elif term.kind == 'join':
844
        raise NotImplementedError
845
    elif term.kind == 'stop':
846
        raise NotImplementedError
847
    elif term.kind == 'return':
848
849
850
851
852
853
854
855
        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
856
857
858
859


@generate.register(ogAST.Floating_label)
def _floating_label(label):
dbarbera's avatar
dbarbera committed
860
    ''' Generate the code for a floating label '''
861
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
862
863
864
865
866


@generate.register(ogAST.Procedure)
def _inner_procedure(proc):
    ''' Generate the code for a procedure '''
867
    param_tys = [core.Type.pointer(_generate_type(p['type'])) for p in proc.fpar]
868
    func = decl_func(str(proc.inputString), g.void, param_tys)
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888

    _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
889
890
891


def _generate_type(ty):
892
    ''' Generate the equivalent LLVM type of a ASN.1 type '''
dbarbera's avatar
dbarbera committed
893
894
    basic_ty = find_basic_type(ty)
    if basic_ty.kind == 'IntegerType':
895
        return g.i32
dbarbera's avatar
dbarbera committed