Skip to content

@ccallable: Emit a header file with given name #1861

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 5 commits into from
May 28, 2023
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 number Diff line number Diff 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
Collaborator Author

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
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.

Copy link
Collaborator Author

@Smit-create Smit-create May 28, 2023

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>


struct dimension_descriptor
{
    int32_t lower_bound, length;
};
struct ArrayWrapped {
 void* array;
};


struct i32
{
    int32_t *data;
    struct dimension_descriptor dims[32];
    int32_t n_dims;
    bool is_allocated;
};


inline void struct_deepcopy_ArrayWrapped(struct ArrayWrapped* src, struct ArrayWrapped* dest);


// Implementations
int32_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(float complex x);

double _lfortran_zaimag(double complex x);

void gpy(void* a, int32_t value, bool offset_value)
{
    g(a, value, offset_value);
}

void f(void* q_void)
{
    int32_t el;
    int32_t i;
    struct i32 q_value;
    struct i32* 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));
        }
    }
}

void h(void* q_void)
{
    int32_t el;
    int32_t i;
    struct i32 q_value;
    struct i32* 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));
        }
    }
}

void run()
{
    void* a;
    struct ArrayWrapped array_wrapped_value;
    struct ArrayWrapped* array_wrapped = &array_wrapped_value;
    struct ArrayWrapped array_wrapped1_value;
    struct ArrayWrapped* 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();
}

int main(int argc, char* argv[])
{
    _lpython_set_argv(argc, argv);
    _lpython_main_program();
    return 0;
}

void struct_deepcopy_ArrayWrapped(struct ArrayWrapped* src, struct ArrayWrapped* 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_t test_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

Copy link
Collaborator Author

@Smit-create Smit-create May 28, 2023

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
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 number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <iostream>
#include <fstream>
#include <memory>

#include <libasr/asr.h>
Expand Down Expand 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 Down Expand 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 number Diff line number Diff 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 Down Expand 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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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