%% -*- erlang-indent-level: 2 -*-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Copyright (c) 2001 by Erik Johansson.  All Rights Reserved 
%% ====================================================================
%%  Filename : 	hipe_rtl_primops.erl
%%  Purpose  :  
%%  Notes    : 
%%  History  :	* 2001-03-15 Erik Johansson (happi@csd.uu.se): 
%%               Created.
%%
%% $Id$
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-module(hipe_rtl_primops). 

-export([gen_primop/3, gen_enter_primop/3, gen_call_builtin/6,
	 gen_enter_builtin/2]).

%% --------------------------------------------------------------------

-include("../main/hipe.hrl").
-include("../icode/hipe_icode_primops.hrl").
-include("hipe_rtl.hrl").
-include("hipe_literals.hrl").

%% --------------------------------------------------------------------
%% Handling of known MFA builtins that are inline expanded

gen_call_builtin(Fun, Dst, Args, IsGuard, Cont, Fail) ->
  case Fun of
    {erlang, apply, 3} ->
      gen_apply(Dst, Args, Cont, Fail);

    {erlang, element, 2} ->
      gen_element(Dst, Args, IsGuard, Cont, Fail);

    {erlang, self, 0} ->
      gen_self(Dst, Cont);

    {erlang, is_tuple, 1} ->
      gen_is_tuple(Dst, Args, Cont);

    {hipe_bifs, in_native, 0} ->
      Dst1 =
	case Dst of
	  [] -> %% The result is not used.
	    hipe_rtl:mk_new_var();
	  [Dst0] -> Dst0
	end,
      [hipe_rtl:mk_load_atom(Dst1, true), hipe_rtl:mk_goto(Cont)];
    
    _ -> []    % not a builtin
  end.

%% (Recall that enters cannot occur within a catch-region in the same
%% function, so we do not need to consider fail-continuations here.)
%% TODO: should we inline expand more functions here? Cf. above.
gen_enter_builtin(Fun, Args) ->
  case Fun of
    {erlang, apply, 3} ->
      gen_enter_apply(Args);

%% TODO
%%     {erlang, element, 2} ->
%%       gen_enter_element(Args, IsGuard);

%% TODO
%%     {erlang, self, 0} ->
%%       gen_enter_self();

    {hipe_bifs, in_native, 0} ->
      Dst = hipe_rtl:mk_new_var(),
      [hipe_rtl:mk_load_atom(Dst, true), hipe_rtl:mk_return([Dst])];

    _ -> []    % not a builtin
  end.

%% --------------------------------------------------------------------
%% Generate code to jump to in case the inlined function fails.

gen_fail_code(Fail, Type) -> 
  gen_fail_code(Fail, Type, false).

gen_fail_code(Fail, Type, IsGuard) -> 
  case IsGuard of
    true when Fail =/= [] ->
      {Fail, []};  % go directly to target
    false ->
      NewLabel =  hipe_rtl:mk_new_label(),
      NewLabelName = hipe_rtl:label_name(NewLabel),
      {NewLabelName, [NewLabel | fail_code(Fail, Type)]}
  end.

fail_code(Fail, Type) when is_atom(Type) ->
  Var = hipe_rtl:mk_new_var(),
  [hipe_rtl:mk_load_atom(Var, Type),
   hipe_rtl_exceptions:gen_fail(error, [Var], Fail)];
fail_code(Fail, {Type, Value}) when is_atom(Type) ->
  Var = hipe_rtl:mk_new_var(),
  [hipe_rtl:mk_load_atom(Var, Type),
   hipe_rtl:mk_gctest(3),  % room for a 2-tuple
   gen_mk_tuple(Var,[Var,Value]),
   hipe_rtl_exceptions:gen_fail(error, [Var], Fail)].

fp_fail_code(TmpFailLbl, FailLbl) ->
  [TmpFailLbl |
   hipe_rtl_arch:handle_fp_exception() ++
   [fail_code(FailLbl, badarith)]].

%% --------------------------------------------------------------------
%% CALL PRIMOP
%%
%% @doc
%%   Generates RTL code for primops. This is mostly a dispatch function.
%%   Tail calls to primops (enter_fun, apply, etc.) are not handled here!
%% @end

gen_primop({Op,Dst,Args,Cont,Fail}, IsGuard, ConstTab) ->
  GotoCont = hipe_rtl:mk_goto(Cont),
  case Op of
    %%
    %% Binary Syntax
    %%
    {hipe_bs_primop, BsOP} ->
      {FailLabelName, FailCode} = gen_fail_code(Fail, badarg, IsGuard),
      {Code1,NewConstTab} = 
	hipe_rtl_binary:gen_rtl(BsOP, Dst, Args, Cont, FailLabelName, ConstTab),
      {[Code1,FailCode], NewConstTab};
    %%
    %% Other primops
    %%
    _ ->
      Code = 
	case Op of
	  %% Arithmetic
	  '+' ->
	    %gen_extra_unsafe_add_2(Dst, Args, Cont);
	    gen_add_sub_2(Dst, Args, Cont, Fail, Op, add);
	  '-' ->
	    gen_add_sub_2(Dst, Args, Cont, Fail, Op, sub);
	  '*' ->
	    gen_mul_2(Dst, Args, Cont, Fail);
	  '/' ->
	    %% BIF call: am_Div -> nbif_div_2 -> erts_mixed_div
	    [hipe_rtl:mk_call(Dst, '/', Args, Cont, Fail, not_remote)];
          'gen_add' ->
            gen_general_add_sub(Dst, Args, Cont, Fail, '+');
          'gen_sub' ->
            gen_general_add_sub(Dst, Args, Cont, Fail, '-');
	  'unsafe_add' ->
	    %gen_extra_unsafe_add_2(Dst, Args, Cont);
	    gen_unsafe_add_sub_2(Dst, Args, Cont, Fail, '+', add);
	  'extra_unsafe_add' ->
	    gen_extra_unsafe_add_2(Dst, Args, Cont);
	  'unsafe_sub' ->
	    gen_unsafe_add_sub_2(Dst, Args, Cont, Fail, '-', sub);
	  'extra_unsafe_sub' ->
	    gen_extra_unsafe_sub_2(Dst, Args, Cont);
          %'unsafe_mul' ->
          %  gen_unsafe_mul_2(Dst, Args, Cont, Fail, '*');
	  'div' ->
	    %% BIF call: am_div -> nbif_intdiv_2 -> intdiv_2
	    [hipe_rtl:mk_call(Dst, 'div', Args, Cont, Fail, not_remote)];
	  'rem' ->
	    %% BIF call: am_rem -> nbif_rem_2 -> rem_2
	    [hipe_rtl:mk_call(Dst, 'rem', Args, Cont, Fail, not_remote)];
	  'band' ->
	    gen_bitop_2(Dst, Args, Cont, Fail, Op, 'and');
	  'bor' ->
	    gen_bitop_2(Dst, Args, Cont, Fail, Op, 'or');
	  'bxor' ->
	    gen_bitop_2(Dst, Args, Cont, Fail, Op, 'xor');
	  'bnot' ->
	    gen_bnot_2(Dst, Args, Cont, Fail, Op);
	  'bsr'->
	    %% BIF call: am_bsr -> nbif_bsr_2 -> bsr_2
	    gen_bsr_2(Dst, Args, Cont, Fail, Op);
	    %[hipe_rtl:mk_call(Dst, 'bsr', Args, Cont, Fail, not_remote)];
	  'bsl' -> 
	    %% BIF call: am_bsl -> nbif_bsl_2 -> bsl_2
	    [hipe_rtl:mk_call(Dst, 'bsl', Args, Cont, Fail, not_remote)];
	  unsafe_band ->
	    gen_unsafe_bitop_2(Dst, Args, Cont, 'and');
	  unsafe_bor -> 
	    gen_unsafe_bitop_2(Dst, Args, Cont, 'or');
	  unsafe_bxor ->
	    gen_unsafe_bitop_2(Dst, Args, Cont, 'xor');
	  unsafe_bnot ->
	    gen_unsafe_bnot_2(Dst, Args, Cont);
	  unsafe_bsr ->
	    gen_unsafe_bsr_2(Dst, Args, Cont);
          unsafe_bsl ->
	    gen_unsafe_bsl_2(Dst, Args, Cont);
	  %%---------------------------------------------
	  %% List handling
	  %%---------------------------------------------
	  cons ->
	    case Dst of
	      [] -> %% The result is not used.
		[GotoCont];
	      [Dst1] ->	    
		[gen_cons(Dst1, Args), GotoCont]
	    end;
	  unsafe_hd ->
	    case Dst of
	      [] -> %% The result is not used.
		[GotoCont];
	      [Dst1] ->	  
		[gen_unsafe_hd(Dst1, Args), GotoCont]
	    end;
	  unsafe_tl ->
	    case Dst of
	      [] -> %% The result is not used.
		[GotoCont];
	      [Dst1] ->	 
		[gen_unsafe_tl(Dst1, Args),GotoCont]
	    end;
	  %%---------------------------------------------
	  %% Tuple handling
	  %%---------------------------------------------
	  mktuple ->
	    case Dst of
	      [] -> %% The result is not used.
		[GotoCont];
	      [Dst1] ->
		[gen_mk_tuple(Dst1, Args),GotoCont]
	    end;
	  #unsafe_element{index=N} ->
	    case Dst of
	      [] -> %% The result is not used.
		[GotoCont];
	      [Dst1] ->
		[Tuple] = Args,
		[gen_unsafe_element(Dst1, hipe_rtl:mk_imm(N), Tuple),GotoCont]
	    end;
	  #unsafe_update_element{index=N} ->
	    [Dst1] = Dst,
	    [Tuple, Value] = Args,
	    [gen_unsafe_update_element(Tuple, hipe_rtl:mk_imm(N), Value),
	     hipe_rtl:mk_move(Dst1, Tuple),
	     GotoCont];
	  {element, TypeInfo} ->
	    Dst1 =
	      case Dst of
		[] -> %% The result is not used.
		  hipe_rtl:mk_new_var();
		[Dst0] -> Dst0
	      end,
	    [TupleInfo, IndexInfo] = TypeInfo,	    
	    [Index, Tuple] = Args,	    
	    [gen_element_1(Dst1, Index, Tuple, IsGuard, Cont, Fail,
			   TupleInfo, IndexInfo)];

	  %%---------------------------------------------
	  %% Apply-fixarity
	  %%---------------------------------------------
	  #apply_N{arity=Arity} ->
	    gen_apply_N(Dst, Arity, Args, Cont, Fail);

	  %%---------------------------------------------
	  %% GC test
	  %%---------------------------------------------
	  #gc_test{need=Need} ->
	    [hipe_rtl:mk_gctest(Need), GotoCont];

	  %%---------------------------------------------
	  %% Process handling
	  %%---------------------------------------------
	  redtest ->
	    [gen_redtest(1), GotoCont];
	  %%---------------------------------------------
	  %% Receives
	  %%---------------------------------------------
	  check_get_msg ->
	    gen_check_get_msg(Dst, GotoCont, Fail);
	  next_msg ->
	    gen_next_msg(Dst, GotoCont);
	  select_msg ->
	    gen_select_msg(Dst, Cont);
	  clear_timeout ->
	    gen_clear_timeout(Dst, GotoCont);
	  set_timeout ->
	    %% BIF call: am_set_timeout -> nbif_set_timeout -> hipe_set_timeout
	    [hipe_rtl:mk_call(Dst, set_timeout, Args, Cont, Fail, not_remote)];
	  suspend_msg ->
	    gen_suspend_msg(Dst, Cont);
	  %%---------------------------------------------
	  %% Closures
	  %%---------------------------------------------
	  call_fun ->
	    gen_call_fun(Dst, Args, Cont, Fail);
	  #mkfun{mfa=MFA, magic_num=MagicNum, index=Index} ->
	    case Dst of
	      [] -> %% The result is not used.
		[GotoCont];
	      _ ->
		[gen_mkfun(Dst, MFA, MagicNum, Index, Args), GotoCont]
	    end;
	  #closure_element{n=N} ->
	    case Dst of
	      [] -> %% The result is not used.
		[GotoCont];
	      [Dst1] ->
		[Closure] = Args,
		[gen_closure_element(Dst1, hipe_rtl:mk_imm(N), Closure),
		 GotoCont]
	    end;
	  %%---------------------------------------------
	  %% Floating point instructions.
	  %%---------------------------------------------
	  fp_add ->
	    [Arg1, Arg2] = Args,
	    case Dst of
	      [] ->
		hipe_rtl:mk_fp(hipe_rtl:mk_new_fpreg(), Arg1, 'fadd', Arg2);
	      [Dst1] ->
		hipe_rtl:mk_fp(Dst1, Arg1, 'fadd', Arg2)
	    end;
	  fp_sub ->
	    [Arg1, Arg2] = Args,
	    case Dst of
	      [] ->
		hipe_rtl:mk_fp(hipe_rtl:mk_new_fpreg(), Arg1, 'fsub', Arg2);
	      [Dst1] ->
		hipe_rtl:mk_fp(Dst1, Arg1, 'fsub', Arg2)
	    end;	  
	  fp_mul ->
	    [Arg1, Arg2] = Args,
	    case Dst of
	      [] ->
		hipe_rtl:mk_fp(hipe_rtl:mk_new_fpreg(), Arg1, 'fmul', Arg2);
	      [Dst1] ->
		hipe_rtl:mk_fp(Dst1, Arg1, 'fmul', Arg2)
	    end;
	  fp_div ->
	    [Arg1, Arg2] = Args,
	    case Dst of
	      [] ->
		hipe_rtl:mk_fp(hipe_rtl:mk_new_fpreg(), Arg1, 'fdiv', Arg2);
	      [Dst1] ->
		hipe_rtl:mk_fp(Dst1, Arg1, 'fdiv', Arg2)
	    end;	  
	  fnegate ->
	    [Arg] = Args,
	    case Dst of
	      [] ->
		hipe_rtl:mk_fp_unop(hipe_rtl:mk_new_fpreg(), Arg, 'fchs');
	      [Dst1] ->
		hipe_rtl:mk_fp_unop(Dst1, Arg, 'fchs')
	    end;	  
	  fclearerror ->
	    gen_fclearerror();
	  fcheckerror ->
	    gen_fcheckerror(Cont, Fail);
	  conv_to_float ->
	    case Dst of
	      [] ->
		gen_conv_to_float(hipe_rtl:mk_new_fpreg(), Args, Cont, Fail);
	      [Dst1] ->
		gen_conv_to_float(Dst1, Args, Cont, Fail)
	    end;
	  unsafe_untag_float ->
	    [Arg] = Args,
	    case Dst of
	      [] ->
		hipe_tagscheme:unsafe_untag_float(hipe_rtl:mk_new_fpreg(),
						  Arg); 
	      [Dst1]->
		hipe_tagscheme:unsafe_untag_float(Dst1, Arg)
	    end;
	  unsafe_tag_float ->
	    [Arg] = Args,
	    case Dst of
	      [] ->
		hipe_tagscheme:unsafe_tag_float(hipe_rtl:mk_new_var(), Arg);
	      [Dst1]->
		hipe_tagscheme:unsafe_tag_float(Dst1, Arg)
	    end;

	  %% Only names listed above are accepted! MFA:s are not primops!
	  _ ->
	    erlang:error({bad_primop, Op})
	end,
      {Code, ConstTab}
  end.

gen_enter_primop({Op, Args}, IsGuard, ConstTab) ->
  case Op of
    enter_fun ->
      %% Tail-call to a closure must preserve tail-callness!
      %% (Passing Continuation = [] to gen_call_fun/5 does this.)
      Code = gen_call_fun([], Args, [], []),
      {Code, ConstTab};

    #apply_N{arity=Arity} ->
      %% Tail-call to a closure must preserve tail-callness!
      %% (Passing Continuation = [] to gen_apply_N/5 does this.)
      Code = gen_apply_N([], Arity, Args, [], []),
      {Code,  ConstTab};

    _ ->
      %% All other primop tail calls are converted to call + return.
      Dst = [hipe_rtl:mk_new_var()],
      OkLab = hipe_rtl:mk_new_label(),
      {Code,ConstTab1} = 
	gen_primop({Op,Dst,Args,hipe_rtl:label_name(OkLab),[]}, 
		   IsGuard, ConstTab),
      {Code ++ [OkLab, hipe_rtl:mk_return(Dst)], ConstTab1}
  end.


%% --------------------------------------------------------------------
%% ARITHMETIC
%% --------------------------------------------------------------------

%%
%% Inline addition & subtraction
%%

gen_general_add_sub(Dst, Args, Cont, Fail, Op) ->
  case Dst of
    [] ->
      [hipe_rtl:mk_call([hipe_rtl:mk_new_var()],
                        Op, Args, Cont, Fail, not_remote)];
    [Res] ->
      [hipe_rtl:mk_call([Res], Op, Args, Cont, Fail, not_remote)]
  end.

gen_add_sub_2(Dst, Args, Cont, Fail, Op, AluOp) ->
  [Arg1, Arg2] = Args,
  GenCaseLabel = hipe_rtl:mk_new_label(),
  case Dst of
    [] ->
      [hipe_tagscheme:test_two_fixnums(Arg1, Arg2,
				       hipe_rtl:label_name(GenCaseLabel))|
       gen_op_general_case(hipe_rtl:mk_new_var(),
			   Op, Args, Cont, Fail, GenCaseLabel)];
    [Res] ->
      [hipe_tagscheme:test_two_fixnums(Arg1, Arg2,
				       hipe_rtl:label_name(GenCaseLabel)),
       hipe_tagscheme:fixnum_addsub(AluOp, Arg1, Arg2, Res, GenCaseLabel)|
       gen_op_general_case(Res,Op, Args, Cont, Fail, GenCaseLabel)]
  end.

gen_unsafe_add_sub_2(Dst, Args, Cont, Fail, Op, AluOp) ->
  [Arg1, Arg2] = Args,
  case Dst of
    [] ->      
      [hipe_rtl:mk_goto(Cont)];
    [Res] ->
      case Fail of
	[]->
	  GenCaseLabel = hipe_rtl:mk_new_label(),
	  [hipe_tagscheme:fixnum_addsub(AluOp, Arg1, Arg2, Res, GenCaseLabel)|
	   gen_op_general_case(Res,Op, Args, Cont, Fail, GenCaseLabel)];
	_ ->
	  [hipe_tagscheme:fixnum_addsub(AluOp, Arg1, Arg2, Res, 
					hipe_rtl:mk_label(Fail))]
      end
  end.

gen_extra_unsafe_add_2(Dst, Args, Cont) ->
  [Arg1, Arg2] = Args,
  case Dst of
    [] ->      
      [hipe_rtl:mk_goto(Cont)];
    [Res] ->
      hipe_tagscheme:unsafe_fixnum_add(Arg1, Arg2, Res)
  end.

gen_extra_unsafe_sub_2(Dst, Args, Cont) ->
  [Arg1, Arg2] = Args,
  case Dst of
    [] ->      
      [hipe_rtl:mk_goto(Cont)];
    [Res] ->
      hipe_tagscheme:unsafe_fixnum_sub(Arg1, Arg2, Res)
  end.

gen_op_general_case(Res, Op, Args, Cont, Fail, GenCaseLabel) ->
  [hipe_rtl:mk_goto(Cont),
   GenCaseLabel,
   hipe_rtl:mk_call([Res], Op, Args, Cont, Fail, not_remote)].

%%
%% Inline multiplication
%%

gen_mul_2(Dst, Args, Cont, Fail) ->
  [Arg1,Arg2] = Args,
  GenCaseLabel = hipe_rtl:mk_new_label(),
  {Res1,I2} =
    case Dst of
      [] ->
	{hipe_rtl:mk_new_var(), []};
      [Res0] ->
	{Res0, hipe_tagscheme:fixnum_mul(Arg1, Arg2, Res0, GenCaseLabel)}
    end,
  [hipe_tagscheme:test_two_fixnums(Arg1, Arg2, hipe_rtl:label_name(GenCaseLabel)),
   I2,
   %% BIF call: am_Times -> nbif_mul_2 -> erts_mixed_times
   gen_op_general_case(Res1, '*', Args, Cont, Fail, GenCaseLabel)].

%% gen_unsafe_mul_2([Res], Args, Cont, Fail, Op) ->
%%    [Arg1, Arg2] = Args,
%%    GenCaseLabel = hipe_rtl:mk_new_label(),
%%    [hipe_tagscheme:test_two_fixnums(Arg1, Arg2,
%% 				    hipe_rtl:label_name(GenCaseLabel)),
%%     hipe_tagscheme:fixnum_mul(Arg1, Arg2, Res, GenCaseLabel)|
%%     gen_op_general_case(Res, Op, Args, Cont, Fail, GenCaseLabel)].

%%
%% Inline bitoperations.
%% Only works for band, bor and bxor.
%% The shift operations are too expensive to inline.
%%

gen_bitop_2(Res, Args, Cont, Fail, Op, BitOp) ->
  [Arg1, Arg2] = Args,
  GenCaseLabel = hipe_rtl:mk_new_label(),
  case Res of
    [] -> %% The result is not used.
      [hipe_tagscheme:test_two_fixnums(Arg1, Arg2,
				       hipe_rtl:label_name(GenCaseLabel))|
       gen_op_general_case(hipe_rtl:mk_new_var(),
			   Op, Args, Cont, Fail, GenCaseLabel)];	
    [Res0] -> 
      [hipe_tagscheme:test_two_fixnums(Arg1, Arg2,
				       hipe_rtl:label_name(GenCaseLabel)),
       hipe_tagscheme:fixnum_andorxor(BitOp, Arg1, Arg2, Res0)|
       gen_op_general_case(Res0, Op, Args, Cont, Fail, GenCaseLabel)]
  end.
  
gen_unsafe_bitop_2(Res, Args, Cont, BitOp) ->
  case Res of
    [] -> %% The result is not used.
      [hipe_rtl:mk_goto(Cont)];
    [Res0] -> 
      [Arg1, Arg2] = Args,
      [hipe_tagscheme:fixnum_andorxor(BitOp, Arg1, Arg2, Res0),
       hipe_rtl:mk_goto(Cont)]
  end.

gen_bsr_2(Res, Args, Cont, Fail, Op) ->
  [Arg1, Arg2] = Args,
  GenCaseLabel = hipe_rtl:mk_new_label(),
  case hipe_rtl:is_imm(Arg2) of
    true ->
      Val =  hipe_tagscheme:fixnum_val(hipe_rtl:imm_value(Arg2)),
      Limit = ?bytes_to_bits(hipe_rtl_arch:word_size()),
      if 
	Val < Limit, Val >= 0 ->
	  case Res of
	    [] ->
	      [hipe_tagscheme:test_fixnum(Arg1,
					  hipe_rtl:label_name(Cont),
					  hipe_rtl:label_name(GenCaseLabel),
					  0.99),
	       gen_op_general_case(hipe_rtl:mk_new_var(), Op, Args, Cont, Fail,
				   GenCaseLabel)];
	    [Res0] ->
	      FixLabel = hipe_rtl:mk_new_label(),
	      [hipe_tagscheme:test_fixnum(Arg1,
					  hipe_rtl:label_name(FixLabel),
					  hipe_rtl:label_name(GenCaseLabel),
					  0.99),
	       FixLabel,
	       hipe_tagscheme:fixnum_bsr(Arg1, Arg2, Res0),
	       gen_op_general_case(Res0, Op, Args, Cont, Fail, GenCaseLabel)]
	  end;
	true ->
	  [hipe_rtl:mk_call(Res, 'bsr', Args, Cont, Fail, not_remote)]
      end;
    false ->
      [hipe_rtl:mk_call(Res, 'bsr', Args, Cont, Fail, not_remote)]
  end.

gen_unsafe_bsr_2(Res, Args, Cont) ->
  case Res of
    [] -> %% The result is not used.
      [hipe_rtl:mk_goto(Cont)];
    [Res0] ->  
      [Arg1, Arg2] = Args,
      [hipe_tagscheme:fixnum_bsr(Arg1, Arg2, Res0),
       hipe_rtl:mk_goto(Cont)]
  end.

gen_unsafe_bsl_2(Res, Args, Cont) ->
  case Res of
    [] -> %% The result is not used.
      [hipe_rtl:mk_goto(Cont)];
    [Res0] ->  
      [Arg1, Arg2] = Args,
      [hipe_tagscheme:fixnum_bsl(Arg1, Arg2, Res0),
       hipe_rtl:mk_goto(Cont)]
  end.

%%
%% Inline not.
%%

gen_bnot_2(Res, Args, Cont, Fail, Op) ->
  [Arg] = Args,
  GenCaseLabel = hipe_rtl:mk_new_label(),
  case Res of
    [] -> %% The result is not used.
      [hipe_tagscheme:test_fixnum(Arg, hipe_rtl:label_name(Cont),
				  hipe_rtl:label_name(GenCaseLabel), 0.99),
       gen_op_general_case(hipe_rtl:mk_new_var(), Op, Args, Cont, Fail, 
			   GenCaseLabel)];
    
    [Res0] -> 
      FixLabel = hipe_rtl:mk_new_label(),
      [hipe_tagscheme:test_fixnum(Arg, hipe_rtl:label_name(FixLabel),
				  hipe_rtl:label_name(GenCaseLabel), 0.99),
       FixLabel,
       hipe_tagscheme:fixnum_not(Arg, Res0),
       gen_op_general_case(Res0, Op, Args, Cont, Fail, GenCaseLabel)]
  end.

gen_unsafe_bnot_2(Res, Args, Cont) ->
  case Res of
    [] -> %% The result is not used.
      [hipe_rtl:mk_goto(Cont)];
    [Res0] ->  
      [Arg1] = Args,
      [hipe_tagscheme:fixnum_not(Arg1, Res0),
       hipe_rtl:mk_goto(Cont)]
  end.
 

%% --------------------------------------------------------------------
%% 

%%
%% Inline cons
%%

gen_cons(Dst, [Arg1, Arg2]) ->
  Tmp = hipe_rtl:mk_new_reg(),
  {GetHPInsn, HP, PutHPInsn} = hipe_rtl_arch:heap_pointer(),
  WordSize = hipe_rtl_arch:word_size(),
  HeapNeed = 2*WordSize,
  [GetHPInsn,
   hipe_rtl:mk_store(HP, hipe_rtl:mk_imm(0), Arg1),
   hipe_rtl:mk_store(HP, hipe_rtl:mk_imm(WordSize), Arg2),
   hipe_rtl:mk_move(Tmp, HP),
   hipe_tagscheme:tag_cons(Dst, Tmp),
   hipe_rtl:mk_alu(HP, HP, add, hipe_rtl:mk_imm(HeapNeed)),
   PutHPInsn].

%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% --------------------------------------------------------------------
%% Handling of closures...
%% --------------------------------------------------------------------

%% --------------------------------------------------------------------
%% gen_mkfun
%%
%%    The gc_test should have expanded to
%%    unsigned needed = ERL_FUN_SIZE + num_free;
%%    ErlFunThing* funp = (ErlFunThing *) HAlloc(p, needed);
%%
%% The code generated should do the equivalent of:
%%  Copy arguments to the fun thing
%%    Eterm* hp = funp->env;
%%    for (i = 0; i < num_free; i++) {
%%	*hp++ = reg[i];
%%    }
%%
%%  Fill in fileds
%%    funp->thing_word = HEADER_FUN;
%%    funp->fe = fe;
%%    funp->num_free = num_free;
%%    funp->creator = p->id;
%%    funp->native_code = fe->native_code;
%%  Increase refcount
%%    fe->refc++;
%%
%%  Link to the process off_heap.funs list
%%    funp->next = p->off_heap.funs;
%%    p->off_heap.funs = funp;
%%
%%  Tag the thing
%%    return make_fun(funp);
%%
gen_mkfun([Dst], {Mod,FunId,Arity}, MagicNr, Index, FreeVars) ->
  {GetHPInsn, HP, PutHPInsn} = hipe_rtl_arch:heap_pointer(),
  NumFree = length(FreeVars),

  %%  Copy arguments to the fun thing
  %%    Eterm* hp = funp->env;
  %%    for (i = 0; i < num_free; i++) {
  %%	*hp++ = reg[i];
  %%    }
  CopyFreeVarsCode = gen_free_vars(FreeVars, HP),

  %%  Fill in fields
  %%    funp->thing_word = HEADER_FUN;
  %%    funp->fe = fe;
  %%    funp->num_free = num_free;
  %%    funp->creator = p->id;
  %%    funp->native_code = fe->native_code;
  %%  Increase refcount
  %%    fe->refc++;

  SkeletonCode = gen_fun_thing_skeleton(HP, {Mod,FunId,Arity},
					NumFree, MagicNr, Index),

  %%  Link to the process off_heap.funs list
  %%    funp->next = p->off_heap.funs;
  %%    p->off_heap.funs = funp;
  LinkCode = gen_link_closure(HP),

  %%  Tag the thing and increase the heap_pointer.
  %%    make_fun(funp);
  WordSize = hipe_rtl_arch:word_size(),
  HeapNeed = (?ERL_FUN_SIZE + NumFree) * WordSize,
  TagCode = [hipe_tagscheme:tag_fun(Dst, HP), 
	     %%  AdjustHPCode 
	     hipe_rtl:mk_alu(HP, HP, add, hipe_rtl:mk_imm(HeapNeed)),
	     PutHPInsn],
  [[GetHPInsn | CopyFreeVarsCode], SkeletonCode, LinkCode, TagCode].


gen_fun_thing_skeleton(FunP, FunName={_Mod,_FunId,Arity}, NumFree, 
		       MagicNr, Index) ->
  %% Assumes that funp == heap_pointer
  %%  Fill in fields
  %%    funp->thing_word = HEADER_FUN;
  %%    funp->fe = fe;
  %%    funp->num_free = num_free;
  %%    funp->creator = p->id;
  %%    funp->native_code = fe->native_code;
  %%  And creates a fe (at load time).
  FeVar = hipe_rtl:mk_new_reg(),
  PidVar = hipe_rtl:mk_new_reg_gcsafe(),
  NativeVar = hipe_rtl:mk_new_reg(),

  [hipe_rtl:mk_load_address(FeVar, {FunName, MagicNr, Index}, closure),
   store_struct_field(FunP, ?EFT_FE, FeVar),
   load_struct_field(NativeVar, FeVar, ?EFE_NATIVE_ADDRESS),
   store_struct_field(FunP, ?EFT_NATIVE_ADDRESS, NativeVar),

   store_struct_field(FunP, ?EFT_ARITY, hipe_rtl:mk_imm(Arity-NumFree)),

   gen_inc_refc(FeVar, ?EFE_REFC),

   store_struct_field(FunP, ?EFT_NUM_FREE, hipe_rtl:mk_imm(NumFree)),
   load_p_field(PidVar, ?P_ID),
   store_struct_field(FunP, ?EFT_CREATOR, PidVar),
   store_struct_field(FunP, ?EFT_THING, hipe_tagscheme:mk_fun_header())].

gen_inc_refc(Ptr, Offset) ->
  case ?ERTS_IS_SMP of
    0 -> gen_inc_refc_notsmp(Ptr, Offset);
    1 -> gen_inc_refc_smp(Ptr, Offset)
  end.

gen_inc_refc_notsmp(Ptr, Offset) ->
  Refc = hipe_rtl:mk_new_reg(),
  [load_struct_field(Refc, Ptr, Offset, int32),
   hipe_rtl:mk_alu(Refc, Refc, add, hipe_rtl:mk_imm(1)),
   store_struct_field(Ptr, Offset, Refc, int32)].

gen_inc_refc_smp(Ptr, Offset) ->
  Refc = hipe_rtl:mk_new_reg(),
  [hipe_rtl:mk_alu(Refc, Ptr, 'add', hipe_rtl:mk_imm(Offset)),
   hipe_rtl:mk_call([], 'atomic_inc', [Refc], [], [], not_remote)].

gen_link_closure(FUNP) ->
  case ?P_OFF_HEAP_FUNS of
    [] -> gen_link_closure_non_private(FUNP);
    _ -> gen_link_closure_private(FUNP)
  end.

gen_link_closure_private(FUNP) ->
  %% Link to the process off_heap.funs list
  %%   funp->next = p->off_heap.funs;
  %%   p->off_heap.funs = funp;
  FunsVar = hipe_rtl:mk_new_reg(),

  [load_p_field(FunsVar,?P_OFF_HEAP_FUNS),
   hipe_rtl:mk_store(FUNP, hipe_rtl:mk_imm(?EFT_NEXT), FunsVar),
   store_p_field(FUNP,?P_OFF_HEAP_FUNS)].

gen_link_closure_non_private(_FUNP) -> [].

load_p_field(Dst,Offset) ->
  hipe_rtl_arch:pcb_load(Dst, Offset).
store_p_field(Src, Offset) ->
  hipe_rtl_arch:pcb_store(Offset, Src).

store_struct_field(StructP, Offset, Src) ->
  hipe_rtl:mk_store(StructP, hipe_rtl:mk_imm(Offset), Src).

load_struct_field(Dest, StructP, Offset) ->
  hipe_rtl:mk_load(Dest, StructP, hipe_rtl:mk_imm(Offset)).

store_struct_field(StructP, Offset, Src, int32) ->
  hipe_rtl:mk_store(StructP, hipe_rtl:mk_imm(Offset), Src, int32).

load_struct_field(Dest, StructP, Offset, int32) ->
  hipe_rtl:mk_load(Dest, StructP, hipe_rtl:mk_imm(Offset), int32, signed).

gen_free_vars(Vars, HPReg) ->
  HPVar = hipe_rtl:mk_new_var(),
  WordSize = hipe_rtl_arch:word_size(),
  [hipe_rtl:mk_alu(HPVar, HPReg, add, hipe_rtl:mk_imm(?EFT_ENV)) |
   gen_free_vars(Vars, HPVar, 0, WordSize, [])].

gen_free_vars([Var|Vars], EnvPVar, Offset, WordSize, AccCode) ->
  Code = hipe_rtl:mk_store(EnvPVar, hipe_rtl:mk_imm(Offset), Var),
  gen_free_vars(Vars, EnvPVar, Offset + WordSize, WordSize,
                [Code|AccCode]);
gen_free_vars([], _, _, _, AccCode) -> AccCode.

%% ------------------------------------------------------------------
%%
%% call_fun (also handles enter_fun when Continuation = [])

gen_call_fun(Dst, ArgsAndFun, Continuation, Fail) ->  
  NAddressReg = hipe_rtl:mk_new_reg(),
  ArityReg = hipe_rtl:mk_new_reg_gcsafe(),
  [Fun|RevArgs] = lists:reverse(ArgsAndFun),

  %% {BadFunLabName, BadFunCode} = gen_fail_code(Fail, {badfun, Fun}),
  Args = lists:reverse(RevArgs),
  NonClosureLabel = hipe_rtl:mk_new_label(),
  CallNonClosureLabel = hipe_rtl:mk_new_label(),
  BadFunLabName = hipe_rtl:label_name(NonClosureLabel),
  BadFunCode =
    [NonClosureLabel,
     hipe_rtl:mk_call([NAddressReg],
		      'nonclosure_address',
		      [Fun, hipe_rtl:mk_imm(length(Args))],
		      hipe_rtl:label_name(CallNonClosureLabel),
		      Fail,
		      not_remote),
     CallNonClosureLabel,
     case Continuation of
       [] ->
	 hipe_rtl:mk_enter(NAddressReg, Args, not_remote);
       _ ->
	 hipe_rtl:mk_call(Dst, NAddressReg, Args,
			  Continuation, Fail, not_remote)
     end],

  {BadArityLabName, BadArityCode} = gen_fail_code(Fail, {badarity, Fun}),

  CheckGetCode = 
    hipe_tagscheme:if_fun_get_arity_and_address(ArityReg, NAddressReg,
						Fun, BadFunLabName,
						0.9),
  CheckArityCode = check_arity(ArityReg, length(RevArgs), BadArityLabName),
  CallCode =
    case Continuation of
      [] -> %% This is a tailcall
	[hipe_rtl:mk_enter(NAddressReg, ArgsAndFun, not_remote)];
      _ -> %% Ordinary call
	[hipe_rtl:mk_call(Dst, NAddressReg, ArgsAndFun,
			  Continuation, Fail, not_remote)]
    end,
  [CheckGetCode, CheckArityCode, CallCode, BadFunCode, BadArityCode].

check_arity(ArityReg, Arity, BadArityLab) ->
  TrueLab1 = hipe_rtl:mk_new_label(),
  [hipe_rtl:mk_branch(ArityReg, eq, hipe_rtl:mk_imm(Arity),  
		      hipe_rtl:label_name(TrueLab1), BadArityLab, 0.9),
   TrueLab1].

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% apply
%%
%% The tail call case is not handled here.

gen_apply(Dst, Args=[_M,_F,_AppArgs], Cont, Fail) ->
  %% Dst can be [Res] or [].
  [hipe_rtl:mk_call(Dst, hipe_apply, Args, Cont, Fail, not_remote)].

gen_enter_apply(Args=[_M,_F,_AppArgs]) ->
  %% 'apply' in tail-call context
  [hipe_rtl:mk_enter(hipe_apply, Args, not_remote)].

%%
%% apply_N
%% also handles tailcall case (Cont=[])
%%

gen_apply_N(Dst, Arity, [M,F|CallArgs], Cont, Fail) ->
  CallLabel = hipe_rtl:mk_new_label(),
  CodeAddress = hipe_rtl:mk_new_reg(),
  [hipe_rtl:mk_call([CodeAddress], find_na_or_make_stub,
		    [M,F,hipe_rtl:mk_imm(hipe_tagscheme:mk_fixnum(Arity))],
		    hipe_rtl:label_name(CallLabel),
		    Fail, not_remote),
   CallLabel,
   case Cont of
     [] ->	% tailcall
       hipe_rtl:mk_enter(CodeAddress, CallArgs, not_remote);
     _ ->	% recursive call
       hipe_rtl:mk_call(Dst, CodeAddress, CallArgs, Cont, Fail, not_remote)
   end].


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% mkTuple
%%

gen_mk_tuple(Dst, Elements) ->
  {GetHPInsn, HP, PutHPInsn} = hipe_rtl_arch:heap_pointer(),
  Arity = length(Elements),
  WordSize = hipe_rtl_arch:word_size(),
  HeapNeed = (Arity+1)*WordSize,
  [GetHPInsn,
   gen_tuple_header(HP, Arity),
   set_tuple_elements(HP, WordSize, WordSize, Elements, []),
   hipe_tagscheme:tag_tuple(Dst, HP),
   hipe_rtl:mk_alu(HP, HP, add, hipe_rtl:mk_imm(HeapNeed)),
   PutHPInsn].

set_tuple_elements(HP, Offset, WordSize, [Element|Elements], Stores) ->
  Store = hipe_rtl:mk_store(HP, hipe_rtl:mk_imm(Offset), Element),
  set_tuple_elements(HP, Offset+WordSize, WordSize, Elements, [Store|Stores]);
set_tuple_elements(_, _, _, [], Stores) ->
  lists:reverse(Stores).

%%
%% @doc Generate RTL code for the reduction test.
%%
gen_redtest(Amount) ->
  {GetFCallsInsn, FCalls, PutFCallsInsn} = hipe_rtl_arch:fcalls(),
  SuspendLabel = hipe_rtl:mk_new_label(),
  StayLabel = hipe_rtl:mk_new_label(),
  ContinueLabel = hipe_rtl:mk_new_label(),
  [GetFCallsInsn,
   hipe_rtl:mk_alub(FCalls, FCalls, 'sub', hipe_rtl:mk_imm(Amount), 'lt',
		    hipe_rtl:label_name(SuspendLabel),
		    hipe_rtl:label_name(StayLabel), 0.01),
   SuspendLabel,
   %% The suspend path should not execute PutFCallsInsn.
   hipe_rtl:mk_call([], suspend_0, [],
		    hipe_rtl:label_name(ContinueLabel), [], not_remote),
   StayLabel,
   PutFCallsInsn,
   ContinueLabel].

gen_self(Dst, Cont) ->
  case Dst of
    [] -> %% The result is not used.
      [hipe_rtl:mk_goto(Cont)];
    [Dst1] ->
      [load_p_field(Dst1, ?P_ID),
       hipe_rtl:mk_goto(Cont)]
  end.

%%
%% @doc Generate is_tuple/1 test
%%
gen_is_tuple(Dst, [Arg], Cont) ->
  GotoCont = hipe_rtl:mk_goto(Cont),
  case Dst of
    [] -> %% The result is not used.
      [GotoCont];
    [Dst1] ->
      TrueLabel = hipe_rtl:mk_new_label(),
      FalseLabel = hipe_rtl:mk_new_label(),
      [hipe_tagscheme:test_tuple(Arg, hipe_rtl:label_name(TrueLabel),
				 hipe_rtl:label_name(FalseLabel), 0.5),
       TrueLabel,
       hipe_rtl:mk_load_atom(Dst1, true),
       GotoCont,
       FalseLabel,
       hipe_rtl:mk_load_atom(Dst1, false),
       GotoCont]
  end.

%%
%% @doc Generate unsafe head
%%
gen_unsafe_hd(Dst, [Arg]) -> hipe_tagscheme:unsafe_car(Dst, Arg).

%%
%% @doc Generate unsafe tail
%%
gen_unsafe_tl(Dst, [Arg]) -> hipe_tagscheme:unsafe_cdr(Dst, Arg).

%%
%% element
%%
gen_element(Dst, Args, IsGuard, Cont, Fail) ->
  Dst1 =
    case Dst of
      [] -> %% The result is not used.
	hipe_rtl:mk_new_var();
      [Dst0] -> Dst0
    end,
  [Index, Tuple] = Args,
  gen_element_1(Dst1, Index, Tuple, IsGuard, Cont, Fail, [], []).

gen_element_1(Dst, Index, Tuple, IsGuard, Cont, Fail, TupleInfo,
	      IndexInfo) ->
  {FailLblName, FailCode} = gen_fail_code(Fail, badarg, IsGuard),
  [hipe_tagscheme:element(Dst, Index, Tuple, FailLblName, TupleInfo, IndexInfo),
   hipe_rtl:mk_goto(Cont),
   FailCode].

%%
%% unsafe element
%%
gen_unsafe_element(Dst, Index, Tuple) ->
  case hipe_rtl:is_imm(Index) of
    true -> hipe_tagscheme:unsafe_constant_element(Dst, Index, Tuple);
    false -> ?EXIT({illegal_index_to_unsafe_element,Index})
  end.

gen_unsafe_update_element(Tuple, Index, Value) ->
  case hipe_rtl:is_imm(Index) of
    true -> 
      hipe_tagscheme:unsafe_update_element(Tuple, Index, Value);
    false ->
      ?EXIT({illegal_index_to_unsafe_update_element,Index})
  end.


gen_closure_element(Dst, Index, Closure) ->
  hipe_tagscheme:unsafe_closure_element(Dst, Index, Closure).

%%
%% @doc Generate RTL code that writes a tuple header.
%%
gen_tuple_header(Ptr, Arity) ->
  Header = hipe_tagscheme:mk_arityval(Arity),
  hipe_rtl:mk_store(Ptr, hipe_rtl:mk_imm(0), hipe_rtl:mk_imm(Header)).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%
%%% Receives

gen_check_get_msg(Dsts, GotoCont, Fail) ->
  case (?ERTS_IS_SMP + ?ERTS_IS_NOFRAG) of
    0 -> gen_check_get_msg_inline(Dsts, GotoCont, Fail);
    _ -> gen_check_get_msg_outofline(Dsts, GotoCont, Fail)
  end.

gen_clear_timeout([], GotoCont) ->
  case ?ERTS_IS_SMP of
    0 -> gen_clear_timeout_notsmp(GotoCont);
    1 -> gen_clear_timeout_smp(GotoCont)
  end.

%%% check_get_msg is:
%%%	if (!PEEK_MESSAGE(p)) goto Fail;
%%%	Dst = ERL_MESSAGE_TERM(PEEK_MESSAGE(p));
%%% i.e.,
%%%	ErlMessage **save = p->msg.save;
%%%	ErlMessage *msg = *save;
%%%	if (!msg) goto Fail;
%%%	Dst = msg->m[0];
gen_check_get_msg_inline(Dsts, GotoCont, Fail) ->
  Save = hipe_rtl:mk_new_reg(),
  Msg = hipe_rtl:mk_new_reg(),
  TrueLbl = hipe_rtl:mk_new_label(),
  [load_p_field(Save, ?P_MSG_SAVE),
   load_struct_field(Msg, Save, 0),
   hipe_rtl:mk_branch(Msg, eq, hipe_rtl:mk_imm(0), Fail,
		      hipe_rtl:label_name(TrueLbl), 0.1),
   TrueLbl |
   case Dsts of
     [Dst] ->
       [load_struct_field(Dst, Msg, ?MSG_MESSAGE),
	GotoCont];
     [] -> % receive which throws away the message
       [GotoCont]
   end].

%%% next_msg is:
%%%	SAVE_MESSAGE(p);
%%% i.e.,
%%%	ErlMessage **save = p->msg.save;
%%%	ErlMessage *msg = *save;
%%%	ErlMessage **next = &msg->next;
%%%	p->msg.save = next;
gen_next_msg([], GotoCont) ->
  Save = hipe_rtl:mk_new_reg(),
  Msg = hipe_rtl:mk_new_reg(),
  Next = hipe_rtl:mk_new_reg(),
  [load_p_field(Save, ?P_MSG_SAVE),
   load_struct_field(Msg, Save, 0),
   hipe_rtl:mk_alu(Next, Msg, 'add', hipe_rtl:mk_imm(?MSG_NEXT)),
   store_p_field(Next, ?P_MSG_SAVE),
   GotoCont].

%%% clear_timeout is:
%%%	p->flags &= ~F_TIMO; JOIN_MESSAGE(p);
%%% i.e.,
%%%	p->flags &= ~F_TIMO;
%%%	p->msg.save = &p->msg.first;
gen_clear_timeout_notsmp(GotoCont) ->
  Flags1 = hipe_rtl:mk_new_reg(),
  Flags2 = hipe_rtl:mk_new_reg_gcsafe(),
  First = hipe_rtl:mk_new_reg_gcsafe(),
  [load_p_field(Flags1, ?P_FLAGS),
   hipe_rtl:mk_alu(Flags2, Flags1, 'and', hipe_rtl:mk_imm(bnot(?F_TIMO))),
   store_p_field(Flags2, ?P_FLAGS),
   hipe_rtl_arch:pcb_address(First, ?P_MSG_FIRST),
   store_p_field(First, ?P_MSG_SAVE),
   GotoCont].

gen_check_get_msg_outofline(Dsts, GotoCont, Fail) ->
  RetLbl = hipe_rtl:mk_new_label(),
  TrueLbl = hipe_rtl:mk_new_label(),
  Tmp = hipe_rtl:mk_new_reg(),
  TheNonValue = hipe_rtl:mk_imm(hipe_tagscheme:mk_non_value()),
  [hipe_rtl_arch:call_bif([Tmp], check_get_msg, [],
			  hipe_rtl:label_name(RetLbl), []),
   RetLbl,
   hipe_rtl:mk_branch(Tmp, eq, TheNonValue, Fail,
		      hipe_rtl:label_name(TrueLbl), 0.1),
   TrueLbl |
   case Dsts of
     [Dst] ->
       [hipe_rtl:mk_move(Dst, Tmp),
	GotoCont];
     [] -> % receive which throws away the message
       [GotoCont]
   end].

gen_clear_timeout_smp(GotoCont) ->
  RetLbl = hipe_rtl:mk_new_label(),
  [hipe_rtl_arch:call_bif([], clear_timeout, [],
			  hipe_rtl:label_name(RetLbl), []),
   RetLbl,
   GotoCont].

gen_select_msg([], Cont) ->
  [hipe_rtl_arch:call_bif([], select_msg, [], Cont, [])].

gen_suspend_msg([], Cont) ->
  [hipe_rtl:mk_call([], suspend_msg, [], Cont, [], not_remote)].

%% --------------------------------------------------------------------
%%
%% Floating point handling 
%%

gen_fclearerror() ->
  case ?P_FP_EXCEPTION of
    [] ->
      [];
    Offset ->
      [hipe_rtl_arch:pcb_store(Offset, hipe_rtl:mk_imm(0), int32)]
  end.

gen_fcheckerror(ContLbl, FailLbl) ->
  Tmp = hipe_rtl:mk_new_reg(),
  TmpFailLbl0 = hipe_rtl:mk_new_label(),
  FailCode = fp_fail_code(TmpFailLbl0, FailLbl),
  hipe_rtl_arch:fwait() ++
    case ?P_FP_EXCEPTION of
      [] ->
	[];
      Offset ->
	[hipe_rtl_arch:pcb_load(Tmp, Offset, int32)]
    end ++
    [hipe_rtl:mk_branch(Tmp, eq, hipe_rtl:mk_imm(0), 
			ContLbl, hipe_rtl:label_name(TmpFailLbl0), 0.9)] ++
    FailCode.

gen_conv_to_float(Dst, [Src], ContLbl, FailLbl) ->
  case hipe_rtl:is_var(Src) of
    true ->
      Tmp = hipe_rtl:mk_new_var(),
      TmpReg = hipe_rtl:mk_new_reg_gcsafe(),
      TrueFixNum = hipe_rtl:mk_new_label(),
      ContFixNum = hipe_rtl:mk_new_label(),
      TrueFp = hipe_rtl:mk_new_label(),
      ContFp = hipe_rtl:mk_new_label(),
      ContBigNum = hipe_rtl:mk_new_label(),
      TestFixNum = hipe_tagscheme:test_fixnum(Src,
					      hipe_rtl:label_name(TrueFixNum),
					      hipe_rtl:label_name(ContFixNum),
					      0.5),
      TestFp = hipe_tagscheme:test_flonum(Src, hipe_rtl:label_name(TrueFp),
					  hipe_rtl:label_name(ContFp), 0.5),
      GotoCont = hipe_rtl:mk_goto(ContLbl),
      TmpFailLbl0 = hipe_rtl:mk_new_label(),
      FailCode = fp_fail_code(TmpFailLbl0, FailLbl),

      TestFixNum ++
	[TrueFixNum, 
    	 hipe_tagscheme:untag_fixnum(TmpReg, Src),
	 hipe_rtl:mk_fconv(Dst, TmpReg),
	 GotoCont,
	 ContFixNum] ++ 
	TestFp ++
	[TrueFp, 
	 hipe_tagscheme:unsafe_untag_float(Dst, Src), 
	 GotoCont, 
	 ContFp] ++
	[hipe_rtl:mk_call([Tmp], conv_big_to_float, [Src],
			  hipe_rtl:label_name(ContBigNum),
			  hipe_rtl:label_name(TmpFailLbl0), not_remote)]++
	FailCode ++
	[ContBigNum,
	 hipe_tagscheme:unsafe_untag_float(Dst, Tmp)];
    _ ->
      %% This must be an attempt to convert an illegal term.
      [gen_fail_code(FailLbl, badarith)]
  end.

