Skip to content
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
9 changes: 9 additions & 0 deletions integration_tests/bindc_03.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,6 +42,14 @@ def h(q_void: CPtr) -> None:
print(el)
assert el == i * i + i%2


@ccallable(header="_test_bindc_03_my_header.h")
def test_emit_header_ccallable() -> i32:
i: i32 = 5
assert i == 5
i = i*5
return i + 10
Comment on lines +46 to +51

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

See this test. The header file will be created and will be used in the original file.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah, the generated file contains the header file?

I think we still need a handwritten file that includes it, to be sure that all the arguments are compatible.

@Smit-createSmit-createMay 28, 2023

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

% lpython --show-c integration_tests/bindc_03.py > b.c
Will generate two file.

  1. main file:
#include<complex.h>
#include<inttypes.h>
#include"_test_bindc_03_my_header.h"
#include"bindc_03b.h"
#include<stdlib.h>
#include<stdbool.h>
#include<stdio.h>
#include<string.h>
#include<lfortran_intrinsics.h>structdimension_descriptor
{
int32_t lower_bound, length;
};
structArrayWrapped {
void* array;
};
structi32
{
int32_t *data;
structdimension_descriptor dims[32];
int32_t n_dims;
bool is_allocated;
};
inlinevoidstruct_deepcopy_ArrayWrapped(structArrayWrapped* src, structArrayWrapped* dest);
// Implementationsint32_t__lpython_overloaded_4___lpython_floordiv(int32_t a, int32_t b)
{
int32_t _lpython_return_variable;
double r;
int32_t result;
r = (double)(a)/(double)(b);
result = (int32_t)(r);
if (r >= 0.00000000000000000e+00 || (double)(result) == r) {
_lpython_return_variable = result;
return _lpython_return_variable;
}
_lpython_return_variable = result - 1;
return _lpython_return_variable;
}
int32_t__lpython_overloaded_2___mod(int32_t a, int32_t b)
{
int32_t _lpython_return_variable;
_lpython_return_variable = a - __lpython_overloaded_4___lpython_floordiv(a, b)*b;
return _lpython_return_variable;
}
float_lfortran_caimag(floatcomplex x);
double_lfortran_zaimag(doublecomplex x);
voidgpy(void* a, int32_t value, bool offset_value)
{
g(a, value, offset_value);
}
voidf(void* q_void)
{
int32_t el;
int32_t i;
structi32 q_value;
structi32* q = &q_value;
q->n_dims = 1;
q->dims[0].lower_bound = 0;
q->dims[0].length = 0;
q->data = (int32_t*) q_void;
for (i=0; i<=10 - 1; i++) {
{
void* q2;
q2 = (void*) &q->data[(i - q->dims[0].lower_bound)];
gpy(q2, i*i, (bool)(__lpython_overloaded_2___mod(i, 2)));
el = q->data[(i - q->dims[0].lower_bound)];
printf("%d\n", el);
ASSERT(el == i*i + __lpython_overloaded_2___mod(i, 2));
}
}
}
voidh(void* q_void)
{
int32_t el;
int32_t i;
structi32 q_value;
structi32* q = &q_value;
q->n_dims = 1;
q->dims[0].lower_bound = 0;
q->dims[0].length = 0;
q->data = (int32_t*) q_void;
for (i=0; i<=10 - 1; i++) {
{
el = q->data[(i - q->dims[0].lower_bound)];
printf("%d\n", el);
ASSERT(el == i*i + __lpython_overloaded_2___mod(i, 2));
}
}
}
voidrun()
{
void* a;
structArrayWrapped array_wrapped_value;
structArrayWrapped* array_wrapped = &array_wrapped_value;
structArrayWrapped array_wrapped1_value;
structArrayWrapped* array_wrapped1 = &array_wrapped1_value;
uint64_t q;
int32_t size;
void* x;
array_wrapped->array = a;
size = 10;
a = get_array(size);
ASSERT(a != NULL);
array_wrapped->array = a;
f(array_wrapped->array);
q = (uint64_t)(a);
x = (void*)(q);
array_wrapped->array = x;
f(array_wrapped->array);
struct_deepcopy_ArrayWrapped(array_wrapped, array_wrapped1);
h(array_wrapped1->array);
ASSERT(test_emit_header_ccallable() == 35);
}
void_lpython_main_program()
{
run();
}
intmain(int argc, char* argv[])
{
_lpython_set_argv(argc, argv);
_lpython_main_program();
return0;
}
voidstruct_deepcopy_ArrayWrapped(structArrayWrapped* src, structArrayWrapped* dest) {
dest->array = src->array;
}
  1. header file:
#ifndef _TEST_BINDC_03_MY_HEADER_H
#define_TEST_BINDC_03_MY_HEADER_H
#include<complex.h>
#include<inttypes.h>
#include<stdlib.h>
#include<stdbool.h>
#include<stdio.h>
#include<string.h>
#include<lfortran_intrinsics.h>int32_ttest_emit_header_ccallable()
{
int32_t _lpython_return_variable;
int32_t i;
i = 5;
ASSERT(i == 5);
i = i*5;
_lpython_return_variable = i + 10;
return _lpython_return_variable;
}
#endif

@Smit-createSmit-createMay 28, 2023

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

I think we still need a handwritten file that includes it, to be sure that all the arguments are compatible.

Yeah, you're right. ccallable needs to be updated in the LLVM backend so that it can call from C.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The test_emit_header_ccallable must be in a C file, not a header file, otherwise the function will be generated twice by the C compiler and you will get linking errors.


def run():
a: CPtr
array_wrapped: ArrayWrapped = ArrayWrapped(a)
Expand All@@ -59,5 +67,6 @@ def run():
f(array_wrapped.array)
array_wrapped1 = array_wrapped
h(array_wrapped1.array)
assert test_emit_header_ccallable() == 35

run()
42 changes: 19 additions & 23 deletions src/libasr/codegen/asr_to_c.cpp
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
#include <iostream>
#include <fstream>
#include <memory>

#include <libasr/asr.h>
Expand DownExpand Up@@ -655,29 +656,6 @@ R"(
#include <string.h>
#include <lfortran_intrinsics.h>

#define ASSERT(cond) \
{ \
if (!(cond)) { \
printf("%s%s", "ASSERT failed: ", __FILE__); \
printf("%s%s", "\nfunction ", __func__); \
printf("%s%d%s", "(), line number ", __LINE__, " at \n"); \
printf("%s%s", #cond, "\n"); \
exit(1); \
} \
}
#define ASSERT_MSG(cond, msg) \
{ \
if (!(cond)) { \
printf("%s%s", "ASSERT failed: ", __FILE__); \
printf("%s%s", "\nfunction ", __func__); \
printf("%s%d%s", "(), line number ", __LINE__, " at \n"); \
printf("%s%s", #cond, "\n"); \
printf("%s", "ERROR MESSAGE:\n"); \
printf("%s%s", msg, "\n"); \
exit(1); \
} \
}

)";

std::string indent(indentation_level * indentation_spaces, ' ');
Expand DownExpand Up@@ -807,6 +785,24 @@ R"(
}
src = to_include + head + array_types_decls + unit_src +
ds_funcs_defined + util_funcs_defined;
if (!emit_headers.empty()) {
std::string to_includes_1 = "";
for (auto &s: headers) {
to_includes_1 += "#include <" + s + ">\n";
}
for (auto &f_name: emit_headers) {
std::ofstream out_file;
std::string out_src = to_includes_1 + head + f_name.second;
std::string ifdefs = f_name.first.substr(0, f_name.first.length() - 2);
std::transform(ifdefs.begin(), ifdefs.end(), ifdefs.begin(), ::toupper);
ifdefs += "_H";
out_src = "#ifndef " + ifdefs + "\n#define " + ifdefs + "\n\n" + out_src;
out_src += "\n\n#endif\n";
out_file.open(f_name.first);
out_file << out_src;
out_file.close();
}
}
}

void visit_Module(const ASR::Module_t &x) {
Expand Down
10 changes: 10 additions & 0 deletions src/libasr/codegen/asr_to_c_cpp.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -104,6 +104,7 @@ class BaseCCPPVisitor : public ASR::BaseVisitor<Struct>
std::map<uint64_t, SymbolInfo> sym_info;
std::map<uint64_t, std::string> const_var_names;
std::map<int32_t, std::string> gotoid2name;
std::map<std::string, std::string> emit_headers;

// Output configuration:
// Use std::string or char*
Expand DownExpand Up@@ -622,6 +623,15 @@ R"(#include <stdio.h>
}
sub += "\n";
src = sub;
if (f_type->m_abi == ASR::abiType::BindC
&& f_type->m_deftype == ASR::deftypeType::Implementation) {
if (x.m_c_header) {
std::string header_name = std::string(x.m_c_header);
user_headers.insert(header_name);
emit_headers[header_name]+= "\n" + src;
src = "";
}
}
current_scope = current_scope_copy;
}

Expand Down
30 changes: 30 additions & 0 deletions src/libasr/runtime/lfortran_intrinsics.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,6 +34,36 @@ typedef double _Complex double_complex_t;
#define LFORTRAN_API /* Nothing */
#endif



#ifndef ASSERT
#define ASSERT(cond) \
{ \
if (!(cond)) { \
printf("%s%s", "ASSERT failed: ", __FILE__); \
printf("%s%s", "\nfunction ", __func__); \
printf("%s%d%s", "(), line number ", __LINE__, " at \n"); \
printf("%s%s", #cond, "\n"); \
exit(1); \
} \
}
#endif

#ifndef ASSERT_MSG
#define ASSERT_MSG(cond, msg) \
{ \
if (!(cond)) { \
printf("%s%s", "ASSERT failed: ", __FILE__); \
printf("%s%s", "\nfunction ", __func__); \
printf("%s%d%s", "(), line number ", __LINE__, " at \n"); \
printf("%s%s", #cond, "\n"); \
printf("%s", "ERROR MESSAGE:\n"); \
printf("%s%s", msg, "\n"); \
exit(1); \
} \
}
#endif

LFORTRAN_API double _lfortran_sum(int n, double *v);
LFORTRAN_API void _lfortran_random_number(int n, double *v);
LFORTRAN_API double _lfortran_random();
Expand Down
6 changes: 3 additions & 3 deletions src/lpython/semantics/python_ast_to_asr.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -3809,9 +3809,9 @@ class SymbolTableVisitor : public CommonVisitor<SymbolTableVisitor> {
AST::Call_t *call_d = AST::down_cast<AST::Call_t>(dec);
if (AST::is_a<AST::Name_t>(*call_d->m_func)) {
std::string name = AST::down_cast<AST::Name_t>(call_d->m_func)->m_id;
if (name == "ccall") {
if (name == "ccall" || "ccallable") {
current_procedure_abi_type = ASR::abiType::BindC;
current_procedure_interface = true;
if (name == "ccall") current_procedure_interface = true;
if (call_d->n_keywords > 0) {
for (size_t i=0; i < call_d->n_keywords; i++) {
if (std::string(call_d->m_keywords[i].m_arg) == "header") {
Expand All@@ -3820,7 +3820,7 @@ class SymbolTableVisitor : public CommonVisitor<SymbolTableVisitor> {
call_d->m_keywords[i].m_value)->m_value;
c_header_file = s2c(al, header_name);
} else {
throw SemanticError("header should be constant string in ccall",
throw SemanticError("header should be constant string in ccall/ccallable",
x.base.base.loc);
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/c-bindc_06-a30d20f.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@
"outfile": null,
"outfile_hash": null,
"stdout": "c-bindc_06-a30d20f.stdout",
"stdout_hash": "e8983d6fcd2d2c6e975daae9aed736c86c8432adaed965caeef12cba",
"stdout_hash": "1592d4a00bd387b8ee0a53ab5671575310052fc871b4dfca170386fd",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
23 changes: 0 additions & 23 deletions tests/reference/c-bindc_06-a30d20f.stdout
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,29 +8,6 @@
#include <string.h>
#include <lfortran_intrinsics.h>

#define ASSERT(cond) \
{ \
if (!(cond)) { \
printf("%s%s", "ASSERT failed: ", __FILE__); \
printf("%s%s", "\nfunction ", __func__); \
printf("%s%d%s", "(), line number ", __LINE__, " at \n"); \
printf("%s%s", #cond, "\n"); \
exit(1); \
} \
}
#define ASSERT_MSG(cond, msg) \
{ \
if (!(cond)) { \
printf("%s%s", "ASSERT failed: ", __FILE__); \
printf("%s%s", "\nfunction ", __func__); \
printf("%s%d%s", "(), line number ", __LINE__, " at \n"); \
printf("%s%s", #cond, "\n"); \
printf("%s", "ERROR MESSAGE:\n"); \
printf("%s%s", msg, "\n"); \
exit(1); \
} \
}


struct dimension_descriptor
{
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/c-c_interop1-e215531.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@
"outfile": null,
"outfile_hash": null,
"stdout": "c-c_interop1-e215531.stdout",
"stdout_hash": "79bf04c21b0db44755fa1828851ba74ce803ba1748da76e0144e2908",
"stdout_hash": "6ff32cb0aeb8edf4f78af311a25512fc6f8f708e682ce94137f160ee",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
23 changes: 0 additions & 23 deletions tests/reference/c-c_interop1-e215531.stdout
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,29 +6,6 @@
#include <string.h>
#include <lfortran_intrinsics.h>

#define ASSERT(cond) \
{ \
if (!(cond)) { \
printf("%s%s", "ASSERT failed: ", __FILE__); \
printf("%s%s", "\nfunction ", __func__); \
printf("%s%d%s", "(), line number ", __LINE__, " at \n"); \
printf("%s%s", #cond, "\n"); \
exit(1); \
} \
}
#define ASSERT_MSG(cond, msg) \
{ \
if (!(cond)) { \
printf("%s%s", "ASSERT failed: ", __FILE__); \
printf("%s%s", "\nfunction ", __func__); \
printf("%s%d%s", "(), line number ", __LINE__, " at \n"); \
printf("%s%s", #cond, "\n"); \
printf("%s", "ERROR MESSAGE:\n"); \
printf("%s%s", msg, "\n"); \
exit(1); \
} \
}


struct dimension_descriptor
{
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/c-expr7-bb2692a.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@
"outfile": null,
"outfile_hash": null,
"stdout": "c-expr7-bb2692a.stdout",
"stdout_hash": "c5afb46e3538eeeff7e1b50eac49c6f2fa57468bb40d278b207ee60d",
"stdout_hash": "6ec5c40f07c1fb2fbfad9c3ff031e50cf077c0ad2412c7be962d9e93",
"stderr": "c-expr7-bb2692a.stderr",
"stderr_hash": "6e9790ac88db1a9ead8f64a91ba8a6605de67167037908a74b77be0c",
"returncode": 0
Expand Down
23 changes: 0 additions & 23 deletions tests/reference/c-expr7-bb2692a.stdout
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,29 +8,6 @@
#include <string.h>
#include <lfortran_intrinsics.h>

#define ASSERT(cond) \
{ \
if (!(cond)) { \
printf("%s%s", "ASSERT failed: ", __FILE__); \
printf("%s%s", "\nfunction ", __func__); \
printf("%s%d%s", "(), line number ", __LINE__, " at \n"); \
printf("%s%s", #cond, "\n"); \
exit(1); \
} \
}
#define ASSERT_MSG(cond, msg) \
{ \
if (!(cond)) { \
printf("%s%s", "ASSERT failed: ", __FILE__); \
printf("%s%s", "\nfunction ", __func__); \
printf("%s%d%s", "(), line number ", __LINE__, " at \n"); \
printf("%s%s", #cond, "\n"); \
printf("%s", "ERROR MESSAGE:\n"); \
printf("%s%s", msg, "\n"); \
exit(1); \
} \
}


struct dimension_descriptor
{
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/c-expr_01-28f449f.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@
"outfile": null,
"outfile_hash": null,
"stdout": "c-expr_01-28f449f.stdout",
"stdout_hash": "f7ff3e7ee6518de7ff6bc41aa1ab242c7dbf9fca4482e5de47209c42",
"stdout_hash": "54d2c8dc7b28702c7907cd829a3777bdc4f2cf13dc597113a0074839",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
23 changes: 0 additions & 23 deletions tests/reference/c-expr_01-28f449f.stdout
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,29 +6,6 @@
#include <string.h>
#include <lfortran_intrinsics.h>

#define ASSERT(cond) \
{ \
if (!(cond)) { \
printf("%s%s", "ASSERT failed: ", __FILE__); \
printf("%s%s", "\nfunction ", __func__); \
printf("%s%d%s", "(), line number ", __LINE__, " at \n"); \
printf("%s%s", #cond, "\n"); \
exit(1); \
} \
}
#define ASSERT_MSG(cond, msg) \
{ \
if (!(cond)) { \
printf("%s%s", "ASSERT failed: ", __FILE__); \
printf("%s%s", "\nfunction ", __func__); \
printf("%s%d%s", "(), line number ", __LINE__, " at \n"); \
printf("%s%s", #cond, "\n"); \
printf("%s", "ERROR MESSAGE:\n"); \
printf("%s%s", msg, "\n"); \
exit(1); \
} \
}


struct dimension_descriptor
{
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/c-expr_11-c452314.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@
"outfile": null,
"outfile_hash": null,
"stdout": "c-expr_11-c452314.stdout",
"stdout_hash": "3ff86fbef1a6ada1ca91694b6cf50b08d58abb9f3d32672698655b45",
"stdout_hash": "990486bcfab642985e506a1ee8ff20218e233b0d19035e8acf2d407c",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
23 changes: 0 additions & 23 deletions tests/reference/c-expr_11-c452314.stdout
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,29 +6,6 @@
#include <string.h>
#include <lfortran_intrinsics.h>

#define ASSERT(cond) \
{ \
if (!(cond)) { \
printf("%s%s", "ASSERT failed: ", __FILE__); \
printf("%s%s", "\nfunction ", __func__); \
printf("%s%d%s", "(), line number ", __LINE__, " at \n"); \
printf("%s%s", #cond, "\n"); \
exit(1); \
} \
}
#define ASSERT_MSG(cond, msg) \
{ \
if (!(cond)) { \
printf("%s%s", "ASSERT failed: ", __FILE__); \
printf("%s%s", "\nfunction ", __func__); \
printf("%s%d%s", "(), line number ", __LINE__, " at \n"); \
printf("%s%s", #cond, "\n"); \
printf("%s", "ERROR MESSAGE:\n"); \
printf("%s%s", msg, "\n"); \
exit(1); \
} \
}


struct dimension_descriptor
{
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/c-expr_12-93c7780.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@
"outfile": null,
"outfile_hash": null,
"stdout": "c-expr_12-93c7780.stdout",
"stdout_hash": "4a67164684f646b94101df4dba378f02ee7585d7087093459a791c39",
"stdout_hash": "eb0832038676f31ac362e2c3b4709d5f1b9a9be2ca9c5b13d1a29613",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
Loading