let make_basic_block stmt_list_list =
  let length = List.length stmt_list_list in
  let successor = Array.create length [] in
  let predecessor = Array.create length [] in
  let jump_destination = Hashtbl.create 32 in
  let add_control_flow i j =
    if not (List.mem j successor.(i)) then
      successor.(i) <- j::successor.(i);
    if not (List.mem i predecessor.(j)) then
      predecessor.(j) <- i::predecessor.(j)
  in
  let add_jump_destination s i =
    Hashtbl.add jump_destination s i
  in
  let get_jump_destination s =
    Hashtbl.find jump_destination s
  in

  let code = Array.of_list stmt_list_list in
  for i = 0 to length - 1 do
    let set_jump_destination = function
        { il0_t = IL0stmtLabel s } -> add_jump_destination s i
      | _ -> ()
    in
    List.iter set_jump_destination code.(i)
  done;
  for i = 0 to length - 1 do
    let rec continue = function
        [] ->
          (* fall-thru flow *)
          if i + 1 < length then
            add_control_flow i (i + 1)
      | stmt::l -> match stmt.il0_t with
          IL0stmtIf( _, _, s ) ->
            add_control_flow i (get_jump_destination s);
            continue l
        | IL0stmtSwitch( _, jump_table ) ->
            List.iter (function _, s -> add_control_flow i (get_jump_destination s)) jump_table
        | IL0stmtGoto s ->
            add_control_flow i (get_jump_destination s)
        | IL0stmtReturn _ -> ()
        | _ -> continue l
    in
    continue code.(i)
  done;
  
  let rec il1s_of_il0s = function
      [] -> []
    | stmt0::l -> 
        let make_il1 i = 
          { il1_defines = stmt0.il0_defines;
            il1_depends = stmt0.il0_depends;
            il1_t = i } in
        match stmt0.il0_t with
          IL0stmtDefTemp(v,ty,ex) -> make_il1 (IL1stmtDefTemp(v,ty,ex))::il1s_of_il0s l
        | IL0stmtReadToTemp(v,ty,lv,fs) -> make_il1 (IL1stmtReadToTemp(v,ty,lv,fs))::il1s_of_il0s l
        | IL0stmtWrite(lv,fs,v) -> make_il1 (IL1stmtWrite(lv,fs,v))::il1s_of_il0s l
        | IL0stmtSequence(is) -> make_il1 (IL1stmtSequence(il1s_of_il0s is))::il1s_of_il0s l
        | IL0stmtParallel(is) -> make_il1 (IL1stmtParallel(il1s_of_il0s is))::il1s_of_il0s l
        | IL0stmtLabel _ -> il1s_of_il0s l
        | IL0stmtDeclAutoScalar( c, t, s, i ) -> make_il1 (IL1stmtDeclAutoScalar( c, t, s, i ))::il1s_of_il0s l
        | IL0stmtDeclBulk( c, t, s, i ) -> make_il1 (IL1stmtDeclBulk( c, t, s, i ))::il1s_of_il0s l
        | IL0stmtIf( f, e, s ) -> make_il1 (IL1stmtIf( f, e, get_jump_destination s ))::il1s_of_il0s l
        | IL0stmtSwitch( e, jump_table ) ->
            make_il1 (IL1stmtSwitch( e, List.map (function c, s -> c, get_jump_destination s) jump_table ))::[]
        | IL0stmtGoto s -> make_il1 (IL1stmtGoto (get_jump_destination s))::[]
        | IL0stmtReturn e -> make_il1 (IL1stmtReturn e)::[]
  in

  let make_basic_block i =
    {
     predecessor = List.sort compare predecessor.(i);
     successor = List.sort compare successor.(i);
     immediate_dominator = -1;
     code = il1s_of_il0s code.(i)
   }
  in
  Array.init length make_basic_block