From 5d961cf9c3067cda3ef32104d3653f926bc928e9 Mon Sep 17 00:00:00 2001 From: kabra1110 Date: Sun, 25 Jun 2023 19:39:03 +0200 Subject: [PATCH 1/5] add less than --- src/libasr/codegen/asr_to_llvm.cpp | 14 +- src/libasr/codegen/llvm_utils.cpp | 170 ++++++++++++++++++++ src/libasr/codegen/llvm_utils.h | 10 ++ src/lpython/semantics/python_ast_to_asr.cpp | 5 +- 4 files changed, 193 insertions(+), 6 deletions(-) diff --git a/src/libasr/codegen/asr_to_llvm.cpp b/src/libasr/codegen/asr_to_llvm.cpp index 7329e37c40..964732e14b 100644 --- a/src/libasr/codegen/asr_to_llvm.cpp +++ b/src/libasr/codegen/asr_to_llvm.cpp @@ -1888,10 +1888,16 @@ 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_less_by_value(left, right, *module, + ASRUtils::expr_type(x.m_left)); } } diff --git a/src/libasr/codegen/llvm_utils.cpp b/src/libasr/codegen/llvm_utils.cpp index 5b3f897a2b..350e11f236 100644 --- a/src/libasr/codegen/llvm_utils.cpp +++ b/src/libasr/codegen/llvm_utils.cpp @@ -310,6 +310,81 @@ namespace LCompilers { } } + llvm::Value* LLVMUtils::is_less_by_value(llvm::Value* left, llvm::Value* right, + llvm::Module& module, ASR::ttype_t* asr_type) { + switch( asr_type->type ) { + case ASR::ttypeType::Integer: { + return builder->CreateICmpSLT(left, right); + } + case ASR::ttypeType::Logical: { + return builder->CreateICmpSLT(left, right); // signed? + } + case ASR::ttypeType::Real: { + return builder->CreateFCmpOLT(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) + ); + cond = builder->CreateAnd(cond, builder->CreateICmpULT(l, r)); // unsigned? + 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); + } + 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); + } + 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) { @@ -3106,6 +3181,82 @@ 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) { + // TODO: + // - ineq operations other than "<" + // - abstract out this code, possibly switch over operators + // - short-circuit. Without initial allocation of res? Also for equality + + 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)); + 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_less_by_value(left_arg, right_arg, module, + item_type); + 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); + + // if a_len < b_len && equality_holds, then left < right + llvm::Value* cond = builder->CreateICmpSLT(a_len, b_len); + cond = builder->CreateAnd(cond, LLVM::CreateLoad(*builder, equality_holds)); + llvm_utils->create_if_else(cond, [&]() { + LLVM::CreateStore(*builder, llvm::ConstantInt::get( + context, llvm::APInt(1, 1)), inequality_holds); + }, [=]() { + // will be already 0 from the loop + }); + + 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) { @@ -3251,6 +3402,25 @@ 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) { + // TODO: operators other than "<" + llvm::Value* inequality_holds = llvm::ConstantInt::get(context, llvm::APInt(1, 0)); + for( size_t i = 0; i < tuple_type->n_type; i++ ) { + 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_less_by_value(t1i, t2i, module, + tuple_type->m_type[i]); + inequality_holds = builder->CreateOr(inequality_holds, res); + } + return 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 ffcfef16c6..76f9e9947d 100644 --- a/src/libasr/codegen/llvm_utils.h +++ b/src/libasr/codegen/llvm_utils.h @@ -143,6 +143,9 @@ 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_less_by_value(llvm::Value* left, llvm::Value* right, + llvm::Module& module, ASR::ttype_t* asr_type); + void set_iterators(); void reset_iterators(); @@ -286,6 +289,9 @@ 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); + void list_repeat_copy(llvm::Value* repeat_list, llvm::Value* init_list, llvm::Value* num_times, llvm::Value* init_list_len, llvm::Module* module); @@ -327,6 +333,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); + 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 0c3a353fbc..6c186e03ff 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -6214,8 +6214,9 @@ class BodyVisitor : public CommonVisitor { } 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) { + throw SemanticError("Only Equal, Not-equal and Less-than 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 f3f720b20872356b2d94e2476267a163f40dc965 Mon Sep 17 00:00:00 2001 From: kabra1110 Date: Sun, 25 Jun 2023 20:49:37 +0200 Subject: [PATCH 2/5] add test for less than --- integration_tests/CMakeLists.txt | 1 + integration_tests/test_list_compare.py | 33 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 integration_tests/test_list_compare.py diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 3d69c73c32..a46cb2a7ab 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -452,6 +452,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..4cfd71111d --- /dev/null +++ b/integration_tests/test_list_compare.py @@ -0,0 +1,33 @@ +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] = [] + i: i32 + + assert l1 < l2 + i = l2.pop() + i = l2.pop() + assert l2 < l1 + assert not (l1 < l2) + + assert l3 < l4 + 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] + +test_list_compare() \ No newline at end of file From 3848245fbc0a137cc39e28e3005743bd7efc861f Mon Sep 17 00:00:00 2001 From: kabra1110 Date: Sun, 25 Jun 2023 21:45:06 +0200 Subject: [PATCH 3/5] correct logic, incomplete tuple --- integration_tests/test_list_compare.py | 16 ++- src/libasr/codegen/llvm_utils.cpp | 103 +++++++++++++++++--- src/lpython/semantics/python_ast_to_asr.cpp | 9 +- 3 files changed, 109 insertions(+), 19 deletions(-) diff --git a/integration_tests/test_list_compare.py b/integration_tests/test_list_compare.py index 4cfd71111d..2c919d5de6 100644 --- a/integration_tests/test_list_compare.py +++ b/integration_tests/test_list_compare.py @@ -8,6 +8,8 @@ def test_list_compare(): l5: list[list[str]] = [[''], ['']] l6: list[str] = [] l7: list[str] = [] + t1: tuple[i32, i32] + t2: tuple[i32, i32] i: i32 assert l1 < l2 @@ -16,9 +18,13 @@ def test_list_compare(): assert l2 < l1 assert not (l1 < l2) - assert l3 < l4 - l4[0] = l3[0] - assert l4 < l3 + l1 = [3,4,5] + l2 = [1,6,7] + assert l2 < l1 + + # assert l3 < l4 + # l4[0] = l3[0] + # assert l4 < l3 for i in range(0, 10): if i % 2 == 0: @@ -29,5 +35,9 @@ def test_list_compare(): l5[1] = l7 if i % 2 == 0: assert l5[1 - i % 2] < l5[i % 2] + + # t1 = (1, 2) + # t2 = (3, 4) + # assert t1 < t2 test_list_compare() \ No newline at end of file diff --git a/src/libasr/codegen/llvm_utils.cpp b/src/libasr/codegen/llvm_utils.cpp index 350e11f236..81472b89be 100644 --- a/src/libasr/codegen/llvm_utils.cpp +++ b/src/libasr/codegen/llvm_utils.cpp @@ -3189,7 +3189,26 @@ namespace LCompilers { // TODO: // - ineq operations other than "<" // - abstract out this code, possibly switch over operators - // - short-circuit. Without initial allocation of res? Also for equality + // - short-circuit without initial allocation of res? Also for equality + + /** + * Equivalent in C++ + * For "<" + * + * 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] < b[i]); + * } + * + * if( i == a_len && a_len < b_len && equality_holds ) { + * inequality_holds = 1; + * } + * + */ llvm::AllocaInst *equality_holds = builder->CreateAlloca( llvm::Type::getInt1Ty(context), nullptr); @@ -3215,6 +3234,7 @@ namespace LCompilers { 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); } @@ -3244,8 +3264,9 @@ namespace LCompilers { // end llvm_utils->start_new_block(loopend); - // if a_len < b_len && equality_holds, then left < right - llvm::Value* cond = builder->CreateICmpSLT(a_len, b_len); + llvm::Value* cond = builder->CreateICmpEQ(LLVM::CreateLoad(*builder, idx), + a_len); + cond = builder->CreateAnd(cond, builder->CreateICmpSLT(a_len, b_len)); cond = builder->CreateAnd(cond, LLVM::CreateLoad(*builder, equality_holds)); llvm_utils->create_if_else(cond, [&]() { LLVM::CreateStore(*builder, llvm::ConstantInt::get( @@ -3408,17 +3429,73 @@ namespace LCompilers { llvm::IRBuilder<>* builder, llvm::Module& module) { // TODO: operators other than "<" - llvm::Value* inequality_holds = llvm::ConstantInt::get(context, llvm::APInt(1, 0)); - for( size_t i = 0; i < tuple_type->n_type; i++ ) { - 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_less_by_value(t1i, t2i, module, - tuple_type->m_type[i]); - inequality_holds = builder->CreateOr(inequality_holds, res); + + /** + * Equivalent in C++ + * For "<" + * + * equality_holds = 1; + * inequality_holds = 0; + * i = 0; + * + * while( i < a_len && equality_holds ) { + * equality_holds &= (a[i] == b[i]); + * inequality_holds |= (a[i] < b[i]); + * } + * + */ + + 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::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, llvm::ConstantInt::get( + context, llvm::APInt(32, tuple_type->n_type))); + 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* 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_less_by_value(t1i, t2i, module, + // tuple_type->m_type[i]); + // 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); + i = builder->CreateAdd(i, llvm::ConstantInt::get(llvm::Type::getInt32Ty(context), + llvm::APInt(32, 1))); + LLVM::CreateStore(*builder, i, idx); } - return inequality_holds; + + builder->CreateBr(loophead); + + // end + llvm_utils->start_new_block(loopend); + return LLVM::CreateLoad(*builder, inequality_holds); } void LLVMTuple::concat(llvm::Value* t1, llvm::Value* t2, ASR::Tuple_t* tuple_type_1, diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index 6c186e03ff..dbcfc79fef 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -6208,15 +6208,18 @@ 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) { + throw SemanticError("Only Equal, Not-equal and Less-than 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 && asr_op != ASR::cmpopType::Lt) { - throw SemanticError("Only Equal, Not-equal and Less-than operators are supported for Lists", + throw SemanticError("Only Equal, Not-equal and Less-than 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 e00fb5e65f8b34fee858fd89b1dece14f45659d3 Mon Sep 17 00:00:00 2001 From: kabra1110 Date: Mon, 3 Jul 2023 12:51:29 +0530 Subject: [PATCH 4/5] use compile time indices for tuples --- integration_tests/test_list_compare.py | 14 +++--- src/libasr/codegen/asr_to_llvm.cpp | 14 ++++-- src/libasr/codegen/llvm_utils.cpp | 59 ++++++++------------------ 3 files changed, 35 insertions(+), 52 deletions(-) diff --git a/integration_tests/test_list_compare.py b/integration_tests/test_list_compare.py index 2c919d5de6..14c1358cdd 100644 --- a/integration_tests/test_list_compare.py +++ b/integration_tests/test_list_compare.py @@ -22,9 +22,9 @@ def test_list_compare(): l2 = [1,6,7] assert l2 < l1 - # assert l3 < l4 - # l4[0] = l3[0] - # assert l4 < l3 + assert l3 < l4 + l4[0] = l3[0] + assert l4 < l3 for i in range(0, 10): if i % 2 == 0: @@ -35,9 +35,9 @@ def test_list_compare(): l5[1] = l7 if i % 2 == 0: assert l5[1 - i % 2] < l5[i % 2] - - # t1 = (1, 2) - # t2 = (3, 4) - # assert t1 < t2 + + t1 = (1, 2) + t2 = (2, 3) + assert 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 964732e14b..084afa71e1 100644 --- a/src/libasr/codegen/asr_to_llvm.cpp +++ b/src/libasr/codegen/asr_to_llvm.cpp @@ -2229,10 +2229,16 @@ 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_less_by_value(left, right, *module, + ASRUtils::expr_type(x.m_left)); } } diff --git a/src/libasr/codegen/llvm_utils.cpp b/src/libasr/codegen/llvm_utils.cpp index 81472b89be..b3eefb87f8 100644 --- a/src/libasr/codegen/llvm_utils.cpp +++ b/src/libasr/codegen/llvm_utils.cpp @@ -3202,6 +3202,7 @@ namespace LCompilers { * while( i < a_len && i < b_len && equality_holds ) { * equality_holds &= (a[i] == b[i]); * inequality_holds |= (a[i] < b[i]); + * i++; * } * * if( i == a_len && a_len < b_len && equality_holds ) { @@ -3271,7 +3272,7 @@ namespace LCompilers { llvm_utils->create_if_else(cond, [&]() { LLVM::CreateStore(*builder, llvm::ConstantInt::get( context, llvm::APInt(1, 1)), inequality_holds); - }, [=]() { + }, []() { // will be already 0 from the loop }); @@ -3439,8 +3440,9 @@ namespace LCompilers { * i = 0; * * while( i < a_len && equality_holds ) { - * equality_holds &= (a[i] == b[i]); * inequality_holds |= (a[i] < b[i]); + * equality_holds &= (a[i] == b[i]); + * i++; * } * */ @@ -3454,47 +3456,22 @@ namespace LCompilers { LLVM::CreateStore(*builder, llvm::ConstantInt::get(context, llvm::APInt(1, 0)), inequality_holds); - 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, llvm::ConstantInt::get( - context, llvm::APInt(32, tuple_type->n_type))); - 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* 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_less_by_value(t1i, t2i, module, - // tuple_type->m_type[i]); - // 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); - i = builder->CreateAdd(i, llvm::ConstantInt::get(llvm::Type::getInt32Ty(context), - llvm::APInt(32, 1))); - LLVM::CreateStore(*builder, i, idx); + 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_less_by_value(t1i, t2i, module, + tuple_type->m_type[i]); + 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); + }, [](){}); } - builder->CreateBr(loophead); - - // end - llvm_utils->start_new_block(loopend); return LLVM::CreateLoad(*builder, inequality_holds); } From e11b611518211bfd3e26b59f9b055835503f9e97 Mon Sep 17 00:00:00 2001 From: kabra1110 Date: Sun, 9 Jul 2023 20:06:23 +0530 Subject: [PATCH 5/5] correct logic, add other ops --- integration_tests/test_list_compare.py | 24 +++- src/libasr/codegen/asr_to_llvm.cpp | 35 ++++- src/libasr/codegen/llvm_utils.cpp | 136 +++++++++++++++----- src/libasr/codegen/llvm_utils.h | 13 +- src/lpython/semantics/python_ast_to_asr.cpp | 10 +- 5 files changed, 165 insertions(+), 53 deletions(-) diff --git a/integration_tests/test_list_compare.py b/integration_tests/test_list_compare.py index 14c1358cdd..24fcc485d5 100644 --- a/integration_tests/test_list_compare.py +++ b/integration_tests/test_list_compare.py @@ -12,17 +12,23 @@ def test_list_compare(): t2: tuple[i32, i32] i: i32 - assert l1 < l2 + assert l1 < l2 and l1 <= l2 + assert not l1 > l2 and not l1 >= l2 i = l2.pop() i = l2.pop() - assert l2 < l1 + assert l2 < l1 and l1 > l2 and l1 >= l2 assert not (l1 < l2) - l1 = [3,4,5] - l2 = [1,6,7] - assert l2 < l1 + l1 = [3, 4, 5] + l2 = [1, 6, 7] + assert l1 > l2 and l1 >= l2 + assert not l1 < l2 and not l1 <= l2 - assert l3 < l4 + 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 @@ -35,9 +41,13 @@ def test_list_compare(): 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 + 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 084afa71e1..6bd7f41ec9 100644 --- a/src/libasr/codegen/asr_to_llvm.cpp +++ b/src/libasr/codegen/asr_to_llvm.cpp @@ -1888,6 +1888,9 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor this->visit_expr(*x.m_right); llvm::Value* right = tmp; ptr_loads = ptr_loads_copy; + + 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)); @@ -1896,8 +1899,20 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor } } else if(x.m_op == ASR::cmpopType::Lt) { - tmp = llvm_utils->is_less_by_value(left, right, *module, - ASRUtils::expr_type(x.m_left)); + 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); } } @@ -2237,8 +2252,20 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor } } else if(x.m_op == ASR::cmpopType::Lt) { - tmp = llvm_utils->is_less_by_value(left, right, *module, - ASRUtils::expr_type(x.m_left)); + 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 b3eefb87f8..7b3fc38eb5 100644 --- a/src/libasr/codegen/llvm_utils.cpp +++ b/src/libasr/codegen/llvm_utils.cpp @@ -310,17 +310,67 @@ namespace LCompilers { } } - llvm::Value* LLVMUtils::is_less_by_value(llvm::Value* left, llvm::Value* right, - llvm::Module& module, ASR::ttype_t* asr_type) { + 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: { - return builder->CreateICmpSLT(left, right); - } + case ASR::ttypeType::Integer: case ASR::ttypeType::Logical: { - return builder->CreateICmpSLT(left, right); // signed? + 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: { - return builder->CreateFCmpOLT(left, right); + 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 ) { @@ -346,7 +396,28 @@ namespace LCompilers { builder->CreateICmpNE(l, null_char), builder->CreateICmpNE(r, null_char) ); - cond = builder->CreateAnd(cond, builder->CreateICmpULT(l, r)); // unsigned? + 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); } @@ -371,12 +442,13 @@ namespace LCompilers { 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); + 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); + context, builder, module, + overload_id, int32_type); } default: { throw LCompilersException("LLVMUtils::is_equal_by_value isn't implemented for " + @@ -3185,15 +3257,10 @@ namespace LCompilers { ASR::ttype_t* item_type, llvm::LLVMContext& context, llvm::IRBuilder<>* builder, - llvm::Module& module) { - // TODO: - // - ineq operations other than "<" - // - abstract out this code, possibly switch over operators - // - short-circuit without initial allocation of res? Also for equality - + llvm::Module& module, int8_t overload_id, + ASR::ttype_t* int32_type) { /** * Equivalent in C++ - * For "<" * * equality_holds = 1; * inequality_holds = 0; @@ -3201,12 +3268,12 @@ namespace LCompilers { * * while( i < a_len && i < b_len && equality_holds ) { * equality_holds &= (a[i] == b[i]); - * inequality_holds |= (a[i] < b[i]); + * inequality_holds |= (a[i] op b[i]); * i++; * } * - * if( i == a_len && a_len < b_len && equality_holds ) { - * inequality_holds = 1; + * if( (i == a_len || i == b_len) && equality_holds ) { + * inequality_holds = a_len op b_len; * } * */ @@ -3247,8 +3314,8 @@ namespace LCompilers { 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_less_by_value(left_arg, right_arg, module, - 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, @@ -3267,13 +3334,15 @@ namespace LCompilers { llvm::Value* cond = builder->CreateICmpEQ(LLVM::CreateLoad(*builder, idx), a_len); - cond = builder->CreateAnd(cond, builder->CreateICmpSLT(a_len, b_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::ConstantInt::get( - context, llvm::APInt(1, 1)), inequality_holds); + LLVM::CreateStore(*builder, llvm_utils->is_ineq_by_value(a_len, b_len, + module, int32_type, overload_id), inequality_holds); }, []() { - // will be already 0 from the loop + // LLVM::CreateStore(*builder, llvm::ConstantInt::get( + // context, llvm::APInt(1, 0)), inequality_holds); }); return LLVM::CreateLoad(*builder, inequality_holds); @@ -3428,23 +3497,24 @@ namespace LCompilers { ASR::Tuple_t* tuple_type, llvm::LLVMContext& context, llvm::IRBuilder<>* builder, - llvm::Module& module) { - // TODO: operators other than "<" - + llvm::Module& module, int8_t overload_id) { /** * Equivalent in C++ - * For "<" * * 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] < b[i]); + * inequality_holds |= (a[i] op b[i]); * equality_holds &= (a[i] == b[i]); * i++; * } * + * return inequality_holds; + * */ llvm::AllocaInst *equality_holds = builder->CreateAlloca( @@ -3462,8 +3532,8 @@ namespace LCompilers { 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_less_by_value(t1i, t2i, module, - 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]); diff --git a/src/libasr/codegen/llvm_utils.h b/src/libasr/codegen/llvm_utils.h index 76f9e9947d..54e17e463b 100644 --- a/src/libasr/codegen/llvm_utils.h +++ b/src/libasr/codegen/llvm_utils.h @@ -143,8 +143,9 @@ 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_less_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(); @@ -289,8 +290,10 @@ 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); + 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, @@ -335,7 +338,7 @@ namespace LCompilers { 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); + 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, diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index dbcfc79fef..46f92e3a6d 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -6209,16 +6209,18 @@ 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 - && asr_op != ASR::cmpopType::Lt) { - throw SemanticError("Only Equal, Not-equal and Less-than operators " + && 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 - && asr_op != ASR::cmpopType::Lt) { - throw SemanticError("Only Equal, Not-equal and Less-than operators " + && 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); }