LlvmGenerator.py 26.6 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
        self.strings = {}
dbarbera's avatar
dbarbera committed
46
        self.funcs = {}
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

        # 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)
dbarbera's avatar
dbarbera committed
63
        self.funcs['printf'] = self.module.add_function(ty, 'printf')
64

dbarbera's avatar
dbarbera committed
65
        self.funcs['memcpy'] = core.Function.intrinsic(
66
67
68
            self.module, core.INTR_MEMCPY,
            [self.i8_ptr, self.i8_ptr, self.i64]
        )
Maxime Perrotin's avatar
Maxime Perrotin committed
69

dbarbera's avatar
dbarbera committed
70

71
72
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
73
        self.name = name
74
75
76
77
78
        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
79
80
81



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

dbarbera's avatar
dbarbera committed
87

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

94
95
    global g
    g = GlobalState(process)
96

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

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

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

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

    # 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]
128
        func_ty = core.Type.function(g.void, param_tys)
dbarbera's avatar
dbarbera committed
129
130
131
        func_name = str(proc.inputString)
        func = core.Function.new(g.module, func_ty, func_name)
        g.funcs[func_name.lower()] = func
dbarbera's avatar
dbarbera committed
132

133
    # Generare process-level vars
134
135
136
137
    for name, (ty, expr) in process.variables.viewitems():
        var_ty = _generate_type(ty)
        global_var = g.module.add_global_variable(var_ty, str(name).lower())
        global_var.initializer = core.Constant.null(var_ty)
138

139
    # Generate process functions
140
141
    g.runtr = _generate_runtr_func(process)
    _generate_startup_func(process)
142

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

dbarbera's avatar
dbarbera committed
147
148
    g.module.verify()

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


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

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

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

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

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

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

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

    func.verify()
197
    g.scope.clear()
198
199
200
    return func


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

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

211
212
213
214
215
216
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
            global_var = g.module.get_global_variable_named(str(name).lower())
            _generate_assign(global_var, expression(expr))

217
    builder.call(g.runtr, [core.Constant.int(core.Type.int(), 0)])
Maxime Perrotin's avatar
Maxime Perrotin committed
218
219
    builder.ret_void()

220
221
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
222
223


224
def _generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
225
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
226
    func_name = g.name + "_" + str(signal['name'])
227
228
229
    param_tys = []
    if 'type' in signal:
        param_tys.append(core.Type.pointer(_generate_type(signal['type'])))
230
231
    func_type = core.Type.function(g.void, param_tys)
    func = core.Function.new(g.module, func_type, func_name)
dbarbera's avatar
dbarbera committed
232
    g.funcs[func_name.lower()] = func
233
234
235

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

238
    runtr_func = g.module.get_function_named('run_transition')
239

240
241
    g_state_val = g.builder.load(g.module.get_global_variable_named('state'))
    switch = g.builder.switch(g_state_val, exit_block)
242

243
    for state_name, state_id in g.states.iteritems():
244
245
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
246
        g.builder.position_at_end(state_block)
247
248
249

        # TODO: Nested states

250
251
252
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
253
                var_val = g.module.get_global_variable_named(str(var_name).lower())
254
255
                _generate_assign(var_val, func.args[0])
            if input.transition:
256
257
                id_val = core.Constant.int(g.i32, input.transition_id)
                g.builder.call(runtr_func, [id_val])
258

259
        g.builder.ret_void()
260

261
262
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
263
264
265
266

    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
267
268
269
270
@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
271
272
273
274

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

275
        if name == 'write':
dbarbera's avatar
dbarbera committed
276
277
            _generate_write(out['params'])
            continue
278
279
280
        elif name == 'writeln':
            _generate_writeln(out['params'])
            continue
dbarbera's avatar
dbarbera committed
281
282
283
284
285
286
287
        elif name == 'reset_timer':
            _generate_reset_timer(out['params'])
            continue
        elif name == 'set_timer':
            _generate_set_timer(out['params'])
            continue

dbarbera's avatar
dbarbera committed
288
        func = g.funcs[str(name).lower()]
289
        g.builder.call(func, [expression(p) for p in out.get('params', [])])
dbarbera's avatar
dbarbera committed
290
291
292
293


def _generate_write(params):
    ''' Generate the code for the write operator '''
294
    zero = core.Constant.int(g.i32, 0)
295
296
297
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
298
299
300
301

        if basic_ty.kind != 'StringType' and expr_val.type.kind == core.TYPE_POINTER:
            expr_val = g.builder.load(expr_val)

302
        if basic_ty.kind == 'IntegerType':
303
            fmt_val = _get_string_cons('% d')
304
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
dbarbera's avatar
dbarbera committed
305
            g.builder.call(g.funcs['printf'], [fmt_ptr, expr_val])
306
        elif basic_ty.kind == 'RealType':
307
            fmt_val = _get_string_cons('% .14E')
308
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
dbarbera's avatar
dbarbera committed
309
            g.builder.call(g.funcs['printf'], [fmt_ptr, expr_val])
310
        elif basic_ty.kind == 'BooleanType':
311
            true_str_val = _get_string_cons('TRUE')
312
            true_str_ptr = g.builder.gep(true_str_val, [zero, zero])
313
            false_str_val = _get_string_cons('FALSE')
314
315
            false_str_ptr = g.builder.gep(false_str_val, [zero, zero])
            str_ptr = g.builder.select(expr_val, true_str_ptr, false_str_ptr)
dbarbera's avatar
dbarbera committed
316
            g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
317
318
        elif basic_ty.kind == 'StringType':
            expr_ptr = g.builder.gep(expr_val, [zero, zero])
dbarbera's avatar
dbarbera committed
319
            g.builder.call(g.funcs['printf'], [expr_ptr])
320
321
322
323
324
325
326
        else:
            raise NotImplementedError


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
327
328

    zero = core.Constant.int(g.i32, 0)
329
    str_cons = _get_string_cons('\n')
330
    str_ptr = g.builder.gep(str_cons, [zero, zero])
dbarbera's avatar
dbarbera committed
331
    g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
332
333
334
335
336
337
338
339
340


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 '''
341
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
342
343
344
345


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
346
    ''' Generate the code of a list of assignments '''
347
348
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
349
350
351
352
353


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
354
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
355
356
357
358


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

dbarbera's avatar
dbarbera committed
362

Maxime Perrotin's avatar
Maxime Perrotin committed
363
364
365
366
# ------ expressions --------

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
367
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
368
369
370
371
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
372
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
373
    ''' Generate the code for a single variable reference '''
374
    return g.module.get_global_variable_named(str(prim.value[0]).lower())
Maxime Perrotin's avatar
Maxime Perrotin committed
375
376


Maxime Perrotin's avatar
Maxime Perrotin committed
377
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
378
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
379
    ''' Generate the code for an of an element list (path) '''
dbarbera's avatar
dbarbera committed
380
381
382
383

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

    if not prim.value:
384
        return var_ptr
dbarbera's avatar
dbarbera committed
385
386
387
388
389
390
391
392
393
394
395
396
397

    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

398
    return var_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
399
400


Maxime Perrotin's avatar
Maxime Perrotin committed
401
402
403
404
405
406
407
408
409
410
411
412
@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
413
414
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
415
416
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
417
418

    # load the value of the expression if it is a pointer
419
    if lefttmp.type.kind == core.TYPE_POINTER:
420
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
421
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
422
        righttmp = g.builder.load(righttmp, 'righttmp')
423
424
425
426
427
428

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

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

Maxime Perrotin's avatar
Maxime Perrotin committed
483

484
485
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
486
    ''' Generate the code for an assign expression '''
487
488
    left = expression(expr.left)
    right = expression(expr.right)
489

490
491
    _generate_assign(left, right)
    return left
492

493
494
495
496
497

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
498
    if left.type.pointee.kind == core.TYPE_STRUCT:
499
500
        size = core.Constant.sizeof(left.type.pointee)
        align = core.Constant.int(g.i32, 0)
501
        volatile = core.Constant.int(g.i1, 0)
502

503
504
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
505

dbarbera's avatar
dbarbera committed
506
        g.builder.call(g.funcs['memcpy'], [left_ptr, right_ptr, size, align, volatile])
507
    else:
508
509
        if right.type.kind == core.TYPE_POINTER:
            right = g.builder.load(right)
510
        g.builder.store(right, left)
dbarbera's avatar
dbarbera committed
511

Maxime Perrotin's avatar
Maxime Perrotin committed
512

Maxime Perrotin's avatar
Maxime Perrotin committed
513
514
515
@expression.register(ogAST.ExprOr)
@expression.register(ogAST.ExprAnd)
@expression.register(ogAST.ExprXor)
dbarbera's avatar
dbarbera committed
516
517
def _logical(expr):
    ''' Generate the code for a logical expression '''
dbarbera's avatar
dbarbera committed
518
519
520
521
522
523
524
525
526
    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:
527
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
dbarbera's avatar
dbarbera committed
528
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
529
        righttmp = g.builder.load(righttmp, 'righttmp')
dbarbera's avatar
dbarbera committed
530

dbarbera's avatar
dbarbera committed
531
532
533
    if expr.operand == 'and':
        return g.builder.and_(lefttmp, righttmp, 'andtmp')
    elif expr.operand == 'or':
534
        return g.builder.or_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
535
    else:
536
        return g.builder.xor(lefttmp, righttmp, 'xortmp')
dbarbera's avatar
dbarbera committed
537

Maxime Perrotin's avatar
Maxime Perrotin committed
538

Maxime Perrotin's avatar
Maxime Perrotin committed
539
@expression.register(ogAST.ExprAppend)
Maxime Perrotin's avatar
Maxime Perrotin committed
540
541
def _append(expr):
    ''' Generate code for the APPEND construct: a // b '''
542
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
543
544


Maxime Perrotin's avatar
Maxime Perrotin committed
545
@expression.register(ogAST.ExprIn)
Maxime Perrotin's avatar
Maxime Perrotin committed
546
def _expr_in(expr):
dbarbera's avatar
dbarbera committed
547
    ''' Generate the code for an in expression '''
548
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
549
550


Maxime Perrotin's avatar
Maxime Perrotin committed
551
@expression.register(ogAST.PrimEnumeratedValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
552
553
def _enumerated_value(primary):
    ''' Generate code for an enumerated value '''
dbarbera's avatar
dbarbera committed
554
555
556
    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
557
558


Maxime Perrotin's avatar
Maxime Perrotin committed
559
@expression.register(ogAST.PrimChoiceDeterminant)
Maxime Perrotin's avatar
Maxime Perrotin committed
560
561
def _choice_determinant(primary):
    ''' Generate code for a choice determinant (enumerated) '''
562
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
563
564


Maxime Perrotin's avatar
Maxime Perrotin committed
565
@expression.register(ogAST.PrimInteger)
566
567
def _integer(primary):
    ''' Generate code for a raw integer value  '''
568
    return core.Constant.int(g.i32, primary.value[0])
569
570


Maxime Perrotin's avatar
Maxime Perrotin committed
571
@expression.register(ogAST.PrimReal)
572
573
def _real(primary):
    ''' Generate code for a raw real value  '''
574
    return core.Constant.real(g.double, primary.value[0])
575
576


Maxime Perrotin's avatar
Maxime Perrotin committed
577
@expression.register(ogAST.PrimBoolean)
578
579
def _boolean(primary):
    ''' Generate code for a raw boolean value  '''
dbarbera's avatar
dbarbera committed
580
    if primary.value[0].lower() == 'true':
581
        return core.Constant.int(g.i1, 1)
dbarbera's avatar
dbarbera committed
582
    else:
583
        return core.Constant.int(g.i1, 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
584
585


Maxime Perrotin's avatar
Maxime Perrotin committed
586
@expression.register(ogAST.PrimEmptyString)
Maxime Perrotin's avatar
Maxime Perrotin committed
587
588
def _empty_string(primary):
    ''' Generate code for an empty SEQUENCE OF: {} '''
589
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
590
591


Maxime Perrotin's avatar
Maxime Perrotin committed
592
@expression.register(ogAST.PrimStringLiteral)
Maxime Perrotin's avatar
Maxime Perrotin committed
593
594
def _string_literal(primary):
    ''' Generate code for a string (Octet String) '''
dbarbera's avatar
dbarbera committed
595
    return _get_string_cons(str(primary.value[1:-1]))
Maxime Perrotin's avatar
Maxime Perrotin committed
596
597


Maxime Perrotin's avatar
Maxime Perrotin committed
598
@expression.register(ogAST.PrimConstant)
Maxime Perrotin's avatar
Maxime Perrotin committed
599
600
def _constant(primary):
    ''' Generate code for a reference to an ASN.1 constant '''
601
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
602
603


Maxime Perrotin's avatar
Maxime Perrotin committed
604
@expression.register(ogAST.PrimMantissaBaseExp)
Maxime Perrotin's avatar
Maxime Perrotin committed
605
606
def _mantissa_base_exp(primary):
    ''' Generate code for a Real with Mantissa-base-Exponent representation '''
607
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
608
609


Maxime Perrotin's avatar
Maxime Perrotin committed
610
@expression.register(ogAST.PrimIfThenElse)
dbarbera's avatar
dbarbera committed
611
def _if_then_else(ifthen):
dbarbera's avatar
dbarbera committed
612
    ''' Generate the code for ternary operator '''
613
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
614
615


Maxime Perrotin's avatar
Maxime Perrotin committed
616
@expression.register(ogAST.PrimSequence)
Maxime Perrotin's avatar
Maxime Perrotin committed
617
def _sequence(seq):
dbarbera's avatar
dbarbera committed
618
    ''' Generate the code for an ASN.1 SEQUENCE '''
dbarbera's avatar
dbarbera committed
619
620
621
622
623
624
625
626
627
628
629
    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
630
631


Maxime Perrotin's avatar
Maxime Perrotin committed
632
@expression.register(ogAST.PrimSequenceOf)
Maxime Perrotin's avatar
Maxime Perrotin committed
633
def _sequence_of(seqof):
dbarbera's avatar
dbarbera committed
634
    ''' Generate the code for an ASN.1 SEQUENCE OF '''
635
    ty = _generate_type(seqof.exprType)
636
637
638
    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])
639
640

    for idx, expr in enumerate(seqof.value):
641
        idx_cons = core.Constant.int(g.i32, idx)
642
        expr_val = expression(expr)
643
644
        pos_ptr = g.builder.gep(array_ptr, [zero_cons, idx_cons])
        g.builder.store(expr_val, pos_ptr)
645
646

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
647
648


Maxime Perrotin's avatar
Maxime Perrotin committed
649
@expression.register(ogAST.PrimChoiceItem)
Maxime Perrotin's avatar
Maxime Perrotin committed
650
def _choiceitem(choice):
dbarbera's avatar
dbarbera committed
651
    ''' Generate the code for a CHOICE expression '''
652
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
653
654
655
656


@generate.register(ogAST.Decision)
def _decision(dec):
dbarbera's avatar
dbarbera committed
657
    ''' Generate the code for a decision '''
658
    func = g.builder.basic_block.function
dbarbera's avatar
dbarbera committed
659
660
661
662

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

663
    g.builder.branch(ans_cond_blocks[0])
dbarbera's avatar
dbarbera committed
664
665
666
667
668

    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')
669
        g.builder.position_at_end(ans_cond_block)
dbarbera's avatar
dbarbera committed
670
671

        if ans.kind == 'constant':
dbarbera's avatar
dbarbera committed
672
            next_block = ans_cond_blocks[idx+1] if idx < len(ans_cond_blocks)-1 else end_block
dbarbera's avatar
dbarbera committed
673
674
675
676
677
678

            expr = ans.openRangeOp()
            expr.left = dec.question
            expr.right = ans.constant
            expr_val = expression(expr)

679
680
681
            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
682
683
        elif ans.kind == 'else':
            if ans.transition:
684
                g.builder.branch(ans_tr_block)
dbarbera's avatar
dbarbera committed
685
            else:
686
                g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
687
688
689
690
        else:
            raise NotImplementedError

        if ans.transition:
691
            g.builder.position_at_end(ans_tr_block)
dbarbera's avatar
dbarbera committed
692
            generate(ans.transition)
693
            g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
694

695
    g.builder.position_at_end(end_block)
Maxime Perrotin's avatar
Maxime Perrotin committed
696
697
698
699


@generate.register(ogAST.Label)
def _label(tr):
dbarbera's avatar
dbarbera committed
700
    ''' TGenerate the code for a Label '''
701
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
702
703
704
705


@generate.register(ogAST.Transition)
def _transition(tr):
dbarbera's avatar
dbarbera committed
706
    ''' Generate the code for a transition '''
707
708
709
710
711
712
    for action in tr.actions:
        generate(action)
        if isinstance(action, ogAST.Label):
            return
    if tr.terminator:
        _generate_terminator(tr.terminator)
713
714


715
def _generate_terminator(term):
dbarbera's avatar
dbarbera committed
716
    ''' Generate the code for a transition termiantor '''
717
    id_ptr = g.scope['id']
718
    if term.label:
719
        raise NotImplementedError
720
721
722
    if term.kind == 'next_state':
        state = term.inputString.lower()
        if state.strip() != '-':
723
724
            next_id_cons = core.Constant.int(g.i32, term.next_id)
            g.builder.store(next_id_cons, id_ptr)
725
            if term.next_id == -1:
726
727
728
                state_ptr = g.module.get_global_variable_named('state')
                state_id_cons = g.states[state]
                g.builder.store(state_id_cons, state_ptr)
729
        else:
730
            raise NotImplementedError
731
    elif term.kind == 'join':
732
        raise NotImplementedError
733
    elif term.kind == 'stop':
734
        raise NotImplementedError
735
    elif term.kind == 'return':
736
        raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
737
738
739
740


@generate.register(ogAST.Floating_label)
def _floating_label(label):
dbarbera's avatar
dbarbera committed
741
    ''' Generate the code for a floating label '''
742
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
743
744
745
746
747


@generate.register(ogAST.Procedure)
def _inner_procedure(proc):
    ''' Generate the code for a procedure '''
748
    raise NotImplementedError
dbarbera's avatar
dbarbera committed
749
750
751


def _generate_type(ty):
752
    ''' Generate the equivalent LLVM type of a ASN.1 type '''
dbarbera's avatar
dbarbera committed
753
754
    basic_ty = find_basic_type(ty)
    if basic_ty.kind == 'IntegerType':
755
        return g.i32
dbarbera's avatar
dbarbera committed
756
    elif basic_ty.kind == 'BooleanType':
757
        return g.i1
dbarbera's avatar
dbarbera committed
758
    elif basic_ty.kind == 'RealType':
759
        return g.double
760
    elif basic_ty.kind == 'SequenceOfType':
dbarbera's avatar
dbarbera committed
761
762
        if ty.ReferencedTypeName in g.structs:
            return g.structs[ty.ReferencedTypeName].ty
763
764
765
766
767
768
769
770

        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)
771
        struct = StructType(ty.ReferencedTypeName, ['_'], [array_ty])
dbarbera's avatar
dbarbera committed
772
773
774
775
776
        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
777
778
779
780

        field_names = []
        field_types = []
        for field_name in Helper.sorted_fields(basic_ty):
781
            field_names.append(field_name.replace('-', '_'))
782
783
784
            field_types.append(_generate_type(basic_ty.Children[field_name].type))

        struct = StructType(ty.ReferencedTypeName, field_names, field_types)
dbarbera's avatar
dbarbera committed
785
786
        g.structs[ty.ReferencedTypeName] = struct
        return struct.ty
dbarbera's avatar
dbarbera committed
787
788
    elif basic_ty.kind == 'EnumeratedType':
        return g.i32
dbarbera's avatar
dbarbera committed
789
790
791
792
    else:
        raise NotImplementedError


793
794
def _get_string_cons(str):
    ''' Returns a reference to a global string constant with the given value '''
795
796
    if str in g.strings:
        return g.strings[str]
797
798

    str_val = core.Constant.stringz(str)
799
    gvar_name = '.str%s' % len(g.strings)
800
    gvar_val = g.module.add_global_variable(str_val.type, gvar_name)
801
    gvar_val.initializer = str_val
802
    g.strings[str] = gvar_val
803
804
805
    return gvar_val


dbarbera's avatar
dbarbera committed
806
807
808
809
810
811
# 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
812
        for typename in g.dataview.viewkeys():
dbarbera's avatar
dbarbera committed
813
            if typename.lower() == basic_type.ReferencedTypeName.lower():
814
                basic_type = g.dataview[typename].type
dbarbera's avatar
dbarbera committed
815
816
                break
    return basic_type