Skip to content

Commit

Permalink
Add list.reverse intrinsic function (#1758)
Browse files Browse the repository at this point in the history
``Expr(expression)`` has also been added.

Co-authored-by: kabra1110 <[email protected]>
  • Loading branch information
virendrakabra14 and kabra1110 committed May 15, 2023
1 parent 9941f2c commit 7727de2
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 2 deletions.
1 change: 1 addition & 0 deletions integration_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ RUN(NAME test_list_10 LABELS cpython llvm c)
RUN(NAME test_list_section LABELS cpython llvm c)
RUN(NAME test_list_count LABELS cpython llvm)
RUN(NAME test_list_index LABELS cpython llvm)
RUN(NAME test_list_reverse LABELS cpython llvm)
RUN(NAME test_tuple_01 LABELS cpython llvm c)
RUN(NAME test_tuple_02 LABELS cpython llvm c)
RUN(NAME test_tuple_03 LABELS cpython llvm c)
Expand Down
47 changes: 47 additions & 0 deletions integration_tests/test_list_reverse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from lpython import i32, f64

def test_list_reverse():
l1: list[i32] = []
l2: list[i32] = []
l3: list[f64] = []
l4: list[f64] = []
l5: list[str] = []
l6: list[str] = []
l7: list[str] = []
i: i32
j: f64
s: str

l1 = [1, 2, 3]
l1.reverse()
assert l1 == [3, 2, 1]

l1 = []
for i in range(10):
l1.reverse()
l1.append(i)
l2.insert(0, i)
l1.reverse()
assert l1 == l2

j = 0.0
while j < 2.1:
l3.reverse()
l3.append(j)
l4.insert(0, j)
l3.insert(0, j + 1.0)
l4.append(j + 1.0)
l3.reverse()
assert l3 == l4
j += 0.1

l5 = ["abcd", "efgh", "ijkl"]
for s in l5:
l6.reverse()
l6.insert(0, s)
l7.append(s)
l6.reverse()
assert l6 == l7


test_list_reverse()
3 changes: 2 additions & 1 deletion src/libasr/ASR.asdl
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ stmt
| ListRemove(expr a, expr ele)
| ListClear(expr a)
| DictInsert(expr a, expr key, expr value)
| Expr(expr expression)


expr
Expand All @@ -225,7 +226,7 @@ expr
| FunctionCall(symbol name, symbol? original_name, call_arg* args,
ttype type, expr? value, expr? dt)
| IntrinsicFunction(int intrinsic_id, expr* args, int overload_id,
ttype type, expr? value)
ttype? type, expr? value)
| StructTypeConstructor(symbol dt_sym, call_arg* args, ttype type, expr? value)
| EnumTypeConstructor(symbol dt_sym, expr* args, ttype type, expr? value)
| UnionTypeConstructor(symbol dt_sym, expr* args, ttype type, expr? value)
Expand Down
20 changes: 20 additions & 0 deletions src/libasr/codegen/asr_to_llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1891,6 +1891,10 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
dict_type->m_value_type, name2memidx);
}

void visit_Expr(const ASR::Expr_t& x) {
this->visit_expr_wrapper(x.m_expression, false);
}

void visit_ListRemove(const ASR::ListRemove_t& x) {
ASR::ttype_t* asr_el_type = ASRUtils::get_contained_type(ASRUtils::expr_type(x.m_a));
int64_t ptr_loads_copy = ptr_loads;
Expand Down Expand Up @@ -1953,6 +1957,18 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
tmp = builder->CreateFSub(exp, one);
}

void generate_ListReverse(ASR::expr_t* m_arg) {
ASR::ttype_t* asr_el_type = ASRUtils::get_contained_type(ASRUtils::expr_type(m_arg));
int64_t ptr_loads_copy = ptr_loads;
ptr_loads = 0;
this->visit_expr(*m_arg);
llvm::Value* plist = tmp;

ptr_loads = !LLVM::is_llvm_struct(asr_el_type);
ptr_loads = ptr_loads_copy;
list_api->reverse(plist, asr_el_type, *module);
}

void visit_IntrinsicFunction(const ASR::IntrinsicFunction_t& x) {
switch (static_cast<ASRUtils::IntrinsicFunctions>(x.m_intrinsic_id)) {
case ASRUtils::IntrinsicFunctions::ListIndex: {
Expand Down Expand Up @@ -2012,6 +2028,10 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
}
break ;
}
case ASRUtils::IntrinsicFunctions::ListReverse: {
generate_ListReverse(x.m_args[0]);
break;
}
default: {
throw CodeGenError( ASRUtils::IntrinsicFunctionRegistry::
get_intrinsic_function_name(x.m_intrinsic_id) +
Expand Down
68 changes: 68 additions & 0 deletions src/libasr/codegen/llvm_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2629,6 +2629,74 @@ namespace LCompilers {
shift_end_point_by_one(list);
}

void LLVMList::reverse(llvm::Value* list, ASR::ttype_t* list_type, llvm::Module& module) {

/* Equivalent in C++:
*
* int i = 0;
* int j = end_point - 1;
*
* tmp;
*
* while(j > i) {
* tmp = list[i];
* list[i] = list[j];
* list[j] = tmp;
* i = i + 1;
* j = j - 1;
* }
*/

llvm::Value* end_point = LLVM::CreateLoad(*builder,
get_pointer_to_current_end_point(list));

llvm::Type* pos_type = llvm::Type::getInt32Ty(context);
llvm::AllocaInst *i = builder->CreateAlloca(pos_type, nullptr);
LLVM::CreateStore(*builder, llvm::ConstantInt::get(
context, llvm::APInt(32, 0)), i); // i = 0
llvm::AllocaInst *j = builder->CreateAlloca(pos_type, nullptr);
llvm::Value* tmp = nullptr;
tmp = builder->CreateSub(end_point, llvm::ConstantInt::get(context, llvm::APInt(32, 1)));
LLVM::CreateStore(*builder, tmp, j); // j = end_point - 1

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 *cond = builder->CreateICmpSGT(LLVM::CreateLoad(*builder, j), LLVM::CreateLoad(*builder, i));
builder->CreateCondBr(cond, loopbody, loopend);
}

// body
llvm_utils->start_new_block(loopbody);
{
tmp = read_item(list, LLVM::CreateLoad(*builder, i),
false, module, LLVM::is_llvm_struct(list_type)); // tmp = list[i]
write_item(list, LLVM::CreateLoad(*builder, i),
read_item(list, LLVM::CreateLoad(*builder, j),
false, module, LLVM::is_llvm_struct(list_type)),
false, module); // list[i] = list[j]
write_item(list, LLVM::CreateLoad(*builder, j),
tmp, false, module); // list[j] = tmp

tmp = builder->CreateAdd(
LLVM::CreateLoad(*builder, i),
llvm::ConstantInt::get(context, llvm::APInt(32, 1)));
LLVM::CreateStore(*builder, tmp, i);
tmp = builder->CreateSub(
LLVM::CreateLoad(*builder, j),
llvm::ConstantInt::get(context, llvm::APInt(32, 1)));
LLVM::CreateStore(*builder, tmp, j);
}
builder->CreateBr(loophead);

// end
llvm_utils->start_new_block(loopend);
}

llvm::Value* LLVMList::find_item_position(llvm::Value* list,
llvm::Value* item, ASR::ttype_t* item_type, llvm::Module& module) {
llvm::Type* pos_type = llvm::Type::getInt32Ty(context);
Expand Down
2 changes: 2 additions & 0 deletions src/libasr/codegen/llvm_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ namespace LCompilers {

void list_clear(llvm::Value* list);

void reverse(llvm::Value* list, ASR::ttype_t* list_type, llvm::Module& module);

llvm::Value* find_item_position(llvm::Value* list,
llvm::Value* item, ASR::ttype_t* item_type,
llvm::Module& module);
Expand Down
36 changes: 35 additions & 1 deletion src/libasr/pass/intrinsic_function_registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ enum class IntrinsicFunctions : int64_t {
Any,
ListIndex,
Partition,
ListReverse,
// ...
};

Expand Down Expand Up @@ -905,6 +906,35 @@ static inline ASR::asr_t* create_ListIndex(Allocator& al, const Location& loc,

} // namespace ListIndex

namespace ListReverse {

static inline ASR::expr_t *eval_list_reverse(Allocator &/*al*/,
const Location &/*loc*/, Vec<ASR::expr_t*>& /*args*/) {
// TODO: To be implemented for ListConstant expression
return nullptr;
}

static inline ASR::asr_t* create_ListReverse(Allocator& al, const Location& loc,
Vec<ASR::expr_t*>& args,
const std::function<void (const std::string &, const Location &)> err) {
if (args.size() != 1) {
err("list.reverse() takes no arguments", loc);
}

Vec<ASR::expr_t*> arg_values;
arg_values.reserve(al, args.size());
for( size_t i = 0; i < args.size(); i++ ) {
arg_values.push_back(al, ASRUtils::expr_value(args[i]));
}
ASR::expr_t* compile_time_value = eval_list_reverse(al, loc, arg_values);
return ASR::make_Expr_t(al, loc,
ASRUtils::EXPR(ASR::make_IntrinsicFunction_t(al, loc,
static_cast<int64_t>(ASRUtils::IntrinsicFunctions::ListReverse),
args.p, args.size(), 0, nullptr, compile_time_value)));
}

} // namespace ListReverse

namespace Any {

static inline ASR::expr_t *eval_Any(Allocator & /*al*/,
Expand Down Expand Up @@ -1276,7 +1306,9 @@ namespace IntrinsicFunctionRegistry {
{static_cast<int64_t>(ASRUtils::IntrinsicFunctions::Expm1),
"expm1"},
{static_cast<int64_t>(ASRUtils::IntrinsicFunctions::ListIndex),
"list.index"}
"list.index"},
{static_cast<int64_t>(ASRUtils::IntrinsicFunctions::ListReverse),
"list.reverse"}
};

static const std::map<std::string,
Expand All @@ -1298,6 +1330,7 @@ namespace IntrinsicFunctionRegistry {
{"expm1", {&Expm1::create_Expm1, &Expm1::eval_Expm1}},
{"any", {&Any::create_Any, &Any::eval_Any}},
{"list.index", {&ListIndex::create_ListIndex, &ListIndex::eval_list_index}},
{"list.reverse", {&ListReverse::create_ListReverse, &ListReverse::eval_list_reverse}},
};

static inline bool is_intrinsic_function(const std::string& name) {
Expand Down Expand Up @@ -1384,6 +1417,7 @@ inline std::string get_intrinsic_name(int x) {
INTRINSIC_NAME_CASE(Any)
INTRINSIC_NAME_CASE(ListIndex)
INTRINSIC_NAME_CASE(Partition)
INTRINSIC_NAME_CASE(ListReverse)
default : {
throw LCompilersException("pickle: intrinsic_id not implemented");
}
Expand Down
15 changes: 15 additions & 0 deletions src/lpython/semantics/python_attribute_eval.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct AttributeHandler {
{"list@remove", &eval_list_remove},
{"list@count", &eval_list_count},
{"list@index", &eval_list_index},
{"list@reverse", &eval_list_reverse},
{"list@clear", &eval_list_clear},
{"list@insert", &eval_list_insert},
{"list@pop", &eval_list_pop},
Expand Down Expand Up @@ -177,6 +178,20 @@ struct AttributeHandler {
{ throw SemanticError(msg, loc); });
}

static ASR::asr_t* eval_list_reverse(ASR::expr_t *s, Allocator &al, const Location &loc,
Vec<ASR::expr_t*> &args, diag::Diagnostics &/*diag*/) {
Vec<ASR::expr_t*> args_with_list;
args_with_list.reserve(al, args.size() + 1);
args_with_list.push_back(al, s);
for(size_t i = 0; i < args.size(); i++) {
args_with_list.push_back(al, args[i]);
}
ASRUtils::create_intrinsic_function create_function =
ASRUtils::IntrinsicFunctionRegistry::get_create_function("list.reverse");
return create_function(al, loc, args_with_list, [&](const std::string &msg, const Location &loc)
{ throw SemanticError(msg, loc); });
}

static ASR::asr_t* eval_list_insert(ASR::expr_t *s, Allocator &al, const Location &loc,
Vec<ASR::expr_t*> &args, diag::Diagnostics &diag) {
if (args.size() != 2) {
Expand Down

0 comments on commit 7727de2

Please sign in to comment.