-
Notifications
You must be signed in to change notification settings - Fork 158
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
C backend: variable is initialized twice #1626
Comments
I have also experienced this issue with the For example, the same example at #1626 (comment) produces the following ...
(func $4 (type 4) (param) (result)
(local f64)
call 3
f64.const 1000000.000000
f64.mul
local.set 0 // first assignment
call 3
f64.const 1000000.000000
f64.mul
local.set 0 // second assignment
... I think the issue is at the from ltypes import i32
def main():
t1: i32 = 5
main() generates the main:
...
(Function
(SymbolTable
2
{
t1:
(Variable
2
t1
[]
Local
(IntegerConstant 5 (Integer 4 [])) // symbolic value for the variable
()
Default
(Integer 4 [])
Source
Public
Required
.false.
)
})
main
(FunctionType
[]
()
Source
Implementation
()
.false.
.false.
.false.
.false.
.false.
[]
[]
.false.
)
[]
[]
[(= // assignment for the variable
(Var 2 t1)
(IntegerConstant 5 (Integer 4 []))
()
)]
()
Public
.false.
.false.
)
... In the above I think ideally the |
Yes, the ASR is wrong. It needs to only do the assignment, and not having the symbolic value set. So that's what we need to fix. |
For the following example: from ltypes import i32
from numpy import empty, int64
def main():
my_arr_len: i32 = 5
my_arr: i64[my_arr_len] = empty((my_arr_len), dtype=int64)
print(my_arr_len)
main() If we ignore the symbolic value, the int32_t my_arr_len;
struct i64 my_arr_value;
struct i64* my_arr = &my_arr_value;
int64_t *my_arr_data = (int64_t*) malloc(sizeof(int64_t)*my_arr_len);
my_arr->data = my_arr_data;
my_arr->n_dims = 1;
my_arr->dims[0].lower_bound = 0;
my_arr->dims[0].length = my_arr_len;
my_arr_len = 5;
printf("%d\n", my_arr_len); We see that the array length dimension is assigned the value of We would have an undefined dimension during array declaration. The dimension value would be available after the array has been declared. How do we solve this? |
I am raising to high priority, this is a blocker for several projects and a big bug causing random segfaults when you do:
The struct |
I'm taking a look into it. Maybe one of the fixes is to look at the dependency list. |
@certik I updated the comment #1626 (comment). Please have a look. I had applied the following change to generate the --- a/src/lpython/semantics/python_ast_to_asr.cpp
+++ b/src/lpython/semantics/python_ast_to_asr.cpp
@@ -2218,8 +2218,7 @@ public:
init_expr, nullptr);
current_body->push_back(al, ASRUtils::STMT(assign));
ASR::Variable_t* v_variable = ASR::down_cast<ASR::Variable_t>(v_sym);
- if( !ASR::is_a<ASR::Const_t>(*type) &&
- ASRUtils::is_aggregate_type(type) ) {
+ if( !ASR::is_a<ASR::Const_t>(*type)) {
v_variable->m_symbolic_value = nullptr;
v_variable->m_value = nullptr;
Vec<char*> variable_dependencies_vec; |
There are probably multiple bugs related to this. There are some bugs in dependencies. Then there is a bug in ASR that we set both he |
Related to dependencies: it's not just declaring the the variables in the right order. It's also the fact that the RHS has side effects, so it must be executed exactly where it was declared in Python, not earlier. Conclusion: unless it is a Const, we should never set |
The problem is that to deal with initialisation at declaration we convert it to assignment statement in the body. Now ideally such things shouldn't be done. We should remove such conversions and only do initialisation at declaration in all the backends. That would be ideal case. |
I think we must be free to "declare" in any order (obeying the dependencies), before any executable statements. Consequently, the only expressions allowed in initializations are such that do not depend on executable statements and runtime values. So essentially compile time constants. And this should be checked in verify(). Then the frontend can decide how to initialize, but it can't use runtime expressions in initializers, as that fundamentally can't work. |
Then we are required to do the following things,
So a suggestion would be that if you are making any change in backend then @Smit-create please test it with LFortran as well. Note the LLVM output as well, define void @__module__global_symbols__xx_lcompilers_changed_main_xx() {
.entry:
%t2 = alloca double, align 8
store double 7.000000e+00, double* %t2, align 8
%t1 = alloca double, align 8
%0 = call double @__module__global_symbols_f()
%1 = fmul double %0, 1.000000e+06
%2 = load double, double* %t2, align 8
%3 = fadd double %1, %2
store double %3, double* %t1, align 8
store double 7.000000e+00, double* %t2, align 8
%4 = call double @__module__global_symbols_f()
%5 = fmul double %4, 1.000000e+06
%6 = load double, double* %t2, align 8
%7 = fadd double %5, %6
store double %7, double* %t1, align 8
%8 = load double, double* %t1, align 8
call void (i8*, ...) @_lfortran_printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @2, i32 0, i32 0), double %8, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @1, i32 0, i32 0))
br label %return
return: ; preds = %.entry
ret void
} |
|
There are tests like |
Yes, the test case The new rules are:
In def f(n: i32, m: i32):
l: i32 = 2
a: i64[n, m, l] = empty((n, m, l), dtype=int64) In here In #651 we have: def f():
a: i32 = 5
x: i32 = 3
x = 5
b: i32 = x+1
g(a*b+3) Here there are no If we don't already have it, also add a test for: x: Const[i32] = 3
x = 5 And ensure it gives a compiler error at Document in ASR documentation that Finally, the C (and future Python) backends can declare a variable at first assignment, or they can choose to declare all variables first. We will lose the information if the user declared the variable at first use, or before that, but I think that's not a problem, since we can add either some heuristics or compiler options for the backend to do the natural thing that the user wants. |
How about just removing the extra assignment and fixing the LLVM, and C backend to initialize (or add assignment statement, etc.) while declaring the variable using |
I'm curious to know if there are any specific advantages to having both
Since, both the attributes would be in sync, it seems one of them could be redundant. |
I think, this might cause Segfault in LLVM. I have seen this in the global variables (I don't know how's the case inside a function). |
That won't work. #651 will create problems. You need to have the extra assignments, that's why I highlighted it in #1626 (comment).
No. When converting back to original source code (like ASR to Fortran or ASR to Python), you will need
I dis-agree. Backend should always use Also, double initialisation will always happen in LPython (in order to support #651). However by using |
Well then double initialisation will also not happen because you are removing |
@Smit-create Please read the above comments and follow #1626 (comment) for doing things. |
You can't have
If Although there is an issue of
See the last paragraph in #1626 (comment) that addresses this. |
If we are allowing |
@czgdp1807 Can you please try out the suggestion #1626 (comment)? Thanks |
The example above is fixed now: ...
void _xx_lcompilers_changed_main_xx()
{
double t1;
t1 = f()* 1.00000000000000000e+06;
printf("%lf\n", t1);
} Thanks @czgdp1807 and @Smit-create for quickly fixing it! |
Example:
This generates:
Which is wrong, it should only be initialized once.
The text was updated successfully, but these errors were encountered: