From 8b2470474c388699ecd49fc2243e8efa7cfb53f0 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Tue, 30 Aug 2022 10:54:24 +0530 Subject: [PATCH 1/5] Add test for dict with tuple as keys --- integration_tests/test_dict_04.py | 80 +++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 integration_tests/test_dict_04.py diff --git a/integration_tests/test_dict_04.py b/integration_tests/test_dict_04.py new file mode 100644 index 0000000000..858eebb9cb --- /dev/null +++ b/integration_tests/test_dict_04.py @@ -0,0 +1,80 @@ +from ltypes import i32, i64, f64 +from math import pi, sin, cos + +def test_dict(): + terms2poly: dict[tuple[i32, i32], i64] = {} + rtheta2coords: dict[tuple[i64, i64], tuple[f64, f64]] = {} + i: i32 + n: i64 + size: i32 = 7000 + size1: i32 + theta: f64 + r: f64 + coords: tuple[f64, f64] + eps: f64 = 1e-12 + + n = 0 + for i in range(1000, 1000 + size, 7): + terms2poly[(i, i*i)] = int(i + i*i) + + theta = float(n) * pi + r = float(i) + rtheta2coords[(int(i), n)] = (r * sin(theta), r * cos(theta)) + + n += int(1) + + size1 = size/7 + n = 0 + for i in range(1000, 1000 + size//2, 7): + assert terms2poly.pop((i, i*i)) == int(i + i*i) + + theta = float(n) * pi + r = float(i) + coords = rtheta2coords.pop((int(i), n)) + assert abs(coords[0] - r * sin(theta)) <= eps + assert abs(coords[1] - r * cos(theta)) <= eps + + size1 = size1 - 1 + assert len(terms2poly) == size1 + n += int(1) + + n = 0 + for i in range(1000, 1000 + size//2, 7): + terms2poly[(i, i*i)] = int(1 + 2*i + i*i) + + theta = float(n) * pi + r = float(i) + rtheta2coords[(int(i), n)] = (r * cos(theta), r * sin(theta)) + + n += int(1) + + n = 0 + for i in range(1000, 1000 + size//2, 7): + assert terms2poly[(i, i*i)] == (i + 1)*(i + 1) + + theta = float(n) * pi + r = float(i) + assert abs(rtheta2coords[(int(i), n)][0] - r * cos(theta)) <= eps + assert abs(rtheta2coords[(int(i), n)][1] - r * sin(theta)) <= eps + + n += int(1) + + n = 0 + for i in range(1000, 1000 + size, 7): + terms2poly[(i, i*i)] = int(1 + 2*i + i*i) + + theta = float(n) * pi + r = float(i) + rtheta2coords[(int(i), n)] = (r * cos(theta), r * sin(theta)) + n += int(1) + + n = 0 + for i in range(1000, 1000 + size, 7): + assert terms2poly[(i, i*i)] == (i + 1)*(i + 1) + + theta = float(n) * pi + r = float(i) + assert abs(r**2 - rtheta2coords[(int(i), n)][0]**2 - r**2 * sin(theta)**2) <= eps + n += int(1) + +test_dict() From 657c3d00898d97bed4885717bdbcf8360b6a8b5d Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Tue, 30 Aug 2022 10:54:58 +0530 Subject: [PATCH 2/5] Allow tuple as subscript index for dict --- src/lpython/semantics/python_ast_to_asr.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index fc60a94a22..d67322b9ce 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -2198,7 +2198,8 @@ class CommonVisitor : public AST::BaseVisitor { } else if (ASR::is_a(*type)) { throw SemanticError("unhashable type in dict: 'slice'", loc); } - } else if(AST::is_a(*m_slice)) { + } else if(AST::is_a(*m_slice) && + !ASR::is_a(*type)) { bool final_result = true; AST::Tuple_t* indices = AST::down_cast(m_slice); for( size_t i = 0; i < indices->n_elts; i++ ) { From d8b5256b6325e2fabaac1f74f5f9e35d0d6c8120 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Tue, 30 Aug 2022 10:55:22 +0530 Subject: [PATCH 3/5] Following changes have been made, 1. Add hash function for tuples 2. Set key mask correctly for optimised linear probing --- src/libasr/codegen/llvm_utils.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/libasr/codegen/llvm_utils.cpp b/src/libasr/codegen/llvm_utils.cpp index a05b2c3cbd..fe3593ad16 100644 --- a/src/libasr/codegen/llvm_utils.cpp +++ b/src/libasr/codegen/llvm_utils.cpp @@ -883,6 +883,12 @@ namespace LFortran { occupancy_ptr); llvm::Value* linear_prob_happened = builder->CreateICmpNE(key_hash, pos); + linear_prob_happened = builder->CreateOr(linear_prob_happened, + builder->CreateICmpEQ( + LLVM::CreateLoad(*builder, llvm_utils->create_ptr_gep(key_mask, key_hash)), + llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), llvm::APInt(8, 2) + )) + ); llvm::Value* set_max_2 = builder->CreateSelect(linear_prob_happened, llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), llvm::APInt(8, 2)), llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), llvm::APInt(8, 1))); @@ -941,7 +947,7 @@ namespace LFortran { } llvm::Value* LLVMDict::get_key_hash(llvm::Value* capacity, llvm::Value* key, - ASR::ttype_t* key_asr_type, llvm::Module& /*module*/) { + ASR::ttype_t* key_asr_type, llvm::Module& module) { // Write specialised hash functions for intrinsic types // This is to avoid unnecessary calls to C-runtime and do // as much as possible in LLVM directly. @@ -951,11 +957,12 @@ namespace LFortran { // We can update it later to do a better hash function // which produces lesser collisions. - return builder->CreateZExtOrTrunc( + llvm::Value* int_hash = builder->CreateZExtOrTrunc( builder->CreateSRem(key, builder->CreateZExtOrTrunc(capacity, key->getType())), capacity->getType() ); + return int_hash; } case ASR::ttypeType::Character: { // Polynomial rolling hash function for strings @@ -1022,6 +1029,18 @@ namespace LFortran { hash = builder->CreateTrunc(hash, llvm::Type::getInt32Ty(context)); return builder->CreateSRem(hash, capacity); } + case ASR::ttypeType::Tuple: { + llvm::Value* tuple_hash = llvm::ConstantInt::get(llvm::Type::getInt32Ty(context), llvm::APInt(32, 0)); + ASR::Tuple_t* asr_tuple = ASR::down_cast(key_asr_type); + for( size_t i = 0; i < asr_tuple->n_type; i++ ) { + llvm::Value* llvm_tuple_i = llvm_utils->tuple_api->read_item(key, i, + LLVM::is_llvm_struct(asr_tuple->m_type[i])); + tuple_hash = builder->CreateAdd(tuple_hash, get_key_hash(capacity, llvm_tuple_i, + asr_tuple->m_type[i], module)); + tuple_hash = builder->CreateSRem(tuple_hash, capacity); + } + return tuple_hash; + } default: { throw LCompilersException("Hashing " + ASRUtils::type_to_str_python(key_asr_type) + " isn't implemented yet."); From 8719ca0766bef25651d8d66b1b339c388ae77566 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Tue, 30 Aug 2022 10:56:37 +0530 Subject: [PATCH 4/5] Load pointer to value of a dict correct number of times --- src/libasr/codegen/asr_to_llvm.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libasr/codegen/asr_to_llvm.cpp b/src/libasr/codegen/asr_to_llvm.cpp index a3bb7327a3..f8391fbed8 100644 --- a/src/libasr/codegen/asr_to_llvm.cpp +++ b/src/libasr/codegen/asr_to_llvm.cpp @@ -1369,6 +1369,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor ptr_loads = !LLVM::is_llvm_struct(dict_type->m_key_type); this->visit_expr_wrapper(x.m_key, true); llvm::Value *key = tmp; + ptr_loads = !LLVM::is_llvm_struct(dict_type->m_value_type); this->visit_expr_wrapper(x.m_value, true); llvm::Value *value = tmp; ptr_loads = ptr_loads_copy; From 72488f9a727483394b9157d39c676aaaaad25c57 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Tue, 30 Aug 2022 10:56:51 +0530 Subject: [PATCH 5/5] Register test_dict_04 --- integration_tests/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 6ea1031cbc..cf8c51700c 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -160,6 +160,7 @@ RUN(NAME test_tuple_02 LABELS cpython llvm) RUN(NAME test_dict_01 LABELS cpython llvm) RUN(NAME test_dict_02 LABELS cpython llvm) RUN(NAME test_dict_03 LABELS cpython llvm) +RUN(NAME test_dict_04 LABELS cpython llvm) RUN(NAME modules_01 LABELS cpython llvm) RUN(NAME modules_02 LABELS cpython llvm) RUN(NAME test_math LABELS cpython llvm)