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
145
    g.runtr = _generate_runtr_func(process)
    _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
163
164
165
166
167

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

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

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

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

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

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

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


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

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

215
216
217
218
219
220
    # 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))

221
    builder.call(g.runtr, [core.Constant.int(core.Type.int(), 0)])
Maxime Perrotin's avatar
Maxime Perrotin committed
222
223
    builder.ret_void()

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


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

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

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

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

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

        # TODO: Nested states

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

263
        g.builder.ret_void()
264

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

    func.verify()


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

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

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


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

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

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


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

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


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


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


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


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

dbarbera's avatar
dbarbera committed
366

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

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


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


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

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

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

    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

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


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

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

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
487

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

494
495
    _generate_assign(left, right)
    return left
496

497
498
499
500
501

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

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
516

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
542

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


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


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


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


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


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


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


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


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


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


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


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


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


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

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

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


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


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

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

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

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

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

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

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

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

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


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


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


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


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


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


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

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

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

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


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

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


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