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

dbarbera's avatar
dbarbera committed
33
# Global context
dbarbera's avatar
dbarbera committed
34
ctx = None
35
36


dbarbera's avatar
dbarbera committed
37
class Context():
38
    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

        # 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)

dbarbera's avatar
dbarbera committed
65
66
67
68
        # Initialize common constants
        self.zero = core.Constant.int(self.i32, 0)
        self.one = core.Constant.int(self.i32, 1)

69
70
        # Intialize built-in functions
        ty = core.Type.function(self.void, [core.Type.pointer(self.i8)], True)
dbarbera's avatar
dbarbera committed
71
        self.funcs['printf'] = self.module.add_function(ty, 'printf')
72

dbarbera's avatar
dbarbera committed
73
        self.funcs['memcpy'] = core.Function.intrinsic(
74
75
            self.module,
            core.INTR_MEMCPY,
76
77
            [self.i8_ptr, self.i8_ptr, self.i64]
        )
Maxime Perrotin's avatar
Maxime Perrotin committed
78

79
80
81
82
83
84
        self.funcs['powi'] = core.Function.intrinsic(
            self.module,
            core.INTR_POWI,
            [self.double]
        )

dbarbera's avatar
dbarbera committed
85
86
87
88
89
90
        self.funcs['fabs'] = core.Function.intrinsic(
            self.module,
            core.INTR_FABS,
            [self.double]
        )

91
92
93
94
95
96
    def open_scope(self):
        self.scope = Scope(self.scope)

    def close_scope(self):
        self.scope = self.scope.parent

dbarbera's avatar
dbarbera committed
97

98
99
class StructType():
    def __init__(self, name, field_names, field_types):
dbarbera's avatar
dbarbera committed
100
        self.name = name
101
102
103
104
105
        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
106
107


dbarbera's avatar
dbarbera committed
108
109
110
111
112
113
114
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
dbarbera's avatar
dbarbera committed
115
116
        self.size = max([ctx.target_data.size(ty) for ty in field_types])
        self.ty = core.Type.struct([ctx.i32, core.Type.array(ctx.i8, self.size)], name)
dbarbera's avatar
dbarbera committed
117
118
119
120
121
122

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


123
124
125
class Scope:
    def __init__(self, parent=None):
        self.vars = {}
dbarbera's avatar
dbarbera committed
126
        self.labels = {}
127
128
129
130
131
132
133
134
135
136
137
138
        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
139
            raise NameError("name '%s' is not defined" % name)
140

dbarbera's avatar
dbarbera committed
141
142
143
144
    def label(self, name):
        name = name.lower()
        label_block = self.labels.get(name)
        if not label_block:
dbarbera's avatar
dbarbera committed
145
            func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
146
147
148
149
            label_block = func.append_basic_block(name)
            self.labels[name] = label_block
        return label_block

dbarbera's avatar
dbarbera committed
150

Maxime Perrotin's avatar
Maxime Perrotin committed
151
152
153
154
@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
155

dbarbera's avatar
dbarbera committed
156

Maxime Perrotin's avatar
Maxime Perrotin committed
157
158
159
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process):
dbarbera's avatar
dbarbera committed
160
    ''' Generate LLVM IR code '''
dbarbera's avatar
dbarbera committed
161
162
    process_name = str(process.processName)
    LOG.info('Generating LLVM IR code for process ' + process_name)
163

dbarbera's avatar
dbarbera committed
164
165
    global ctx
    ctx = Context(process)
166

dbarbera's avatar
dbarbera committed
167
168
169
170
171
172
    # 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
173

174
175
    # Initialize states
    for name, val in process.mapping.viewitems():
176
        if not name.endswith('START'):
dbarbera's avatar
dbarbera committed
177
178
            cons_val = core.Constant.int(ctx.i32, len(ctx.states))
            ctx.states[name] = cons_val
179
        elif name != 'START':
dbarbera's avatar
dbarbera committed
180
181
            cons_val = core.Constant.int(ctx.i32, val)
            ctx.states[name] = cons_val
182

183
    # Generate state var
dbarbera's avatar
dbarbera committed
184
185
186
    state_cons = ctx.module.add_global_variable(ctx.i32, 'state')
    state_cons.initializer = core.Constant.int(ctx.i32, -1)
    ctx.scope.define('state', state_cons)
187

188
189
    # Generare process-level vars
    for name, (ty, expr) in process.variables.viewitems():
dbarbera's avatar
dbarbera committed
190
        var_ty = generate_type(ty)
dbarbera's avatar
dbarbera committed
191
        global_var = ctx.module.add_global_variable(var_ty, str(name))
192
        global_var.initializer = core.Constant.null(var_ty)
dbarbera's avatar
dbarbera committed
193
        ctx.scope.define(str(name).lower(), global_var)
194

dbarbera's avatar
dbarbera committed
195
196
197
    # Declare timer set/reset functions
    for timer in process.timers:
        # TODO: Should be uint?
dbarbera's avatar
dbarbera committed
198
199
        decl_func("set_%s" % str(timer), ctx.void, [ctx.i32_ptr], True)
        decl_func("reset_%s" % str(timer), ctx.void, [], True)
dbarbera's avatar
dbarbera committed
200

201
    # Declare output signal functions
dbarbera's avatar
dbarbera committed
202
    for signal in process.output_signals:
203
        if 'type' in signal:
dbarbera's avatar
dbarbera committed
204
            param_tys = [core.Type.pointer(generate_type(signal['type']))]
205
206
        else:
            param_tys = []
dbarbera's avatar
dbarbera committed
207
        decl_func(str(signal['name']), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
208

209
    # Declare external procedures functions
dbarbera's avatar
dbarbera committed
210
    for proc in [proc for proc in process.procedures if proc.external]:
dbarbera's avatar
dbarbera committed
211
        param_tys = [core.Type.pointer(generate_type(p['type'])) for p in proc.fpar]
dbarbera's avatar
dbarbera committed
212
        decl_func(str(proc.inputString), ctx.void, param_tys, True)
dbarbera's avatar
dbarbera committed
213

214
215
    # Generate internal procedures
    for proc in process.content.inner_procedures:
216
        generate(proc)
217

218
    # Generate process functions
dbarbera's avatar
dbarbera committed
219
220
    generate_runtr_func(process)
    generate_startup_func(process)
221

222
223
    # Generate input signals
    for signal in process.input_signals:
dbarbera's avatar
dbarbera committed
224
        generate_input_signal(signal, mapping[signal['name']])
225

dbarbera's avatar
dbarbera committed
226
    ctx.module.verify()
dbarbera's avatar
dbarbera committed
227

dbarbera's avatar
dbarbera committed
228
229
    with open(ctx.name + '.ll', 'w') as ll_file:
        ll_file.write(str(ctx.module))
230
231


dbarbera's avatar
dbarbera committed
232
def generate_runtr_func(process):
dbarbera's avatar
dbarbera committed
233
    ''' Generate code for the run_transition function '''
dbarbera's avatar
dbarbera committed
234
    func = decl_func('run_transition', ctx.void, [ctx.i32])
235

236
    ctx.open_scope()
237

238
239
240
241
242
    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
243
    ctx.builder = core.Builder.new(entry_block)
244
245

    # entry
dbarbera's avatar
dbarbera committed
246
247
248
249
    id_ptr = ctx.builder.alloca(ctx.i32, None, 'id')
    ctx.scope.define('id', id_ptr)
    ctx.builder.store(func.args[0], id_ptr)
    ctx.builder.branch(cond_block)
250
251

    # cond
dbarbera's avatar
dbarbera committed
252
253
254
255
256
    ctx.builder.position_at_end(cond_block)
    no_tr_cons = core.Constant.int(ctx.i32, -1)
    id_val = ctx.builder.load(id_ptr)
    cond_val = ctx.builder.icmp(core.ICMP_NE, id_val, no_tr_cons, 'cond')
    ctx.builder.cbranch(cond_val, body_block, exit_block)
257
258

    # body
dbarbera's avatar
dbarbera committed
259
260
    ctx.builder.position_at_end(body_block)
    switch = ctx.builder.switch(func.args[0], exit_block)
261
262
263
264

    # transitions
    for idx, tr in enumerate(process.transitions):
        tr_block = func.append_basic_block('tr%d' % idx)
dbarbera's avatar
dbarbera committed
265
        const = core.Constant.int(ctx.i32, idx)
266
        switch.add_case(const, tr_block)
dbarbera's avatar
dbarbera committed
267
        ctx.builder.position_at_end(tr_block)
268
        generate(tr)
dbarbera's avatar
dbarbera committed
269
270
        if not ctx.builder.basic_block.terminator:
            ctx.builder.branch(cond_block)
271
272

    # exit
dbarbera's avatar
dbarbera committed
273
274
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
275

dbarbera's avatar
dbarbera committed
276
277
278
279
280
    Helper.inner_labels_to_floating(process)
    for label in process.content.floating_labels:
        generate(label)

    # TODO: Use defined cond_block instead?
dbarbera's avatar
dbarbera committed
281
282
283
    next_tr_label_block = ctx.scope.label('next_transition')
    ctx.builder.position_at_end(next_tr_label_block)
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
284

285
    ctx.close_scope()
286

287
288
289
290
    func.verify()
    return func


dbarbera's avatar
dbarbera committed
291
def generate_startup_func(process):
dbarbera's avatar
dbarbera committed
292
    ''' Generate code for the startup function '''
dbarbera's avatar
dbarbera committed
293
    func = decl_func(ctx.name + '_startup', ctx.void, [])
294

295
    ctx.open_scope()
296

297
    entry_block = func.append_basic_block('entry')
dbarbera's avatar
dbarbera committed
298
    ctx.builder = core.Builder.new(entry_block)
299

300
301
302
    # Initialize process level variables
    for name, (ty, expr) in process.variables.viewitems():
        if expr:
dbarbera's avatar
dbarbera committed
303
            global_var = ctx.scope.resolve(str(name))
dbarbera's avatar
dbarbera committed
304
            generate_assign(global_var, expression(expr))
305

dbarbera's avatar
dbarbera committed
306
307
    ctx.builder.call(ctx.funcs['run_transition'], [core.Constant.int(ctx.i32, 0)])
    ctx.builder.ret_void()
Maxime Perrotin's avatar
Maxime Perrotin committed
308

309
    ctx.close_scope()
310

311
312
    func.verify()
    return func
Maxime Perrotin's avatar
Maxime Perrotin committed
313
314


dbarbera's avatar
dbarbera committed
315
def generate_input_signal(signal, inputs):
dbarbera's avatar
dbarbera committed
316
    ''' Generate code for an input signal '''
dbarbera's avatar
dbarbera committed
317
    func_name = ctx.name + "_" + str(signal['name'])
318
319
    param_tys = []
    if 'type' in signal:
dbarbera's avatar
dbarbera committed
320
        param_tys.append(core.Type.pointer(generate_type(signal['type'])))
321

dbarbera's avatar
dbarbera committed
322
    func = decl_func(func_name, ctx.void, param_tys)
323

324
    ctx.open_scope()
325

326
327
    entry_block = func.append_basic_block('entry')
    exit_block = func.append_basic_block('exit')
dbarbera's avatar
dbarbera committed
328
    ctx.builder = core.Builder.new(entry_block)
329

dbarbera's avatar
dbarbera committed
330
331
    g_state_val = ctx.builder.load(ctx.global_scope.resolve('state'))
    switch = ctx.builder.switch(g_state_val, exit_block)
332

dbarbera's avatar
dbarbera committed
333
    for state_name, state_id in ctx.states.iteritems():
dbarbera's avatar
dbarbera committed
334
335
        if state_name.endswith('START'):
            continue
336
337
        state_block = func.append_basic_block('state_%s' % str(state_name))
        switch.add_case(state_id, state_block)
dbarbera's avatar
dbarbera committed
338
        ctx.builder.position_at_end(state_block)
339
340
341

        # TODO: Nested states

342
343
344
        input = inputs.get(state_name)
        if input:
            for var_name in input.parameters:
dbarbera's avatar
dbarbera committed
345
                var_ptr = ctx.scope.resolve(str(var_name))
346
347
348
                if is_struct_ptr(var_ptr):
                    generate_assign(var_ptr, func.args[0])
                else:
dbarbera's avatar
dbarbera committed
349
                    generate_assign(var_ptr, ctx.builder.load(func.args[0]))
350
            if input.transition:
dbarbera's avatar
dbarbera committed
351
352
                id_val = core.Constant.int(ctx.i32, input.transition_id)
                ctx.builder.call(ctx.funcs['run_transition'], [id_val])
353

dbarbera's avatar
dbarbera committed
354
        ctx.builder.ret_void()
355

dbarbera's avatar
dbarbera committed
356
357
    ctx.builder.position_at_end(exit_block)
    ctx.builder.ret_void()
358

359
    ctx.close_scope()
360

361
362
363
    func.verify()


Maxime Perrotin's avatar
Maxime Perrotin committed
364
365
366
367
@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
368
369
370
    for out in output.output:
        name = out['outputName'].lower()

371
        if name == 'write':
dbarbera's avatar
dbarbera committed
372
            generate_write(out['params'])
dbarbera's avatar
dbarbera committed
373
            continue
374
        elif name == 'writeln':
dbarbera's avatar
dbarbera committed
375
            generate_writeln(out['params'])
376
            continue
dbarbera's avatar
dbarbera committed
377
        elif name == 'reset_timer':
dbarbera's avatar
dbarbera committed
378
            generate_reset_timer(out['params'])
dbarbera's avatar
dbarbera committed
379
380
            continue
        elif name == 'set_timer':
dbarbera's avatar
dbarbera committed
381
            generate_set_timer(out['params'])
dbarbera's avatar
dbarbera committed
382
383
            continue

dbarbera's avatar
dbarbera committed
384
        func = ctx.funcs[str(name).lower()]
385
386
387
388
389
390

        params = []
        for p in out.get('params', []):
            p_val = expression(p)
            # Pass by reference
            if p_val.type.kind != core.TYPE_POINTER:
dbarbera's avatar
dbarbera committed
391
392
                p_var = ctx.builder.alloca(p_val.type, None)
                ctx.builder.store(p_val, p_var)
393
394
395
396
                params.append(p_var)
            else:
                params.append(p_val)

dbarbera's avatar
dbarbera committed
397
        ctx.builder.call(func, params)
dbarbera's avatar
dbarbera committed
398
399


dbarbera's avatar
dbarbera committed
400
def generate_write(params):
dbarbera's avatar
dbarbera committed
401
    ''' Generate the code for the write operator '''
402
403
404
    for param in params:
        basic_ty = find_basic_type(param.exprType)
        expr_val = expression(param)
405

406
        if basic_ty.kind in ['IntegerType', 'Integer32Type']:
dbarbera's avatar
dbarbera committed
407
            fmt_val = get_string_cons('% d')
dbarbera's avatar
dbarbera committed
408
409
            fmt_ptr = ctx.builder.gep(fmt_val, [ctx.zero, ctx.zero])
            ctx.builder.call(ctx.funcs['printf'], [fmt_ptr, expr_val])
410
        elif basic_ty.kind == 'RealType':
dbarbera's avatar
dbarbera committed
411
            fmt_val = get_string_cons('% .14E')
dbarbera's avatar
dbarbera committed
412
413
            fmt_ptr = ctx.builder.gep(fmt_val, [ctx.zero, ctx.zero])
            ctx.builder.call(ctx.funcs['printf'], [fmt_ptr, expr_val])
414
        elif basic_ty.kind == 'BooleanType':
dbarbera's avatar
dbarbera committed
415
            true_str_val = get_string_cons('TRUE')
dbarbera's avatar
dbarbera committed
416
            true_str_ptr = ctx.builder.gep(true_str_val, [ctx.zero, ctx.zero])
dbarbera's avatar
dbarbera committed
417
            false_str_val = get_string_cons('FALSE')
dbarbera's avatar
dbarbera committed
418
419
420
            false_str_ptr = ctx.builder.gep(false_str_val, [ctx.zero, ctx.zero])
            str_ptr = ctx.builder.select(expr_val, true_str_ptr, false_str_ptr)
            ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
421
422
        elif basic_ty.kind in ['StringType', 'OctetStringType']:
            fmt_val = get_string_cons('%s')
dbarbera's avatar
dbarbera committed
423
424
425
            fmt_ptr = ctx.builder.gep(fmt_val, [ctx.zero, ctx.zero])
            arr_ptr = ctx.builder.gep(expr_val, [ctx.zero, ctx.one])
            ctx.builder.call(ctx.funcs['printf'], [fmt_ptr, arr_ptr])
426
427
428
429
        else:
            raise NotImplementedError


dbarbera's avatar
dbarbera committed
430
def generate_writeln(params):
431
    ''' Generate the code for the writeln operator '''
dbarbera's avatar
dbarbera committed
432
    generate_write(params)
433

dbarbera's avatar
dbarbera committed
434
    zero = core.Constant.int(ctx.i32, 0)
dbarbera's avatar
dbarbera committed
435
    str_cons = get_string_cons('\n')
dbarbera's avatar
dbarbera committed
436
437
    str_ptr = ctx.builder.gep(str_cons, [zero, zero])
    ctx.builder.call(ctx.funcs['printf'], [str_ptr])
dbarbera's avatar
dbarbera committed
438
439


dbarbera's avatar
dbarbera committed
440
def generate_reset_timer(params):
dbarbera's avatar
dbarbera committed
441
    ''' Generate the code for the reset timer operator '''
dbarbera's avatar
dbarbera committed
442
    timer_id = params[0]
443
    reset_func_name = 'reset_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
444
    reset_func = ctx.funcs[reset_func_name.lower()]
dbarbera's avatar
dbarbera committed
445

dbarbera's avatar
dbarbera committed
446
    ctx.builder.call(reset_func, [])
dbarbera's avatar
dbarbera committed
447
448


dbarbera's avatar
dbarbera committed
449
def generate_set_timer(params):
dbarbera's avatar
dbarbera committed
450
    ''' Generate the code for the set timer operator '''
dbarbera's avatar
dbarbera committed
451
    timer_expr, timer_id = params
452
    set_func_name = 'set_%s' % timer_id.value[0]
dbarbera's avatar
dbarbera committed
453
    set_func = ctx.funcs[set_func_name.lower()]
dbarbera's avatar
dbarbera committed
454
455
456

    expr_val = expression(timer_expr)

dbarbera's avatar
dbarbera committed
457
458
    tmp_ptr = ctx.builder.alloca(expr_val.type)
    ctx.builder.store(expr_val, tmp_ptr)
dbarbera's avatar
dbarbera committed
459

dbarbera's avatar
dbarbera committed
460
    ctx.builder.call(set_func, [tmp_ptr])
Maxime Perrotin's avatar
Maxime Perrotin committed
461
462
463
464


@generate.register(ogAST.TaskAssign)
def _task_assign(task):
dbarbera's avatar
dbarbera committed
465
    ''' Generate the code of a list of assignments '''
466
467
    for expr in task.elems:
        expression(expr)
Maxime Perrotin's avatar
Maxime Perrotin committed
468
469
470
471
472


@generate.register(ogAST.TaskInformalText)
def _task_informal_text(task):
    ''' Generate comments for informal text '''
dbarbera's avatar
dbarbera committed
473
    pass
Maxime Perrotin's avatar
Maxime Perrotin committed
474
475
476
477


@generate.register(ogAST.TaskForLoop)
def _task_forloop(task):
dbarbera's avatar
dbarbera committed
478
    ''' Generate the code for a for loop '''
dbarbera's avatar
dbarbera committed
479
480
    for loop in task.elems:
        if loop['range']:
dbarbera's avatar
dbarbera committed
481
            generate_for_range(loop)
dbarbera's avatar
dbarbera committed
482
        else:
dbarbera's avatar
dbarbera committed
483
            generate_for_iterable(loop)
dbarbera's avatar
dbarbera committed
484
485


dbarbera's avatar
dbarbera committed
486
def generate_for_range(loop):
dbarbera's avatar
dbarbera committed
487
    ''' Generate the code for a for x in range loop '''
dbarbera's avatar
dbarbera committed
488
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
489
490
491
492
493
    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('')

494
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
495

dbarbera's avatar
dbarbera committed
496
497
    loop_var = ctx.builder.alloca(ctx.i32, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), loop_var)
dbarbera's avatar
dbarbera committed
498
499
500

    if loop['range']['start']:
        start_val = expression(loop['range']['start'])
dbarbera's avatar
dbarbera committed
501
        ctx.builder.store(start_val, loop_var)
dbarbera's avatar
dbarbera committed
502
    else:
dbarbera's avatar
dbarbera committed
503
        ctx.builder.store(core.Constant.int(ctx.i32, 0), loop_var)
dbarbera's avatar
dbarbera committed
504
505

    stop_val = expression(loop['range']['stop'])
dbarbera's avatar
dbarbera committed
506
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
507

dbarbera's avatar
dbarbera committed
508
509
510
511
    ctx.builder.position_at_end(cond_block)
    loop_val = ctx.builder.load(loop_var)
    cond_val = ctx.builder.icmp(core.ICMP_SLT, loop_val, stop_val)
    ctx.builder.cbranch(cond_val, body_block, end_block)
dbarbera's avatar
dbarbera committed
512

dbarbera's avatar
dbarbera committed
513
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
514
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
515
    ctx.builder.branch(inc_block)
dbarbera's avatar
dbarbera committed
516

dbarbera's avatar
dbarbera committed
517
518
519
520
521
522
    ctx.builder.position_at_end(inc_block)
    step_val = core.Constant.int(ctx.i32, loop['range']['step'])
    loop_val = ctx.builder.load(loop_var)
    temp_val = ctx.builder.add(loop_val, step_val)
    ctx.builder.store(temp_val, loop_var)
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
523

dbarbera's avatar
dbarbera committed
524
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
525

526
    ctx.close_scope()
dbarbera's avatar
dbarbera committed
527
528


dbarbera's avatar
dbarbera committed
529
def generate_for_iterable(loop):
dbarbera's avatar
dbarbera committed
530
    ''' Generate the code for a for x in iterable loop'''
dbarbera's avatar
dbarbera committed
531
    seqof_asn1ty = find_basic_type(loop['list'].exprType)
532
    is_variable_size = seqof_asn1ty.Min != seqof_asn1ty.Max
dbarbera's avatar
dbarbera committed
533

dbarbera's avatar
dbarbera committed
534
    func = ctx.builder.basic_block.function
dbarbera's avatar
dbarbera committed
535
536
537
538
539
540
541
542
543
544

    # block for loading the value from the secuence
    # at the current index, incrementing the index afterwards
    load_block = func.append_basic_block('forin:load')
    # block for the body of the loop
    body_block = func.append_basic_block('forin:body')
    # block for checking if should loop again or terminate
    cond_block = func.append_basic_block('forin:cond')
    end_block = func.append_basic_block('')

545
    ctx.open_scope()
dbarbera's avatar
dbarbera committed
546

dbarbera's avatar
dbarbera committed
547
548
    idx_ptr = ctx.builder.alloca(ctx.i32)
    ctx.builder.store(core.Constant.int(ctx.i32, 0), idx_ptr)
dbarbera's avatar
dbarbera committed
549
    seqof_struct_ptr = expression(loop['list'])
550
551
552

    if is_variable_size:
        # In variable size SequenceOfs the array values are in the second field
dbarbera's avatar
dbarbera committed
553
        array_ptr = ctx.builder.gep(seqof_struct_ptr, [ctx.zero, ctx.one])
554
    else:
dbarbera's avatar
dbarbera committed
555
        array_ptr = ctx.builder.gep(seqof_struct_ptr, [ctx.zero, ctx.zero])
556

dbarbera's avatar
dbarbera committed
557
    element_typ = array_ptr.type.pointee.element
558
559
560

    if is_variable_size:
        # load the current number of elements that is on the first field
dbarbera's avatar
dbarbera committed
561
        end_idx = ctx.builder.load(ctx.builder.gep(seqof_struct_ptr, [ctx.zero, ctx.zero]))
562
    else:
dbarbera's avatar
dbarbera committed
563
        end_idx = core.Constant.int(ctx.i32, array_ptr.type.pointee.count)
dbarbera's avatar
dbarbera committed
564

dbarbera's avatar
dbarbera committed
565
566
    var_ptr = ctx.builder.alloca(element_typ, None, str(loop['var']))
    ctx.scope.define(str(loop['var']), var_ptr)
dbarbera's avatar
dbarbera committed
567

dbarbera's avatar
dbarbera committed
568
    ctx.builder.branch(load_block)
dbarbera's avatar
dbarbera committed
569
570

    # load block
dbarbera's avatar
dbarbera committed
571
572
    ctx.builder.position_at_end(load_block)
    idx_var = ctx.builder.load(idx_ptr)
573
    if element_typ.kind == core.TYPE_STRUCT:
dbarbera's avatar
dbarbera committed
574
        generate_assign(var_ptr, ctx.builder.gep(array_ptr, [ctx.zero, idx_var]))
575
    else:
dbarbera's avatar
dbarbera committed
576
577
        generate_assign(var_ptr, ctx.builder.load(ctx.builder.gep(array_ptr, [ctx.zero, idx_var])))
    ctx.builder.branch(body_block)
dbarbera's avatar
dbarbera committed
578
579

    # body block
dbarbera's avatar
dbarbera committed
580
    ctx.builder.position_at_end(body_block)
dbarbera's avatar
dbarbera committed
581
    generate(loop['transition'])
dbarbera's avatar
dbarbera committed
582
    ctx.builder.branch(cond_block)
dbarbera's avatar
dbarbera committed
583
584

    # cond block
dbarbera's avatar
dbarbera committed
585
586
587
588
589
    ctx.builder.position_at_end(cond_block)
    tmp_val = ctx.builder.add(idx_var, ctx.one)
    ctx.builder.store(tmp_val, idx_ptr)
    cond_val = ctx.builder.icmp(core.ICMP_SLT, tmp_val, end_idx)
    ctx.builder.cbranch(cond_val, load_block, end_block)
dbarbera's avatar
dbarbera committed
590

dbarbera's avatar
dbarbera committed
591
    ctx.builder.position_at_end(end_block)
dbarbera's avatar
dbarbera committed
592

593
    ctx.close_scope()
Maxime Perrotin's avatar
Maxime Perrotin committed
594

dbarbera's avatar
dbarbera committed
595

dbarbera's avatar
dbarbera committed
596
597
598
@singledispatch
def reference(prim):
    ''' Generate a variable reference '''
dbarbera's avatar
flake8    
dbarbera committed
599
    raise TypeError('Unsupported reference: ' + str(prim))
dbarbera's avatar
dbarbera committed
600
601
602
603
604


@reference.register(ogAST.PrimVariable)
def _prim_var_reference(prim):
    ''' Generate a primary variable reference '''
dbarbera's avatar
dbarbera committed
605
    return ctx.scope.resolve(str(prim.value[0]))
dbarbera's avatar
dbarbera committed
606
607
608
609
610


@reference.register(ogAST.PrimPath)
def _prim_path_reference(prim):
    ''' Generate a primary path reference '''
611
    var_name = prim.value[0].lower()
dbarbera's avatar
dbarbera committed
612
    var_ptr = ctx.scope.resolve(str(var_name))
dbarbera's avatar
dbarbera committed
613
614
615
616

    if not prim.value:
        return var_ptr

dbarbera's avatar
dbarbera committed
617
618
619
620
    for elem in prim.value[1:]:
        if type(elem) == dict:
            if 'index' in elem:
                idx_val = expression(elem['index'][0])
dbarbera's avatar
dbarbera committed
621
                array_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.zero])
622
                # TODO: Refactor this
dbarbera's avatar
dbarbera committed
623
624
                if array_ptr.type.pointee.kind != core.TYPE_ARRAY:
                    # If is not an array this is a pointer to a variable size SeqOf
625
                    # The array is in the second field of the struct
dbarbera's avatar
dbarbera committed
626
                    var_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.one, idx_val])
627
                else:
dbarbera's avatar
dbarbera committed
628
                    var_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.zero, idx_val])
dbarbera's avatar
dbarbera committed
629
630
631
632
            else:
                raise NotImplementedError
        else:
            var_ty = var_ptr.type
dbarbera's avatar
dbarbera committed
633
634
635
636
            if var_ty.pointee.name in ctx.structs:
                struct = ctx.structs[var_ty.pointee.name]
                field_idx_cons = core.Constant.int(ctx.i32, struct.idx(elem))
                field_ptr = ctx.builder.gep(var_ptr, [ctx.zero, field_idx_cons])
dbarbera's avatar
dbarbera committed
637
                var_ptr = field_ptr
dbarbera's avatar
dbarbera committed
638
639
            elif var_ty.pointee.name in ctx.unions:
                union = ctx.unions[var_ty.pointee.name]
dbarbera's avatar
dbarbera committed
640
                _, field_ty = union.kind(elem)
dbarbera's avatar
dbarbera committed
641
642
                field_ptr = ctx.builder.gep(var_ptr, [ctx.zero, ctx.one])
                var_ptr = ctx.builder.bitcast(field_ptr, core.Type.pointer(field_ty))
dbarbera's avatar
dbarbera committed
643
644
            else:
                raise NotImplementedError
dbarbera's avatar
dbarbera committed
645
646
    return var_ptr

Maxime Perrotin's avatar
Maxime Perrotin committed
647
648
649

@singledispatch
def expression(expr):
dbarbera's avatar
dbarbera committed
650
    ''' Generate the code for Expression-classes '''
Maxime Perrotin's avatar
Maxime Perrotin committed
651
652
653
654
    raise TypeError('Unsupported expression: ' + str(expr))


@expression.register(ogAST.PrimVariable)
Maxime Perrotin's avatar
Maxime Perrotin committed
655
def _primary_variable(prim):
dbarbera's avatar
dbarbera committed
656
657
    ''' Generate the code for a variable expression '''
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
658
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
659
660


Maxime Perrotin's avatar
Maxime Perrotin committed
661
@expression.register(ogAST.PrimPath)
dbarbera's avatar
dbarbera committed
662
def _prim_path(prim):
dbarbera's avatar
dbarbera committed
663
    ''' Generate the code for an of path expression '''
664
665
666
667
668
669
670
671
672
    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
673
674
675
676
677
678
679
    name = prim.value[0].lower()
    if name in specops_generators:
        generator = specops_generators[name]
        return generator(prim.value[1]['procParams'])

    return generate_access(prim)

680

dbarbera's avatar
dbarbera committed
681
682
def generate_access(prim):
    ''' Generate the code for an access '''
dbarbera's avatar
dbarbera committed
683
    var_ptr = reference(prim)
dbarbera's avatar
dbarbera committed
684
    return var_ptr if is_struct_ptr(var_ptr) else ctx.builder.load(var_ptr)
Maxime Perrotin's avatar
Maxime Perrotin committed
685
686


687
def generate_length(params):
dbarbera's avatar
dbarbera committed
688
    ''' Generate the code for the built-in length operation'''
dbarbera's avatar
dbarbera committed
689
    seq_ptr = reference(params[0])
dbarbera's avatar
dbarbera committed
690
    arr_ty = seq_ptr.type.pointee.elements[0]
dbarbera's avatar
dbarbera committed
691
    return core.Constant.int(ctx.i32, arr_ty.count)
692
693
694


def generate_present(params):
dbarbera's avatar
dbarbera committed
695
    ''' Generate the code for the built-in present operation'''
696
697
698
699
    raise NotImplementedError


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

dbarbera's avatar
dbarbera committed
703
    if expr_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
704
705
706
        expr_conv = ctx.builder.sitofp(expr_val, ctx.double)
        res_val = ctx.builder.call(ctx.funcs['fabs'], [expr_conv])
        return ctx.builder.fptosi(res_val, ctx.i32)
dbarbera's avatar
dbarbera committed
707
    else:
dbarbera's avatar
dbarbera committed
708
        return ctx.builder.call(ctx.funcs['fabs'], [expr_val])
709
710
711


def generate_fix(params):
dbarbera's avatar
dbarbera committed
712
    ''' Generate the code for the built-in fix operation'''
dbarbera's avatar
dbarbera committed
713
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
714
    return ctx.builder.fptosi(expr_val, ctx.i32)
715
716
717


def generate_float(params):
dbarbera's avatar
dbarbera committed
718
    ''' Generate the code for the built-in float operation'''
dbarbera's avatar
dbarbera committed
719
    expr_val = expression(params[0])
dbarbera's avatar
dbarbera committed
720
    return ctx.builder.sitofp(expr_val, ctx.double)
721
722
723


def generate_power(params):
dbarbera's avatar
dbarbera committed
724
    ''' Generate the code for the built-in power operation'''
725
726
727
728
    left_val = expression(params[0])
    right_val = expression(params[1])

    if left_val.type.kind == core.TYPE_INTEGER:
dbarbera's avatar
dbarbera committed
729
730
731
        left_conv = ctx.builder.sitofp(left_val, ctx.double)
        res_val = ctx.builder.call(ctx.funcs['powi'], [left_conv, right_val])
        return ctx.builder.fptosi(res_val, ctx.i32)
732
    else:
dbarbera's avatar
dbarbera committed
733
        return ctx.builder.call(ctx.funcs['powi'], [left_val, right_val])
734
735


Maxime Perrotin's avatar
Maxime Perrotin committed
736
737
738
739
740
741
742
743
744
745
746
747
@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
748
749
def _basic(expr):
    ''' Generate the code for an arithmetic of relational expression '''
750
751
    lefttmp = expression(expr.left)
    righttmp = expression(expr.right)
752

753
754
    if lefttmp.type.kind == core.TYPE_INTEGER:
        if expr.operand == '+':
dbarbera's avatar
dbarbera committed
755
            return ctx.builder.add(lefttmp, righttmp, 'addtmp')
756
        elif expr.operand == '-':
dbarbera's avatar
dbarbera committed
757
            return ctx.builder.sub(lefttmp, righttmp, 'subtmp')
758
        elif expr.operand == '*':
dbarbera's avatar
dbarbera committed
759
            return ctx.builder.mul(lefttmp, righttmp, 'multmp')
760
        elif expr.operand == '/':
dbarbera's avatar
dbarbera committed
761
            return ctx.builder.sdiv(lefttmp, righttmp, 'divtmp')
762
        elif expr.operand == 'mod':
763
            # l mod r == (((l rem r) + r) rem r)
dbarbera's avatar
dbarbera committed
764
765
766
            remtmp = ctx.builder.srem(lefttmp, righttmp)
            addtmp = ctx.builder.add(remtmp, righttmp)
            return ctx.builder.srem(addtmp, righttmp, 'modtmp')
767
        elif expr.operand == 'rem':
dbarbera's avatar
dbarbera committed
768
            return ctx.builder.srem(lefttmp, righttmp, 'remtmp')
769
        elif expr.operand == '<':
dbarbera's avatar
dbarbera committed
770
            return ctx.builder.icmp(core.ICMP_SLT, lefttmp, righttmp, 'lttmp')
771
        elif expr.operand == '<=':
dbarbera's avatar
dbarbera committed
772
            return ctx.builder.icmp(core.ICMP_SLE, lefttmp, righttmp, 'letmp')
773
        elif expr.operand == '=':
dbarbera's avatar
dbarbera committed
774
            return ctx.builder.icmp(core.ICMP_EQ, lefttmp, righttmp, 'eqtmp')
775
        elif expr.operand == '/=':
dbarbera's avatar
dbarbera committed
776
            return ctx.builder.icmp(core.ICMP_NE, lefttmp, righttmp, 'netmp')
777
        elif expr.operand == '>=':
dbarbera's avatar
dbarbera committed
778
            return ctx.builder.icmp(core.ICMP_SGE, lefttmp, righttmp, 'getmp')
779
        elif expr.operand == '>':
dbarbera's avatar
dbarbera committed
780
            return ctx.builder.icmp(core.ICMP_SGT, lefttmp, righttmp, 'gttmp')
781
782
783
784
        else:
            raise NotImplementedError
    elif lefttmp.type.kind == core.TYPE_DOUBLE:
        if expr.operand == '+':
dbarbera's avatar
dbarbera committed
785
            return ctx.builder.fadd(lefttmp, righttmp, 'addtmp')
786
        elif expr.operand == '-':
dbarbera's avatar
dbarbera committed
787
            return ctx.builder.fsub(lefttmp, righttmp, 'subtmp')
788
        elif expr.operand == '*':
dbarbera's avatar
dbarbera committed
789
            return ctx.builder.fmul(lefttmp, righttmp, 'multmp')
790
        elif expr.operand == '/':
dbarbera's avatar
dbarbera committed
791
            return ctx.builder.fdiv(lefttmp, righttmp, 'divtmp')
792
        elif expr.operand == '<':
dbarbera's avatar
dbarbera committed
793
            return ctx.builder.fcmp(core.FCMP_OLT, lefttmp, righttmp, 'lttmp')
794
        elif expr.operand == '<=':
dbarbera's avatar
dbarbera committed
795
            return ctx.builder.fcmp(core.FCMP_OLE, lefttmp, righttmp, 'letmp')
796
        elif expr.operand == '=':
dbarbera's avatar
dbarbera committed
797
            return ctx.builder.fcmp(core.FCMP_OEQ, lefttmp, righttmp, 'eqtmp')
798
        elif expr.operand == '/=':
dbarbera's avatar
dbarbera committed
799
            return ctx.builder.fcmp(core.FCMP_ONE, lefttmp, righttmp, 'netmp')
800
        elif expr.operand == '>=':
dbarbera's avatar
dbarbera committed
801
            return ctx.builder.fcmp(core.FCMP_OGE, lefttmp, righttmp, 'getmp')
802
        elif expr.operand == '>':
dbarbera's avatar
dbarbera committed
803
            return ctx.builder.fcmp(core.FCMP_OGT, lefttmp, righttmp, 'gttmp')
804
805
        else:
            raise NotImplementedError
806
    else:
807
        raise NotImplementedError
808

Maxime Perrotin's avatar
Maxime Perrotin committed
809

810
811
@expression.register(ogAST.ExprAssign)
def _assign(expr):
dbarbera's avatar
dbarbera committed
812
    ''' Generate the code for an assign expression '''
dbarbera's avatar
dbarbera committed
813
    generate_assign(reference(expr.left), expression(expr.right))
814