LlvmGenerator.py 25.5 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

dbarbera's avatar
dbarbera committed
70
71
72
73
74
75
76
77
78
79
80
class Struct():
    def __init__(self, name, fields):
        self.name = name
        self.fields = fields
        self.field_names = [n for n, _ in self.fields]
        self.ty = core.Type.struct([ty for _, ty in self.fields], self.name)

    def idx(self, name):
        return self.field_names.index(name)


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
115
    # Initialize output signals
    for signal in process.output_signals:
        param_tys = [core.Type.pointer(_generate_type(signal['type']))]
116
117
        func_ty = core.Type.function(g.void, param_tys)
        core.Function.new(g.module, func_ty, str(signal['name']))
dbarbera's avatar
dbarbera committed
118
119
120
121

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

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

131
        if def_value:
dbarbera's avatar
dbarbera committed
132
            raise NotImplementedError
133

134
    # Generate process functions
135
136
    g.runtr = _generate_runtr_func(process)
    _generate_startup_func(process)
137

138
139
140
141
    # Generate input signals
    for signal in process.input_signals:
        _generate_input_signal(signal, mapping[signal['name']])

dbarbera's avatar
dbarbera committed
142
143
    g.module.verify()

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


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

    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
159
    g.builder = core.Builder.new(entry_block)
160
161

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

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

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

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

    # exit
dbarbera's avatar
dbarbera committed
188
189
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
190
191

    func.verify()
192
    g.scope.clear()
193
194
195
    return func


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

    entry_block = func.append_basic_block('entry')
    builder = core.Builder.new(entry_block)
204
    g.builder = builder
205
206

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

210
211
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
212
213


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

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

227
    runtr_func = g.module.get_function_named('run_transition')
228

229
230
    g_state_val = g.builder.load(g.module.get_global_variable_named('state'))
    switch = g.builder.switch(g_state_val, exit_block)
231

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

        # TODO: Nested states

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

248
        g.builder.ret_void()
249

250
251
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
252
253
254
255

    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
256
257
258
259
@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
260
261
262
263

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

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

277
278
        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
279
280
281
282


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


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
309
310

    zero = core.Constant.int(g.i32, 0)
311
    str_cons = _get_string_cons('\n')
312
313
    str_ptr = g.builder.gep(str_cons, [zero, zero])
    g.builder.call(g.printf, [str_ptr])
dbarbera's avatar
dbarbera committed
314
315
316
317
318
319
320
321
322


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 '''
323
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
324
325
326
327


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
328
    ''' Generate the code of a list of assignments '''
329
330
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
331
332
333
334
335


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
336
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
337
338
339
340


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

dbarbera's avatar
dbarbera committed
344

Maxime Perrotin's avatar
Maxime Perrotin committed
345
346
347
348
# ------ expressions --------

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
349
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
350
351
352
353
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
354
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
355
    ''' Generate the code for a single variable reference '''
356
    return g.module.get_global_variable_named(str(prim.value[0]).lower())
Maxime Perrotin's avatar
Maxime Perrotin committed
357
358


Maxime Perrotin's avatar
Maxime Perrotin committed
359
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
360
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
361
    ''' Generate the code for an of an element list (path) '''
dbarbera's avatar
dbarbera committed
362
363
364
365

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

    if not prim.value:
dbarbera's avatar
dbarbera committed
366
        return g.builder.load(var_ptr)
dbarbera's avatar
dbarbera committed
367
368
369
370
371
372
373
374
375
376
377
378
379

    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
380
    return g.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
381
382


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

    # load the value of the expression if it is a pointer
401
    if lefttmp.type.kind == core.TYPE_POINTER:
402
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
403
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
404
        righttmp = g.builder.load(righttmp, 'righttmp')
405
406
407
408
409
410

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
465

466
467
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
468
    ''' Generate the code for an assign expression '''
469
470
    left = expression(expr.left)
    right = expression(expr.right)
471
472
    _generate_assign(left, right)
    return left
473

474
475
476
477
478

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
479
    if left.type.kind == core.TYPE_POINTER and left.type.pointee.kind == core.TYPE_STRUCT:
480
481
        size = core.Constant.sizeof(left.type.pointee)
        align = core.Constant.int(g.i32, 0)
482
        volatile = core.Constant.int(g.i1, 0)
483

484
485
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
486

487
        g.builder.call(g.memcpy, [left_ptr, right_ptr, size, align, volatile])
488
    else:
489
        g.builder.store(right, left)
dbarbera's avatar
dbarbera committed
490

Maxime Perrotin's avatar
Maxime Perrotin committed
491

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

dbarbera's avatar
dbarbera committed
510
511
512
    if expr.operand == 'and':
        return g.builder.and_(lefttmp, righttmp, 'andtmp')
    elif expr.operand == 'or':
513
        return g.builder.or_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
514
    else:
515
        return g.builder.xor(lefttmp, righttmp, 'xortmp')
dbarbera's avatar
dbarbera committed
516

Maxime Perrotin's avatar
Maxime Perrotin committed
517

Maxime Perrotin's avatar
Maxime Perrotin committed
518
@expression.register(ogAST.ExprAppend)
Maxime Perrotin's avatar
Maxime Perrotin committed
519
520
def _append(expr):
    ''' Generate code for the APPEND construct: a // b '''
521
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
522
523


Maxime Perrotin's avatar
Maxime Perrotin committed
524
@expression.register(ogAST.ExprIn)
Maxime Perrotin's avatar
Maxime Perrotin committed
525
def _expr_in(expr):
dbarbera's avatar
dbarbera committed
526
    ''' Generate the code for an in expression '''
527
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
528
529


Maxime Perrotin's avatar
Maxime Perrotin committed
530
@expression.register(ogAST.PrimEnumeratedValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
531
532
def _enumerated_value(primary):
    ''' Generate code for an enumerated value '''
533
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
534
535


Maxime Perrotin's avatar
Maxime Perrotin committed
536
@expression.register(ogAST.PrimChoiceDeterminant)
Maxime Perrotin's avatar
Maxime Perrotin committed
537
538
def _choice_determinant(primary):
    ''' Generate code for a choice determinant (enumerated) '''
539
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
540
541


Maxime Perrotin's avatar
Maxime Perrotin committed
542
@expression.register(ogAST.PrimInteger)
543
544
def _integer(primary):
    ''' Generate code for a raw integer value  '''
545
    return core.Constant.int(g.i32, primary.value[0])
546
547


Maxime Perrotin's avatar
Maxime Perrotin committed
548
@expression.register(ogAST.PrimReal)
549
550
def _real(primary):
    ''' Generate code for a raw real value  '''
551
    return core.Constant.real(g.double, primary.value[0])
552
553


Maxime Perrotin's avatar
Maxime Perrotin committed
554
@expression.register(ogAST.PrimBoolean)
555
556
def _boolean(primary):
    ''' Generate code for a raw boolean value  '''
dbarbera's avatar
dbarbera committed
557
    if primary.value[0].lower() == 'true':
558
        return core.Constant.int(g.i1, 1)
dbarbera's avatar
dbarbera committed
559
    else:
560
        return core.Constant.int(g.i1, 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
561
562


Maxime Perrotin's avatar
Maxime Perrotin committed
563
@expression.register(ogAST.PrimEmptyString)
Maxime Perrotin's avatar
Maxime Perrotin committed
564
565
def _empty_string(primary):
    ''' Generate code for an empty SEQUENCE OF: {} '''
566
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
567
568


Maxime Perrotin's avatar
Maxime Perrotin committed
569
@expression.register(ogAST.PrimStringLiteral)
Maxime Perrotin's avatar
Maxime Perrotin committed
570
571
def _string_literal(primary):
    ''' Generate code for a string (Octet String) '''
572
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
573
574


Maxime Perrotin's avatar
Maxime Perrotin committed
575
@expression.register(ogAST.PrimConstant)
Maxime Perrotin's avatar
Maxime Perrotin committed
576
577
def _constant(primary):
    ''' Generate code for a reference to an ASN.1 constant '''
578
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
579
580


Maxime Perrotin's avatar
Maxime Perrotin committed
581
@expression.register(ogAST.PrimMantissaBaseExp)
Maxime Perrotin's avatar
Maxime Perrotin committed
582
583
def _mantissa_base_exp(primary):
    ''' Generate code for a Real with Mantissa-base-Exponent representation '''
584
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
585
586


Maxime Perrotin's avatar
Maxime Perrotin committed
587
@expression.register(ogAST.PrimIfThenElse)
dbarbera's avatar
dbarbera committed
588
def _if_then_else(ifthen):
dbarbera's avatar
dbarbera committed
589
    ''' Generate the code for ternary operator '''
590
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
591
592


Maxime Perrotin's avatar
Maxime Perrotin committed
593
@expression.register(ogAST.PrimSequence)
Maxime Perrotin's avatar
Maxime Perrotin committed
594
def _sequence(seq):
dbarbera's avatar
dbarbera committed
595
    ''' Generate the code for an ASN.1 SEQUENCE '''
dbarbera's avatar
dbarbera committed
596
597
598
599
600
601
602
603
604
605
606
    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
607
608


Maxime Perrotin's avatar
Maxime Perrotin committed
609
@expression.register(ogAST.PrimSequenceOf)
Maxime Perrotin's avatar
Maxime Perrotin committed
610
def _sequence_of(seqof):
dbarbera's avatar
dbarbera committed
611
    ''' Generate the code for an ASN.1 SEQUENCE OF '''
612
    ty = _generate_type(seqof.exprType)
613
614
615
    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])
616
617

    for idx, expr in enumerate(seqof.value):
618
        idx_cons = core.Constant.int(g.i32, idx)
619
        expr_val = expression(expr)
620
621
        pos_ptr = g.builder.gep(array_ptr, [zero_cons, idx_cons])
        g.builder.store(expr_val, pos_ptr)
622
623

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
624
625


Maxime Perrotin's avatar
Maxime Perrotin committed
626
@expression.register(ogAST.PrimChoiceItem)
Maxime Perrotin's avatar
Maxime Perrotin committed
627
def _choiceitem(choice):
dbarbera's avatar
dbarbera committed
628
    ''' Generate the code for a CHOICE expression '''
629
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
630
631
632
633


@generate.register(ogAST.Decision)
def _decision(dec):
dbarbera's avatar
dbarbera committed
634
    ''' Generate the code for a decision '''
635
    func = g.builder.basic_block.function
dbarbera's avatar
dbarbera committed
636
637
638
639

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

640
    g.builder.branch(ans_cond_blocks[0])
dbarbera's avatar
dbarbera committed
641
642
643
644
645

    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')
646
        g.builder.position_at_end(ans_cond_block)
dbarbera's avatar
dbarbera committed
647
648
649
650
651
652
653
654
655

        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)

656
657
658
            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
659
660
        elif ans.kind == 'else':
            if ans.transition:
661
                g.builder.branch(ans_tr_block)
dbarbera's avatar
dbarbera committed
662
            else:
663
                g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
664
665
666
667
        else:
            raise NotImplementedError

        if ans.transition:
668
            g.builder.position_at_end(ans_tr_block)
dbarbera's avatar
dbarbera committed
669
            generate(ans.transition)
670
            g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
671

672
    g.builder.position_at_end(end_block)
Maxime Perrotin's avatar
Maxime Perrotin committed
673
674
675
676


@generate.register(ogAST.Label)
def _label(tr):
dbarbera's avatar
dbarbera committed
677
    ''' TGenerate the code for a Label '''
678
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
679
680
681
682


@generate.register(ogAST.Transition)
def _transition(tr):
dbarbera's avatar
dbarbera committed
683
    ''' Generate the code for a transition '''
684
685
686
687
688
689
    for action in tr.actions:
        generate(action)
        if isinstance(action, ogAST.Label):
            return
    if tr.terminator:
        _generate_terminator(tr.terminator)
690
691


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


@generate.register(ogAST.Floating_label)
def _floating_label(label):
dbarbera's avatar
dbarbera committed
718
    ''' Generate the code for a floating label '''
719
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
720
721
722
723
724


@generate.register(ogAST.Procedure)
def _inner_procedure(proc):
    ''' Generate the code for a procedure '''
725
    raise NotImplementedError
dbarbera's avatar
dbarbera committed
726
727
728


def _generate_type(ty):
729
    ''' Generate the equivalent LLVM type of a ASN.1 type '''
dbarbera's avatar
dbarbera committed
730
731
    basic_ty = find_basic_type(ty)
    if basic_ty.kind == 'IntegerType':
732
        return g.i32
dbarbera's avatar
dbarbera committed
733
    elif basic_ty.kind == 'BooleanType':
734
        return g.i1
dbarbera's avatar
dbarbera committed
735
    elif basic_ty.kind == 'RealType':
736
        return g.double
737
    elif basic_ty.kind == 'SequenceOfType':
dbarbera's avatar
dbarbera committed
738
739
        if ty.ReferencedTypeName in g.structs:
            return g.structs[ty.ReferencedTypeName].ty
740
741
742
743
744
745
746
747

        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)
dbarbera's avatar
dbarbera committed
748
749
750
751
752
753
754
755
756
757
758
        struct = Struct(ty.ReferencedTypeName, ['_', array_ty])
        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
        # TODO: Fields should be iterated in the same order as defined in the type
        fields = [[n, _generate_type(f.type)] for n, f in basic_ty.Children.viewitems()]
        struct = Struct(ty.ReferencedTypeName, fields)
        g.structs[ty.ReferencedTypeName] = struct
        return struct.ty
dbarbera's avatar
dbarbera committed
759
760
761
762
    else:
        raise NotImplementedError


763
764
def _get_string_cons(str):
    ''' Returns a reference to a global string constant with the given value '''
765
766
    if str in g.strings:
        return g.strings[str]
767
768

    str_val = core.Constant.stringz(str)
769
    gvar_name = '.str%s' % len(g.strings)
770
    gvar_val = g.module.add_global_variable(str_val.type, gvar_name)
771
    gvar_val.initializer = str_val
772
    g.strings[str] = gvar_val
773
774
775
    return gvar_val


dbarbera's avatar
dbarbera committed
776
777
778
779
780
781
# 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
782
        for typename in g.dataview.viewkeys():
dbarbera's avatar
dbarbera committed
783
            if typename.lower() == basic_type.ReferencedTypeName.lower():
784
                basic_type = g.dataview[typename].type
dbarbera's avatar
dbarbera committed
785
786
                break
    return basic_type