Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for pointer in LC by porting integration_tests/associate_02.f90 from LFortran #83

Merged
merged 7 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions integration_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,6 @@ RUN(NAME struct_02.cpp LABELS gcc llvm NOFAST)
RUN(NAME struct_03.cpp LABELS gcc llvm NOFAST)
RUN(NAME struct_04.cpp LABELS gcc llvm NOFAST)

RUN(NAME pointer_01.cpp LABELS gcc llvm NOFAST)

RUN(NAME function_01.cpp LABELS gcc llvm NOFAST)
49 changes: 49 additions & 0 deletions integration_tests/pointer_01.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include <iostream>
#include <complex>

using namespace std::complex_literals;

#define check(p, t, v) if( *p != v ) { \
exit(2); \
} \
if( t != v ) { \
exit(2); \
} \

int main() {

int* p1;
double* p2;
int t1 = 2;
double t2 = 2.0;
std::complex<double>* p3;
std::complex<double> t3 = 2.0 + 1i*3.0;

p1 = &t1;
p2 = &t2;
p3 = &t3;
*p1 = 1;
*p2 = 4.0;

std::cout << *p1 << std::endl;
std::cout << t1 << std::endl;
check(p1, t1, 1)

t1 = *p2 + *p1;

std::cout << *p1 << std::endl;
std::cout << t1 << std::endl;
check(p1, t1, 5);

t1 = 8;

std::cout << *p1 << std::endl;
std::cout << t1 << std::endl;
check(p1, t1, 8);

*p3 = 2.0 * (*p3);
std::cout << *p3 << std::endl;
std::cout << t3 << std::endl;
check(p3, t3, 4.0 + 1i*6.0);

}
68 changes: 45 additions & 23 deletions src/lc/clang_ast_to_asr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,17 @@ class ClangASTtoASRVisitor: public clang::RecursiveASTVisitor<ClangASTtoASRVisit
return dummy_variable_sym;
}

ASR::asr_t* make_Assignment_t_util(Allocator &al, const Location &a_loc,
ASR::expr_t* a_target, ASR::expr_t* a_value, ASR::stmt_t* a_overloaded) {
if( ASR::is_a<ASR::AddressOf_t>(*a_value) ) {
a_value = ASRUtils::EXPR(ASR::make_Var_t(al, a_value->base.loc,
ASR::down_cast<ASR::AddressOf_t>(a_value)->m_v));
return ASR::make_Associate_t(al, a_loc, a_target, a_value);
}

return ASR::make_Assignment_t(al, a_loc, a_target, a_value, a_overloaded);
}

void construct_program() {
// Convert the main function into a program
ASR::TranslationUnit_t* tu = (ASR::TranslationUnit_t*)this->tu;
Expand All @@ -236,7 +247,7 @@ class ClangASTtoASRVisitor: public clang::RecursiveASTVisitor<ClangASTtoASRVisit

ASR::symbol_t* exit_variable_sym = declare_dummy_variable("exit_code", program_scope, loc, int32_type);
ASR::expr_t* variable_var = ASRUtils::EXPR(ASR::make_Var_t(al, loc, exit_variable_sym));
ASR::asr_t* assign_stmt = ASR::make_Assignment_t(al, loc, variable_var, ASRUtils::EXPR(func_call_main), nullptr);
ASR::asr_t* assign_stmt = make_Assignment_t_util(al, loc, variable_var, ASRUtils::EXPR(func_call_main), nullptr);

prog_body.push_back(al, ASRUtils::STMT(assign_stmt));

Expand Down Expand Up @@ -726,7 +737,7 @@ class ClangASTtoASRVisitor: public clang::RecursiveASTVisitor<ClangASTtoASRVisit
ASR::expr_t* value = ASRUtils::EXPR(tmp.get());
cast_helper(obj, value, true);
ASRUtils::make_ArrayBroadcast_t_util(al, Lloc(x), obj, value);
tmp = ASR::make_Assignment_t(al, Lloc(x), obj, value, nullptr);
tmp = make_Assignment_t_util(al, Lloc(x), obj, value, nullptr);
is_stmt_created = true;
}
assignment_target = nullptr;
Expand All @@ -737,7 +748,7 @@ class ClangASTtoASRVisitor: public clang::RecursiveASTVisitor<ClangASTtoASRVisit
if( !is_stmt_created ) {
ASR::expr_t* value = ASRUtils::EXPR(tmp.get());
cast_helper(obj, value, true);
tmp = ASR::make_Assignment_t(al, Lloc(x), obj, value, nullptr);
tmp = make_Assignment_t_util(al, Lloc(x), obj, value, nullptr);
is_stmt_created = true;
}
assignment_target = nullptr;
Expand Down Expand Up @@ -962,7 +973,7 @@ class ClangASTtoASRVisitor: public clang::RecursiveASTVisitor<ClangASTtoASRVisit
ASR::abiType::Source, false, target_physical_type, override_physical_type, false);
ASR::expr_t* new_shape = ASRUtils::cast_to_descriptor(al, args.p[0]);
tmp = ASR::make_ArrayReshape_t(al, Lloc(x), callee, new_shape, target_type, nullptr);
tmp = ASR::make_Assignment_t(al, Lloc(x), callee, ASRUtils::EXPR(tmp.get()), nullptr);
tmp = make_Assignment_t_util(al, Lloc(x), callee, ASRUtils::EXPR(tmp.get()), nullptr);
is_stmt_created = true;
} else if (sf == SpecialFunc::Size) {
if( args.size() != 0 ) {
Expand All @@ -982,7 +993,7 @@ class ClangASTtoASRVisitor: public clang::RecursiveASTVisitor<ClangASTtoASRVisit

ASR::expr_t* arg = args.p[0];
ASRUtils::make_ArrayBroadcast_t_util(al, Lloc(x), callee, arg);
tmp = ASR::make_Assignment_t(al, Lloc(x), callee, arg, nullptr);
tmp = make_Assignment_t_util(al, Lloc(x), callee, arg, nullptr);
is_stmt_created = true;
} else if( sf == SpecialFunc::All ) {
// Handles xt::all() - no arguments
Expand Down Expand Up @@ -1331,7 +1342,7 @@ class ClangASTtoASRVisitor: public clang::RecursiveASTVisitor<ClangASTtoASRVisit
if( tmp != nullptr && !is_stmt_created ) {
ASR::expr_t* init_val = ASRUtils::EXPR(tmp.get());
add_reshape_if_needed(init_val, var);
tmp = ASR::make_Assignment_t(al, Lloc(x), var, init_val, nullptr);
tmp = make_Assignment_t_util(al, Lloc(x), var, init_val, nullptr);
is_stmt_created = true;
}
}
Expand Down Expand Up @@ -1545,7 +1556,7 @@ class ClangASTtoASRVisitor: public clang::RecursiveASTVisitor<ClangASTtoASRVisit
if( op == clang::BO_Assign ) {
cast_helper(x_lhs, x_rhs, true);
ASRUtils::make_ArrayBroadcast_t_util(al, Lloc(x), x_lhs, x_rhs);
tmp = ASR::make_Assignment_t(al, Lloc(x), x_lhs, x_rhs, nullptr);
tmp = make_Assignment_t_util(al, Lloc(x), x_lhs, x_rhs, nullptr);
is_stmt_created = true;
} else {
bool is_binop = false, is_cmpop = false;
Expand Down Expand Up @@ -1689,22 +1700,10 @@ class ClangASTtoASRVisitor: public clang::RecursiveASTVisitor<ClangASTtoASRVisit
}

void cast_helper(ASR::expr_t*& left, ASR::expr_t*& right, bool is_assign) {
bool no_cast = ((ASR::is_a<ASR::Pointer_t>(*ASRUtils::expr_type(left)) &&
ASR::is_a<ASR::Var_t>(*left)) ||
(ASR::is_a<ASR::Pointer_t>(*ASRUtils::expr_type(right)) &&
ASR::is_a<ASR::Var_t>(*right)));
ASR::ttype_t *right_type = ASRUtils::expr_type(right);
ASR::ttype_t *left_type = ASRUtils::expr_type(left);
left_type = ASRUtils::extract_type(left_type);
right_type = ASRUtils::extract_type(right_type);
if( no_cast ) {
int lkind = ASRUtils::extract_kind_from_ttype_t(left_type);
int rkind = ASRUtils::extract_kind_from_ttype_t(right_type);
if( left_type->type != right_type->type || lkind != rkind ) {
throw SemanticError("Casting for mismatching pointer types not supported yet.",
right_type->base.loc);
}
}

// Handle presence of logical types in binary operations
// by converting them into 32-bit integers.
Expand Down Expand Up @@ -1886,7 +1885,7 @@ class ClangASTtoASRVisitor: public clang::RecursiveASTVisitor<ClangASTtoASRVisit
ASR::symbol_t* return_sym = current_scope->resolve_symbol("__return_var");
ASR::expr_t* return_var = ASRUtils::EXPR(ASR::make_Var_t(al, Lloc(x), return_sym));
TraverseStmt(x->getRetValue());
tmp = ASR::make_Assignment_t(al, Lloc(x), return_var, ASRUtils::EXPR(tmp.get()), nullptr);
tmp = make_Assignment_t_util(al, Lloc(x), return_var, ASRUtils::EXPR(tmp.get()), nullptr);
current_body->push_back(al, ASRUtils::STMT(tmp.get()));
tmp = ASR::make_Return_t(al, Lloc(x));
is_stmt_created = true;
Expand Down Expand Up @@ -1939,7 +1938,7 @@ class ClangASTtoASRVisitor: public clang::RecursiveASTVisitor<ClangASTtoASRVisit
ASR::expr_t* x_rhs = ASRUtils::EXPR(tmp.get());
CreateBinOp(x_lhs, x_rhs, ASR::binopType::Add, Lloc(x));
ASR::expr_t* sum_expr = ASRUtils::EXPR(tmp.get());
tmp = ASR::make_Assignment_t(al, Lloc(x), x_lhs, sum_expr, nullptr);
tmp = make_Assignment_t_util(al, Lloc(x), x_lhs, sum_expr, nullptr);
is_stmt_created = true;
return true;
}
Expand All @@ -1956,7 +1955,7 @@ class ClangASTtoASRVisitor: public clang::RecursiveASTVisitor<ClangASTtoASRVisit
ASRUtils::EXPR(ASR::make_IntegerConstant_t(
al, Lloc(x), 1, ASRUtils::expr_type(var))),
ASRUtils::expr_type(var), nullptr));
tmp = ASR::make_Assignment_t(al, Lloc(x), var, incbyone, nullptr);
tmp = make_Assignment_t_util(al, Lloc(x), var, incbyone, nullptr);
is_stmt_created = true;
break;
}
Expand All @@ -1968,8 +1967,31 @@ class ClangASTtoASRVisitor: public clang::RecursiveASTVisitor<ClangASTtoASRVisit
CreateLogicalNot(var, Lloc(x));
break;
}
case clang::UnaryOperatorKind::UO_AddrOf: {
if( !ASR::is_a<ASR::Var_t>(*var) ) {
throw std::runtime_error("Address of operator is only supported for symbols.");
}

ASR::Var_t* var_t = ASR::down_cast<ASR::Var_t>(var);
tmp = ASR::make_AddressOf_t(al, Lloc(x), var_t->m_v, ASRUtils::TYPE(
ASR::make_Pointer_t(al, Lloc(x), ASRUtils::expr_type(var))));
is_stmt_created = false;
break;
}
case clang::UnaryOperatorKind::UO_Deref: {
if( !ASR::is_a<ASR::Var_t>(*var) ) {
throw std::runtime_error("Dereference operator is only supported for symbols.");
}

ASR::Var_t* var_t = ASR::down_cast<ASR::Var_t>(var);
tmp = ASR::make_DereferencePointer_t(al, Lloc(x), var_t->m_v,
ASRUtils::type_get_past_pointer(ASRUtils::expr_type(var)));
is_stmt_created = false;
break;
}
default: {
throw std::runtime_error("Only postfix increment and minus are supported so far.");
throw std::runtime_error("Only postfix increment, minus and "
"address of operators are supported so far.");
}
}
return true;
Expand Down
2 changes: 2 additions & 0 deletions src/libasr/ASR.asdl
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ expr
| DictLen(expr arg, ttype type, expr? value)

| Var(symbol v)
| AddressOf(symbol v, ttype type)
Copy link
Contributor

@certik certik Feb 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to be careful here.

This seems to be the same as CLoc (or GetPointer). These two nodes should be merged then.

Also we want to be able to add an optional debug time checking for dangling pointers. So we have to be careful how these nodes are designed, so that it is possible to add checking.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be the same as CLoc (or GetPointer). These two nodes should be merged then.

AddressOf can be called on anything including array items, any type of variable, functions. However, c_loc can only be called on variables that too with target or pointer attribute. Also, AddressOf as a separate node will be very helpful when implementing pointer arithmetic features. Merging nodes can be option when we implement pointer arithmetic features, until then I would suggest not to merge nodes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not support pointer arithmetic, unless we absolutely have to. That's a feature that will be hard to check in Debug mode. We have to be extremely careful with introducing new features to ASR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See here: lfortran/lfortran#3354.

I am afraid we'll have to revert this change.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I will revert this PR and turn off the test for llvm.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in #88

| DereferencePointer(symbol v, ttype type)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we introduce a node like this one, it should be consistently used everywhere. I think it might simplify the LLVM backend with all the loads.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a compile time value is needed. Furthermore, isn't this the same as Var? Var currently automatically dereferences.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a compile time value is needed.

I don't think so. Pointer is an address, available at runtime. So we can only know what's in the address when we run the code. Consider the following example,

#include <iostream>

int main() {

    int p = 10;
    const int* ptr = &p;

    p = 20;

    std::cout << *ptr << std::endl;

    return 0;
}
(lc) 14:43:21:~/lc_project/lc % ./a.out             
20

| FunctionParam(int param_number, ttype type, expr? value) --- used in types

| ArrayConstant(expr* args, ttype type, arraystorage storage_format)
Expand Down
3 changes: 3 additions & 0 deletions src/libasr/asdl_cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2603,6 +2603,9 @@ def make_visitor(self, name, fields):
elif name.endswith("Constant") or name == "IntegerBOZ":
self.emit("case ASR::exprType::%s: { return f; }"\
% (name), 2, new_line=False)
elif name == "AddressOf" or name == "DereferencePointer":
self.emit("case ASR::exprType::%s: { return nullptr; }"\
% (name), 2, new_line=False)
else:
self.emit("case ASR::exprType::%s: { return ((ASR::%s_t*)f)->m_value; }"\
% (name, name), 2, new_line=False)
Expand Down
10 changes: 8 additions & 2 deletions src/libasr/asr_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,14 @@ static inline int extract_kind_from_ttype_t(const ASR::ttype_t* type) {

static inline ASR::Variable_t* EXPR2VAR(const ASR::expr_t *f)
{
return ASR::down_cast<ASR::Variable_t>(symbol_get_past_external(
ASR::down_cast<ASR::Var_t>(f)->m_v));
if( ASR::is_a<ASR::Var_t>(*f) ) {
return ASR::down_cast<ASR::Variable_t>(symbol_get_past_external(
ASR::down_cast<ASR::Var_t>(f)->m_v));
} else if( ASR::is_a<ASR::DereferencePointer_t>(*f) ) {
return ASR::down_cast<ASR::Variable_t>(symbol_get_past_external(
ASR::down_cast<ASR::DereferencePointer_t>(f)->m_v));
}
return nullptr;
}

static inline ASR::Function_t* EXPR2FUN(const ASR::expr_t *f)
Expand Down
11 changes: 10 additions & 1 deletion src/libasr/codegen/asr_to_llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6520,12 +6520,21 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
}
}

void visit_Var(const ASR::Var_t &x) {
template <typename T>
void visit_SymbolContainingExpr(const T& x) {
ASR::Variable_t *v = ASR::down_cast<ASR::Variable_t>(
symbol_get_past_external(x.m_v));
fetch_var(v);
}

void visit_Var(const ASR::Var_t& x) {
visit_SymbolContainingExpr(x);
}

void visit_DereferencePointer(const ASR::DereferencePointer_t& x) {
visit_SymbolContainingExpr(x);
}

inline ASR::ttype_t* extract_ttype_t_from_expr(ASR::expr_t* expr) {
return ASRUtils::expr_type(expr);
}
Expand Down
Loading