LlvmGenerator.py 26.3 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
296
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
        if basic_ty.kind == 'IntegerType':
297
            fmt_val = _get_string_cons('% d')
298
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
dbarbera's avatar
dbarbera committed
299
            g.builder.call(g.funcs['printf'], [fmt_ptr, expr_val])
300
        elif basic_ty.kind == 'RealType':
301
            fmt_val = _get_string_cons('% .14E')
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 == 'BooleanType':
305
            true_str_val = _get_string_cons('TRUE')
306
            true_str_ptr = g.builder.gep(true_str_val, [zero, zero])
307
            false_str_val = _get_string_cons('FALSE')
308
309
            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
310
            g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
311
312
        elif basic_ty.kind == 'StringType':
            expr_ptr = g.builder.gep(expr_val, [zero, zero])
dbarbera's avatar
dbarbera committed
313
            g.builder.call(g.funcs['printf'], [expr_ptr])
314
315
316
317
318
319
320
        else:
            raise NotImplementedError


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
321
322

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


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 '''
335
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
336
337
338
339


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


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
348
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
349
350
351
352


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

dbarbera's avatar
dbarbera committed
356

Maxime Perrotin's avatar
Maxime Perrotin committed
357
358
359
360
# ------ expressions --------

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
361
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
362
363
364
365
    raise TypeError('Unsupported expression: ' + str(expr))


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


Maxime Perrotin's avatar
Maxime Perrotin committed
371
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
372
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
373
    ''' Generate the code for an of an element list (path) '''
dbarbera's avatar
dbarbera committed
374
375
376
377

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

    if not prim.value:
dbarbera's avatar
dbarbera committed
378
        return g.builder.load(var_ptr)
dbarbera's avatar
dbarbera committed
379
380
381
382
383
384
385
386
387
388
389
390
391

    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

dbarbera's avatar
dbarbera committed
392
    return g.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
393
394


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

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

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
477

478
479
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
480
    ''' Generate the code for an assign expression '''
481
482
    left = expression(expr.left)
    right = expression(expr.right)
483
484
    _generate_assign(left, right)
    return left
485

486
487
488
489
490

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
491
    if left.type.kind == core.TYPE_POINTER and left.type.pointee.kind == core.TYPE_STRUCT:
492
493
        size = core.Constant.sizeof(left.type.pointee)
        align = core.Constant.int(g.i32, 0)
494
        volatile = core.Constant.int(g.i1, 0)
495

496
497
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
498

dbarbera's avatar
dbarbera committed
499
        g.builder.call(g.funcs['memcpy'], [left_ptr, right_ptr, size, align, volatile])
500
    else:
501
        g.builder.store(right, left)
dbarbera's avatar
dbarbera committed
502

Maxime Perrotin's avatar
Maxime Perrotin committed
503

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

dbarbera's avatar
dbarbera committed
522
523
524
    if expr.operand == 'and':
        return g.builder.and_(lefttmp, righttmp, 'andtmp')
    elif expr.operand == 'or':
525
        return g.builder.or_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
526
    else:
527
        return g.builder.xor(lefttmp, righttmp, 'xortmp')
dbarbera's avatar
dbarbera committed
528

Maxime Perrotin's avatar
Maxime Perrotin committed
529

Maxime Perrotin's avatar
Maxime Perrotin committed
530
@expression.register(ogAST.ExprAppend)
Maxime Perrotin's avatar
Maxime Perrotin committed
531
532
def _append(expr):
    ''' Generate code for the APPEND construct: a // b '''
533
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
534
535


Maxime Perrotin's avatar
Maxime Perrotin committed
536
@expression.register(ogAST.ExprIn)
Maxime Perrotin's avatar
Maxime Perrotin committed
537
def _expr_in(expr):
dbarbera's avatar
dbarbera committed
538
    ''' Generate the code for an in expression '''
539
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
540
541


Maxime Perrotin's avatar
Maxime Perrotin committed
542
@expression.register(ogAST.PrimEnumeratedValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
543
544
def _enumerated_value(primary):
    ''' Generate code for an enumerated value '''
dbarbera's avatar
dbarbera committed
545
546
547
    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
548
549


Maxime Perrotin's avatar
Maxime Perrotin committed
550
@expression.register(ogAST.PrimChoiceDeterminant)
Maxime Perrotin's avatar
Maxime Perrotin committed
551
552
def _choice_determinant(primary):
    ''' Generate code for a choice determinant (enumerated) '''
553
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
554
555


Maxime Perrotin's avatar
Maxime Perrotin committed
556
@expression.register(ogAST.PrimInteger)
557
558
def _integer(primary):
    ''' Generate code for a raw integer value  '''
559
    return core.Constant.int(g.i32, primary.value[0])
560
561


Maxime Perrotin's avatar
Maxime Perrotin committed
562
@expression.register(ogAST.PrimReal)
563
564
def _real(primary):
    ''' Generate code for a raw real value  '''
565
    return core.Constant.real(g.double, primary.value[0])
566
567


Maxime Perrotin's avatar
Maxime Perrotin committed
568
@expression.register(ogAST.PrimBoolean)
569
570
def _boolean(primary):
    ''' Generate code for a raw boolean value  '''
dbarbera's avatar
dbarbera committed
571
    if primary.value[0].lower() == 'true':
572
        return core.Constant.int(g.i1, 1)
dbarbera's avatar
dbarbera committed
573
    else:
574
        return core.Constant.int(g.i1, 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
575
576


Maxime Perrotin's avatar
Maxime Perrotin committed
577
@expression.register(ogAST.PrimEmptyString)
Maxime Perrotin's avatar
Maxime Perrotin committed
578
579
def _empty_string(primary):
    ''' Generate code for an empty SEQUENCE OF: {} '''
580
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
581
582


Maxime Perrotin's avatar
Maxime Perrotin committed
583
@expression.register(ogAST.PrimStringLiteral)
Maxime Perrotin's avatar
Maxime Perrotin committed
584
585
def _string_literal(primary):
    ''' Generate code for a string (Octet String) '''
dbarbera's avatar
dbarbera committed
586
    return _get_string_cons(str(primary.value[1:-1]))
Maxime Perrotin's avatar
Maxime Perrotin committed
587
588


Maxime Perrotin's avatar
Maxime Perrotin committed
589
@expression.register(ogAST.PrimConstant)
Maxime Perrotin's avatar
Maxime Perrotin committed
590
591
def _constant(primary):
    ''' Generate code for a reference to an ASN.1 constant '''
592
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
593
594


Maxime Perrotin's avatar
Maxime Perrotin committed
595
@expression.register(ogAST.PrimMantissaBaseExp)
Maxime Perrotin's avatar
Maxime Perrotin committed
596
597
def _mantissa_base_exp(primary):
    ''' Generate code for a Real with Mantissa-base-Exponent representation '''
598
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
599
600


Maxime Perrotin's avatar
Maxime Perrotin committed
601
@expression.register(ogAST.PrimIfThenElse)
dbarbera's avatar
dbarbera committed
602
def _if_then_else(ifthen):
dbarbera's avatar
dbarbera committed
603
    ''' Generate the code for ternary operator '''
604
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
605
606


Maxime Perrotin's avatar
Maxime Perrotin committed
607
@expression.register(ogAST.PrimSequence)
Maxime Perrotin's avatar
Maxime Perrotin committed
608
def _sequence(seq):
dbarbera's avatar
dbarbera committed
609
    ''' Generate the code for an ASN.1 SEQUENCE '''
dbarbera's avatar
dbarbera committed
610
611
612
613
614
615
616
617
618
619
620
    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
621
622


Maxime Perrotin's avatar
Maxime Perrotin committed
623
@expression.register(ogAST.PrimSequenceOf)
Maxime Perrotin's avatar
Maxime Perrotin committed
624
def _sequence_of(seqof):
dbarbera's avatar
dbarbera committed
625
    ''' Generate the code for an ASN.1 SEQUENCE OF '''
626
    ty = _generate_type(seqof.exprType)
627
628
629
    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])
630
631

    for idx, expr in enumerate(seqof.value):
632
        idx_cons = core.Constant.int(g.i32, idx)
633
        expr_val = expression(expr)
634
635
        pos_ptr = g.builder.gep(array_ptr, [zero_cons, idx_cons])
        g.builder.store(expr_val, pos_ptr)
636
637

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
638
639


Maxime Perrotin's avatar
Maxime Perrotin committed
640
@expression.register(ogAST.PrimChoiceItem)
Maxime Perrotin's avatar
Maxime Perrotin committed
641
def _choiceitem(choice):
dbarbera's avatar
dbarbera committed
642
    ''' Generate the code for a CHOICE expression '''
643
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
644
645
646
647


@generate.register(ogAST.Decision)
def _decision(dec):
dbarbera's avatar
dbarbera committed
648
    ''' Generate the code for a decision '''
649
    func = g.builder.basic_block.function
dbarbera's avatar
dbarbera committed
650
651
652
653

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

654
    g.builder.branch(ans_cond_blocks[0])
dbarbera's avatar
dbarbera committed
655
656
657
658
659

    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')
660
        g.builder.position_at_end(ans_cond_block)
dbarbera's avatar
dbarbera committed
661
662
663
664
665
666
667
668
669

        if ans.kind == 'constant':
            next_block = ans_cond_blocks[idx+1] if idx < len(ans_cond_blocks) else end_block

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

670
671
672
            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
673
674
        elif ans.kind == 'else':
            if ans.transition:
675
                g.builder.branch(ans_tr_block)
dbarbera's avatar
dbarbera committed
676
            else:
677
                g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
678
679
680
681
        else:
            raise NotImplementedError

        if ans.transition:
682
            g.builder.position_at_end(ans_tr_block)
dbarbera's avatar
dbarbera committed
683
            generate(ans.transition)
684
            g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
685

686
    g.builder.position_at_end(end_block)
Maxime Perrotin's avatar
Maxime Perrotin committed
687
688
689
690


@generate.register(ogAST.Label)
def _label(tr):
dbarbera's avatar
dbarbera committed
691
    ''' TGenerate the code for a Label '''
692
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
693
694
695
696


@generate.register(ogAST.Transition)
def _transition(tr):
dbarbera's avatar
dbarbera committed
697
    ''' Generate the code for a transition '''
698
699
700
701
702
703
    for action in tr.actions:
        generate(action)
        if isinstance(action, ogAST.Label):
            return
    if tr.terminator:
        _generate_terminator(tr.terminator)
704
705


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


@generate.register(ogAST.Floating_label)
def _floating_label(label):
dbarbera's avatar
dbarbera committed
732
    ''' Generate the code for a floating label '''
733
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
734
735
736
737
738


@generate.register(ogAST.Procedure)
def _inner_procedure(proc):
    ''' Generate the code for a procedure '''
739
    raise NotImplementedError
dbarbera's avatar
dbarbera committed
740
741
742


def _generate_type(ty):
743
    ''' Generate the equivalent LLVM type of a ASN.1 type '''
dbarbera's avatar
dbarbera committed
744
745
    basic_ty = find_basic_type(ty)
    if basic_ty.kind == 'IntegerType':
746
        return g.i32
dbarbera's avatar
dbarbera committed
747
    elif basic_ty.kind == 'BooleanType':
748
        return g.i1
dbarbera's avatar
dbarbera committed
749
    elif basic_ty.kind == 'RealType':
750
        return g.double
751
    elif basic_ty.kind == 'SequenceOfType':
dbarbera's avatar
dbarbera committed
752
753
        if ty.ReferencedTypeName in g.structs:
            return g.structs[ty.ReferencedTypeName].ty
754
755
756
757
758
759
760
761

        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)
762
        struct = StructType(ty.ReferencedTypeName, ['_'], [array_ty])
dbarbera's avatar
dbarbera committed
763
764
765
766
767
        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
768
769
770
771
772
773
774
775

        field_names = []
        field_types = []
        for field_name in Helper.sorted_fields(basic_ty):
            field_names.append(field_name)
            field_types.append(_generate_type(basic_ty.Children[field_name].type))

        struct = StructType(ty.ReferencedTypeName, field_names, field_types)
dbarbera's avatar
dbarbera committed
776
777
        g.structs[ty.ReferencedTypeName] = struct
        return struct.ty
dbarbera's avatar
dbarbera committed
778
779
    elif basic_ty.kind == 'EnumeratedType':
        return g.i32
dbarbera's avatar
dbarbera committed
780
781
782
783
    else:
        raise NotImplementedError


784
785
def _get_string_cons(str):
    ''' Returns a reference to a global string constant with the given value '''
786
787
    if str in g.strings:
        return g.strings[str]
788
789

    str_val = core.Constant.stringz(str)
790
    gvar_name = '.str%s' % len(g.strings)
791
    gvar_val = g.module.add_global_variable(str_val.type, gvar_name)
792
    gvar_val.initializer = str_val
793
    g.strings[str] = gvar_val
794
795
796
    return gvar_val


dbarbera's avatar
dbarbera committed
797
798
799
800
801
802
# 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
803
        for typename in g.dataview.viewkeys():
dbarbera's avatar
dbarbera committed
804
            if typename.lower() == basic_type.ReferencedTypeName.lower():
805
                basic_type = g.dataview[typename].type
dbarbera's avatar
dbarbera committed
806
807
                break
    return basic_type