Is there a "select case" equivalent in Julia?

I believe the kind of pattern you show up top will be compiled to a switch and then jump table (via Julia and LLVM respectively), much like I assume a Fortran compiler could.

Edit: it doesn’t generate a jump table, but I wasn’t able to coax gfortran to create one either on Godbolt. If someone figures out how to, please let me know :slight_smile:

Adapting that example:

step11(it) = print("step11", it)
step12(it) = print("step12", it)

function steps(it)
  if it === 11
    step11(it)
  elseif it === 12
    step12(it)
  else
    throw("illegal value $it")
  end
  return
end

@code_llvm steps(1) clearly shows a jump table being constructed:

define void @julia_steps_214(i64 signext %0) #0 {
top:
  %1 = alloca [2 x {}*], align 8
  %gcframe3 = alloca [3 x {}*], align 16
  %gcframe3.sub = getelementptr inbounds [3 x {}*], [3 x {}*]* %gcframe3, i64 0, i64 0
  %.sub = getelementptr inbounds [2 x {}*], [2 x {}*]* %1, i64 0, i64 0
  %2 = bitcast [3 x {}*]* %gcframe3 to i8*
  call void @llvm.memset.p0i8.i32(i8* nonnull align 16 dereferenceable(24) %2, i8 0, i32 24, i1 false)
  %thread_ptr = call i8* asm "movq %fs:0, $0", "=r"() #8
  %ppgcstack_i8 = getelementptr i8, i8* %thread_ptr, i64 -8
  %ppgcstack = bitcast i8* %ppgcstack_i8 to {}****
  %pgcstack = load {}***, {}**** %ppgcstack, align 8
;  @ REPL[3]:2 within `steps`
  %3 = bitcast [3 x {}*]* %gcframe3 to i64*
  store i64 4, i64* %3, align 16
  %4 = getelementptr inbounds [3 x {}*], [3 x {}*]* %gcframe3, i64 0, i64 1
  %5 = bitcast {}** %4 to {}***
  %6 = load {}**, {}*** %pgcstack, align 8
  store {}** %6, {}*** %5, align 8
  %7 = bitcast {}*** %pgcstack to {}***
  store {}** %gcframe3.sub, {}*** %7, align 8
  switch i64 %0, label %L11 [
    i64 11, label %L3
    i64 12, label %L8
  ]

L3:                                               ; preds = %top
;  @ REPL[3]:3 within `steps`
; ┌ @ REPL[1]:1 within `step11`
   call void @j_print_216({}* inttoptr (i64 140108222856496 to {}*), i64 signext 11) #0
; └
  br label %L14

L8:                                               ; preds = %top
;  @ REPL[3]:5 within `steps`
; ┌ @ REPL[2]:1 within `step12`
   call void @j_print_217({}* inttoptr (i64 140108217624080 to {}*), i64 signext 12) #0
; └
  br label %L14

L11:                                              ; preds = %top
;  @ REPL[3]:7 within `steps`
  %8 = call nonnull {}* @jl_box_int64(i64 signext %0)
  %9 = getelementptr inbounds [3 x {}*], [3 x {}*]* %gcframe3, i64 0, i64 2
  store {}* %8, {}** %9, align 16
  store {}* inttoptr (i64 140108212808048 to {}*), {}** %.sub, align 8
  %10 = getelementptr inbounds [2 x {}*], [2 x {}*]* %1, i64 0, i64 1
  store {}* %8, {}** %10, align 8
  %11 = call nonnull {}* @jl_apply_generic({}* inttoptr (i64 140108035749248 to {}*), {}** nonnull %.sub, i32 2)
  call void @jl_throw({}* %11)
  unreachable

L14:                                              ; preds = %L8, %L3
  %12 = load {}*, {}** %4, align 8
  %13 = bitcast {}*** %pgcstack to {}**
  store {}* %12, {}** %13, align 8
;  @ REPL[3]:9 within `steps`
  ret void
}

See Computed goto (or labels as values) in Julia? for more.

For a terser way of switching on values, have a look into https://github.com/thautwarm/MLStyle.jl:

using MLStyle
...
function steps_match(it)
  @match it begin
    11 => step11(it)
    12 => step12(it)
    _ => throw("illegal value $it")
  end
  return
end

I’ve verified that this also generates a switch at the LLVM level.

6 Likes