LlvmGenerator.py 25.5 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

dbarbera's avatar
dbarbera committed
70
71
72
73
74
75
76
77
78
79
80
class Struct():
    def __init__(self, name, fields):
        self.name = name
        self.fields = fields
        self.field_names = [n for n, _ in self.fields]
        self.ty = core.Type.struct([ty for _, ty in self.fields], self.name)

    def idx(self, name):
        return self.field_names.index(name)


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 '''
536
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
537
538


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


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


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


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


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


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


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


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


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


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


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

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

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
627
628


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


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

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

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

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

        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)

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

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

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


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


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


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


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


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


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

        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)
dbarbera's avatar
dbarbera committed
751
        struct = Struct(ty.ReferencedTypeName, [['_', array_ty]])
dbarbera's avatar
dbarbera committed
752
753
754
755
756
757
758
759
760
761
        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
        # TODO: Fields should be iterated in the same order as defined in the type
        fields = [[n, _generate_type(f.type)] for n, f in basic_ty.Children.viewitems()]
        struct = Struct(ty.ReferencedTypeName, fields)
        g.structs[ty.ReferencedTypeName] = struct
        return struct.ty
dbarbera's avatar
dbarbera committed
762
763
764
765
    else:
        raise NotImplementedError


766
767
def _get_string_cons(str):
    ''' Returns a reference to a global string constant with the given value '''
768
769
    if str in g.strings:
        return g.strings[str]
770
771

    str_val = core.Constant.stringz(str)
772
    gvar_name = '.str%s' % len(g.strings)
773
    gvar_val = g.module.add_global_variable(str_val.type, gvar_name)
774
    gvar_val.initializer = str_val
775
    g.strings[str] = gvar_val
776
777
778
    return gvar_val


dbarbera's avatar
dbarbera committed
779
780
781
782
783
784
# 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
785
        for typename in g.dataview.viewkeys():
dbarbera's avatar
dbarbera committed
786
            if typename.lower() == basic_type.ReferencedTypeName.lower():
787
                basic_type = g.dataview[typename].type
dbarbera's avatar
dbarbera committed
788
789
                break
    return basic_type