Skip to content

Commit 6c917f0

Browse files
kabra1110czgdp1807
andauthored
Add list.pop (#1845)
Co-authored-by: Gagandeep Singh <[email protected]>
1 parent 13a386b commit 6c917f0

14 files changed

+347
-50
lines changed

integration_tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,8 @@ RUN(NAME test_list_section LABELS cpython llvm c NOFAST)
421421
RUN(NAME test_list_count LABELS cpython llvm)
422422
RUN(NAME test_list_index LABELS cpython llvm)
423423
RUN(NAME test_list_reverse LABELS cpython llvm)
424+
RUN(NAME test_list_pop LABELS cpython llvm NOFAST) # TODO: Remove NOFAST from here.
425+
RUN(NAME test_list_pop2 LABELS cpython llvm NOFAST) # TODO: Remove NOFAST from here.
424426
RUN(NAME test_tuple_01 LABELS cpython llvm c)
425427
RUN(NAME test_tuple_02 LABELS cpython llvm c NOFAST)
426428
RUN(NAME test_tuple_03 LABELS cpython llvm c)

integration_tests/test_list_pop.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from lpython import i32, f64
2+
3+
def test_list_pop():
4+
l1: list[i32]
5+
l2: list[tuple[i32, f64]]
6+
l3: list[list[str]]
7+
i: i32
8+
j: i32
9+
total: i32
10+
x: tuple[i32, f64]
11+
12+
l1 = [1, 2, 3]
13+
assert l1.pop() == 3
14+
assert l1 == [1, 2]
15+
16+
l1 = []
17+
total = 10
18+
for i in range(total):
19+
l1.append(i)
20+
if i % 2 == 1:
21+
assert l1.pop() == i
22+
for i in range(total // 2):
23+
assert l1[i] == 2 * i
24+
25+
l2 = [(1, 2.0)]
26+
x = l2.pop()
27+
assert x == (1, 2.0)
28+
assert len(l2) == 0
29+
l2.append((2, 3.0))
30+
assert x == (1, 2.0)
31+
32+
l3 = []
33+
for i in range(total):
34+
l3.insert(0, ["a"])
35+
for j in range(len(l3)):
36+
l3[j] += ["a"]
37+
while len(l3) > 0:
38+
total = len(l3)
39+
assert len(l3.pop()) == total + 1
40+
assert len(l3) == 0
41+
42+
l1 = [0, 1, 2, 3, 4]
43+
assert l1.pop(3) == 3
44+
assert l1.pop(0) == 0
45+
assert l1.pop(len(l1) - 1) == 4
46+
assert l1 == [1, 2]
47+
48+
total = 10
49+
l1 = []
50+
for i in range(total):
51+
l1.append(i)
52+
j = 0
53+
for i in range(total):
54+
assert l1.pop(j - i) == i
55+
j += 1
56+
assert len(l1) == 0
57+
58+
total = 10
59+
l2 = []
60+
for i in range(total):
61+
l2.append((i, f64(i * i)))
62+
j = 0
63+
for i in range(total):
64+
assert l2.pop(j - i) == (i, f64(i * i))
65+
j += 1
66+
assert len(l2) == 0
67+
68+
test_list_pop()

integration_tests/test_list_pop2.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from lpython import i32, f64, InOut
2+
3+
def pop_wrapper(l: InOut[list[tuple[i32, f64]]], idx: i32) -> tuple[i32, f64]:
4+
return l.pop(idx)
5+
6+
def test_list_pop():
7+
l1: list[tuple[i32, f64]]
8+
x: tuple[i32, f64]
9+
i: i32
10+
11+
l1 = [(1, 1.0), (2, 4.0), (3, 6.0)]
12+
13+
x = pop_wrapper(l1, 0)
14+
assert x == (1, 1.0)
15+
16+
l1.append((4, 8.0))
17+
assert x == (1, 1.0)
18+
19+
for i in range(len(l1)):
20+
assert l1[i] == (i + 2, 2.0 * f64(i + 2))
21+
22+
test_list_pop()

src/libasr/ASR.asdl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,6 @@ expr
330330
| ListItem(expr a, expr pos, ttype type, expr? value)
331331
| TupleItem(expr a, expr pos, ttype type, expr? value)
332332
| ListSection(expr a, array_index section, ttype type, expr? value)
333-
| ListPop(expr a, expr? index, ttype type, expr? value)
334333
| DictPop(expr a, expr key, ttype type, expr? value)
335334
| SetPop(expr a, ttype type, expr? value)
336335
| IntegerBitLen(expr a, ttype type, expr? value)

src/libasr/codegen/asr_to_llvm.cpp

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2021,6 +2021,32 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
20212021
list_api->reverse(plist, asr_el_type, *module);
20222022
}
20232023

2024+
void generate_ListPop_0(ASR::expr_t* m_arg) {
2025+
ASR::ttype_t* asr_el_type = ASRUtils::get_contained_type(ASRUtils::expr_type(m_arg));
2026+
int64_t ptr_loads_copy = ptr_loads;
2027+
ptr_loads = 0;
2028+
this->visit_expr(*m_arg);
2029+
llvm::Value* plist = tmp;
2030+
2031+
ptr_loads = !LLVM::is_llvm_struct(asr_el_type);
2032+
ptr_loads = ptr_loads_copy;
2033+
tmp = list_api->pop_last(plist, asr_el_type, *module);
2034+
}
2035+
2036+
void generate_ListPop_1(ASR::expr_t* m_arg, ASR::expr_t* m_ele) {
2037+
ASR::ttype_t* asr_el_type = ASRUtils::get_contained_type(ASRUtils::expr_type(m_arg));
2038+
int64_t ptr_loads_copy = ptr_loads;
2039+
ptr_loads = 0;
2040+
this->visit_expr(*m_arg);
2041+
llvm::Value* plist = tmp;
2042+
2043+
ptr_loads = 2;
2044+
this->visit_expr_wrapper(m_ele, true);
2045+
ptr_loads = ptr_loads_copy;
2046+
llvm::Value *pos = tmp;
2047+
tmp = list_api->pop_position(plist, pos, asr_el_type, module.get(), name2memidx);
2048+
}
2049+
20242050
void visit_IntrinsicFunction(const ASR::IntrinsicFunction_t& x) {
20252051
switch (static_cast<ASRUtils::IntrinsicFunctions>(x.m_intrinsic_id)) {
20262052
case ASRUtils::IntrinsicFunctions::ListIndex: {
@@ -2038,6 +2064,21 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
20382064
}
20392065
break ;
20402066
}
2067+
case ASRUtils::IntrinsicFunctions::ListReverse: {
2068+
generate_ListReverse(x.m_args[0]);
2069+
break;
2070+
}
2071+
case ASRUtils::IntrinsicFunctions::ListPop: {
2072+
switch(x.m_overload_id) {
2073+
case 0:
2074+
generate_ListPop_0(x.m_args[0]);
2075+
break;
2076+
case 1:
2077+
generate_ListPop_1(x.m_args[0], x.m_args[1]);
2078+
break;
2079+
}
2080+
break;
2081+
}
20412082
case ASRUtils::IntrinsicFunctions::Exp: {
20422083
switch (x.m_overload_id) {
20432084
case 0: {
@@ -2080,10 +2121,6 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
20802121
}
20812122
break ;
20822123
}
2083-
case ASRUtils::IntrinsicFunctions::ListReverse: {
2084-
generate_ListReverse(x.m_args[0]);
2085-
break;
2086-
}
20872124
default: {
20882125
throw CodeGenError( ASRUtils::IntrinsicFunctionRegistry::
20892126
get_intrinsic_function_name(x.m_intrinsic_id) +

src/libasr/codegen/llvm_utils.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2915,6 +2915,111 @@ namespace LCompilers {
29152915
builder->CreateStore(end_point, end_point_ptr);
29162916
}
29172917

2918+
llvm::Value* LLVMList::pop_last(llvm::Value* list, ASR::ttype_t* list_type, llvm::Module& module) {
2919+
// If list is empty, output error
2920+
2921+
llvm::Value* end_point_ptr = get_pointer_to_current_end_point(list);
2922+
llvm::Value* end_point = LLVM::CreateLoad(*builder, end_point_ptr);
2923+
2924+
llvm::Value* cond = builder->CreateICmpEQ(llvm::ConstantInt::get(
2925+
context, llvm::APInt(32, 0)), end_point);
2926+
llvm_utils->create_if_else(cond, [&]() {
2927+
std::string message = "pop from empty list";
2928+
llvm::Value *fmt_ptr = builder->CreateGlobalStringPtr("IndexError: %s\n");
2929+
llvm::Value *fmt_ptr2 = builder->CreateGlobalStringPtr(message);
2930+
print_error(context, module, *builder, {fmt_ptr, fmt_ptr2});
2931+
int exit_code_int = 1;
2932+
llvm::Value *exit_code = llvm::ConstantInt::get(context,
2933+
llvm::APInt(32, exit_code_int));
2934+
exit(context, module, *builder, exit_code);
2935+
}, [=]() {
2936+
});
2937+
2938+
// Get last element of list
2939+
llvm::Value* tmp = builder->CreateSub(end_point, llvm::ConstantInt::get(
2940+
context, llvm::APInt(32, 1)));
2941+
tmp = read_item(list, tmp, false, module, LLVM::is_llvm_struct(list_type));
2942+
2943+
// Decrement end point by one
2944+
end_point = builder->CreateSub(end_point, llvm::ConstantInt::get(
2945+
context, llvm::APInt(32, 1)));
2946+
builder->CreateStore(end_point, end_point_ptr);
2947+
return tmp;
2948+
}
2949+
2950+
llvm::Value* LLVMList::pop_position(llvm::Value* list, llvm::Value* pos,
2951+
ASR::ttype_t* list_element_type, llvm::Module* module,
2952+
std::map<std::string, std::map<std::string, int>>& name2memidx) {
2953+
2954+
/* Equivalent in C++:
2955+
* while(end_point > pos) {
2956+
* tmp = pos + 1;
2957+
* list[pos] = list[tmp];
2958+
* pos = tmp;
2959+
* }
2960+
*/
2961+
2962+
llvm::Value* end_point_ptr = get_pointer_to_current_end_point(list);
2963+
llvm::Value* end_point = LLVM::CreateLoad(*builder, end_point_ptr);
2964+
2965+
llvm::AllocaInst *pos_ptr = builder->CreateAlloca(
2966+
llvm::Type::getInt32Ty(context), nullptr);
2967+
LLVM::CreateStore(*builder, pos, pos_ptr);
2968+
llvm::Value* tmp = nullptr;
2969+
2970+
// Get element to return
2971+
llvm::Value* item = read_item(list, LLVM::CreateLoad(*builder, pos_ptr),
2972+
true, *module, LLVM::is_llvm_struct(list_element_type));
2973+
// TODO: Create a macro for the following code to allocate auxiliary variables
2974+
// on stack.
2975+
if( LLVM::is_llvm_struct(list_element_type) ) {
2976+
std::string list_element_type_code = ASRUtils::get_type_code(list_element_type);
2977+
llvm::BasicBlock &entry_block = builder->GetInsertBlock()->getParent()->getEntryBlock();
2978+
llvm::IRBuilder<> builder0(context);
2979+
builder0.SetInsertPoint(&entry_block, entry_block.getFirstInsertionPt());
2980+
LCOMPILERS_ASSERT(typecode2listtype.find(list_element_type_code) != typecode2listtype.end());
2981+
llvm::AllocaInst *target = builder0.CreateAlloca(
2982+
std::get<2>(typecode2listtype[list_element_type_code]), nullptr,
2983+
"pop_position_item");
2984+
llvm_utils->deepcopy(item, target, list_element_type, module, name2memidx);
2985+
item = target;
2986+
}
2987+
2988+
llvm::BasicBlock *loophead = llvm::BasicBlock::Create(context, "loop.head");
2989+
llvm::BasicBlock *loopbody = llvm::BasicBlock::Create(context, "loop.body");
2990+
llvm::BasicBlock *loopend = llvm::BasicBlock::Create(context, "loop.end");
2991+
2992+
// head
2993+
llvm_utils->start_new_block(loophead);
2994+
{
2995+
llvm::Value *cond = builder->CreateICmpSGT(end_point,
2996+
LLVM::CreateLoad(*builder, pos_ptr));
2997+
builder->CreateCondBr(cond, loopbody, loopend);
2998+
}
2999+
3000+
// body
3001+
llvm_utils->start_new_block(loopbody);
3002+
{
3003+
tmp = builder->CreateAdd(
3004+
LLVM::CreateLoad(*builder, pos_ptr),
3005+
llvm::ConstantInt::get(context, llvm::APInt(32, 1)));
3006+
write_item(list, LLVM::CreateLoad(*builder, pos_ptr),
3007+
read_item(list, tmp, false, *module, false), false, *module);
3008+
LLVM::CreateStore(*builder, tmp, pos_ptr);
3009+
}
3010+
builder->CreateBr(loophead);
3011+
3012+
// end
3013+
llvm_utils->start_new_block(loopend);
3014+
3015+
// Decrement end point by one
3016+
end_point = builder->CreateSub(end_point, llvm::ConstantInt::get(
3017+
context, llvm::APInt(32, 1)));
3018+
builder->CreateStore(end_point, end_point_ptr);
3019+
3020+
return item;
3021+
}
3022+
29183023
void LLVMList::list_clear(llvm::Value* list) {
29193024
llvm::Value* end_point_ptr = get_pointer_to_current_end_point(list);
29203025
llvm::Value* zero = llvm::ConstantInt::get(llvm::Type::getInt32Ty(context),

src/libasr/codegen/llvm_utils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,12 @@ namespace LCompilers {
259259
void remove(llvm::Value* list, llvm::Value* item,
260260
ASR::ttype_t* item_type, llvm::Module& module);
261261

262+
llvm::Value* pop_position(llvm::Value* list, llvm::Value* pos,
263+
ASR::ttype_t* list_type, llvm::Module* module,
264+
std::map<std::string, std::map<std::string, int>>& name2memidx);
265+
266+
llvm::Value* pop_last(llvm::Value* list, ASR::ttype_t* list_type, llvm::Module& module);
267+
262268
void list_clear(llvm::Value* list);
263269

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

0 commit comments

Comments
 (0)