Skip to content

Pointers in emulation mode #1336

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

Merged
merged 7 commits into from
Dec 1, 2022
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
1 change: 1 addition & 0 deletions integration_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ RUN(NAME structs_11 LABELS cpython llvm c)
RUN(NAME structs_12 LABELS cpython llvm c)
RUN(NAME structs_13 LABELS llvm c
EXTRAFILES structs_13b.c)
RUN(NAME structs_15 LABELS cpython llvm c)
RUN(NAME sizeof_01 LABELS llvm c
EXTRAFILES sizeof_01b.c)
RUN(NAME enum_01 LABELS cpython llvm c)
Expand Down
5 changes: 2 additions & 3 deletions integration_tests/bindc_01.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from ltypes import c_p_pointer, CPtr, i16
from ltypes import c_p_pointer, CPtr, i16, Pointer

queries: CPtr
x: Pointer[i16]
c_p_pointer(queries, x)
x: Pointer[i16] = c_p_pointer(queries, i16)
print(queries, x)
5 changes: 2 additions & 3 deletions integration_tests/bindc_02.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from ltypes import c_p_pointer, CPtr, pointer, i16, Pointer

queries: CPtr
x: Pointer[i16[:]]
c_p_pointer(queries, x)
x: Pointer[i16[:]] = c_p_pointer(queries, i16[:])
print(queries, x)

def f():
Expand All @@ -17,7 +16,7 @@ def f():
assert yptr1[0] == i16(1)
assert yptr1[1] == i16(2)

c_p_pointer(yq, yptr1)
yptr1 = c_p_pointer(yq, i16[:])

print(yq, yptr1)

Expand Down
6 changes: 2 additions & 4 deletions integration_tests/bindc_03.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ def get_array(size: i32) -> CPtr:
def f(q_void: CPtr) -> None:
i: i32
el: i32
q: Pointer[i32[:]]
c_p_pointer(q_void, q)
q: Pointer[i32[:]] = c_p_pointer(q_void, i32[:])
for i in range(10):
q2: CPtr
p_c_pointer(pointer(q[i]), q2)
Expand All @@ -33,8 +32,7 @@ def f(q_void: CPtr) -> None:
def h(q_void: CPtr) -> None:
i: i32
el: i32
q: Pointer[i32[:]]
c_p_pointer(q_void, q)
q: Pointer[i32[:]] = c_p_pointer(q_void, i32[:])
for i in range(10):
# TODO: Use q[i] directly in the assert.
el = q[i]
Expand Down
41 changes: 41 additions & 0 deletions integration_tests/structs_15.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from ltypes import i32, i16, i8, i64, CPtr, dataclass, ccall, Pointer, c_p_pointer, sizeof

@dataclass
class A:
x: i16
y: i8

@ccall
def _lfortran_malloc(size: i32) -> CPtr:
pass

@ccall
def _lfortran_memset(cptr: CPtr, value: i32, size: i32):
pass

def add_A_members(Ax: i16, Ay: i8) -> i16:
return Ax + i16(Ay)

def test_Aptr_member_passing():
print(sizeof(A))

a_cptr: CPtr = _lfortran_malloc(i32(sizeof(A)))
_lfortran_memset(a_cptr, 2, i32(sizeof(A)))
b_cptr: CPtr = _lfortran_malloc(i32(sizeof(A)))
_lfortran_memset(b_cptr, 6, i32(sizeof(A)))

a_ptr: Pointer[A] = c_p_pointer(a_cptr, A)
b_ptr: Pointer[A] = c_p_pointer(b_cptr, A)
print(a_ptr.x, a_ptr.y)
print(b_ptr.x, b_ptr.y)
assert a_ptr.x * i16(3) == b_ptr.x
assert a_ptr.y * i8(3) == b_ptr.y

a_ptr.y = i8(-18)
assert a_ptr.x * i16(3) == b_ptr.x
a_ptr.x = i16(20)
print(a_ptr.x, a_ptr.y)
print(add_A_members(a_ptr.x, a_ptr.y))
assert add_A_members(a_ptr.x, a_ptr.y) == i16(2)

test_Aptr_member_passing()
8 changes: 6 additions & 2 deletions src/libasr/runtime/lfortran_intrinsics.c
Original file line number Diff line number Diff line change
Expand Up @@ -886,8 +886,12 @@ LFORTRAN_API char* _lfortran_str_chr(int val)
return dest_char;
}

LFORTRAN_API char* _lfortran_malloc(int size) {
return (char*)malloc(size);
LFORTRAN_API void _lfortran_memset(void* s, int32_t c, int32_t size) {
memset(s, c, size);
}

LFORTRAN_API void* _lfortran_malloc(int32_t size) {
return malloc(size);
}

LFORTRAN_API int8_t* _lfortran_realloc(int8_t* ptr, int32_t size) {
Expand Down
3 changes: 2 additions & 1 deletion src/libasr/runtime/lfortran_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ LFORTRAN_API int _lfortran_str_ord(char** s);
LFORTRAN_API int _lfortran_str_ord_c(char* s);
LFORTRAN_API char* _lfortran_str_chr(int c);
LFORTRAN_API int _lfortran_str_to_int(char** s);
LFORTRAN_API char* _lfortran_malloc(int size);
LFORTRAN_API void* _lfortran_malloc(int32_t size);
LFORTRAN_API void _lfortran_memset(void* s, int32_t c, int32_t size);
LFORTRAN_API int8_t* _lfortran_realloc(int8_t* ptr, int32_t size);
LFORTRAN_API int8_t* _lfortran_calloc(int32_t count, int32_t size);
LFORTRAN_API void _lfortran_free(char* ptr);
Expand Down
61 changes: 55 additions & 6 deletions src/lpython/semantics/python_ast_to_asr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,8 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
std::string import_path;
Vec<ASR::stmt_t*> *current_body;
ASR::ttype_t* ann_assign_target_type;
AST::expr_t* assign_ast_target;
bool is_c_p_pointer_call;

std::map<std::string, int> generic_func_nums;
std::map<std::string, std::map<std::string, ASR::ttype_t*>> generic_func_subs;
Expand All @@ -551,8 +553,8 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
std::string import_path, bool allow_implicit_casting_)
: diag{diagnostics}, al{al}, lm{lm}, current_scope{symbol_table}, main_module{main_module},
ast_overload{ast_overload}, parent_dir{parent_dir}, import_path{import_path},
current_body{nullptr}, ann_assign_target_type{nullptr},
allow_implicit_casting{allow_implicit_casting_} {
current_body{nullptr}, ann_assign_target_type{nullptr}, assign_ast_target{nullptr},
is_c_p_pointer_call{false}, allow_implicit_casting{allow_implicit_casting_} {
current_module_dependencies.reserve(al, 4);
}

Expand Down Expand Up @@ -2211,6 +2213,16 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
current_scope->add_symbol(var_name, v_sym);
}

ASR::asr_t* create_CPtrToPointerFromArgs(AST::expr_t* ast_cptr, AST::expr_t* ast_pptr,
const Location& loc) {
this->visit_expr(*ast_cptr);
ASR::expr_t* cptr = ASRUtils::EXPR(tmp);
this->visit_expr(*ast_pptr);
ASR::expr_t* pptr = ASRUtils::EXPR(tmp);
return ASR::make_CPtrToPointer_t(al, loc, cptr,
pptr, nullptr);
}

void visit_AnnAssignUtil(const AST::AnnAssign_t& x, std::string& var_name,
bool wrap_derived_type_in_pointer=false,
ASR::expr_t* init_expr=nullptr, ASR::abiType abi=ASR::abiType::Source) {
Expand All @@ -2222,13 +2234,25 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
type = ASRUtils::TYPE(ASR::make_Pointer_t(al, type->base.loc, type));
}

bool is_c_p_pointer_call_copy = is_c_p_pointer_call;
ASR::expr_t *value = nullptr;
if( !init_expr ) {
tmp = nullptr;
is_c_p_pointer_call = false;
if (x.m_value) {
this->visit_expr(*x.m_value);
}
if (tmp) {
if( is_c_p_pointer_call ) {
create_add_variable_to_scope(var_name, nullptr, nullptr, type,
x.base.base.loc, abi);
AST::Call_t* c_p_pointer_call = AST::down_cast<AST::Call_t>(x.m_value);
AST::expr_t* cptr = c_p_pointer_call->m_args[0];
AST::expr_t* pptr = assign_ast_target;
tmp = create_CPtrToPointerFromArgs(cptr, pptr, x.base.base.loc);
// if( current_body ) {
// current_body->push_back(al, ASRUtils::STMT(tmp));
// }
} else if (tmp) {
value = ASRUtils::EXPR(tmp);
ASR::ttype_t* underlying_type = type;
if( ASR::is_a<ASR::Const_t>(*type) ) {
Expand Down Expand Up @@ -2256,10 +2280,16 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
} else {
cast_helper(type, init_expr, init_expr->base.loc);
}
create_add_variable_to_scope(var_name, init_expr, value, type,
x.base.base.loc, abi);

tmp = nullptr;
if( !is_c_p_pointer_call ) {
create_add_variable_to_scope(var_name, init_expr, value, type,
x.base.base.loc, abi);
}

if( !is_c_p_pointer_call ) {
tmp = nullptr;
}
is_c_p_pointer_call = is_c_p_pointer_call_copy;
ann_assign_target_type = ann_assign_target_type_copy;
}

Expand Down Expand Up @@ -3869,6 +3899,8 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
// We treat this as a declaration
std::string var_name;
std::string var_annotation;
AST::expr_t* assign_ast_target_copy = assign_ast_target;
assign_ast_target = x.m_target;
if (AST::is_a<AST::Name_t>(*x.m_target)) {
AST::Name_t *n = AST::down_cast<AST::Name_t>(x.m_target);
var_name = n->m_id;
Expand All @@ -3893,6 +3925,7 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
}

visit_AnnAssignUtil(x, var_name);
assign_ast_target = assign_ast_target_copy;
}

void visit_Delete(const AST::Delete_t &x) {
Expand Down Expand Up @@ -3924,7 +3957,19 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {

void visit_Assign(const AST::Assign_t &x) {
ASR::expr_t *target, *assign_value = nullptr, *tmp_value;
bool is_c_p_pointer_call_copy = is_c_p_pointer_call;
is_c_p_pointer_call = false;
this->visit_expr(*x.m_value);
if( is_c_p_pointer_call ) {
LFORTRAN_ASSERT(x.n_targets == 1);
AST::Call_t* c_p_pointer_call = AST::down_cast<AST::Call_t>(x.m_value);
AST::expr_t* cptr = c_p_pointer_call->m_args[0];
AST::expr_t* pptr = x.m_targets[0];
tmp = create_CPtrToPointerFromArgs(cptr, pptr, x.base.base.loc);
is_c_p_pointer_call = is_c_p_pointer_call;
return ;
}
is_c_p_pointer_call = is_c_p_pointer_call_copy;
if (tmp) {
// This happens if `m.m_value` is `empty`, such as in:
// a = empty(16)
Expand Down Expand Up @@ -5813,6 +5858,10 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
tmp = intrinsic_node_handler.get_intrinsic_node(call_name, al,
x.base.base.loc, args);
return;
} else if (call_name == "c_p_pointer") {
is_c_p_pointer_call = true;
tmp = nullptr;
return ;
} else {
// The function was not found and it is not intrinsic
throw SemanticError("Function '" + call_name + "' is not declared and not intrinsic",
Expand Down
44 changes: 41 additions & 3 deletions src/runtime/ltypes/ltypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
__slots__ = ["i8", "i16", "i32", "i64", "f32", "f64", "c32", "c64", "CPtr",
"overload", "ccall", "TypeVar", "pointer", "c_p_pointer", "Pointer",
"p_c_pointer", "vectorize", "inline", "Union", "static", "with_goto",
"packed", "Const"]
"packed", "Const", "sizeof"]

# data-types

Expand All @@ -25,6 +25,8 @@ def __call__(self, arg):

class Pointer:
def __getitem__(self, type):
if is_dataclass(type):
return convert_to_ctypes_Structure(type)
return type

class ConstType(Type):
Expand Down Expand Up @@ -174,6 +176,8 @@ def convert_type_to_ctype(arg):
elif isinstance(arg, Array):
type = convert_type_to_ctype(arg._type)
return ctypes.POINTER(type)
elif is_dataclass(arg):
return convert_to_ctypes_Structure(arg)
else:
raise NotImplementedError("Type %r not implemented" % arg)

Expand All @@ -196,7 +200,7 @@ def get_lib_name(name):
else:
raise NotImplementedError("Platform not implemented")
def get_crtlib_path():
py_mod = os.environ["LPYTHON_PY_MOD_NAME"]
py_mod = os.environ.get("LPYTHON_PY_MOD_NAME", "")
if py_mod == "":
return os.path.join(get_rtlib_dir(),
get_lib_name("lpython_runtime"))
Expand Down Expand Up @@ -242,6 +246,19 @@ def convert_to_ctypes_Union(f):

return f

def convert_to_ctypes_Structure(f):
fields = []
for name in f.__annotations__:
ltype_ = f.__annotations__[name]
fields.append((name, convert_type_to_ctype(ltype_)))

class ctypes_Structure(ctypes.Structure):
_fields_ = fields

ctypes_Structure.__name__ = f.__name__

return ctypes_Structure

def ccall(f):
if isclass(f) and issubclass(f, Union):
return f
Expand Down Expand Up @@ -280,11 +297,32 @@ def pointer(x, type=None):
else:
raise Exception("Type not supported in pointer()")

class PointerToStruct:

def __init__(self, ctypes_ptr_):
self.__dict__["ctypes_ptr"] = ctypes_ptr_

def __getattr__(self, name: str):
if name == "ctypes_ptr":
return self.__dict__[name]
return self.ctypes_ptr.contents.__getattribute__(name)

def __setattr__(self, name: str, value):
self.ctypes_ptr.contents.__setattr__(name, value)

def c_p_pointer(cptr, targettype):
return pointer(targettype)
targettype_ptr = ctypes.POINTER(convert_type_to_ctype(targettype))
newa = ctypes.cast(cptr, targettype_ptr)
if is_dataclass(targettype):
# return after wrapping newa inside PointerToStruct
return PointerToStruct(newa)
return newa

def p_c_pointer(ptr, cptr):
cptr.value = ptr.value

def empty_c_void_p():
return ctypes.c_void_p()

def sizeof(arg):
return ctypes.sizeof(convert_type_to_ctype(arg))
2 changes: 1 addition & 1 deletion tests/reference/asr-bindc_01-6d521a9.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"basename": "asr-bindc_01-6d521a9",
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
"infile": "tests/../integration_tests/bindc_01.py",
"infile_hash": "d4690d665de9b941c2581ad02041822e1e322dba55485cf8534dfe0a",
"infile_hash": "ce44dc48f31bcf876253727ca139210d99193565be1cf9b5fd3dea40",
"outfile": null,
"outfile_hash": null,
"stdout": "asr-bindc_01-6d521a9.stdout",
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/asr-bindc_02-bc1a7ea.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"basename": "asr-bindc_02-bc1a7ea",
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
"infile": "tests/../integration_tests/bindc_02.py",
"infile_hash": "26491ee7e9450cc430e5e6b443510ea0cc7507975baa8d8768e6f287",
"infile_hash": "f7a7faa22c2440be545994170ef21f451ce95960c7c7c217db5221fb",
"outfile": null,
"outfile_hash": null,
"stdout": "asr-bindc_02-bc1a7ea.stdout",
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/llvm-bindc_01-c984f09.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"basename": "llvm-bindc_01-c984f09",
"cmd": "lpython --no-color --show-llvm {infile} -o {outfile}",
"infile": "tests/../integration_tests/bindc_01.py",
"infile_hash": "d4690d665de9b941c2581ad02041822e1e322dba55485cf8534dfe0a",
"infile_hash": "ce44dc48f31bcf876253727ca139210d99193565be1cf9b5fd3dea40",
"outfile": null,
"outfile_hash": null,
"stdout": "llvm-bindc_01-c984f09.stdout",
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/llvm-bindc_02-3cf74e9.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"basename": "llvm-bindc_02-3cf74e9",
"cmd": "lpython --no-color --show-llvm {infile} -o {outfile}",
"infile": "tests/../integration_tests/bindc_02.py",
"infile_hash": "26491ee7e9450cc430e5e6b443510ea0cc7507975baa8d8768e6f287",
"infile_hash": "f7a7faa22c2440be545994170ef21f451ce95960c7c7c217db5221fb",
"outfile": null,
"outfile_hash": null,
"stdout": "llvm-bindc_02-3cf74e9.stdout",
Expand Down