let promote_variable t parameter_list f =
    let address_of_variable, max_temp_id = get_address_of f in
    let declaration = hoist_declaration max_temp_id f in
    let max_temp_id = max_temp_id + List.length declaration in
    let environment = Array.create (max_temp_id * 2) null_variable_attribute in
    let variable_map = Hashtbl.create 32 in
    let variable_counter = ref (max_temp_id + 1) in
    let new_temp_id() =
        let n = !variable_counter in
        incr variable_counter;
        n
    in
    let promote s =
        try
            Hashtbl.find variable_map s
            with Not_found ->
              let n = new_temp_id() in
              Hashtbl.add variable_map s n;
              n
    in
    let promote_lvalue = function
        ILlvPtr _ as v -> v
      | ILlvVar( s, _ ) as v ->
          if Hashtbl.mem address_of_variable s then
              v
          else
              ILlvTemp (promote s)
      | ILlvTemp _ as v -> v
    in
    let promote_expression = function
        ILexpCoerce _ as e -> e
      | ILexpConstant _ as e -> e
      | ILexpBinop _ as e -> e
      | ILexpUnaryop _ as e -> e
      | ILexpInvoke( v, l ) -> ILexpInvoke( promote_lvalue v, l )
      | ILexpAddress _ as e -> e
      | ILexpArgument _ as e -> e
      | ILexpIdent _ as e -> e
    in
    let rec promote_statement = function
        [] -> []
      |        i::l ->
          match i.il1_t with
              IL1stmtDeclAutoScalar _ -> assert false
            | IL1stmtDeclBulk( c, t, s, i ) ->
                begin
                    match promote_lvalue (ILlvVar( s, t )) with
                        ILlvTemp n -> ILstmtInitialize( n, t, i )::promote_statement l
                      |        lv ->
                          let n = new_temp_id() in
                          ILstmtRead( n, t, lv, [] )
                          ::ILstmtInitialize( n, t, i )
                          ::promote_statement l
                end
            | IL1stmtSequence statement_list ->
                ILstmtSequence (promote_statement statement_list)
                ::promote_statement l
            | IL1stmtParallel statement_list ->
                ILstmtParallel (promote_statement statement_list)
                ::promote_statement l
            | IL1stmtIf( c, n, m ) -> ILstmtIf( c, n, m )::promote_statement l
            | IL1stmtSwitch( n, jump_table ) ->
                ILstmtSwitch( n, jump_table )::promote_statement l
            | IL1stmtGoto n -> ILstmtGoto n::promote_statement l
            | IL1stmtReturn n -> ILstmtReturn n::promote_statement l
            | IL1stmtDefTemp( n, t, e ) ->
                ILstmtAssign( n, t, e )::promote_statement l
            | IL1stmtReadToTemp( n, t, v, field_list ) ->
                begin
                    match promote_lvalue v, field_list with
                        ILlvTemp m, [] -> ILstmtAssign( n, t, ILexpIdent m )
                      |        lv, _ -> ILstmtRead( n, t, lv, field_list )
                end
                ::promote_statement l
            | IL1stmtWriteILlvVar( _, t ) as lv, field_list, n ) ->
                begin
                    match promote_lvalue lv, field_list with
                        ILlvTemp m, [] -> ILstmtAssign( m, t, ILexpIdent n )
                      |        lv, _ -> ILstmtWrite( lv, field_list, n )
                end
                ::promote_statement l
            | IL1stmtWriteILlvPtr _ as lv, field_list, n ) ->
                ILstmtWrite( lv, field_list, n )::promote_statement l
            | IL1stmtWrite _ -> assert false
    in
    let f =
        Array.map (function b ->
          {
            predecessor = b.Il1.predecessor;
            successor = b.Il1.successor;
            immediate_dominator = b.Il1.immediate_dominator;
            nest_level = 0;
            phi_function = [];
            code = promote_statement b.Il1.code
          }) f
    in
    let parameter_type_list = match_parameter_type t.ct_ty parameter_list in
    let rec make_parameter_initialization i = function
        [] -> []
      |        ( s, t )::l ->
          try
              let n = Hashtbl.find variable_map s in
              environment.(n) <-
                { original_name = Some s; variable_type = t; storage_class = FuncArgs };
              ILstmtAssign( n, t, ILexpArgument i )::make_parameter_initialization (i + 1) l
              with Not_found ->
                let n = new_temp_id() in
                environment.(n) <-
                  { original_name = Some s; variable_type = t; storage_class = FuncArgs };
                ILstmtAssign( n, t, ILexpArgument i )
                ::ILstmtWriteILlvVar( s, t ), [], n )
                ::make_parameter_initialization (i + 1) l
    in
    let set_environment( c, t, s ) =
        try
            let n = Hashtbl.find variable_map s in
            environment.(n) <-
              { original_name = Some s; variable_type = t; storage_class = c }
                with Not_found -> ()
    in
    List.iter set_environment declaration;
    f.(0).code <- (make_parameter_initialization 0 parameter_type_list)@f.(0).code;
    f, Array.sub environment 0 (!variable_counter)