LlvmGenerator.py 25.8 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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
        self.strings = {}

        # 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)
        self.printf = self.module.add_function(ty, 'printf')

        self.memcpy = core.Function.intrinsic(
            self.module, core.INTR_MEMCPY,
            [self.i8_ptr, self.i8_ptr, self.i64]
        )
Maxime Perrotin's avatar
Maxime Perrotin committed
68

dbarbera's avatar
dbarbera committed
69

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



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

dbarbera's avatar
dbarbera committed
86

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

93
94
    global g
    g = GlobalState(process)
95

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

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

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

dbarbera's avatar
dbarbera committed
113
114
    # Initialize output signals
    for signal in process.output_signals:
115
116
117
118
        if 'type' in signal:
            param_tys = [core.Type.pointer(_generate_type(signal['type']))]
        else:
            param_tys = []
119
120
        func_ty = core.Type.function(g.void, param_tys)
        core.Function.new(g.module, func_ty, str(signal['name']))
dbarbera's avatar
dbarbera committed
121
122
123
124

    # 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]
125
126
        func_ty = core.Type.function(g.void, param_tys)
        core.Function.new(g.module, func_ty, str(proc.inputString))
dbarbera's avatar
dbarbera committed
127

128
    # Generare process-level vars
dbarbera's avatar
dbarbera committed
129
    for var_name, (var_asn1_type, def_value) in process.variables.viewitems():
130
        var_type = _generate_type(var_asn1_type)
131
        global_var = g.module.add_global_variable(var_type, str(var_name).lower())
dbarbera's avatar
dbarbera committed
132
        global_var.initializer = core.Constant.null(var_type)
133

134
        if def_value:
dbarbera's avatar
dbarbera committed
135
            raise NotImplementedError
136

137
    # Generate process functions
138
139
    g.runtr = _generate_runtr_func(process)
    _generate_startup_func(process)
140

141
142
143
144
    # Generate input signals
    for signal in process.input_signals:
        _generate_input_signal(signal, mapping[signal['name']])

dbarbera's avatar
dbarbera committed
145
146
    g.module.verify()

dbarbera's avatar
dbarbera committed
147
    with open(g.name  + '.ll', 'w') as ll_file:
dbarbera's avatar
dbarbera committed
148
        ll_file.write(str(g.module))
149
150
151


def _generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
152
    ''' Generate code for the run_transition function '''
153
    func_name = 'run_transition'
154
155
    func_type = core.Type.function(g.void, [g.i32])
    func = core.Function.new(g.module, func_type, func_name)
156
157
158
159
160
161

    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
162
    g.builder = core.Builder.new(entry_block)
163
164

    # entry
dbarbera's avatar
dbarbera committed
165
    id_ptr = g.builder.alloca(g.i32, None, 'id')
166
    g.scope['id'] = id_ptr
dbarbera's avatar
dbarbera committed
167
168
    g.builder.store(func.args[0], id_ptr)
    g.builder.branch(cond_block)
169
170

    # cond
dbarbera's avatar
dbarbera committed
171
    g.builder.position_at_end(cond_block)
172
    no_tr_cons = core.Constant.int(g.i32, -1)
173
174
    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
175
    g.builder.cbranch(cond_val, body_block, exit_block)
176
177

    # body
dbarbera's avatar
dbarbera committed
178
179
    g.builder.position_at_end(body_block)
    switch = g.builder.switch(func.args[0], exit_block)
180
181
182
183

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
184
        const = core.Constant.int(g.i32, idx)
185
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
186
        g.builder.position_at_end(tr_block)
187
        generate(tr)
dbarbera's avatar
dbarbera committed
188
        g.builder.branch(cond_block)
189
190

    # exit
dbarbera's avatar
dbarbera committed
191
192
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
193
194

    func.verify()
195
    g.scope.clear()
196
197
198
    return func


199
def _generate_startup_func(process):
dbarbera's avatar
dbarbera committed
200
    ''' Generate code for the startup function '''
dbarbera's avatar
dbarbera committed
201
    func_name = g.name + '_startup'
202
203
    func_type = core.Type.function(g.void, [])
    func = core.Function.new(g.module, func_type, func_name)
204
205
206

    entry_block = func.append_basic_block('entry')
    builder = core.Builder.new(entry_block)
207
    g.builder = builder
208
209

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

213
214
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
215
216


217
def _generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
218
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
219
    func_name = g.name + "_" + str(signal['name'])
220
221
222
    param_tys = []
    if 'type' in signal:
        param_tys.append(core.Type.pointer(_generate_type(signal['type'])))
223
224
    func_type = core.Type.function(g.void, param_tys)
    func = core.Function.new(g.module, func_type, func_name)
225
226
227

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

230
    runtr_func = g.module.get_function_named('run_transition')
231

232
233
    g_state_val = g.builder.load(g.module.get_global_variable_named('state'))
    switch = g.builder.switch(g_state_val, exit_block)
234

235
    for state_name, state_id in g.states.iteritems():
236
237
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
238
        g.builder.position_at_end(state_block)
239
240
241

        # TODO: Nested states

242
243
244
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
245
                var_val = g.module.get_global_variable_named(str(var_name).lower())
246
247
                _generate_assign(var_val, func.args[0])
            if input.transition:
248
249
                id_val = core.Constant.int(g.i32, input.transition_id)
                g.builder.call(runtr_func, [id_val])
250

251
        g.builder.ret_void()
252

253
254
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
255
256
257
258

    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
259
260
261
262
@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
263
264
265
266

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

267
        if name == 'write':
dbarbera's avatar
dbarbera committed
268
269
            _generate_write(out['params'])
            continue
270
271
272
        elif name == 'writeln':
            _generate_writeln(out['params'])
            continue
dbarbera's avatar
dbarbera committed
273
274
275
276
277
278
279
        elif name == 'reset_timer':
            _generate_reset_timer(out['params'])
            continue
        elif name == 'set_timer':
            _generate_set_timer(out['params'])
            continue

280
281
        func = g.module.get_function_named(str(name))
        g.builder.call(func, [expression(p) for p in out.get('params', [])])
dbarbera's avatar
dbarbera committed
282
283
284
285


def _generate_write(params):
    ''' Generate the code for the write operator '''
286
    zero = core.Constant.int(g.i32, 0)
287
288
289
290
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
        if basic_ty.kind == 'IntegerType':
291
            fmt_val = _get_string_cons('% d')
292
293
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
            g.builder.call(g.printf, [fmt_ptr, expr_val])
294
        elif basic_ty.kind == 'RealType':
295
            fmt_val = _get_string_cons('% .14E')
296
297
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
            g.builder.call(g.printf, [fmt_ptr, expr_val])
298
        elif basic_ty.kind == 'BooleanType':
299
            true_str_val = _get_string_cons('TRUE')
300
            true_str_ptr = g.builder.gep(true_str_val, [zero, zero])
301
            false_str_val = _get_string_cons('FALSE')
302
303
304
            false_str_ptr = g.builder.gep(false_str_val, [zero, zero])
            str_ptr = g.builder.select(expr_val, true_str_ptr, false_str_ptr)
            g.builder.call(g.printf, [str_ptr])
305
306
307
308
309
310
311
        else:
            raise NotImplementedError


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
312
313

    zero = core.Constant.int(g.i32, 0)
314
    str_cons = _get_string_cons('\n')
315
316
    str_ptr = g.builder.gep(str_cons, [zero, zero])
    g.builder.call(g.printf, [str_ptr])
dbarbera's avatar
dbarbera committed
317
318
319
320
321
322
323
324
325


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 '''
326
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
327
328
329
330


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
331
    ''' Generate the code of a list of assignments '''
332
333
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
334
335
336
337
338


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
339
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
340
341
342
343


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

dbarbera's avatar
dbarbera committed
347

Maxime Perrotin's avatar
Maxime Perrotin committed
348
349
350
351
# ------ expressions --------

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
352
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
353
354
355
356
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
357
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
358
    ''' Generate the code for a single variable reference '''
359
    return g.module.get_global_variable_named(str(prim.value[0]).lower())
Maxime Perrotin's avatar
Maxime Perrotin committed
360
361


Maxime Perrotin's avatar
Maxime Perrotin committed
362
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
363
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
364
    ''' Generate the code for an of an element list (path) '''
dbarbera's avatar
dbarbera committed
365
366
367
368

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

    if not prim.value:
dbarbera's avatar
dbarbera committed
369
        return g.builder.load(var_ptr)
dbarbera's avatar
dbarbera committed
370
371
372
373
374
375
376
377
378
379
380
381
382

    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
383
    return g.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
384
385


Maxime Perrotin's avatar
Maxime Perrotin committed
386
387
388
389
390
391
392
393
394
395
396
397
@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
398
399
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
400
401
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
402
403

    # load the value of the expression if it is a pointer
404
    if lefttmp.type.kind == core.TYPE_POINTER:
405
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
406
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
407
        righttmp = g.builder.load(righttmp, 'righttmp')
408
409
410
411
412
413

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
468

469
470
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
471
    ''' Generate the code for an assign expression '''
472
473
    left = expression(expr.left)
    right = expression(expr.right)
474
475
    _generate_assign(left, right)
    return left
476

477
478
479
480
481

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
482
    if left.type.kind == core.TYPE_POINTER and left.type.pointee.kind == core.TYPE_STRUCT:
483
484
        size = core.Constant.sizeof(left.type.pointee)
        align = core.Constant.int(g.i32, 0)
485
        volatile = core.Constant.int(g.i1, 0)
486

487
488
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
489

490
        g.builder.call(g.memcpy, [left_ptr, right_ptr, size, align, volatile])
491
    else:
492
        g.builder.store(right, left)
dbarbera's avatar
dbarbera committed
493

Maxime Perrotin's avatar
Maxime Perrotin committed
494

Maxime Perrotin's avatar
Maxime Perrotin committed
495
496
497
@expression.register(ogAST.ExprOr)
@expression.register(ogAST.ExprAnd)
@expression.register(ogAST.ExprXor)
dbarbera's avatar
dbarbera committed
498
499
def _logical(expr):
    ''' Generate the code for a logical expression '''
dbarbera's avatar
dbarbera committed
500
501
502
503
504
505
506
507
508
    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:
509
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
dbarbera's avatar
dbarbera committed
510
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
511
        righttmp = g.builder.load(righttmp, 'righttmp')
dbarbera's avatar
dbarbera committed
512

dbarbera's avatar
dbarbera committed
513
514
515
    if expr.operand == 'and':
        return g.builder.and_(lefttmp, righttmp, 'andtmp')
    elif expr.operand == 'or':
516
        return g.builder.or_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
517
    else:
518
        return g.builder.xor(lefttmp, righttmp, 'xortmp')
dbarbera's avatar
dbarbera committed
519

Maxime Perrotin's avatar
Maxime Perrotin committed
520

Maxime Perrotin's avatar
Maxime Perrotin committed
521
@expression.register(ogAST.ExprAppend)
Maxime Perrotin's avatar
Maxime Perrotin committed
522
523
def _append(expr):
    ''' Generate code for the APPEND construct: a // b '''
524
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
525
526


Maxime Perrotin's avatar
Maxime Perrotin committed
527
@expression.register(ogAST.ExprIn)
Maxime Perrotin's avatar
Maxime Perrotin committed
528
def _expr_in(expr):
dbarbera's avatar
dbarbera committed
529
    ''' Generate the code for an in expression '''
530
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
531
532


Maxime Perrotin's avatar
Maxime Perrotin committed
533
@expression.register(ogAST.PrimEnumeratedValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
534
535
def _enumerated_value(primary):
    ''' Generate code for an enumerated value '''
dbarbera's avatar
dbarbera committed
536
537
538
    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
539
540


Maxime Perrotin's avatar
Maxime Perrotin committed
541
@expression.register(ogAST.PrimChoiceDeterminant)
Maxime Perrotin's avatar
Maxime Perrotin committed
542
543
def _choice_determinant(primary):
    ''' Generate code for a choice determinant (enumerated) '''
544
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
545
546


Maxime Perrotin's avatar
Maxime Perrotin committed
547
@expression.register(ogAST.PrimInteger)
548
549
def _integer(primary):
    ''' Generate code for a raw integer value  '''
550
    return core.Constant.int(g.i32, primary.value[0])
551
552


Maxime Perrotin's avatar
Maxime Perrotin committed
553
@expression.register(ogAST.PrimReal)
554
555
def _real(primary):
    ''' Generate code for a raw real value  '''
556
    return core.Constant.real(g.double, primary.value[0])
557
558


Maxime Perrotin's avatar
Maxime Perrotin committed
559
@expression.register(ogAST.PrimBoolean)
560
561
def _boolean(primary):
    ''' Generate code for a raw boolean value  '''
dbarbera's avatar
dbarbera committed
562
    if primary.value[0].lower() == 'true':
563
        return core.Constant.int(g.i1, 1)
dbarbera's avatar
dbarbera committed
564
    else:
565
        return core.Constant.int(g.i1, 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
566
567


Maxime Perrotin's avatar
Maxime Perrotin committed
568
@expression.register(ogAST.PrimEmptyString)
Maxime Perrotin's avatar
Maxime Perrotin committed
569
570
def _empty_string(primary):
    ''' Generate code for an empty SEQUENCE OF: {} '''
571
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
572
573


Maxime Perrotin's avatar
Maxime Perrotin committed
574
@expression.register(ogAST.PrimStringLiteral)
Maxime Perrotin's avatar
Maxime Perrotin committed
575
576
def _string_literal(primary):
    ''' Generate code for a string (Octet String) '''
577
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
578
579


Maxime Perrotin's avatar
Maxime Perrotin committed
580
@expression.register(ogAST.PrimConstant)
Maxime Perrotin's avatar
Maxime Perrotin committed
581
582
def _constant(primary):
    ''' Generate code for a reference to an ASN.1 constant '''
583
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
584
585


Maxime Perrotin's avatar
Maxime Perrotin committed
586
@expression.register(ogAST.PrimMantissaBaseExp)
Maxime Perrotin's avatar
Maxime Perrotin committed
587
588
def _mantissa_base_exp(primary):
    ''' Generate code for a Real with Mantissa-base-Exponent representation '''
589
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
590
591


Maxime Perrotin's avatar
Maxime Perrotin committed
592
@expression.register(ogAST.PrimIfThenElse)
dbarbera's avatar
dbarbera committed
593
def _if_then_else(ifthen):
dbarbera's avatar
dbarbera committed
594
    ''' Generate the code for ternary operator '''
595
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
596
597


Maxime Perrotin's avatar
Maxime Perrotin committed
598
@expression.register(ogAST.PrimSequence)
Maxime Perrotin's avatar
Maxime Perrotin committed
599
def _sequence(seq):
dbarbera's avatar
dbarbera committed
600
    ''' Generate the code for an ASN.1 SEQUENCE '''
dbarbera's avatar
dbarbera committed
601
602
603
604
605
606
607
608
609
610
611
    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
612
613


Maxime Perrotin's avatar
Maxime Perrotin committed
614
@expression.register(ogAST.PrimSequenceOf)
Maxime Perrotin's avatar
Maxime Perrotin committed
615
def _sequence_of(seqof):
dbarbera's avatar
dbarbera committed
616
    ''' Generate the code for an ASN.1 SEQUENCE OF '''
617
    ty = _generate_type(seqof.exprType)
618
619
620
    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])
621
622

    for idx, expr in enumerate(seqof.value):
623
        idx_cons = core.Constant.int(g.i32, idx)
624
        expr_val = expression(expr)
625
626
        pos_ptr = g.builder.gep(array_ptr, [zero_cons, idx_cons])
        g.builder.store(expr_val, pos_ptr)
627
628

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
629
630


Maxime Perrotin's avatar
Maxime Perrotin committed
631
@expression.register(ogAST.PrimChoiceItem)
Maxime Perrotin's avatar
Maxime Perrotin committed
632
def _choiceitem(choice):
dbarbera's avatar
dbarbera committed
633
    ''' Generate the code for a CHOICE expression '''
634
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
635
636
637
638


@generate.register(ogAST.Decision)
def _decision(dec):
dbarbera's avatar
dbarbera committed
639
    ''' Generate the code for a decision '''
640
    func = g.builder.basic_block.function
dbarbera's avatar
dbarbera committed
641
642
643
644

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

645
    g.builder.branch(ans_cond_blocks[0])
dbarbera's avatar
dbarbera committed
646
647
648
649
650

    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')
651
        g.builder.position_at_end(ans_cond_block)
dbarbera's avatar
dbarbera committed
652
653
654
655
656
657
658
659
660

        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)

661
662
663
            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
664
665
        elif ans.kind == 'else':
            if ans.transition:
666
                g.builder.branch(ans_tr_block)
dbarbera's avatar
dbarbera committed
667
            else:
668
                g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
669
670
671
672
        else:
            raise NotImplementedError

        if ans.transition:
673
            g.builder.position_at_end(ans_tr_block)
dbarbera's avatar
dbarbera committed
674
            generate(ans.transition)
675
            g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
676

677
    g.builder.position_at_end(end_block)
Maxime Perrotin's avatar
Maxime Perrotin committed
678
679
680
681


@generate.register(ogAST.Label)
def _label(tr):
dbarbera's avatar
dbarbera committed
682
    ''' TGenerate the code for a Label '''
683
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
684
685
686
687


@generate.register(ogAST.Transition)
def _transition(tr):
dbarbera's avatar
dbarbera committed
688
    ''' Generate the code for a transition '''
689
690
691
692
693
694
    for action in tr.actions:
        generate(action)
        if isinstance(action, ogAST.Label):
            return
    if tr.terminator:
        _generate_terminator(tr.terminator)
695
696


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


@generate.register(ogAST.Floating_label)
def _floating_label(label):
dbarbera's avatar
dbarbera committed
723
    ''' Generate the code for a floating label '''
724
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
725
726
727
728
729


@generate.register(ogAST.Procedure)
def _inner_procedure(proc):
    ''' Generate the code for a procedure '''
730
    raise NotImplementedError
dbarbera's avatar
dbarbera committed
731
732
733


def _generate_type(ty):
734
    ''' Generate the equivalent LLVM type of a ASN.1 type '''
dbarbera's avatar
dbarbera committed
735
736
    basic_ty = find_basic_type(ty)
    if basic_ty.kind == 'IntegerType':
737
        return g.i32
dbarbera's avatar
dbarbera committed
738
    elif basic_ty.kind == 'BooleanType':
739
        return g.i1
dbarbera's avatar
dbarbera committed
740
    elif basic_ty.kind == 'RealType':
741
        return g.double
742
    elif basic_ty.kind == 'SequenceOfType':
dbarbera's avatar
dbarbera committed
743
744
        if ty.ReferencedTypeName in g.structs:
            return g.structs[ty.ReferencedTypeName].ty
745
746
747
748
749
750
751
752

        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)
753
        struct = StructType(ty.ReferencedTypeName, [['_', array_ty]])
dbarbera's avatar
dbarbera committed
754
755
756
757
758
        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
759
760
761
762
763
764
765
766

        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
767
768
        g.structs[ty.ReferencedTypeName] = struct
        return struct.ty
dbarbera's avatar
dbarbera committed
769
770
    elif basic_ty.kind == 'EnumeratedType':
        return g.i32
dbarbera's avatar
dbarbera committed
771
772
773
774
    else:
        raise NotImplementedError


775
776
def _get_string_cons(str):
    ''' Returns a reference to a global string constant with the given value '''
777
778
    if str in g.strings:
        return g.strings[str]
779
780

    str_val = core.Constant.stringz(str)
781
    gvar_name = '.str%s' % len(g.strings)
782
    gvar_val = g.module.add_global_variable(str_val.type, gvar_name)
783
    gvar_val.initializer = str_val
784
    g.strings[str] = gvar_val
785
786
787
    return gvar_val


dbarbera's avatar
dbarbera committed
788
789
790
791
792
793
# 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
794
        for typename in g.dataview.viewkeys():
dbarbera's avatar
dbarbera committed
795
            if typename.lower() == basic_type.ReferencedTypeName.lower():
796
                basic_type = g.dataview[typename].type
dbarbera's avatar
dbarbera committed
797
798
                break
    return basic_type