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

"""
    OpenGEODE - A tiny SDL Editor for TASTE

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

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

    Copyright (c) 2012-2013 European Space Agency

    Designed and implemented by Maxime Perrotin

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

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

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

LOG = logging.getLogger(__name__)

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

Maxime Perrotin's avatar
Maxime Perrotin committed
31

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


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

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

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

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

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

dbarbera's avatar
dbarbera committed
68

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

dbarbera's avatar
dbarbera committed
74

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

81
82
    global g
    g = GlobalState(process)
83

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

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

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

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

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

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

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

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

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

127
    print g.module
128
129
130


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

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

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

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


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


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

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


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


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


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


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

dbarbera's avatar
dbarbera committed
327

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

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


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


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


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

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

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
437

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

446
447
448
449
450

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

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
463

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
489

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


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


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


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


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


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


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


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


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


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


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


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


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


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

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

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


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


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

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

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

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

        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)

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

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

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


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


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


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


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


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


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

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


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

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


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