We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
BUG JIT optimizer removes index-variables after the loop
It seemes that the optimizer removes all variables that look like index variables after the loop is completed (or before the next loop is started).

For example, the following code:

```
from numba import jit
import numpy as np

@jit('f8[:](f8[:])')
def test(x):
    res = np.zeros(len(x))
    ind = 0
    for ii in range(len(x)):
        ind += 1
        res[ind] = x[ind]
        if x[ind] >= 10:
            break

    # ind    #### Uncomment this line to get correct result
    for ii in range(ind+1, len(x)):
        res[ii] = 0
    return res

x = np.array([1.,4,2,-3,5,2,10,5,2,6])
print test(x)
```

returns `[ 0.  0.  0.  0.  0.  0.  0.  0.  0.  0.]` as if before the second loop the variable had value: `ind=0`

But when the line `# ind` is not commented (i.e. when variable `ind` is read before the second loop) the result is correct: `[  0.   4.   2.  -3.   5.   2.  10.   0.   0.   0.]`

In first case the loop is jitted and in second is rejected. So it seemed that the access to the variable in `range(ind+1, len(x))` is not considered by the compiler as a reason to reject jitting.

Just in case:
Python 2.7.6
LLVM: 3.3
llvmpy (0.12.7)
numba (0.14.0)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `numba/looplifting.py`
Content:
```
1 from __future__ import print_function, division, absolute_import
2 
3 from numba import utils
4 from numba.bytecode import ByteCodeInst, CustomByteCode
5 
6 
7 def lift_loop(bytecode, dispatcher_factory):
8     """Lift the top-level loops.
9 
10     Returns (outer, loops)
11     ------------------------
12     * outer: ByteCode of a copy of the loop-less function.
13     * loops: a list of ByteCode of the loops.
14     """
15     outer = []
16     loops = []
17     separate_loops(bytecode, outer, loops)
18 
19     # Discover variables references
20     outer_rds, outer_wrs = find_varnames_uses(bytecode, outer)
21     outer_wrs |= set(bytecode.argspec.args)
22 
23     dispatchers = []
24     outerlabels = set(bytecode.labels)
25     outernames = list(bytecode.co_names)
26 
27     for loop in loops:
28         args, rets = discover_args_and_returns(bytecode, loop, outer_rds,
29                                                outer_wrs)
30 
31         disp = insert_loop_call(bytecode, loop, args,
32                                 outer, outerlabels, rets,
33                                 dispatcher_factory)
34         dispatchers.append(disp)
35 
36     # Build outer bytecode
37     codetable = utils.SortedMap((i.offset, i) for i in outer)
38     outerbc = CustomByteCode(func=bytecode.func,
39                              func_qualname=bytecode.func_qualname,
40                              argspec=bytecode.argspec,
41                              filename=bytecode.filename,
42                              co_names=outernames,
43                              co_varnames=bytecode.co_varnames,
44                              co_consts=bytecode.co_consts,
45                              co_freevars=bytecode.co_freevars,
46                              table=codetable,
47                              labels=outerlabels & set(codetable.keys()))
48 
49     return outerbc, dispatchers
50 
51 @utils.total_ordering
52 class SubOffset(object):
53     """The loop-jitting may insert bytecode between two bytecode but we
54     cannot guarantee that there is enough integral space between two offsets.
55     This class workaround the problem by introducing a fractional part to the
56     offset.
57     """
58     def __init__(self, val, sub=1):
59         assert sub > 0, "fractional part cannot be <= 0"
60         self.val = val
61         self.sub = sub
62 
63     def next(self):
64         """Helper method to get the next suboffset by incrementing the
65         fractional part only
66         """
67         return SubOffset(self.val, self.sub + 1)
68 
69     def __add__(self, other):
70         """Adding to a suboffset will only increment the fractional part.
71         The integral part is immutable.
72         """
73         return SubOffset(self.val, self.sub + other)
74 
75     def __hash__(self):
76         return hash((self.val, self.sub))
77 
78     def __lt__(self, other):
79         """Can only compare to SubOffset or int
80         """
81         if isinstance(other, SubOffset):
82             if self.val < other.val:
83                 return self
84             elif self.val == other.val:
85                 return self.sub < other.sub
86             else:
87                 return False
88         elif isinstance(other, int):
89             return self.val < other
90         else:
91             return NotImplemented
92 
93     def __eq__(self, other):
94         if isinstance(other, SubOffset):
95             return self.val == other.val and self.sub == other.sub
96         elif isinstance(other, int):
97             # Can never be equal to a integer by definition
98             return False
99         else:
100             return NotImplemented
101 
102     def __repr__(self):
103         """Print like a floating-point by it is not one at all.
104         """
105         return "{0}.{1}".format(self.val, self.sub)
106 
107 
108 def insert_loop_call(bytecode, loop, args, outer, outerlabels, returns,
109                      dispatcher_factory):
110     endloopoffset = loop[-1].next
111     # Accepted. Create a bytecode object for the loop
112     args = tuple(args)
113 
114     lbc = make_loop_bytecode(bytecode, loop, args, returns)
115 
116     # Generate dispatcher for this inner loop, and append it to the
117     # consts tuple.
118     disp = dispatcher_factory(lbc)
119     disp_idx = len(bytecode.co_consts)
120     bytecode.co_consts += (disp,)
121 
122     # Insert jump to the end
123     insertpt = SubOffset(loop[0].next)
124     jmp = ByteCodeInst.get(loop[0].offset, 'JUMP_ABSOLUTE', insertpt)
125     jmp.lineno = loop[0].lineno
126     insert_instruction(outer, jmp)
127 
128     outerlabels.add(outer[-1].next)
129 
130     # Prepare arguments
131     loadfn = ByteCodeInst.get(insertpt, "LOAD_CONST", disp_idx)
132     loadfn.lineno = loop[0].lineno
133     insert_instruction(outer, loadfn)
134 
135     insertpt = insertpt.next()
136     for arg in args:
137         loadarg = ByteCodeInst.get(insertpt, 'LOAD_FAST',
138                                    bytecode.co_varnames.index(arg))
139         loadarg.lineno = loop[0].lineno
140         insert_instruction(outer, loadarg)
141         insertpt = insertpt.next()
142 
143     # Call function
144     assert len(args) < 256
145     call = ByteCodeInst.get(insertpt, "CALL_FUNCTION", len(args))
146     call.lineno = loop[0].lineno
147     insert_instruction(outer, call)
148 
149     insertpt = insertpt.next()
150 
151     if returns:
152         # Unpack arguments
153         unpackseq = ByteCodeInst.get(insertpt, "UNPACK_SEQUENCE",
154                                   len(returns))
155         unpackseq.lineno = loop[0].lineno
156         insert_instruction(outer, unpackseq)
157         insertpt = insertpt.next()
158 
159         for out in returns:
160             # Store each variable
161             storefast = ByteCodeInst.get(insertpt, "STORE_FAST",
162                                       bytecode.co_varnames.index(out))
163             storefast.lineno = loop[0].lineno
164             insert_instruction(outer, storefast)
165             insertpt = insertpt.next()
166     else:
167         # No return value
168         poptop = ByteCodeInst.get(outer[-1].next, "POP_TOP", None)
169         poptop.lineno = loop[0].lineno
170         insert_instruction(outer, poptop)
171         insertpt = insertpt.next()
172 
173     jmpback = ByteCodeInst.get(insertpt, 'JUMP_ABSOLUTE',
174                                endloopoffset)
175 
176     jmpback.lineno = loop[0].lineno
177     insert_instruction(outer, jmpback)
178 
179     return disp
180 
181 
182 def insert_instruction(insts, item):
183     i = find_previous_inst(insts, item.offset)
184     insts.insert(i, item)
185 
186 
187 def find_previous_inst(insts, offset):
188     for i, inst in enumerate(insts):
189         if inst.offset > offset:
190             return i
191     return len(insts)
192 
193 
194 def make_loop_bytecode(bytecode, loop, args, returns):
195     # Add return None
196     co_consts = tuple(bytecode.co_consts)
197     if None not in co_consts:
198         co_consts += (None,)
199 
200     if returns:
201         for out in returns:
202             # Load output
203             loadfast = ByteCodeInst.get(loop[-1].next, "LOAD_FAST",
204                                          bytecode.co_varnames.index(out))
205             loadfast.lineno = loop[-1].lineno
206             loop.append(loadfast)
207             # Build tuple
208             buildtuple = ByteCodeInst.get(loop[-1].next, "BUILD_TUPLE",
209                                         len(returns))
210             buildtuple.lineno = loop[-1].lineno
211             loop.append(buildtuple)
212 
213     else:
214         # Load None
215         load_none = ByteCodeInst.get(loop[-1].next, "LOAD_CONST",
216                                      co_consts.index(None))
217         load_none.lineno = loop[-1].lineno
218         loop.append(load_none)
219 
220     # Return TOS
221     return_value = ByteCodeInst.get(loop[-1].next, "RETURN_VALUE", 0)
222     return_value.lineno = loop[-1].lineno
223     loop.append(return_value)
224 
225     # Function name
226     loop_qualname = bytecode.func_qualname + ".__numba__loop%d__" % loop[0].offset
227 
228     # Argspec
229     argspectype = type(bytecode.argspec)
230     argspec = argspectype(args=args, varargs=(), keywords=(), defaults=())
231 
232     # Code table
233     codetable = utils.SortedMap((i.offset, i) for i in loop)
234 
235     # Custom bytecode object
236     lbc = CustomByteCode(func=bytecode.func,
237                          func_qualname=loop_qualname,
238                          argspec=argspec,
239                          filename=bytecode.filename,
240                          co_names=bytecode.co_names,
241                          co_varnames=bytecode.co_varnames,
242                          co_consts=co_consts,
243                          co_freevars=bytecode.co_freevars,
244                          table=codetable,
245                          labels=bytecode.labels)
246 
247     return lbc
248 
249 
250 def stitch_instructions(outer, loop):
251     begin = loop[0].offset
252     i = find_previous_inst(outer, begin)
253     return outer[:i] + loop + outer[i:]
254 
255 
256 def discover_args_and_returns(bytecode, insts, outer_rds, outer_wrs):
257     """
258     Basic analysis for args and returns
259     This completely ignores the ordering or the read-writes.
260     """
261     rdnames, wrnames = find_varnames_uses(bytecode, insts)
262     # Pass names that are written outside and read locally
263     args = outer_wrs & rdnames
264     # Return values that it written locally and read outside
265     rets = wrnames & outer_rds
266     return args, rets
267 
268 
269 def find_varnames_uses(bytecode, insts):
270     rdnames = set()
271     wrnames = set()
272     for inst in insts:
273         if inst.opname == 'LOAD_FAST':
274             rdnames.add(bytecode.co_varnames[inst.arg])
275         elif inst.opname == 'STORE_FAST':
276             wrnames.add(bytecode.co_varnames[inst.arg])
277     return rdnames, wrnames
278 
279 
280 def separate_loops(bytecode, outer, loops):
281     """
282     Separate top-level loops from the function
283 
284     Stores loopless instructions from the original function into `outer`.
285     Stores list of loop instructions into `loops`.
286     Both `outer` and `loops` are list-like (`append(item)` defined).
287     """
288     endloop = None
289     cur = None
290     for inst in bytecode:
291         if endloop is None:
292             if inst.opname == 'SETUP_LOOP':
293                 cur = [inst]
294                 # Python may set the end of loop to the final jump destination
295                 # when nested in a if-else.  We need to scan the bytecode to
296                 # find the actual end of loop
297                 endloop = _scan_real_end_loop(bytecode, inst)
298             else:
299                 outer.append(inst)
300         else:
301             cur.append(inst)
302             if inst.next == endloop:
303                 for inst in cur:
304                     if inst.opname == 'RETURN_VALUE':
305                         # Reject if return inside loop
306                         outer.extend(cur)
307                         break
308                 else:
309                     loops.append(cur)
310                 endloop = None
311 
312 
313 def _scan_real_end_loop(bytecode, setuploop_inst):
314     """Find the end of loop.
315     Return the instruction offset.
316     """
317     start = setuploop_inst.next
318     end = start + setuploop_inst.arg
319     offset = start
320     depth = 0
321     while offset < end:
322         inst = bytecode[offset]
323         depth += inst.block_effect
324         if depth < 0:
325             return inst.next
326         offset = inst.next
327 
328
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/numba/looplifting.py b/numba/looplifting.py
--- a/numba/looplifting.py
+++ b/numba/looplifting.py
@@ -20,6 +20,13 @@
     outer_rds, outer_wrs = find_varnames_uses(bytecode, outer)
     outer_wrs |= set(bytecode.argspec.args)
 
+    # Find in-loop references to variables
+    for loop in loops:
+        args, rets = discover_args_and_returns(bytecode, loop, outer_rds,
+                                               outer_wrs)
+        outer_rds |= args
+        outer_wrs |= rets
+
     dispatchers = []
     outerlabels = set(bytecode.labels)
     outernames = list(bytecode.co_names)

{"golden_diff": "diff --git a/numba/looplifting.py b/numba/looplifting.py\n--- a/numba/looplifting.py\n+++ b/numba/looplifting.py\n@@ -20,6 +20,13 @@\n     outer_rds, outer_wrs = find_varnames_uses(bytecode, outer)\n     outer_wrs |= set(bytecode.argspec.args)\n \n+    # Find in-loop references to variables\n+    for loop in loops:\n+        args, rets = discover_args_and_returns(bytecode, loop, outer_rds,\n+                                               outer_wrs)\n+        outer_rds |= args\n+        outer_wrs |= rets\n+\n     dispatchers = []\n     outerlabels = set(bytecode.labels)\n     outernames = list(bytecode.co_names)\n", "issue": "BUG JIT optimizer removes index-variables after the loop\nIt seemes that the optimizer removes all variables that look like index variables after the loop is completed (or before the next loop is started).\n\nFor example, the following code:\n\n```\nfrom numba import jit\nimport numpy as np\n\n@jit('f8[:](f8[:])')\ndef test(x):\n    res = np.zeros(len(x))\n    ind = 0\n    for ii in range(len(x)):\n        ind += 1\n        res[ind] = x[ind]\n        if x[ind] >= 10:\n            break\n\n    # ind    #### Uncomment this line to get correct result\n    for ii in range(ind+1, len(x)):\n        res[ii] = 0\n    return res\n\nx = np.array([1.,4,2,-3,5,2,10,5,2,6])\nprint test(x)\n```\n\nreturns `[ 0.  0.  0.  0.  0.  0.  0.  0.  0.  0.]` as if before the second loop the variable had value: `ind=0`\n\nBut when the line `# ind` is not commented (i.e. when variable `ind` is read before the second loop) the result is correct: `[  0.   4.   2.  -3.   5.   2.  10.   0.   0.   0.]`\n\nIn first case the loop is jitted and in second is rejected. So it seemed that the access to the variable in `range(ind+1, len(x))` is not considered by the compiler as a reason to reject jitting.\n\nJust in case:\nPython 2.7.6\nLLVM: 3.3\nllvmpy (0.12.7)\nnumba (0.14.0)\n\n", "before_files": [{"content": "from __future__ import print_function, division, absolute_import\n\nfrom numba import utils\nfrom numba.bytecode import ByteCodeInst, CustomByteCode\n\n\ndef lift_loop(bytecode, dispatcher_factory):\n    \"\"\"Lift the top-level loops.\n\n    Returns (outer, loops)\n    ------------------------\n    * outer: ByteCode of a copy of the loop-less function.\n    * loops: a list of ByteCode of the loops.\n    \"\"\"\n    outer = []\n    loops = []\n    separate_loops(bytecode, outer, loops)\n\n    # Discover variables references\n    outer_rds, outer_wrs = find_varnames_uses(bytecode, outer)\n    outer_wrs |= set(bytecode.argspec.args)\n\n    dispatchers = []\n    outerlabels = set(bytecode.labels)\n    outernames = list(bytecode.co_names)\n\n    for loop in loops:\n        args, rets = discover_args_and_returns(bytecode, loop, outer_rds,\n                                               outer_wrs)\n\n        disp = insert_loop_call(bytecode, loop, args,\n                                outer, outerlabels, rets,\n                                dispatcher_factory)\n        dispatchers.append(disp)\n\n    # Build outer bytecode\n    codetable = utils.SortedMap((i.offset, i) for i in outer)\n    outerbc = CustomByteCode(func=bytecode.func,\n                             func_qualname=bytecode.func_qualname,\n                             argspec=bytecode.argspec,\n                             filename=bytecode.filename,\n                             co_names=outernames,\n                             co_varnames=bytecode.co_varnames,\n                             co_consts=bytecode.co_consts,\n                             co_freevars=bytecode.co_freevars,\n                             table=codetable,\n                             labels=outerlabels & set(codetable.keys()))\n\n    return outerbc, dispatchers\n\n@utils.total_ordering\nclass SubOffset(object):\n    \"\"\"The loop-jitting may insert bytecode between two bytecode but we\n    cannot guarantee that there is enough integral space between two offsets.\n    This class workaround the problem by introducing a fractional part to the\n    offset.\n    \"\"\"\n    def __init__(self, val, sub=1):\n        assert sub > 0, \"fractional part cannot be <= 0\"\n        self.val = val\n        self.sub = sub\n\n    def next(self):\n        \"\"\"Helper method to get the next suboffset by incrementing the\n        fractional part only\n        \"\"\"\n        return SubOffset(self.val, self.sub + 1)\n\n    def __add__(self, other):\n        \"\"\"Adding to a suboffset will only increment the fractional part.\n        The integral part is immutable.\n        \"\"\"\n        return SubOffset(self.val, self.sub + other)\n\n    def __hash__(self):\n        return hash((self.val, self.sub))\n\n    def __lt__(self, other):\n        \"\"\"Can only compare to SubOffset or int\n        \"\"\"\n        if isinstance(other, SubOffset):\n            if self.val < other.val:\n                return self\n            elif self.val == other.val:\n                return self.sub < other.sub\n            else:\n                return False\n        elif isinstance(other, int):\n            return self.val < other\n        else:\n            return NotImplemented\n\n    def __eq__(self, other):\n        if isinstance(other, SubOffset):\n            return self.val == other.val and self.sub == other.sub\n        elif isinstance(other, int):\n            # Can never be equal to a integer by definition\n            return False\n        else:\n            return NotImplemented\n\n    def __repr__(self):\n        \"\"\"Print like a floating-point by it is not one at all.\n        \"\"\"\n        return \"{0}.{1}\".format(self.val, self.sub)\n\n\ndef insert_loop_call(bytecode, loop, args, outer, outerlabels, returns,\n                     dispatcher_factory):\n    endloopoffset = loop[-1].next\n    # Accepted. Create a bytecode object for the loop\n    args = tuple(args)\n\n    lbc = make_loop_bytecode(bytecode, loop, args, returns)\n\n    # Generate dispatcher for this inner loop, and append it to the\n    # consts tuple.\n    disp = dispatcher_factory(lbc)\n    disp_idx = len(bytecode.co_consts)\n    bytecode.co_consts += (disp,)\n\n    # Insert jump to the end\n    insertpt = SubOffset(loop[0].next)\n    jmp = ByteCodeInst.get(loop[0].offset, 'JUMP_ABSOLUTE', insertpt)\n    jmp.lineno = loop[0].lineno\n    insert_instruction(outer, jmp)\n\n    outerlabels.add(outer[-1].next)\n\n    # Prepare arguments\n    loadfn = ByteCodeInst.get(insertpt, \"LOAD_CONST\", disp_idx)\n    loadfn.lineno = loop[0].lineno\n    insert_instruction(outer, loadfn)\n\n    insertpt = insertpt.next()\n    for arg in args:\n        loadarg = ByteCodeInst.get(insertpt, 'LOAD_FAST',\n                                   bytecode.co_varnames.index(arg))\n        loadarg.lineno = loop[0].lineno\n        insert_instruction(outer, loadarg)\n        insertpt = insertpt.next()\n\n    # Call function\n    assert len(args) < 256\n    call = ByteCodeInst.get(insertpt, \"CALL_FUNCTION\", len(args))\n    call.lineno = loop[0].lineno\n    insert_instruction(outer, call)\n\n    insertpt = insertpt.next()\n\n    if returns:\n        # Unpack arguments\n        unpackseq = ByteCodeInst.get(insertpt, \"UNPACK_SEQUENCE\",\n                                  len(returns))\n        unpackseq.lineno = loop[0].lineno\n        insert_instruction(outer, unpackseq)\n        insertpt = insertpt.next()\n\n        for out in returns:\n            # Store each variable\n            storefast = ByteCodeInst.get(insertpt, \"STORE_FAST\",\n                                      bytecode.co_varnames.index(out))\n            storefast.lineno = loop[0].lineno\n            insert_instruction(outer, storefast)\n            insertpt = insertpt.next()\n    else:\n        # No return value\n        poptop = ByteCodeInst.get(outer[-1].next, \"POP_TOP\", None)\n        poptop.lineno = loop[0].lineno\n        insert_instruction(outer, poptop)\n        insertpt = insertpt.next()\n\n    jmpback = ByteCodeInst.get(insertpt, 'JUMP_ABSOLUTE',\n                               endloopoffset)\n\n    jmpback.lineno = loop[0].lineno\n    insert_instruction(outer, jmpback)\n\n    return disp\n\n\ndef insert_instruction(insts, item):\n    i = find_previous_inst(insts, item.offset)\n    insts.insert(i, item)\n\n\ndef find_previous_inst(insts, offset):\n    for i, inst in enumerate(insts):\n        if inst.offset > offset:\n            return i\n    return len(insts)\n\n\ndef make_loop_bytecode(bytecode, loop, args, returns):\n    # Add return None\n    co_consts = tuple(bytecode.co_consts)\n    if None not in co_consts:\n        co_consts += (None,)\n\n    if returns:\n        for out in returns:\n            # Load output\n            loadfast = ByteCodeInst.get(loop[-1].next, \"LOAD_FAST\",\n                                         bytecode.co_varnames.index(out))\n            loadfast.lineno = loop[-1].lineno\n            loop.append(loadfast)\n            # Build tuple\n            buildtuple = ByteCodeInst.get(loop[-1].next, \"BUILD_TUPLE\",\n                                        len(returns))\n            buildtuple.lineno = loop[-1].lineno\n            loop.append(buildtuple)\n\n    else:\n        # Load None\n        load_none = ByteCodeInst.get(loop[-1].next, \"LOAD_CONST\",\n                                     co_consts.index(None))\n        load_none.lineno = loop[-1].lineno\n        loop.append(load_none)\n\n    # Return TOS\n    return_value = ByteCodeInst.get(loop[-1].next, \"RETURN_VALUE\", 0)\n    return_value.lineno = loop[-1].lineno\n    loop.append(return_value)\n\n    # Function name\n    loop_qualname = bytecode.func_qualname + \".__numba__loop%d__\" % loop[0].offset\n\n    # Argspec\n    argspectype = type(bytecode.argspec)\n    argspec = argspectype(args=args, varargs=(), keywords=(), defaults=())\n\n    # Code table\n    codetable = utils.SortedMap((i.offset, i) for i in loop)\n\n    # Custom bytecode object\n    lbc = CustomByteCode(func=bytecode.func,\n                         func_qualname=loop_qualname,\n                         argspec=argspec,\n                         filename=bytecode.filename,\n                         co_names=bytecode.co_names,\n                         co_varnames=bytecode.co_varnames,\n                         co_consts=co_consts,\n                         co_freevars=bytecode.co_freevars,\n                         table=codetable,\n                         labels=bytecode.labels)\n\n    return lbc\n\n\ndef stitch_instructions(outer, loop):\n    begin = loop[0].offset\n    i = find_previous_inst(outer, begin)\n    return outer[:i] + loop + outer[i:]\n\n\ndef discover_args_and_returns(bytecode, insts, outer_rds, outer_wrs):\n    \"\"\"\n    Basic analysis for args and returns\n    This completely ignores the ordering or the read-writes.\n    \"\"\"\n    rdnames, wrnames = find_varnames_uses(bytecode, insts)\n    # Pass names that are written outside and read locally\n    args = outer_wrs & rdnames\n    # Return values that it written locally and read outside\n    rets = wrnames & outer_rds\n    return args, rets\n\n\ndef find_varnames_uses(bytecode, insts):\n    rdnames = set()\n    wrnames = set()\n    for inst in insts:\n        if inst.opname == 'LOAD_FAST':\n            rdnames.add(bytecode.co_varnames[inst.arg])\n        elif inst.opname == 'STORE_FAST':\n            wrnames.add(bytecode.co_varnames[inst.arg])\n    return rdnames, wrnames\n\n\ndef separate_loops(bytecode, outer, loops):\n    \"\"\"\n    Separate top-level loops from the function\n\n    Stores loopless instructions from the original function into `outer`.\n    Stores list of loop instructions into `loops`.\n    Both `outer` and `loops` are list-like (`append(item)` defined).\n    \"\"\"\n    endloop = None\n    cur = None\n    for inst in bytecode:\n        if endloop is None:\n            if inst.opname == 'SETUP_LOOP':\n                cur = [inst]\n                # Python may set the end of loop to the final jump destination\n                # when nested in a if-else.  We need to scan the bytecode to\n                # find the actual end of loop\n                endloop = _scan_real_end_loop(bytecode, inst)\n            else:\n                outer.append(inst)\n        else:\n            cur.append(inst)\n            if inst.next == endloop:\n                for inst in cur:\n                    if inst.opname == 'RETURN_VALUE':\n                        # Reject if return inside loop\n                        outer.extend(cur)\n                        break\n                else:\n                    loops.append(cur)\n                endloop = None\n\n\ndef _scan_real_end_loop(bytecode, setuploop_inst):\n    \"\"\"Find the end of loop.\n    Return the instruction offset.\n    \"\"\"\n    start = setuploop_inst.next\n    end = start + setuploop_inst.arg\n    offset = start\n    depth = 0\n    while offset < end:\n        inst = bytecode[offset]\n        depth += inst.block_effect\n        if depth < 0:\n            return inst.next\n        offset = inst.next\n\n", "path": "numba/looplifting.py"}], "after_files": [{"content": "from __future__ import print_function, division, absolute_import\n\nfrom numba import utils\nfrom numba.bytecode import ByteCodeInst, CustomByteCode\n\n\ndef lift_loop(bytecode, dispatcher_factory):\n    \"\"\"Lift the top-level loops.\n\n    Returns (outer, loops)\n    ------------------------\n    * outer: ByteCode of a copy of the loop-less function.\n    * loops: a list of ByteCode of the loops.\n    \"\"\"\n    outer = []\n    loops = []\n    separate_loops(bytecode, outer, loops)\n\n    # Discover variables references\n    outer_rds, outer_wrs = find_varnames_uses(bytecode, outer)\n    outer_wrs |= set(bytecode.argspec.args)\n\n    # Find in-loop references to variables\n    for loop in loops:\n        args, rets = discover_args_and_returns(bytecode, loop, outer_rds,\n                                               outer_wrs)\n        outer_rds |= args\n        outer_wrs |= rets\n\n    dispatchers = []\n    outerlabels = set(bytecode.labels)\n    outernames = list(bytecode.co_names)\n\n    for loop in loops:\n        args, rets = discover_args_and_returns(bytecode, loop, outer_rds,\n                                               outer_wrs)\n\n        disp = insert_loop_call(bytecode, loop, args,\n                                outer, outerlabels, rets,\n                                dispatcher_factory)\n        dispatchers.append(disp)\n\n    # Build outer bytecode\n    codetable = utils.SortedMap((i.offset, i) for i in outer)\n    outerbc = CustomByteCode(func=bytecode.func,\n                             func_qualname=bytecode.func_qualname,\n                             argspec=bytecode.argspec,\n                             filename=bytecode.filename,\n                             co_names=outernames,\n                             co_varnames=bytecode.co_varnames,\n                             co_consts=bytecode.co_consts,\n                             co_freevars=bytecode.co_freevars,\n                             table=codetable,\n                             labels=outerlabels & set(codetable.keys()))\n\n    return outerbc, dispatchers\n\n@utils.total_ordering\nclass SubOffset(object):\n    \"\"\"The loop-jitting may insert bytecode between two bytecode but we\n    cannot guarantee that there is enough integral space between two offsets.\n    This class workaround the problem by introducing a fractional part to the\n    offset.\n    \"\"\"\n    def __init__(self, val, sub=1):\n        assert sub > 0, \"fractional part cannot be <= 0\"\n        self.val = val\n        self.sub = sub\n\n    def next(self):\n        \"\"\"Helper method to get the next suboffset by incrementing the\n        fractional part only\n        \"\"\"\n        return SubOffset(self.val, self.sub + 1)\n\n    def __add__(self, other):\n        \"\"\"Adding to a suboffset will only increment the fractional part.\n        The integral part is immutable.\n        \"\"\"\n        return SubOffset(self.val, self.sub + other)\n\n    def __hash__(self):\n        return hash((self.val, self.sub))\n\n    def __lt__(self, other):\n        \"\"\"Can only compare to SubOffset or int\n        \"\"\"\n        if isinstance(other, SubOffset):\n            if self.val < other.val:\n                return self\n            elif self.val == other.val:\n                return self.sub < other.sub\n            else:\n                return False\n        elif isinstance(other, int):\n            return self.val < other\n        else:\n            return NotImplemented\n\n    def __eq__(self, other):\n        if isinstance(other, SubOffset):\n            return self.val == other.val and self.sub == other.sub\n        elif isinstance(other, int):\n            # Can never be equal to a integer by definition\n            return False\n        else:\n            return NotImplemented\n\n    def __repr__(self):\n        \"\"\"Print like a floating-point by it is not one at all.\n        \"\"\"\n        return \"{0}.{1}\".format(self.val, self.sub)\n\n\ndef insert_loop_call(bytecode, loop, args, outer, outerlabels, returns,\n                     dispatcher_factory):\n    endloopoffset = loop[-1].next\n    # Accepted. Create a bytecode object for the loop\n    args = tuple(args)\n\n    lbc = make_loop_bytecode(bytecode, loop, args, returns)\n\n    # Generate dispatcher for this inner loop, and append it to the\n    # consts tuple.\n    disp = dispatcher_factory(lbc)\n    disp_idx = len(bytecode.co_consts)\n    bytecode.co_consts += (disp,)\n\n    # Insert jump to the end\n    insertpt = SubOffset(loop[0].next)\n    jmp = ByteCodeInst.get(loop[0].offset, 'JUMP_ABSOLUTE', insertpt)\n    jmp.lineno = loop[0].lineno\n    insert_instruction(outer, jmp)\n\n    outerlabels.add(outer[-1].next)\n\n    # Prepare arguments\n    loadfn = ByteCodeInst.get(insertpt, \"LOAD_CONST\", disp_idx)\n    loadfn.lineno = loop[0].lineno\n    insert_instruction(outer, loadfn)\n\n    insertpt = insertpt.next()\n    for arg in args:\n        loadarg = ByteCodeInst.get(insertpt, 'LOAD_FAST',\n                                   bytecode.co_varnames.index(arg))\n        loadarg.lineno = loop[0].lineno\n        insert_instruction(outer, loadarg)\n        insertpt = insertpt.next()\n\n    # Call function\n    assert len(args) < 256\n    call = ByteCodeInst.get(insertpt, \"CALL_FUNCTION\", len(args))\n    call.lineno = loop[0].lineno\n    insert_instruction(outer, call)\n\n    insertpt = insertpt.next()\n\n    if returns:\n        # Unpack arguments\n        unpackseq = ByteCodeInst.get(insertpt, \"UNPACK_SEQUENCE\",\n                                  len(returns))\n        unpackseq.lineno = loop[0].lineno\n        insert_instruction(outer, unpackseq)\n        insertpt = insertpt.next()\n\n        for out in returns:\n            # Store each variable\n            storefast = ByteCodeInst.get(insertpt, \"STORE_FAST\",\n                                      bytecode.co_varnames.index(out))\n            storefast.lineno = loop[0].lineno\n            insert_instruction(outer, storefast)\n            insertpt = insertpt.next()\n    else:\n        # No return value\n        poptop = ByteCodeInst.get(outer[-1].next, \"POP_TOP\", None)\n        poptop.lineno = loop[0].lineno\n        insert_instruction(outer, poptop)\n        insertpt = insertpt.next()\n\n    jmpback = ByteCodeInst.get(insertpt, 'JUMP_ABSOLUTE',\n                               endloopoffset)\n\n    jmpback.lineno = loop[0].lineno\n    insert_instruction(outer, jmpback)\n\n    return disp\n\n\ndef insert_instruction(insts, item):\n    i = find_previous_inst(insts, item.offset)\n    insts.insert(i, item)\n\n\ndef find_previous_inst(insts, offset):\n    for i, inst in enumerate(insts):\n        if inst.offset > offset:\n            return i\n    return len(insts)\n\n\ndef make_loop_bytecode(bytecode, loop, args, returns):\n    # Add return None\n    co_consts = tuple(bytecode.co_consts)\n    if None not in co_consts:\n        co_consts += (None,)\n\n    if returns:\n        for out in returns:\n            # Load output\n            loadfast = ByteCodeInst.get(loop[-1].next, \"LOAD_FAST\",\n                                         bytecode.co_varnames.index(out))\n            loadfast.lineno = loop[-1].lineno\n            loop.append(loadfast)\n            # Build tuple\n            buildtuple = ByteCodeInst.get(loop[-1].next, \"BUILD_TUPLE\",\n                                        len(returns))\n            buildtuple.lineno = loop[-1].lineno\n            loop.append(buildtuple)\n\n    else:\n        # Load None\n        load_none = ByteCodeInst.get(loop[-1].next, \"LOAD_CONST\",\n                                     co_consts.index(None))\n        load_none.lineno = loop[-1].lineno\n        loop.append(load_none)\n\n    # Return TOS\n    return_value = ByteCodeInst.get(loop[-1].next, \"RETURN_VALUE\", 0)\n    return_value.lineno = loop[-1].lineno\n    loop.append(return_value)\n\n    # Function name\n    loop_qualname = bytecode.func_qualname + \".__numba__loop%d__\" % loop[0].offset\n\n    # Argspec\n    argspectype = type(bytecode.argspec)\n    argspec = argspectype(args=args, varargs=(), keywords=(), defaults=())\n\n    # Code table\n    codetable = utils.SortedMap((i.offset, i) for i in loop)\n\n    # Custom bytecode object\n    lbc = CustomByteCode(func=bytecode.func,\n                         func_qualname=loop_qualname,\n                         argspec=argspec,\n                         filename=bytecode.filename,\n                         co_names=bytecode.co_names,\n                         co_varnames=bytecode.co_varnames,\n                         co_consts=co_consts,\n                         co_freevars=bytecode.co_freevars,\n                         table=codetable,\n                         labels=bytecode.labels)\n\n    return lbc\n\n\ndef stitch_instructions(outer, loop):\n    begin = loop[0].offset\n    i = find_previous_inst(outer, begin)\n    return outer[:i] + loop + outer[i:]\n\n\ndef discover_args_and_returns(bytecode, insts, outer_rds, outer_wrs):\n    \"\"\"\n    Basic analysis for args and returns\n    This completely ignores the ordering or the read-writes.\n    \"\"\"\n    rdnames, wrnames = find_varnames_uses(bytecode, insts)\n    # Pass names that are written outside and read locally\n    args = outer_wrs & rdnames\n    # Return values that it written locally and read outside\n    rets = wrnames & outer_rds\n    return args, rets\n\n\ndef find_varnames_uses(bytecode, insts):\n    rdnames = set()\n    wrnames = set()\n    for inst in insts:\n        if inst.opname == 'LOAD_FAST':\n            rdnames.add(bytecode.co_varnames[inst.arg])\n        elif inst.opname == 'STORE_FAST':\n            wrnames.add(bytecode.co_varnames[inst.arg])\n    return rdnames, wrnames\n\n\ndef separate_loops(bytecode, outer, loops):\n    \"\"\"\n    Separate top-level loops from the function\n\n    Stores loopless instructions from the original function into `outer`.\n    Stores list of loop instructions into `loops`.\n    Both `outer` and `loops` are list-like (`append(item)` defined).\n    \"\"\"\n    endloop = None\n    cur = None\n    for inst in bytecode:\n        if endloop is None:\n            if inst.opname == 'SETUP_LOOP':\n                cur = [inst]\n                # Python may set the end of loop to the final jump destination\n                # when nested in a if-else.  We need to scan the bytecode to\n                # find the actual end of loop\n                endloop = _scan_real_end_loop(bytecode, inst)\n            else:\n                outer.append(inst)\n        else:\n            cur.append(inst)\n            if inst.next == endloop:\n                for inst in cur:\n                    if inst.opname == 'RETURN_VALUE':\n                        # Reject if return inside loop\n                        outer.extend(cur)\n                        break\n                else:\n                    loops.append(cur)\n                endloop = None\n\n\ndef _scan_real_end_loop(bytecode, setuploop_inst):\n    \"\"\"Find the end of loop.\n    Return the instruction offset.\n    \"\"\"\n    start = setuploop_inst.next\n    end = start + setuploop_inst.arg\n    offset = start\n    depth = 0\n    while offset < end:\n        inst = bytecode[offset]\n        depth += inst.block_effect\n        if depth < 0:\n            return inst.next\n        offset = inst.next\n\n", "path": "numba/looplifting.py"}]}