LlvmGenerator.py 23.9 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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# Global state
g = None


class GlobalState():
    def __init__(self, process):
        self.module = core.Module.new(str(process.processName))
        self.dataview = process.dataview

        self.scope = {}
        self.states = {}
        self.types = {}
        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
67

dbarbera's avatar
dbarbera committed
68

Maxime Perrotin's avatar
Maxime Perrotin committed
69
70
71
72
@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
73

dbarbera's avatar
dbarbera committed
74

Maxime Perrotin's avatar
Maxime Perrotin committed
75
76
77
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
78
    ''' Generate LLVM IR code '''
79
    LOG.info('Generating LLVM IR code for process ' + str(process.processName))
80

81
82
    global g
    g = GlobalState(process)
83

dbarbera's avatar
dbarbera committed
84
85
86
87
88
89
    # 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
90

91
92
93
    # Initialize states enum
    for name in process.mapping.iterkeys():
        if not name.endswith('START'):
94
95
            cons = core.Constant.int(g.i32, len(g.states))
            g.states[name] = cons
96

97
    # Generate state var
98
    g.module.add_global_variable(g.i32, 'state')
99

dbarbera's avatar
dbarbera committed
100
101
102
    # Initialize output signals
    for signal in process.output_signals:
        param_tys = [core.Type.pointer(_generate_type(signal['type']))]
103
104
        func_ty = core.Type.function(g.void, param_tys)
        core.Function.new(g.module, func_ty, str(signal['name']))
dbarbera's avatar
dbarbera committed
105
106
107
108

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

112
    # Generare process-level vars
dbarbera's avatar
dbarbera committed
113
    for var_name, (var_asn1_type, def_value) in process.variables.viewitems():
114
        var_type = _generate_type(var_asn1_type)
115
        g.module.add_global_variable(var_type, str(var_name).lower())
116
        if def_value:
dbarbera's avatar
dbarbera committed
117
            raise NotImplementedError
118

119
    # Generate process functions
120
    runtr_func = _generate_runtr_func(process)
121
    _generate_startup_func(process, str(process.processName), runtr_func)
122

123
124
125
126
    # Generate input signals
    for signal in process.input_signals:
        _generate_input_signal(signal, mapping[signal['name']])

127
    print g.module
128
129
130


def _generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
131
    ''' Generate code for the run_transition function '''
132
    func_name = 'run_transition'
133
134
    func_type = core.Type.function(g.void, [g.i32])
    func = core.Function.new(g.module, func_type, func_name)
135
136
137
138
139
140

    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
141
    g.builder = core.Builder.new(entry_block)
142
143

    # entry
dbarbera's avatar
dbarbera committed
144
    id_ptr = g.builder.alloca(g.i32, None, 'id')
145
    g.scope['id'] = id_ptr
dbarbera's avatar
dbarbera committed
146
147
    g.builder.store(func.args[0], id_ptr)
    g.builder.branch(cond_block)
148
149

    # cond
dbarbera's avatar
dbarbera committed
150
    g.builder.position_at_end(cond_block)
151
    id_ptr = func.args[0]
152
    no_tr_cons = core.Constant.int(g.i32, -1)
dbarbera's avatar
dbarbera committed
153
154
    cond_val = g.builder.icmp(core.ICMP_NE, id_ptr, no_tr_cons, 'cond')
    g.builder.cbranch(cond_val, body_block, exit_block)
155
156

    # body
dbarbera's avatar
dbarbera committed
157
158
    g.builder.position_at_end(body_block)
    switch = g.builder.switch(func.args[0], exit_block)
159
160
161
162

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
163
        const = core.Constant.int(g.i32, idx)
164
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
165
        g.builder.position_at_end(tr_block)
166
        generate(tr)
dbarbera's avatar
dbarbera committed
167
        g.builder.branch(cond_block)
168
169

    # exit
dbarbera's avatar
dbarbera committed
170
171
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
172
173

    func.verify()
174
    g.scope.clear()
175
176
177
178
    return func


def _generate_startup_func(process, process_name, runtr_func):
dbarbera's avatar
dbarbera committed
179
    ''' Generate code for the startup function '''
180
    func_name = process_name + '_startup'
181
182
    func_type = core.Type.function(g.void, [])
    func = core.Function.new(g.module, func_type, func_name)
183
184
185

    entry_block = func.append_basic_block('entry')
    builder = core.Builder.new(entry_block)
186
    g.builder = builder
187
188
189

    # entry
    builder.call(runtr_func, [core.Constant.int(core.Type.int(), 0)])
Maxime Perrotin's avatar
Maxime Perrotin committed
190
191
    builder.ret_void()

192
193
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
194
195


196
def _generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
197
    ''' Generate code for an input signal '''
198
    func_name = str(signal['name'])
199
200
201
    param_tys = []
    if 'type' in signal:
        param_tys.append(core.Type.pointer(_generate_type(signal['type'])))
202
203
    func_type = core.Type.function(g.void, param_tys)
    func = core.Function.new(g.module, func_type, func_name)
204
205
206

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

209
    runtr_func = g.module.get_function_named('run_transition')
210

211
212
    g_state_val = g.builder.load(g.module.get_global_variable_named('state'))
    switch = g.builder.switch(g_state_val, exit_block)
213

214
    for state_name, state_id in g.states.iteritems():
215
216
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
217
        g.builder.position_at_end(state_block)
218
219
220

        # TODO: Nested states

221
222
223
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
224
                var_val = g.module.get_global_variable_named(str(var_name).lower())
225
226
                _generate_assign(var_val, func.args[0])
            if input.transition:
227
228
                id_val = core.Constant.int(g.i32, input.transition_id)
                g.builder.call(runtr_func, [id_val])
229

230
        g.builder.ret_void()
231

232
233
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
234
235
236
237

    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
238
239
240
241
@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
242
243
244
245

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

246
        if name == 'write':
dbarbera's avatar
dbarbera committed
247
248
            _generate_write(out['params'])
            continue
249
250
251
        elif name == 'writeln':
            _generate_writeln(out['params'])
            continue
dbarbera's avatar
dbarbera committed
252
253
254
255
256
257
258
        elif name == 'reset_timer':
            _generate_reset_timer(out['params'])
            continue
        elif name == 'set_timer':
            _generate_set_timer(out['params'])
            continue

259
260
        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
261
262
263
264


def _generate_write(params):
    ''' Generate the code for the write operator '''
265
    zero = core.Constant.int(g.i32, 0)
266
267
268
269
270
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
        if basic_ty.kind == 'IntegerType':
            fmt_val = _get_string_cons('%d')
271
272
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
            g.builder.call(g.printf, [fmt_ptr, expr_val])
273
274
        elif basic_ty.kind == 'RealType':
            fmt_val = _get_string_cons('%.14E')
275
276
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
            g.builder.call(g.printf, [fmt_ptr, expr_val])
277
278
        elif basic_ty.kind == 'BooleanType':
            true_str_val = _get_string_cons('true')
279
            true_str_ptr = g.builder.gep(true_str_val, [zero, zero])
280
            false_str_val = _get_string_cons('false')
281
282
283
            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])
284
285
286
287
288
289
290
        else:
            raise NotImplementedError


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
291
292

    zero = core.Constant.int(g.i32, 0)
293
    str_cons = _get_string_cons('\n')
294
295
    str_ptr = g.builder.gep(str_cons, [zero, zero])
    g.builder.call(g.printf, [str_ptr])
dbarbera's avatar
dbarbera committed
296
297
298
299
300
301
302
303
304


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 '''
305
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
306
307
308
309


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
310
    ''' Generate the code of a list of assignments '''
311
312
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
313
314
315
316
317


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
318
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
319
320
321
322


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

dbarbera's avatar
dbarbera committed
326

Maxime Perrotin's avatar
Maxime Perrotin committed
327
328
329
330
# ------ expressions --------

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
331
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
332
333
334
335
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
336
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
337
    ''' Generate the code for a single variable reference '''
338
    return g.module.get_global_variable_named(str(prim.value[0]).lower())
Maxime Perrotin's avatar
Maxime Perrotin committed
339
340


Maxime Perrotin's avatar
Maxime Perrotin committed
341
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
342
def _prim_path(primary_id):
dbarbera's avatar
dbarbera committed
343
    ''' Generate the code for an of an element list (path) '''
344
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
345
346


Maxime Perrotin's avatar
Maxime Perrotin committed
347
348
349
350
351
352
353
354
355
356
357
358
@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
359
360
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
361
362
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
363
364

    # load the value of the expression if it is a pointer
365
    if lefttmp.type.kind == core.TYPE_POINTER:
366
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
367
    if righttmp.type.kind == core.TYPE_POINTER:
368
        righttmp = g.builder.load(righttmp, 'lefttmp')
369
370
371
372
373
374

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

    if lefttmp.type.kind == core.TYPE_INTEGER:
        if expr.operand == '+':
375
            return g.builder.add(lefttmp, righttmp, 'addtmp')
376
        elif expr.operand == '-':
377
            return g.builder.sub(lefttmp, righttmp, 'subtmp')
378
        elif expr.operand == '*':
379
            return g.builder.mul(lefttmp, righttmp, 'multmp')
380
        elif expr.operand == '/':
381
            return g.builder.sdiv(lefttmp, righttmp, 'divtmp')
382
        elif expr.operand == 'mod':
383
            # l mod r == (((l rem r) + r) rem r)
384
385
386
            remtmp = g.builder.srem(lefttmp, righttmp)
            addtmp = g.builder.add(remtmp, righttmp)
            return g.builder.srem(addtmp, righttmp, 'modtmp')
387
        elif expr.operand == 'rem':
388
            return g.builder.srem(lefttmp, righttmp, 'remtmp')
389
        elif expr.operand == '<':
390
            return g.builder.icmp(core.ICMP_SLT, lefttmp, righttmp, 'lttmp')
391
        elif expr.operand == '<=':
392
            return g.builder.icmp(core.ICMP_SLE, lefttmp, righttmp, 'letmp')
393
        elif expr.operand == '=':
394
            return g.builder.icmp(core.ICMP_EQ, lefttmp, righttmp, 'eqtmp')
395
        elif expr.operand == '/=':
396
            return g.builder.icmp(core.ICMP_NE, lefttmp, righttmp, 'netmp')
397
        elif expr.operand == '>=':
398
            return g.builder.icmp(core.ICMP_SGE, lefttmp, righttmp, 'getmp')
399
        elif expr.operand == '>':
400
            return g.builder.icmp(core.ICMP_SGT, lefttmp, righttmp, 'gttmp')
401
402
403
404
        else:
            raise NotImplementedError
    elif lefttmp.type.kind == core.TYPE_DOUBLE:
        if expr.operand == '+':
405
            return g.builder.fadd(lefttmp, righttmp, 'addtmp')
406
        elif expr.operand == '-':
407
            return g.builder.fsub(lefttmp, righttmp, 'subtmp')
408
        elif expr.operand == '*':
409
            return g.builder.fmul(lefttmp, righttmp, 'multmp')
410
        elif expr.operand == '/':
411
            return g.builder.fdiv(lefttmp, righttmp, 'divtmp')
412
        elif expr.operand == 'mod':
413
            # l mod r == (((l rem r) + r) rem r)
414
415
416
            remtmp = g.builder.frem(lefttmp, righttmp)
            addtmp = g.builder.fadd(remtmp, righttmp)
            return g.builder.frem(addtmp, righttmp, 'modtmp')
417
        elif expr.operand == 'rem':
418
            return g.builder.frem(lefttmp, righttmp, 'remtmp')
419
        elif expr.operand == '<':
420
            return g.builder.icmp(core.FCMP_OLT, lefttmp, righttmp, 'lttmp')
421
        elif expr.operand == '<=':
422
            return g.builder.icmp(core.FCMP_OLE, lefttmp, righttmp, 'letmp')
423
        elif expr.operand == '=':
424
            return g.builder.icmp(core.FCMP_OEQ, lefttmp, righttmp, 'eqtmp')
425
        elif expr.operand == '/=':
426
            return g.builder.icmp(core.FCMP_ONE, lefttmp, righttmp, 'netmp')
427
        elif expr.operand == '>=':
428
            return g.builder.icmp(core.FCMP_OGE, lefttmp, righttmp, 'getmp')
429
        elif expr.operand == '>':
430
            return g.builder.icmp(core.FCMP_OGT, lefttmp, righttmp, 'gttmp')
431
432
        else:
            raise NotImplementedError
433
    else:
434
        raise NotImplementedError
435

Maxime Perrotin's avatar
Maxime Perrotin committed
436

437
438
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
439
    ''' Generate the code for an assign expression '''
440
441
    left = expression(expr.left)
    right = expression(expr.right)
442
443
    _generate_assign(left, right)
    return left
444

445
446
447
448
449

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
450
    if left.type.kind == core.TYPE_POINTER and left.type.pointee.kind == core.TYPE_STRUCT:
451
452
453
        size = core.Constant.int(g.i64, 2)
        align = core.Constant.int(g.i32, 1)
        volatile = core.Constant.int(g.i1, 0)
454

455
456
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
457

458
        g.builder.call(g.memcpy, [left_ptr, right_ptr, size, align, volatile])
459
    else:
460
        g.builder.store(right, left)
dbarbera's avatar
dbarbera committed
461

Maxime Perrotin's avatar
Maxime Perrotin committed
462

Maxime Perrotin's avatar
Maxime Perrotin committed
463
464
465
@expression.register(ogAST.ExprOr)
@expression.register(ogAST.ExprAnd)
@expression.register(ogAST.ExprXor)
dbarbera's avatar
dbarbera committed
466
467
def _logical(expr):
    ''' Generate the code for a logical expression '''
dbarbera's avatar
dbarbera committed
468
469
470
471
472
473
474
475
476
    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:
477
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
dbarbera's avatar
dbarbera committed
478
    if righttmp.type.kind == core.TYPE_POINTER:
479
        righttmp = g.builder.load(righttmp, 'lefttmp')
dbarbera's avatar
dbarbera committed
480
481

    if expr.operand == '&&':
482
        return g.builder.and_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
483
    elif expr.operand == '||':
484
        return g.builder.or_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
485
    else:
486
        return g.builder.xor(lefttmp, righttmp, 'xortmp')
dbarbera's avatar
dbarbera committed
487

Maxime Perrotin's avatar
Maxime Perrotin committed
488

Maxime Perrotin's avatar
Maxime Perrotin committed
489
@expression.register(ogAST.ExprAppend)
Maxime Perrotin's avatar
Maxime Perrotin committed
490
491
def _append(expr):
    ''' Generate code for the APPEND construct: a // b '''
492
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
493
494


Maxime Perrotin's avatar
Maxime Perrotin committed
495
@expression.register(ogAST.ExprIn)
Maxime Perrotin's avatar
Maxime Perrotin committed
496
def _expr_in(expr):
dbarbera's avatar
dbarbera committed
497
    ''' Generate the code for an in expression '''
498
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
499
500


Maxime Perrotin's avatar
Maxime Perrotin committed
501
@expression.register(ogAST.PrimEnumeratedValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
502
503
def _enumerated_value(primary):
    ''' Generate code for an enumerated value '''
504
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
505
506


Maxime Perrotin's avatar
Maxime Perrotin committed
507
@expression.register(ogAST.PrimChoiceDeterminant)
Maxime Perrotin's avatar
Maxime Perrotin committed
508
509
def _choice_determinant(primary):
    ''' Generate code for a choice determinant (enumerated) '''
510
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
511
512


Maxime Perrotin's avatar
Maxime Perrotin committed
513
@expression.register(ogAST.PrimInteger)
514
515
def _integer(primary):
    ''' Generate code for a raw integer value  '''
516
    return core.Constant.int(g.i32, primary.value[0])
517
518


Maxime Perrotin's avatar
Maxime Perrotin committed
519
@expression.register(ogAST.PrimReal)
520
521
def _real(primary):
    ''' Generate code for a raw real value  '''
522
    return core.Constant.real(g.double, primary.value[0])
523
524


Maxime Perrotin's avatar
Maxime Perrotin committed
525
@expression.register(ogAST.PrimBoolean)
526
527
def _boolean(primary):
    ''' Generate code for a raw boolean value  '''
dbarbera's avatar
dbarbera committed
528
    if primary.value[0].lower() == 'true':
529
        return core.Constant.int(g.i1, 1)
dbarbera's avatar
dbarbera committed
530
    else:
531
        return core.Constant.int(g.i1, 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
532
533


Maxime Perrotin's avatar
Maxime Perrotin committed
534
@expression.register(ogAST.PrimEmptyString)
Maxime Perrotin's avatar
Maxime Perrotin committed
535
536
def _empty_string(primary):
    ''' Generate code for an empty SEQUENCE OF: {} '''
537
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
538
539


Maxime Perrotin's avatar
Maxime Perrotin committed
540
@expression.register(ogAST.PrimStringLiteral)
Maxime Perrotin's avatar
Maxime Perrotin committed
541
542
def _string_literal(primary):
    ''' Generate code for a string (Octet String) '''
543
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
544
545


Maxime Perrotin's avatar
Maxime Perrotin committed
546
@expression.register(ogAST.PrimConstant)
Maxime Perrotin's avatar
Maxime Perrotin committed
547
548
def _constant(primary):
    ''' Generate code for a reference to an ASN.1 constant '''
549
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
550
551


Maxime Perrotin's avatar
Maxime Perrotin committed
552
@expression.register(ogAST.PrimMantissaBaseExp)
Maxime Perrotin's avatar
Maxime Perrotin committed
553
554
def _mantissa_base_exp(primary):
    ''' Generate code for a Real with Mantissa-base-Exponent representation '''
555
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
556
557


Maxime Perrotin's avatar
Maxime Perrotin committed
558
@expression.register(ogAST.PrimIfThenElse)
dbarbera's avatar
dbarbera committed
559
def _if_then_else(ifthen):
dbarbera's avatar
dbarbera committed
560
    ''' Generate the code for ternary operator '''
561
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
562
563


Maxime Perrotin's avatar
Maxime Perrotin committed
564
@expression.register(ogAST.PrimSequence)
Maxime Perrotin's avatar
Maxime Perrotin committed
565
def _sequence(seq):
dbarbera's avatar
dbarbera committed
566
    ''' Generate the code for an ASN.1 SEQUENCE '''
567
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
568
569


Maxime Perrotin's avatar
Maxime Perrotin committed
570
@expression.register(ogAST.PrimSequenceOf)
Maxime Perrotin's avatar
Maxime Perrotin committed
571
def _sequence_of(seqof):
dbarbera's avatar
dbarbera committed
572
    ''' Generate the code for an ASN.1 SEQUENCE OF '''
573
    ty = _generate_type(seqof.exprType)
574
575
576
    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])
577
578

    for idx, expr in enumerate(seqof.value):
579
        idx_cons = core.Constant.int(g.i32, idx)
580
        expr_val = expression(expr)
581
582
        pos_ptr = g.builder.gep(array_ptr, [zero_cons, idx_cons])
        g.builder.store(expr_val, pos_ptr)
583
584

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
585
586


Maxime Perrotin's avatar
Maxime Perrotin committed
587
@expression.register(ogAST.PrimChoiceItem)
Maxime Perrotin's avatar
Maxime Perrotin committed
588
def _choiceitem(choice):
dbarbera's avatar
dbarbera committed
589
    ''' Generate the code for a CHOICE expression '''
590
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
591
592
593
594


@generate.register(ogAST.Decision)
def _decision(dec):
dbarbera's avatar
dbarbera committed
595
    ''' Generate the code for a decision '''
596
    func = g.builder.basic_block.function
dbarbera's avatar
dbarbera committed
597
598
599
600

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

601
    g.builder.branch(ans_cond_blocks[0])
dbarbera's avatar
dbarbera committed
602
603
604
605
606

    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')
607
        g.builder.position_at_end(ans_cond_block)
dbarbera's avatar
dbarbera committed
608
609
610
611
612
613
614
615
616

        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)

617
618
619
            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
620
621
        elif ans.kind == 'else':
            if ans.transition:
622
                g.builder.branch(ans_tr_block)
dbarbera's avatar
dbarbera committed
623
            else:
624
                g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
625
626
627
628
        else:
            raise NotImplementedError

        if ans.transition:
629
            g.builder.position_at_end(ans_tr_block)
dbarbera's avatar
dbarbera committed
630
            generate(ans.transition)
631
            g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
632

633
    g.builder.position_at_end(end_block)
Maxime Perrotin's avatar
Maxime Perrotin committed
634
635
636
637


@generate.register(ogAST.Label)
def _label(tr):
dbarbera's avatar
dbarbera committed
638
    ''' TGenerate the code for a Label '''
639
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
640
641
642
643


@generate.register(ogAST.Transition)
def _transition(tr):
dbarbera's avatar
dbarbera committed
644
    ''' Generate the code for a transition '''
645
646
647
648
649
650
    for action in tr.actions:
        generate(action)
        if isinstance(action, ogAST.Label):
            return
    if tr.terminator:
        _generate_terminator(tr.terminator)
651
652


653
def _generate_terminator(term):
dbarbera's avatar
dbarbera committed
654
    ''' Generate the code for a transition termiantor '''
655
    id_ptr = g.scope['id']
656
    if term.label:
657
        raise NotImplementedError
658
659
660
    if term.kind == 'next_state':
        state = term.inputString.lower()
        if state.strip() != '-':
661
662
            next_id_cons = core.Constant.int(g.i32, term.next_id)
            g.builder.store(next_id_cons, id_ptr)
663
            if term.next_id == -1:
664
665
666
                state_ptr = g.module.get_global_variable_named('state')
                state_id_cons = g.states[state]
                g.builder.store(state_id_cons, state_ptr)
667
        else:
668
            raise NotImplementedError
669
    elif term.kind == 'join':
670
        raise NotImplementedError
671
    elif term.kind == 'stop':
672
        raise NotImplementedError
673
    elif term.kind == 'return':
674
        raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
675
676
677
678


@generate.register(ogAST.Floating_label)
def _floating_label(label):
dbarbera's avatar
dbarbera committed
679
    ''' Generate the code for a floating label '''
680
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
681
682
683
684
685


@generate.register(ogAST.Procedure)
def _inner_procedure(proc):
    ''' Generate the code for a procedure '''
686
    raise NotImplementedError
dbarbera's avatar
dbarbera committed
687
688
689


def _generate_type(ty):
690
    ''' Generate the equivalent LLVM type of a ASN.1 type '''
dbarbera's avatar
dbarbera committed
691
692
    basic_ty = find_basic_type(ty)
    if basic_ty.kind == 'IntegerType':
693
        return g.i32
dbarbera's avatar
dbarbera committed
694
    elif basic_ty.kind == 'BooleanType':
695
        return g.i1
dbarbera's avatar
dbarbera committed
696
    elif basic_ty.kind == 'RealType':
697
        return g.double
698
    elif basic_ty.kind == 'SequenceOfType':
699
700
        if ty.ReferencedTypeName in g.types:
            return g.types[ty.ReferencedTypeName]
701
702
703
704
705
706
707
708
709

        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)
        struct_ty = core.Type.struct([array_ty], ty.ReferencedTypeName)
710
        g.types[ty.ReferencedTypeName] = struct_ty
711
        return struct_ty
dbarbera's avatar
dbarbera committed
712
713
714
715
    else:
        raise NotImplementedError


716
717
def _get_string_cons(str):
    ''' Returns a reference to a global string constant with the given value '''
718
719
    if str in g.strings:
        return g.strings[str]
720
721
722

    str_val = core.Constant.stringz(str)
    # TODO: This names can cause conflicts with user defined variables
723
724
    gvar_name = 'str_%s' % len(g.strings)
    gvar_val = g.module.add_global_variable(str_val.type, gvar_name)
725
    gvar_val.initializer = str_val
726
    g.strings[str] = gvar_val
727
728
729
    return gvar_val


dbarbera's avatar
dbarbera committed
730
731
732
733
734
735
# 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
736
        for typename in g.dataview.viewkeys():
dbarbera's avatar
dbarbera committed
737
            if typename.lower() == basic_type.ReferencedTypeName.lower():
738
                basic_type = g.dataview[typename].type
dbarbera's avatar
dbarbera committed
739
740
                break
    return basic_type