Repository: numba/numba

Issue:
Dead-code eliminator removes a call with side-effect.
HPAT, which depends on Numba, uses remove_dead function from numba/ir_utils.py to eliminate dead code.  For the following function, it erroneously removes the np.random.seed() call

```python
import numpy as np                                                                                                                                                                                                                            
import hpat                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                              
@hpat.jit                                                                                                                                                                                                                                     
def foo():                                                                                                                                                                                                                                    
  np.random.seed(2)                                                                                                                                                                                                                           
  return np.random.ranf(10)                                                                                                                                                                                                                   
                                                                                                                                                                                                                                              
print(foo())                 

as can be seen from the following IR.  The call to np.random.seed() is eliminated after the IO pass:

```python
------------------------------IR starting IO: foo-------------------------------                                                                                                                                                              
label 0:                                                                                                                                                                                                                                      
    $0.1 = global(np: <module 'numpy' from '/home/agayev/miniconda3/envs/h/lib/python3.6/site-packages/numpy/__init__.py'>) ['$0.1']                                                                                                          
    $0.2 = getattr(value=$0.1, attr=random)  ['$0.1', '$0.2']                                                                                                                                                                                 
    del $0.1                                 []                                                                                                                                                                                               
    $0.3 = getattr(value=$0.2, attr=seed)    ['$0.2', '$0.3']                                                                                                                                                                                 
    del $0.2                                 []                                                                                                                                                                                               
    $const0.4 = const(int, 2)                ['$const0.4']                                                                                                                                                                                    
    $0.5 = call $0.3($const0.4, func=$0.3, args=[Var($const0.4, examples/r.py (6))], kws=(), vararg=None) ['$0.3', '$0.5', '$const0.4']                                                                                                       
    del $const0.4                            []                                                                                                                                                                                               
    del $0.5                                 []                                                                                                                                                                                               
    del $0.3                                 []                                                                                                                                                                                               
    $0.6 = global(np: <module 'numpy' from '/home/agayev/miniconda3/envs/h/lib/python3.6/site-packages/numpy/__init__.py'>) ['$0.6']                                                                                                          
    $0.7 = getattr(value=$0.6, attr=random)  ['$0.6', '$0.7']                                                                                                                                                                                 
    del $0.6                                 []                                                                                                                                                                                               
    $0.8 = getattr(value=$0.7, attr=ranf)    ['$0.7', '$0.8']                                                                                                                                                                                 
    del $0.7                                 []                                                                                                                                                                                               
    $const0.9 = const(int, 10)               ['$const0.9']                                                                                                                                                                                    
    $0.10 = call $0.8($const0.9, func=$0.8, args=[Var($const0.9, examples/r.py (7))], kws=(), vararg=None) ['$0.10', '$0.8', '$const0.9']                                                                                                     
    del $const0.9                            []                                                                                                                                                                                               
    del $0.8                                 []                                                                                                                                                                                               
    $0.11 = cast(value=$0.10)                ['$0.10', '$0.11']                                                                                                                                                                               
    del $0.10                                []                                                                                                                                                                                               
    return $0.11                             ['$0.11']                                                                                                                                                                                        
----------------------------------------                                                                                                                                                                                                      
alias map: {'$0.10': {'$0.10', '$0.11'}, '$0.11': {'$0.10', '$0.11'}}                                                                                                                                                                         
--------------------------------IR after IO: foo--------------------------------                                                                                                                                                              
label 0:                                                                                                                                                                                                                                      
    del $0.1                                 []                                                                                                                                                                                               
    del $0.2                                 []                                                                                                                                                                                               
    del $const0.4                            []                                                                                                                                                                                               
    del $0.5                                 []                                                                                                                                                                                               
    del $0.3                                 []                                                                                                                                                                                               
    $0.6 = global(np: <module 'numpy' from '/home/agayev/miniconda3/envs/h/lib/python3.6/site-packages/numpy/__init__.py'>) ['$0.6']                                                                                                          
    $0.7 = getattr(value=$0.6, attr=random)  ['$0.6', '$0.7']                                                                                                                                                                                 
    del $0.6                                 []                                                                                                                                                                                               
    $0.8 = getattr(value=$0.7, attr=ranf)    ['$0.7', '$0.8']                                                                                                                                                                                 
    del $0.7                                 []                                                                                                                                                                                               
    $const0.9 = const(int, 10)               ['$const0.9']                                                                                                                                                                                    
    $0.10 = call $0.8($const0.9, func=$0.8, args=[Var($const0.9, examples/r.py (7))], kws=(), vararg=None) ['$0.10', '$0.8', '$const0.9']                                                                                                     
    del $const0.9                            []                                                                                                                                                                                               
    del $0.8                                 []                                                                                                                                                                                               
    $0.11 = cast(value=$0.10)                ['$0.10', '$0.11']                                                                                                                                                                               
    del $0.10                                []                                                                                                                                                                                               
    return $0.11                             ['$0.11']                                                                                                                                                                                        
----------------------------------------                                                                                                                                                                                                      



Discussion:

Resolving patch:
diff --git a/numba/ir_utils.py b/numba/ir_utils.py
--- a/numba/ir_utils.py
+++ b/numba/ir_utils.py
@@ -575,7 +575,7 @@ def remove_dead_block(block, lives, call_table, arg_aliases, alias_map, alias_se
 
 def remove_dead_random_call(rhs, lives, call_list):
     if len(call_list) == 3 and call_list[1:] == ['random', numpy]:
-        return True
+        return call_list[0] != 'seed'
     return False
 
 remove_call_handlers.append(remove_dead_random_call)


Test patch:
diff --git a/numba/tests/test_remove_dead.py b/numba/tests/test_remove_dead.py
--- a/numba/tests/test_remove_dead.py
+++ b/numba/tests/test_remove_dead.py
@@ -12,6 +12,7 @@
 from numba.ir_utils import copy_propagate, apply_copy_propagate, get_name_var_table, remove_dels, remove_dead
 from numba import ir
 from numba import unittest_support as unittest
+import numpy as np
 
 def test_will_propagate(b, z, w):
     x = 3
@@ -63,5 +64,22 @@ def test1(self):
             remove_dead(test_ir.blocks, test_ir.arg_names)
             self.assertFalse(findLhsAssign(test_ir, "x"))
 
+    def test2(self):
+        def call_np_random_seed():
+            np.random.seed(2)
+
+        def seed_call_exists(func_ir):
+            for inst in func_ir.blocks[0].body:
+                if (isinstance(inst, ir.Assign) and
+                    isinstance(inst.value, ir.Expr) and
+                    inst.value.op == 'call' and
+                    func_ir.get_definition(inst.value.func).attr == 'seed'):
+                    return True
+            return False
+
+        test_ir = compiler.run_frontend(call_np_random_seed)
+        remove_dead(test_ir.blocks, test_ir.arg_names)
+        self.assertTrue(seed_call_exists(test_ir))
+
 if __name__ == "__main__":
     unittest.main()