Skip to content

Commit 254efcf

Browse files
authored
Pointers in emulation mode (#1336)
The syntax was changed from a subroutine to a function and AST->ASR updated. Works in emulation, LLVM and C modes.
1 parent 0c1397e commit 254efcf

13 files changed

+156
-26
lines changed

integration_tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ RUN(NAME structs_11 LABELS cpython llvm c)
328328
RUN(NAME structs_12 LABELS cpython llvm c)
329329
RUN(NAME structs_13 LABELS llvm c
330330
EXTRAFILES structs_13b.c)
331+
RUN(NAME structs_15 LABELS cpython llvm c)
331332
RUN(NAME sizeof_01 LABELS llvm c
332333
EXTRAFILES sizeof_01b.c)
333334
RUN(NAME enum_01 LABELS cpython llvm c)

integration_tests/bindc_01.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
from ltypes import c_p_pointer, CPtr, i16
1+
from ltypes import c_p_pointer, CPtr, i16, Pointer
22

33
queries: CPtr
4-
x: Pointer[i16]
5-
c_p_pointer(queries, x)
4+
x: Pointer[i16] = c_p_pointer(queries, i16)
65
print(queries, x)

integration_tests/bindc_02.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
from ltypes import c_p_pointer, CPtr, pointer, i16, Pointer
22

33
queries: CPtr
4-
x: Pointer[i16[:]]
5-
c_p_pointer(queries, x)
4+
x: Pointer[i16[:]] = c_p_pointer(queries, i16[:])
65
print(queries, x)
76

87
def f():
@@ -17,7 +16,7 @@ def f():
1716
assert yptr1[0] == i16(1)
1817
assert yptr1[1] == i16(2)
1918

20-
c_p_pointer(yq, yptr1)
19+
yptr1 = c_p_pointer(yq, i16[:])
2120

2221
print(yq, yptr1)
2322

integration_tests/bindc_03.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ def get_array(size: i32) -> CPtr:
1919
def f(q_void: CPtr) -> None:
2020
i: i32
2121
el: i32
22-
q: Pointer[i32[:]]
23-
c_p_pointer(q_void, q)
22+
q: Pointer[i32[:]] = c_p_pointer(q_void, i32[:])
2423
for i in range(10):
2524
q2: CPtr
2625
p_c_pointer(pointer(q[i]), q2)
@@ -33,8 +32,7 @@ def f(q_void: CPtr) -> None:
3332
def h(q_void: CPtr) -> None:
3433
i: i32
3534
el: i32
36-
q: Pointer[i32[:]]
37-
c_p_pointer(q_void, q)
35+
q: Pointer[i32[:]] = c_p_pointer(q_void, i32[:])
3836
for i in range(10):
3937
# TODO: Use q[i] directly in the assert.
4038
el = q[i]

integration_tests/structs_15.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from ltypes import i32, i16, i8, i64, CPtr, dataclass, ccall, Pointer, c_p_pointer, sizeof
2+
3+
@dataclass
4+
class A:
5+
x: i16
6+
y: i8
7+
8+
@ccall
9+
def _lfortran_malloc(size: i32) -> CPtr:
10+
pass
11+
12+
@ccall
13+
def _lfortran_memset(cptr: CPtr, value: i32, size: i32):
14+
pass
15+
16+
def add_A_members(Ax: i16, Ay: i8) -> i16:
17+
return Ax + i16(Ay)
18+
19+
def test_Aptr_member_passing():
20+
print(sizeof(A))
21+
22+
a_cptr: CPtr = _lfortran_malloc(i32(sizeof(A)))
23+
_lfortran_memset(a_cptr, 2, i32(sizeof(A)))
24+
b_cptr: CPtr = _lfortran_malloc(i32(sizeof(A)))
25+
_lfortran_memset(b_cptr, 6, i32(sizeof(A)))
26+
27+
a_ptr: Pointer[A] = c_p_pointer(a_cptr, A)
28+
b_ptr: Pointer[A] = c_p_pointer(b_cptr, A)
29+
print(a_ptr.x, a_ptr.y)
30+
print(b_ptr.x, b_ptr.y)
31+
assert a_ptr.x * i16(3) == b_ptr.x
32+
assert a_ptr.y * i8(3) == b_ptr.y
33+
34+
a_ptr.y = i8(-18)
35+
assert a_ptr.x * i16(3) == b_ptr.x
36+
a_ptr.x = i16(20)
37+
print(a_ptr.x, a_ptr.y)
38+
print(add_A_members(a_ptr.x, a_ptr.y))
39+
assert add_A_members(a_ptr.x, a_ptr.y) == i16(2)
40+
41+
test_Aptr_member_passing()

src/libasr/runtime/lfortran_intrinsics.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -886,8 +886,12 @@ LFORTRAN_API char* _lfortran_str_chr(int val)
886886
return dest_char;
887887
}
888888

889-
LFORTRAN_API char* _lfortran_malloc(int size) {
890-
return (char*)malloc(size);
889+
LFORTRAN_API void _lfortran_memset(void* s, int32_t c, int32_t size) {
890+
memset(s, c, size);
891+
}
892+
893+
LFORTRAN_API void* _lfortran_malloc(int32_t size) {
894+
return malloc(size);
891895
}
892896

893897
LFORTRAN_API int8_t* _lfortran_realloc(int8_t* ptr, int32_t size) {

src/libasr/runtime/lfortran_intrinsics.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ LFORTRAN_API int _lfortran_str_ord(char** s);
161161
LFORTRAN_API int _lfortran_str_ord_c(char* s);
162162
LFORTRAN_API char* _lfortran_str_chr(int c);
163163
LFORTRAN_API int _lfortran_str_to_int(char** s);
164-
LFORTRAN_API char* _lfortran_malloc(int size);
164+
LFORTRAN_API void* _lfortran_malloc(int32_t size);
165+
LFORTRAN_API void _lfortran_memset(void* s, int32_t c, int32_t size);
165166
LFORTRAN_API int8_t* _lfortran_realloc(int8_t* ptr, int32_t size);
166167
LFORTRAN_API int8_t* _lfortran_calloc(int32_t count, int32_t size);
167168
LFORTRAN_API void _lfortran_free(char* ptr);

src/lpython/semantics/python_ast_to_asr.cpp

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,8 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
538538
std::string import_path;
539539
Vec<ASR::stmt_t*> *current_body;
540540
ASR::ttype_t* ann_assign_target_type;
541+
AST::expr_t* assign_ast_target;
542+
bool is_c_p_pointer_call;
541543

542544
std::map<std::string, int> generic_func_nums;
543545
std::map<std::string, std::map<std::string, ASR::ttype_t*>> generic_func_subs;
@@ -551,8 +553,8 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
551553
std::string import_path, bool allow_implicit_casting_)
552554
: diag{diagnostics}, al{al}, lm{lm}, current_scope{symbol_table}, main_module{main_module},
553555
ast_overload{ast_overload}, parent_dir{parent_dir}, import_path{import_path},
554-
current_body{nullptr}, ann_assign_target_type{nullptr},
555-
allow_implicit_casting{allow_implicit_casting_} {
556+
current_body{nullptr}, ann_assign_target_type{nullptr}, assign_ast_target{nullptr},
557+
is_c_p_pointer_call{false}, allow_implicit_casting{allow_implicit_casting_} {
556558
current_module_dependencies.reserve(al, 4);
557559
}
558560

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

2216+
ASR::asr_t* create_CPtrToPointerFromArgs(AST::expr_t* ast_cptr, AST::expr_t* ast_pptr,
2217+
const Location& loc) {
2218+
this->visit_expr(*ast_cptr);
2219+
ASR::expr_t* cptr = ASRUtils::EXPR(tmp);
2220+
this->visit_expr(*ast_pptr);
2221+
ASR::expr_t* pptr = ASRUtils::EXPR(tmp);
2222+
return ASR::make_CPtrToPointer_t(al, loc, cptr,
2223+
pptr, nullptr);
2224+
}
2225+
22142226
void visit_AnnAssignUtil(const AST::AnnAssign_t& x, std::string& var_name,
22152227
bool wrap_derived_type_in_pointer=false,
22162228
ASR::expr_t* init_expr=nullptr, ASR::abiType abi=ASR::abiType::Source) {
@@ -2222,13 +2234,25 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
22222234
type = ASRUtils::TYPE(ASR::make_Pointer_t(al, type->base.loc, type));
22232235
}
22242236

2237+
bool is_c_p_pointer_call_copy = is_c_p_pointer_call;
22252238
ASR::expr_t *value = nullptr;
22262239
if( !init_expr ) {
22272240
tmp = nullptr;
2241+
is_c_p_pointer_call = false;
22282242
if (x.m_value) {
22292243
this->visit_expr(*x.m_value);
22302244
}
2231-
if (tmp) {
2245+
if( is_c_p_pointer_call ) {
2246+
create_add_variable_to_scope(var_name, nullptr, nullptr, type,
2247+
x.base.base.loc, abi);
2248+
AST::Call_t* c_p_pointer_call = AST::down_cast<AST::Call_t>(x.m_value);
2249+
AST::expr_t* cptr = c_p_pointer_call->m_args[0];
2250+
AST::expr_t* pptr = assign_ast_target;
2251+
tmp = create_CPtrToPointerFromArgs(cptr, pptr, x.base.base.loc);
2252+
// if( current_body ) {
2253+
// current_body->push_back(al, ASRUtils::STMT(tmp));
2254+
// }
2255+
} else if (tmp) {
22322256
value = ASRUtils::EXPR(tmp);
22332257
ASR::ttype_t* underlying_type = type;
22342258
if( ASR::is_a<ASR::Const_t>(*type) ) {
@@ -2256,10 +2280,16 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
22562280
} else {
22572281
cast_helper(type, init_expr, init_expr->base.loc);
22582282
}
2259-
create_add_variable_to_scope(var_name, init_expr, value, type,
2260-
x.base.base.loc, abi);
22612283

2262-
tmp = nullptr;
2284+
if( !is_c_p_pointer_call ) {
2285+
create_add_variable_to_scope(var_name, init_expr, value, type,
2286+
x.base.base.loc, abi);
2287+
}
2288+
2289+
if( !is_c_p_pointer_call ) {
2290+
tmp = nullptr;
2291+
}
2292+
is_c_p_pointer_call = is_c_p_pointer_call_copy;
22632293
ann_assign_target_type = ann_assign_target_type_copy;
22642294
}
22652295

@@ -3869,6 +3899,8 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
38693899
// We treat this as a declaration
38703900
std::string var_name;
38713901
std::string var_annotation;
3902+
AST::expr_t* assign_ast_target_copy = assign_ast_target;
3903+
assign_ast_target = x.m_target;
38723904
if (AST::is_a<AST::Name_t>(*x.m_target)) {
38733905
AST::Name_t *n = AST::down_cast<AST::Name_t>(x.m_target);
38743906
var_name = n->m_id;
@@ -3893,6 +3925,7 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
38933925
}
38943926

38953927
visit_AnnAssignUtil(x, var_name);
3928+
assign_ast_target = assign_ast_target_copy;
38963929
}
38973930

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

39253958
void visit_Assign(const AST::Assign_t &x) {
39263959
ASR::expr_t *target, *assign_value = nullptr, *tmp_value;
3960+
bool is_c_p_pointer_call_copy = is_c_p_pointer_call;
3961+
is_c_p_pointer_call = false;
39273962
this->visit_expr(*x.m_value);
3963+
if( is_c_p_pointer_call ) {
3964+
LFORTRAN_ASSERT(x.n_targets == 1);
3965+
AST::Call_t* c_p_pointer_call = AST::down_cast<AST::Call_t>(x.m_value);
3966+
AST::expr_t* cptr = c_p_pointer_call->m_args[0];
3967+
AST::expr_t* pptr = x.m_targets[0];
3968+
tmp = create_CPtrToPointerFromArgs(cptr, pptr, x.base.base.loc);
3969+
is_c_p_pointer_call = is_c_p_pointer_call;
3970+
return ;
3971+
}
3972+
is_c_p_pointer_call = is_c_p_pointer_call_copy;
39283973
if (tmp) {
39293974
// This happens if `m.m_value` is `empty`, such as in:
39303975
// a = empty(16)
@@ -5813,6 +5858,10 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
58135858
tmp = intrinsic_node_handler.get_intrinsic_node(call_name, al,
58145859
x.base.base.loc, args);
58155860
return;
5861+
} else if (call_name == "c_p_pointer") {
5862+
is_c_p_pointer_call = true;
5863+
tmp = nullptr;
5864+
return ;
58165865
} else {
58175866
// The function was not found and it is not intrinsic
58185867
throw SemanticError("Function '" + call_name + "' is not declared and not intrinsic",

src/runtime/ltypes/ltypes.py

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
__slots__ = ["i8", "i16", "i32", "i64", "f32", "f64", "c32", "c64", "CPtr",
1010
"overload", "ccall", "TypeVar", "pointer", "c_p_pointer", "Pointer",
1111
"p_c_pointer", "vectorize", "inline", "Union", "static", "with_goto",
12-
"packed", "Const"]
12+
"packed", "Const", "sizeof"]
1313

1414
# data-types
1515

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

2626
class Pointer:
2727
def __getitem__(self, type):
28+
if is_dataclass(type):
29+
return convert_to_ctypes_Structure(type)
2830
return type
2931

3032
class ConstType(Type):
@@ -174,6 +176,8 @@ def convert_type_to_ctype(arg):
174176
elif isinstance(arg, Array):
175177
type = convert_type_to_ctype(arg._type)
176178
return ctypes.POINTER(type)
179+
elif is_dataclass(arg):
180+
return convert_to_ctypes_Structure(arg)
177181
else:
178182
raise NotImplementedError("Type %r not implemented" % arg)
179183

@@ -196,7 +200,7 @@ def get_lib_name(name):
196200
else:
197201
raise NotImplementedError("Platform not implemented")
198202
def get_crtlib_path():
199-
py_mod = os.environ["LPYTHON_PY_MOD_NAME"]
203+
py_mod = os.environ.get("LPYTHON_PY_MOD_NAME", "")
200204
if py_mod == "":
201205
return os.path.join(get_rtlib_dir(),
202206
get_lib_name("lpython_runtime"))
@@ -242,6 +246,19 @@ def convert_to_ctypes_Union(f):
242246

243247
return f
244248

249+
def convert_to_ctypes_Structure(f):
250+
fields = []
251+
for name in f.__annotations__:
252+
ltype_ = f.__annotations__[name]
253+
fields.append((name, convert_type_to_ctype(ltype_)))
254+
255+
class ctypes_Structure(ctypes.Structure):
256+
_fields_ = fields
257+
258+
ctypes_Structure.__name__ = f.__name__
259+
260+
return ctypes_Structure
261+
245262
def ccall(f):
246263
if isclass(f) and issubclass(f, Union):
247264
return f
@@ -280,11 +297,32 @@ def pointer(x, type=None):
280297
else:
281298
raise Exception("Type not supported in pointer()")
282299

300+
class PointerToStruct:
301+
302+
def __init__(self, ctypes_ptr_):
303+
self.__dict__["ctypes_ptr"] = ctypes_ptr_
304+
305+
def __getattr__(self, name: str):
306+
if name == "ctypes_ptr":
307+
return self.__dict__[name]
308+
return self.ctypes_ptr.contents.__getattribute__(name)
309+
310+
def __setattr__(self, name: str, value):
311+
self.ctypes_ptr.contents.__setattr__(name, value)
312+
283313
def c_p_pointer(cptr, targettype):
284-
return pointer(targettype)
314+
targettype_ptr = ctypes.POINTER(convert_type_to_ctype(targettype))
315+
newa = ctypes.cast(cptr, targettype_ptr)
316+
if is_dataclass(targettype):
317+
# return after wrapping newa inside PointerToStruct
318+
return PointerToStruct(newa)
319+
return newa
285320

286321
def p_c_pointer(ptr, cptr):
287322
cptr.value = ptr.value
288323

289324
def empty_c_void_p():
290325
return ctypes.c_void_p()
326+
327+
def sizeof(arg):
328+
return ctypes.sizeof(convert_type_to_ctype(arg))

tests/reference/asr-bindc_01-6d521a9.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"basename": "asr-bindc_01-6d521a9",
33
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
44
"infile": "tests/../integration_tests/bindc_01.py",
5-
"infile_hash": "d4690d665de9b941c2581ad02041822e1e322dba55485cf8534dfe0a",
5+
"infile_hash": "ce44dc48f31bcf876253727ca139210d99193565be1cf9b5fd3dea40",
66
"outfile": null,
77
"outfile_hash": null,
88
"stdout": "asr-bindc_01-6d521a9.stdout",

tests/reference/asr-bindc_02-bc1a7ea.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"basename": "asr-bindc_02-bc1a7ea",
33
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
44
"infile": "tests/../integration_tests/bindc_02.py",
5-
"infile_hash": "26491ee7e9450cc430e5e6b443510ea0cc7507975baa8d8768e6f287",
5+
"infile_hash": "f7a7faa22c2440be545994170ef21f451ce95960c7c7c217db5221fb",
66
"outfile": null,
77
"outfile_hash": null,
88
"stdout": "asr-bindc_02-bc1a7ea.stdout",

tests/reference/llvm-bindc_01-c984f09.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"basename": "llvm-bindc_01-c984f09",
33
"cmd": "lpython --no-color --show-llvm {infile} -o {outfile}",
44
"infile": "tests/../integration_tests/bindc_01.py",
5-
"infile_hash": "d4690d665de9b941c2581ad02041822e1e322dba55485cf8534dfe0a",
5+
"infile_hash": "ce44dc48f31bcf876253727ca139210d99193565be1cf9b5fd3dea40",
66
"outfile": null,
77
"outfile_hash": null,
88
"stdout": "llvm-bindc_01-c984f09.stdout",

tests/reference/llvm-bindc_02-3cf74e9.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"basename": "llvm-bindc_02-3cf74e9",
33
"cmd": "lpython --no-color --show-llvm {infile} -o {outfile}",
44
"infile": "tests/../integration_tests/bindc_02.py",
5-
"infile_hash": "26491ee7e9450cc430e5e6b443510ea0cc7507975baa8d8768e6f287",
5+
"infile_hash": "f7a7faa22c2440be545994170ef21f451ce95960c7c7c217db5221fb",
66
"outfile": null,
77
"outfile_hash": null,
88
"stdout": "llvm-bindc_02-3cf74e9.stdout",

0 commit comments

Comments
 (0)