diff --git a/examples/df.py b/examples/df.py index 08483bba0..35a858d6a 100644 --- a/examples/df.py +++ b/examples/df.py @@ -1,6 +1,6 @@ import sys import json -from collections import namedtuple +from collections import namedtuple, OrderedDict from form_blocks import form_blocks import cfg @@ -20,6 +20,28 @@ def union(sets): return out +def with_func_args(func, blocks): + """Inserts function arguments in a pre-cursor block, so that + they're not missed in the analysis. For example, + @main(x: int) { ... } + will insert the following pre-cursor block: + + .__main_arguments: + x: int = id x; + """ + if 'args' not in func: + return blocks + + blocks = list(blocks.items()) + # Id each argument of the function, to ensure it is not skipped in the worklist. + pre_cursor = [{'op': 'id', 'dest': a['name'], 'type': a['type'], 'args': [a['name']]} for a in func['args']] + # Jump to original entry block. + pre_cursor.append({'op': 'jmp', 'labels': [blocks[0][0]]}) + + blocks.insert(0, ('__{}_arguments'.format(func['name']), pre_cursor)) + return OrderedDict([(k, v) for k, v in blocks]) + + def df_worklist(blocks, analysis): """The worklist algorithm for iterating a data flow analysis to a fixed point. @@ -85,7 +107,8 @@ def run_df(bril, analysis): blocks = cfg.block_map(form_blocks(func['instrs'])) cfg.add_terminators(blocks) - in_, out = df_worklist(blocks, analysis) + # Insert a pre-cursor block for function arguments. + in_, out = df_worklist(with_func_args(func, blocks), analysis) for block in blocks: print('{}:'.format(block)) print(' in: ', fmt(in_[block])) diff --git a/examples/lvn.py b/examples/lvn.py index cf7f834bc..4b57a553c 100644 --- a/examples/lvn.py +++ b/examples/lvn.py @@ -67,10 +67,8 @@ def read_first(instrs): def lvn_block(block, lookup, canonicalize, fold): """Use local value numbering to optimize a basic block. Modify the instructions in place. - You can extend the basic LVN algorithm to bring interesting language semantics with these functions: - - `lookup`. Arguments: a value-to-number map and a value. Return the corresponding number (or None if it does not exist). - `canonicalize`. Argument: a value. Returns an equivalent value in @@ -158,7 +156,7 @@ def lvn_block(block, lookup, canonicalize, fold): num2var[newnum] = var instr['dest'] = var - if val: + if val is not None: # Is this value foldable to a constant? const = fold(num2const, val) if const is not None: @@ -199,6 +197,9 @@ def _lookup(value2num, value): 'le': lambda a, b: a <= b, 'ne': lambda a, b: a != b, 'eq': lambda a, b: a == b, + 'or': lambda a, b: a or b, + 'and': lambda a, b: a and b, + 'not': lambda a: not a } @@ -212,6 +213,14 @@ def _fold(num2const, value): # Equivalent arguments may be evaluated for equality. # E.g. `eq x x`, where `x` is not a constant evaluates to `true`. return value.op != 'ne' + + if value.op in {'and', 'or'} and any(v in num2const for v in value.args): + # Short circuiting the logical operators `and` and `or` for two cases: + # (1) `and x c0` -> false, where `c0` a constant that evaluates to `false`. + # (2) `or x c1` -> true, where `c1` a constant that evaluates to `true`. + const_val = num2const[value.args[0] if value.args[0] in num2const else value.args[1]] + if (value.op == 'and' and not const_val) or (value.op == 'or' and const_val): + return const_val return None except ZeroDivisionError: # If we hit a dynamic error, bail! return None diff --git a/examples/test/df/cond-args-defined.bril b/examples/test/df/cond-args-defined.bril new file mode 100644 index 000000000..439e75300 --- /dev/null +++ b/examples/test/df/cond-args-defined.bril @@ -0,0 +1,6 @@ +# ARGS: defined + +@main(awesome_integer: int) { +.entry: + print awesome_integer; +} diff --git a/examples/test/df/cond-args-defined.out b/examples/test/df/cond-args-defined.out new file mode 100644 index 000000000..588df27f2 --- /dev/null +++ b/examples/test/df/cond-args-defined.out @@ -0,0 +1,3 @@ +entry: + in: awesome_integer + out: awesome_integer diff --git a/examples/test/df/cond-args.bril b/examples/test/df/cond-args-live.bril similarity index 100% rename from examples/test/df/cond-args.bril rename to examples/test/df/cond-args-live.bril diff --git a/examples/test/df/cond-args.out b/examples/test/df/cond-args-live.out similarity index 100% rename from examples/test/df/cond-args.out rename to examples/test/df/cond-args-live.out diff --git a/examples/test/df/fact-defined.bril b/examples/test/df/fact-defined.bril new file mode 100644 index 000000000..b86be5137 --- /dev/null +++ b/examples/test/df/fact-defined.bril @@ -0,0 +1,24 @@ +# ARGS: defined + +@main { + result: int = const 1; + i: int = const 8; + +.header: + # Enter body if i >= 0. + zero: int = const 0; + cond: bool = gt i zero; + br cond .body .end; + +.body: + result: int = mul result i; + + # i-- + one: int = const 1; + i: int = sub i one; + + jmp .header; + +.end: + print result; +} diff --git a/examples/test/df/fact-defined.out b/examples/test/df/fact-defined.out new file mode 100644 index 000000000..2c8f1514c --- /dev/null +++ b/examples/test/df/fact-defined.out @@ -0,0 +1,12 @@ +b1: + in: ∅ + out: i, result +header: + in: cond, i, one, result, zero + out: cond, i, one, result, zero +body: + in: cond, i, one, result, zero + out: cond, i, one, result, zero +end: + in: cond, i, one, result, zero + out: cond, i, one, result, zero diff --git a/examples/test/df/fact.bril b/examples/test/df/fact-live.bril similarity index 100% rename from examples/test/df/fact.bril rename to examples/test/df/fact-live.bril diff --git a/examples/test/df/fact.out b/examples/test/df/fact-live.out similarity index 100% rename from examples/test/df/fact.out rename to examples/test/df/fact-live.out