LlvmGenerator.py 23.8 KB
Newer Older
Maxime Perrotin's avatar
Maxime Perrotin committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    OpenGEODE - A tiny SDL Editor for TASTE

    This module generates LLVM IR code from SDL process models, allowing
    generation of a binary application without an intermediate language.
    LLVM also allows for various code verification, analysis, and optimization.

    The design is based on the Ada code generator. Check it for details.

    Copyright (c) 2012-2013 European Space Agency

    Designed and implemented by Maxime Perrotin

    Contact: maxime.perrotin@esa.int
"""

import logging
Maxime Perrotin's avatar
Maxime Perrotin committed
21
from singledispatch import singledispatch
22
from llvm import core
Maxime Perrotin's avatar
Maxime Perrotin committed
23
24

import ogAST
Maxime Perrotin's avatar
Maxime Perrotin committed
25
import Helper
Maxime Perrotin's avatar
Maxime Perrotin committed
26
27
28

LOG = logging.getLogger(__name__)

Maxime Perrotin's avatar
Maxime Perrotin committed
29
30
__all__ = ['generate']

Maxime Perrotin's avatar
Maxime Perrotin committed
31

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# Global state
g = None


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

        self.scope = {}
        self.states = {}
        self.types = {}
        self.strings = {}

        # Initialize built-in types
        self.i1 = core.Type.int(1)
        self.i8 = core.Type.int(8)
        self.i32 = core.Type.int(32)
        self.i64 = core.Type.int(64)
        self.void = core.Type.void()
        self.double = core.Type.double()
        self.i1_ptr = core.Type.pointer(self.i1)
        self.i8_ptr = core.Type.pointer(self.i8)
        self.i32_ptr = core.Type.pointer(self.i32)
        self.i64_ptr = core.Type.pointer(self.i64)
        self.double_ptr = core.Type.pointer(self.double)

        # Intialize built-in functions
        ty = core.Type.function(self.void, [core.Type.pointer(self.i8)], True)
        self.printf = self.module.add_function(ty, 'printf')

        self.memcpy = core.Function.intrinsic(
            self.module, core.INTR_MEMCPY,
            [self.i8_ptr, self.i8_ptr, self.i64]
        )
Maxime Perrotin's avatar
Maxime Perrotin committed
67

dbarbera's avatar
dbarbera committed
68

Maxime Perrotin's avatar
Maxime Perrotin committed
69
70
71
72
@singledispatch
def generate(ast):
    ''' Generate the code for an item of the AST '''
    raise TypeError('[Backend] Unsupported AST construct')
Maxime Perrotin's avatar
Maxime Perrotin committed
73

dbarbera's avatar
dbarbera committed
74

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

81
82
    global g
    g = GlobalState(process)
83

dbarbera's avatar
dbarbera committed
84
85
86
87
88
89
    # In case model has nested states, flatten everything
    Helper.flatten(process)

    # Make an maping {input: {state: transition...}} in order to easily
    # generate the lookup tables for the state machine runtime
    mapping = Helper.map_input_state(process)
Maxime Perrotin's avatar
Maxime Perrotin committed
90

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

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

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

    # Initialize external procedures
    for proc in [proc for proc in process.procedures if proc.external]:
        param_tys = [core.Type.pointer(_generate_type(p['type'])) for p in proc.fpar]
109
110
        func_ty = core.Type.function(g.void, param_tys)
        core.Function.new(g.module, func_ty, str(proc.inputString))
dbarbera's avatar
dbarbera committed
111

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

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

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

127
    print g.module
128
129
130


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

    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')

    builder = core.Builder.new(entry_block)
142
    g.builder = builder
143
144

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

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

    # body
    builder.position_at_end(body_block)
    switch = builder.switch(func.args[0], exit_block)

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

    # exit
    builder.position_at_end(exit_block)
    builder.ret_void()

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


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

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

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

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


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

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

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

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

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

        # TODO: Nested states

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

231
        g.builder.ret_void()
232

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

    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
239
240
241
242
@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
243
244
245
246
247
248
249
250
251
252
253
254
255
256

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

        if name in ('write', 'writeln'):
            _generate_write(out['params'])
            continue
        elif name == 'reset_timer':
            _generate_reset_timer(out['params'])
            continue
        elif name == 'set_timer':
            _generate_set_timer(out['params'])
            continue

257
258
        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
259
260
261
262


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


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
289
290

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


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


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


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


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

dbarbera's avatar
dbarbera committed
324

Maxime Perrotin's avatar
Maxime Perrotin committed
325
326
327
328
# ------ expressions --------

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


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


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


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

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

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
434

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

443
444
445
446
447

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

453
454
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
455

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

Maxime Perrotin's avatar
Maxime Perrotin committed
460

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
486

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


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


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


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


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


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


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


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


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


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


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


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


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


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

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

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
583
584


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


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

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

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

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

        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)

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

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

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


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


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


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


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


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


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

        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)
708
        g.types[ty.ReferencedTypeName] = struct_ty
709
        return struct_ty
dbarbera's avatar
dbarbera committed
710
711
712
713
    else:
        raise NotImplementedError


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

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


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