LlvmGenerator.py 24 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
100
    state_cons = g.module.add_global_variable(g.i32, 'state')
    state_cons.initializer = core.Constant.int(g.i32, -1)
101

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

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

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

121
    # Generate process functions
122
123
    g.runtr = _generate_runtr_func(process)
    _generate_startup_func(process)
124

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

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


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

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

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

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

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

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

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

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


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

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

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

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


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

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

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

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

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

        # TODO: Nested states

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

233
        g.builder.ret_void()
234

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

    func.verify()


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

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

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

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


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


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

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


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


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


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


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

dbarbera's avatar
dbarbera committed
329

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

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


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


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


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

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

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
439

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

448
449
450
451
452

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

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
465

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
491

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


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


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


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


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


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


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


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


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


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


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


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


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


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

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

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


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


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

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

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

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

        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)

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

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

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


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


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


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


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


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


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

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


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

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


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