Confusion in The return Keyword

In this code given in Julia documentation i got following results. Is there some discrepancy?

julia> function hypot(x, y)
           x = abs(x)
           y = abs(y)
           if x > y
               r = y/x
               return x*sqrt(1 + r*r)
           end
           if y == 0
               return zero(x)
           end
           r = x/y
           return y*sqrt(1 + r*r)
       end
hypot (generic function with 1 method)

julia> hypot(2,0)
2.0

julia> zero(2)
0

This code runs in order. Since x>y this hits the first return.

5 Likes

I think you meant to refer to Functions Β· The Julia Language

There is no discrepancy, at least not in the code. For the first x=2 and y=0 we have x > y so the first case kicks in

If you do hypot(0,0) the second case is reached and you indeed get an integer 0.

1 Like

Of course, in a purely linear function body like g, the usage of return is pointless since the expression x + y is never evaluated and we could simply make x * y the last expression in the function and omit the return. In conjunction with other control flow, however, return is of real use.

  • Who determines whether a part of code will be evaluated, conditional if or return ?
@code_llvm hypot(0,0)
julia> @code_llvm hypot(0,0)
; Function Signature: hypot(Int64, Int64)
;  @ REPL[1]:1 within `hypot`
define { ptr, i8 } @julia_hypot_924(ptr noalias nocapture noundef nonnull align 8 dereferenceable(8) %union_bytes_return, i64 signext %"x::Int64", i64 signext %"y::Int64") #0 {
top:
;  @ REPL[1]:2 within `hypot`
; β”Œ @ int.jl:188 within `abs`
; β”‚β”Œ @ int.jl:142 within `flipsign`
    %0 = call i64 @llvm.abs.i64(i64 %"x::Int64", i1 false)
; β””β””
;  @ REPL[1]:3 within `hypot`
; β”Œ @ int.jl:188 within `abs`
; β”‚β”Œ @ int.jl:142 within `flipsign`
    %1 = call i64 @llvm.abs.i64(i64 %"y::Int64", i1 false)
; β””β””
;  @ REPL[1]:4 within `hypot`
; β”Œ @ operators.jl:379 within `>`
; β”‚β”Œ @ int.jl:83 within `<`
    %.not = icmp slt i64 %1, %0
; β””β””
  br i1 %.not, label %L14, label %L19

L14:                                              ; preds = %top
;  @ REPL[1]:5 within `hypot`
; β”Œ @ int.jl:97 within `/`
; β”‚β”Œ @ float.jl:374 within `float`
; β”‚β”‚β”Œ @ float.jl:348 within `AbstractFloat`
; β”‚β”‚β”‚β”Œ @ float.jl:239 within `Float64`
      %2 = sitofp i64 %1 to double
      %3 = sitofp i64 %0 to double
; β”‚β””β””β””
; β”‚ @ int.jl:97 within `/` @ float.jl:494
   %4 = fdiv double %2, %3
; β””
;  @ REPL[1]:6 within `hypot`
; β”Œ @ float.jl:493 within `*`
   %5 = fmul double %4, %4
; β””
; β”Œ @ promotion.jl:429 within `+` @ float.jl:491
   %6 = fadd double %5, 1.000000e+00
; β””
; β”Œ @ math.jl:609 within `sqrt`
   %7 = call double @llvm.sqrt.f64(double %6)
; β””
; β”Œ @ promotion.jl:430 within `*` @ float.jl:493
   %8 = fmul double %7, %3
; β””
  store double %8, ptr %union_bytes_return, align 8
  br label %common.ret

L19:                                              ; preds = %top
;  @ REPL[1]:8 within `hypot`
; β”Œ @ promotion.jl:639 within `==`
   %.not23 = icmp eq i64 %"y::Int64", 0
; β””
  br i1 %.not23, label %common.ret, label %L31

common.ret:                                       ; preds = %L31, %L19, %L14
  %common.ret.op = phi { ptr, i8 } [ { ptr null, i8 1 }, %L14 ], [ { ptr null, i8 1 }, %L31 ], [ { ptr @"jl_global#939.jit", i8 -126 }, %L19 ]
;  @ REPL[1] within `hypot`
  ret { ptr, i8 } %common.ret.op

L31:                                              ; preds = %L19
;  @ REPL[1]:11 within `hypot`
; β”Œ @ int.jl:97 within `/`
; β”‚β”Œ @ float.jl:374 within `float`
; β”‚β”‚β”Œ @ float.jl:348 within `AbstractFloat`
; β”‚β”‚β”‚β”Œ @ float.jl:239 within `Float64`
      %9 = sitofp i64 %0 to double
      %10 = sitofp i64 %1 to double
; β”‚β””β””β””
; β”‚ @ int.jl:97 within `/` @ float.jl:494
   %11 = fdiv double %9, %10
; β””
;  @ REPL[1]:12 within `hypot`
; β”Œ @ float.jl:493 within `*`
   %12 = fmul double %11, %11
; β””
; β”Œ @ promotion.jl:429 within `+` @ float.jl:491
   %13 = fadd double %12, 1.000000e+00
; β””
; β”Œ @ math.jl:609 within `sqrt`
   %14 = call double @llvm.sqrt.f64(double %13)
; β””
; β”Œ @ promotion.jl:430 within `*` @ float.jl:493
   %15 = fmul double %14, %10
; β””
  store double %15, ptr %union_bytes_return, align 8
  br label %common.ret
}

:backhand_index_pointing_up: contains sqrt() part while for following g(x,y) function

julia> function g(x, y)
           return x * y
           x + y
       end
@code_llvm g(2,3)
julia> @code_llvm g(2,3)
; Function Signature: g(Int64, Int64)
;  @ REPL[5]:1 within `g`
define i64 @julia_g_1024(i64 signext %"x::Int64", i64 signext %"y::Int64") #0 {
top:
;  @ REPL[5]:2 within `g`
; β”Œ @ int.jl:88 within `*`
   %0 = mul i64 %"y::Int64", %"x::Int64"
; β””
  ret i64 %0
}


don’t have x+y part.

As was already explained in theprevious responses, in your example, you called hypot(2, 0). This means that x > y, and thus it hits the if x > y branch and before it ever reaches the if y == 0 branch.

In the if x > y branch, there’s a return there, so the function terminates when it hits that branch, meaning your if y == 0 branch is never reached.


Regarding

@code_llvm hypot(0,0)

that is just (annoyingly misleading) sugar for

code_llvm(hypot, (typeof(0), typeof(0)))

That is, the llvm code there does not know anything about the values 0, 0, and is showing you the generated code in the generic case. This means that all 3 code-paths are present in the generated code, because none can be eliminated.