Repository: numba/numba

Issue:
vectorize doesn't like more than one ufunc call
The following code works:

```
from numba import vectorize
import numpy as np

@vectorize(["(float64,float64)"])
def foo(x1,x2):
    return np.add(x1,x2)

a = np.arange(10, dtype='f8')
b = np.arange(10, dtype='f8')
print foo(a,b)
```

but adding another ufunc call produces an error:

<pre>
from numba import vectorize
import numpy as np

@vectorize(["(float64,float64)"])
def foo(x1,x2):
    <b>np.add(x1,x2)</b>
    return np.add(x1,x2)

a = np.arange(10, dtype='f8')
b = np.arange(10, dtype='f8')
print foo(a,b)
</pre>


Traceback (most recent call last):
  File "test6.py", line 4, in <module>
    @vectorize(["(float64,float64)"])
  File "/Users/jayvius/Projects/numba/numba/npyufunc/decorators.py", line 69, in wrap
    return vec.build_ufunc()
  File "/Users/jayvius/Projects/numba/numba/npyufunc/ufuncbuilder.py", line 91, in build_ufunc
    dtypenums, ptr = self.build(cres)
  File "/Users/jayvius/Projects/numba/numba/npyufunc/ufuncbuilder.py", line 113, in build
    ctx.finalize(wrapper, cres.fndesc)
  File "/Users/jayvius/Projects/numba/numba/targets/cpu.py", line 260, in finalize
    im.run(func.module)
  File "/Users/jayvius/Projects/numba/numba/targets/intrinsics.py", line 65, in run
    self.translate_intrinsic_to_cmath(module)
  File "/Users/jayvius/Projects/numba/numba/targets/intrinsics.py", line 85, in translate_intrinsic_to_cmath
    fn.name = INTR_TO_CMATH[fn.name]
KeyError: 'llvm.stacksave'



Discussion:
Ufunc is generating stack allocation (`builder.alloca()`) in blocks other than the entry block.  I got a fix and will try to grep through the code for similar problems


Resolving patch:
diff --git a/numba/lowering.py b/numba/lowering.py
--- a/numba/lowering.py
+++ b/numba/lowering.py
@@ -606,5 +606,4 @@ def alloca(self, name, type):
         return self.alloca_lltype(name, lltype)
 
     def alloca_lltype(self, name, lltype):
-        with cgutils.goto_block(self.builder, self.entry_block):
-            return self.builder.alloca(lltype, name=name)
+        return cgutils.alloca_once(self.builder, lltype, name=name)
diff --git a/numba/targets/builtins.py b/numba/targets/builtins.py
--- a/numba/targets/builtins.py
+++ b/numba/targets/builtins.py
@@ -173,8 +173,8 @@ def int_upower_impl(context, builder, sig, args):
 
 
 def int_power_func_body(context, builder, x, y):
-    pcounter = builder.alloca(y.type)
-    presult = builder.alloca(x.type)
+    pcounter = cgutils.alloca_once(builder, y.type)
+    presult = cgutils.alloca_once(builder, x.type)
     result = Constant.int(x.type, 1)
     counter = y
     builder.store(counter, pcounter)
@@ -339,7 +339,7 @@ def int_sign_impl(context, builder, sig, args):
     cmp_zero = builder.icmp(lc.ICMP_EQ, x, ZERO)
     cmp_pos = builder.icmp(lc.ICMP_SGT, x, ZERO)
 
-    presult = builder.alloca(x.type)
+    presult = cgutils.alloca_once(builder, x.type)
 
     bb_zero = cgutils.append_basic_block(builder, ".zero")
     bb_postest = cgutils.append_basic_block(builder, ".postest")
@@ -509,9 +509,9 @@ def real_divmod_func_body(context, builder, vx, wx):
     #     }
     #     return Py_BuildValue("(dd)", floordiv, mod);
     # }
-    pmod = builder.alloca(vx.type)
-    pdiv = builder.alloca(vx.type)
-    pfloordiv = builder.alloca(vx.type)
+    pmod = cgutils.alloca_once(builder, vx.type)
+    pdiv = cgutils.alloca_once(builder, vx.type)
+    pfloordiv = cgutils.alloca_once(builder, vx.type)
 
     mod = builder.frem(vx, wx)
     div = builder.fdiv(builder.fsub(vx, mod), wx)
@@ -652,7 +652,7 @@ def real_sign_impl(context, builder, sig, args):
     cmp_zero = builder.fcmp(lc.FCMP_OEQ, x, ZERO)
     cmp_pos = builder.fcmp(lc.FCMP_OGT, x, ZERO)
 
-    presult = builder.alloca(x.type)
+    presult = cgutils.alloca_once(builder, x.type)
 
     bb_zero = cgutils.append_basic_block(builder, ".zero")
     bb_postest = cgutils.append_basic_block(builder, ".postest")
diff --git a/numba/targets/npyimpl.py b/numba/targets/npyimpl.py
--- a/numba/targets/npyimpl.py
+++ b/numba/targets/npyimpl.py
@@ -81,7 +81,7 @@ def __init__(self, ctxt, bld, val, ty):
         self.base_type = ty
         intpty = ctxt.get_value_type(types.intp)
         self.shape = [Constant.int(intpty, 1)]
-        self._ptr = bld.alloca(ctxt.get_data_type(ty))
+        self._ptr = cgutils.alloca_once(bld, ctxt.get_data_type(ty))
 
     def create_iter_indices(self):
         return _ScalarIndexingHelper()
@@ -138,7 +138,7 @@ def create_iter_indices(self):
 
         indices = []
         for i in range(self.ndim):
-            x = self.builder.alloca(Type.int(intpty.width))
+            x = cgutils.alloca_once(self.builder, Type.int(intpty.width))
             self.builder.store(ZERO, x)
             indices.append(x)
         return _ArrayIndexingHelper(self, indices)


Test patch:
diff --git a/numba/tests/test_ufuncs.py b/numba/tests/test_ufuncs.py
--- a/numba/tests/test_ufuncs.py
+++ b/numba/tests/test_ufuncs.py
@@ -8,10 +8,10 @@
 import functools
 
 import numba.unittest_support as unittest
-from numba.compiler import compile_isolated, Flags, DEFAULT_FLAGS
+from numba.compiler import compile_isolated, Flags
 from numba import types, utils
+from numba import vectorize
 from numba.config import PYVERSION
-from numba.targets import cpu
 from numba.typeinfer import TypingError
 from numba.tests.support import TestCase, CompilationCache
 
@@ -1089,6 +1089,17 @@ def test_scalar_binary_mixed_ufuncs_npm(self):
         self.test_scalar_binary_mixed_ufunc(flags=no_pyobj_flags)
 
 
+class TestUfuncIssues(TestCase):
+    def test_issue_651(self):
+        # Exercise the code path to make sure this does not fail
+        @vectorize(["(float64,float64)"])
+        def foo(x1, x2):
+            return np.add(x1, x2) + np.add(x1, x2)
+
+        a = np.arange(10, dtype='f8')
+        b = np.arange(10, dtype='f8')
+        self.assertTrue(np.all(foo(a, b) == (a + b) + (a + b)))
+
 
 if __name__ == '__main__':
     unittest.main()