Skip to content

Commit ba07d7b

Browse files
authored
Merge pull request #2752 from Vipul-Cariappa/top-lists
Support to print lists in REPL
2 parents a77c390 + d6f1e15 commit ba07d7b

File tree

7 files changed

+266
-77
lines changed

7 files changed

+266
-77
lines changed

src/bin/lpython.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,16 @@ int interactive_python_repl(
10071007
}
10081008
break;
10091009
}
1010+
case (LCompilers::PythonCompiler::EvalResult::struct_type) : {
1011+
if (verbose) {
1012+
std::cout << "Return type: "
1013+
<< LCompilers::ASRUtils::get_type_code(r.structure.ttype)
1014+
<< std::endl;
1015+
}
1016+
if (verbose) section("Result:");
1017+
std::cout << fe.aggregate_type_to_string(r) << std::endl;
1018+
break;
1019+
}
10101020
case (LCompilers::PythonCompiler::EvalResult::none) : {
10111021
if (verbose) {
10121022
std::cout << "Return type: none" << std::endl;

src/libasr/codegen/evaluator.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ std::string LLVMModule::str()
9090
return LLVMEvaluator::module_to_string(*m_m);
9191
}
9292

93+
llvm::Function *LLVMModule::get_function(const std::string &fn_name) {
94+
llvm::Module *m = m_m.get();
95+
return m->getFunction(fn_name);
96+
}
97+
9398
std::string LLVMModule::get_return_type(const std::string &fn_name)
9499
{
95100
llvm::Module *m = m_m.get();
@@ -121,12 +126,9 @@ std::string LLVMModule::get_return_type(const std::string &fn_name)
121126
return "complex4";
122127
} else if (startswith(std::string(st->getName()), "complex_8")) {
123128
return "complex8";
124-
} else {
125-
throw LCompilersException("LLVMModule::get_return_type(): Struct return type `" + std::string(st->getName()) + "` not supported");
126129
}
127-
} else {
128-
throw LCompilersException("LLVMModule::get_return_type(): Noname struct return type not supported");
129130
}
131+
return "struct";
130132
} else if (type->isVectorTy()) {
131133
// Used for passing complex_4 on some platforms
132134
return "complex4";
@@ -377,6 +379,10 @@ llvm::LLVMContext &LLVMEvaluator::get_context()
377379
return *context;
378380
}
379381

382+
const llvm::DataLayout &LLVMEvaluator::get_jit_data_layout() {
383+
return jit->getDataLayout();
384+
}
385+
380386
void LLVMEvaluator::print_targets()
381387
{
382388
llvm::InitializeNativeTarget();

src/libasr/codegen/evaluator.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace llvm {
1919
class Module;
2020
class Function;
2121
class TargetMachine;
22+
class DataLayout;
2223
namespace orc {
2324
class KaleidoscopeJIT;
2425
}
@@ -35,6 +36,7 @@ class LLVMModule
3536
std::string str();
3637
// Return a function return type as a string (real / integer)
3738
std::string get_return_type(const std::string &fn_name);
39+
llvm::Function *get_function(const std::string &fn_name);
3840
};
3941

4042
class LLVMEvaluator
@@ -60,6 +62,7 @@ class LLVMEvaluator
6062
static std::string module_to_string(llvm::Module &m);
6163
static void print_version_message();
6264
llvm::LLVMContext &get_context();
65+
const llvm::DataLayout &get_jit_data_layout();
6366
static void print_targets();
6467
static std::string get_default_target_triple();
6568

src/libasr/pass/global_stmts.cpp

Lines changed: 29 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ void pass_wrap_global_stmts(Allocator &al,
3131
char *fn_name = s.c_str(al);
3232
SymbolTable *fn_scope = al.make_new<SymbolTable>(unit.m_symtab);
3333

34-
ASR::ttype_t *type;
34+
ASR::ttype_t *type = nullptr;
3535
Location loc = unit.base.base.loc;
3636
ASR::asr_t *return_var=nullptr;
3737
ASR::expr_t *return_var_ref=nullptr;
@@ -45,58 +45,14 @@ void pass_wrap_global_stmts(Allocator &al,
4545
ASR::expr_t *target;
4646
ASR::expr_t *value = EXPR(unit.m_items[i]);
4747
// Create a new variable with the right type
48-
if (ASRUtils::expr_type(value)->type == ASR::ttypeType::Integer) {
49-
s.from_str(al, fn_name_s + std::to_string(idx));
50-
var_name = s.c_str(al);
51-
52-
int a_kind = down_cast<ASR::Integer_t>(ASRUtils::expr_type(value))->m_kind;
53-
54-
type = ASRUtils::TYPE(ASR::make_Integer_t(al, loc, a_kind));
55-
return_var = ASR::make_Variable_t(al, loc,
56-
fn_scope, var_name, nullptr, 0, ASRUtils::intent_local, nullptr, nullptr,
57-
ASR::storage_typeType::Default, type,
58-
nullptr, ASR::abiType::BindC,
59-
ASR::Public, ASR::presenceType::Required, false);
60-
return_var_ref = EXPR(ASR::make_Var_t(al, loc,
61-
down_cast<ASR::symbol_t>(return_var)));
62-
fn_scope->add_symbol(std::string(var_name), down_cast<ASR::symbol_t>(return_var));
63-
target = return_var_ref;
64-
idx++;
65-
} else if (ASRUtils::expr_type(value)->type == ASR::ttypeType::UnsignedInteger) {
66-
s.from_str(al, fn_name_s + std::to_string(idx));
67-
var_name = s.c_str(al);
68-
69-
int a_kind = down_cast<ASR::UnsignedInteger_t>(ASRUtils::expr_type(value))->m_kind;
70-
71-
type = ASRUtils::TYPE(ASR::make_UnsignedInteger_t(al, loc, a_kind));
72-
return_var = ASR::make_Variable_t(al, loc,
73-
fn_scope, var_name, nullptr, 0, ASRUtils::intent_local, nullptr, nullptr,
74-
ASR::storage_typeType::Default, type,
75-
nullptr, ASR::abiType::BindC,
76-
ASR::Public, ASR::presenceType::Required, false);
77-
return_var_ref = EXPR(ASR::make_Var_t(al, loc,
78-
down_cast<ASR::symbol_t>(return_var)));
79-
fn_scope->add_symbol(std::string(var_name), down_cast<ASR::symbol_t>(return_var));
80-
target = return_var_ref;
81-
idx++;
82-
} else if (ASRUtils::expr_type(value)->type == ASR::ttypeType::Logical) {
83-
s.from_str(al, fn_name_s + std::to_string(idx));
84-
var_name = s.c_str(al);
85-
86-
int a_kind = down_cast<ASR::Logical_t>(ASRUtils::expr_type(value))->m_kind;
87-
88-
type = ASRUtils::TYPE(ASR::make_Logical_t(al, loc, a_kind));
89-
return_var = ASR::make_Variable_t(al, loc,
90-
fn_scope, var_name, nullptr, 0, ASRUtils::intent_local, nullptr, nullptr,
91-
ASR::storage_typeType::Default, type,
92-
nullptr, ASR::abiType::BindC,
93-
ASR::Public, ASR::presenceType::Required, false);
94-
return_var_ref = EXPR(ASR::make_Var_t(al, loc,
95-
down_cast<ASR::symbol_t>(return_var)));
96-
fn_scope->add_symbol(std::string(var_name), down_cast<ASR::symbol_t>(return_var));
97-
target = return_var_ref;
98-
idx++;
99-
} else if (ASRUtils::expr_type(value)->type == ASR::ttypeType::Real) {
48+
if ((ASRUtils::expr_type(value)->type == ASR::ttypeType::Integer) ||
49+
(ASRUtils::expr_type(value)->type == ASR::ttypeType::UnsignedInteger) ||
50+
(ASRUtils::expr_type(value)->type == ASR::ttypeType::Logical) ||
51+
(ASRUtils::expr_type(value)->type == ASR::ttypeType::Real) ||
52+
(ASRUtils::expr_type(value)->type == ASR::ttypeType::Complex) ||
53+
(ASRUtils::expr_type(value)->type == ASR::ttypeType::Character) ||
54+
(ASRUtils::expr_type(value)->type == ASR::ttypeType::List) ||
55+
(ASRUtils::expr_type(value)->type == ASR::ttypeType::Tuple)) {
10056
s.from_str(al, fn_name_s + std::to_string(idx));
10157
var_name = s.c_str(al);
10258
type = ASRUtils::expr_type(value);
@@ -110,22 +66,7 @@ void pass_wrap_global_stmts(Allocator &al,
11066
fn_scope->add_symbol(std::string(var_name), down_cast<ASR::symbol_t>(return_var));
11167
target = return_var_ref;
11268
idx++;
113-
} else if ((ASRUtils::expr_type(value)->type == ASR::ttypeType::Complex) ||
114-
(ASRUtils::expr_type(value)->type == ASR::ttypeType::Character)) {
115-
s.from_str(al, fn_name_s + std::to_string(idx));
116-
var_name = s.c_str(al);
117-
type = ASRUtils::expr_type(value);
118-
return_var = ASR::make_Variable_t(al, loc,
119-
fn_scope, var_name, nullptr, 0, ASRUtils::intent_local, nullptr, nullptr,
120-
ASR::storage_typeType::Default, type,
121-
nullptr, ASR::abiType::BindC,
122-
ASR::Public, ASR::presenceType::Required, false);
123-
return_var_ref = EXPR(ASR::make_Var_t(al, loc,
124-
down_cast<ASR::symbol_t>(return_var)));
125-
fn_scope->add_symbol(std::string(var_name), down_cast<ASR::symbol_t>(return_var));
126-
target = return_var_ref;
127-
idx++;
128-
} else {
69+
} else {
12970
throw LCompilersException("Return type not supported in interactive mode");
13071
}
13172
ASR::stmt_t* asr_stmt = ASRUtils::STMT(ASR::make_Assignment_t(al, loc, target, value, nullptr));
@@ -141,7 +82,26 @@ void pass_wrap_global_stmts(Allocator &al,
14182

14283
if (return_var) {
14384
// The last defined `return_var` is the actual return value
85+
LCOMPILERS_ASSERT(type)
86+
LCOMPILERS_ASSERT(return_var_ref)
87+
14488
ASR::down_cast2<ASR::Variable_t>(return_var)->m_intent = ASRUtils::intent_return_var;
89+
std::string global_underscore_name = "_" + fn_name_s;
90+
s.from_str(al, global_underscore_name);
91+
92+
ASR::asr_t *global_underscore = ASR::make_Variable_t(al, loc,
93+
unit.m_symtab, s.c_str(al), nullptr, 0, ASRUtils::intent_local, nullptr, nullptr,
94+
ASR::storage_typeType::Default, type,
95+
nullptr, ASR::abiType::Source,
96+
ASR::Public, ASR::presenceType::Required, false);
97+
ASR::expr_t * global_underscore_ref = EXPR(ASR::make_Var_t(al, loc, down_cast<ASR::symbol_t>(global_underscore)));
98+
99+
if (fn_scope->parent->get_symbol(global_underscore_name) != nullptr) {
100+
throw LCompilersException("Global variable already defined");
101+
}
102+
unit.m_symtab->add_symbol(global_underscore_name, down_cast<ASR::symbol_t>(global_underscore));
103+
ASR::stmt_t* asr_stmt = ASRUtils::STMT(ASR::make_Assignment_t(al, loc, global_underscore_ref, return_var_ref, nullptr));
104+
body.push_back(al, asr_stmt);
145105
}
146106

147107
ASR::asr_t *fn = ASRUtils::make_Function_t_util(

src/lpython/python_evaluator.cpp

Lines changed: 159 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
#ifdef HAVE_LFORTRAN_LLVM
1717
#include <libasr/codegen/evaluator.h>
1818
#include <libasr/codegen/asr_to_llvm.h>
19+
#include <llvm/IR/Function.h>
20+
#include <llvm/IR/Type.h>
21+
#include <llvm/IR/DataLayout.h>
1922
#else
2023
namespace LCompilers {
2124
class LLVMEvaluator {};
@@ -120,10 +123,24 @@ Result<PythonCompiler::EvalResult> PythonCompiler::evaluate(
120123
}
121124

122125
bool call_run_fn = false;
123-
std::string return_type;
124-
if (m->get_return_type(run_fn) != "none") {
125-
call_run_fn = true;
126-
return_type = m->get_return_type(run_fn);
126+
std::string return_type = m->get_return_type(run_fn);
127+
if (return_type != "none") {
128+
call_run_fn = true;
129+
}
130+
131+
ASR::symbol_t *global_underscore_sym = symbol_table->get_symbol("_" + run_fn);
132+
if ((return_type == "struct") && (global_underscore_sym)) {
133+
// we compute the offsets of the struct's attribute here
134+
// we will be using it later in aggregate_type_to_string to print the struct
135+
136+
// we compute the offsets here instead of computing it in aggregate_type_to_string
137+
// because once we call `e->add_module`, internally LLVM may deallocate all the
138+
// type info after compiling the IR into machine code
139+
140+
llvm::Function *fn = m->get_function(run_fn);
141+
llvm::Type *llvm_type = fn->getReturnType();
142+
LCOMPILERS_ASSERT(llvm_type->isStructTy())
143+
compute_offsets(llvm_type, global_underscore_sym, result);
127144
}
128145

129146
e->add_module(std::move(m));
@@ -213,6 +230,14 @@ Result<PythonCompiler::EvalResult> PythonCompiler::evaluate(
213230
bool r = e->execfn<bool>(run_fn);
214231
result.type = EvalResult::boolean;
215232
result.b = r;
233+
} else if (return_type == "struct") {
234+
e->execfn<void>(run_fn);
235+
if (global_underscore_sym) {
236+
void *r = (void*)e->get_symbol_address("_" + run_fn);
237+
LCOMPILERS_ASSERT(r)
238+
result.structure.structure = r;
239+
result.type = EvalResult::struct_type;
240+
}
216241
} else if (return_type == "void") {
217242
e->execfn<void>(run_fn);
218243
result.type = EvalResult::statement;
@@ -449,4 +474,134 @@ Result<std::string> PythonCompiler::get_asm(
449474
}
450475

451476

477+
void print_type(ASR::ttype_t *t, void *data, std::string &result);
478+
479+
std::string PythonCompiler::aggregate_type_to_string(const struct EvalResult &r) {
480+
ASR::ttype_t *asr_type = r.structure.ttype;
481+
void *data = r.structure.structure;
482+
size_t *offsets = r.structure.offsets;
483+
size_t element_size = r.structure.element_size;
484+
485+
std::string result;
486+
487+
if (asr_type->type == ASR::ttypeType::List) {
488+
int32_t size = *(int32_t*)(((char*)data)+offsets[0]);
489+
void *array = *(void**)(((char*)data)+offsets[2]);
490+
ASR::ttype_t *element_ttype = ASR::down_cast<ASR::List_t>(asr_type)->m_type;
491+
492+
result += "[";
493+
for (int32_t i = 0; i < size - 1; i++) {
494+
print_type(element_ttype, ((char*)array)+(i*element_size), result);
495+
result += ", ";
496+
}
497+
print_type(element_ttype, ((char*)array)+((size - 1)*element_size), result);
498+
result += "]";
499+
500+
} else {
501+
throw LCompilersException("PythonCompiler::evaluate(): Return type not supported");
502+
}
503+
504+
return result;
505+
}
506+
507+
void PythonCompiler::compute_offsets(llvm::Type *type, ASR::symbol_t *asr_type, EvalResult &result) {
508+
#ifdef HAVE_LFORTRAN_LLVM
509+
LCOMPILERS_ASSERT(type->isStructTy())
510+
511+
const llvm::DataLayout &dl = e->get_jit_data_layout();
512+
size_t elements_count = type->getStructNumElements();
513+
LCompilers::Vec<size_t> offsets;
514+
offsets.reserve(al, elements_count);
515+
for (size_t i = 0; i < elements_count; i++) {
516+
size_t offset = dl.getStructLayout((llvm::StructType*)type)->getElementOffset(i);
517+
offsets.push_back(al, offset);
518+
}
519+
result.structure.offsets = offsets.p;
520+
521+
result.structure.ttype = ASR::down_cast<ASR::Variable_t>(asr_type)->m_type;
522+
if (result.structure.ttype->type == ASR::ttypeType::List) {
523+
type = type->getStructElementType(2);
524+
LCOMPILERS_ASSERT(type->isPointerTy())
525+
result.structure.element_size = e->get_jit_data_layout().getTypeAllocSize(
526+
#if LLVM_VERSION_MAJOR >= 14
527+
type->getNonOpaquePointerElementType()
528+
#else
529+
type->getPointerElementType()
530+
#endif
531+
);
532+
}
533+
#else
534+
throw LCompilersException("LLVM is not enabled");
535+
#endif
536+
}
537+
538+
void print_type(ASR::ttype_t *t, void *data, std::string &result) {
539+
switch (t->type) {
540+
case ASR::ttypeType::Logical:
541+
result += (*(bool*)data ? "True" : "False");
542+
break;
543+
case ASR::ttypeType::Integer: {
544+
int64_t a_kind = ASR::down_cast<ASR::Integer_t>(t)->m_kind;
545+
switch (a_kind) {
546+
case 1:
547+
result += std::to_string(int(*(int8_t*)data));
548+
break;
549+
case 2:
550+
result += std::to_string(*(int16_t*)data);
551+
break;
552+
case 4:
553+
result += std::to_string(*(int32_t*)data);
554+
break;
555+
case 8:
556+
result += std::to_string(*(int64_t*)data);
557+
break;
558+
default:
559+
throw LCompilersException("Unaccepted int size");
560+
}
561+
break;
562+
}
563+
case ASR::ttypeType::UnsignedInteger: {
564+
int64_t a_kind = ASR::down_cast<ASR::UnsignedInteger_t>(t)->m_kind;
565+
switch (a_kind) {
566+
case 1:
567+
result += std::to_string(int(*(uint8_t*)data));
568+
break;
569+
case 2:
570+
result += std::to_string(*(uint16_t*)data);
571+
break;
572+
case 4:
573+
result += std::to_string(*(uint32_t*)data);
574+
break;
575+
case 8:
576+
result += std::to_string(*(uint64_t*)data);
577+
break;
578+
default:
579+
throw LCompilersException("Unaccepted int size");
580+
}
581+
break;
582+
}
583+
case ASR::ttypeType::Real: {
584+
int64_t a_kind = ASR::down_cast<ASR::Real_t>(t)->m_kind;
585+
switch (a_kind) {
586+
case 4:
587+
result += std::to_string(*(float*)data);
588+
break;
589+
case 8:
590+
result += std::to_string(*(double*)data);
591+
break;
592+
default:
593+
throw LCompilersException("Unaccepted real size");
594+
}
595+
break;
596+
}
597+
case ASR::ttypeType::Character:
598+
result += '"';
599+
result += std::string(*(char**)data); // TODO: replace \n with \\n
600+
result += '"';
601+
break;
602+
default:
603+
throw LCompilersException("PythonCompiler::print_type(): type not supported");
604+
}
605+
}
606+
452607
} // namespace LCompilers::LPython

0 commit comments

Comments
 (0)