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

"""
    OpenGEODE - A tiny SDL Editor for TASTE

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

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

    Copyright (c) 2012-2013 European Space Agency

    Designed and implemented by Maxime Perrotin

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

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

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

LOG = logging.getLogger(__name__)

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

Maxime Perrotin's avatar
Maxime Perrotin committed
31

32
33
34
35
36
37
# Global state
g = None


class GlobalState():
    def __init__(self, process):
dbarbera's avatar
dbarbera committed
38
39
        self.name = str(process.processName)
        self.module = core.Module.new(self.name)
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
        self.dataview = process.dataview

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

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

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

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

dbarbera's avatar
dbarbera committed
69

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

dbarbera's avatar
dbarbera committed
75

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

82
83
    global g
    g = GlobalState(process)
84

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

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

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

98
    # Generate state var
99
100
    state_cons = g.module.add_global_variable(g.i32, 'state')
    state_cons.initializer = core.Constant.int(g.i32, -1)
101

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

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

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

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

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

dbarbera's avatar
dbarbera committed
129
130
    g.module.verify()

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


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

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

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

    # cond
dbarbera's avatar
dbarbera committed
155
    g.builder.position_at_end(cond_block)
156
    no_tr_cons = core.Constant.int(g.i32, -1)
157
158
    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
159
    g.builder.cbranch(cond_val, body_block, exit_block)
160
161

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

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

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

    func.verify()
179
    g.scope.clear()
180
181
182
    return func


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

    entry_block = func.append_basic_block('entry')
    builder = core.Builder.new(entry_block)
191
    g.builder = builder
192
193

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

197
198
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
199
200


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

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

214
    runtr_func = g.module.get_function_named('run_transition')
215

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

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

        # TODO: Nested states

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

235
        g.builder.ret_void()
236

237
238
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
239
240
241
242

    func.verify()


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

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

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

264
265
        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
266
267
268
269


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


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
296
297

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


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


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


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


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

dbarbera's avatar
dbarbera committed
331

Maxime Perrotin's avatar
Maxime Perrotin committed
332
333
334
335
# ------ expressions --------

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


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


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


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

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

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
441

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

450
451
452
453
454

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

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
467

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
493

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


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


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


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


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


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


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


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


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


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


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


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


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


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

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

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


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


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

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

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

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

        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)

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

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

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


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


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


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


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


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


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

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


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

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


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