LlvmGenerator.py 26 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
        self.dataview = process.dataview

        self.scope = {}
        self.states = {}
dbarbera's avatar
dbarbera committed
44
        self.structs = {}
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
        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

70
71
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
72
        self.name = name
73
74
75
76
77
        self.field_names = field_names
        self.ty = core.Type.struct(field_types, self.name)

    def idx(self, field_name):
        return self.field_names.index(field_name)
dbarbera's avatar
dbarbera committed
78
79
80



Maxime Perrotin's avatar
Maxime Perrotin committed
81
82
83
84
@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
85

dbarbera's avatar
dbarbera committed
86

Maxime Perrotin's avatar
Maxime Perrotin committed
87
88
89
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
90
    ''' Generate LLVM IR code '''
91
    LOG.info('Generating LLVM IR code for process ' + str(process.processName))
92

93
94
    global g
    g = GlobalState(process)
95

dbarbera's avatar
dbarbera committed
96
97
98
99
100
101
    # 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
102

103
104
105
    # Initialize states enum
    for name in process.mapping.iterkeys():
        if not name.endswith('START'):
106
107
            cons = core.Constant.int(g.i32, len(g.states))
            g.states[name] = cons
108

109
    # Generate state var
110
111
    state_cons = g.module.add_global_variable(g.i32, 'state')
    state_cons.initializer = core.Constant.int(g.i32, -1)
112

dbarbera's avatar
dbarbera committed
113
114
    # Initialize output signals
    for signal in process.output_signals:
115
116
117
118
        if 'type' in signal:
            param_tys = [core.Type.pointer(_generate_type(signal['type']))]
        else:
            param_tys = []
119
120
        func_ty = core.Type.function(g.void, param_tys)
        core.Function.new(g.module, func_ty, str(signal['name']))
dbarbera's avatar
dbarbera committed
121
122
123
124

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

128
    # Generare process-level vars
dbarbera's avatar
dbarbera committed
129
    for var_name, (var_asn1_type, def_value) in process.variables.viewitems():
130
        var_type = _generate_type(var_asn1_type)
131
        global_var = g.module.add_global_variable(var_type, str(var_name).lower())
dbarbera's avatar
dbarbera committed
132
        global_var.initializer = core.Constant.null(var_type)
133

134
        if def_value:
dbarbera's avatar
dbarbera committed
135
            raise NotImplementedError
136

137
    # Generate process functions
138
139
    g.runtr = _generate_runtr_func(process)
    _generate_startup_func(process)
140

141
142
143
144
    # Generate input signals
    for signal in process.input_signals:
        _generate_input_signal(signal, mapping[signal['name']])

dbarbera's avatar
dbarbera committed
145
146
    g.module.verify()

dbarbera's avatar
dbarbera committed
147
    with open(g.name  + '.ll', 'w') as ll_file:
dbarbera's avatar
dbarbera committed
148
        ll_file.write(str(g.module))
149
150
151


def _generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
152
    ''' Generate code for the run_transition function '''
153
    func_name = 'run_transition'
154
155
    func_type = core.Type.function(g.void, [g.i32])
    func = core.Function.new(g.module, func_type, func_name)
156
157
158
159
160
161

    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
162
    g.builder = core.Builder.new(entry_block)
163
164

    # entry
dbarbera's avatar
dbarbera committed
165
    id_ptr = g.builder.alloca(g.i32, None, 'id')
166
    g.scope['id'] = id_ptr
dbarbera's avatar
dbarbera committed
167
168
    g.builder.store(func.args[0], id_ptr)
    g.builder.branch(cond_block)
169
170

    # cond
dbarbera's avatar
dbarbera committed
171
    g.builder.position_at_end(cond_block)
172
    no_tr_cons = core.Constant.int(g.i32, -1)
173
174
    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
175
    g.builder.cbranch(cond_val, body_block, exit_block)
176
177

    # body
dbarbera's avatar
dbarbera committed
178
179
    g.builder.position_at_end(body_block)
    switch = g.builder.switch(func.args[0], exit_block)
180
181
182
183

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
184
        const = core.Constant.int(g.i32, idx)
185
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
186
        g.builder.position_at_end(tr_block)
187
        generate(tr)
dbarbera's avatar
dbarbera committed
188
        g.builder.branch(cond_block)
189
190

    # exit
dbarbera's avatar
dbarbera committed
191
192
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
193
194

    func.verify()
195
    g.scope.clear()
196
197
198
    return func


199
def _generate_startup_func(process):
dbarbera's avatar
dbarbera committed
200
    ''' Generate code for the startup function '''
dbarbera's avatar
dbarbera committed
201
    func_name = g.name + '_startup'
202
203
    func_type = core.Type.function(g.void, [])
    func = core.Function.new(g.module, func_type, func_name)
204
205
206

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

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

213
214
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
215
216


217
def _generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
218
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
219
    func_name = g.name + "_" + str(signal['name'])
220
221
222
    param_tys = []
    if 'type' in signal:
        param_tys.append(core.Type.pointer(_generate_type(signal['type'])))
223
224
    func_type = core.Type.function(g.void, param_tys)
    func = core.Function.new(g.module, func_type, func_name)
225
226
227

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

230
    runtr_func = g.module.get_function_named('run_transition')
231

232
233
    g_state_val = g.builder.load(g.module.get_global_variable_named('state'))
    switch = g.builder.switch(g_state_val, exit_block)
234

235
    for state_name, state_id in g.states.iteritems():
236
237
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
238
        g.builder.position_at_end(state_block)
239
240
241

        # TODO: Nested states

242
243
244
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
245
                var_val = g.module.get_global_variable_named(str(var_name).lower())
246
247
                _generate_assign(var_val, func.args[0])
            if input.transition:
248
249
                id_val = core.Constant.int(g.i32, input.transition_id)
                g.builder.call(runtr_func, [id_val])
250

251
        g.builder.ret_void()
252

253
254
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
255
256
257
258

    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
259
260
261
262
@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
263
264
265
266

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

267
        if name == 'write':
dbarbera's avatar
dbarbera committed
268
269
            _generate_write(out['params'])
            continue
270
271
272
        elif name == 'writeln':
            _generate_writeln(out['params'])
            continue
dbarbera's avatar
dbarbera committed
273
274
275
276
277
278
279
        elif name == 'reset_timer':
            _generate_reset_timer(out['params'])
            continue
        elif name == 'set_timer':
            _generate_set_timer(out['params'])
            continue

280
281
        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
282
283
284
285


def _generate_write(params):
    ''' Generate the code for the write operator '''
286
    zero = core.Constant.int(g.i32, 0)
287
288
289
290
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
        if basic_ty.kind == 'IntegerType':
291
            fmt_val = _get_string_cons('% d')
292
293
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
            g.builder.call(g.printf, [fmt_ptr, expr_val])
294
        elif basic_ty.kind == 'RealType':
295
            fmt_val = _get_string_cons('% .14E')
296
297
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
            g.builder.call(g.printf, [fmt_ptr, expr_val])
298
        elif basic_ty.kind == 'BooleanType':
299
            true_str_val = _get_string_cons('TRUE')
300
            true_str_ptr = g.builder.gep(true_str_val, [zero, zero])
301
            false_str_val = _get_string_cons('FALSE')
302
303
304
            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])
dbarbera's avatar
dbarbera committed
305
306
307
        elif basic_ty.kind == 'StringType':
            expr_ptr = g.builder.gep(expr_val, [zero, zero])
            g.builder.call(g.printf, [expr_ptr])
308
309
310
311
312
313
314
        else:
            raise NotImplementedError


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
315
316

    zero = core.Constant.int(g.i32, 0)
317
    str_cons = _get_string_cons('\n')
318
319
    str_ptr = g.builder.gep(str_cons, [zero, zero])
    g.builder.call(g.printf, [str_ptr])
dbarbera's avatar
dbarbera committed
320
321
322
323
324
325
326
327
328


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 '''
329
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
330
331
332
333


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
334
    ''' Generate the code of a list of assignments '''
335
336
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
337
338
339
340
341


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
342
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
343
344
345
346


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

dbarbera's avatar
dbarbera committed
350

Maxime Perrotin's avatar
Maxime Perrotin committed
351
352
353
354
# ------ expressions --------

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
355
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
356
357
358
359
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
360
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
361
    ''' Generate the code for a single variable reference '''
362
    return g.module.get_global_variable_named(str(prim.value[0]).lower())
Maxime Perrotin's avatar
Maxime Perrotin committed
363
364


Maxime Perrotin's avatar
Maxime Perrotin committed
365
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
366
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
367
    ''' Generate the code for an of an element list (path) '''
dbarbera's avatar
dbarbera committed
368
369
370
371

    var_ptr = g.module.get_global_variable_named(str(prim.value.pop(0)).lower())

    if not prim.value:
dbarbera's avatar
dbarbera committed
372
        return g.builder.load(var_ptr)
dbarbera's avatar
dbarbera committed
373
374
375
376
377
378
379
380
381
382
383
384
385

    zero_cons = core.Constant.int(g.i32, 0)

    for field_name in prim.value:
        var_ty = var_ptr.type
        if var_ty.kind == core.TYPE_POINTER and var_ty.pointee.kind == core.TYPE_STRUCT:
            struct = g.structs[var_ty.pointee.name]
            field_idx_cons = core.Constant.int(g.i32, struct.idx(field_name))
            field_ptr = g.builder.gep(var_ptr, [zero_cons, field_idx_cons])
            var_ptr = field_ptr
        else:
            raise NotImplementedError

dbarbera's avatar
dbarbera committed
386
    return g.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
387
388


Maxime Perrotin's avatar
Maxime Perrotin committed
389
390
391
392
393
394
395
396
397
398
399
400
@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
401
402
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
403
404
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
405
406

    # load the value of the expression if it is a pointer
407
    if lefttmp.type.kind == core.TYPE_POINTER:
408
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
409
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
410
        righttmp = g.builder.load(righttmp, 'righttmp')
411
412
413
414
415
416

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

    if lefttmp.type.kind == core.TYPE_INTEGER:
        if expr.operand == '+':
417
            return g.builder.add(lefttmp, righttmp, 'addtmp')
418
        elif expr.operand == '-':
419
            return g.builder.sub(lefttmp, righttmp, 'subtmp')
420
        elif expr.operand == '*':
421
            return g.builder.mul(lefttmp, righttmp, 'multmp')
422
        elif expr.operand == '/':
423
            return g.builder.sdiv(lefttmp, righttmp, 'divtmp')
424
        elif expr.operand == 'mod':
425
            # l mod r == (((l rem r) + r) rem r)
426
427
428
            remtmp = g.builder.srem(lefttmp, righttmp)
            addtmp = g.builder.add(remtmp, righttmp)
            return g.builder.srem(addtmp, righttmp, 'modtmp')
429
        elif expr.operand == 'rem':
430
            return g.builder.srem(lefttmp, righttmp, 'remtmp')
431
        elif expr.operand == '<':
432
            return g.builder.icmp(core.ICMP_SLT, lefttmp, righttmp, 'lttmp')
433
        elif expr.operand == '<=':
434
            return g.builder.icmp(core.ICMP_SLE, lefttmp, righttmp, 'letmp')
435
        elif expr.operand == '=':
436
            return g.builder.icmp(core.ICMP_EQ, lefttmp, righttmp, 'eqtmp')
437
        elif expr.operand == '/=':
438
            return g.builder.icmp(core.ICMP_NE, lefttmp, righttmp, 'netmp')
439
        elif expr.operand == '>=':
440
            return g.builder.icmp(core.ICMP_SGE, lefttmp, righttmp, 'getmp')
441
        elif expr.operand == '>':
442
            return g.builder.icmp(core.ICMP_SGT, lefttmp, righttmp, 'gttmp')
443
444
445
446
        else:
            raise NotImplementedError
    elif lefttmp.type.kind == core.TYPE_DOUBLE:
        if expr.operand == '+':
447
            return g.builder.fadd(lefttmp, righttmp, 'addtmp')
448
        elif expr.operand == '-':
449
            return g.builder.fsub(lefttmp, righttmp, 'subtmp')
450
        elif expr.operand == '*':
451
            return g.builder.fmul(lefttmp, righttmp, 'multmp')
452
        elif expr.operand == '/':
453
            return g.builder.fdiv(lefttmp, righttmp, 'divtmp')
454
        elif expr.operand == '<':
455
            return g.builder.fcmp(core.FCMP_OLT, lefttmp, righttmp, 'lttmp')
456
        elif expr.operand == '<=':
457
            return g.builder.fcmp(core.FCMP_OLE, lefttmp, righttmp, 'letmp')
458
        elif expr.operand == '=':
459
            return g.builder.fcmp(core.FCMP_OEQ, lefttmp, righttmp, 'eqtmp')
460
        elif expr.operand == '/=':
461
            return g.builder.fcmp(core.FCMP_ONE, lefttmp, righttmp, 'netmp')
462
        elif expr.operand == '>=':
463
            return g.builder.fcmp(core.FCMP_OGE, lefttmp, righttmp, 'getmp')
464
        elif expr.operand == '>':
465
            return g.builder.fcmp(core.FCMP_OGT, lefttmp, righttmp, 'gttmp')
466
467
        else:
            raise NotImplementedError
468
    else:
469
        raise NotImplementedError
470

Maxime Perrotin's avatar
Maxime Perrotin committed
471

472
473
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
474
    ''' Generate the code for an assign expression '''
475
476
    left = expression(expr.left)
    right = expression(expr.right)
477
478
    _generate_assign(left, right)
    return left
479

480
481
482
483
484

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
485
    if left.type.kind == core.TYPE_POINTER and left.type.pointee.kind == core.TYPE_STRUCT:
486
487
        size = core.Constant.sizeof(left.type.pointee)
        align = core.Constant.int(g.i32, 0)
488
        volatile = core.Constant.int(g.i1, 0)
489

490
491
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
492

493
        g.builder.call(g.memcpy, [left_ptr, right_ptr, size, align, volatile])
494
    else:
495
        g.builder.store(right, left)
dbarbera's avatar
dbarbera committed
496

Maxime Perrotin's avatar
Maxime Perrotin committed
497

Maxime Perrotin's avatar
Maxime Perrotin committed
498
499
500
@expression.register(ogAST.ExprOr)
@expression.register(ogAST.ExprAnd)
@expression.register(ogAST.ExprXor)
dbarbera's avatar
dbarbera committed
501
502
def _logical(expr):
    ''' Generate the code for a logical expression '''
dbarbera's avatar
dbarbera committed
503
504
505
506
507
508
509
510
511
    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:
512
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
dbarbera's avatar
dbarbera committed
513
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
514
        righttmp = g.builder.load(righttmp, 'righttmp')
dbarbera's avatar
dbarbera committed
515

dbarbera's avatar
dbarbera committed
516
517
518
    if expr.operand == 'and':
        return g.builder.and_(lefttmp, righttmp, 'andtmp')
    elif expr.operand == 'or':
519
        return g.builder.or_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
520
    else:
521
        return g.builder.xor(lefttmp, righttmp, 'xortmp')
dbarbera's avatar
dbarbera committed
522

Maxime Perrotin's avatar
Maxime Perrotin committed
523

Maxime Perrotin's avatar
Maxime Perrotin committed
524
@expression.register(ogAST.ExprAppend)
Maxime Perrotin's avatar
Maxime Perrotin committed
525
526
def _append(expr):
    ''' Generate code for the APPEND construct: a // b '''
527
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
528
529


Maxime Perrotin's avatar
Maxime Perrotin committed
530
@expression.register(ogAST.ExprIn)
Maxime Perrotin's avatar
Maxime Perrotin committed
531
def _expr_in(expr):
dbarbera's avatar
dbarbera committed
532
    ''' Generate the code for an in expression '''
533
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
534
535


Maxime Perrotin's avatar
Maxime Perrotin committed
536
@expression.register(ogAST.PrimEnumeratedValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
537
538
def _enumerated_value(primary):
    ''' Generate code for an enumerated value '''
dbarbera's avatar
dbarbera committed
539
540
541
    enumerant = primary.value[0].replace('_', '-')
    basic_ty = find_basic_type(primary.exprType)
    return core.Constant.int(g.i32, basic_ty.EnumValues[enumerant].IntValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
542
543


Maxime Perrotin's avatar
Maxime Perrotin committed
544
@expression.register(ogAST.PrimChoiceDeterminant)
Maxime Perrotin's avatar
Maxime Perrotin committed
545
546
def _choice_determinant(primary):
    ''' Generate code for a choice determinant (enumerated) '''
547
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
548
549


Maxime Perrotin's avatar
Maxime Perrotin committed
550
@expression.register(ogAST.PrimInteger)
551
552
def _integer(primary):
    ''' Generate code for a raw integer value  '''
553
    return core.Constant.int(g.i32, primary.value[0])
554
555


Maxime Perrotin's avatar
Maxime Perrotin committed
556
@expression.register(ogAST.PrimReal)
557
558
def _real(primary):
    ''' Generate code for a raw real value  '''
559
    return core.Constant.real(g.double, primary.value[0])
560
561


Maxime Perrotin's avatar
Maxime Perrotin committed
562
@expression.register(ogAST.PrimBoolean)
563
564
def _boolean(primary):
    ''' Generate code for a raw boolean value  '''
dbarbera's avatar
dbarbera committed
565
    if primary.value[0].lower() == 'true':
566
        return core.Constant.int(g.i1, 1)
dbarbera's avatar
dbarbera committed
567
    else:
568
        return core.Constant.int(g.i1, 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
569
570


Maxime Perrotin's avatar
Maxime Perrotin committed
571
@expression.register(ogAST.PrimEmptyString)
Maxime Perrotin's avatar
Maxime Perrotin committed
572
573
def _empty_string(primary):
    ''' Generate code for an empty SEQUENCE OF: {} '''
574
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
575
576


Maxime Perrotin's avatar
Maxime Perrotin committed
577
@expression.register(ogAST.PrimStringLiteral)
Maxime Perrotin's avatar
Maxime Perrotin committed
578
579
def _string_literal(primary):
    ''' Generate code for a string (Octet String) '''
dbarbera's avatar
dbarbera committed
580
    return _get_string_cons(str(primary.value[1:-1]))
Maxime Perrotin's avatar
Maxime Perrotin committed
581
582


Maxime Perrotin's avatar
Maxime Perrotin committed
583
@expression.register(ogAST.PrimConstant)
Maxime Perrotin's avatar
Maxime Perrotin committed
584
585
def _constant(primary):
    ''' Generate code for a reference to an ASN.1 constant '''
586
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
587
588


Maxime Perrotin's avatar
Maxime Perrotin committed
589
@expression.register(ogAST.PrimMantissaBaseExp)
Maxime Perrotin's avatar
Maxime Perrotin committed
590
591
def _mantissa_base_exp(primary):
    ''' Generate code for a Real with Mantissa-base-Exponent representation '''
592
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
593
594


Maxime Perrotin's avatar
Maxime Perrotin committed
595
@expression.register(ogAST.PrimIfThenElse)
dbarbera's avatar
dbarbera committed
596
def _if_then_else(ifthen):
dbarbera's avatar
dbarbera committed
597
    ''' Generate the code for ternary operator '''
598
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
599
600


Maxime Perrotin's avatar
Maxime Perrotin committed
601
@expression.register(ogAST.PrimSequence)
Maxime Perrotin's avatar
Maxime Perrotin committed
602
def _sequence(seq):
dbarbera's avatar
dbarbera committed
603
    ''' Generate the code for an ASN.1 SEQUENCE '''
dbarbera's avatar
dbarbera committed
604
605
606
607
608
609
610
611
612
613
614
    struct = g.structs[seq.exprType.ReferencedTypeName]
    struct_ptr = g.builder.alloca(struct.ty)
    zero_cons = core.Constant.int(g.i32, 0)

    for field_name, field_expr in seq.value.viewitems():
        field_val = expression(field_expr)
        field_idx_cons = core.Constant.int(g.i32, struct.idx(field_name))
        field_ptr = g.builder.gep(struct_ptr, [zero_cons, field_idx_cons])
        g.builder.store(field_val, field_ptr)

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
615
616


Maxime Perrotin's avatar
Maxime Perrotin committed
617
@expression.register(ogAST.PrimSequenceOf)
Maxime Perrotin's avatar
Maxime Perrotin committed
618
def _sequence_of(seqof):
dbarbera's avatar
dbarbera committed
619
    ''' Generate the code for an ASN.1 SEQUENCE OF '''
620
    ty = _generate_type(seqof.exprType)
621
622
623
    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])
624
625

    for idx, expr in enumerate(seqof.value):
626
        idx_cons = core.Constant.int(g.i32, idx)
627
        expr_val = expression(expr)
628
629
        pos_ptr = g.builder.gep(array_ptr, [zero_cons, idx_cons])
        g.builder.store(expr_val, pos_ptr)
630
631

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
632
633


Maxime Perrotin's avatar
Maxime Perrotin committed
634
@expression.register(ogAST.PrimChoiceItem)
Maxime Perrotin's avatar
Maxime Perrotin committed
635
def _choiceitem(choice):
dbarbera's avatar
dbarbera committed
636
    ''' Generate the code for a CHOICE expression '''
637
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
638
639
640
641


@generate.register(ogAST.Decision)
def _decision(dec):
dbarbera's avatar
dbarbera committed
642
    ''' Generate the code for a decision '''
643
    func = g.builder.basic_block.function
dbarbera's avatar
dbarbera committed
644
645
646
647

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

648
    g.builder.branch(ans_cond_blocks[0])
dbarbera's avatar
dbarbera committed
649
650
651
652
653

    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')
654
        g.builder.position_at_end(ans_cond_block)
dbarbera's avatar
dbarbera committed
655
656
657
658
659
660
661
662
663

        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)

664
665
666
            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
667
668
        elif ans.kind == 'else':
            if ans.transition:
669
                g.builder.branch(ans_tr_block)
dbarbera's avatar
dbarbera committed
670
            else:
671
                g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
672
673
674
675
        else:
            raise NotImplementedError

        if ans.transition:
676
            g.builder.position_at_end(ans_tr_block)
dbarbera's avatar
dbarbera committed
677
            generate(ans.transition)
678
            g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
679

680
    g.builder.position_at_end(end_block)
Maxime Perrotin's avatar
Maxime Perrotin committed
681
682
683
684


@generate.register(ogAST.Label)
def _label(tr):
dbarbera's avatar
dbarbera committed
685
    ''' TGenerate the code for a Label '''
686
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
687
688
689
690


@generate.register(ogAST.Transition)
def _transition(tr):
dbarbera's avatar
dbarbera committed
691
    ''' Generate the code for a transition '''
692
693
694
695
696
697
    for action in tr.actions:
        generate(action)
        if isinstance(action, ogAST.Label):
            return
    if tr.terminator:
        _generate_terminator(tr.terminator)
698
699


700
def _generate_terminator(term):
dbarbera's avatar
dbarbera committed
701
    ''' Generate the code for a transition termiantor '''
702
    id_ptr = g.scope['id']
703
    if term.label:
704
        raise NotImplementedError
705
706
707
    if term.kind == 'next_state':
        state = term.inputString.lower()
        if state.strip() != '-':
708
709
            next_id_cons = core.Constant.int(g.i32, term.next_id)
            g.builder.store(next_id_cons, id_ptr)
710
            if term.next_id == -1:
711
712
713
                state_ptr = g.module.get_global_variable_named('state')
                state_id_cons = g.states[state]
                g.builder.store(state_id_cons, state_ptr)
714
        else:
715
            raise NotImplementedError
716
    elif term.kind == 'join':
717
        raise NotImplementedError
718
    elif term.kind == 'stop':
719
        raise NotImplementedError
720
    elif term.kind == 'return':
721
        raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
722
723
724
725


@generate.register(ogAST.Floating_label)
def _floating_label(label):
dbarbera's avatar
dbarbera committed
726
    ''' Generate the code for a floating label '''
727
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
728
729
730
731
732


@generate.register(ogAST.Procedure)
def _inner_procedure(proc):
    ''' Generate the code for a procedure '''
733
    raise NotImplementedError
dbarbera's avatar
dbarbera committed
734
735
736


def _generate_type(ty):
737
    ''' Generate the equivalent LLVM type of a ASN.1 type '''
dbarbera's avatar
dbarbera committed
738
739
    basic_ty = find_basic_type(ty)
    if basic_ty.kind == 'IntegerType':
740
        return g.i32
dbarbera's avatar
dbarbera committed
741
    elif basic_ty.kind == 'BooleanType':
742
        return g.i1
dbarbera's avatar
dbarbera committed
743
    elif basic_ty.kind == 'RealType':
744
        return g.double
745
    elif basic_ty.kind == 'SequenceOfType':
dbarbera's avatar
dbarbera committed
746
747
        if ty.ReferencedTypeName in g.structs:
            return g.structs[ty.ReferencedTypeName].ty
748
749
750
751
752
753
754
755

        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)
756
        struct = StructType(ty.ReferencedTypeName, ['_'], [array_ty])
dbarbera's avatar
dbarbera committed
757
758
759
760
761
        g.structs[ty.ReferencedTypeName] = struct
        return struct.ty
    elif basic_ty.kind == 'SequenceType':
        if ty.ReferencedTypeName in g.structs:
            return g.structs[ty.ReferencedTypeName].ty
762
763
764
765
766
767
768
769

        field_names = []
        field_types = []
        for field_name in Helper.sorted_fields(basic_ty):
            field_names.append(field_name)
            field_types.append(_generate_type(basic_ty.Children[field_name].type))

        struct = StructType(ty.ReferencedTypeName, field_names, field_types)
dbarbera's avatar
dbarbera committed
770
771
        g.structs[ty.ReferencedTypeName] = struct
        return struct.ty
dbarbera's avatar
dbarbera committed
772
773
    elif basic_ty.kind == 'EnumeratedType':
        return g.i32
dbarbera's avatar
dbarbera committed
774
775
776
777
    else:
        raise NotImplementedError


778
779
def _get_string_cons(str):
    ''' Returns a reference to a global string constant with the given value '''
780
781
    if str in g.strings:
        return g.strings[str]
782
783

    str_val = core.Constant.stringz(str)
784
    gvar_name = '.str%s' % len(g.strings)
785
    gvar_val = g.module.add_global_variable(str_val.type, gvar_name)
786
    gvar_val.initializer = str_val
787
    g.strings[str] = gvar_val
788
789
790
    return gvar_val


dbarbera's avatar
dbarbera committed
791
792
793
794
795
796
# 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
797
        for typename in g.dataview.viewkeys():
dbarbera's avatar
dbarbera committed
798
            if typename.lower() == basic_type.ReferencedTypeName.lower():
799
                basic_type = g.dataview[typename].type
dbarbera's avatar
dbarbera committed
800
801
                break
    return basic_type