Why superfluous function call is not eliminated?

function f(x)
       T = typeof(sin(x))
       convert(T, x)
end

It seems that the call sin(x) can be ellided most of the time, because sin is type-stable. However,

@code_llvm f(1.)
define double @julia_f_35182(double) {
top:
  %1 = call double @julia_sin_35097(double %0)
  ret double %0
}

(I deleted the comments).

This contains the call to sin. Why there is no “constant propagation” here (not sure if this is the correct term here)? The compiler can infer the type of sin(x) when x is Float64, hence the sin call is not necessary.

This is probably related to Getting the type of `f(x)` at compile time (without evaluating `f(x)`), for type-stable functions, but I thought it was a separate question.

What kind of constant propagation were you hoping for? Do you just want LLVM code for returning the constant value of f(1.)?

I was expecting sin not to be called to be called and T to be inferred at compile-time.

I think T is inferred because it returns %0 directly but LLVM thinks sin has side-effects maybe.

I remember a comment by yuyichao that might apply here. A possible side-effect is that sin can throw an exception if x == Inf.

However,

function f(x)
  T = typeof(exp(x))
  convert(T, x)
end

still calls exp (which I don’t think throws any exceptions)

@code_llvm f(1.)
define double @julia_f_35146(double) {
top:
  %1 = call double @julia_exp_35147(double %0)
  ret double %0
}
1 Like

Because there’s no constant to be propagated here.

If your question is instead why pure function isn’t being eliminated, then,

  1. sin isn’t a pure function.
  2. We currently don’t do that optimization.
3 Likes

Is exp a pure function? In that case it could be eliminated (say in future Julia)?

I mean, any optimization could be implemented but none of them will be guaranteed to be implemented…

Ref https://github.com/JuliaLang/julia/pull/29566 though.

2 Likes