Skip to content

Missing Feature C Backend: Paradoxical Code Generation For Functions #1610

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

Open
ronnuriel opened this issue Mar 23, 2023 · 4 comments
Open
Labels
asr ASR related changes c Label for C language related changes

Comments

@ronnuriel
Copy link
Collaborator

python c backend generates paradoxical code for functions by return value:
Example:

from ltypes import i32
def test_pow():
    a: i32
    a = i32(pow(2, 2))

def test_pow_1(a: i32, b: i32) -> i32:
    res: i32
    res = i32(pow(a, b))
    return res

def main0():
    test_pow()
    c: i32
    c = test_pow_1(1, 2)

def foo():
    d: i32 = test_pow()


main0()

Run
python --show-c

Output:

Note: if any of the above error or warning messages are not clear or are lacking
context please report it to us (we consider that a bug that must be fixed).
#include <complex.h>
#include <inttypes.h>
#include <math.h>

#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#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
{
    int32_t lower_bound, length;
};

// Implementations
double __lpython_overloaded_0__pow(int32_t x, int32_t y)
{
    double _lpython_return_variable;
    _lpython_return_variable = (double)(pow(x, y));
    return _lpython_return_variable;
}

float _lfortran_caimag(float complex x);

double _lfortran_zaimag(double complex x);

void test_pow()
{
    int32_t a;
    a = (int32_t)(__lpython_overloaded_0__pow(2, 2));
}

int32_t test_pow_1(int32_t a, int32_t b)
{
    int32_t _lpython_return_variable;
    int32_t res;
    res = (int32_t)(__lpython_overloaded_0__pow(a, b));
    _lpython_return_variable = res;
    return _lpython_return_variable;
}

void main0()
{
    int32_t c;
    test_pow();
    c = test_pow_1(1, 2);
}

void _lpython_main_program()
{
    main0();
}

void foo()
{
    int32_t d = test_pow_1(3, 2);
    d = test_pow_1(3, 2);
}

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

As you can see foo does not need to be generated (Only function call it)
That unexpected behavior will return it self when declare return val as None:
def foo() -> None:

But won't return(Not generating c code for foo) when returns an actual value like:
def foo() -> i32:
etc...

@ronnuriel ronnuriel added c Label for C language related changes asr ASR related changes labels Mar 23, 2023
@ronnuriel ronnuriel changed the title Missing feature C backend: Paradoxical code generation for functions Missing Feature C Backend: Paradoxical Code Generation For Functions Mar 23, 2023
@certik
Copy link
Contributor

certik commented Mar 23, 2023

The issue I think is in our unused function pass that only removes functions returning a value, but leaves functions that return None (subroutines). We have to fix it.

@Shaikh-Ubaid
Copy link
Collaborator

The issue I think is in our unused function pass that only removes functions returning a value, but leaves functions that return None (subroutines). We have to fix it.

I think subroutines are supported in pass_unused_functions() at LFortran lfortran/lfortran#1408. We just need to sync the Libasr of the two repositories.

@Smit-create
Copy link
Collaborator

I tried to generate the C code with the given snippet and throws me the following error:

File "/Users/thebigbool/repos/lpython/src/lpython/semantics/python_ast_to_asr.cpp", line 4013
    visit_AnnAssignUtil(x, var_name);
  File "/Users/thebigbool/repos/lpython/src/lpython/semantics/python_ast_to_asr.cpp", line 2289
    value = ASRUtils::EXPR(tmp);
  File "/Users/thebigbool/repos/lpython/src/libasr/asr_utils.h", line 32
    return ASR::down_cast<ASR::expr_t>(f);
  File "/Users/thebigbool/repos/lpython/src/libasr/asr.h", line 40
    LCOMPILERS_ASSERT(is_a<T>(*f));
AssertFailed: is_a<T>(*f)

This is expected as d: i32 = test_pow() should not be supported as we have strict type checking. Can you please have a look at the original code snippet again? Thanks

@Smit-create
Copy link
Collaborator

Smit-create commented Apr 10, 2023

C now support passes and I think the following C generated code is fine for:

from lpython import i32

def test_pow():
    a: i32
    a = i32(pow(2, 2))

def test_pow_1(a: i32, b: i32) -> i32:
    res: i32
    res = i32(pow(a, b))
    return res

def main0():
    test_pow()
    c: i32
    c = test_pow_1(1, 2)

def foo():
    d: i32 = test_pow_1(1, 2)

main0()
C code

#include <complex.h>
#include <inttypes.h>
#include <math.h>

#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#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
{
    int32_t lower_bound, length;
};

// Implementations
double __lpython_overloaded_0__pow(int32_t x, int32_t y)
{
    double _lpython_return_variable;
    _lpython_return_variable = (double)(pow(x, y));
    return _lpython_return_variable;
}

float _lfortran_caimag(float complex x);

double _lfortran_zaimag(double complex x);

void test_pow()
{
    int32_t a;
    a = (int32_t)(__lpython_overloaded_0__pow(2, 2));
}

int32_t test_pow_1(int32_t a, int32_t b)
{
    int32_t _lpython_return_variable;
    int32_t res;
    res = (int32_t)(__lpython_overloaded_0__pow(a, b));
    _lpython_return_variable = res;
    return _lpython_return_variable;
}

void main0()
{
    int32_t c;
    test_pow();
    c = test_pow_1(1, 2);
}

void _lpython_main_program()
{
    main0();
}

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

We can see that foo is not generated now. Is this the expected fix for the issue @ronnuriel @certik ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
asr ASR related changes c Label for C language related changes
Projects
None yet
Development

No branches or pull requests

4 participants