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

Maxime Perrotin's avatar
Maxime Perrotin committed
22
from singledispatch import singledispatch
dbarbera's avatar
dbarbera committed
23
from llvm import core, ee
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)
dbarbera's avatar
dbarbera committed
41
        self.target_data = ee.TargetData.new(self.module.data_layout)
42
43
        self.dataview = process.dataview

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

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

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

75
76
77
78
79
80
        self.funcs['powi'] = core.Function.intrinsic(
            self.module,
            core.INTR_POWI,
            [self.double]
        )

dbarbera's avatar
dbarbera committed
81
82
83
84
85
86
        self.funcs['fabs'] = core.Function.intrinsic(
            self.module,
            core.INTR_FABS,
            [self.double]
        )

dbarbera's avatar
dbarbera committed
87

88
89
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
90
        self.name = name
91
92
93
94
95
        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
96
97


dbarbera's avatar
dbarbera committed
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
class UnionType():
    def __init__(self, name, field_names, field_types):
        self.name = name
        self.field_names = field_names
        self.field_types = field_types
        # Unions are represented a struct with a field indicating the index of its type
        # and a byte array with the size of the biggest type in the union
        self.size = max([g.target_data.size(ty) for ty in field_types])
        self.ty = core.Type.struct([g.i32, core.Type.array(g.i8, self.size)], name)

    def kind(self, name):
        idx = self.field_names.index(name)
        return (idx, self.field_types[idx])


113
114
115
class Scope:
    def __init__(self, parent=None):
        self.vars = {}
dbarbera's avatar
dbarbera committed
116
        self.labels = {}
117
118
119
120
121
122
123
124
125
126
127
128
        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
129
            raise NameError("name '%s' is not defined" % name)
130

dbarbera's avatar
dbarbera committed
131
132
133
134
135
136
137
138
139
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
            func = g.builder.basic_block.function
            label_block = func.append_basic_block(name)
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
140

Maxime Perrotin's avatar
Maxime Perrotin committed
141
142
143
144
@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
145

dbarbera's avatar
dbarbera committed
146

Maxime Perrotin's avatar
Maxime Perrotin committed
147
148
149
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
150
    ''' Generate LLVM IR code '''
dbarbera's avatar
dbarbera committed
151
152
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
153

154
155
    global g
    g = GlobalState(process)
156

dbarbera's avatar
dbarbera committed
157
158
159
160
161
162
    # 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
163

164
165
166
    # Initialize states enum
    for name in process.mapping.iterkeys():
        if not name.endswith('START'):
167
168
            cons = core.Constant.int(g.i32, len(g.states))
            g.states[name] = cons
169

170
    # Generate state var
171
172
    state_cons = g.module.add_global_variable(g.i32, 'state')
    state_cons.initializer = core.Constant.int(g.i32, -1)
173
    g.scope.define('state', state_cons)
174

175
176
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
dbarbera's avatar
dbarbera committed
177
        var_ty = generate_type(ty)
178
        global_var = g.module.add_global_variable(var_ty, str(name))
179
        global_var.initializer = core.Constant.null(var_ty)
180
        g.scope.define(str(name).lower(), global_var)
181

dbarbera's avatar
dbarbera committed
182
183
184
    # Declare timer set/reset functions
    for timer in process.timers:
        # TODO: Should be uint?
185
186
        decl_func("set_%s" % str(timer), g.void, [g.i32_ptr], True)
        decl_func("reset_%s" % str(timer), g.void, [], True)
dbarbera's avatar
dbarbera committed
187

188
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
189
    for signal in process.output_signals:
190
        if 'type' in signal:
dbarbera's avatar
dbarbera committed
191
            param_tys = [core.Type.pointer(generate_type(signal['type']))]
192
193
        else:
            param_tys = []
194
        decl_func(str(signal['name']), g.void, param_tys, True)
dbarbera's avatar
dbarbera committed
195

196
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
197
    for proc in [proc for proc in process.procedures if proc.external]:
dbarbera's avatar
dbarbera committed
198
        param_tys = [core.Type.pointer(generate_type(p['type'])) for p in proc.fpar]
199
        decl_func(str(proc.inputString), g.void, param_tys, True)
dbarbera's avatar
dbarbera committed
200

201
202
    # Generate internal procedures
    for proc in process.content.inner_procedures:
203
        generate(proc)
204

205
    # Generate process functions
dbarbera's avatar
dbarbera committed
206
207
    generate_runtr_func(process)
    generate_startup_func(process)
208

209
210
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
211
        generate_input_signal(signal, mapping[signal['name']])
212

dbarbera's avatar
dbarbera committed
213
214
    g.module.verify()

dbarbera's avatar
dbarbera committed
215
    with open(g.name + '.ll', 'w') as ll_file:
dbarbera's avatar
dbarbera committed
216
        ll_file.write(str(g.module))
217
218


dbarbera's avatar
dbarbera committed
219
def generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
220
    ''' Generate code for the run_transition function '''
221
    func = decl_func('run_transition', g.void, [g.i32])
222

dbarbera's avatar
dbarbera committed
223
    open_scope()
224

225
226
227
228
229
    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
230
    g.builder = core.Builder.new(entry_block)
231
232

    # entry
dbarbera's avatar
dbarbera committed
233
    id_ptr = g.builder.alloca(g.i32, None, 'id')
234
    g.scope.define('id', id_ptr)
dbarbera's avatar
dbarbera committed
235
236
    g.builder.store(func.args[0], id_ptr)
    g.builder.branch(cond_block)
237
238

    # cond
dbarbera's avatar
dbarbera committed
239
    g.builder.position_at_end(cond_block)
240
    no_tr_cons = core.Constant.int(g.i32, -1)
241
242
    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
243
    g.builder.cbranch(cond_val, body_block, exit_block)
244
245

    # body
dbarbera's avatar
dbarbera committed
246
247
    g.builder.position_at_end(body_block)
    switch = g.builder.switch(func.args[0], exit_block)
248
249
250
251

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
252
        const = core.Constant.int(g.i32, idx)
253
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
254
        g.builder.position_at_end(tr_block)
255
        generate(tr)
dbarbera's avatar
dbarbera committed
256
257
        if not g.builder.basic_block.terminator:
            g.builder.branch(cond_block)
258
259

    # exit
dbarbera's avatar
dbarbera committed
260
261
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
262

dbarbera's avatar
dbarbera committed
263
264
265
266
267
268
269
270
271
    Helper.inner_labels_to_floating(process)
    for label in process.content.floating_labels:
        generate(label)

    # TODO: Use defined cond_block instead?
    next_tr_label_block = g.scope.label('next_transition')
    g.builder.position_at_end(next_tr_label_block)
    g.builder.branch(cond_block)

dbarbera's avatar
dbarbera committed
272
    close_scope()
273

274
275
276
277
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
278
def generate_startup_func(process):
dbarbera's avatar
dbarbera committed
279
    ''' Generate code for the startup function '''
280
    func = decl_func(g.name + '_startup', g.void, [])
281

dbarbera's avatar
dbarbera committed
282
    open_scope()
283

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

287
288
289
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
290
            global_var = g.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
291
            generate_assign(global_var, expression(expr))
292

293
294
    g.builder.call(g.funcs['run_transition'], [core.Constant.int(g.i32, 0)])
    g.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
295

dbarbera's avatar
dbarbera committed
296
    close_scope()
297

298
299
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
300
301


dbarbera's avatar
dbarbera committed
302
def generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
303
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
304
    func_name = g.name + "_" + str(signal['name'])
305
306
    param_tys = []
    if 'type' in signal:
dbarbera's avatar
dbarbera committed
307
        param_tys.append(core.Type.pointer(generate_type(signal['type'])))
308
309

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

dbarbera's avatar
dbarbera committed
311
    open_scope()
312

313
314
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
315
    g.builder = core.Builder.new(entry_block)
316

317
    g_state_val = g.builder.load(g.global_scope.resolve('state'))
318
    switch = g.builder.switch(g_state_val, exit_block)
319

320
    for state_name, state_id in g.states.iteritems():
321
322
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
323
        g.builder.position_at_end(state_block)
324
325
326

        # TODO: Nested states

327
328
329
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
330
                var_ptr = g.scope.resolve(str(var_name))
331
332
333
334
                if is_struct_ptr(var_ptr):
                    generate_assign(var_ptr, func.args[0])
                else:
                    generate_assign(var_ptr, g.builder.load(func.args[0]))
335
            if input.transition:
336
                id_val = core.Constant.int(g.i32, input.transition_id)
337
                g.builder.call(g.funcs['run_transition'], [id_val])
338

339
        g.builder.ret_void()
340

341
342
    g.builder.position_at_end(exit_block)
    g.builder.ret_void()
343

dbarbera's avatar
dbarbera committed
344
    close_scope()
345

346
347
348
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
349
350
351
352
@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
353
354
355
    for out in output.output:
        name = out['outputName'].lower()

356
        if name == 'write':
dbarbera's avatar
dbarbera committed
357
            generate_write(out['params'])
dbarbera's avatar
dbarbera committed
358
            continue
359
        elif name == 'writeln':
dbarbera's avatar
dbarbera committed
360
            generate_writeln(out['params'])
361
            continue
dbarbera's avatar
dbarbera committed
362
        elif name == 'reset_timer':
dbarbera's avatar
dbarbera committed
363
            generate_reset_timer(out['params'])
dbarbera's avatar
dbarbera committed
364
365
            continue
        elif name == 'set_timer':
dbarbera's avatar
dbarbera committed
366
            generate_set_timer(out['params'])
dbarbera's avatar
dbarbera committed
367
368
            continue

dbarbera's avatar
dbarbera committed
369
        func = g.funcs[str(name).lower()]
370
371
372
373
374
375
376
377
378
379
380
381
382

        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
383
384


dbarbera's avatar
dbarbera committed
385
def generate_write(params):
dbarbera's avatar
dbarbera committed
386
    ''' Generate the code for the write operator '''
387
    zero = core.Constant.int(g.i32, 0)
388
389
390
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
391

392
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
dbarbera's avatar
dbarbera committed
393
            fmt_val = get_string_cons('% d')
394
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
dbarbera's avatar
dbarbera committed
395
            g.builder.call(g.funcs['printf'], [fmt_ptr, expr_val])
396
        elif basic_ty.kind == 'RealType':
dbarbera's avatar
dbarbera committed
397
            fmt_val = get_string_cons('% .14E')
398
            fmt_ptr = g.builder.gep(fmt_val, [zero, zero])
dbarbera's avatar
dbarbera committed
399
            g.builder.call(g.funcs['printf'], [fmt_ptr, expr_val])
400
        elif basic_ty.kind == 'BooleanType':
dbarbera's avatar
dbarbera committed
401
            true_str_val = get_string_cons('TRUE')
402
            true_str_ptr = g.builder.gep(true_str_val, [zero, zero])
dbarbera's avatar
dbarbera committed
403
            false_str_val = get_string_cons('FALSE')
404
405
            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
406
            g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
407
408
        elif basic_ty.kind == 'StringType':
            expr_ptr = g.builder.gep(expr_val, [zero, zero])
dbarbera's avatar
dbarbera committed
409
            g.builder.call(g.funcs['printf'], [expr_ptr])
410
411
412
413
        else:
            raise NotImplementedError


dbarbera's avatar
dbarbera committed
414
def generate_writeln(params):
415
    ''' Generate the code for the writeln operator '''
dbarbera's avatar
dbarbera committed
416
    generate_write(params)
417
418

    zero = core.Constant.int(g.i32, 0)
dbarbera's avatar
dbarbera committed
419
    str_cons = get_string_cons('\n')
420
    str_ptr = g.builder.gep(str_cons, [zero, zero])
dbarbera's avatar
dbarbera committed
421
    g.builder.call(g.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
422
423


dbarbera's avatar
dbarbera committed
424
def generate_reset_timer(params):
dbarbera's avatar
dbarbera committed
425
    ''' Generate the code for the reset timer operator '''
dbarbera's avatar
dbarbera committed
426
    timer_id = params[0]
427
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
428
429
430
    reset_func = g.funcs[reset_func_name.lower()]

    g.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
431
432


dbarbera's avatar
dbarbera committed
433
def generate_set_timer(params):
dbarbera's avatar
dbarbera committed
434
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
435
    timer_expr, timer_id = params
436
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
437
438
439
440
441
442
443
444
    set_func = g.funcs[set_func_name.lower()]

    expr_val = expression(timer_expr)

    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
445
446
447
448


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
449
    ''' Generate the code of a list of assignments '''
450
451
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
452
453
454
455
456


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
457
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
458
459
460
461


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
462
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
463
464
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
465
            generate_for_range(loop)
dbarbera's avatar
dbarbera committed
466
        else:
dbarbera's avatar
dbarbera committed
467
            generate_for_iterable(loop)
dbarbera's avatar
dbarbera committed
468
469


dbarbera's avatar
dbarbera committed
470
def generate_for_range(loop):
dbarbera's avatar
dbarbera committed
471
472
473
474
475
476
477
    ''' 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('')

dbarbera's avatar
dbarbera committed
478
    open_scope()
dbarbera's avatar
dbarbera committed
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509

    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'])
        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'])
    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)

dbarbera's avatar
dbarbera committed
510
    close_scope()
dbarbera's avatar
dbarbera committed
511
512


dbarbera's avatar
dbarbera committed
513
def generate_for_iterable(loop):
dbarbera's avatar
dbarbera committed
514
    ''' Generate the code for a for x in iterable loop'''
515
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
516

dbarbera's avatar
dbarbera committed
517

dbarbera's avatar
dbarbera committed
518
519
520
@singledispatch
def reference(prim):
    ''' Generate a variable reference '''
dbarbera's avatar
flake8    
dbarbera committed
521
    raise TypeError('Unsupported reference: ' + str(prim))
dbarbera's avatar
dbarbera committed
522
523
524
525
526
527
528
529
530
531
532


@reference.register(ogAST.PrimVariable)
def _prim_var_reference(prim):
    ''' Generate a primary variable reference '''
    return g.scope.resolve(str(prim.value[0]))


@reference.register(ogAST.PrimPath)
def _prim_path_reference(prim):
    ''' Generate a primary path reference '''
533
    var_name = prim.value[0].lower()
dbarbera's avatar
dbarbera committed
534
535
536
537
538
539
540
    var_ptr = g.scope.resolve(str(var_name))

    if not prim.value:
        return var_ptr

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

dbarbera's avatar
dbarbera committed
541
542
543
544
545
546
547
548
549
    for elem in prim.value[1:]:
        if type(elem) == dict:
            if 'index' in elem:
                idx_val = expression(elem['index'][0])
                var_ptr = g.builder.gep(var_ptr, [zero_cons, zero_cons, idx_val])
            else:
                raise NotImplementedError
        else:
            var_ty = var_ptr.type
dbarbera's avatar
dbarbera committed
550
551
552
553
554
555
556
557
558
559
560
561
            if var_ty.pointee.name in g.structs:
                struct = g.structs[var_ty.pointee.name]
                field_idx_cons = core.Constant.int(g.i32, struct.idx(elem))
                field_ptr = g.builder.gep(var_ptr, [zero_cons, field_idx_cons])
                var_ptr = field_ptr
            elif var_ty.pointee.name in g.unions:
                union = g.unions[var_ty.pointee.name]
                _, field_ty = union.kind(elem)
                field_ptr = g.builder.gep(var_ptr, [zero_cons, core.Constant.int(g.i32, 1)])
                var_ptr = g.builder.bitcast(field_ptr, core.Type.pointer(field_ty))
            else:
                raise NotImplementedError
dbarbera's avatar
dbarbera committed
562
563
    return var_ptr

Maxime Perrotin's avatar
Maxime Perrotin committed
564
565
566

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
567
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
568
569
570
571
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
572
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
573
574
575
    ''' Generate the code for a variable expression '''
    var_ptr = reference(prim)
    return var_ptr if is_struct_ptr(var_ptr) else g.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
576
577


Maxime Perrotin's avatar
Maxime Perrotin committed
578
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
579
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
580
    ''' Generate the code for an of path expression '''
581
582
583
584
585
586
587
588
589
    specops_generators = {
        'length': generate_length,
        'present': generate_present,
        'abs': generate_abs,
        'fix': generate_fix,
        'float': generate_float,
        'power': generate_power
    }

dbarbera's avatar
dbarbera committed
590
591
592
593
594
595
596
    name = prim.value[0].lower()
    if name in specops_generators:
        generator = specops_generators[name]
        return generator(prim.value[1]['procParams'])

    return generate_access(prim)

597

dbarbera's avatar
dbarbera committed
598
599
def generate_access(prim):
    ''' Generate the code for an access '''
dbarbera's avatar
dbarbera committed
600
601
    var_ptr = reference(prim)
    return var_ptr if is_struct_ptr(var_ptr) else g.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
602
603


604
def generate_length(params):
dbarbera's avatar
dbarbera committed
605
    ''' Generate the code for the built-in length operation'''
dbarbera's avatar
dbarbera committed
606
    seq_ptr = reference(params[0])
dbarbera's avatar
dbarbera committed
607
608
    arr_ty = seq_ptr.type.pointee.elements[0]
    return core.Constant.int(g.i32, arr_ty.count)
609
610
611


def generate_present(params):
dbarbera's avatar
dbarbera committed
612
    ''' Generate the code for the built-in present operation'''
613
614
615
616
    raise NotImplementedError


def generate_abs(params):
dbarbera's avatar
dbarbera committed
617
    ''' Generate the code for the built-in abs operation'''
dbarbera's avatar
dbarbera committed
618
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
619

dbarbera's avatar
dbarbera committed
620
621
622
    if expr_val.type.kind == core.TYPE_INTEGER:
        expr_conv = g.builder.sitofp(expr_val, g.double)
        res_val = g.builder.call(g.funcs['fabs'], [expr_conv])
dbarbera's avatar
dbarbera committed
623
624
        return g.builder.fptosi(res_val, g.i32)
    else:
dbarbera's avatar
dbarbera committed
625
        return g.builder.call(g.funcs['fabs'], [expr_val])
626
627
628


def generate_fix(params):
dbarbera's avatar
dbarbera committed
629
    ''' Generate the code for the built-in fix operation'''
dbarbera's avatar
dbarbera committed
630
631
    expr_val = expression(params[0])
    return g.builder.fptosi(expr_val, g.i32)
632
633
634


def generate_float(params):
dbarbera's avatar
dbarbera committed
635
    ''' Generate the code for the built-in float operation'''
dbarbera's avatar
dbarbera committed
636
637
    expr_val = expression(params[0])
    return g.builder.sitofp(expr_val, g.double)
638
639
640


def generate_power(params):
dbarbera's avatar
dbarbera committed
641
    ''' Generate the code for the built-in power operation'''
642
643
644
645
646
647
648
649
650
651
652
    left_val = expression(params[0])
    right_val = expression(params[1])

    if left_val.type.kind == core.TYPE_INTEGER:
        left_conv = g.builder.sitofp(left_val, g.double)
        res_val = g.builder.call(g.funcs['powi'], [left_conv, right_val])
        return g.builder.fptosi(res_val, g.i32)
    else:
        return g.builder.call(g.funcs['powi'], [left_val, right_val])


Maxime Perrotin's avatar
Maxime Perrotin committed
653
654
655
656
657
658
659
660
661
662
663
664
@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
665
666
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
667
668
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
669

670
671
    if lefttmp.type.kind == core.TYPE_INTEGER:
        if expr.operand == '+':
672
            return g.builder.add(lefttmp, righttmp, 'addtmp')
673
        elif expr.operand == '-':
674
            return g.builder.sub(lefttmp, righttmp, 'subtmp')
675
        elif expr.operand == '*':
676
            return g.builder.mul(lefttmp, righttmp, 'multmp')
677
        elif expr.operand == '/':
678
            return g.builder.sdiv(lefttmp, righttmp, 'divtmp')
679
        elif expr.operand == 'mod':
680
            # l mod r == (((l rem r) + r) rem r)
681
682
683
            remtmp = g.builder.srem(lefttmp, righttmp)
            addtmp = g.builder.add(remtmp, righttmp)
            return g.builder.srem(addtmp, righttmp, 'modtmp')
684
        elif expr.operand == 'rem':
685
            return g.builder.srem(lefttmp, righttmp, 'remtmp')
686
        elif expr.operand == '<':
687
            return g.builder.icmp(core.ICMP_SLT, lefttmp, righttmp, 'lttmp')
688
        elif expr.operand == '<=':
689
            return g.builder.icmp(core.ICMP_SLE, lefttmp, righttmp, 'letmp')
690
        elif expr.operand == '=':
691
            return g.builder.icmp(core.ICMP_EQ, lefttmp, righttmp, 'eqtmp')
692
        elif expr.operand == '/=':
693
            return g.builder.icmp(core.ICMP_NE, lefttmp, righttmp, 'netmp')
694
        elif expr.operand == '>=':
695
            return g.builder.icmp(core.ICMP_SGE, lefttmp, righttmp, 'getmp')
696
        elif expr.operand == '>':
697
            return g.builder.icmp(core.ICMP_SGT, lefttmp, righttmp, 'gttmp')
698
699
700
701
        else:
            raise NotImplementedError
    elif lefttmp.type.kind == core.TYPE_DOUBLE:
        if expr.operand == '+':
702
            return g.builder.fadd(lefttmp, righttmp, 'addtmp')
703
        elif expr.operand == '-':
704
            return g.builder.fsub(lefttmp, righttmp, 'subtmp')
705
        elif expr.operand == '*':
706
            return g.builder.fmul(lefttmp, righttmp, 'multmp')
707
        elif expr.operand == '/':
708
            return g.builder.fdiv(lefttmp, righttmp, 'divtmp')
709
        elif expr.operand == '<':
710
            return g.builder.fcmp(core.FCMP_OLT, lefttmp, righttmp, 'lttmp')
711
        elif expr.operand == '<=':
712
            return g.builder.fcmp(core.FCMP_OLE, lefttmp, righttmp, 'letmp')
713
        elif expr.operand == '=':
714
            return g.builder.fcmp(core.FCMP_OEQ, lefttmp, righttmp, 'eqtmp')
715
        elif expr.operand == '/=':
716
            return g.builder.fcmp(core.FCMP_ONE, lefttmp, righttmp, 'netmp')
717
        elif expr.operand == '>=':
718
            return g.builder.fcmp(core.FCMP_OGE, lefttmp, righttmp, 'getmp')
719
        elif expr.operand == '>':
720
            return g.builder.fcmp(core.FCMP_OGT, lefttmp, righttmp, 'gttmp')
721
722
        else:
            raise NotImplementedError
723
    else:
724
        raise NotImplementedError
725

Maxime Perrotin's avatar
Maxime Perrotin committed
726

727
728
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
729
    ''' Generate the code for an assign expression '''
dbarbera's avatar
dbarbera committed
730
    generate_assign(reference(expr.left), expression(expr.right))
731

732

dbarbera's avatar
dbarbera committed
733
def generate_assign(left, right):
734
735
736
    ''' Generate code for an assign from two LLVM values'''
    # This is extracted as an standalone function because is used by
    # multiple generation rules
dbarbera's avatar
dbarbera committed
737
    if is_struct_ptr(left):
738
739
        size = core.Constant.sizeof(left.type.pointee)
        align = core.Constant.int(g.i32, 0)
740
        volatile = core.Constant.int(g.i1, 0)
741

742
743
        right_ptr = g.builder.bitcast(right, g.i8_ptr)
        left_ptr = g.builder.bitcast(left, g.i8_ptr)
744

dbarbera's avatar
dbarbera committed
745
        g.builder.call(g.funcs['memcpy'], [left_ptr, right_ptr, size, align, volatile])
746
    else:
747
        g.builder.store(right, left)
dbarbera's avatar
dbarbera committed
748

Maxime Perrotin's avatar
Maxime Perrotin committed
749

Maxime Perrotin's avatar
Maxime Perrotin committed
750
751
752
@expression.register(ogAST.ExprOr)
@expression.register(ogAST.ExprAnd)
@expression.register(ogAST.ExprXor)
dbarbera's avatar
dbarbera committed
753
754
def _logical(expr):
    ''' Generate the code for a logical expression '''
dbarbera's avatar
dbarbera committed
755
756
757
758
759
760
761
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)

    ty = find_basic_type(expr.exprType)
    if ty.kind != 'BooleanType':
        raise NotImplementedError

dbarbera's avatar
dbarbera committed
762
763
764
    if expr.operand == 'and':
        return g.builder.and_(lefttmp, righttmp, 'andtmp')
    elif expr.operand == 'or':
765
        return g.builder.or_(lefttmp, righttmp, 'ortmp')
dbarbera's avatar
dbarbera committed
766
    else:
767
        return g.builder.xor(lefttmp, righttmp, 'xortmp')
dbarbera's avatar
dbarbera committed
768

Maxime Perrotin's avatar
Maxime Perrotin committed
769

Maxime Perrotin's avatar
Maxime Perrotin committed
770
@expression.register(ogAST.ExprAppend)
Maxime Perrotin's avatar
Maxime Perrotin committed
771
772
def _append(expr):
    ''' Generate code for the APPEND construct: a // b '''
773
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
774
775


Maxime Perrotin's avatar
Maxime Perrotin committed
776
@expression.register(ogAST.ExprIn)
Maxime Perrotin's avatar
Maxime Perrotin committed
777
def _expr_in(expr):
dbarbera's avatar
dbarbera committed
778
    ''' Generate the code for an in expression '''
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
    func = g.builder.basic_block.function

    next_block = func.append_basic_block('in:next')
    check_block = func.append_basic_block('in:check')
    end_block = func.append_basic_block('in:end')

    seq_asn1_ty = find_basic_type(expr.left.exprType)
    if seq_asn1_ty.Min != seq_asn1_ty.Max:
        # variable size sequence
        raise NotImplementedError

    idx_ptr = g.builder.alloca(g.i32)
    g.builder.store(core.Constant.int(g.i32, 0), idx_ptr)

    # TODO: Should be 'left' in 'right'?
    value_val = expression(expr.right)
    array_ptr = expression(expr.left)

    array_ty = array_ptr.type.pointee.elements[0]
    array_size = core.Constant.int(g.i32, array_ty.count)
    zero_cons = core.Constant.int(g.i32, 0)

    g.builder.branch(check_block)

    g.builder.position_at_end(check_block)
    idx_val = g.builder.load(idx_ptr)
    elem_val = g.builder.load(g.builder.gep(array_ptr, [zero_cons, zero_cons, idx_val]))
    if value_val.type.kind == core.TYPE_INTEGER:
        cond_val = g.builder.icmp(core.ICMP_EQ, value_val, elem_val)
    elif value_val.type.kind == core.TYPE_DOUBLE:
        cond_val = g.builder.fcmp(core.FCMP_OEQ, value_val, elem_val)
    else:
        raise NotImplementedError
    g.builder.cbranch(cond_val, end_block, next_block)

    g.builder.position_at_end(next_block)
    idx_tmp_val = g.builder.add(idx_val, core.Constant.int(g.i32, 1))
    g.builder.store(idx_tmp_val, idx_ptr)
    end_cond_val = g.builder.icmp(core.ICMP_SGE, idx_tmp_val, array_size)
    g.builder.cbranch(end_cond_val, end_block, check_block)

    g.builder.position_at_end(end_block)
    return cond_val
Maxime Perrotin's avatar
Maxime Perrotin committed
822
823


Maxime Perrotin's avatar
Maxime Perrotin committed
824
@expression.register(ogAST.PrimEnumeratedValue)
Maxime Perrotin's avatar
Maxime Perrotin committed
825
826
def _enumerated_value(primary):
    ''' Generate code for an enumerated value '''
dbarbera's avatar
dbarbera committed
827
828
829
    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
830
831


Maxime Perrotin's avatar
Maxime Perrotin committed
832
@expression.register(ogAST.PrimChoiceDeterminant)
Maxime Perrotin's avatar
Maxime Perrotin committed
833
834
def _choice_determinant(primary):
    ''' Generate code for a choice determinant (enumerated) '''
835
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
836
837


Maxime Perrotin's avatar
Maxime Perrotin committed
838
@expression.register(ogAST.PrimInteger)
839
840
def _integer(primary):
    ''' Generate code for a raw integer value  '''
841
    return core.Constant.int(g.i32, primary.value[0])
842
843


Maxime Perrotin's avatar
Maxime Perrotin committed
844
@expression.register(ogAST.PrimReal)
845
846
def _real(primary):
    ''' Generate code for a raw real value  '''
847
    return core.Constant.real(g.double, primary.value[0])
848
849


Maxime Perrotin's avatar
Maxime Perrotin committed
850
@expression.register(ogAST.PrimBoolean)
851
852
def _boolean(primary):
    ''' Generate code for a raw boolean value  '''
dbarbera's avatar
dbarbera committed
853
    if primary.value[0].lower() == 'true':
854
        return core.Constant.int(g.i1, 1)
dbarbera's avatar
dbarbera committed
855
    else:
856
        return core.Constant.int(g.i1, 0)
Maxime Perrotin's avatar
Maxime Perrotin committed
857
858


Maxime Perrotin's avatar
Maxime Perrotin committed
859
@expression.register(ogAST.PrimEmptyString)
Maxime Perrotin's avatar
Maxime Perrotin committed
860
861
def _empty_string(primary):
    ''' Generate code for an empty SEQUENCE OF: {} '''
862
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
863
864


Maxime Perrotin's avatar
Maxime Perrotin committed
865
@expression.register(ogAST.PrimStringLiteral)
Maxime Perrotin's avatar
Maxime Perrotin committed
866
867
def _string_literal(primary):
    ''' Generate code for a string (Octet String) '''
dbarbera's avatar
dbarbera committed
868
    return get_string_cons(str(primary.value[1:-1]))
Maxime Perrotin's avatar
Maxime Perrotin committed
869
870


Maxime Perrotin's avatar
Maxime Perrotin committed
871
@expression.register(ogAST.PrimConstant)
Maxime Perrotin's avatar
Maxime Perrotin committed
872
873
def _constant(primary):
    ''' Generate code for a reference to an ASN.1 constant '''
874
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
875
876


Maxime Perrotin's avatar
Maxime Perrotin committed
877
@expression.register(ogAST.PrimMantissaBaseExp)
Maxime Perrotin's avatar
Maxime Perrotin committed
878
879
def _mantissa_base_exp(primary):
    ''' Generate code for a Real with Mantissa-base-Exponent representation '''
880
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
881
882


Maxime Perrotin's avatar
Maxime Perrotin committed
883
@expression.register(ogAST.PrimIfThenElse)
dbarbera's avatar
dbarbera committed
884
def _if_then_else(ifthen):
dbarbera's avatar
dbarbera committed
885
    ''' Generate the code for ternary operator '''
886
    raise NotImplementedError
Maxime Perrotin's avatar
Maxime Perrotin committed
887
888


Maxime Perrotin's avatar
Maxime Perrotin committed
889
@expression.register(ogAST.PrimSequence)
Maxime Perrotin's avatar
Maxime Perrotin committed
890
def _sequence(seq):
dbarbera's avatar
dbarbera committed
891
    ''' Generate the code for an ASN.1 SEQUENCE '''
dbarbera's avatar
dbarbera committed
892
893
894
895
896
897
898
    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_idx_cons = core.Constant.int(g.i32, struct.idx(field_name))
        field_ptr = g.builder.gep(struct_ptr, [zero_cons, field_idx_cons])