diff --git a/integration_tests/test_list_01.py b/integration_tests/test_list_01.py index 7b2f5e95f7..4aa8bb0b2c 100644 --- a/integration_tests/test_list_01.py +++ b/integration_tests/test_list_01.py @@ -1,12 +1,16 @@ -from ltypes import i32 - -def test_list_i32(): - a: list[i32] = [1] - a.append(2) - a.append(3) - a.append(4) - a.append(5) - print(a[1]) - assert a[1] == 2 or a[1] == 3 - -test_list_i32() +from ltypes import f64, i32 + +def test_list(): + a: list[i32] = [0, 1, 2, 3, 4] + f: list[f64] = [1.0, 2.0, 3.0, 4.0, 5.0] + i: i32 + + for i in range(10): + a.append(i + 5) + f.append(float(i + 6)) + + + for i in range(15): + assert (f[i] - a[i]) == 1.0 + +test_list() diff --git a/src/libasr/asr_utils.h b/src/libasr/asr_utils.h index 84615e8afe..31df14e889 100644 --- a/src/libasr/asr_utils.h +++ b/src/libasr/asr_utils.h @@ -617,6 +617,90 @@ static inline std::string type_python_1dim_helper(const std::string & res, return res; } +static inline void encode_dimensions(size_t n_dims, std::string& res) { + if( n_dims > 0 ) { + res += "["; + } + for( size_t i = 0; i < n_dims; i++ ) { + res += ":"; + if( i == n_dims - 1 ) { + res += "]"; + } else { + res += ", "; + } + } +} + +static inline std::string get_type_code(const ASR::ttype_t *t) +{ + switch (t->type) { + case ASR::ttypeType::Integer: { + ASR::Integer_t *integer = ASR::down_cast(t); + std::string res = "i" + std::to_string(integer->m_kind * 8); + encode_dimensions(integer->n_dims, res); + return res; + } + case ASR::ttypeType::Real: { + ASR::Real_t *real = ASR::down_cast(t); + std::string res = "r" + std::to_string(real->m_kind * 8); + encode_dimensions(real->n_dims, res); + return res; + } + case ASR::ttypeType::Complex: { + ASR::Complex_t *complx = ASR::down_cast(t); + std::string res = "r" + std::to_string(complx->m_kind * 8); + encode_dimensions(complx->n_dims, res); + return res; + } + case ASR::ttypeType::Logical: { + return "bool"; + } + case ASR::ttypeType::Character: { + return "str"; + } + case ASR::ttypeType::Tuple: { + ASR::Tuple_t *tup = ASR::down_cast(t); + std::string result = "tuple["; + for (size_t i = 0; i < tup->n_type; i++) { + result += get_type_code(tup->m_type[i]); + if (i + 1 != tup->n_type) { + result += ", "; + } + } + result += "]"; + return result; + } + case ASR::ttypeType::Set: { + ASR::Set_t *s = ASR::down_cast(t); + return "set[" + get_type_code(s->m_type) + "]"; + } + case ASR::ttypeType::Dict: { + ASR::Dict_t *d = ASR::down_cast(t); + return "dict[" + get_type_code(d->m_key_type) + + ", " + get_type_code(d->m_value_type) + "]"; + } + case ASR::ttypeType::List: { + ASR::List_t *l = ASR::down_cast(t); + return "list[" + get_type_code(l->m_type) + "]"; + } + case ASR::ttypeType::CPtr: { + return "CPtr"; + } + case ASR::ttypeType::Derived: { + ASR::Derived_t* d = ASR::down_cast(t); + return symbol_name(d->m_derived_type); + } + case ASR::ttypeType::Pointer: { + ASR::Pointer_t* p = ASR::down_cast(t); + return "Pointer[" + get_type_code(p->m_type) + "]"; + } + default: { + throw LCompilersException("Type encoding not implemented for " + + std::to_string(t->type)); + } + } +} + static inline std::string type_to_str_python(const ASR::ttype_t *t, bool for_error_message=true) { diff --git a/src/libasr/codegen/asr_to_llvm.cpp b/src/libasr/codegen/asr_to_llvm.cpp index b4813499b8..b146fa4ef1 100644 --- a/src/libasr/codegen/asr_to_llvm.cpp +++ b/src/libasr/codegen/asr_to_llvm.cpp @@ -250,6 +250,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor types */ std::unique_ptr llvm_utils; + std::unique_ptr list_api; std::unique_ptr arr_descr; uint64_t ptr_loads; @@ -263,6 +264,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor al{al}, prototype_only(false), llvm_utils(std::make_unique(context, builder.get())), + list_api(std::make_unique(context, llvm_utils.get(), builder.get())), arr_descr(LLVMArrUtils::Descriptor::get_descriptor(context, builder.get(), llvm_utils.get(), @@ -310,7 +312,8 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor } llvm::Type* - get_el_type(ASR::ttype_t* m_type_, int a_kind) { + get_el_type(ASR::ttype_t* m_type_) { + int a_kind = ASRUtils::extract_kind_from_ttype_t(m_type_); llvm::Type* el_type = nullptr; if (ASR::is_a(*m_type_)) { ASR::ttype_t *t2 = ASR::down_cast(m_type_)->m_type; @@ -875,49 +878,6 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor return res; } - llvm::Value* lcompilers_list_init_i32() - { - std::string runtime_func_name = "_lcompilers_list_init_i32"; - llvm::Function *fn = module->getFunction(runtime_func_name); - if (!fn) { - llvm::FunctionType *function_type = llvm::FunctionType::get( - list_type, { }, false); - fn = llvm::Function::Create(function_type, - llvm::Function::ExternalLinkage, runtime_func_name, *module); - } - return builder->CreateCall(fn, {}); - } - - void lcompilers_list_append_i32(llvm::Value* plist, llvm::Value *item) - { - std::string runtime_func_name = "_lcompilers_list_append_i32"; - llvm::Function *fn = module->getFunction(runtime_func_name); - if (!fn) { - llvm::FunctionType *function_type = llvm::FunctionType::get( - llvm::Type::getVoidTy(context), { - list_type, llvm::Type::getInt32Ty(context) - }, false); - fn = llvm::Function::Create(function_type, - llvm::Function::ExternalLinkage, runtime_func_name, *module); - } - builder->CreateCall(fn, {plist, item}); - } - - llvm::Value* lcompilers_list_item_i32(llvm::Value* plist, llvm::Value *pos) - { - std::string runtime_func_name = "_lcompilers_list_item_i32"; - llvm::Function *fn = module->getFunction(runtime_func_name); - if (!fn) { - llvm::FunctionType *function_type = llvm::FunctionType::get( - llvm::Type::getInt32Ty(context), { - list_type, llvm::Type::getInt32Ty(context) - }, false); - fn = llvm::Function::Create(function_type, - llvm::Function::ExternalLinkage, runtime_func_name, *module); - } - return builder->CreateCall(fn, {plist, pos}); - } - // This function is called as: // float complex_re(complex a) // And it extracts the real part of the complex number @@ -1182,54 +1142,49 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor _Deallocate(x); } + void visit_ListConstant(const ASR::ListConstant_t& x) { + ASR::List_t* list_type = ASR::down_cast(x.m_type); + llvm::Type* llvm_el_type = get_el_type(list_type->m_type); + std::string type_code = ASRUtils::get_type_code(list_type->m_type); + int32_t type_size = ASRUtils::extract_kind_from_ttype_t(list_type->m_type); + llvm::Type* const_list_type = list_api->get_list_type(llvm_el_type, type_code, type_size); + llvm::Value* const_list = builder->CreateAlloca(const_list_type, nullptr, "const_list"); + list_api->list_init(type_code, const_list, *module, x.n_args, x.n_args); + for( size_t i = 0; i < x.n_args; i++ ) { + this->visit_expr(*x.m_args[i]); + llvm::Value* item = tmp; + llvm::Value* pos = llvm::ConstantInt::get(context, llvm::APInt(32, i)); + list_api->write_item(const_list, pos, item); + } + tmp = const_list; + } + void visit_ListAppend(const ASR::ListAppend_t& x) { - ASR::Variable_t *l = EXPR2VAR(x.m_a); - uint32_t v_h = get_hash((ASR::asr_t*)l); - LFORTRAN_ASSERT(llvm_symtab.find(v_h) != llvm_symtab.end()); - llvm::Value *plist = llvm_symtab[v_h]; + uint64_t ptr_loads_copy = ptr_loads; + ptr_loads = 0; + this->visit_expr(*x.m_a); + ptr_loads = ptr_loads_copy; + llvm::Value* plist = tmp; this->visit_expr_wrapper(x.m_ele, true); - llvm::Value *ele = tmp; - - ASR::ttype_t *el_type = ASR::down_cast(l->m_type)->m_type; - if (is_a(*el_type)) { - int kind = ASR::down_cast(el_type)->m_kind; - if (kind == 4) { - llvm::Value *plist2 = CreateLoad(plist); - lcompilers_list_append_i32(plist2, ele); - } else { - throw CodeGenError("Integer kind not supported yet in ListAppend", x.base.base.loc); - } - - } else { - throw CodeGenError("List type not supported yet in ListAppend", x.base.base.loc); - } + llvm::Value *item = tmp; + ASR::List_t* asr_list = ASR::down_cast(ASRUtils::expr_type(x.m_a)); + std::string type_code = ASRUtils::get_type_code(asr_list->m_type); + list_api->append(plist, item, *module, type_code); } void visit_ListItem(const ASR::ListItem_t& x) { - ASR::Variable_t *l = EXPR2VAR(x.m_a); - uint32_t v_h = get_hash((ASR::asr_t*)l); - LFORTRAN_ASSERT(llvm_symtab.find(v_h) != llvm_symtab.end()); - llvm::Value *plist = llvm_symtab[v_h]; + uint64_t ptr_loads_copy = ptr_loads; + ptr_loads = 0; + this->visit_expr(*x.m_a); + ptr_loads = ptr_loads_copy; + llvm::Value* plist = tmp; this->visit_expr_wrapper(x.m_pos, true); llvm::Value *pos = tmp; - ASR::ttype_t *el_type = ASR::down_cast(l->m_type)->m_type; - if (is_a(*el_type)) { - int kind = ASR::down_cast(el_type)->m_kind; - if (kind == 4) { - llvm::Value *plist2 = CreateLoad(plist); - tmp = lcompilers_list_item_i32(plist2, pos); - } else { - throw CodeGenError("Integer kind not supported as index in ListItem", x.base.base.loc); - } - - } else { - throw CodeGenError("List type not supported yet in ListItem", x.base.base.loc); - } - + tmp = list_api->read_item(plist, pos); } void visit_ArrayItem(const ASR::ArrayItem_t& x) { @@ -1501,10 +1456,10 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor } else if(x.m_type->type == ASR::ttypeType::Pointer) { ASR::dimension_t* m_dims = nullptr; int n_dims = -1, a_kind = -1; - bool is_array_type = false, is_malloc_array_type = false; + bool is_array_type = false, is_malloc_array_type = false, is_list = false; llvm::Type* x_ptr = get_type_from_ttype_t(x.m_type, x.m_storage, is_array_type, - is_malloc_array_type, m_dims, n_dims, - a_kind); + is_malloc_array_type, is_list, + m_dims, n_dims, a_kind); llvm::Constant *ptr = module->getOrInsertGlobal(x.m_name, x_ptr); if (!external) { @@ -1630,8 +1585,8 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor llvm::Type* get_type_from_ttype_t(ASR::ttype_t* asr_type, ASR::storage_typeType m_storage, bool& is_array_type, bool& is_malloc_array_type, - ASR::dimension_t*& m_dims, int& n_dims, - int& a_kind) { + bool& is_list, ASR::dimension_t*& m_dims, + int& n_dims, int& a_kind) { llvm::Type* llvm_type = nullptr; switch (asr_type->type) { case (ASR::ttypeType::Integer) : { @@ -1641,7 +1596,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor a_kind = v_type->m_kind; if( n_dims > 0 ) { is_array_type = true; - llvm::Type* el_type = get_el_type(asr_type, a_kind); + llvm::Type* el_type = get_el_type(asr_type); if( m_storage == ASR::storage_typeType::Allocatable ) { is_malloc_array_type = true; llvm_type = arr_descr->get_malloc_array_type(asr_type, a_kind, n_dims, el_type); @@ -1660,7 +1615,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor a_kind = v_type->m_kind; if( n_dims > 0 ) { is_array_type = true; - llvm::Type* el_type = get_el_type(asr_type, a_kind); + llvm::Type* el_type = get_el_type(asr_type); if( m_storage == ASR::storage_typeType::Allocatable ) { is_malloc_array_type = true; llvm_type = arr_descr->get_malloc_array_type(asr_type, a_kind, n_dims, el_type); @@ -1679,7 +1634,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor a_kind = v_type->m_kind; if( n_dims > 0 ) { is_array_type = true; - llvm::Type* el_type = get_el_type(asr_type, a_kind); + llvm::Type* el_type = get_el_type(asr_type); if( m_storage == ASR::storage_typeType::Allocatable ) { is_malloc_array_type = true; llvm_type = arr_descr->get_malloc_array_type(asr_type, a_kind, n_dims, el_type); @@ -1698,7 +1653,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor a_kind = v_type->m_kind; if( n_dims > 0 ) { is_array_type = true; - llvm::Type* el_type = get_el_type(asr_type, a_kind); + llvm::Type* el_type = get_el_type(asr_type); if( m_storage == ASR::storage_typeType::Allocatable ) { is_malloc_array_type = true; llvm_type = arr_descr->get_malloc_array_type(asr_type, a_kind, n_dims, el_type); @@ -1717,7 +1672,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor a_kind = v_type->m_kind; if( n_dims > 0 ) { is_array_type = true; - llvm::Type* el_type = get_el_type(asr_type, a_kind); + llvm::Type* el_type = get_el_type(asr_type); if( m_storage == ASR::storage_typeType::Allocatable ) { is_malloc_array_type = true; llvm_type = arr_descr->get_malloc_array_type(asr_type, a_kind, n_dims, el_type); @@ -1735,7 +1690,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor n_dims = v_type->n_dims; if( n_dims > 0 ) { is_array_type = true; - llvm::Type* el_type = get_el_type(asr_type, a_kind); + llvm::Type* el_type = get_el_type(asr_type); if( m_storage == ASR::storage_typeType::Allocatable ) { is_malloc_array_type = true; llvm_type = arr_descr->get_malloc_array_type(asr_type, a_kind, n_dims, el_type); @@ -1750,14 +1705,19 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor case (ASR::ttypeType::Pointer) : { ASR::ttype_t *t2 = ASR::down_cast(asr_type)->m_type; llvm_type = get_type_from_ttype_t(t2, m_storage, is_array_type, - is_malloc_array_type, m_dims, n_dims, a_kind); + is_malloc_array_type, is_list, m_dims, n_dims, + a_kind); llvm_type = llvm_type->getPointerTo(); break; } case (ASR::ttypeType::List) : { - //ASR::List_t* v_type = down_cast(v->m_type); - //ASR::ttype_t *el_type = v_type->m_type; - llvm_type = list_type; + is_list = true; + ASR::List_t* asr_list = ASR::down_cast(asr_type); + llvm::Type* el_llvm_type = get_type_from_ttype_t(asr_list->m_type, m_storage, + is_array_type, is_malloc_array_type, + is_list, m_dims, n_dims, a_kind); + std::string el_type_code = ASRUtils::get_type_code(asr_list->m_type); + llvm_type = list_api->get_list_type(el_llvm_type, el_type_code, a_kind); break; } case (ASR::ttypeType::CPtr) : { @@ -1783,11 +1743,12 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor ASR::dimension_t* m_dims = nullptr; bool is_array_type = false; bool is_malloc_array_type = false; + bool is_list = false; if (v->m_intent == intent_local || v->m_intent == intent_return_var || !v->m_intent) { type = get_type_from_ttype_t(v->m_type, v->m_storage, is_array_type, - is_malloc_array_type, m_dims, n_dims, + is_malloc_array_type, is_list, m_dims, n_dims, a_kind); /* * The following if block is used for converting any @@ -1811,7 +1772,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor m_h = get_hash((ASR::asr_t*)_sub); is_v_arg = is_argument(v, _sub->m_args, _sub->n_args); } - if( is_array_type ) { + if( is_array_type && !is_list ) { /* The first element in an array descriptor can be either of * llvm::ArrayType or llvm::PointerType. However, a * function only accepts llvm::PointerType for arrays. Hence, @@ -1832,15 +1793,18 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor llvm::AllocaInst *ptr = builder->CreateAlloca(type, nullptr, v->m_name); llvm_symtab[h] = ptr; if( is_malloc_array_type && - v->m_type->type != ASR::ttypeType::Pointer ) { + v->m_type->type != ASR::ttypeType::Pointer && + !is_list ) { arr_descr->fill_dimension_descriptor(ptr, n_dims); } if( is_array_type && !is_malloc_array_type && - v->m_type->type != ASR::ttypeType::Pointer ) { + v->m_type->type != ASR::ttypeType::Pointer && + !is_list ) { fill_array_details(ptr, m_dims, n_dims); } if( is_array_type && is_malloc_array_type && - v->m_type->type != ASR::ttypeType::Pointer) { + v->m_type->type != ASR::ttypeType::Pointer && + !is_list ) { // Set allocatable arrays as unallocated arr_descr->set_is_allocated_flag(ptr, 0); } @@ -1865,7 +1829,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor idx)); } } else { - if (is_a(*v->m_type) && !is_array_type) { + if (is_a(*v->m_type) && !is_array_type && !is_list) { ASR::Character_t *t = down_cast(v->m_type); target_var = ptr; int strlen = t->m_len; @@ -1884,17 +1848,16 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor llvm::Value *arg_size = tmp; arg_size = builder->CreateAdd(arg_size, llvm::ConstantInt::get(context, llvm::APInt(32, 1))); // TODO: this temporary string is never deallocated (leaks memory) - llvm::Value *init_value = LLVMArrUtils::lfortran_malloc(context, *module, *builder, arg_size); + llvm::Value *init_value = LLVM::lfortran_malloc(context, *module, *builder, arg_size); string_init(context, *module, *builder, arg_size, init_value); builder->CreateStore(init_value, target_var); } else { throw CodeGenError("Unsupported len value in ASR"); } - } else if (is_a(*v->m_type)) { - // TODO: do a different initialization based on element type - llvm::Value *init_value = lcompilers_list_init_i32(); - target_var = ptr; - builder->CreateStore(init_value, target_var); + } else if (is_list) { + ASR::List_t* asr_list = ASR::down_cast(v->m_type); + std::string type_code = ASRUtils::get_type_code(asr_list->m_type); + list_api->list_init(type_code, ptr, *module); } } } @@ -1919,7 +1882,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor type = getIntType(a_kind, true); } else { is_array_type = true; - llvm::Type* el_type = get_el_type(asr_type, a_kind); + llvm::Type* el_type = get_el_type(asr_type); if( m_storage == ASR::storage_typeType::Allocatable ) { type = arr_descr->get_malloc_array_type(asr_type, a_kind, n_dims, el_type, true); } else { @@ -1954,7 +1917,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor type = getFPType(a_kind, true); } else { is_array_type = true; - llvm::Type* el_type = get_el_type(asr_type, a_kind); + llvm::Type* el_type = get_el_type(asr_type); if( m_storage == ASR::storage_typeType::Allocatable ) { type = arr_descr->get_malloc_array_type(asr_type, a_kind, n_dims, el_type, true); } else { @@ -1977,7 +1940,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor a_kind = v_type->m_kind; if( n_dims > 0 ) { is_array_type = true; - llvm::Type* el_type = get_el_type(asr_type, a_kind); + llvm::Type* el_type = get_el_type(asr_type); if( m_storage == ASR::storage_typeType::Allocatable ) { type = arr_descr->get_malloc_array_type(asr_type, a_kind, n_dims, el_type, true); } else { @@ -2029,7 +1992,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor a_kind = v_type->m_kind; if( n_dims > 0 ) { is_array_type = true; - llvm::Type* el_type = get_el_type(asr_type, a_kind); + llvm::Type* el_type = get_el_type(asr_type); if( m_storage == ASR::storage_typeType::Allocatable ) { type = arr_descr->get_malloc_array_type(asr_type, a_kind, n_dims, el_type, true); } else { @@ -2045,7 +2008,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor n_dims = v_type->n_dims; if( n_dims > 0 ) { is_array_type = true; - llvm::Type* el_type = get_el_type(asr_type, a_kind); + llvm::Type* el_type = get_el_type(asr_type); if( m_storage == ASR::storage_typeType::Allocatable ) { type = arr_descr->get_malloc_array_type(asr_type, a_kind, n_dims, el_type, true); } else { @@ -2061,7 +2024,7 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor n_dims = v_type->n_dims; if( n_dims > 0 ) { is_array_type = true; - llvm::Type* el_type = get_el_type(asr_type, a_kind); + llvm::Type* el_type = get_el_type(asr_type); if( m_storage == ASR::storage_typeType::Allocatable ) { type = arr_descr->get_malloc_array_type(asr_type, a_kind, n_dims, el_type, true); } else { @@ -2881,10 +2844,22 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor } // TODO: Remove this check after supporting ListConstant - if( ASR::is_a(*ASRUtils::expr_type(x.m_value)) ) { + bool is_target_list = ASR::is_a(*ASRUtils::expr_type(x.m_target)); + bool is_value_list = ASR::is_a(*ASRUtils::expr_type(x.m_value)); + if( is_target_list && is_value_list ) { + uint64_t ptr_loads_copy = ptr_loads; + ptr_loads = 0; + this->visit_expr(*x.m_target); + llvm::Value* target_list = tmp; + this->visit_expr(*x.m_value); + llvm::Value* value_list = tmp; + ptr_loads = ptr_loads_copy; + ASR::List_t* value_asr_list = ASR::down_cast( + ASRUtils::expr_type(x.m_value)); + std::string value_type_code = ASRUtils::get_type_code(value_asr_list->m_type); + list_api->list_deepcopy(value_list, target_list, value_type_code, *module); return ; } - if( ASR::is_a(*ASRUtils::expr_type(x.m_target)) && ASR::is_a(*x.m_value) ) { ASR::Variable_t *asr_target = EXPR2VAR(x.m_target); diff --git a/src/libasr/codegen/llvm_array_utils.cpp b/src/libasr/codegen/llvm_array_utils.cpp index 4b2aa86693..4d8f9a8b23 100644 --- a/src/libasr/codegen/llvm_array_utils.cpp +++ b/src/libasr/codegen/llvm_array_utils.cpp @@ -5,22 +5,6 @@ namespace LFortran { namespace LLVMArrUtils { - llvm::Value* lfortran_malloc(llvm::LLVMContext &context, llvm::Module &module, - llvm::IRBuilder<> &builder, llvm::Value* arg_size) { - std::string func_name = "_lfortran_malloc"; - llvm::Function *fn = module.getFunction(func_name); - if (!fn) { - llvm::FunctionType *function_type = llvm::FunctionType::get( - llvm::Type::getInt8PtrTy(context), { - llvm::Type::getInt32Ty(context) - }, true); - fn = llvm::Function::Create(function_type, - llvm::Function::ExternalLinkage, func_name, module); - } - std::vector args = {arg_size}; - return builder.CreateCall(fn, args); - } - bool compile_time_dimensions_t(ASR::dimension_t* m_dims, int n_dims) { if( n_dims <= 0 ) { return false; @@ -362,7 +346,7 @@ namespace LFortran { llvm::Value* llvm_size = llvm::ConstantInt::get(context, llvm::APInt(32, size)); num_elements = builder->CreateMul(num_elements, llvm_size); builder->CreateStore(num_elements, arg_size); - llvm::Value* ptr_as_char_ptr = lfortran_malloc(context, *module, *builder, LLVM::CreateLoad(*builder, arg_size)); + llvm::Value* ptr_as_char_ptr = LLVM::lfortran_malloc(context, *module, *builder, LLVM::CreateLoad(*builder, arg_size)); llvm::Value* first_ptr = builder->CreateBitCast(ptr_as_char_ptr, ptr_type); builder->CreateStore(first_ptr, ptr2firstptr); } diff --git a/src/libasr/codegen/llvm_array_utils.h b/src/libasr/codegen/llvm_array_utils.h index 8d0673c95d..eed29a7446 100644 --- a/src/libasr/codegen/llvm_array_utils.h +++ b/src/libasr/codegen/llvm_array_utils.h @@ -19,9 +19,6 @@ namespace LFortran { namespace LLVMArrUtils { - llvm::Value* lfortran_malloc(llvm::LLVMContext &context, llvm::Module &module, - llvm::IRBuilder<> &builder, llvm::Value* arg_size); - /* * This function checks whether the * dimensions are available at compile time. diff --git a/src/libasr/codegen/llvm_utils.cpp b/src/libasr/codegen/llvm_utils.cpp index e082423d82..4182215d9c 100644 --- a/src/libasr/codegen/llvm_utils.cpp +++ b/src/libasr/codegen/llvm_utils.cpp @@ -31,6 +31,42 @@ namespace LFortran { llvm::Type *t2 = t->getContainedType(0); return builder.CreateInBoundsGEP(t2, x, idx); } + + llvm::Value* lfortran_malloc(llvm::LLVMContext &context, llvm::Module &module, + llvm::IRBuilder<> &builder, llvm::Value* arg_size) { + std::string func_name = "_lfortran_malloc"; + llvm::Function *fn = module.getFunction(func_name); + if (!fn) { + llvm::FunctionType *function_type = llvm::FunctionType::get( + llvm::Type::getInt8PtrTy(context), { + llvm::Type::getInt32Ty(context) + }, true); + fn = llvm::Function::Create(function_type, + llvm::Function::ExternalLinkage, func_name, module); + } + std::vector args = {arg_size}; + return builder.CreateCall(fn, args); + } + + llvm::Value* lfortran_realloc(llvm::LLVMContext &context, llvm::Module &module, + llvm::IRBuilder<> &builder, llvm::Value* ptr, llvm::Value* arg_size) { + std::string func_name = "_lfortran_realloc"; + llvm::Function *fn = module.getFunction(func_name); + if (!fn) { + llvm::FunctionType *function_type = llvm::FunctionType::get( + llvm::Type::getInt8PtrTy(context), { + llvm::Type::getInt8PtrTy(context), + llvm::Type::getInt32Ty(context) + }, true); + fn = llvm::Function::Create(function_type, + llvm::Function::ExternalLinkage, func_name, module); + } + std::vector args = { + builder.CreateBitCast(ptr, llvm::Type::getInt8PtrTy(context)), + arg_size + }; + return builder.CreateCall(fn, args); + } } // namespace LLVM LLVMUtils::LLVMUtils(llvm::LLVMContext& context, @@ -119,4 +155,139 @@ namespace LFortran { builder->SetInsertPoint(bb); } + LLVMList::LLVMList(llvm::LLVMContext& context_, + LLVMUtils* llvm_utils_, + llvm::IRBuilder<>* builder_): + context(context_), + llvm_utils(std::move(llvm_utils_)), + builder(std::move(builder_)) {} + + llvm::Type* LLVMList::get_list_type(llvm::Type* el_type, std::string& type_code, + int32_t type_size) { + if( typecode2listtype.find(type_code) != typecode2listtype.end() ) { + return std::get<0>(typecode2listtype[type_code]); + } + std::vector list_type_vec = {llvm::Type::getInt32Ty(context), + llvm::Type::getInt32Ty(context), + el_type->getPointerTo()}; + llvm::StructType* list_desc = llvm::StructType::create(context, list_type_vec, "list"); + typecode2listtype[type_code] = std::make_tuple(list_desc, type_size, el_type); + return list_desc; + } + + llvm::Value* LLVMList::get_pointer_to_list_data(llvm::Value* list) { + return llvm_utils->create_gep(list, 2); + } + + llvm::Value* LLVMList::get_pointer_to_current_end_point(llvm::Value* list) { + return llvm_utils->create_gep(list, 0); + } + + llvm::Value* LLVMList::get_pointer_to_current_capacity(llvm::Value* list) { + return llvm_utils->create_gep(list, 1); + } + + void LLVMList::list_init(std::string& type_code, llvm::Value* list, + llvm::Module& module, int32_t initial_capacity, int32_t n) { + if( typecode2listtype.find(type_code) == typecode2listtype.end() ) { + LCompilersException("list for " + type_code + " not declared yet."); + } + int32_t type_size = std::get<1>(typecode2listtype[type_code]); + llvm::Value* arg_size = llvm::ConstantInt::get(context, + llvm::APInt(32, type_size * initial_capacity)); + + llvm::Value* list_data = LLVM::lfortran_malloc(context, module, *builder, + arg_size); + llvm::Type* el_type = std::get<2>(typecode2listtype[type_code]); + list_data = builder->CreateBitCast(list_data, el_type->getPointerTo()); + llvm::Value* list_data_ptr = get_pointer_to_list_data(list); + builder->CreateStore(list_data, list_data_ptr); + llvm::Value* current_end_point = llvm::ConstantInt::get(context, llvm::APInt(32, n)); + llvm::Value* current_capacity = llvm::ConstantInt::get(context, llvm::APInt(32, initial_capacity)); + builder->CreateStore(current_end_point, get_pointer_to_current_end_point(list)); + builder->CreateStore(current_capacity, get_pointer_to_current_capacity(list)); + } + + void LLVMList::list_deepcopy(llvm::Value* src, llvm::Value* dest, + std::string& src_type_code, + llvm::Module& module) { + LFORTRAN_ASSERT(src->getType() == dest->getType()); + llvm::Value* src_end_point = builder->CreateLoad(get_pointer_to_current_end_point(src)); + llvm::Value* src_capacity = builder->CreateLoad(get_pointer_to_current_capacity(src)); + llvm::Value* dest_end_point_ptr = get_pointer_to_current_end_point(dest); + llvm::Value* dest_capacity_ptr = get_pointer_to_current_capacity(dest); + builder->CreateStore(src_end_point, dest_end_point_ptr); + builder->CreateStore(src_capacity, dest_capacity_ptr); + llvm::Value* src_data = builder->CreateLoad(get_pointer_to_list_data(src)); + int32_t type_size = std::get<1>(typecode2listtype[src_type_code]); + llvm::Value* arg_size = builder->CreateMul(llvm::ConstantInt::get(context, + llvm::APInt(32, type_size)), src_capacity); + llvm::Value* copy_data = LLVM::lfortran_malloc(context, module, *builder, + arg_size); + llvm::Type* el_type = std::get<2>(typecode2listtype[src_type_code]); + copy_data = builder->CreateBitCast(copy_data, el_type->getPointerTo()); + builder->CreateMemCpy(copy_data, llvm::MaybeAlign(), src_data, + llvm::MaybeAlign(), arg_size); + builder->CreateStore(copy_data, get_pointer_to_list_data(dest)); + } + + void LLVMList::write_item(llvm::Value* list, llvm::Value* pos, llvm::Value* item) { + llvm::Value* list_data = builder->CreateLoad(get_pointer_to_list_data(list)); + llvm::Value* element_ptr = llvm_utils->create_ptr_gep(list_data, pos); + builder->CreateStore(item, element_ptr); + } + + llvm::Value* LLVMList::read_item(llvm::Value* list, llvm::Value* pos) { + llvm::Value* list_data = builder->CreateLoad(get_pointer_to_list_data(list)); + llvm::Value* element_ptr = llvm_utils->create_ptr_gep(list_data, pos); + return builder->CreateLoad(element_ptr); + } + + void LLVMList::resize_if_needed(llvm::Value* list, llvm::Value* n, + llvm::Value* capacity, int32_t type_size, + llvm::Type* el_type, llvm::Module& module) { + llvm::Value *cond = builder->CreateICmpEQ(n, capacity); + llvm::Function *fn = builder->GetInsertBlock()->getParent(); + llvm::BasicBlock *thenBB = llvm::BasicBlock::Create(context, "then", fn); + llvm::BasicBlock *elseBB = llvm::BasicBlock::Create(context, "else"); + llvm::BasicBlock *mergeBB = llvm::BasicBlock::Create(context, "ifcont"); + builder->CreateCondBr(cond, thenBB, elseBB); + builder->SetInsertPoint(thenBB); + llvm::Value* new_capacity = builder->CreateMul(llvm::ConstantInt::get(context, + llvm::APInt(32, 2)), capacity); + llvm::Value* arg_size = builder->CreateMul(llvm::ConstantInt::get(context, + llvm::APInt(32, type_size)), + new_capacity); + llvm::Value* copy_data_ptr = get_pointer_to_list_data(list); + llvm::Value* copy_data = builder->CreateLoad(copy_data_ptr); + copy_data = LLVM::lfortran_realloc(context, module, *builder, + copy_data, arg_size); + copy_data = builder->CreateBitCast(copy_data, el_type->getPointerTo()); + builder->CreateStore(copy_data, copy_data_ptr); + builder->CreateStore(new_capacity, get_pointer_to_current_capacity(list)); + builder->CreateBr(mergeBB); + llvm_utils->start_new_block(elseBB); + llvm_utils->start_new_block(mergeBB); + } + + void LLVMList::shift_end_point_by_one(llvm::Value* list) { + llvm::Value* end_point_ptr = get_pointer_to_current_end_point(list); + llvm::Value* end_point = builder->CreateLoad(end_point_ptr); + end_point = builder->CreateAdd(end_point, llvm::ConstantInt::get(context, llvm::APInt(32, 1))); + builder->CreateStore(end_point, end_point_ptr); + } + + void LLVMList::append(llvm::Value* list, llvm::Value* item, + llvm::Module& module, + std::string& type_code) { + llvm::Value* current_end_point = builder->CreateLoad(get_pointer_to_current_end_point(list)); + llvm::Value* current_capacity = builder->CreateLoad(get_pointer_to_current_capacity(list)); + int type_size = std::get<1>(typecode2listtype[type_code]); + llvm::Type* el_type = std::get<2>(typecode2listtype[type_code]); + resize_if_needed(list, current_end_point, current_capacity, + type_size, el_type, module); + write_item(list, current_end_point, item); + shift_end_point_by_one(list); + } + } // namespace LFortran diff --git a/src/libasr/codegen/llvm_utils.h b/src/libasr/codegen/llvm_utils.h index fcf7b80621..153af67d35 100644 --- a/src/libasr/codegen/llvm_utils.h +++ b/src/libasr/codegen/llvm_utils.h @@ -6,6 +6,9 @@ #include #include +#include +#include + namespace LFortran { namespace LLVM { @@ -14,6 +17,10 @@ namespace LFortran { llvm::Value* CreateStore(llvm::IRBuilder<> &builder, llvm::Value *x, llvm::Value *y); llvm::Value* CreateGEP(llvm::IRBuilder<> &builder, llvm::Value *x, std::vector &idx); llvm::Value* CreateInBoundsGEP(llvm::IRBuilder<> &builder, llvm::Value *x, std::vector &idx); + llvm::Value* lfortran_malloc(llvm::LLVMContext &context, llvm::Module &module, + llvm::IRBuilder<> &builder, llvm::Value* arg_size); + llvm::Value* lfortran_realloc(llvm::LLVMContext &context, llvm::Module &module, + llvm::IRBuilder<> &builder, llvm::Value* ptr, llvm::Value* arg_size); } class LLVMUtils { @@ -42,6 +49,51 @@ namespace LFortran { }; // LLVMUtils + class LLVMList { + private: + + llvm::LLVMContext& context; + LLVMUtils* llvm_utils; + llvm::IRBuilder<>* builder; + + std::map> typecode2listtype; + + void resize_if_needed(llvm::Value* list, llvm::Value* n, + llvm::Value* capacity, int32_t type_size, + llvm::Type* el_type, llvm::Module& module); + + void shift_end_point_by_one(llvm::Value* list); + + public: + + LLVMList(llvm::LLVMContext& context_, LLVMUtils* llvm_utils, + llvm::IRBuilder<>* builder); + + llvm::Type* get_list_type(llvm::Type* el_type, std::string& type_code, + int32_t type_size); + + void list_init(std::string& type_code, llvm::Value* list, + llvm::Module& module, int32_t initial_capacity=1, + int32_t n=0); + + llvm::Value* get_pointer_to_list_data(llvm::Value* list); + + llvm::Value* get_pointer_to_current_end_point(llvm::Value* list); + + llvm::Value* get_pointer_to_current_capacity(llvm::Value* list); + + void list_deepcopy(llvm::Value* src, llvm::Value* dest, + std::string& src_type_code, + llvm::Module& module); + + llvm::Value* read_item(llvm::Value* list, llvm::Value* pos); + + void write_item(llvm::Value* list, llvm::Value* pos, llvm::Value* item); + + void append(llvm::Value* list, llvm::Value* item, + llvm::Module& module, std::string& type_code); + }; + } // LFortran #endif // LFORTRAN_LLVM_UTILS_H diff --git a/src/runtime/impure/lfortran_intrinsics.c b/src/runtime/impure/lfortran_intrinsics.c index acb709eec8..1b91a80705 100644 --- a/src/runtime/impure/lfortran_intrinsics.c +++ b/src/runtime/impure/lfortran_intrinsics.c @@ -776,6 +776,10 @@ LFORTRAN_API char* _lfortran_malloc(int size) { return (char*)malloc(size); } +LFORTRAN_API int8_t* _lfortran_realloc(int8_t* ptr, int32_t size) { + return (int8_t*) realloc(ptr, size); +} + LFORTRAN_API void _lfortran_free(char* ptr) { free((void*)ptr); } @@ -789,45 +793,6 @@ LFORTRAN_API void _lfortran_string_init(int size_plus_one, char *s) { s[size] = '\0'; } -// List ----------------------------------------------------------------------- - -struct _lcompilers_list_i32 { - uint64_t n; - uint64_t capacity; - int32_t *p; -}; - -LFORTRAN_API int8_t* _lcompilers_list_init_i32() { - struct _lcompilers_list_i32 *l; - l = (struct _lcompilers_list_i32*)malloc( - sizeof(struct _lcompilers_list_i32)); - l->n = 0; - l->capacity = 4; - l->p = (int32_t*)malloc(l->capacity*sizeof(int32_t)); - return (int8_t*)l; -} - -LFORTRAN_API void _lcompilers_list_append_i32(int8_t* s, int32_t item) { - struct _lcompilers_list_i32 *l = (struct _lcompilers_list_i32 *)s; - if (l->n == l->capacity) { - l->capacity = 2*l->capacity; - l->p = realloc(l->p, sizeof(int32_t)*l->capacity); - } - l->p[l->n] = item; - l->n++; -} - -// pos is the index = 1..n -LFORTRAN_API int32_t _lcompilers_list_item_i32(int8_t* s, int32_t pos) { - struct _lcompilers_list_i32 *l = (struct _lcompilers_list_i32 *)s; - if (pos >= 1 && pos <= l->n) { - return l->p[pos-1]; - } else { - printf("Out of bounds\n"); - return 0; - } -} - // bit ------------------------------------------------------------------------ LFORTRAN_API int32_t _lfortran_iand32(int32_t x, int32_t y) {