metagraph-dev / mlir-graphblas

MLIR tools and dialect for GraphBLAS
https://mlir-graphblas.readthedocs.io/en/latest/
Apache License 2.0
15 stars 6 forks source link

Returning the same sparse tensor multiple times in IR builder causes errors. #219

Open paul-tqh-nguyen opened 2 years ago

paul-tqh-nguyen commented 2 years ago
good.py ```python from mlir_graphblas import MlirJitEngine from mlir_graphblas.mlir_builder import MLIRFunctionBuilder, GRAPHBLAS_PASSES from mlir_graphblas.algorithms import _build_common_aliases import numpy as np engine = MlirJitEngine() irb = MLIRFunctionBuilder( "good_func", input_types=[], return_types=["tensor"], aliases=_build_common_aliases(), ) c0 = irb.constant(0, "index") c1 = irb.constant(1, "index") c8 = irb.constant(8, "index") c3_i64 = irb.constant(3, "i64") vec = irb.util.new_sparse_tensor("tensor", c8) vec_ptr8 = irb.util.tensor_to_ptr8(vec) irb.util.resize_sparse_index(vec_ptr8, c0, c1) irb.util.resize_sparse_values(vec_ptr8, c1) vec_indices = irb.sparse_tensor.indices(vec, c0) vec_values = irb.sparse_tensor.values(vec) irb.memref.store(c3_i64, vec_indices, c0) irb.memref.store(c3_i64, vec_values, c0) irb.return_vars(vec) good_func = irb.compile(engine=engine, passes=GRAPHBLAS_PASSES) ans = good_func() print() print(f"ans.pointers {repr(ans.pointers)}") print(f"ans.indices {repr(ans.indices)}") print(f"ans.values {repr(ans.values)}") print(f"ans.toarray() {repr(ans.toarray())}") print() ```
bad.py ```python from mlir_graphblas import MlirJitEngine from mlir_graphblas.mlir_builder import MLIRFunctionBuilder, GRAPHBLAS_PASSES from mlir_graphblas.algorithms import _build_common_aliases import numpy as np engine = MlirJitEngine() irb = MLIRFunctionBuilder( "bad_func", input_types=[], return_types=["tensor", "tensor"], aliases=_build_common_aliases(), ) c0 = irb.constant(0, "index") c1 = irb.constant(1, "index") c8 = irb.constant(8, "index") c3_i64 = irb.constant(3, "i64") vec = irb.util.new_sparse_tensor("tensor", c8) vec_ptr8 = irb.util.tensor_to_ptr8(vec) irb.util.resize_sparse_index(vec_ptr8, c0, c1) irb.util.resize_sparse_values(vec_ptr8, c1) vec_indices = irb.sparse_tensor.indices(vec, c0) vec_values = irb.sparse_tensor.values(vec) irb.memref.store(c3_i64, vec_indices, c0) irb.memref.store(c3_i64, vec_values, c0) irb.return_vars(vec, vec) bad_func = irb.compile(engine=engine, passes=GRAPHBLAS_PASSES) first_vec, second_vec = bad_func() print() print(f"first_vec.pointers {repr(first_vec.pointers)}") print(f"first_vec.indices {repr(first_vec.indices)}") print(f"first_vec.values {repr(first_vec.values)}") print(f"first_vec.toarray() {repr(first_vec.toarray())}") print() print(f"second_vec.pointers {repr(second_vec.pointers)}") print(f"second_vec.indices {repr(second_vec.indices)}") print(f"second_vec.values {repr(second_vec.values)}") print(f"second_vec.toarray() {repr(second_vec.toarray())}") print() ```
Results ``` (mlir_graphblas) pnguyen@CONDA-0584:/Users/pnguyen/code/mlir-graphblas$ python3 /tmp/good.py Using development graphblas-opt: /Users/pnguyen/code/mlir-graphblas/mlir_graphblas/src/build/bin/graphblas-opt ans.pointers (array([0, 0], dtype=uint64),) ans.indices (array([3], dtype=uint64),) ans.values array([3]) ans.toarray() array([0, 0, 0, 3, 0, 0, 0, 0]) (mlir_graphblas) pnguyen@CONDA-0584:/Users/pnguyen/code/mlir-graphblas$ python3 /tmp/bad.py Using development graphblas-opt: /Users/pnguyen/code/mlir-graphblas/mlir_graphblas/src/build/bin/graphblas-opt first_vec.pointers (array([0, 0], dtype=uint64),) first_vec.indices (array([3], dtype=uint64),) first_vec.values array([3]) first_vec.toarray() array([0, 0, 0, 3, 0, 0, 0, 0]) second_vec.pointers (array([0, 0], dtype=uint64),) second_vec.indices (array([3], dtype=uint64),) second_vec.values array([3]) second_vec.toarray() array([0, 0, 0, 3, 0, 0, 0, 0]) python3(28303,0x10dbd5e00) malloc: *** error for object 0x7f949622c3c0: pointer being freed was not allocated python3(28303,0x10dbd5e00) malloc: *** set a breakpoint in malloc_error_break to debug Abort trap: 6 (mlir_graphblas) pnguyen@CONDA-0584:/Users/pnguyen/code/mlir-graphblas$ ```

Both of these scripts run the same MLIR code. The only difference is that bad.py returns the same sparse vector twice. This causes an error on the Python-side for some reason. Not sure if this is a bug or if I'm doing something wrong.

CC @eriknw @jim22k

seibert commented 2 years ago

This looks like a double-free. Both Python wrappers around the data structure assume they own it, and don't realize it is a shared C++ object, so they both try to free it when their refcount goes to zero. Unfortunately, I think the only solution to this would be to have an intermediate "storage" wrapper responsible just for tracking / freeing the C++, and then to also do some kind of duplicate detection on the raw C++ memory pointers.