From f2500fa77846fdced96744efed949057fdf223ff Mon Sep 17 00:00:00 2001 From: kabra1110 <131529938+kabra1110@users.noreply.github.com> Date: Sun, 9 Jul 2023 20:59:09 +0530 Subject: [PATCH 01/21] Add `list` comparison (#2025) --- integration_tests/CMakeLists.txt | 1 + integration_tests/test_list_compare.py | 53 ++++ src/libasr/codegen/asr_to_llvm.cpp | 55 +++- src/libasr/codegen/llvm_utils.cpp | 294 ++++++++++++++++++++ src/libasr/codegen/llvm_utils.h | 13 + src/lpython/semantics/python_ast_to_asr.cpp | 14 +- 6 files changed, 418 insertions(+), 12 deletions(-) create mode 100644 integration_tests/test_list_compare.py diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 2a08363d08..fd6cbc095d 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -457,6 +457,7 @@ RUN(NAME test_list_repeat LABELS cpython llvm NOFAST) RUN(NAME test_list_reverse LABELS cpython llvm) RUN(NAME test_list_pop LABELS cpython llvm NOFAST) # TODO: Remove NOFAST from here. RUN(NAME test_list_pop2 LABELS cpython llvm NOFAST) # TODO: Remove NOFAST from here. +RUN(NAME test_list_compare LABELS cpython llvm) RUN(NAME test_tuple_01 LABELS cpython llvm c) RUN(NAME test_tuple_02 LABELS cpython llvm c NOFAST) RUN(NAME test_tuple_03 LABELS cpython llvm c) diff --git a/integration_tests/test_list_compare.py b/integration_tests/test_list_compare.py new file mode 100644 index 0000000000..24fcc485d5 --- /dev/null +++ b/integration_tests/test_list_compare.py @@ -0,0 +1,53 @@ +from lpython import i32, f64 + +def test_list_compare(): + l1: list[i32] = [1, 2, 3] + l2: list[i32] = [1, 2, 3, 4] + l3: list[tuple[i32, f64, str]] = [(1, 2.0, 'a'), (3, 4.0, 'b')] + l4: list[tuple[i32, f64, str]] = [(1, 3.0, 'a')] + l5: list[list[str]] = [[''], ['']] + l6: list[str] = [] + l7: list[str] = [] + t1: tuple[i32, i32] + t2: tuple[i32, i32] + i: i32 + + assert l1 < l2 and l1 <= l2 + assert not l1 > l2 and not l1 >= l2 + i = l2.pop() + i = l2.pop() + assert l2 < l1 and l1 > l2 and l1 >= l2 + assert not (l1 < l2) + + l1 = [3, 4, 5] + l2 = [1, 6, 7] + assert l1 > l2 and l1 >= l2 + assert not l1 < l2 and not l1 <= l2 + + l1 = l2 + assert l1 == l2 and l1 <= l2 and l1 >= l2 + assert not l1 < l2 and not l1 > l2 + + assert l4 > l3 and l4 >= l3 + l4[0] = l3[0] + assert l4 < l3 + + for i in range(0, 10): + if i % 2 == 0: + l6.append('a') + else: + l7.append('a') + l5[0] = l6 + l5[1] = l7 + if i % 2 == 0: + assert l5[1 - i % 2] < l5[i % 2] + assert l5[1 - i % 2] <= l5[i % 2] + assert not l5[1 - i % 2] > l5[i % 2] + assert not l5[1 - i % 2] >= l5[i % 2] + + t1 = (1, 2) + t2 = (2, 3) + assert t1 < t2 and t1 <= t2 + assert not t1 > t2 and not t1 >= t2 + +test_list_compare() \ No newline at end of file diff --git a/src/libasr/codegen/asr_to_llvm.cpp b/src/libasr/codegen/asr_to_llvm.cpp index 1170bb491f..3cd6826cad 100644 --- a/src/libasr/codegen/asr_to_llvm.cpp +++ b/src/libasr/codegen/asr_to_llvm.cpp @@ -1427,10 +1427,31 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor this->visit_expr(*x.m_right); llvm::Value* right = tmp; ptr_loads = ptr_loads_copy; - tmp = llvm_utils->is_equal_by_value(left, right, *module, - ASRUtils::expr_type(x.m_left)); - if (x.m_op == ASR::cmpopType::NotEq) { - tmp = builder->CreateNot(tmp); + + ASR::ttype_t* int32_type = ASRUtils::TYPE(ASR::make_Integer_t(al, x.base.base.loc, 4)); + + if(x.m_op == ASR::cmpopType::Eq || x.m_op == ASR::cmpopType::NotEq) { + tmp = llvm_utils->is_equal_by_value(left, right, *module, + ASRUtils::expr_type(x.m_left)); + if (x.m_op == ASR::cmpopType::NotEq) { + tmp = builder->CreateNot(tmp); + } + } + else if(x.m_op == ASR::cmpopType::Lt) { + tmp = llvm_utils->is_ineq_by_value(left, right, *module, + ASRUtils::expr_type(x.m_left), 0, int32_type); + } + else if(x.m_op == ASR::cmpopType::LtE) { + tmp = llvm_utils->is_ineq_by_value(left, right, *module, + ASRUtils::expr_type(x.m_left), 1, int32_type); + } + else if(x.m_op == ASR::cmpopType::Gt) { + tmp = llvm_utils->is_ineq_by_value(left, right, *module, + ASRUtils::expr_type(x.m_left), 2, int32_type); + } + else if(x.m_op == ASR::cmpopType::GtE) { + tmp = llvm_utils->is_ineq_by_value(left, right, *module, + ASRUtils::expr_type(x.m_left), 3, int32_type); } } @@ -1761,10 +1782,28 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor this->visit_expr(*x.m_right); llvm::Value* right = tmp; ptr_loads = ptr_loads_copy; - tmp = llvm_utils->is_equal_by_value(left, right, *module, - ASRUtils::expr_type(x.m_left)); - if (x.m_op == ASR::cmpopType::NotEq) { - tmp = builder->CreateNot(tmp); + if(x.m_op == ASR::cmpopType::Eq || x.m_op == ASR::cmpopType::NotEq) { + tmp = llvm_utils->is_equal_by_value(left, right, *module, + ASRUtils::expr_type(x.m_left)); + if (x.m_op == ASR::cmpopType::NotEq) { + tmp = builder->CreateNot(tmp); + } + } + else if(x.m_op == ASR::cmpopType::Lt) { + tmp = llvm_utils->is_ineq_by_value(left, right, *module, + ASRUtils::expr_type(x.m_left), 0); + } + else if(x.m_op == ASR::cmpopType::LtE) { + tmp = llvm_utils->is_ineq_by_value(left, right, *module, + ASRUtils::expr_type(x.m_left), 1); + } + else if(x.m_op == ASR::cmpopType::Gt) { + tmp = llvm_utils->is_ineq_by_value(left, right, *module, + ASRUtils::expr_type(x.m_left), 2); + } + else if(x.m_op == ASR::cmpopType::GtE) { + tmp = llvm_utils->is_ineq_by_value(left, right, *module, + ASRUtils::expr_type(x.m_left), 3); } } diff --git a/src/libasr/codegen/llvm_utils.cpp b/src/libasr/codegen/llvm_utils.cpp index 0599e97130..4c2a241a84 100644 --- a/src/libasr/codegen/llvm_utils.cpp +++ b/src/libasr/codegen/llvm_utils.cpp @@ -1452,6 +1452,153 @@ namespace LCompilers { } } + llvm::Value* LLVMUtils::is_ineq_by_value(llvm::Value* left, llvm::Value* right, + llvm::Module& module, ASR::ttype_t* asr_type, + int8_t overload_id, ASR::ttype_t* int32_type) { + /** + * overloads: + * 0 < + * 1 <= + * 2 > + * 3 >= + */ + llvm::CmpInst::Predicate pred; + + switch( asr_type->type ) { + case ASR::ttypeType::Integer: + case ASR::ttypeType::Logical: { + switch( overload_id ) { + case 0: { + pred = llvm::CmpInst::Predicate::ICMP_SLT; + break; + } + case 1: { + pred = llvm::CmpInst::Predicate::ICMP_SLE; + break; + } + case 2: { + pred = llvm::CmpInst::Predicate::ICMP_SGT; + break; + } + case 3: { + pred = llvm::CmpInst::Predicate::ICMP_SGE; + break; + } + default: { + // can exit with error + } + } + return builder->CreateCmp(pred, left, right); + } + case ASR::ttypeType::Real: { + switch( overload_id ) { + case 0: { + pred = llvm::CmpInst::Predicate::FCMP_OLT; + break; + } + case 1: { + pred = llvm::CmpInst::Predicate::FCMP_OLE; + break; + } + case 2: { + pred = llvm::CmpInst::Predicate::FCMP_OGT; + break; + } + case 3: { + pred = llvm::CmpInst::Predicate::FCMP_OGE; + break; + } + default: { + // can exit with error + } + } + return builder->CreateCmp(pred, left, right); + } + case ASR::ttypeType::Character: { + if( !are_iterators_set ) { + str_cmp_itr = builder->CreateAlloca(llvm::Type::getInt32Ty(context), nullptr); + } + llvm::Value* null_char = llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), + llvm::APInt(8, '\0')); + llvm::Value* idx = str_cmp_itr; + LLVM::CreateStore(*builder, + llvm::ConstantInt::get(llvm::Type::getInt32Ty(context), llvm::APInt(32, 0)), + idx); + llvm::BasicBlock *loophead = llvm::BasicBlock::Create(context, "loop.head"); + llvm::BasicBlock *loopbody = llvm::BasicBlock::Create(context, "loop.body"); + llvm::BasicBlock *loopend = llvm::BasicBlock::Create(context, "loop.end"); + + // head + start_new_block(loophead); + { + llvm::Value* i = LLVM::CreateLoad(*builder, idx); + llvm::Value* l = LLVM::CreateLoad(*builder, create_ptr_gep(left, i)); + llvm::Value* r = LLVM::CreateLoad(*builder, create_ptr_gep(right, i)); + llvm::Value *cond = builder->CreateAnd( + builder->CreateICmpNE(l, null_char), + builder->CreateICmpNE(r, null_char) + ); + switch( overload_id ) { + case 0: { + pred = llvm::CmpInst::Predicate::ICMP_ULT; + break; + } + case 1: { + pred = llvm::CmpInst::Predicate::ICMP_ULE; + break; + } + case 2: { + pred = llvm::CmpInst::Predicate::ICMP_UGT; + break; + } + case 3: { + pred = llvm::CmpInst::Predicate::ICMP_UGE; + break; + } + default: { + // can exit with error + } + } + cond = builder->CreateAnd(cond, builder->CreateCmp(pred, l, r)); + builder->CreateCondBr(cond, loopbody, loopend); + } + + // body + start_new_block(loopbody); + { + llvm::Value* i = LLVM::CreateLoad(*builder, idx); + i = builder->CreateAdd(i, llvm::ConstantInt::get(llvm::Type::getInt32Ty(context), + llvm::APInt(32, 1))); + LLVM::CreateStore(*builder, i, idx); + } + + builder->CreateBr(loophead); + + // end + start_new_block(loopend); + llvm::Value* i = LLVM::CreateLoad(*builder, idx); + llvm::Value* l = LLVM::CreateLoad(*builder, create_ptr_gep(left, i)); + llvm::Value* r = LLVM::CreateLoad(*builder, create_ptr_gep(right, i)); + return builder->CreateICmpULT(l, r); + } + case ASR::ttypeType::Tuple: { + ASR::Tuple_t* tuple_type = ASR::down_cast(asr_type); + return tuple_api->check_tuple_inequality(left, right, tuple_type, context, + builder, module, overload_id); + } + case ASR::ttypeType::List: { + ASR::List_t* list_type = ASR::down_cast(asr_type); + return list_api->check_list_inequality(left, right, list_type->m_type, + context, builder, module, + overload_id, int32_type); + } + default: { + throw LCompilersException("LLVMUtils::is_equal_by_value isn't implemented for " + + ASRUtils::type_to_str_python(asr_type)); + } + } + } + void LLVMUtils::deepcopy(llvm::Value* src, llvm::Value* dest, ASR::ttype_t* asr_type, llvm::Module* module, std::map>& name2memidx) { @@ -4276,6 +4423,101 @@ namespace LCompilers { return LLVM::CreateLoad(*builder, is_equal); } + llvm::Value* LLVMList::check_list_inequality(llvm::Value* l1, llvm::Value* l2, + ASR::ttype_t* item_type, + llvm::LLVMContext& context, + llvm::IRBuilder<>* builder, + llvm::Module& module, int8_t overload_id, + ASR::ttype_t* int32_type) { + /** + * Equivalent in C++ + * + * equality_holds = 1; + * inequality_holds = 0; + * i = 0; + * + * while( i < a_len && i < b_len && equality_holds ) { + * equality_holds &= (a[i] == b[i]); + * inequality_holds |= (a[i] op b[i]); + * i++; + * } + * + * if( (i == a_len || i == b_len) && equality_holds ) { + * inequality_holds = a_len op b_len; + * } + * + */ + + llvm::AllocaInst *equality_holds = builder->CreateAlloca( + llvm::Type::getInt1Ty(context), nullptr); + LLVM::CreateStore(*builder, llvm::ConstantInt::get(context, llvm::APInt(1, 1)), + equality_holds); + llvm::AllocaInst *inequality_holds = builder->CreateAlloca( + llvm::Type::getInt1Ty(context), nullptr); + LLVM::CreateStore(*builder, llvm::ConstantInt::get(context, llvm::APInt(1, 0)), + inequality_holds); + + llvm::Value *a_len = llvm_utils->list_api->len(l1); + llvm::Value *b_len = llvm_utils->list_api->len(l2); + llvm::AllocaInst *idx = builder->CreateAlloca(llvm::Type::getInt32Ty(context), nullptr); + LLVM::CreateStore(*builder, llvm::ConstantInt::get( + context, llvm::APInt(32, 0)), idx); + llvm::BasicBlock *loophead = llvm::BasicBlock::Create(context, "loop.head"); + llvm::BasicBlock *loopbody = llvm::BasicBlock::Create(context, "loop.body"); + llvm::BasicBlock *loopend = llvm::BasicBlock::Create(context, "loop.end"); + + // head + llvm_utils->start_new_block(loophead); + { + llvm::Value* i = LLVM::CreateLoad(*builder, idx); + llvm::Value* cnd = builder->CreateICmpSLT(i, a_len); + cnd = builder->CreateAnd(cnd, builder->CreateICmpSLT(i, b_len)); + cnd = builder->CreateAnd(cnd, LLVM::CreateLoad(*builder, equality_holds)); + builder->CreateCondBr(cnd, loopbody, loopend); + } + + // body + llvm_utils->start_new_block(loopbody); + { + llvm::Value* i = LLVM::CreateLoad(*builder, idx); + llvm::Value* left_arg = llvm_utils->list_api->read_item(l1, i, + false, module, LLVM::is_llvm_struct(item_type)); + llvm::Value* right_arg = llvm_utils->list_api->read_item(l2, i, + false, module, LLVM::is_llvm_struct(item_type)); + llvm::Value* res = llvm_utils->is_ineq_by_value(left_arg, right_arg, module, + item_type, overload_id); + res = builder->CreateOr(LLVM::CreateLoad(*builder, inequality_holds), res); + LLVM::CreateStore(*builder, res, inequality_holds); + res = llvm_utils->is_equal_by_value(left_arg, right_arg, module, + item_type); + res = builder->CreateAnd(LLVM::CreateLoad(*builder, equality_holds), res); + LLVM::CreateStore(*builder, res, equality_holds); + i = builder->CreateAdd(i, llvm::ConstantInt::get(llvm::Type::getInt32Ty(context), + llvm::APInt(32, 1))); + LLVM::CreateStore(*builder, i, idx); + } + + builder->CreateBr(loophead); + + // end + llvm_utils->start_new_block(loopend); + + llvm::Value* cond = builder->CreateICmpEQ(LLVM::CreateLoad(*builder, idx), + a_len); + cond = builder->CreateOr(cond, builder->CreateICmpEQ( + LLVM::CreateLoad(*builder, idx), b_len)); + cond = builder->CreateAnd(cond, LLVM::CreateLoad(*builder, equality_holds)); + llvm_utils->create_if_else(cond, [&]() { + LLVM::CreateStore(*builder, llvm_utils->is_ineq_by_value(a_len, b_len, + module, int32_type, overload_id), inequality_holds); + }, []() { + // LLVM::CreateStore(*builder, llvm::ConstantInt::get( + // context, llvm::APInt(1, 0)), inequality_holds); + }); + + return LLVM::CreateLoad(*builder, inequality_holds); + } + void LLVMList::list_repeat_copy(llvm::Value* repeat_list, llvm::Value* init_list, llvm::Value* num_times, llvm::Value* init_list_len, llvm::Module* module) { @@ -4421,6 +4663,58 @@ namespace LCompilers { return is_equal; } + llvm::Value* LLVMTuple::check_tuple_inequality(llvm::Value* t1, llvm::Value* t2, + ASR::Tuple_t* tuple_type, + llvm::LLVMContext& context, + llvm::IRBuilder<>* builder, + llvm::Module& module, int8_t overload_id) { + /** + * Equivalent in C++ + * + * equality_holds = 1; + * inequality_holds = 0; + * i = 0; + * + * // owing to compile-time access of indices, + * // loop is unrolled into multiple if statements + * while( i < a_len && equality_holds ) { + * inequality_holds |= (a[i] op b[i]); + * equality_holds &= (a[i] == b[i]); + * i++; + * } + * + * return inequality_holds; + * + */ + + llvm::AllocaInst *equality_holds = builder->CreateAlloca( + llvm::Type::getInt1Ty(context), nullptr); + LLVM::CreateStore(*builder, llvm::ConstantInt::get(context, llvm::APInt(1, 1)), + equality_holds); + llvm::AllocaInst *inequality_holds = builder->CreateAlloca( + llvm::Type::getInt1Ty(context), nullptr); + LLVM::CreateStore(*builder, llvm::ConstantInt::get(context, llvm::APInt(1, 0)), + inequality_holds); + + for( size_t i = 0; i < tuple_type->n_type; i++ ) { + llvm_utils->create_if_else(LLVM::CreateLoad(*builder, equality_holds), [&]() { + llvm::Value* t1i = llvm_utils->tuple_api->read_item(t1, i, LLVM::is_llvm_struct( + tuple_type->m_type[i])); + llvm::Value* t2i = llvm_utils->tuple_api->read_item(t2, i, LLVM::is_llvm_struct( + tuple_type->m_type[i])); + llvm::Value* res = llvm_utils->is_ineq_by_value(t1i, t2i, module, + tuple_type->m_type[i], overload_id); + res = builder->CreateOr(LLVM::CreateLoad(*builder, inequality_holds), res); + LLVM::CreateStore(*builder, res, inequality_holds); + res = llvm_utils->is_equal_by_value(t1i, t2i, module, tuple_type->m_type[i]); + res = builder->CreateAnd(LLVM::CreateLoad(*builder, equality_holds), res); + LLVM::CreateStore(*builder, res, equality_holds); + }, [](){}); + } + + return LLVM::CreateLoad(*builder, inequality_holds); + } + void LLVMTuple::concat(llvm::Value* t1, llvm::Value* t2, ASR::Tuple_t* tuple_type_1, ASR::Tuple_t* tuple_type_2, llvm::Value* concat_tuple, ASR::Tuple_t* concat_tuple_type, llvm::Module& module, diff --git a/src/libasr/codegen/llvm_utils.h b/src/libasr/codegen/llvm_utils.h index fcdfaddeb1..54bbaef366 100644 --- a/src/libasr/codegen/llvm_utils.h +++ b/src/libasr/codegen/llvm_utils.h @@ -220,6 +220,10 @@ namespace LCompilers { llvm::Value* is_equal_by_value(llvm::Value* left, llvm::Value* right, llvm::Module& module, ASR::ttype_t* asr_type); + llvm::Value* is_ineq_by_value(llvm::Value* left, llvm::Value* right, + llvm::Module& module, ASR::ttype_t* asr_type, + int8_t overload_id, ASR::ttype_t* int32_type=nullptr); + void set_iterators(); void reset_iterators(); @@ -413,6 +417,11 @@ namespace LCompilers { llvm::Value* check_list_equality(llvm::Value* l1, llvm::Value* l2, ASR::ttype_t *item_type, llvm::LLVMContext& context, llvm::IRBuilder<>* builder, llvm::Module& module); + llvm::Value* check_list_inequality(llvm::Value* l1, llvm::Value* l2, + ASR::ttype_t *item_type, llvm::LLVMContext& context, + llvm::IRBuilder<>* builder, llvm::Module& module, + int8_t overload_id, ASR::ttype_t* int32_type=nullptr); + void list_repeat_copy(llvm::Value* repeat_list, llvm::Value* init_list, llvm::Value* num_times, llvm::Value* init_list_len, llvm::Module* module); @@ -454,6 +463,10 @@ namespace LCompilers { ASR::Tuple_t* tuple_type, llvm::LLVMContext& context, llvm::IRBuilder<>* builder, llvm::Module& module); + llvm::Value* check_tuple_inequality(llvm::Value* t1, llvm::Value* t2, + ASR::Tuple_t* tuple_type, llvm::LLVMContext& context, + llvm::IRBuilder<>* builder, llvm::Module& module, int8_t overload_id); + void concat(llvm::Value* t1, llvm::Value* t2, ASR::Tuple_t* tuple_type_1, ASR::Tuple_t* tuple_type_2, llvm::Value* concat_tuple, ASR::Tuple_t* concat_tuple_type, llvm::Module& module, diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index 2f876f394f..1611d23d04 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -6303,14 +6303,20 @@ class BodyVisitor : public CommonVisitor { tmp = ASR::make_StringCompare_t(al, x.base.base.loc, left, asr_op, right, type, value); } else if (ASR::is_a(*dest_type)) { - if (asr_op != ASR::cmpopType::Eq && asr_op != ASR::cmpopType::NotEq) { - throw SemanticError("Only Equal and Not-equal operators are supported for Tuples", + if (asr_op != ASR::cmpopType::Eq && asr_op != ASR::cmpopType::NotEq + && asr_op != ASR::cmpopType::Lt && asr_op != ASR::cmpopType::LtE + && asr_op != ASR::cmpopType::Gt && asr_op != ASR::cmpopType::GtE) { + throw SemanticError("Only ==, !=, <, <=, >, >= operators " + "are supported for Tuples", x.base.base.loc); } tmp = ASR::make_TupleCompare_t(al, x.base.base.loc, left, asr_op, right, type, value); } else if (ASR::is_a(*dest_type)) { - if (asr_op != ASR::cmpopType::Eq && asr_op != ASR::cmpopType::NotEq) { - throw SemanticError("Only Equal and Not-equal operators are supported for Tuples", + if (asr_op != ASR::cmpopType::Eq && asr_op != ASR::cmpopType::NotEq + && asr_op != ASR::cmpopType::Lt && asr_op != ASR::cmpopType::LtE + && asr_op != ASR::cmpopType::Gt && asr_op != ASR::cmpopType::GtE) { + throw SemanticError("Only ==, !=, <, <=, >, >= operators " + "are supported for Lists", x.base.base.loc); } tmp = ASR::make_ListCompare_t(al, x.base.base.loc, left, asr_op, right, type, value); From de65afc0240d18cfcd0ff37378f80ac883a474b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Sun, 9 Jul 2023 10:08:56 -0600 Subject: [PATCH 02/21] Add a test for a struct from a C pointer --- integration_tests/CMakeLists.txt | 1 + integration_tests/bindc_10.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 integration_tests/bindc_10.py diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index fd6cbc095d..ebff574f6d 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -374,6 +374,7 @@ RUN(NAME bindc_04 LABELS llvm c NOFAST) RUN(NAME bindc_07 LABELS cpython llvm c NOFAST) RUN(NAME bindc_08 LABELS cpython llvm c) RUN(NAME bindc_09 LABELS cpython llvm c) +RUN(NAME bindc_10 LABELS cpython llvm c) RUN(NAME exit_01 LABELS cpython llvm c NOFAST) RUN(NAME exit_02 FAIL LABELS cpython llvm c NOFAST) RUN(NAME exit_03 LABELS cpython llvm c wasm wasm_x86 wasm_x64) diff --git a/integration_tests/bindc_10.py b/integration_tests/bindc_10.py new file mode 100644 index 0000000000..934eacfd43 --- /dev/null +++ b/integration_tests/bindc_10.py @@ -0,0 +1,32 @@ +from lpython import (i64, i16, CPtr, c_p_pointer, Pointer, sizeof, packed, + dataclass, ccallable, ccall, i32) + +@ccall +def _lfortran_malloc(size: i32) -> CPtr: + pass + + +def alloc(buf_size:i64) -> CPtr: + return _lfortran_malloc(i32(buf_size)) + + +@ccallable +@packed +@dataclass +class S: + a: i16 + b: i64 + + +def main(): + p1: CPtr = alloc(sizeof(S)) + print(p1) + p2: Pointer[S] = c_p_pointer(p1, S) + p2.a = i16(5) + p2.b = i64(4) + print(p2.a, p2.b) + assert p2.a == i16(5) + assert p2.b == i64(4) + + +main() From 662935a56ebe16c83a55c7fee334cf69212d7d14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Sun, 9 Jul 2023 10:12:55 -0600 Subject: [PATCH 03/21] Add a CPython test for using NumPy to allocate --- integration_tests/CMakeLists.txt | 1 + integration_tests/bindc_11.py | 34 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 integration_tests/bindc_11.py diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index ebff574f6d..b80ca30a83 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -375,6 +375,7 @@ RUN(NAME bindc_07 LABELS cpython llvm c NOFAST) RUN(NAME bindc_08 LABELS cpython llvm c) RUN(NAME bindc_09 LABELS cpython llvm c) RUN(NAME bindc_10 LABELS cpython llvm c) +RUN(NAME bindc_11 LABELS cpython) # This is CPython test only RUN(NAME exit_01 LABELS cpython llvm c NOFAST) RUN(NAME exit_02 FAIL LABELS cpython llvm c NOFAST) RUN(NAME exit_03 LABELS cpython llvm c wasm wasm_x86 wasm_x64) diff --git a/integration_tests/bindc_11.py b/integration_tests/bindc_11.py new file mode 100644 index 0000000000..c2e81c9a0a --- /dev/null +++ b/integration_tests/bindc_11.py @@ -0,0 +1,34 @@ +import numpy, ctypes +from lpython import (i64, i16, CPtr, c_p_pointer, Pointer, sizeof, packed, + dataclass, ccallable, ccall, i32) + +global_arrays = [] + + +def alloc(buf_size:i64) -> CPtr: + xs = numpy.empty(buf_size, dtype=numpy.uint8) + global_arrays.append(xs) + p = ctypes.c_void_p(xs.ctypes.data) + return ctypes.cast(p.value, ctypes.c_void_p) + + +@ccallable +@packed +@dataclass +class S: + a: i16 + b: i64 + + +def main(): + p1: CPtr = alloc(sizeof(S)) + print(p1) + p2: Pointer[S] = c_p_pointer(p1, S) + p2.a = i16(5) + p2.b = i64(4) + print(p2.a, p2.b) + assert p2.a == i16(5) + assert p2.b == i64(4) + + +main() From 7701d26b1d21d768553515ef875fdbc90e4c5bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Sun, 9 Jul 2023 10:50:46 -0600 Subject: [PATCH 04/21] Disable the --fast test --- integration_tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index b80ca30a83..72ecb5d702 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -374,7 +374,7 @@ RUN(NAME bindc_04 LABELS llvm c NOFAST) RUN(NAME bindc_07 LABELS cpython llvm c NOFAST) RUN(NAME bindc_08 LABELS cpython llvm c) RUN(NAME bindc_09 LABELS cpython llvm c) -RUN(NAME bindc_10 LABELS cpython llvm c) +RUN(NAME bindc_10 LABELS cpython llvm c NOFAST) RUN(NAME bindc_11 LABELS cpython) # This is CPython test only RUN(NAME exit_01 LABELS cpython llvm c NOFAST) RUN(NAME exit_02 FAIL LABELS cpython llvm c NOFAST) From 91de6046801102aa9d8c71fe522eec8753a14828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Sun, 9 Jul 2023 11:40:30 -0600 Subject: [PATCH 05/21] Add a test for an error message for bindc --- tests/errors/bindc_10e.py | 31 +++++++++++++++++++++++++++++++ tests/tests.toml | 4 ++++ 2 files changed, 35 insertions(+) create mode 100644 tests/errors/bindc_10e.py diff --git a/tests/errors/bindc_10e.py b/tests/errors/bindc_10e.py new file mode 100644 index 0000000000..bdbff7df6c --- /dev/null +++ b/tests/errors/bindc_10e.py @@ -0,0 +1,31 @@ +from lpython import (i64, i16, CPtr, c_p_pointer, Pointer, sizeof, packed, + dataclass, ccallable, ccall, i32) + +@ccall +def _lfortran_malloc(size: i32) -> CPtr: + pass + + +def alloc(buf_size:i64) -> CPtr: + return _lfortran_malloc(i32(buf_size)) + + +@packed +@dataclass +class S: + a: i16 + b: i64 + + +def main(): + p1: CPtr = alloc(sizeof(S)) + print(p1) + p2: Pointer[S] = c_p_pointer(p1, S) + p2.a = i16(5) + p2.b = i64(4) + print(p2.a, p2.b) + assert p2.a == i16(5) + assert p2.b == i64(4) + + +main() diff --git a/tests/tests.toml b/tests/tests.toml index 057269e597..05734f688c 100644 --- a/tests/tests.toml +++ b/tests/tests.toml @@ -1151,6 +1151,10 @@ asr = true filename = "errors/bindc_04.py" asr = true +[[test]] +filename = "errors/bindc_10e.py" +asr = true + [[test]] filename = "errors/cptr_01.py" asr = true From 27c5c5cb1e3928fb211670f4d652fcea9273e144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Sun, 9 Jul 2023 11:57:41 -0600 Subject: [PATCH 06/21] Add error message for non C interoperable structs --- src/lpython/semantics/python_ast_to_asr.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index 1611d23d04..ade3a2f39d 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -2734,6 +2734,21 @@ class CommonVisitor : public AST::BaseVisitor { ); throw SemanticAbort(); } + if (ASR::is_a(*asr_alloc_type)) { + ASR::StructType_t *st = ASR::down_cast(ASR::down_cast(asr_alloc_type)->m_derived_type); + if (st->m_abi != ASR::abiType::BindC) { + diag.add(diag::Diagnostic( + "The struct in c_p_pointer must be C interoperable", + diag::Level::Error, diag::Stage::Semantic, { + diag::Label("not C interoperable", + {asr_alloc_type->base.loc}), + diag::Label("help: add the @ccallable decorator to this struct to make it C interoperable", + {st->base.base.loc}, false) + }) + ); + throw SemanticAbort(); + } + } fill_shape_and_lower_bound_for_CPtrToPointer(); return ASR::make_CPtrToPointer_t(al, loc, cptr, pptr, target_shape, lower_bounds); } From fb00846aebfae72358d32323fee57b732cfaedbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Sun, 9 Jul 2023 12:11:27 -0600 Subject: [PATCH 07/21] Update struct_13 --- integration_tests/structs_13.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/integration_tests/structs_13.py b/integration_tests/structs_13.py index 651518fc05..9806fa1ac9 100644 --- a/integration_tests/structs_13.py +++ b/integration_tests/structs_13.py @@ -1,6 +1,8 @@ -from lpython import i32, i16, i64, CPtr, dataclass, ccall, Pointer, c_p_pointer, sizeof +from lpython import (i32, i16, i64, CPtr, dataclass, ccall, Pointer, + c_p_pointer, sizeof, ccallable) from numpy import array +@ccallable @dataclass class A: x: i32 From c4d1a648286a207771bf57cac56a644e80fa5ca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Sun, 9 Jul 2023 12:14:16 -0600 Subject: [PATCH 08/21] Fix struct_02 test --- integration_tests/CMakeLists.txt | 1 + integration_tests/bindc_09.py | 1 + integration_tests/structs_02.py | 2 -- integration_tests/structs_02b.py | 42 ++++++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 integration_tests/structs_02b.py diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 72ecb5d702..3b8d04c1f2 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -568,6 +568,7 @@ RUN(NAME test_bool_binop LABELS cpython llvm c) RUN(NAME test_issue_518 LABELS cpython llvm c NOFAST) RUN(NAME structs_01 LABELS cpython llvm c) RUN(NAME structs_02 LABELS cpython llvm c) +RUN(NAME structs_02b LABELS cpython llvm c) RUN(NAME structs_03 LABELS llvm c) RUN(NAME structs_04 LABELS cpython llvm c) RUN(NAME structs_05 LABELS cpython llvm c) diff --git a/integration_tests/bindc_09.py b/integration_tests/bindc_09.py index 18fdaad29e..41e0b7e42d 100644 --- a/integration_tests/bindc_09.py +++ b/integration_tests/bindc_09.py @@ -8,6 +8,7 @@ class Value(Enum): ONE: i32 = 1 FIVE: i32 = 5 +@ccallable @dataclass class Foo: value: Value diff --git a/integration_tests/structs_02.py b/integration_tests/structs_02.py index f3325d6a9a..0e99458c34 100644 --- a/integration_tests/structs_02.py +++ b/integration_tests/structs_02.py @@ -17,8 +17,6 @@ def f(a: CPtr) -> None: y = a2.y assert x == 3 assert f64(y) == 3.25 - a2 = c_p_pointer(a, A) - print(a, a2, pointer(a1)) def g(): b: CPtr = empty_c_void_p() diff --git a/integration_tests/structs_02b.py b/integration_tests/structs_02b.py new file mode 100644 index 0000000000..ef675f6bb1 --- /dev/null +++ b/integration_tests/structs_02b.py @@ -0,0 +1,42 @@ +from lpython import (i32, f32, dataclass, CPtr, Pointer, c_p_pointer, pointer, + ccallable, empty_c_void_p, f64, ccall, sizeof, i64) + +@ccall +def _lfortran_malloc(size: i32) -> CPtr: + pass + +def alloc(buf_size:i64) -> CPtr: + return _lfortran_malloc(i32(buf_size)) + +@ccallable +@dataclass +class A: + x: i32 + y: f32 + +@ccallable +def f(a: CPtr) -> None: + x: i32 + y: f32 + a1: A = A(3, f32(3.25)) + a2: Pointer[A] + a2 = pointer(a1) + print(a2, pointer(a1)) + x = a2.x + y = a2.y + assert x == 3 + assert f64(y) == 3.25 + a2 = c_p_pointer(a, A) + print(a, a2, pointer(a1)) + print(a2.x, a2.y) + assert a2.x == 5 + assert a2.y == f32(6.0) + +def g(): + b: CPtr = alloc(sizeof(A)) + b2: Pointer[A] = c_p_pointer(b, A) + b2.x = 5 + b2.y = f32(6) + f(b) + +g() From d322907c6a2dcfc2b0f32b46a9df20f517acdccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Sun, 9 Jul 2023 12:48:09 -0600 Subject: [PATCH 09/21] Fix bindc_09 --- integration_tests/CMakeLists.txt | 1 + integration_tests/bindc_09.py | 10 +++----- integration_tests/bindc_09b.py | 44 ++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 integration_tests/bindc_09b.py diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 3b8d04c1f2..9b54269f3c 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -374,6 +374,7 @@ RUN(NAME bindc_04 LABELS llvm c NOFAST) RUN(NAME bindc_07 LABELS cpython llvm c NOFAST) RUN(NAME bindc_08 LABELS cpython llvm c) RUN(NAME bindc_09 LABELS cpython llvm c) +RUN(NAME bindc_09b LABELS cpython llvm c) RUN(NAME bindc_10 LABELS cpython llvm c NOFAST) RUN(NAME bindc_11 LABELS cpython) # This is CPython test only RUN(NAME exit_01 LABELS cpython llvm c NOFAST) diff --git a/integration_tests/bindc_09.py b/integration_tests/bindc_09.py index 41e0b7e42d..956b56b658 100644 --- a/integration_tests/bindc_09.py +++ b/integration_tests/bindc_09.py @@ -1,6 +1,7 @@ from enum import Enum -from lpython import CPtr, c_p_pointer, p_c_pointer, dataclass, empty_c_void_p, pointer, Pointer, i32, ccallable +from lpython import (CPtr, c_p_pointer, p_c_pointer, dataclass, empty_c_void_p, + pointer, Pointer, i32, ccallable, InOut) class Value(Enum): TEN: i32 = 10 @@ -8,7 +9,6 @@ class Value(Enum): ONE: i32 = 1 FIVE: i32 = 5 -@ccallable @dataclass class Foo: value: Value @@ -18,8 +18,7 @@ class Foo: class FooC: value: Value -def bar(foo_ptr: CPtr) -> None: - foo: Pointer[Foo] = c_p_pointer(foo_ptr, Foo) +def bar(foo: InOut[Foo]) -> None: foo.value = Value.FIVE def barc(foo_ptr: CPtr) -> None: @@ -31,8 +30,7 @@ def main() -> None: fooc: FooC = FooC(Value.TWO) foo_ptr: CPtr = empty_c_void_p() - p_c_pointer(pointer(foo), foo_ptr) - bar(foo_ptr) + bar(foo) print(foo.value, foo.value.name) assert foo.value == Value.FIVE diff --git a/integration_tests/bindc_09b.py b/integration_tests/bindc_09b.py new file mode 100644 index 0000000000..58be1e768b --- /dev/null +++ b/integration_tests/bindc_09b.py @@ -0,0 +1,44 @@ +from enum import Enum + +from lpython import CPtr, c_p_pointer, p_c_pointer, dataclass, empty_c_void_p, pointer, Pointer, i32, ccallable + +class Value(Enum): + TEN: i32 = 10 + TWO: i32 = 2 + ONE: i32 = 1 + FIVE: i32 = 5 + +@ccallable +@dataclass +class Foo: + value: Value + +@ccallable +@dataclass +class FooC: + value: Value + +def bar(foo_ptr: CPtr) -> None: + foo: Pointer[Foo] = c_p_pointer(foo_ptr, Foo) + foo.value = Value.FIVE + +def barc(foo_ptr: CPtr) -> None: + foo: Pointer[FooC] = c_p_pointer(foo_ptr, FooC) + foo.value = Value.ONE + +def main() -> None: + foo: Foo = Foo(Value.TEN) + fooc: FooC = FooC(Value.TWO) + foo_ptr: CPtr = empty_c_void_p() + + p_c_pointer(pointer(foo), foo_ptr) + bar(foo_ptr) + print(foo.value) + assert foo.value == Value.FIVE.value + + p_c_pointer(pointer(fooc), foo_ptr) + barc(foo_ptr) + print(fooc.value) + assert fooc.value == Value.ONE.value + +main() From d006b0626021e1b07b13150e278d25579cae958f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Sun, 9 Jul 2023 12:59:36 -0600 Subject: [PATCH 10/21] Comment out non-working code --- integration_tests/structs_02b.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/integration_tests/structs_02b.py b/integration_tests/structs_02b.py index ef675f6bb1..ed061ff6ce 100644 --- a/integration_tests/structs_02b.py +++ b/integration_tests/structs_02b.py @@ -22,10 +22,11 @@ def f(a: CPtr) -> None: a2: Pointer[A] a2 = pointer(a1) print(a2, pointer(a1)) - x = a2.x - y = a2.y - assert x == 3 - assert f64(y) == 3.25 + # TODO: does not work: + #x = a2.x + #y = a2.y + #assert x == 3 + #assert f64(y) == 3.25 a2 = c_p_pointer(a, A) print(a, a2, pointer(a1)) print(a2.x, a2.y) From 235538543b54e37203b8fccfe71cf305da4c186c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Sun, 9 Jul 2023 13:00:19 -0600 Subject: [PATCH 11/21] Update reference tests --- tests/reference/asr-bindc_10e-8b10394.json | 13 +++++++++++ tests/reference/asr-bindc_10e-8b10394.stderr | 12 ++++++++++ tests/reference/asr-structs_02-2ab459a.json | 4 ++-- tests/reference/asr-structs_02-2ab459a.stdout | 22 ------------------- 4 files changed, 27 insertions(+), 24 deletions(-) create mode 100644 tests/reference/asr-bindc_10e-8b10394.json create mode 100644 tests/reference/asr-bindc_10e-8b10394.stderr diff --git a/tests/reference/asr-bindc_10e-8b10394.json b/tests/reference/asr-bindc_10e-8b10394.json new file mode 100644 index 0000000000..9fdf2f9acd --- /dev/null +++ b/tests/reference/asr-bindc_10e-8b10394.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-bindc_10e-8b10394", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/bindc_10e.py", + "infile_hash": "36d1b5d366716d6a601db544fe8ff32aba76518181e71a98a4e6500c", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-bindc_10e-8b10394.stderr", + "stderr_hash": "7686dd8c9b718548d301e28d39cd15a85b00030172c5658b38a75af5", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-bindc_10e-8b10394.stderr b/tests/reference/asr-bindc_10e-8b10394.stderr new file mode 100644 index 0000000000..3639b97f68 --- /dev/null +++ b/tests/reference/asr-bindc_10e-8b10394.stderr @@ -0,0 +1,12 @@ +semantic error: The struct in c_p_pointer must be C interoperable + --> tests/errors/bindc_10e.py:23:38 + | +23 | p2: Pointer[S] = c_p_pointer(p1, S) + | ^ not C interoperable + | +15 | class S: + | ~~~~~~~~... +... + | +17 | b: i64 + | ...~~~~~~~~~~ help: add the @ccallable decorator to this struct to make it C interoperable diff --git a/tests/reference/asr-structs_02-2ab459a.json b/tests/reference/asr-structs_02-2ab459a.json index 2056a9f832..4d7980c0f1 100644 --- a/tests/reference/asr-structs_02-2ab459a.json +++ b/tests/reference/asr-structs_02-2ab459a.json @@ -2,11 +2,11 @@ "basename": "asr-structs_02-2ab459a", "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", "infile": "tests/../integration_tests/structs_02.py", - "infile_hash": "6d54aa7c2bb850cbce2c0add7b77f9f72c9323162ae080c7eef4867a", + "infile_hash": "281bc75fb743f18fb6f011b278d7ab8103cc688f5856a59cb1f54895", "outfile": null, "outfile_hash": null, "stdout": "asr-structs_02-2ab459a.stdout", - "stdout_hash": "e5df0fb84bfc694b884736c1c4a5ddaec25933d8dba54e894de3e961", + "stdout_hash": "3a3e1108225886c52d459a08a26d5b35d4aea338982034d43079769d", "stderr": null, "stderr_hash": null, "returncode": 0 diff --git a/tests/reference/asr-structs_02-2ab459a.stdout b/tests/reference/asr-structs_02-2ab459a.stdout index feaf95ffa6..83bfd63a07 100644 --- a/tests/reference/asr-structs_02-2ab459a.stdout +++ b/tests/reference/asr-structs_02-2ab459a.stdout @@ -304,28 +304,6 @@ () ) () - ) - (CPtrToPointer - (Var 3 a) - (Var 3 a2) - () - () - ) - (Print - () - [(Var 3 a) - (Var 3 a2) - (GetPointer - (Var 3 a1) - (Pointer - (Struct - 8 A - ) - ) - () - )] - () - () )] () Public From cc5ade4d8383e67371d4035a9a3fa5fd1fc22627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Sun, 9 Jul 2023 13:12:51 -0600 Subject: [PATCH 12/21] Add NOFAST for structs_02b --- integration_tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 9b54269f3c..3d58ab8a1e 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -569,7 +569,7 @@ RUN(NAME test_bool_binop LABELS cpython llvm c) RUN(NAME test_issue_518 LABELS cpython llvm c NOFAST) RUN(NAME structs_01 LABELS cpython llvm c) RUN(NAME structs_02 LABELS cpython llvm c) -RUN(NAME structs_02b LABELS cpython llvm c) +RUN(NAME structs_02b LABELS cpython llvm c NOFAST) RUN(NAME structs_03 LABELS llvm c) RUN(NAME structs_04 LABELS cpython llvm c) RUN(NAME structs_05 LABELS cpython llvm c) From b5eb0f6a1d81837888375cb4f251a6b22460f829 Mon Sep 17 00:00:00 2001 From: Smit-create Date: Mon, 10 Jul 2023 09:06:23 +0530 Subject: [PATCH 13/21] Fix struct deepcopy --- src/libasr/codegen/c_utils.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libasr/codegen/c_utils.h b/src/libasr/codegen/c_utils.h index 84dbed81e2..4ac7fb7e94 100644 --- a/src/libasr/codegen/c_utils.h +++ b/src/libasr/codegen/c_utils.h @@ -842,14 +842,14 @@ class CCPPDSUtils { + struct_type_str + "* src, " + struct_type_str + "* dest)"; func_decls += "inline " + signature + ";\n"; - generated_code += indent + signature + " {\n"; + std::string tmp_generated = indent + signature + " {\n"; for(size_t i=0; i < struct_type_t->n_members; i++) { std::string mem_name = std::string(struct_type_t->m_members[i]); ASR::symbol_t* member = struct_type_t->m_symtab->get_symbol(mem_name); ASR::ttype_t* member_type_asr = ASRUtils::symbol_type(member); if( CUtils::is_non_primitive_DT(member_type_asr) || ASR::is_a(*member_type_asr) ) { - generated_code += indent + tab + get_deepcopy(member_type_asr, "&(src->" + mem_name + ")", + tmp_generated += indent + tab + get_deepcopy(member_type_asr, "&(src->" + mem_name + ")", "&(dest->" + mem_name + ")") + ";\n"; } else if( ASRUtils::is_array(member_type_asr) ) { ASR::dimension_t* m_dims = nullptr; @@ -857,17 +857,18 @@ class CCPPDSUtils { if( ASRUtils::is_fixed_size_array(m_dims, n_dims) ) { std::string array_size = std::to_string(ASRUtils::get_fixed_size_of_array(m_dims, n_dims)); array_size += "*sizeof(" + CUtils::get_c_type_from_ttype_t(member_type_asr) + ")"; - generated_code += indent + tab + "memcpy(dest->" + mem_name + ", src->" + mem_name + + tmp_generated += indent + tab + "memcpy(dest->" + mem_name + ", src->" + mem_name + ", " + array_size + ");\n"; } else { - generated_code += indent + tab + get_deepcopy(member_type_asr, "src->" + mem_name, + tmp_generated += indent + tab + get_deepcopy(member_type_asr, "src->" + mem_name, "dest->" + mem_name) + ";\n"; } } else { - generated_code += indent + tab + "dest->" + mem_name + " = " + " src->" + mem_name + ";\n"; + tmp_generated += indent + tab + "dest->" + mem_name + " = " + " src->" + mem_name + ";\n"; } } - generated_code += indent + "}\n\n"; + tmp_generated += indent + "}\n\n"; + generated_code += tmp_generated; } void list_deepcopy(std::string list_struct_type, From 20677d05b76ed8ff863840b310dc0ebc26b04970 Mon Sep 17 00:00:00 2001 From: Smit-create Date: Mon, 10 Jul 2023 09:08:09 +0530 Subject: [PATCH 14/21] Add a test --- integration_tests/CMakeLists.txt | 1 + integration_tests/structs_33.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 integration_tests/structs_33.py diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 3d58ab8a1e..0e85f2d872 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -605,6 +605,7 @@ RUN(NAME structs_29 LABELS cpython llvm) RUN(NAME structs_30 LABELS cpython llvm c) RUN(NAME structs_31 LABELS cpython llvm c) RUN(NAME structs_32 LABELS cpython llvm c) +RUN(NAME structs_33 LABELS cpython llvm c) RUN(NAME symbolics_01 LABELS cpython_sym c_sym) RUN(NAME symbolics_02 LABELS cpython_sym c_sym) diff --git a/integration_tests/structs_33.py b/integration_tests/structs_33.py new file mode 100644 index 0000000000..c8f02cb8e6 --- /dev/null +++ b/integration_tests/structs_33.py @@ -0,0 +1,24 @@ +from lpython import packed, dataclass, i32, ccallback, CPtr, ccall + +# test issue 2125 + +@packed +@dataclass +class inner_struct: + inner_field: i32 = 0 + + +@packed +@dataclass +class outer_struct: + inner_s : inner_struct = inner_struct() + + +def check() -> None: + outer_struct_instance : outer_struct = outer_struct(inner_struct(5)) + outer_struct_instance2 : outer_struct = outer_struct_instance + inner_struct_instance : inner_struct = outer_struct_instance2.inner_s + assert inner_struct_instance.inner_field == 5 + + +check() From 054b93f038973e4863de61369468876f5964238c Mon Sep 17 00:00:00 2001 From: Smit-create Date: Mon, 10 Jul 2023 09:09:23 +0530 Subject: [PATCH 15/21] Update references --- tests/reference/c-structs_33-4a3339f.json | 13 +++++ tests/reference/c-structs_33-4a3339f.stdout | 61 +++++++++++++++++++++ tests/tests.toml | 4 ++ 3 files changed, 78 insertions(+) create mode 100644 tests/reference/c-structs_33-4a3339f.json create mode 100644 tests/reference/c-structs_33-4a3339f.stdout diff --git a/tests/reference/c-structs_33-4a3339f.json b/tests/reference/c-structs_33-4a3339f.json new file mode 100644 index 0000000000..73a432ee7b --- /dev/null +++ b/tests/reference/c-structs_33-4a3339f.json @@ -0,0 +1,13 @@ +{ + "basename": "c-structs_33-4a3339f", + "cmd": "lpython --no-color --show-c {infile}", + "infile": "tests/../integration_tests/structs_33.py", + "infile_hash": "15fdcb483c864b6322ebcf6c495e635bbe917f1d8b407ccf4dc3e2ee", + "outfile": null, + "outfile_hash": null, + "stdout": "c-structs_33-4a3339f.stdout", + "stdout_hash": "425a22068f547e15ba187e2974220f28f0af3a70305b67cfd4a6a655", + "stderr": null, + "stderr_hash": null, + "returncode": 0 +} \ No newline at end of file diff --git a/tests/reference/c-structs_33-4a3339f.stdout b/tests/reference/c-structs_33-4a3339f.stdout new file mode 100644 index 0000000000..5aaf931d35 --- /dev/null +++ b/tests/reference/c-structs_33-4a3339f.stdout @@ -0,0 +1,61 @@ +#include + +#include +#include +#include +#include +#include + +struct dimension_descriptor +{ + int32_t lower_bound, length; +}; +struct __attribute__((packed)) inner_struct { + int32_t inner_field; +}; + +struct __attribute__((packed)) outer_struct { + struct inner_struct inner_s; +}; + + +inline void struct_deepcopy_outer_struct(struct outer_struct* src, struct outer_struct* dest); +inline void struct_deepcopy_inner_struct(struct inner_struct* src, struct inner_struct* dest); + + +// Implementations +void check() +{ + struct inner_struct inner_struct_instance_value; + struct inner_struct* inner_struct_instance = &inner_struct_instance_value; + struct outer_struct outer_struct_instance_value; + struct outer_struct* outer_struct_instance = &outer_struct_instance_value; + struct outer_struct outer_struct_instance2_value; + struct outer_struct* outer_struct_instance2 = &outer_struct_instance2_value; + outer_struct_instance->inner_s.inner_field = 5; + struct_deepcopy_outer_struct(outer_struct_instance, outer_struct_instance2); + struct_deepcopy_inner_struct(&outer_struct_instance2->inner_s, inner_struct_instance); + ASSERT(inner_struct_instance->inner_field == 5); +} + +void _lpython_main_program() +{ + check(); +} + +int main(int argc, char* argv[]) +{ + _lpython_set_argv(argc, argv); + _lpython_main_program(); + return 0; +} + +void struct_deepcopy_inner_struct(struct inner_struct* src, struct inner_struct* dest) { + dest->inner_field = src->inner_field; +} + +void struct_deepcopy_outer_struct(struct outer_struct* src, struct outer_struct* dest) { + struct_deepcopy_inner_struct(&(src->inner_s), &(dest->inner_s));; +} + + diff --git a/tests/tests.toml b/tests/tests.toml index 05734f688c..13ac8257cd 100644 --- a/tests/tests.toml +++ b/tests/tests.toml @@ -337,6 +337,10 @@ asr = true filename = "../integration_tests/structs_05.py" asr = true +[[test]] +filename = "../integration_tests/structs_33.py" +c = true + [[test]] filename = "../integration_tests/callback_01.py" asr = true From c1c64d86db3b0b57df2c9c055857b871ab84b029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Sun, 9 Jul 2023 22:50:28 -0600 Subject: [PATCH 16/21] Implement lcompilers_unique_ID global variable This is initialized to a unique number for each run. --- src/bin/lpython.cpp | 7 +++++++ src/libasr/asr_scopes.cpp | 2 ++ src/libasr/utils.h | 2 ++ src/libasr/utils2.cpp | 15 +++++++++++++++ 4 files changed, 26 insertions(+) diff --git a/src/bin/lpython.cpp b/src/bin/lpython.cpp index 4eca2b4da3..013c89e04f 100644 --- a/src/bin/lpython.cpp +++ b/src/bin/lpython.cpp @@ -44,6 +44,9 @@ #include #include #endif + +extern std::string lcompilers_unique_ID; + namespace { using LCompilers::endswith; @@ -54,6 +57,7 @@ enum class Backend { llvm, cpp, c, x86, wasm, wasm_x86, wasm_x64 }; + std::string remove_extension(const std::string& filename) { size_t lastdot = filename.find_last_of("."); if (lastdot == std::string::npos) return filename; @@ -1610,6 +1614,9 @@ int main(int argc, char *argv[]) app.require_subcommand(0, 1); CLI11_PARSE(app, argc, argv); + lcompilers_unique_ID = LCompilers::get_unique_ID(); + + if( compiler_options.fast && compiler_options.enable_bounds_checking ) { // ReleaseSafe Mode } else if ( compiler_options.fast ) { diff --git a/src/libasr/asr_scopes.cpp b/src/libasr/asr_scopes.cpp index acbb50d9c6..ebd3b7c476 100644 --- a/src/libasr/asr_scopes.cpp +++ b/src/libasr/asr_scopes.cpp @@ -4,6 +4,8 @@ #include #include +std::string lcompilers_unique_ID; + namespace LCompilers { template< typename T > diff --git a/src/libasr/utils.h b/src/libasr/utils.h index a0b0c00451..e19e0e3ae4 100644 --- a/src/libasr/utils.h +++ b/src/libasr/utils.h @@ -20,6 +20,8 @@ enum Platform { std::string pf2s(Platform); Platform get_platform(); +std::string get_unique_ID(); + struct CompilerOptions { std::filesystem::path mod_files_dir; std::vector include_dirs; diff --git a/src/libasr/utils2.cpp b/src/libasr/utils2.cpp index 62cabe162d..51a2a3c5f4 100644 --- a/src/libasr/utils2.cpp +++ b/src/libasr/utils2.cpp @@ -4,6 +4,8 @@ #endif #include +#include +#include #include #include @@ -11,6 +13,19 @@ namespace LCompilers { +std::string get_unique_ID() { + static std::random_device dev; + static std::mt19937 rng(dev()); + std::uniform_int_distribution dist(0, 61); + const std::string v = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + std::string res; + for (int i = 0; i < 22; i++) { + res += v[dist(rng)]; + } + return res; +} + bool read_file(const std::string &filename, std::string &text) { std::ifstream ifs(filename.c_str(), std::ios::in | std::ios::binary From cd3d76d2927971c932359d3f3861f196e0c6e7aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Mon, 10 Jul 2023 00:00:10 -0600 Subject: [PATCH 17/21] Only emit the error for Structs for now --- src/lpython/semantics/python_ast_to_asr.cpp | 27 ++++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index ade3a2f39d..947d870e98 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -2735,18 +2735,21 @@ class CommonVisitor : public AST::BaseVisitor { throw SemanticAbort(); } if (ASR::is_a(*asr_alloc_type)) { - ASR::StructType_t *st = ASR::down_cast(ASR::down_cast(asr_alloc_type)->m_derived_type); - if (st->m_abi != ASR::abiType::BindC) { - diag.add(diag::Diagnostic( - "The struct in c_p_pointer must be C interoperable", - diag::Level::Error, diag::Stage::Semantic, { - diag::Label("not C interoperable", - {asr_alloc_type->base.loc}), - diag::Label("help: add the @ccallable decorator to this struct to make it C interoperable", - {st->base.base.loc}, false) - }) - ); - throw SemanticAbort(); + ASR::symbol_t *sym = ASR::down_cast(asr_alloc_type)->m_derived_type; + if (ASR::is_a(*sym)) { + ASR::StructType_t *st = ASR::down_cast(sym); + if (st->m_abi != ASR::abiType::BindC) { + diag.add(diag::Diagnostic( + "The struct in c_p_pointer must be C interoperable", + diag::Level::Error, diag::Stage::Semantic, { + diag::Label("not C interoperable", + {asr_alloc_type->base.loc}), + diag::Label("help: add the @ccallable decorator to this struct to make it C interoperable", + {st->base.base.loc}, false) + }) + ); + throw SemanticAbort(); + } } } fill_shape_and_lower_bound_for_CPtrToPointer(); From e830b0fc05de8cebead3196eff451c09e5c38709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Mon, 10 Jul 2023 10:53:31 -0600 Subject: [PATCH 18/21] Remove global variables Due to #2129, the global variables do not always work well. Until that issue is fixed, a simple workaround is to move these global variables into local variables. Reference tests updated. --- src/runtime/lpython_intrinsic_numpy.py | 7 ++++--- tests/reference/c-bindc_06-a30d20f.json | 2 +- tests/reference/c-bindc_06-a30d20f.stdout | 2 -- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/runtime/lpython_intrinsic_numpy.py b/src/runtime/lpython_intrinsic_numpy.py index 17243dd994..43b1f3a8d7 100644 --- a/src/runtime/lpython_intrinsic_numpy.py +++ b/src/runtime/lpython_intrinsic_numpy.py @@ -1,8 +1,5 @@ from lpython import i32, i64, f64, f32, ccall, vectorize, overload -pi_64: f64 = f64(3.141592653589793238462643383279502884197) -pi_32: f32 = f32(3.141592653589793238462643383279502884197) - ########## sin ########## @ccall @@ -274,11 +271,13 @@ def arctan(x: f32) -> f32: @overload @vectorize def degrees(x: f64) -> f64: + pi_64: Const[f64] = f64(3.141592653589793238462643383279502884197) return x*180.0/pi_64 @overload @vectorize def degrees(x: f32) -> f32: + pi_32: Const[f32] = f32(3.141592653589793238462643383279502884197) return x*f32(f32(180)/pi_32) ########## radians ########## @@ -286,11 +285,13 @@ def degrees(x: f32) -> f32: @overload @vectorize def radians(x: f64) -> f64: + pi_64: Const[f64] = f64(3.141592653589793238462643383279502884197) return x*pi_64/180.0 @overload @vectorize def radians(x: f32) -> f32: + pi_32: Const[f32] = f32(3.141592653589793238462643383279502884197) return x*f32(pi_32/f32(180)) ########## arcsinh ########## diff --git a/tests/reference/c-bindc_06-a30d20f.json b/tests/reference/c-bindc_06-a30d20f.json index 2b91aefedc..6449300ee9 100644 --- a/tests/reference/c-bindc_06-a30d20f.json +++ b/tests/reference/c-bindc_06-a30d20f.json @@ -6,7 +6,7 @@ "outfile": null, "outfile_hash": null, "stdout": "c-bindc_06-a30d20f.stdout", - "stdout_hash": "6a93f686a1c2fe616c5de50dd3e339f5db7d226f48b29e5569412402", + "stdout_hash": "7fdf946af48e4b286aa2a5a328ed8edb8e50b98c252f7290f273d1a0", "stderr": null, "stderr_hash": null, "returncode": 0 diff --git a/tests/reference/c-bindc_06-a30d20f.stdout b/tests/reference/c-bindc_06-a30d20f.stdout index a9e0d8e2fe..e99f8e9dcc 100644 --- a/tests/reference/c-bindc_06-a30d20f.stdout +++ b/tests/reference/c-bindc_06-a30d20f.stdout @@ -81,8 +81,6 @@ float _lfortran_caimag(float complex x); double _lfortran_zaimag(double complex x); -float pi_32 = 3.14159265358979312e+00; -double pi_64 = 3.14159265358979312e+00; double _lfortran_dacos(double x); double _lfortran_dacosh(double x); From 1e21b0582c104daafbf975a4eea04e2d1cb6744a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Mon, 10 Jul 2023 12:01:01 -0600 Subject: [PATCH 19/21] Make the test use external symbol --- tests/errors/bindc_10e.py | 12 ++---------- tests/errors/bindc_10e_mod.py | 7 +++++++ 2 files changed, 9 insertions(+), 10 deletions(-) create mode 100644 tests/errors/bindc_10e_mod.py diff --git a/tests/errors/bindc_10e.py b/tests/errors/bindc_10e.py index bdbff7df6c..f8d9ca5c4b 100644 --- a/tests/errors/bindc_10e.py +++ b/tests/errors/bindc_10e.py @@ -1,5 +1,5 @@ -from lpython import (i64, i16, CPtr, c_p_pointer, Pointer, sizeof, packed, - dataclass, ccallable, ccall, i32) +from lpython import i64, i16, CPtr, c_p_pointer, Pointer, sizeof, ccall, i32 +from bindc_10e_mod import S @ccall def _lfortran_malloc(size: i32) -> CPtr: @@ -9,14 +9,6 @@ def _lfortran_malloc(size: i32) -> CPtr: def alloc(buf_size:i64) -> CPtr: return _lfortran_malloc(i32(buf_size)) - -@packed -@dataclass -class S: - a: i16 - b: i64 - - def main(): p1: CPtr = alloc(sizeof(S)) print(p1) diff --git a/tests/errors/bindc_10e_mod.py b/tests/errors/bindc_10e_mod.py new file mode 100644 index 0000000000..1b0f48e086 --- /dev/null +++ b/tests/errors/bindc_10e_mod.py @@ -0,0 +1,7 @@ +from lpython import packed, dataclass, i16, i64 + +@packed +@dataclass +class S: + a: i16 + b: i64 From b35f614d85740593c137806d6355c641956e26d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Mon, 10 Jul 2023 12:02:07 -0600 Subject: [PATCH 20/21] Update reference tests --- tests/reference/asr-bindc_10e-8b10394.json | 4 ++-- tests/reference/asr-bindc_10e-8b10394.stderr | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/reference/asr-bindc_10e-8b10394.json b/tests/reference/asr-bindc_10e-8b10394.json index 9fdf2f9acd..38439231b2 100644 --- a/tests/reference/asr-bindc_10e-8b10394.json +++ b/tests/reference/asr-bindc_10e-8b10394.json @@ -2,12 +2,12 @@ "basename": "asr-bindc_10e-8b10394", "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", "infile": "tests/errors/bindc_10e.py", - "infile_hash": "36d1b5d366716d6a601db544fe8ff32aba76518181e71a98a4e6500c", + "infile_hash": "fa023d4ad17e426f778a99326998e6dbeac5428fa252974a39a3219d", "outfile": null, "outfile_hash": null, "stdout": null, "stdout_hash": null, "stderr": "asr-bindc_10e-8b10394.stderr", - "stderr_hash": "7686dd8c9b718548d301e28d39cd15a85b00030172c5658b38a75af5", + "stderr_hash": "9ea4ff2c8a8789057456196deb5772e4b3a2dffadbd10ecfe5f15f29", "returncode": 2 } \ No newline at end of file diff --git a/tests/reference/asr-bindc_10e-8b10394.stderr b/tests/reference/asr-bindc_10e-8b10394.stderr index 3639b97f68..cc7c1cd0aa 100644 --- a/tests/reference/asr-bindc_10e-8b10394.stderr +++ b/tests/reference/asr-bindc_10e-8b10394.stderr @@ -1,12 +1,14 @@ semantic error: The struct in c_p_pointer must be C interoperable - --> tests/errors/bindc_10e.py:23:38 + --> tests/errors/bindc_10e.py:15:38 | -23 | p2: Pointer[S] = c_p_pointer(p1, S) +15 | p2: Pointer[S] = c_p_pointer(p1, S) | ^ not C interoperable + + --> tests/errors/bindc_10e_mod.py:5:1 - 7:10 | -15 | class S: + 5 | class S: | ~~~~~~~~... ... | -17 | b: i64 + 7 | b: i64 | ...~~~~~~~~~~ help: add the @ccallable decorator to this struct to make it C interoperable From ade9e79c846c95e3dcfaa37978712770460ca21f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C4=8Cert=C3=ADk?= Date: Mon, 10 Jul 2023 12:02:51 -0600 Subject: [PATCH 21/21] Get past external --- src/lpython/semantics/python_ast_to_asr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index 947d870e98..b5e8c153d8 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -2735,7 +2735,7 @@ class CommonVisitor : public AST::BaseVisitor { throw SemanticAbort(); } if (ASR::is_a(*asr_alloc_type)) { - ASR::symbol_t *sym = ASR::down_cast(asr_alloc_type)->m_derived_type; + ASR::symbol_t *sym = ASRUtils::symbol_get_past_external(ASR::down_cast(asr_alloc_type)->m_derived_type); if (ASR::is_a(*sym)) { ASR::StructType_t *st = ASR::down_cast(sym); if (st->m_abi != ASR::abiType::BindC) {