Repository: numba/numba

Issue:
Using shared memory in more than one function (global or device) doesn't work
The following code:

``` python
from numba import cuda, int32

@cuda.jit(device=True)
def inner():
    inner_arr = cuda.shared.array(1, dtype=int32)

@cuda.jit
def outer():
    outer_arr = cuda.shared.array(1, dtype=int32)
    inner()

outer()
```

due to the use of a shared array in the caller and the callee, produces:

```
$ python main.py 
Traceback (most recent call last):
  File "main.py", line 12, in <module>
    outer()
  File "/home/gmarkall/work/numba/numba/cuda/compiler.py", line 455, in __call__
    kernel = self.specialize(*args)
  File "/home/gmarkall/work/numba/numba/cuda/compiler.py", line 467, in specialize
    **self.targetoptions)
  File "/home/gmarkall/work/numba/numba/cuda/compiler.py", line 49, in compile_kernel
    cres = compile_cuda(pyfunc, types.void, args, debug=debug, inline=inline)
  File "/home/gmarkall/work/numba/numba/cuda/compiler.py", line 42, in compile_cuda
    library.finalize()
  File "/home/gmarkall/work/numba/numba/targets/codegen.py", line 145, in finalize
    library._get_module_for_linking(), preserve=True)
  File "/home/gmarkall/.conda/envs/mrrc/lib/python3.4/site-packages/llvmlite/binding/module.py", line 130, in link_in
    link_modules(self, other, preserve)
  File "/home/gmarkall/.conda/envs/mrrc/lib/python3.4/site-packages/llvmlite/binding/linker.py", line 11, in link_modules
    raise RuntimeError(str(outerr))
RuntimeError: Linking globals named '_cudapy_smem': symbol multiply defined!
```



Discussion:
I commented out setting internal linkage to shared memory due to a NVVM bug: https://github.com/numba/numba/blob/afd9617eace3009ff1f61485b342f1f5e7b058c0/numba/cuda/cudaimpl.py#L295
I will try to fix it.

Ah, I see. I wasn't sure if I shouldn't have been declaring shared arrays in separate functions.


Resolving patch:
diff --git a/numba/cuda/cudaimpl.py b/numba/cuda/cudaimpl.py
--- a/numba/cuda/cudaimpl.py
+++ b/numba/cuda/cudaimpl.py
@@ -127,12 +127,25 @@ def ptx_cmem_arylike(context, builder, sig, args):
     return ary._getvalue()
 
 
+_unique_smem_id = 0
+
+
+def _get_unique_smem_id(name):
+    """Due to bug with NVVM invalid internalizing of shared memory in the
+    PTX output.  We can't mark shared memory to be internal. We have to
+    ensure unique name is generated for shared memory symbol.
+    """
+    global _unique_smem_id
+    _unique_smem_id += 1
+    return "{0}_{1}".format(name, _unique_smem_id)
+
+
 @register
 @implement('ptx.smem.alloc', types.intp, types.Any)
 def ptx_smem_alloc_intp(context, builder, sig, args):
     length, dtype = args
     return _generic_array(context, builder, shape=(length,), dtype=dtype,
-                          symbol_name='_cudapy_smem',
+                          symbol_name=_get_unique_smem_id('_cudapy_smem'),
                           addrspace=nvvm.ADDRSPACE_SHARED,
                           can_dynsized=True)
 
@@ -142,7 +155,7 @@ def ptx_smem_alloc_intp(context, builder, sig, args):
 def ptx_smem_alloc_array(context, builder, sig, args):
     shape, dtype = args
     return _generic_array(context, builder, shape=shape, dtype=dtype,
-                          symbol_name='_cudapy_smem',
+                          symbol_name=_get_unique_smem_id('_cudapy_smem'),
                           addrspace=nvvm.ADDRSPACE_SHARED,
                           can_dynsized=True)
 
@@ -292,7 +305,9 @@ def _generic_array(context, builder, shape, dtype, symbol_name, addrspace,
             ## Comment out the following line to workaround a NVVM bug
             ## which generates a invalid symbol name when the linkage
             ## is internal and in some situation.
+            ## See _get_unique_smem_id()
             # gvmem.linkage = lc.LINKAGE_INTERNAL
+
             gvmem.initializer = lc.Constant.undef(laryty)
 
         if dtype not in types.number_domain:


Test patch:
diff --git a/numba/cuda/tests/cudapy/test_sm.py b/numba/cuda/tests/cudapy/test_sm.py
new file mode 100644
--- /dev/null
+++ b/numba/cuda/tests/cudapy/test_sm.py
@@ -0,0 +1,21 @@
+from numba import cuda, int32
+
+from numba.cuda.testing import unittest
+
+
+class TestSharedMemoryIssue(unittest.TestCase):
+    def test_issue_953_sm_linkage_conflict(self):
+        @cuda.jit(device=True)
+        def inner():
+            inner_arr = cuda.shared.array(1, dtype=int32)
+
+        @cuda.jit
+        def outer():
+            outer_arr = cuda.shared.array(1, dtype=int32)
+            inner()
+
+        outer()
+
+
+if __name__ == '__main__':
+    unittest.main()