LlvmGenerator.py 30.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
21

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

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

LOG = logging.getLogger(__name__)

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

Maxime Perrotin's avatar
Maxime Perrotin committed
32

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


class GlobalState():
    def __init__(self, process):
dbarbera's avatar
dbarbera committed
39
40
        self.name = str(process.processName)
        self.module = core.Module.new(self.name)
41
42
        self.dataview = process.dataview

43
44
        self.scope = Scope()
        self.global_scope = self.scope
45
        self.states = {}
dbarbera's avatar
dbarbera committed
46
        self.structs = {}
47
        self.strings = {}
dbarbera's avatar
dbarbera committed
48
        self.funcs = {}
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

        # 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
65
        self.funcs['printf'] = self.module.add_function(ty, 'printf')
66

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

dbarbera's avatar
dbarbera committed
72

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


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
class Scope:
    def __init__(self, parent=None):
        self.vars = {}
        self.parent = parent

    def define(self, name, var):
        self.vars[name.lower()] = var

    def resolve(self, name):
        var = self.vars.get(name.lower())
        if var:
            return var
        if self.parent:
            return self.parent.resolve(name)
        else:
dbarbera's avatar
dbarbera committed
98
            print name
99
100
            raise NameError

dbarbera's avatar
dbarbera committed
101

Maxime Perrotin's avatar
Maxime Perrotin committed
102
103
104
105
@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
106

dbarbera's avatar
dbarbera committed
107

Maxime Perrotin's avatar
Maxime Perrotin committed
108
109
110
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
111
    ''' Generate LLVM IR code '''
dbarbera's avatar
dbarbera committed
112
113
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
114

115
116
    global g
    g = GlobalState(process)
117

dbarbera's avatar
dbarbera committed
118
119
120
121
122
123
    # 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
124

125
126
127
    # Initialize states enum
    for name in process.mapping.iterkeys():
        if not name.endswith('START'):
128
129
            cons = core.Constant.int(g.i32, len(g.states))
            g.states[name] = cons
130

131
    # Generate state var
132
133
    state_cons = g.module.add_global_variable(g.i32, 'state')
    state_cons.initializer = core.Constant.int(g.i32, -1)
134
    g.scope.define('state', state_cons)
135

136
137
138
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
        var_ty = _generate_type(ty)
139
        global_var = g.module.add_global_variable(var_ty, str(name))
140
        global_var.initializer = core.Constant.null(var_ty)
141
        g.scope.define(str(name).lower(), global_var)
142

dbarbera's avatar
dbarbera committed
143
144
145
146
147
148
149
150
    # Declare timer set/reset functions
    for timer in process.timers:
        func_name = '%s_RI_set_%s' % (process_name, str(timer))
        # TODO: Should be uint?
        decl_func(func_name, g.void, [g.i32_ptr])
        func_name = '%s_RI_reset_%s' % (process_name, str(timer))
        decl_func(func_name, g.void, [])

151
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
152
    for signal in process.output_signals:
153
154
155
156
        if 'type' in signal:
            param_tys = [core.Type.pointer(_generate_type(signal['type']))]
        else:
            param_tys = []
157
        decl_func(str(signal['name']), g.void, param_tys)
dbarbera's avatar
dbarbera committed
158

159
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
160
161
    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]
162
        decl_func(str(proc.inputString), g.void, param_tys)
dbarbera's avatar
dbarbera committed
163

164
165
    # Generate internal procedures
    for proc in process.content.inner_procedures:
166
        generate(proc)
167

168
    # Generate process functions
169
    _generate_runtr_func(process)
170
    _generate_startup_func(process)
171

172
173
174
175
    # Generate input signals
    for signal in process.input_signals:
        _generate_input_signal(signal, mapping[signal['name']])

dbarbera's avatar
dbarbera committed
176
177
    g.module.verify()

dbarbera's avatar
dbarbera committed
178
    with open(g.name  + '.ll', 'w') as ll_file:
dbarbera's avatar
dbarbera committed
179
        ll_file.write(str(g.module))
180
181
182


def _generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
183
    ''' Generate code for the run_transition function '''
184
    func = decl_func('run_transition', g.void, [g.i32])
185

186
187
    _push_scope()

188
189
190
191
192
    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
193
    g.builder = core.Builder.new(entry_block)
194
195

    # entry
dbarbera's avatar
dbarbera committed
196
    id_ptr = g.builder.alloca(g.i32, None, 'id')
197
    g.scope.define('id', id_ptr)
dbarbera's avatar
dbarbera committed
198
199
    g.builder.store(func.args[0], id_ptr)
    g.builder.branch(cond_block)
200
201

    # cond
dbarbera's avatar
dbarbera committed
202
    g.builder.position_at_end(cond_block)
203
    no_tr_cons = core.Constant.int(g.i32, -1)
204
205
    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
206
    g.builder.cbranch(cond_val, body_block, exit_block)
207
208

    # body
dbarbera's avatar
dbarbera committed
209
210
    g.builder.position_at_end(body_block)
    switch = g.builder.switch(func.args[0], exit_block)
211
212
213
214

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
215
        const = core.Constant.int(g.i32, idx)
216
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
217
        g.builder.position_at_end(tr_block)
218
        generate(tr)
dbarbera's avatar
dbarbera committed
219
        g.builder.branch(cond_block)
220
221

    # exit
dbarbera's avatar
dbarbera committed
222
223
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
224

225
226
    _pop_scope()

227
228
229
230
    func.verify()
    return func


231
def _generate_startup_func(process):
dbarbera's avatar
dbarbera committed
232
    ''' Generate code for the startup function '''
233
    func = decl_func(g.name + '_startup', g.void, [])
234

235
236
    _push_scope()

237
    entry_block = func.append_basic_block('entry')
238
    g.builder = core.Builder.new(entry_block)
239

240
241
242
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
243
            global_var = g.scope.resolve(str(name))
244
245
            _generate_assign(global_var, expression(expr))

246
247
    g.builder.call(g.funcs['run_transition'], [core.Constant.int(g.i32, 0)])
    g.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
248

249
250
    _pop_scope()

251
252
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
253
254


255
def _generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
256
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
257
    func_name = g.name + "_" + str(signal['name'])
258
259
260
    param_tys = []
    if 'type' in signal:
        param_tys.append(core.Type.pointer(_generate_type(signal['type'])))
261
262

    func = decl_func(func_name, g.void, param_tys)
263

264
265
    _push_scope()

266
267
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
268
    g.builder = core.Builder.new(entry_block)
269

270
    g_state_val = g.builder.load(g.global_scope.resolve('state'))
271
    switch = g.builder.switch(g_state_val, exit_block)
272

273
    for state_name, state_id in g.states.iteritems():
274
275
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
276
        g.builder.position_at_end(state_block)
277
278
279

        # TODO: Nested states

280
281
282
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
283
                var_val = g.scope.resolve(str(var_name))
284
285
                _generate_assign(var_val, func.args[0])
            if input.transition:
286
                id_val = core.Constant.int(g.i32, input.transition_id)
287
                g.builder.call(g.funcs['run_transition'], [id_val])
288

289
        g.builder.ret_void()
290

291
292
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
293

294
295
    _pop_scope()

296
297
298
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
299
300
301
302
@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 '''
303
    zero = core.Constant.int(g.i32, 0)
dbarbera's avatar
dbarbera committed
304
305
306
    for out in output.output:
        name = out['outputName'].lower()

307
        if name == 'write':
dbarbera's avatar
dbarbera committed
308
309
            _generate_write(out['params'])
            continue
310
311
312
        elif name == 'writeln':
            _generate_writeln(out['params'])
            continue
dbarbera's avatar
dbarbera committed
313
314
315
316
317
318
319
        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
320
        func = g.funcs[str(name).lower()]
321
322
323
324
325
326
327
328
329
330
331
332
333

        params = []
        for p in out.get('params', []):
            p_val = expression(p)
            # Pass by reference
            if p_val.type.kind != core.TYPE_POINTER:
                p_var = g.builder.alloca(p_val.type, None)
                g.builder.store(p_val, p_var)
                params.append(p_var)
            else:
                params.append(p_val)

        g.builder.call(func, params)
dbarbera's avatar
dbarbera committed
334
335
336
337


def _generate_write(params):
    ''' Generate the code for the write operator '''
338
    zero = core.Constant.int(g.i32, 0)
339
340
341
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
342
343
344
345

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

346
        if basic_ty.kind == 'IntegerType':
347
            fmt_val = _get_string_cons('% d')
348
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
dbarbera's avatar
dbarbera committed
349
            g.builder.call(g.funcs['printf'], [fmt_ptr, expr_val])
350
        elif basic_ty.kind == 'RealType':
351
            fmt_val = _get_string_cons('% .14E')
352
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
dbarbera's avatar
dbarbera committed
353
            g.builder.call(g.funcs['printf'], [fmt_ptr, expr_val])
354
        elif basic_ty.kind == 'BooleanType':
355
            true_str_val = _get_string_cons('TRUE')
356
            true_str_ptr = g.builder.gep(true_str_val, [zero, zero])
357
            false_str_val = _get_string_cons('FALSE')
358
359
            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
360
            g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
361
362
        elif basic_ty.kind == 'StringType':
            expr_ptr = g.builder.gep(expr_val, [zero, zero])
dbarbera's avatar
dbarbera committed
363
            g.builder.call(g.funcs['printf'], [expr_ptr])
364
365
366
367
368
369
370
        else:
            raise NotImplementedError


def _generate_writeln(params):
    ''' Generate the code for the writeln operator '''
    _generate_write(params)
371
372

    zero = core.Constant.int(g.i32, 0)
373
    str_cons = _get_string_cons('\n')
374
    str_ptr = g.builder.gep(str_cons, [zero, zero])
dbarbera's avatar
dbarbera committed
375
    g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
376
377
378
379


def _generate_reset_timer(params):
    ''' Generate the code for the reset timer operator '''
dbarbera's avatar
dbarbera committed
380
381
382
383
384
    timer_id = params[0]
    reset_func_name = '%s_RI_reset_%s' % (g.name, timer_id.value[0])
    reset_func = g.funcs[reset_func_name.lower()]

    g.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
385
386
387
388


def _generate_set_timer(params):
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
389
390
391
392
393
394
395
396
397
398
399
400
401
    timer_expr, timer_id = params
    set_func_name = '%s_RI_set_%s' % (g.name, timer_id.value[0])
    set_func = g.funcs[set_func_name.lower()]

    expr_val = expression(timer_expr)

    if type(timer_expr) in [ogAST.PrimPath, ogAST.PrimVariable]:
        expr_val = g.builder.load(expr_val)

    tmp_ptr = g.builder.alloca(expr_val.type)
    g.builder.store(expr_val, tmp_ptr)

    g.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
402
403
404
405


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
406
    ''' Generate the code of a list of assignments '''
407
408
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
409
410
411
412
413


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
414
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
415
416
417
418


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
419
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
    for loop in task.elems:
        if loop['range']:
            _generate_for_range(loop)
        else:
            _generate_for_iterable(loop)


def _generate_for_range(loop):
    ''' Generate the code for a for x in range loop '''
    func = g.builder.basic_block.function
    cond_block = func.append_basic_block('for:cond')
    body_block = func.append_basic_block('for:body')
    inc_block = func.append_basic_block('for:inc')
    end_block = func.append_basic_block('')

    _push_scope()

    loop_var = g.builder.alloca(g.i32, None, str(loop['var']))
    g.scope.define(str(loop['var']), loop_var)

    if loop['range']['start']:
        start_val = expression(loop['range']['start'])
        if type(loop['range']['start']) in [ogAST.PrimPath, ogAST.PrimVariable]:
            start_val = g.builder.load(start_val)
        g.builder.store(start_val, loop_var)
    else:
        g.builder.store(core.Constant.int(g.i32, 0), loop_var)

    stop_val = expression(loop['range']['stop'])
    if type(loop['range']['stop']) in [ogAST.PrimPath, ogAST.PrimVariable]:
        stop_val = g.builder.load(stop_val)
    g.builder.branch(cond_block)

    g.builder.position_at_end(cond_block)
    loop_val = g.builder.load(loop_var)
    cond_val = g.builder.icmp(core.ICMP_SLT, loop_val, stop_val)
    g.builder.cbranch(cond_val, body_block, end_block)

    g.builder.position_at_end(body_block)
    generate(loop['transition'])
    g.builder.branch(inc_block)

    g.builder.position_at_end(inc_block)
    step_val = core.Constant.int(g.i32, loop['range']['step'])
    loop_val = g.builder.load(loop_var)
    temp_val = g.builder.add(loop_val, step_val)
    g.builder.store(temp_val, loop_var)
    g.builder.branch(cond_block)

    g.builder.position_at_end(end_block)

    _pop_scope()


def _generate_for_iterable(loop):
    ''' Generate the code for a for x in iterable loop'''
476
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
477

dbarbera's avatar
dbarbera committed
478

Maxime Perrotin's avatar
Maxime Perrotin committed
479
480
481
482
# ------ expressions --------

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
483
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
484
485
486
487
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
488
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
489
    ''' Generate the code for a single variable reference '''
490
    return g.scope.resolve(str(prim.value[0]))
Maxime Perrotin's avatar
Maxime Perrotin committed
491
492


Maxime Perrotin's avatar
Maxime Perrotin committed
493
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
494
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
495
    ''' Generate the code for an of an element list (path) '''
dbarbera's avatar
dbarbera committed
496

497
    var_ptr = g.scope.resolve(str(prim.value.pop(0)))
dbarbera's avatar
dbarbera committed
498
499

    if not prim.value:
500
        return var_ptr
dbarbera's avatar
dbarbera committed
501
502
503
504
505
506
507
508
509
510
511
512
513

    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

514
    return var_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
515
516


Maxime Perrotin's avatar
Maxime Perrotin committed
517
518
519
520
521
522
523
524
525
526
527
528
@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
529
530
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
531
532
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
533
534

    # load the value of the expression if it is a pointer
535
    if lefttmp.type.kind == core.TYPE_POINTER:
536
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
537
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
538
        righttmp = g.builder.load(righttmp, 'righttmp')
539
540
541
542
543
544

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

    if lefttmp.type.kind == core.TYPE_INTEGER:
        if expr.operand == '+':
545
            return g.builder.add(lefttmp, righttmp, 'addtmp')
546
        elif expr.operand == '-':
547
            return g.builder.sub(lefttmp, righttmp, 'subtmp')
548
        elif expr.operand == '*':
549
            return g.builder.mul(lefttmp, righttmp, 'multmp')
550
        elif expr.operand == '/':
551
            return g.builder.sdiv(lefttmp, righttmp, 'divtmp')
552
        elif expr.operand == 'mod':
553
            # l mod r == (((l rem r) + r) rem r)
554
555
556
            remtmp = g.builder.srem(lefttmp, righttmp)
            addtmp = g.builder.add(remtmp, righttmp)
            return g.builder.srem(addtmp, righttmp, 'modtmp')
557
        elif expr.operand == 'rem':
558
            return g.builder.srem(lefttmp, righttmp, 'remtmp')
559
        elif expr.operand == '<':
560
            return g.builder.icmp(core.ICMP_SLT, lefttmp, righttmp, 'lttmp')
561
        elif expr.operand == '<=':
562
            return g.builder.icmp(core.ICMP_SLE, lefttmp, righttmp, 'letmp')
563
        elif expr.operand == '=':
564
            return g.builder.icmp(core.ICMP_EQ, lefttmp, righttmp, 'eqtmp')
565
        elif expr.operand == '/=':
566
            return g.builder.icmp(core.ICMP_NE, lefttmp, righttmp, 'netmp')
567
        elif expr.operand == '>=':
568
            return g.builder.icmp(core.ICMP_SGE, lefttmp, righttmp, 'getmp')
569
        elif expr.operand == '>':
570
            return g.builder.icmp(core.ICMP_SGT, lefttmp, righttmp, 'gttmp')
571
572
573
574
        else:
            raise NotImplementedError
    elif lefttmp.type.kind == core.TYPE_DOUBLE:
        if expr.operand == '+':
575
            return g.builder.fadd(lefttmp, righttmp, 'addtmp')
576
        elif expr.operand == '-':
577
            return g.builder.fsub(lefttmp, righttmp, 'subtmp')
578
        elif expr.operand == '*':
579
            return g.builder.fmul(lefttmp, righttmp, 'multmp')
580
        elif expr.operand == '/':
581
            return g.builder.fdiv(lefttmp, righttmp, 'divtmp')
582
        elif expr.operand == '<':
583
            return g.builder.fcmp(core.FCMP_OLT, lefttmp, righttmp, 'lttmp')
584
        elif expr.operand == '<=':
585
            return g.builder.fcmp(core.FCMP_OLE, lefttmp, righttmp, 'letmp')
586
        elif expr.operand == '=':
587
            return g.builder.fcmp(core.FCMP_OEQ, lefttmp, righttmp, 'eqtmp')
588
        elif expr.operand == '/=':
589
            return g.builder.fcmp(core.FCMP_ONE, lefttmp, righttmp, 'netmp')
590
        elif expr.operand == '>=':
591
            return g.builder.fcmp(core.FCMP_OGE, lefttmp, righttmp, 'getmp')
592
        elif expr.operand == '>':
593
            return g.builder.fcmp(core.FCMP_OGT, lefttmp, righttmp, 'gttmp')
594
595
        else:
            raise NotImplementedError
596
    else:
597
        raise NotImplementedError
598

Maxime Perrotin's avatar
Maxime Perrotin committed
599

600
601
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
602
    ''' Generate the code for an assign expression '''
603
604
    left = expression(expr.left)
    right = expression(expr.right)
605

606
607
    _generate_assign(left, right)
    return left
608

609
610
611
612
613

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
614
    if left.type.pointee.kind == core.TYPE_STRUCT:
615
616
        size = core.Constant.sizeof(left.type.pointee)
        align = core.Constant.int(g.i32, 0)
617
        volatile = core.Constant.int(g.i1, 0)
618

619
620
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
621

dbarbera's avatar
dbarbera committed
622
        g.builder.call(g.funcs['memcpy'], [left_ptr, right_ptr, size, align, volatile])
623
    else:
624
625
        if right.type.kind == core.TYPE_POINTER:
            right = g.builder.load(right)
626
        g.builder.store(right, left)
dbarbera's avatar
dbarbera committed
627

Maxime Perrotin's avatar
Maxime Perrotin committed
628

Maxime Perrotin's avatar
Maxime Perrotin committed
629
630
631
@expression.register(ogAST.ExprOr)
@expression.register(ogAST.ExprAnd)
@expression.register(ogAST.ExprXor)
dbarbera's avatar
dbarbera committed
632
633
def _logical(expr):
    ''' Generate the code for a logical expression '''
dbarbera's avatar
dbarbera committed
634
635
636
637
638
639
640
641
642
    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:
643
        lefttmp = g.builder.load(lefttmp, 'lefttmp')
dbarbera's avatar
dbarbera committed
644
    if righttmp.type.kind == core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
645
        righttmp = g.builder.load(righttmp, 'righttmp')
dbarbera's avatar
dbarbera committed
646

dbarbera's avatar
dbarbera committed
647
648
649
    if expr.operand == 'and':
        return g.builder.and_(lefttmp, righttmp, 'andtmp')
    elif expr.operand == 'or':
650
        return g.builder.or_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
651
    else:
652
        return g.builder.xor(lefttmp, righttmp, 'xortmp')
dbarbera's avatar
dbarbera committed
653

Maxime Perrotin's avatar
Maxime Perrotin committed
654

Maxime Perrotin's avatar
Maxime Perrotin committed
655
@expression.register(ogAST.ExprAppend)
Maxime Perrotin's avatar
Maxime Perrotin committed
656
657
def _append(expr):
    ''' Generate code for the APPEND construct: a // b '''
658
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
659
660


Maxime Perrotin's avatar
Maxime Perrotin committed
661
@expression.register(ogAST.ExprIn)
Maxime Perrotin's avatar
Maxime Perrotin committed
662
def _expr_in(expr):
dbarbera's avatar
dbarbera committed
663
    ''' Generate the code for an in expression '''
664
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
665
666


Maxime Perrotin's avatar
Maxime Perrotin committed
667
@expression.register(ogAST.PrimEnumeratedValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
668
669
def _enumerated_value(primary):
    ''' Generate code for an enumerated value '''
dbarbera's avatar
dbarbera committed
670
671
672
    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
673
674


Maxime Perrotin's avatar
Maxime Perrotin committed
675
@expression.register(ogAST.PrimChoiceDeterminant)
Maxime Perrotin's avatar
Maxime Perrotin committed
676
677
def _choice_determinant(primary):
    ''' Generate code for a choice determinant (enumerated) '''
678
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
679
680


Maxime Perrotin's avatar
Maxime Perrotin committed
681
@expression.register(ogAST.PrimInteger)
682
683
def _integer(primary):
    ''' Generate code for a raw integer value  '''
684
    return core.Constant.int(g.i32, primary.value[0])
685
686


Maxime Perrotin's avatar
Maxime Perrotin committed
687
@expression.register(ogAST.PrimReal)
688
689
def _real(primary):
    ''' Generate code for a raw real value  '''
690
    return core.Constant.real(g.double, primary.value[0])
691
692


Maxime Perrotin's avatar
Maxime Perrotin committed
693
@expression.register(ogAST.PrimBoolean)
694
695
def _boolean(primary):
    ''' Generate code for a raw boolean value  '''
dbarbera's avatar
dbarbera committed
696
    if primary.value[0].lower() == 'true':
697
        return core.Constant.int(g.i1, 1)
dbarbera's avatar
dbarbera committed
698
    else:
699
        return core.Constant.int(g.i1, 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
700
701


Maxime Perrotin's avatar
Maxime Perrotin committed
702
@expression.register(ogAST.PrimEmptyString)
Maxime Perrotin's avatar
Maxime Perrotin committed
703
704
def _empty_string(primary):
    ''' Generate code for an empty SEQUENCE OF: {} '''
705
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
706
707


Maxime Perrotin's avatar
Maxime Perrotin committed
708
@expression.register(ogAST.PrimStringLiteral)
Maxime Perrotin's avatar
Maxime Perrotin committed
709
710
def _string_literal(primary):
    ''' Generate code for a string (Octet String) '''
dbarbera's avatar
dbarbera committed
711
    return _get_string_cons(str(primary.value[1:-1]))
Maxime Perrotin's avatar
Maxime Perrotin committed
712
713


Maxime Perrotin's avatar
Maxime Perrotin committed
714
@expression.register(ogAST.PrimConstant)
Maxime Perrotin's avatar
Maxime Perrotin committed
715
716
def _constant(primary):
    ''' Generate code for a reference to an ASN.1 constant '''
717
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
718
719


Maxime Perrotin's avatar
Maxime Perrotin committed
720
@expression.register(ogAST.PrimMantissaBaseExp)
Maxime Perrotin's avatar
Maxime Perrotin committed
721
722
def _mantissa_base_exp(primary):
    ''' Generate code for a Real with Mantissa-base-Exponent representation '''
723
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
724
725


Maxime Perrotin's avatar
Maxime Perrotin committed
726
@expression.register(ogAST.PrimIfThenElse)
dbarbera's avatar
dbarbera committed
727
def _if_then_else(ifthen):
dbarbera's avatar
dbarbera committed
728
    ''' Generate the code for ternary operator '''
729
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
730
731


Maxime Perrotin's avatar
Maxime Perrotin committed
732
@expression.register(ogAST.PrimSequence)
Maxime Perrotin's avatar
Maxime Perrotin committed
733
def _sequence(seq):
dbarbera's avatar
dbarbera committed
734
    ''' Generate the code for an ASN.1 SEQUENCE '''
dbarbera's avatar
dbarbera committed
735
736
737
738
739
740
741
742
743
744
745
    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
746
747


Maxime Perrotin's avatar
Maxime Perrotin committed
748
@expression.register(ogAST.PrimSequenceOf)
Maxime Perrotin's avatar
Maxime Perrotin committed
749
def _sequence_of(seqof):
dbarbera's avatar
dbarbera committed
750
    ''' Generate the code for an ASN.1 SEQUENCE OF '''
751
    ty = _generate_type(seqof.exprType)
752
753
754
    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])
755
756

    for idx, expr in enumerate(seqof.value):
757
        idx_cons = core.Constant.int(g.i32, idx)
758
        expr_val = expression(expr)
759
760
        pos_ptr = g.builder.gep(array_ptr, [zero_cons, idx_cons])
        g.builder.store(expr_val, pos_ptr)
761
762

    return struct_ptr
Maxime Perrotin's avatar
Maxime Perrotin committed
763
764


Maxime Perrotin's avatar
Maxime Perrotin committed
765
@expression.register(ogAST.PrimChoiceItem)
Maxime Perrotin's avatar
Maxime Perrotin committed
766
def _choiceitem(choice):
dbarbera's avatar
dbarbera committed
767
    ''' Generate the code for a CHOICE expression '''
768
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
769
770
771
772


@generate.register(ogAST.Decision)
def _decision(dec):
dbarbera's avatar
dbarbera committed
773
    ''' Generate the code for a decision '''
774
    func = g.builder.basic_block.function
dbarbera's avatar
dbarbera committed
775
776
777
778

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

779
    g.builder.branch(ans_cond_blocks[0])
dbarbera's avatar
dbarbera committed
780
781
782
783
784

    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')
785
        g.builder.position_at_end(ans_cond_block)
dbarbera's avatar
dbarbera committed
786
787

        if ans.kind == 'constant':
dbarbera's avatar
dbarbera committed
788
            next_block = ans_cond_blocks[idx+1] if idx < len(ans_cond_blocks)-1 else end_block
dbarbera's avatar
dbarbera committed
789
790
791
792
793
794

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

795
796
797
            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
798
799
        elif ans.kind == 'else':
            if ans.transition:
800
                g.builder.branch(ans_tr_block)
dbarbera's avatar
dbarbera committed
801
            else:
802
                g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
803
804
805
806
        else:
            raise NotImplementedError

        if ans.transition:
807
            g.builder.position_at_end(ans_tr_block)
dbarbera's avatar
dbarbera committed
808
            generate(ans.transition)
809
            g.builder.branch(end_block)
dbarbera's avatar
dbarbera committed
810

811
    g.builder.position_at_end(end_block)
Maxime Perrotin's avatar
Maxime Perrotin committed
812
813
814
815


@generate.register(ogAST.Label)
def _label(tr):
dbarbera's avatar
dbarbera committed
816
    ''' TGenerate the code for a Label '''
817
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
818
819
820
821


@generate.register(ogAST.Transition)
def _transition(tr):
dbarbera's avatar
dbarbera committed
822
    ''' Generate the code for a transition '''
823
824
825
826
827
828
    for action in tr.actions:
        generate(action)
        if isinstance(action, ogAST.Label):
            return
    if tr.terminator:
        _generate_terminator(tr.terminator)
829
830


831
def _generate_terminator(term):
dbarbera's avatar
dbarbera committed
832
    ''' Generate the code for a transition termiantor '''
833
    if term.label:
834
        raise NotImplementedError
835
836
837
    if term.kind == 'next_state':
        state = term.inputString.lower()
        if state.strip() != '-':
838
            next_id_cons = core.Constant.int(g.i32, term.next_id)
839
            g.builder.store(next_id_cons, g.scope.resolve('id'))
840
            if term.next_id == -1:
841
                state_ptr = g.global_scope.resolve('state')
842
843
                state_id_cons = g.states[state]
                g.builder.store(state_id_cons, state_ptr)
844
        else:
845
            raise NotImplementedError
846
    elif term.kind == 'join':
847
        raise NotImplementedError
848
    elif term.kind == 'stop':
849
        raise NotImplementedError
850
    elif term.kind == 'return':
851
852
853
854
855
856
857
858
        if term.next_id == -1 and term.return_expr:
            g.builder.ret(expression(term.return_expr))
        elif term.next_id == -1:
            g.builder.ret_void()
        else:
            next_id_cons = core.Constant.int(g.i32, term.next_id)
            g.builder.store(next_id_cons, g.scope.resolve('id'))
            g.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
859
860
861
862


@generate.register(ogAST.Floating_label)
def _floating_label(label):
dbarbera's avatar
dbarbera committed
863
    ''' Generate the code for a floating label '''
864
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
865
866
867
868
869


@generate.register(ogAST.Procedure)
def _inner_procedure(proc):
    ''' Generate the code for a procedure '''
870
    param_tys = [core.Type.pointer(_generate_type(p['type'])) for p in proc.fpar]
871
    func = decl_func(str(proc.inputString), g.void, param_tys)
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891

    _push_scope()

    for arg, param in zip(func.args, proc.fpar):
        g.scope.define(str(param['name']), arg)

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

    for name, (ty, expr) in proc.variables.viewitems():
        raise NotImplementedError

    generate(proc.content.start.transition)

    for label in proc.content.floating_labels:
        raise NotImplementedError

    _pop_scope()

    func.verify()
dbarbera's avatar
dbarbera committed
892
893
894


def _generate_type(ty):
895
    ''' Generate the equivalent LLVM type of a ASN.1 type '''
dbarbera's avatar
dbarbera committed
896
897
    basic_ty = find_basic_type(ty)
    if basic_ty.kind == 'IntegerType':
898
        return g.i32
dbarbera's avatar
dbarbera committed
899
    elif basic_ty.kind == 'BooleanType':
900
        return g.i1
dbarbera's avatar
dbarbera committed
901
    elif basic_ty.kind == 'RealType':
902
        return g.double