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
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
118
119
120
121
122
123
        global_var = g.module.add_global_variable(var_type, str(var_name).lower())

        if var_type.kind == core.TYPE_INTEGER:
            global_var.initializer = core.Constant.int(var_type, 0)
        elif var_type.kind == core.TYPE_DOUBLE:
            global_var.initializer = core.Constant.real(var_type, 0.0)

124
        if def_value:
dbarbera's avatar
dbarbera committed
125
            raise NotImplementedError
126

127
    # Generate process functions
128
129
    g.runtr = _generate_runtr_func(process)
    _generate_startup_func(process)
130

131
132
133
134
    # Generate input signals
    for signal in process.input_signals:
        _generate_input_signal(signal, mapping[signal['name']])

dbarbera's avatar
dbarbera committed
135
136
    g.module.verify()

dbarbera's avatar
dbarbera committed
137
    with open(g.name  + '.ll', 'w') as ll_file:
dbarbera's avatar
dbarbera committed
138
        ll_file.write(str(g.module))
139
140
141


def _generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
142
    ''' Generate code for the run_transition function '''
143
    func_name = 'run_transition'
144
145
    func_type = core.Type.function(g.void, [g.i32])
    func = core.Function.new(g.module, func_type, func_name)
146
147
148
149
150
151

    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
152
    g.builder = core.Builder.new(entry_block)
153
154

    # entry
dbarbera's avatar
dbarbera committed
155
    id_ptr = g.builder.alloca(g.i32, None, 'id')
156
    g.scope['id'] = id_ptr
dbarbera's avatar
dbarbera committed
157
158
    g.builder.store(func.args[0], id_ptr)
    g.builder.branch(cond_block)
159
160

    # cond
dbarbera's avatar
dbarbera committed
161
    g.builder.position_at_end(cond_block)
162
    no_tr_cons = core.Constant.int(g.i32, -1)
163
164
    id_val = g.builder.load(id_ptr)
    cond_val = g.builder.icmp(core.ICMP_NE, id_val, no_tr_cons, 'cond')
dbarbera's avatar
dbarbera committed
165
    g.builder.cbranch(cond_val, body_block, exit_block)
166
167

    # body
dbarbera's avatar
dbarbera committed
168
169
    g.builder.position_at_end(body_block)
    switch = g.builder.switch(func.args[0], exit_block)
170
171
172
173

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
174
        const = core.Constant.int(g.i32, idx)
175
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
176
        g.builder.position_at_end(tr_block)
177
        generate(tr)
dbarbera's avatar
dbarbera committed
178
        g.builder.branch(cond_block)
179
180

    # exit
dbarbera's avatar
dbarbera committed
181
182
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
183
184

    func.verify()
185
    g.scope.clear()
186
187
188
    return func


189
def _generate_startup_func(process):
dbarbera's avatar
dbarbera committed
190
    ''' Generate code for the startup function '''
dbarbera's avatar
dbarbera committed
191
    func_name = g.name + '_startup'
192
193
    func_type = core.Type.function(g.void, [])
    func = core.Function.new(g.module, func_type, func_name)
194
195
196

    entry_block = func.append_basic_block('entry')
    builder = core.Builder.new(entry_block)
197
    g.builder = builder
198
199

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

203
204
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
205
206


207
def _generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
208
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
209
    func_name = g.name + "_" + str(signal['name'])
210
211
212
    param_tys = []
    if 'type' in signal:
        param_tys.append(core.Type.pointer(_generate_type(signal['type'])))
213
214
    func_type = core.Type.function(g.void, param_tys)
    func = core.Function.new(g.module, func_type, func_name)
215
216
217

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

220
    runtr_func = g.module.get_function_named('run_transition')
221

222
223
    g_state_val = g.builder.load(g.module.get_global_variable_named('state'))
    switch = g.builder.switch(g_state_val, exit_block)
224

225
    for state_name, state_id in g.states.iteritems():
226
227
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
228
        g.builder.position_at_end(state_block)
229
230
231

        # TODO: Nested states

232
233
234
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
235
                var_val = g.module.get_global_variable_named(str(var_name).lower())
236
237
                _generate_assign(var_val, func.args[0])
            if input.transition:
238
239
                id_val = core.Constant.int(g.i32, input.transition_id)
                g.builder.call(runtr_func, [id_val])
240

241
        g.builder.ret_void()
242

243
244
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
245
246
247
248

    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
249
250
251
252
@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
253
254
255
256

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

257
        if name == 'write':
dbarbera's avatar
dbarbera committed
258
259
            _generate_write(out['params'])
            continue
260
261
262
        elif name == 'writeln':
            _generate_writeln(out['params'])
            continue
dbarbera's avatar
dbarbera committed
263
264
265
266
267
268
269
        elif name == 'reset_timer':
            _generate_reset_timer(out['params'])
            continue
        elif name == 'set_timer':
            _generate_set_timer(out['params'])
            continue

270
271
        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
272
273
274
275


def _generate_write(params):
    ''' Generate the code for the write operator '''
276
    zero = core.Constant.int(g.i32, 0)
277
278
279
280
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
        if basic_ty.kind == 'IntegerType':
281
            fmt_val = _get_string_cons('% d')
282
283
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
            g.builder.call(g.printf, [fmt_ptr, expr_val])
284
        elif basic_ty.kind == 'RealType':
285
            fmt_val = _get_string_cons('% .14E')
286
287
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
            g.builder.call(g.printf, [fmt_ptr, expr_val])
288
        elif basic_ty.kind == 'BooleanType':
289
            true_str_val = _get_string_cons('TRUE')
290
            true_str_ptr = g.builder.gep(true_str_val, [zero, zero])
291
            false_str_val = _get_string_cons('FALSE')
292
293
294
            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])
295
296
297
298
299
300
301
        else:
            raise NotImplementedError


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
302
303

    zero = core.Constant.int(g.i32, 0)
304
    str_cons = _get_string_cons('\n')
305
306
    str_ptr = g.builder.gep(str_cons, [zero, zero])
    g.builder.call(g.printf, [str_ptr])
dbarbera's avatar
dbarbera committed
307
308
309
310
311
312
313
314
315


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 '''
316
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
317
318
319
320


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
321
    ''' Generate the code of a list of assignments '''
322
323
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
324
325
326
327
328


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
329
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
330
331
332
333


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

dbarbera's avatar
dbarbera committed
337

Maxime Perrotin's avatar
Maxime Perrotin committed
338
339
340
341
# ------ expressions --------

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
342
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
343
344
345
346
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
347
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
348
    ''' Generate the code for a single variable reference '''
349
    return g.module.get_global_variable_named(str(prim.value[0]).lower())
Maxime Perrotin's avatar
Maxime Perrotin committed
350
351


Maxime Perrotin's avatar
Maxime Perrotin committed
352
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
353
def _prim_path(primary_id):
dbarbera's avatar
dbarbera committed
354
    ''' Generate the code for an of an element list (path) '''
355
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
356
357


Maxime Perrotin's avatar
Maxime Perrotin committed
358
359
360
361
362
363
364
365
366
367
368
369
@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
370
371
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
372
373
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
374
375

    # load the value of the expression if it is a pointer
376
    if lefttmp.type.kind == core.TYPE_POINTER:
377
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
378
    if righttmp.type.kind == core.TYPE_POINTER:
379
        righttmp = g.builder.load(righttmp, 'lefttmp')
380
381
382
383
384
385

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
440

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

449
450
451
452
453

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

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
466

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
492

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


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


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


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


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


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


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


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


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


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


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


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


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


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

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

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


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


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

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

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

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

        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)

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

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

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


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


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


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


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


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


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

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


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

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


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