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

"""
    OpenGEODE - A tiny SDL Editor for TASTE

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

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

    Copyright (c) 2012-2013 European Space Agency

    Designed and implemented by Maxime Perrotin

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

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

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

LOG = logging.getLogger(__name__)

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

Maxime Perrotin's avatar
Maxime Perrotin committed
31

32
33
34
35
36
37
# 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(primary_id):
dbarbera's avatar
dbarbera committed
361
    ''' Generate the code for an of an element list (path) '''
362
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
363
364


Maxime Perrotin's avatar
Maxime Perrotin committed
365
366
367
368
369
370
371
372
373
374
375
376
@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
377
378
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
379
380
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
381
382

    # load the value of the expression if it is a pointer
383
    if lefttmp.type.kind == core.TYPE_POINTER:
384
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
385
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
386
        righttmp = g.builder.load(righttmp, 'righttmp')
387
388
389
390
391
392

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
447

448
449
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
450
    ''' Generate the code for an assign expression '''
451
452
    left = expression(expr.left)
    right = expression(expr.right)
453
454
    _generate_assign(left, right)
    return left
455

456
457
458
459
460

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
461
    if left.type.kind == core.TYPE_POINTER and left.type.pointee.kind == core.TYPE_STRUCT:
462
463
464
        size = core.Constant.int(g.i64, 2)
        align = core.Constant.int(g.i32, 1)
        volatile = core.Constant.int(g.i1, 0)
465

466
467
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
468

469
        g.builder.call(g.memcpy, [left_ptr, right_ptr, size, align, volatile])
470
    else:
471
        g.builder.store(right, left)
dbarbera's avatar
dbarbera committed
472

Maxime Perrotin's avatar
Maxime Perrotin committed
473

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

dbarbera's avatar
dbarbera committed
492
493
494
    if expr.operand == 'and':
        return g.builder.and_(lefttmp, righttmp, 'andtmp')
    elif expr.operand == 'or':
495
        return g.builder.or_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
496
    else:
497
        return g.builder.xor(lefttmp, righttmp, 'xortmp')
dbarbera's avatar
dbarbera committed
498

Maxime Perrotin's avatar
Maxime Perrotin committed
499

Maxime Perrotin's avatar
Maxime Perrotin committed
500
@expression.register(ogAST.ExprAppend)
Maxime Perrotin's avatar
Maxime Perrotin committed
501
502
def _append(expr):
    ''' Generate code for the APPEND construct: a // b '''
503
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
504
505


Maxime Perrotin's avatar
Maxime Perrotin committed
506
@expression.register(ogAST.ExprIn)
Maxime Perrotin's avatar
Maxime Perrotin committed
507
def _expr_in(expr):
dbarbera's avatar
dbarbera committed
508
    ''' Generate the code for an in expression '''
509
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
510
511


Maxime Perrotin's avatar
Maxime Perrotin committed
512
@expression.register(ogAST.PrimEnumeratedValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
513
514
def _enumerated_value(primary):
    ''' Generate code for an enumerated value '''
515
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
516
517


Maxime Perrotin's avatar
Maxime Perrotin committed
518
@expression.register(ogAST.PrimChoiceDeterminant)
Maxime Perrotin's avatar
Maxime Perrotin committed
519
520
def _choice_determinant(primary):
    ''' Generate code for a choice determinant (enumerated) '''
521
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
522
523


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


Maxime Perrotin's avatar
Maxime Perrotin committed
530
@expression.register(ogAST.PrimReal)
531
532
def _real(primary):
    ''' Generate code for a raw real value  '''
533
    return core.Constant.real(g.double, primary.value[0])
534
535


Maxime Perrotin's avatar
Maxime Perrotin committed
536
@expression.register(ogAST.PrimBoolean)
537
538
def _boolean(primary):
    ''' Generate code for a raw boolean value  '''
dbarbera's avatar
dbarbera committed
539
    if primary.value[0].lower() == 'true':
540
        return core.Constant.int(g.i1, 1)
dbarbera's avatar
dbarbera committed
541
    else:
542
        return core.Constant.int(g.i1, 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
543
544


Maxime Perrotin's avatar
Maxime Perrotin committed
545
@expression.register(ogAST.PrimEmptyString)
Maxime Perrotin's avatar
Maxime Perrotin committed
546
547
def _empty_string(primary):
    ''' Generate code for an empty SEQUENCE OF: {} '''
548
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
549
550


Maxime Perrotin's avatar
Maxime Perrotin committed
551
@expression.register(ogAST.PrimStringLiteral)
Maxime Perrotin's avatar
Maxime Perrotin committed
552
553
def _string_literal(primary):
    ''' Generate code for a string (Octet String) '''
554
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
555
556


Maxime Perrotin's avatar
Maxime Perrotin committed
557
@expression.register(ogAST.PrimConstant)
Maxime Perrotin's avatar
Maxime Perrotin committed
558
559
def _constant(primary):
    ''' Generate code for a reference to an ASN.1 constant '''
560
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
561
562


Maxime Perrotin's avatar
Maxime Perrotin committed
563
@expression.register(ogAST.PrimMantissaBaseExp)
Maxime Perrotin's avatar
Maxime Perrotin committed
564
565
def _mantissa_base_exp(primary):
    ''' Generate code for a Real with Mantissa-base-Exponent representation '''
566
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
567
568


Maxime Perrotin's avatar
Maxime Perrotin committed
569
@expression.register(ogAST.PrimIfThenElse)
dbarbera's avatar
dbarbera committed
570
def _if_then_else(ifthen):
dbarbera's avatar
dbarbera committed
571
    ''' Generate the code for ternary operator '''
572
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
573
574


Maxime Perrotin's avatar
Maxime Perrotin committed
575
@expression.register(ogAST.PrimSequence)
Maxime Perrotin's avatar
Maxime Perrotin committed
576
def _sequence(seq):
dbarbera's avatar
dbarbera committed
577
    ''' Generate the code for an ASN.1 SEQUENCE '''
dbarbera's avatar
dbarbera committed
578
579
580
581
582
583
584
585
586
587
588
    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
589
590


Maxime Perrotin's avatar
Maxime Perrotin committed
591
@expression.register(ogAST.PrimSequenceOf)
Maxime Perrotin's avatar
Maxime Perrotin committed
592
def _sequence_of(seqof):
dbarbera's avatar
dbarbera committed
593
    ''' Generate the code for an ASN.1 SEQUENCE OF '''
594
    ty = _generate_type(seqof.exprType)
595
596
597
    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])
598
599

    for idx, expr in enumerate(seqof.value):
600
        idx_cons = core.Constant.int(g.i32, idx)
601
        expr_val = expression(expr)
602
603
        pos_ptr = g.builder.gep(array_ptr, [zero_cons, idx_cons])
        g.builder.store(expr_val, pos_ptr)
604
605

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
606
607


Maxime Perrotin's avatar
Maxime Perrotin committed
608
@expression.register(ogAST.PrimChoiceItem)
Maxime Perrotin's avatar
Maxime Perrotin committed
609
def _choiceitem(choice):
dbarbera's avatar
dbarbera committed
610
    ''' Generate the code for a CHOICE expression '''
611
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
612
613
614
615


@generate.register(ogAST.Decision)
def _decision(dec):
dbarbera's avatar
dbarbera committed
616
    ''' Generate the code for a decision '''
617
    func = g.builder.basic_block.function
dbarbera's avatar
dbarbera committed
618
619
620
621

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

622
    g.builder.branch(ans_cond_blocks[0])
dbarbera's avatar
dbarbera committed
623
624
625
626
627

    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')
628
        g.builder.position_at_end(ans_cond_block)
dbarbera's avatar
dbarbera committed
629
630
631
632
633
634
635
636
637

        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)

638
639
640
            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
641
642
        elif ans.kind == 'else':
            if ans.transition:
643
                g.builder.branch(ans_tr_block)
dbarbera's avatar
dbarbera committed
644
            else:
645
                g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
646
647
648
649
        else:
            raise NotImplementedError

        if ans.transition:
650
            g.builder.position_at_end(ans_tr_block)
dbarbera's avatar
dbarbera committed
651
            generate(ans.transition)
652
            g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
653

654
    g.builder.position_at_end(end_block)
Maxime Perrotin's avatar
Maxime Perrotin committed
655
656
657
658


@generate.register(ogAST.Label)
def _label(tr):
dbarbera's avatar
dbarbera committed
659
    ''' TGenerate the code for a Label '''
660
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
661
662
663
664


@generate.register(ogAST.Transition)
def _transition(tr):
dbarbera's avatar
dbarbera committed
665
    ''' Generate the code for a transition '''
666
667
668
669
670
671
    for action in tr.actions:
        generate(action)
        if isinstance(action, ogAST.Label):
            return
    if tr.terminator:
        _generate_terminator(tr.terminator)
672
673


674
def _generate_terminator(term):
dbarbera's avatar
dbarbera committed
675
    ''' Generate the code for a transition termiantor '''
676
    id_ptr = g.scope['id']
677
    if term.label:
678
        raise NotImplementedError
679
680
681
    if term.kind == 'next_state':
        state = term.inputString.lower()
        if state.strip() != '-':
682
683
            next_id_cons = core.Constant.int(g.i32, term.next_id)
            g.builder.store(next_id_cons, id_ptr)
684
            if term.next_id == -1:
685
686
687
                state_ptr = g.module.get_global_variable_named('state')
                state_id_cons = g.states[state]
                g.builder.store(state_id_cons, state_ptr)
688
        else:
689
            raise NotImplementedError
690
    elif term.kind == 'join':
691
        raise NotImplementedError
692
    elif term.kind == 'stop':
693
        raise NotImplementedError
694
    elif term.kind == 'return':
695
        raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
696
697
698
699


@generate.register(ogAST.Floating_label)
def _floating_label(label):
dbarbera's avatar
dbarbera committed
700
    ''' Generate the code for a floating label '''
701
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
702
703
704
705
706


@generate.register(ogAST.Procedure)
def _inner_procedure(proc):
    ''' Generate the code for a procedure '''
707
    raise NotImplementedError
dbarbera's avatar
dbarbera committed
708
709
710


def _generate_type(ty):
711
    ''' Generate the equivalent LLVM type of a ASN.1 type '''
dbarbera's avatar
dbarbera committed
712
713
    basic_ty = find_basic_type(ty)
    if basic_ty.kind == 'IntegerType':
714
        return g.i32
dbarbera's avatar
dbarbera committed
715
    elif basic_ty.kind == 'BooleanType':
716
        return g.i1
dbarbera's avatar
dbarbera committed
717
    elif basic_ty.kind == 'RealType':
718
        return g.double
719
    elif basic_ty.kind == 'SequenceOfType':
dbarbera's avatar
dbarbera committed
720
721
        if ty.ReferencedTypeName in g.structs:
            return g.structs[ty.ReferencedTypeName].ty
722
723
724
725
726
727
728
729

        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
730
731
732
733
734
735
736
737
738
739
740
        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
741
742
743
744
    else:
        raise NotImplementedError


745
746
def _get_string_cons(str):
    ''' Returns a reference to a global string constant with the given value '''
747
748
    if str in g.strings:
        return g.strings[str]
749
750
751

    str_val = core.Constant.stringz(str)
    # TODO: This names can cause conflicts with user defined variables
752
753
    gvar_name = 'str_%s' % len(g.strings)
    gvar_val = g.module.add_global_variable(str_val.type, gvar_name)
754
    gvar_val.initializer = str_val
755
    g.strings[str] = gvar_val
756
757
758
    return gvar_val


dbarbera's avatar
dbarbera committed
759
760
761
762
763
764
# 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
765
        for typename in g.dataview.viewkeys():
dbarbera's avatar
dbarbera committed
766
            if typename.lower() == basic_type.ReferencedTypeName.lower():
767
                basic_type = g.dataview[typename].type
dbarbera's avatar
dbarbera committed
768
769
                break
    return basic_type