LlvmGenerator.py 26.7 KB
Newer Older
Maxime Perrotin's avatar
Maxime Perrotin committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    OpenGEODE - A tiny SDL Editor for TASTE

    This module generates LLVM IR code from SDL process models, allowing
    generation of a binary application without an intermediate language.
    LLVM also allows for various code verification, analysis, and optimization.

    The design is based on the Ada code generator. Check it for details.

    Copyright (c) 2012-2013 European Space Agency

    Designed and implemented by Maxime Perrotin

    Contact: maxime.perrotin@esa.int
"""

import logging
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

114
115
116
117
118
119
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
        var_ty = _generate_type(ty)
        global_var = g.module.add_global_variable(var_ty, str(name).lower())
        global_var.initializer = core.Constant.null(var_ty)

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

    # 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]
134
        func_ty = core.Type.function(g.void, param_tys)
dbarbera's avatar
dbarbera committed
135
136
137
        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
138

139
140
141
    # Generate internal procedures
    for proc in process.content.inner_procedures:
        raise NotImplementedError
142

143
    # Generate process functions
144
    _generate_runtr_func(process)
145
    _generate_startup_func(process)
146

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

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

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


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

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

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

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

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

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

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

    func.verify()
202
    g.scope.clear()
203
204
205
    return func


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

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

216
217
218
219
220
221
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
            global_var = g.module.get_global_variable_named(str(name).lower())
            _generate_assign(global_var, expression(expr))

222
223
    g.builder.call(g.funcs['run_transition'], [core.Constant.int(g.i32, 0)])
    g.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
224

225
226
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
227
228


229
def _generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
230
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
231
    func_name = g.name + "_" + str(signal['name'])
232
233
234
    param_tys = []
    if 'type' in signal:
        param_tys.append(core.Type.pointer(_generate_type(signal['type'])))
235
236
    func_type = core.Type.function(g.void, param_tys)
    func = core.Function.new(g.module, func_type, func_name)
dbarbera's avatar
dbarbera committed
237
    g.funcs[func_name.lower()] = func
238
239
240

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

243
    runtr_func = g.module.get_function_named('run_transition')
244

245
246
    g_state_val = g.builder.load(g.module.get_global_variable_named('state'))
    switch = g.builder.switch(g_state_val, exit_block)
247

248
    for state_name, state_id in g.states.iteritems():
249
250
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
251
        g.builder.position_at_end(state_block)
252
253
254

        # TODO: Nested states

255
256
257
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
258
                var_val = g.module.get_global_variable_named(str(var_name).lower())
259
260
                _generate_assign(var_val, func.args[0])
            if input.transition:
261
262
                id_val = core.Constant.int(g.i32, input.transition_id)
                g.builder.call(runtr_func, [id_val])
263

264
        g.builder.ret_void()
265

266
267
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
268
269
270
271

    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
272
273
274
275
@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
276
277
278
279

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

280
        if name == 'write':
dbarbera's avatar
dbarbera committed
281
282
            _generate_write(out['params'])
            continue
283
284
285
        elif name == 'writeln':
            _generate_writeln(out['params'])
            continue
dbarbera's avatar
dbarbera committed
286
287
288
289
290
291
292
        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
293
        func = g.funcs[str(name).lower()]
294
        g.builder.call(func, [expression(p) for p in out.get('params', [])])
dbarbera's avatar
dbarbera committed
295
296
297
298


def _generate_write(params):
    ''' Generate the code for the write operator '''
299
    zero = core.Constant.int(g.i32, 0)
300
301
302
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
303
304
305
306

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

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


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
332
333

    zero = core.Constant.int(g.i32, 0)
334
    str_cons = _get_string_cons('\n')
335
    str_ptr = g.builder.gep(str_cons, [zero, zero])
dbarbera's avatar
dbarbera committed
336
    g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
337
338
339
340
341
342
343
344
345


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 '''
346
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
347
348
349
350


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
351
    ''' Generate the code of a list of assignments '''
352
353
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
354
355
356
357
358


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
359
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
360
361
362
363


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

dbarbera's avatar
dbarbera committed
367

Maxime Perrotin's avatar
Maxime Perrotin committed
368
369
370
371
# ------ expressions --------

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
372
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
373
374
375
376
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
377
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
378
    ''' Generate the code for a single variable reference '''
379
    return g.module.get_global_variable_named(str(prim.value[0]).lower())
Maxime Perrotin's avatar
Maxime Perrotin committed
380
381


Maxime Perrotin's avatar
Maxime Perrotin committed
382
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
383
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
384
    ''' Generate the code for an of an element list (path) '''
dbarbera's avatar
dbarbera committed
385
386
387
388

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

    if not prim.value:
389
        return var_ptr
dbarbera's avatar
dbarbera committed
390
391
392
393
394
395
396
397
398
399
400
401
402

    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

403
    return var_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
404
405


Maxime Perrotin's avatar
Maxime Perrotin committed
406
407
408
409
410
411
412
413
414
415
416
417
@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
418
419
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
420
421
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
422
423

    # load the value of the expression if it is a pointer
424
    if lefttmp.type.kind == core.TYPE_POINTER:
425
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
426
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
427
        righttmp = g.builder.load(righttmp, 'righttmp')
428
429
430
431
432
433

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
488

489
490
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
491
    ''' Generate the code for an assign expression '''
492
493
    left = expression(expr.left)
    right = expression(expr.right)
494

495
496
    _generate_assign(left, right)
    return left
497

498
499
500
501
502

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
503
    if left.type.pointee.kind == core.TYPE_STRUCT:
504
505
        size = core.Constant.sizeof(left.type.pointee)
        align = core.Constant.int(g.i32, 0)
506
        volatile = core.Constant.int(g.i1, 0)
507

508
509
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
510

dbarbera's avatar
dbarbera committed
511
        g.builder.call(g.funcs['memcpy'], [left_ptr, right_ptr, size, align, volatile])
512
    else:
513
514
        if right.type.kind == core.TYPE_POINTER:
            right = g.builder.load(right)
515
        g.builder.store(right, left)
dbarbera's avatar
dbarbera committed
516

Maxime Perrotin's avatar
Maxime Perrotin committed
517

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

dbarbera's avatar
dbarbera committed
536
537
538
    if expr.operand == 'and':
        return g.builder.and_(lefttmp, righttmp, 'andtmp')
    elif expr.operand == 'or':
539
        return g.builder.or_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
540
    else:
541
        return g.builder.xor(lefttmp, righttmp, 'xortmp')
dbarbera's avatar
dbarbera committed
542

Maxime Perrotin's avatar
Maxime Perrotin committed
543

Maxime Perrotin's avatar
Maxime Perrotin committed
544
@expression.register(ogAST.ExprAppend)
Maxime Perrotin's avatar
Maxime Perrotin committed
545
546
def _append(expr):
    ''' Generate code for the APPEND construct: a // b '''
547
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
548
549


Maxime Perrotin's avatar
Maxime Perrotin committed
550
@expression.register(ogAST.ExprIn)
Maxime Perrotin's avatar
Maxime Perrotin committed
551
def _expr_in(expr):
dbarbera's avatar
dbarbera committed
552
    ''' Generate the code for an in expression '''
553
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
554
555


Maxime Perrotin's avatar
Maxime Perrotin committed
556
@expression.register(ogAST.PrimEnumeratedValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
557
558
def _enumerated_value(primary):
    ''' Generate code for an enumerated value '''
dbarbera's avatar
dbarbera committed
559
560
561
    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
562
563


Maxime Perrotin's avatar
Maxime Perrotin committed
564
@expression.register(ogAST.PrimChoiceDeterminant)
Maxime Perrotin's avatar
Maxime Perrotin committed
565
566
def _choice_determinant(primary):
    ''' Generate code for a choice determinant (enumerated) '''
567
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
568
569


Maxime Perrotin's avatar
Maxime Perrotin committed
570
@expression.register(ogAST.PrimInteger)
571
572
def _integer(primary):
    ''' Generate code for a raw integer value  '''
573
    return core.Constant.int(g.i32, primary.value[0])
574
575


Maxime Perrotin's avatar
Maxime Perrotin committed
576
@expression.register(ogAST.PrimReal)
577
578
def _real(primary):
    ''' Generate code for a raw real value  '''
579
    return core.Constant.real(g.double, primary.value[0])
580
581


Maxime Perrotin's avatar
Maxime Perrotin committed
582
@expression.register(ogAST.PrimBoolean)
583
584
def _boolean(primary):
    ''' Generate code for a raw boolean value  '''
dbarbera's avatar
dbarbera committed
585
    if primary.value[0].lower() == 'true':
586
        return core.Constant.int(g.i1, 1)
dbarbera's avatar
dbarbera committed
587
    else:
588
        return core.Constant.int(g.i1, 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
589
590


Maxime Perrotin's avatar
Maxime Perrotin committed
591
@expression.register(ogAST.PrimEmptyString)
Maxime Perrotin's avatar
Maxime Perrotin committed
592
593
def _empty_string(primary):
    ''' Generate code for an empty SEQUENCE OF: {} '''
594
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
595
596


Maxime Perrotin's avatar
Maxime Perrotin committed
597
@expression.register(ogAST.PrimStringLiteral)
Maxime Perrotin's avatar
Maxime Perrotin committed
598
599
def _string_literal(primary):
    ''' Generate code for a string (Octet String) '''
dbarbera's avatar
dbarbera committed
600
    return _get_string_cons(str(primary.value[1:-1]))
Maxime Perrotin's avatar
Maxime Perrotin committed
601
602


Maxime Perrotin's avatar
Maxime Perrotin committed
603
@expression.register(ogAST.PrimConstant)
Maxime Perrotin's avatar
Maxime Perrotin committed
604
605
def _constant(primary):
    ''' Generate code for a reference to an ASN.1 constant '''
606
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
607
608


Maxime Perrotin's avatar
Maxime Perrotin committed
609
@expression.register(ogAST.PrimMantissaBaseExp)
Maxime Perrotin's avatar
Maxime Perrotin committed
610
611
def _mantissa_base_exp(primary):
    ''' Generate code for a Real with Mantissa-base-Exponent representation '''
612
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
613
614


Maxime Perrotin's avatar
Maxime Perrotin committed
615
@expression.register(ogAST.PrimIfThenElse)
dbarbera's avatar
dbarbera committed
616
def _if_then_else(ifthen):
dbarbera's avatar
dbarbera committed
617
    ''' Generate the code for ternary operator '''
618
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
619
620


Maxime Perrotin's avatar
Maxime Perrotin committed
621
@expression.register(ogAST.PrimSequence)
Maxime Perrotin's avatar
Maxime Perrotin committed
622
def _sequence(seq):
dbarbera's avatar
dbarbera committed
623
    ''' Generate the code for an ASN.1 SEQUENCE '''
dbarbera's avatar
dbarbera committed
624
625
626
627
628
629
630
631
632
633
634
    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
635
636


Maxime Perrotin's avatar
Maxime Perrotin committed
637
@expression.register(ogAST.PrimSequenceOf)
Maxime Perrotin's avatar
Maxime Perrotin committed
638
def _sequence_of(seqof):
dbarbera's avatar
dbarbera committed
639
    ''' Generate the code for an ASN.1 SEQUENCE OF '''
640
    ty = _generate_type(seqof.exprType)
641
642
643
    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])
644
645

    for idx, expr in enumerate(seqof.value):
646
        idx_cons = core.Constant.int(g.i32, idx)
647
        expr_val = expression(expr)
648
649
        pos_ptr = g.builder.gep(array_ptr, [zero_cons, idx_cons])
        g.builder.store(expr_val, pos_ptr)
650
651

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
652
653


Maxime Perrotin's avatar
Maxime Perrotin committed
654
@expression.register(ogAST.PrimChoiceItem)
Maxime Perrotin's avatar
Maxime Perrotin committed
655
def _choiceitem(choice):
dbarbera's avatar
dbarbera committed
656
    ''' Generate the code for a CHOICE expression '''
657
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
658
659
660
661


@generate.register(ogAST.Decision)
def _decision(dec):
dbarbera's avatar
dbarbera committed
662
    ''' Generate the code for a decision '''
663
    func = g.builder.basic_block.function
dbarbera's avatar
dbarbera committed
664
665
666
667

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

668
    g.builder.branch(ans_cond_blocks[0])
dbarbera's avatar
dbarbera committed
669
670
671
672
673

    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')
674
        g.builder.position_at_end(ans_cond_block)
dbarbera's avatar
dbarbera committed
675
676

        if ans.kind == 'constant':
dbarbera's avatar
dbarbera committed
677
            next_block = ans_cond_blocks[idx+1] if idx < len(ans_cond_blocks)-1 else end_block
dbarbera's avatar
dbarbera committed
678
679
680
681
682
683

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

684
685
686
            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
687
688
        elif ans.kind == 'else':
            if ans.transition:
689
                g.builder.branch(ans_tr_block)
dbarbera's avatar
dbarbera committed
690
            else:
691
                g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
692
693
694
695
        else:
            raise NotImplementedError

        if ans.transition:
696
            g.builder.position_at_end(ans_tr_block)
dbarbera's avatar
dbarbera committed
697
            generate(ans.transition)
698
            g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
699

700
    g.builder.position_at_end(end_block)
Maxime Perrotin's avatar
Maxime Perrotin committed
701
702
703
704


@generate.register(ogAST.Label)
def _label(tr):
dbarbera's avatar
dbarbera committed
705
    ''' TGenerate the code for a Label '''
706
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
707
708
709
710


@generate.register(ogAST.Transition)
def _transition(tr):
dbarbera's avatar
dbarbera committed
711
    ''' Generate the code for a transition '''
712
713
714
715
716
717
    for action in tr.actions:
        generate(action)
        if isinstance(action, ogAST.Label):
            return
    if tr.terminator:
        _generate_terminator(tr.terminator)
718
719


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


@generate.register(ogAST.Floating_label)
def _floating_label(label):
dbarbera's avatar
dbarbera committed
746
    ''' Generate the code for a floating label '''
747
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
748
749
750
751
752


@generate.register(ogAST.Procedure)
def _inner_procedure(proc):
    ''' Generate the code for a procedure '''
753
    raise NotImplementedError
dbarbera's avatar
dbarbera committed
754
755
756


def _generate_type(ty):
757
    ''' Generate the equivalent LLVM type of a ASN.1 type '''
dbarbera's avatar
dbarbera committed
758
759
    basic_ty = find_basic_type(ty)
    if basic_ty.kind == 'IntegerType':
760
        return g.i32
dbarbera's avatar
dbarbera committed
761
    elif basic_ty.kind == 'BooleanType':
762
        return g.i1
dbarbera's avatar
dbarbera committed
763
    elif basic_ty.kind == 'RealType':
764
        return g.double
765
    elif basic_ty.kind == 'SequenceOfType':
dbarbera's avatar
dbarbera committed
766
767
        if ty.ReferencedTypeName in g.structs:
            return g.structs[ty.ReferencedTypeName].ty
768
769
770
771
772
773
774
775

        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)
776
        struct = StructType(ty.ReferencedTypeName, ['_'], [array_ty])
dbarbera's avatar
dbarbera committed
777
778
779
780
781
        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
782
783
784
785

        field_names = []
        field_types = []
        for field_name in Helper.sorted_fields(basic_ty):
786
            field_names.append(field_name.replace('-', '_'))
787
788
789
            field_types.append(_generate_type(basic_ty.Children[field_name].type))

        struct = StructType(ty.ReferencedTypeName, field_names, field_types)
dbarbera's avatar
dbarbera committed
790
791
        g.structs[ty.ReferencedTypeName] = struct
        return struct.ty
dbarbera's avatar
dbarbera committed
792
793
    elif basic_ty.kind == 'EnumeratedType':
        return g.i32
dbarbera's avatar
dbarbera committed
794
795
796
797
    else:
        raise NotImplementedError


798
799
def _get_string_cons(str):
    ''' Returns a reference to a global string constant with the given value '''
800
801
    if str in g.strings:
        return g.strings[str]
802
803

    str_val = core.Constant.stringz(str)
804
    gvar_name = '.str%s' % len(g.strings)
805
    gvar_val = g.module.add_global_variable(str_val.type, gvar_name)
806
    gvar_val.initializer = str_val
807
    g.strings[str] = gvar_val
808
809
810
    return gvar_val


dbarbera's avatar
dbarbera committed
811
812
813
814
815
816
# 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
817
        for typename in g.dataview.viewkeys():
dbarbera's avatar
dbarbera committed
818
            if typename.lower() == basic_type.ReferencedTypeName.lower():
819
                basic_type = g.dataview[typename].type
dbarbera's avatar
dbarbera committed
820
821
                break
    return basic_type