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
# 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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
        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
68

dbarbera's avatar
dbarbera committed
69

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

dbarbera's avatar
dbarbera committed
75

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

82
83
    global g
    g = GlobalState(process)
84

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

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

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

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

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

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

120
    # Generate process functions
121
    runtr_func = _generate_runtr_func(process)
dbarbera's avatar
dbarbera committed
122
    _generate_startup_func(process, runtr_func)
123

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

dbarbera's avatar
dbarbera committed
128
    with open(g.name  + '.ll', 'w') as ll_file:
dbarbera's avatar
dbarbera committed
129
        ll_file.write(str(g.module))
130
131
132


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

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

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

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

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

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

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

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


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

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

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

194
195
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
196
197


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

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

211
    runtr_func = g.module.get_function_named('run_transition')
212

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

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

        # TODO: Nested states

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

232
        g.builder.ret_void()
233

234
235
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
236
237
238
239

    func.verify()


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

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

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

261
262
        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
263
264
265
266


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


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
293
294

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


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


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


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


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

dbarbera's avatar
dbarbera committed
328

Maxime Perrotin's avatar
Maxime Perrotin committed
329
330
331
332
# ------ expressions --------

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


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


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


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

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

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
438

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

447
448
449
450
451

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

457
458
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
459

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

Maxime Perrotin's avatar
Maxime Perrotin committed
464

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
490

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


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


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


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


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


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


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


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


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


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


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


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


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


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

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

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
587
588


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


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

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

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

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

        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)

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

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

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


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


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


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


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


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


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

        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)
712
        g.types[ty.ReferencedTypeName] = struct_ty
713
        return struct_ty
dbarbera's avatar
dbarbera committed
714
715
716
717
    else:
        raise NotImplementedError


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

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


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