LlvmGenerator.py 26.4 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
Maxime Perrotin's avatar
Maxime Perrotin committed
21
from singledispatch import singledispatch
22
from llvm import core
Maxime Perrotin's avatar
Maxime Perrotin committed
23
24

import ogAST
Maxime Perrotin's avatar
Maxime Perrotin committed
25
import Helper
Maxime Perrotin's avatar
Maxime Perrotin committed
26
27
28

LOG = logging.getLogger(__name__)

Maxime Perrotin's avatar
Maxime Perrotin committed
29
30
__all__ = ['generate']

Maxime Perrotin's avatar
Maxime Perrotin committed
31

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


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

        self.scope = {}
        self.states = {}
dbarbera's avatar
dbarbera committed
44
        self.structs = {}
45
        self.strings = {}
dbarbera's avatar
dbarbera committed
46
        self.funcs = {}
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

        # Initialize built-in types
        self.i1 = core.Type.int(1)
        self.i8 = core.Type.int(8)
        self.i32 = core.Type.int(32)
        self.i64 = core.Type.int(64)
        self.void = core.Type.void()
        self.double = core.Type.double()
        self.i1_ptr = core.Type.pointer(self.i1)
        self.i8_ptr = core.Type.pointer(self.i8)
        self.i32_ptr = core.Type.pointer(self.i32)
        self.i64_ptr = core.Type.pointer(self.i64)
        self.double_ptr = core.Type.pointer(self.double)

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

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

dbarbera's avatar
dbarbera committed
70

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



Maxime Perrotin's avatar
Maxime Perrotin committed
82
83
84
85
@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
86

dbarbera's avatar
dbarbera committed
87

Maxime Perrotin's avatar
Maxime Perrotin committed
88
89
90
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
91
    ''' Generate LLVM IR code '''
92
    LOG.info('Generating LLVM IR code for process ' + str(process.processName))
93

94
95
    global g
    g = GlobalState(process)
96

dbarbera's avatar
dbarbera committed
97
98
99
100
101
102
    # 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
103

104
105
106
    # Initialize states enum
    for name in process.mapping.iterkeys():
        if not name.endswith('START'):
107
108
            cons = core.Constant.int(g.i32, len(g.states))
            g.states[name] = cons
109

110
    # Generate state var
111
112
    state_cons = g.module.add_global_variable(g.i32, 'state')
    state_cons.initializer = core.Constant.int(g.i32, -1)
113

dbarbera's avatar
dbarbera committed
114
115
    # Initialize output signals
    for signal in process.output_signals:
116
117
118
119
        if 'type' in signal:
            param_tys = [core.Type.pointer(_generate_type(signal['type']))]
        else:
            param_tys = []
120
        func_ty = core.Type.function(g.void, param_tys)
dbarbera's avatar
dbarbera committed
121
122
123
        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
124
125
126
127

    # 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]
128
        func_ty = core.Type.function(g.void, param_tys)
dbarbera's avatar
dbarbera committed
129
130
131
        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
132

133
    # Generare process-level vars
dbarbera's avatar
dbarbera committed
134
    for var_name, (var_asn1_type, def_value) in process.variables.viewitems():
135
        var_type = _generate_type(var_asn1_type)
136
        global_var = g.module.add_global_variable(var_type, str(var_name).lower())
dbarbera's avatar
dbarbera committed
137
        global_var.initializer = core.Constant.null(var_type)
138

139
        if def_value:
dbarbera's avatar
dbarbera committed
140
            raise NotImplementedError
141

142
    # Generate process functions
143
144
    g.runtr = _generate_runtr_func(process)
    _generate_startup_func(process)
145

146
147
148
149
    # Generate input signals
    for signal in process.input_signals:
        _generate_input_signal(signal, mapping[signal['name']])

dbarbera's avatar
dbarbera committed
150
151
    g.module.verify()

dbarbera's avatar
dbarbera committed
152
    with open(g.name  + '.ll', 'w') as ll_file:
dbarbera's avatar
dbarbera committed
153
        ll_file.write(str(g.module))
154
155
156


def _generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
157
    ''' Generate code for the run_transition function '''
158
    func_name = 'run_transition'
159
160
    func_type = core.Type.function(g.void, [g.i32])
    func = core.Function.new(g.module, func_type, func_name)
161
162
163
164
165
166

    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
167
    g.builder = core.Builder.new(entry_block)
168
169

    # entry
dbarbera's avatar
dbarbera committed
170
    id_ptr = g.builder.alloca(g.i32, None, 'id')
171
    g.scope['id'] = id_ptr
dbarbera's avatar
dbarbera committed
172
173
    g.builder.store(func.args[0], id_ptr)
    g.builder.branch(cond_block)
174
175

    # cond
dbarbera's avatar
dbarbera committed
176
    g.builder.position_at_end(cond_block)
177
    no_tr_cons = core.Constant.int(g.i32, -1)
178
179
    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
180
    g.builder.cbranch(cond_val, body_block, exit_block)
181
182

    # body
dbarbera's avatar
dbarbera committed
183
184
    g.builder.position_at_end(body_block)
    switch = g.builder.switch(func.args[0], exit_block)
185
186
187
188

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
189
        const = core.Constant.int(g.i32, idx)
190
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
191
        g.builder.position_at_end(tr_block)
192
        generate(tr)
dbarbera's avatar
dbarbera committed
193
        g.builder.branch(cond_block)
194
195

    # exit
dbarbera's avatar
dbarbera committed
196
197
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
198
199

    func.verify()
200
    g.scope.clear()
201
202
203
    return func


204
def _generate_startup_func(process):
dbarbera's avatar
dbarbera committed
205
    ''' Generate code for the startup function '''
dbarbera's avatar
dbarbera committed
206
    func_name = g.name + '_startup'
207
208
    func_type = core.Type.function(g.void, [])
    func = core.Function.new(g.module, func_type, func_name)
209
210
211

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

    # entry
215
    builder.call(g.runtr, [core.Constant.int(core.Type.int(), 0)])
Maxime Perrotin's avatar
Maxime Perrotin committed
216
217
    builder.ret_void()

218
219
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
220
221


222
def _generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
223
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
224
    func_name = g.name + "_" + str(signal['name'])
225
226
227
    param_tys = []
    if 'type' in signal:
        param_tys.append(core.Type.pointer(_generate_type(signal['type'])))
228
229
    func_type = core.Type.function(g.void, param_tys)
    func = core.Function.new(g.module, func_type, func_name)
dbarbera's avatar
dbarbera committed
230
    g.funcs[func_name.lower()] = func
231
232
233

    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
234
    g.builder = core.Builder.new(entry_block)
235

236
    runtr_func = g.module.get_function_named('run_transition')
237

238
239
    g_state_val = g.builder.load(g.module.get_global_variable_named('state'))
    switch = g.builder.switch(g_state_val, exit_block)
240

241
    for state_name, state_id in g.states.iteritems():
242
243
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
244
        g.builder.position_at_end(state_block)
245
246
247

        # TODO: Nested states

248
249
250
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
251
                var_val = g.module.get_global_variable_named(str(var_name).lower())
252
253
                _generate_assign(var_val, func.args[0])
            if input.transition:
254
255
                id_val = core.Constant.int(g.i32, input.transition_id)
                g.builder.call(runtr_func, [id_val])
256

257
        g.builder.ret_void()
258

259
260
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
261
262
263
264

    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
265
266
267
268
@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
269
270
271
272

    for out in output.output:
        name = out['outputName'].lower()

273
        if name == 'write':
dbarbera's avatar
dbarbera committed
274
275
            _generate_write(out['params'])
            continue
276
277
278
        elif name == 'writeln':
            _generate_writeln(out['params'])
            continue
dbarbera's avatar
dbarbera committed
279
280
281
282
283
284
285
        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
286
        func = g.funcs[str(name).lower()]
287
        g.builder.call(func, [expression(p) for p in out.get('params', [])])
dbarbera's avatar
dbarbera committed
288
289
290
291


def _generate_write(params):
    ''' Generate the code for the write operator '''
292
    zero = core.Constant.int(g.i32, 0)
293
294
295
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
296
297
298
299

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

300
        if basic_ty.kind == 'IntegerType':
301
            fmt_val = _get_string_cons('% d')
302
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
dbarbera's avatar
dbarbera committed
303
            g.builder.call(g.funcs['printf'], [fmt_ptr, expr_val])
304
        elif basic_ty.kind == 'RealType':
305
            fmt_val = _get_string_cons('% .14E')
306
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
dbarbera's avatar
dbarbera committed
307
            g.builder.call(g.funcs['printf'], [fmt_ptr, expr_val])
308
        elif basic_ty.kind == 'BooleanType':
309
            true_str_val = _get_string_cons('TRUE')
310
            true_str_ptr = g.builder.gep(true_str_val, [zero, zero])
311
            false_str_val = _get_string_cons('FALSE')
312
313
            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
314
            g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
315
316
        elif basic_ty.kind == 'StringType':
            expr_ptr = g.builder.gep(expr_val, [zero, zero])
dbarbera's avatar
dbarbera committed
317
            g.builder.call(g.funcs['printf'], [expr_ptr])
318
319
320
321
322
323
324
        else:
            raise NotImplementedError


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
325
326

    zero = core.Constant.int(g.i32, 0)
327
    str_cons = _get_string_cons('\n')
328
    str_ptr = g.builder.gep(str_cons, [zero, zero])
dbarbera's avatar
dbarbera committed
329
    g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
330
331
332
333
334
335
336
337
338


def _generate_reset_timer(params):
    ''' Generate the code for the reset timer operator '''
    raise NotImplementedError


def _generate_set_timer(params):
    ''' Generate the code for the set timer operator '''
339
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
340
341
342
343


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
344
    ''' Generate the code of a list of assignments '''
345
346
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
347
348
349
350
351


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
352
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
353
354
355
356


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
357
    ''' Generate the code for a for loop '''
358
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
359

dbarbera's avatar
dbarbera committed
360

Maxime Perrotin's avatar
Maxime Perrotin committed
361
362
363
364
# ------ expressions --------

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
365
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
366
367
368
369
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
370
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
371
    ''' Generate the code for a single variable reference '''
372
    return g.module.get_global_variable_named(str(prim.value[0]).lower())
Maxime Perrotin's avatar
Maxime Perrotin committed
373
374


Maxime Perrotin's avatar
Maxime Perrotin committed
375
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
376
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
377
    ''' Generate the code for an of an element list (path) '''
dbarbera's avatar
dbarbera committed
378
379
380
381

    var_ptr = g.module.get_global_variable_named(str(prim.value.pop(0)).lower())

    if not prim.value:
382
        return var_ptr
dbarbera's avatar
dbarbera committed
383
384
385
386
387
388
389
390
391
392
393
394
395

    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

396
    return var_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
397
398


Maxime Perrotin's avatar
Maxime Perrotin committed
399
400
401
402
403
404
405
406
407
408
409
410
@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
411
412
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
413
414
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
415
416

    # load the value of the expression if it is a pointer
417
    if lefttmp.type.kind == core.TYPE_POINTER:
418
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
419
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
420
        righttmp = g.builder.load(righttmp, 'righttmp')
421
422
423
424
425
426

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

    if lefttmp.type.kind == core.TYPE_INTEGER:
        if expr.operand == '+':
427
            return g.builder.add(lefttmp, righttmp, 'addtmp')
428
        elif expr.operand == '-':
429
            return g.builder.sub(lefttmp, righttmp, 'subtmp')
430
        elif expr.operand == '*':
431
            return g.builder.mul(lefttmp, righttmp, 'multmp')
432
        elif expr.operand == '/':
433
            return g.builder.sdiv(lefttmp, righttmp, 'divtmp')
434
        elif expr.operand == 'mod':
435
            # l mod r == (((l rem r) + r) rem r)
436
437
438
            remtmp = g.builder.srem(lefttmp, righttmp)
            addtmp = g.builder.add(remtmp, righttmp)
            return g.builder.srem(addtmp, righttmp, 'modtmp')
439
        elif expr.operand == 'rem':
440
            return g.builder.srem(lefttmp, righttmp, 'remtmp')
441
        elif expr.operand == '<':
442
            return g.builder.icmp(core.ICMP_SLT, lefttmp, righttmp, 'lttmp')
443
        elif expr.operand == '<=':
444
            return g.builder.icmp(core.ICMP_SLE, lefttmp, righttmp, 'letmp')
445
        elif expr.operand == '=':
446
            return g.builder.icmp(core.ICMP_EQ, lefttmp, righttmp, 'eqtmp')
447
        elif expr.operand == '/=':
448
            return g.builder.icmp(core.ICMP_NE, lefttmp, righttmp, 'netmp')
449
        elif expr.operand == '>=':
450
            return g.builder.icmp(core.ICMP_SGE, lefttmp, righttmp, 'getmp')
451
        elif expr.operand == '>':
452
            return g.builder.icmp(core.ICMP_SGT, lefttmp, righttmp, 'gttmp')
453
454
455
456
        else:
            raise NotImplementedError
    elif lefttmp.type.kind == core.TYPE_DOUBLE:
        if expr.operand == '+':
457
            return g.builder.fadd(lefttmp, righttmp, 'addtmp')
458
        elif expr.operand == '-':
459
            return g.builder.fsub(lefttmp, righttmp, 'subtmp')
460
        elif expr.operand == '*':
461
            return g.builder.fmul(lefttmp, righttmp, 'multmp')
462
        elif expr.operand == '/':
463
            return g.builder.fdiv(lefttmp, righttmp, 'divtmp')
464
        elif expr.operand == '<':
465
            return g.builder.fcmp(core.FCMP_OLT, lefttmp, righttmp, 'lttmp')
466
        elif expr.operand == '<=':
467
            return g.builder.fcmp(core.FCMP_OLE, lefttmp, righttmp, 'letmp')
468
        elif expr.operand == '=':
469
            return g.builder.fcmp(core.FCMP_OEQ, lefttmp, righttmp, 'eqtmp')
470
        elif expr.operand == '/=':
471
            return g.builder.fcmp(core.FCMP_ONE, lefttmp, righttmp, 'netmp')
472
        elif expr.operand == '>=':
473
            return g.builder.fcmp(core.FCMP_OGE, lefttmp, righttmp, 'getmp')
474
        elif expr.operand == '>':
475
            return g.builder.fcmp(core.FCMP_OGT, lefttmp, righttmp, 'gttmp')
476
477
        else:
            raise NotImplementedError
478
    else:
479
        raise NotImplementedError
480

Maxime Perrotin's avatar
Maxime Perrotin committed
481

482
483
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
484
    ''' Generate the code for an assign expression '''
485
486
    left = expression(expr.left)
    right = expression(expr.right)
487

488
489
    _generate_assign(left, right)
    return left
490

491
492
493
494
495

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
496
    if left.type.pointee.kind == core.TYPE_STRUCT:
497
498
        size = core.Constant.sizeof(left.type.pointee)
        align = core.Constant.int(g.i32, 0)
499
        volatile = core.Constant.int(g.i1, 0)
500

501
502
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
503

dbarbera's avatar
dbarbera committed
504
        g.builder.call(g.funcs['memcpy'], [left_ptr, right_ptr, size, align, volatile])
505
    else:
506
507
        if right.type.kind == core.TYPE_POINTER:
            right = g.builder.load(right)
508
        g.builder.store(right, left)
dbarbera's avatar
dbarbera committed
509

Maxime Perrotin's avatar
Maxime Perrotin committed
510

Maxime Perrotin's avatar
Maxime Perrotin committed
511
512
513
@expression.register(ogAST.ExprOr)
@expression.register(ogAST.ExprAnd)
@expression.register(ogAST.ExprXor)
dbarbera's avatar
dbarbera committed
514
515
def _logical(expr):
    ''' Generate the code for a logical expression '''
dbarbera's avatar
dbarbera committed
516
517
518
519
520
521
522
523
524
    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:
525
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
dbarbera's avatar
dbarbera committed
526
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
527
        righttmp = g.builder.load(righttmp, 'righttmp')
dbarbera's avatar
dbarbera committed
528

dbarbera's avatar
dbarbera committed
529
530
531
    if expr.operand == 'and':
        return g.builder.and_(lefttmp, righttmp, 'andtmp')
    elif expr.operand == 'or':
532
        return g.builder.or_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
533
    else:
534
        return g.builder.xor(lefttmp, righttmp, 'xortmp')
dbarbera's avatar
dbarbera committed
535

Maxime Perrotin's avatar
Maxime Perrotin committed
536

Maxime Perrotin's avatar
Maxime Perrotin committed
537
@expression.register(ogAST.ExprAppend)
Maxime Perrotin's avatar
Maxime Perrotin committed
538
539
def _append(expr):
    ''' Generate code for the APPEND construct: a // b '''
540
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
541
542


Maxime Perrotin's avatar
Maxime Perrotin committed
543
@expression.register(ogAST.ExprIn)
Maxime Perrotin's avatar
Maxime Perrotin committed
544
def _expr_in(expr):
dbarbera's avatar
dbarbera committed
545
    ''' Generate the code for an in expression '''
546
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
547
548


Maxime Perrotin's avatar
Maxime Perrotin committed
549
@expression.register(ogAST.PrimEnumeratedValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
550
551
def _enumerated_value(primary):
    ''' Generate code for an enumerated value '''
dbarbera's avatar
dbarbera committed
552
553
554
    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
555
556


Maxime Perrotin's avatar
Maxime Perrotin committed
557
@expression.register(ogAST.PrimChoiceDeterminant)
Maxime Perrotin's avatar
Maxime Perrotin committed
558
559
def _choice_determinant(primary):
    ''' Generate code for a choice determinant (enumerated) '''
560
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
561
562


Maxime Perrotin's avatar
Maxime Perrotin committed
563
@expression.register(ogAST.PrimInteger)
564
565
def _integer(primary):
    ''' Generate code for a raw integer value  '''
566
    return core.Constant.int(g.i32, primary.value[0])
567
568


Maxime Perrotin's avatar
Maxime Perrotin committed
569
@expression.register(ogAST.PrimReal)
570
571
def _real(primary):
    ''' Generate code for a raw real value  '''
572
    return core.Constant.real(g.double, primary.value[0])
573
574


Maxime Perrotin's avatar
Maxime Perrotin committed
575
@expression.register(ogAST.PrimBoolean)
576
577
def _boolean(primary):
    ''' Generate code for a raw boolean value  '''
dbarbera's avatar
dbarbera committed
578
    if primary.value[0].lower() == 'true':
579
        return core.Constant.int(g.i1, 1)
dbarbera's avatar
dbarbera committed
580
    else:
581
        return core.Constant.int(g.i1, 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
582
583


Maxime Perrotin's avatar
Maxime Perrotin committed
584
@expression.register(ogAST.PrimEmptyString)
Maxime Perrotin's avatar
Maxime Perrotin committed
585
586
def _empty_string(primary):
    ''' Generate code for an empty SEQUENCE OF: {} '''
587
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
588
589


Maxime Perrotin's avatar
Maxime Perrotin committed
590
@expression.register(ogAST.PrimStringLiteral)
Maxime Perrotin's avatar
Maxime Perrotin committed
591
592
def _string_literal(primary):
    ''' Generate code for a string (Octet String) '''
dbarbera's avatar
dbarbera committed
593
    return _get_string_cons(str(primary.value[1:-1]))
Maxime Perrotin's avatar
Maxime Perrotin committed
594
595


Maxime Perrotin's avatar
Maxime Perrotin committed
596
@expression.register(ogAST.PrimConstant)
Maxime Perrotin's avatar
Maxime Perrotin committed
597
598
def _constant(primary):
    ''' Generate code for a reference to an ASN.1 constant '''
599
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
600
601


Maxime Perrotin's avatar
Maxime Perrotin committed
602
@expression.register(ogAST.PrimMantissaBaseExp)
Maxime Perrotin's avatar
Maxime Perrotin committed
603
604
def _mantissa_base_exp(primary):
    ''' Generate code for a Real with Mantissa-base-Exponent representation '''
605
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
606
607


Maxime Perrotin's avatar
Maxime Perrotin committed
608
@expression.register(ogAST.PrimIfThenElse)
dbarbera's avatar
dbarbera committed
609
def _if_then_else(ifthen):
dbarbera's avatar
dbarbera committed
610
    ''' Generate the code for ternary operator '''
611
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
612
613


Maxime Perrotin's avatar
Maxime Perrotin committed
614
@expression.register(ogAST.PrimSequence)
Maxime Perrotin's avatar
Maxime Perrotin committed
615
def _sequence(seq):
dbarbera's avatar
dbarbera committed
616
    ''' Generate the code for an ASN.1 SEQUENCE '''
dbarbera's avatar
dbarbera committed
617
618
619
620
621
622
623
624
625
626
627
    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
628
629


Maxime Perrotin's avatar
Maxime Perrotin committed
630
@expression.register(ogAST.PrimSequenceOf)
Maxime Perrotin's avatar
Maxime Perrotin committed
631
def _sequence_of(seqof):
dbarbera's avatar
dbarbera committed
632
    ''' Generate the code for an ASN.1 SEQUENCE OF '''
633
    ty = _generate_type(seqof.exprType)
634
635
636
    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])
637
638

    for idx, expr in enumerate(seqof.value):
639
        idx_cons = core.Constant.int(g.i32, idx)
640
        expr_val = expression(expr)
641
642
        pos_ptr = g.builder.gep(array_ptr, [zero_cons, idx_cons])
        g.builder.store(expr_val, pos_ptr)
643
644

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
645
646


Maxime Perrotin's avatar
Maxime Perrotin committed
647
@expression.register(ogAST.PrimChoiceItem)
Maxime Perrotin's avatar
Maxime Perrotin committed
648
def _choiceitem(choice):
dbarbera's avatar
dbarbera committed
649
    ''' Generate the code for a CHOICE expression '''
650
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
651
652
653
654


@generate.register(ogAST.Decision)
def _decision(dec):
dbarbera's avatar
dbarbera committed
655
    ''' Generate the code for a decision '''
656
    func = g.builder.basic_block.function
dbarbera's avatar
dbarbera committed
657
658
659
660

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

661
    g.builder.branch(ans_cond_blocks[0])
dbarbera's avatar
dbarbera committed
662
663
664
665
666

    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')
667
        g.builder.position_at_end(ans_cond_block)
dbarbera's avatar
dbarbera committed
668
669

        if ans.kind == 'constant':
dbarbera's avatar
dbarbera committed
670
            next_block = ans_cond_blocks[idx+1] if idx < len(ans_cond_blocks)-1 else end_block
dbarbera's avatar
dbarbera committed
671
672
673
674
675
676

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

677
678
679
            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
680
681
        elif ans.kind == 'else':
            if ans.transition:
682
                g.builder.branch(ans_tr_block)
dbarbera's avatar
dbarbera committed
683
            else:
684
                g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
685
686
687
688
        else:
            raise NotImplementedError

        if ans.transition:
689
            g.builder.position_at_end(ans_tr_block)
dbarbera's avatar
dbarbera committed
690
            generate(ans.transition)
691
            g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
692

693
    g.builder.position_at_end(end_block)
Maxime Perrotin's avatar
Maxime Perrotin committed
694
695
696
697


@generate.register(ogAST.Label)
def _label(tr):
dbarbera's avatar
dbarbera committed
698
    ''' TGenerate the code for a Label '''
699
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
700
701
702
703


@generate.register(ogAST.Transition)
def _transition(tr):
dbarbera's avatar
dbarbera committed
704
    ''' Generate the code for a transition '''
705
706
707
708
709
710
    for action in tr.actions:
        generate(action)
        if isinstance(action, ogAST.Label):
            return
    if tr.terminator:
        _generate_terminator(tr.terminator)
711
712


713
def _generate_terminator(term):
dbarbera's avatar
dbarbera committed
714
    ''' Generate the code for a transition termiantor '''
715
    id_ptr = g.scope['id']
716
    if term.label:
717
        raise NotImplementedError
718
719
720
    if term.kind == 'next_state':
        state = term.inputString.lower()
        if state.strip() != '-':
721
722
            next_id_cons = core.Constant.int(g.i32, term.next_id)
            g.builder.store(next_id_cons, id_ptr)
723
            if term.next_id == -1:
724
725
726
                state_ptr = g.module.get_global_variable_named('state')
                state_id_cons = g.states[state]
                g.builder.store(state_id_cons, state_ptr)
727
        else:
728
            raise NotImplementedError
729
    elif term.kind == 'join':
730
        raise NotImplementedError
731
    elif term.kind == 'stop':
732
        raise NotImplementedError
733
    elif term.kind == 'return':
734
        raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
735
736
737
738


@generate.register(ogAST.Floating_label)
def _floating_label(label):
dbarbera's avatar
dbarbera committed
739
    ''' Generate the code for a floating label '''
740
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
741
742
743
744
745


@generate.register(ogAST.Procedure)
def _inner_procedure(proc):
    ''' Generate the code for a procedure '''
746
    raise NotImplementedError
dbarbera's avatar
dbarbera committed
747
748
749


def _generate_type(ty):
750
    ''' Generate the equivalent LLVM type of a ASN.1 type '''
dbarbera's avatar
dbarbera committed
751
752
    basic_ty = find_basic_type(ty)
    if basic_ty.kind == 'IntegerType':
753
        return g.i32
dbarbera's avatar
dbarbera committed
754
    elif basic_ty.kind == 'BooleanType':
755
        return g.i1
dbarbera's avatar
dbarbera committed
756
    elif basic_ty.kind == 'RealType':
757
        return g.double
758
    elif basic_ty.kind == 'SequenceOfType':
dbarbera's avatar
dbarbera committed
759
760
        if ty.ReferencedTypeName in g.structs:
            return g.structs[ty.ReferencedTypeName].ty
761
762
763
764
765
766
767
768

        min_size = int(basic_ty.Max)
        max_size = int(basic_ty.Min)
        if min_size != max_size:
            raise NotImplementedError

        elem_ty = _generate_type(basic_ty.type)
        array_ty = core.Type.array(elem_ty, max_size)
769
        struct = StructType(ty.ReferencedTypeName, ['_'], [array_ty])
dbarbera's avatar
dbarbera committed
770
771
772
773
774
        g.structs[ty.ReferencedTypeName] = struct
        return struct.ty
    elif basic_ty.kind == 'SequenceType':
        if ty.ReferencedTypeName in g.structs:
            return g.structs[ty.ReferencedTypeName].ty
775
776
777
778

        field_names = []
        field_types = []
        for field_name in Helper.sorted_fields(basic_ty):
779
            field_names.append(field_name.replace('-', '_'))
780
781
782
            field_types.append(_generate_type(basic_ty.Children[field_name].type))

        struct = StructType(ty.ReferencedTypeName, field_names, field_types)
dbarbera's avatar
dbarbera committed
783
784
        g.structs[ty.ReferencedTypeName] = struct
        return struct.ty
dbarbera's avatar
dbarbera committed
785
786
    elif basic_ty.kind == 'EnumeratedType':
        return g.i32
dbarbera's avatar
dbarbera committed
787
788
789
790
    else:
        raise NotImplementedError


791
792
def _get_string_cons(str):
    ''' Returns a reference to a global string constant with the given value '''
793
794
    if str in g.strings:
        return g.strings[str]
795
796

    str_val = core.Constant.stringz(str)
797
    gvar_name = '.str%s' % len(g.strings)
798
    gvar_val = g.module.add_global_variable(str_val.type, gvar_name)
799
    gvar_val.initializer = str_val
800
    g.strings[str] = gvar_val
801
802
803
    return gvar_val


dbarbera's avatar
dbarbera committed
804
805
806
807
808
809
# TODO: Refactor this into the helper module
def find_basic_type(a_type):
    ''' Return the ASN.1 basic type of a_type '''
    basic_type = a_type
    while basic_type.kind == 'ReferenceType':
        # Find type with proper case in the data view
810
        for typename in g.dataview.viewkeys():
dbarbera's avatar
dbarbera committed
811
            if typename.lower() == basic_type.ReferencedTypeName.lower():
812
                basic_type = g.dataview[typename].type
dbarbera's avatar
dbarbera committed
813
814
                break
    return basic_type