%% -*- erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
%% 
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% %CopyrightEnd%
%%
%% ====================================================================
%%  Module   :	hipe_rtl_inline_bs_ops
%%  Purpose  :  
%%  Notes    : 
%%  History  :	* 2001-06-14 Erik Johansson (happi@it.uu.se): Created.
%% ====================================================================
%%  Exports  :
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-module(hipe_rtl_binary_construct).
-export([gen_rtl/7]).
-import(hipe_tagscheme, [set_field_from_term/3,
			 get_field_from_term/3,
			 set_field_from_pointer/3,
			 get_field_from_pointer/3]).
%%-------------------------------------------------------------------------

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

-define(BYTE_SHIFT, hipe_rtl:mk_imm(3)). %% Turn bits into bytes or vice versa
-define(LOW_BITS, hipe_rtl:mk_imm(7)). %% Three lowest bits set
-define(LOW_BITS_INT, 7).
-define(BYTE_SIZE, 8).
-define(MAX_BINSIZE, ((1 bsl ((hipe_rtl_arch:word_size()*?BYTE_SIZE)-3)) - 1)).


%% -------------------------------------------------------------------------
%% The code is generated as a list of lists, it will be flattened later.
%% 

gen_rtl(BsOP, Dst, Args, TrueLblName, FalseLblName, SystemLimitLblName, ConstTab) ->
  %%io:format("~w, ~w, ~w~n", [BsOP, Args, Dst]),
  case BsOP of
    {bs_put_string, String, SizeInBytes} ->
      [NewOffset] = get_real(Dst),
      [Base, Offset] = Args,
      put_string(NewOffset, ConstTab, String, SizeInBytes, Base, Offset, 
		 TrueLblName);
    _ -> 
      Code = 
	case BsOP of
	  {bs_init, Size, _Flags} -> 
	    [] = Args,
	    [Dst0, Base, Offset] = Dst,
	    case is_illegal_const(Size bsl 3) of
	      true ->
		hipe_rtl:mk_goto(SystemLimitLblName);
	      false ->
		const_init2(Size, Dst0, Base, Offset, TrueLblName)
	    end;
	  
	  {bs_init, _Flags} -> 
	    [Size] = Args,
	    [Dst0, Base, Offset] = Dst,
	    var_init2(Size, Dst0, Base, Offset, TrueLblName, 
		      SystemLimitLblName, FalseLblName);

	  {bs_init_bits, Size, _Flags} -> 
	    [] = Args,
	    [Dst0, Base, Offset] = Dst,
	    case is_illegal_const(Size) of
	      true ->
		hipe_rtl:mk_goto(SystemLimitLblName);
	      false ->
		const_init_bits(Size, Dst0, Base, Offset, TrueLblName)
	    end;
	  
	  {bs_init_bits, _Flags} -> 
	    [Size] = Args,
	    [Dst0, Base, Offset] = Dst,
	    var_init_bits(Size, Dst0, Base, Offset, TrueLblName, 
			  SystemLimitLblName, FalseLblName);
	  
	  {bs_put_binary_all, _Flags} ->
	    [Src, Base, Offset] = Args,
	    [NewOffset] = get_real(Dst),
	    put_binary_all(NewOffset, Src, Base, Offset, TrueLblName, FalseLblName);
	   
	  {bs_put_binary, Size, _Flags} ->
	    case is_illegal_const(Size) of
	      true ->
		[hipe_rtl:mk_goto(FalseLblName)];
	      false ->
		[NewOffset] = get_real(Dst),
		case Args of
		  [Src, Base, Offset] ->
		    put_static_binary(NewOffset, Src, Size, Base, Offset, 
				      TrueLblName, FalseLblName);
		  [Src, Bits, Base, Offset]  ->
		    {SizeCode, SizeReg} = make_size(Size, Bits, FalseLblName),
		    InCode = put_dynamic_binary(NewOffset, Src, SizeReg, Base, 
						Offset, TrueLblName, FalseLblName),
		    SizeCode ++ InCode
		end
	    end;
		
	  {bs_put_float, Size, Flags, ConstInfo} ->    
	    [NewOffset] = get_real(Dst),
	    Aligned = aligned(Flags),
	    LittleEndian = littleendian(Flags),
	   case is_illegal_const(Size) of
	     true ->
	     	[hipe_rtl:mk_goto(FalseLblName)];
	     false ->
	       case Args of
		 [Src, Base, Offset] ->
		   CCode = static_float_c_code(NewOffset, Src, Base, Offset, Size, Flags, 
					       TrueLblName, FalseLblName),
		   put_float(NewOffset, Src, Base, Offset, Size, CCode, Aligned, 
			     LittleEndian, ConstInfo, TrueLblName);
		 [Src, Bits, Base, Offset] ->
		   {SizeCode, SizeReg} = make_size(Size, Bits, FalseLblName),
		   InCode = float_c_code(NewOffset, Src, Base, Offset, SizeReg, 
					 Flags, TrueLblName, FalseLblName),
		   SizeCode ++ InCode
	       end
	   end;

	  {bs_put_integer, Size, Flags, ConstInfo} -> 
	    Aligned = aligned(Flags), 
	    LittleEndian = littleendian(Flags),
	    [NewOffset] = get_real(Dst),
	    case is_illegal_const(Size) of
	      true ->
		[hipe_rtl:mk_goto(FalseLblName)];
	      false ->
		case ConstInfo of 
		  fail ->
		    [hipe_rtl:mk_goto(FalseLblName)];
		  _ -> 
		    case Args of 
		      [Src, Base, Offset] ->  
			CCode = static_int_c_code(NewOffset, Src,
						  Base, Offset, Size, 
						  Flags, TrueLblName, 
						  FalseLblName),
			put_static_int(NewOffset, Src, Base, Offset, Size, 
				       CCode, Aligned, LittleEndian, TrueLblName); 
		      [Src, Bits, Base, Offset] -> 
		    {SizeCode, SizeReg} = make_size(Size, Bits,
						    FalseLblName), 
			CCode = int_c_code(NewOffset, Src, Base,
					   Offset, SizeReg, Flags, 
					   TrueLblName, FalseLblName), 
			InCode =
			  put_dynamic_int(NewOffset, Src, Base, Offset, 
					  SizeReg, CCode, Aligned,
					  LittleEndian, TrueLblName), 
			SizeCode ++ InCode 
		    end 
		end 
	    end;
	  
	  {unsafe_bs_put_integer, 0, _Flags, _ConstInfo} -> 
	    [NewOffset] = get_real(Dst), 
	    case Args of
	      [_Src, _Base, Offset] ->
		[hipe_rtl:mk_move(NewOffset,Offset),
		 hipe_rtl:mk_goto(TrueLblName)]; 
	      [_Src, _Bits, _Base, Offset] -> 
		[hipe_rtl:mk_move(NewOffset,Offset),
		 hipe_rtl:mk_goto(TrueLblName)] 
	    end; 
	  
	  {unsafe_bs_put_integer, Size, Flags, ConstInfo} -> 
	     case is_illegal_const(Size) of
	      true ->
		[hipe_rtl:mk_goto(FalseLblName)];
	      false ->
		 Aligned = aligned(Flags),
		 LittleEndian = littleendian(Flags), 
		 [NewOffset] = get_real(Dst), 
		 case ConstInfo of 
		   fail ->
		     [hipe_rtl:mk_goto(FalseLblName)]; 
		   _ -> 
		     case Args of 
		       [Src, Base, Offset] -> 
			 CCode = static_int_c_code(NewOffset, Src,
						   Base, Offset, Size, 
						   Flags, TrueLblName, 
						   FalseLblName),
			 put_unsafe_static_int(NewOffset, Src, Base, 
					       Offset, Size,
					       CCode, Aligned, LittleEndian, 
					       TrueLblName); 
		       [Src, Bits, Base, Offset] -> 
			 {SizeCode, SizeReg} = make_size(Size, Bits,
							 FalseLblName), 
			 CCode = int_c_code(NewOffset, Src, Base,
					    Offset, SizeReg, Flags, 
					    TrueLblName, FalseLblName), 
			 InCode =
			   put_unsafe_dynamic_int(NewOffset, Src, Base, 
						  Offset, SizeReg, CCode, 
						  Aligned, LittleEndian, 
						  TrueLblName),
			 SizeCode ++ InCode 
		     end 
		 end
	     end; 
	  
	  bs_utf8_size ->
	    case Dst of
	      [_DstVar] ->
		[_Arg] = Args,
		[hipe_rtl:mk_call(Dst, bs_utf8_size, Args,
				  TrueLblName, [], not_remote)];
	      [] ->
		[hipe_rtl:mk_goto(TrueLblName)]
	    end;

	  bs_put_utf8 ->
	    [_Src, _Base, _Offset] = Args,
	    NewDsts = get_real(Dst),
	    [hipe_rtl:mk_call(NewDsts, bs_put_utf8, Args,
			      TrueLblName, FalseLblName, not_remote)];

	  bs_utf16_size ->
	    case Dst of
	      [_DstVar] ->
		[_Arg] = Args,
		[hipe_rtl:mk_call(Dst, bs_utf16_size, Args,
				  TrueLblName, [], not_remote)];
	      [] ->
		[hipe_rtl:mk_goto(TrueLblName)]
	    end;

	  {bs_put_utf16, Flags} ->
	    [_Src, _Base, _Offset] = Args,
	    NewDsts = get_real(Dst),
	    PrimOp =	% workaround for bif/primop arity restrictions
	      case littleendian(Flags) of
		false -> bs_put_utf16be;
		true -> bs_put_utf16le
	      end,
	    [hipe_rtl:mk_call(NewDsts, PrimOp, Args,
			      TrueLblName, FalseLblName, not_remote)];

	  bs_validate_unicode ->
	    [_Arg] = Args,
	    [hipe_rtl:mk_call([], bs_validate_unicode, Args,
			      TrueLblName, FalseLblName, not_remote)];

	  bs_final -> 
	    Zero = hipe_rtl:mk_imm(0),
	    [Src, Offset] = Args, 
	    [BitSize, ByteSize] = create_regs(2),
	    [ShortLbl, LongLbl] = create_lbls(2),
	    case Dst of 
	      [DstVar] -> 
		[hipe_rtl:mk_alub(BitSize, Offset, 'and', ?LOW_BITS, eq,
				  hipe_rtl:label_name(ShortLbl),
				  hipe_rtl:label_name(LongLbl)), ShortLbl,
		 hipe_rtl:mk_move(DstVar, Src),
		 hipe_rtl:mk_goto(TrueLblName),
		 LongLbl,
		 hipe_rtl:mk_alu(ByteSize, Offset, 'srl', ?BYTE_SHIFT),
		 hipe_tagscheme:mk_sub_binary(DstVar, ByteSize,
					      Zero, BitSize, Zero, Src),
		 hipe_rtl:mk_goto(TrueLblName)]; 
	      [] ->
		[hipe_rtl:mk_goto(TrueLblName)] 
	    end; 
	  
	  bs_init_writable ->
	    Zero = hipe_rtl:mk_imm(0),
	    [Size] = Args,
	    [DstVar] = Dst,
	    [SizeReg] = create_regs(1),
	    [Base] = create_unsafe_regs(1),
	    [hipe_rtl:mk_gctest(?PROC_BIN_WORDSIZE + ?SUB_BIN_WORDSIZE),
	     check_and_untag_fixnum(Size, SizeReg, FalseLblName),
	     allocate_writable(DstVar, Base, SizeReg, Zero, Zero),
	     hipe_rtl:mk_goto(TrueLblName)]; 
	  
	  {bs_private_append, _U, _F} ->
	    [Size, Bin] = Args, 
	    [DstVar, Base, Offset] = Dst,
	    [ProcBin] = create_vars(1),
	    [SubSize, SizeReg, EndSubSize, EndSubBitSize] = create_regs(4),
	    SubBinSize = {sub_binary, binsize},
	    [get_field_from_term({sub_binary, orig}, Bin, ProcBin),
	     get_field_from_term(SubBinSize, Bin, SubSize),
	     check_and_untag_fixnum(Size, SizeReg, FalseLblName),
	     realloc_binary(SizeReg, ProcBin, Base),
	     calculate_sizes(Bin, SizeReg, Offset, EndSubSize, EndSubBitSize),
	     set_field_from_term(SubBinSize, Bin, EndSubSize),
	     set_field_from_term({sub_binary, bitsize}, Bin, EndSubBitSize),
	     hipe_rtl:mk_move(DstVar, Bin),
	     hipe_rtl:mk_goto(TrueLblName)]; 

	  {bs_append, _U, _F, _B, _Bla} -> 
	    [Size, Bin] = Args, 
	    [DstVar, Base, Offset] = Dst, 
	    [ProcBin] = create_vars(1),
	    [Flags, SizeReg, IsWritable, EndSubSize, EndSubBitSize] = 
	      create_regs(5),
	    [ContLbl,ContLbl2,ContLbl3,WritableLbl,NotWritableLbl] = Lbls =
	      create_lbls(5),
	    [ContLblName, ContLbl2Name, ContLbl3Name, Writable, NotWritable] =
	      [hipe_rtl:label_name(Lbl) || Lbl <- Lbls],
	    Zero = hipe_rtl:mk_imm(0),
	    SubIsWritable = {sub_binary, is_writable},
	    [hipe_rtl:mk_gctest(?SUB_BIN_WORDSIZE + ?PROC_BIN_WORDSIZE),
	     check_and_untag_fixnum(Size, SizeReg, FalseLblName),
	     hipe_tagscheme:test_bitstr(Bin, ContLblName, FalseLblName, 0.99), 
	     ContLbl, 
	     hipe_tagscheme:test_subbinary(Bin,ContLbl2Name, NotWritable), 
	     ContLbl2,
	     get_field_from_term(SubIsWritable, Bin, IsWritable),
	     hipe_rtl:mk_branch(IsWritable, 'ne', Zero,
				ContLbl3Name, NotWritable),
	     ContLbl3,
	     get_field_from_term({sub_binary, orig}, Bin, ProcBin),
	     get_field_from_term({proc_bin, flags}, ProcBin, Flags),
	     hipe_rtl:mk_alub(Flags, Flags, 'and',
			      hipe_rtl:mk_imm(?PB_IS_WRITABLE), 
			      eq, NotWritable, Writable, 0.01),
	     WritableLbl,
	     set_field_from_term(SubIsWritable, Bin, Zero),
	     realloc_binary(SizeReg, ProcBin, Base),
	     calculate_sizes(Bin, SizeReg, Offset, EndSubSize, EndSubBitSize),
	     hipe_tagscheme:mk_sub_binary(DstVar, EndSubSize, Zero,
					  EndSubBitSize, Zero,
					  hipe_rtl:mk_imm(1), ProcBin),
	     hipe_rtl:mk_goto(TrueLblName),
	     NotWritableLbl,
	     not_writable_code(Bin, SizeReg, DstVar, Base, Offset, 
			       TrueLblName, FalseLblName)]
	end,
      {Code, ConstTab}
  end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%%  Code that is used in the append and init writeable functions
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

not_writable_code(Bin, SizeReg, Dst, Base, Offset, 
		  TrueLblName, FalseLblName) ->
  [SrcBase] = create_unsafe_regs(1),
  [SrcOffset, SrcSize, TotSize, TotBytes, UsedBytes] = create_regs(5),
  [IncLbl,AllLbl] = Lbls = create_lbls(2),
  [IncLblName,AllLblName] = get_label_names(Lbls),
  [get_base_offset_size(Bin, SrcBase, SrcOffset, SrcSize, FalseLblName),
   hipe_rtl:mk_alu(TotSize, SrcSize, add, SizeReg),
   hipe_rtl:mk_alu(TotBytes, TotSize, add, ?LOW_BITS),
   hipe_rtl:mk_alu(TotBytes, TotBytes, srl, ?BYTE_SHIFT),
   hipe_rtl:mk_alu(UsedBytes, TotBytes, sll, hipe_rtl:mk_imm(1)),
   hipe_rtl:mk_branch(UsedBytes, ge, hipe_rtl:mk_imm(256), 
		      AllLblName, IncLblName),
   IncLbl,
   hipe_rtl:mk_move(UsedBytes, hipe_rtl:mk_imm(256)),
   AllLbl,
   allocate_writable(Dst, Base, UsedBytes, TotBytes, TotSize),
   put_binary_all(Offset, Bin, Base, hipe_rtl:mk_imm(0), 
		  TrueLblName, FalseLblName)].
  
allocate_writable(Dst, Base, UsedBytes, TotBytes, TotSize) ->
  Zero = hipe_rtl:mk_imm(0),
  [NextLbl] = create_lbls(1),
  [EndSubSize, EndSubBitSize, ProcBin] = create_regs(3),
  [hipe_rtl:mk_call([Base], bs_allocate, [UsedBytes],
		    hipe_rtl:label_name(NextLbl), [], not_remote),
   NextLbl,
   hipe_tagscheme:create_refc_binary(Base, TotBytes, 
				     hipe_rtl:mk_imm(?PB_IS_WRITABLE bor
						     ?PB_ACTIVE_WRITER),
				     ProcBin),
   hipe_rtl:mk_alu(EndSubSize, TotSize, srl, ?BYTE_SHIFT),
   hipe_rtl:mk_alu(EndSubBitSize, TotSize, 'and', ?LOW_BITS),
   hipe_tagscheme:mk_sub_binary(Dst, EndSubSize, Zero, EndSubBitSize,
				Zero, hipe_rtl:mk_imm(1), ProcBin)].

check_and_untag_fixnum(Size, SizeReg, FalseLblName) ->
  [ContLbl,NextLbl] = Lbls = create_lbls(2),
  [ContLblName,NextLblName] = get_label_names(Lbls),
  [hipe_tagscheme:test_fixnum(Size, ContLblName, FalseLblName, 0.99),
   ContLbl,
   hipe_tagscheme:untag_fixnum(SizeReg,Size),
   hipe_rtl:mk_branch(SizeReg, ge, hipe_rtl:mk_imm(0), NextLblName, 
		      FalseLblName),
   NextLbl].

realloc_binary(SizeReg, ProcBin, Base) -> 
  [NoReallocLbl, ReallocLbl, NextLbl, ContLbl] = Lbls = create_lbls(4),
  [NoReallocLblName, ReallocLblName, NextLblName, ContLblName] =
    [hipe_rtl:label_name(Lbl) || Lbl <- Lbls],
  [PBSize, Tmp, ByteSize, NewSize, Flags, ResultingSize, OrigSize,
   BinPointer] = create_regs(8),
  ProcBinSizeTag = {proc_bin, binsize},
  ProcBinFlagsTag = {proc_bin, flags},
  ProcBinValTag = {proc_bin, val},
  ProcBinBytesTag = {proc_bin, bytes},
  BinOrigSizeTag = {binary, orig_size},
  [get_field_from_term(ProcBinSizeTag, ProcBin, PBSize),
   hipe_rtl:mk_alu(Tmp, SizeReg, 'add', ?LOW_BITS),
   hipe_rtl:mk_alu(ByteSize, Tmp, 'srl', ?BYTE_SHIFT),
   hipe_rtl:mk_alu(ResultingSize, ByteSize, 'add', PBSize),
   set_field_from_term(ProcBinSizeTag, ProcBin, ResultingSize),
   get_field_from_term(ProcBinFlagsTag, ProcBin, Flags),
   hipe_rtl:mk_alu(Flags, Flags, 'or', hipe_rtl:mk_imm(?PB_ACTIVE_WRITER)),
   set_field_from_term(ProcBinFlagsTag, ProcBin, Flags),
   get_field_from_term(ProcBinValTag, ProcBin, BinPointer),
   get_field_from_pointer(BinOrigSizeTag, BinPointer, OrigSize),
   hipe_rtl:mk_branch(OrigSize, 'lt', ResultingSize, 
		      ReallocLblName, NoReallocLblName),
   NoReallocLbl,
   get_field_from_term(ProcBinBytesTag, ProcBin, Base),
   hipe_rtl:mk_goto(ContLblName),
   ReallocLbl,
   hipe_rtl:mk_alu(NewSize, ResultingSize, 'sll', hipe_rtl:mk_imm(1)),
   hipe_rtl:mk_call([BinPointer], bs_reallocate, [BinPointer, NewSize], 
		    NextLblName, [], not_remote),
   NextLbl,
   set_field_from_pointer(BinOrigSizeTag, BinPointer, NewSize),
   set_field_from_term(ProcBinValTag, ProcBin, BinPointer),
   hipe_tagscheme:extract_binary_bytes(BinPointer, Base),
   set_field_from_term(ProcBinBytesTag, ProcBin, Base),
   ContLbl].

calculate_sizes(Bin, SizeReg, Offset, EndSubSize, EndSubBitSize) ->
  [SubSize, SubBitSize, EndSize] = create_regs(3),
  [get_field_from_term({sub_binary, binsize}, Bin, SubSize),
   get_field_from_term({sub_binary, bitsize}, Bin, SubBitSize),
   hipe_rtl:mk_alu(Offset, SubSize, 'sll', ?BYTE_SHIFT),
   hipe_rtl:mk_alu(Offset, Offset, 'add', SubBitSize),
   hipe_rtl:mk_alu(EndSize, Offset, 'add', SizeReg),
   hipe_rtl:mk_alu(EndSubSize, EndSize, srl, ?BYTE_SHIFT),
   hipe_rtl:mk_alu(EndSubBitSize, EndSize, 'and', ?LOW_BITS)].

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%%  Code that is used to create calls to beam functions
%%
%%  X_c_code/8, used for putting terms into binaries
%%
%%  X_get_c_code/10, used for getting terms from binaries
%%
%% - gen_test_sideffect_bs_call/4 is used to make a C-call that might
%%       fail but doesn't return an erlang value.
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

static_float_c_code(NewOffset, Src, Base, Offset, Size, Flags,
		    TrueLblName, FalseLblName) ->
  [SizeReg] = create_regs(1),
  [hipe_rtl:mk_move(SizeReg, hipe_rtl:mk_imm(Size))|
   float_c_code(NewOffset, Src, Base, Offset, SizeReg, Flags,
		TrueLblName, FalseLblName)].

float_c_code(NewOffset, Src, Base, Offset, SizeReg, Flags,
	     TrueLblName, FalseLblName) ->
  put_c_code(bs_put_small_float, NewOffset, Src, Base, Offset, SizeReg, 
	     Flags, TrueLblName, FalseLblName).

static_int_c_code(NewOffset, Src, Base, Offset, Size, Flags,
		  TrueLblName, FalseLblName) ->
  [SizeReg] = create_regs(1),
  [hipe_rtl:mk_move(SizeReg, hipe_rtl:mk_imm(Size))|
   int_c_code(NewOffset, Src, Base, Offset, SizeReg, Flags,
	      TrueLblName, FalseLblName)].

int_c_code(NewOffset, Src, Base, Offset, SizeReg, Flags,
	   TrueLblName, FalseLblName) ->
  put_c_code(bs_put_big_integer, NewOffset, Src, Base, Offset, SizeReg, 
	     Flags, TrueLblName, FalseLblName).

binary_c_code(NewOffset, Src, Base, Offset, Size, TrueLblName) ->
  PassedLbl = hipe_rtl:mk_new_label(),
  [SizeReg, FlagsReg] = create_regs(2),
  [hipe_rtl:mk_move(FlagsReg, hipe_rtl:mk_imm(0)),
   hipe_rtl:mk_move(SizeReg, Size),
   hipe_rtl:mk_call([], bs_put_bits, [Src, SizeReg, Base, Offset, FlagsReg], 
		    hipe_rtl:label_name(PassedLbl),[],not_remote),
   PassedLbl,
   hipe_rtl:mk_alu(NewOffset, Offset, add, SizeReg),
   hipe_rtl:mk_goto(TrueLblName)].

put_c_code(Func, NewOffset, Src, Base, Offset, SizeReg, Flags,
	   TrueLblName, FalseLblName) ->
  PassedLbl = hipe_rtl:mk_new_label(),
  [FlagsReg] = create_regs(1),
  [hipe_rtl:mk_move(FlagsReg, hipe_rtl:mk_imm(Flags)),
   gen_test_sideffect_bs_call(Func, [Src, SizeReg, Base, Offset, FlagsReg], 
			      hipe_rtl:label_name(PassedLbl), FalseLblName),
   PassedLbl,
   hipe_rtl:mk_alu(NewOffset, Offset, add, SizeReg),
   hipe_rtl:mk_goto(TrueLblName)].

gen_test_sideffect_bs_call(Name, Args, TrueLblName, FalseLblName) ->
  [Tmp1] = create_regs(1),
  RetLbl = hipe_rtl:mk_new_label(),
  [hipe_rtl:mk_call([Tmp1], Name, Args,
		    hipe_rtl:label_name(RetLbl), [], not_remote),
   RetLbl,
   hipe_rtl:mk_branch(Tmp1, eq, hipe_rtl:mk_imm(0), 
		      FalseLblName, TrueLblName, 0.01)].

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Small utility functions:
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

create_regs(X) when X > 0 ->
  [hipe_rtl:mk_new_reg_gcsafe()|create_regs(X-1)];
create_regs(0) ->
  [].

create_unsafe_regs(X) when X > 0 ->
  [hipe_rtl:mk_new_reg()|create_unsafe_regs(X-1)];
create_unsafe_regs(0) ->
  [].

create_vars(X) when X > 0 ->
  [hipe_rtl:mk_new_var()|create_vars(X-1)];
create_vars(0) -> 
  [].

create_lbls(X) when X > 0 ->
  [hipe_rtl:mk_new_label()|create_lbls(X-1)];
create_lbls(0) ->
  [].

get_label_names(Lbls) ->
  [hipe_rtl:label_name(Lbl) || Lbl <- Lbls].

aligned(Flags) ->
  case Flags band ?BSF_ALIGNED of
    1 -> true;
    0 -> false
  end.

littleendian(Flags) ->
  case Flags band 2 of
    2 -> true;
    0 -> false
  end.

is_illegal_const(Const) ->
  Const >= (1 bsl (hipe_rtl_arch:word_size() * ?BYTE_SIZE)) orelse Const < 0.

get_real(Dst) ->
  case Dst of
    [_NewOffset] -> Dst;
    [] -> create_regs(1)
  end.

%%-----------------------------------------------------------------------------
%% Help functions implementing the bs operations in rtl code.
%%
%% The following functions are called from the translation switch:
%%
%% - put_string/7 creates code to copy a string to a binary
%%       starting at base+offset and ending at base+newoffset   
%%
%% - const_init2/6 initializes the creation of a binary of constant size
%%
%% - var_init2/6 initializes the creation of a binary of variable size
%%
%% - get_int_from_unaligned_bin/11 creates code to extract a fixed
%%       size integer from a binary or makes a c-call if it does not
%%       conform to some certain rules.
%%
%% - get_unknown_size_int/11 creates code to extract a variable size
%%       byte-aligned integer from a binary or makes a c-call if it
%%       does not conform to some certain rules.
%%
%% - skip_no_of_bits/5 creates code to skip a variable amount of bits
%%       in a binary.
%%
%% - load_match_buffer/7 reloads the C-matchbuffer to RTL registers.
%%
%% - expand_runtime/4 creates code that calculates a maximal heap need
%%       before a binary match
%%-----------------------------------------------------------------------------

put_string(NewOffset, ConstTab, String, SizeInBytes, Base, Offset, TLName) ->
  [StringBase] = create_regs(1),
  {NewTab, Lbl} = hipe_consttab:insert_block(ConstTab, byte, String),
  {[hipe_rtl:mk_load_address(StringBase, Lbl, constant)|
    copy_string(StringBase, SizeInBytes, Base, Offset, 
		NewOffset, TLName)],
   NewTab}.
							  
const_init2(Size, Dst, Base, Offset, TrueLblName) ->
  Log2WordSize = hipe_rtl_arch:log2_word_size(),
  WordSize = hipe_rtl_arch:word_size(),
  NextLbl = hipe_rtl:mk_new_label(),
  case Size =< ?MAX_HEAP_BIN_SIZE of
    true ->
      [hipe_rtl:mk_gctest(((Size + 3*WordSize-1) bsr Log2WordSize)+?SUB_BIN_WORDSIZE),
       hipe_tagscheme:create_heap_binary(Base, Size, Dst),
       hipe_rtl:mk_move(Offset, hipe_rtl:mk_imm(0)),
       hipe_rtl:mk_goto(TrueLblName)];
    false ->
      ByteSize = hipe_rtl:mk_new_reg(),
      [hipe_rtl:mk_gctest(?PROC_BIN_WORDSIZE+?SUB_BIN_WORDSIZE),
       hipe_rtl:mk_move(Offset, hipe_rtl:mk_imm(0)),
       hipe_rtl:mk_move(ByteSize, hipe_rtl:mk_imm(Size)),
       hipe_rtl:mk_call([Base], bs_allocate, [ByteSize],
			hipe_rtl:label_name(NextLbl), [], not_remote),
       NextLbl,
       hipe_tagscheme:create_refc_binary(Base, ByteSize, Dst),
       hipe_rtl:mk_goto(TrueLblName)]
  end.

const_init_bits(Size, Dst, Base, Offset, TrueLblName) ->
  Log2WordSize = hipe_rtl_arch:log2_word_size(),
  WordSize = hipe_rtl_arch:word_size(),
  [NextLbl] = create_lbls(1),
  TmpDst = hipe_rtl:mk_new_var(),
  Zero = hipe_rtl:mk_imm(0),
  {ExtraSpace, SubBinCode} =
    if (Size rem ?BYTE_SIZE) =:= 0 -> 
	{0,[hipe_rtl:mk_move(Dst, TmpDst)]};
       true ->
	{?SUB_BIN_WORDSIZE,
	 hipe_tagscheme:mk_sub_binary(Dst, hipe_rtl:mk_imm(Size bsr 3), Zero, 
				      hipe_rtl:mk_imm(Size band ?LOW_BITS_INT),
				      Zero, TmpDst)}
    end,
  BaseBinCode =
    if Size =< (?MAX_HEAP_BIN_SIZE * 8) ->
	ByteSize  = (Size + 7) div 8,
	[hipe_rtl:mk_gctest(((ByteSize+ 3*WordSize-1) bsr Log2WordSize)+ ExtraSpace),
	 hipe_tagscheme:create_heap_binary(Base, ByteSize, TmpDst),
	 hipe_rtl:mk_move(Offset, Zero)];
       true ->
	ByteSize = hipe_rtl:mk_new_reg(),
	[hipe_rtl:mk_gctest(?PROC_BIN_WORDSIZE+ExtraSpace),
	 hipe_rtl:mk_move(Offset, Zero),
	 hipe_rtl:mk_move(ByteSize, hipe_rtl:mk_imm((Size+7) bsr 3)),
	 hipe_rtl:mk_call([Base], bs_allocate, [ByteSize],
			  hipe_rtl:label_name(NextLbl),[],not_remote),
	 NextLbl,
	 hipe_tagscheme:create_refc_binary(Base, ByteSize, TmpDst)]
    end,
  [BaseBinCode, SubBinCode, hipe_rtl:mk_goto(TrueLblName)].

var_init2(Size, Dst, Base, Offset, TrueLblName, SystemLimitLblName, FalseLblName) ->
  Log2WordSize = hipe_rtl_arch:log2_word_size(),
  WordSize = hipe_rtl_arch:word_size(),
  [ContLbl,HeapLbl,REFCLbl,NextLbl] = create_lbls(4),
  [USize,Tmp] = create_unsafe_regs(2),
  [get_32_bit_value(Size, USize, SystemLimitLblName, FalseLblName),
   hipe_rtl:mk_branch(USize, le, hipe_rtl:mk_imm(?MAX_BINSIZE), 
		      hipe_rtl:label_name(ContLbl), 
		      SystemLimitLblName),
   ContLbl,
   hipe_rtl:mk_move(Offset, hipe_rtl:mk_imm(0)),
   hipe_rtl:mk_branch(USize, le, hipe_rtl:mk_imm(?MAX_HEAP_BIN_SIZE), 
		      hipe_rtl:label_name(HeapLbl), 
		      hipe_rtl:label_name(REFCLbl)),
   HeapLbl,
   hipe_rtl:mk_alu(Tmp, USize, add, hipe_rtl:mk_imm(3*WordSize-1)),
   hipe_rtl:mk_alu(Tmp, Tmp, srl, hipe_rtl:mk_imm(Log2WordSize)),
   hipe_rtl:mk_alu(Tmp, Tmp, add, hipe_rtl:mk_imm(?SUB_BIN_WORDSIZE)),
   hipe_rtl:mk_gctest(Tmp),
   hipe_tagscheme:create_heap_binary(Base, USize, Dst),
   hipe_rtl:mk_goto(TrueLblName),
   REFCLbl,
   hipe_rtl:mk_gctest(?PROC_BIN_WORDSIZE+?SUB_BIN_WORDSIZE),
   hipe_rtl:mk_call([Base], bs_allocate, [USize],
		    hipe_rtl:label_name(NextLbl), [], not_remote),
   NextLbl,
   hipe_tagscheme:create_refc_binary(Base, USize, Dst),
   hipe_rtl:mk_goto(TrueLblName)].

var_init_bits(Size, Dst, Base, Offset, TrueLblName, SystemLimitLblName, FalseLblName) ->
  [HeapLbl,REFCLbl,NextLbl,NoSubLbl,SubLbl,ContLbl,
   NoCreateSubBin, CreateSubBin, JoinLbl, JoinLbl2] = create_lbls(10),
  [USize,ByteSize,TotByteSize,OffsetBits] = create_regs(4),
  [TmpDst] = create_unsafe_regs(1),
  Log2WordSize = hipe_rtl_arch:log2_word_size(),
  WordSize = hipe_rtl_arch:word_size(),
  MaximumWords = 
    erlang:max((?MAX_HEAP_BIN_SIZE + 3*WordSize) bsr Log2WordSize,
	       ?PROC_BIN_WORDSIZE) + ?SUB_BIN_WORDSIZE,
  Zero = hipe_rtl:mk_imm(0),
  [hipe_rtl:mk_gctest(MaximumWords),
   get_32_bit_value(Size, USize, SystemLimitLblName, FalseLblName),
   hipe_rtl:mk_alu(ByteSize, USize, srl, ?BYTE_SHIFT),
   hipe_rtl:mk_alub(OffsetBits, USize, 'and', ?LOW_BITS, eq, 
		    hipe_rtl:label_name(NoSubLbl),
		    hipe_rtl:label_name(SubLbl)),
   NoSubLbl,
   hipe_rtl:mk_move(TotByteSize, ByteSize),
   hipe_rtl:mk_goto(hipe_rtl:label_name(JoinLbl)),
   SubLbl,
   hipe_rtl:mk_alu(TotByteSize, ByteSize, 'add', hipe_rtl:mk_imm(1)),
   JoinLbl,
   hipe_rtl:mk_branch(USize, le, hipe_rtl:mk_imm(?MAX_BINSIZE), 
		      hipe_rtl:label_name(ContLbl), 
		      SystemLimitLblName),
   ContLbl,
   hipe_rtl:mk_branch(TotByteSize, 'le', hipe_rtl:mk_imm(?MAX_HEAP_BIN_SIZE),
		      hipe_rtl:label_name(HeapLbl), 
		      hipe_rtl:label_name(REFCLbl)),
   HeapLbl,
   hipe_tagscheme:create_heap_binary(Base, TotByteSize, TmpDst),
   hipe_rtl:mk_goto(hipe_rtl:label_name(JoinLbl2)),
   REFCLbl,
   hipe_rtl:mk_call([Base], bs_allocate, [TotByteSize],
		    hipe_rtl:label_name(NextLbl),[],not_remote),
   NextLbl,
   hipe_tagscheme:create_refc_binary(Base, TotByteSize, TmpDst),
   JoinLbl2,
   hipe_rtl:mk_move(Offset, Zero),
   hipe_rtl:mk_branch(OffsetBits, 'eq', Zero,
		      hipe_rtl:label_name(NoCreateSubBin), 
		      hipe_rtl:label_name(CreateSubBin)),
   CreateSubBin,
   hipe_tagscheme:mk_sub_binary(Dst, ByteSize, Zero, OffsetBits, Zero, TmpDst),
   hipe_rtl:mk_goto(TrueLblName),
   NoCreateSubBin,
   hipe_rtl:mk_move(Dst, TmpDst),
   hipe_rtl:mk_goto(TrueLblName)].

put_binary_all(NewOffset, Src, Base, Offset, TLName, FLName) -> 
  [SrcBase,SrcOffset,NumBits] = create_regs(3),
  CCode = binary_c_code(NewOffset, Src, Base, Offset, NumBits, TLName),
  AlignedCode = copy_aligned_bytes(SrcBase, SrcOffset, NumBits, Base, Offset, 
				   NewOffset, TLName),
  get_base_offset_size(Src, SrcBase, SrcOffset, NumBits,FLName) ++
    test_alignment(SrcOffset, NumBits, Offset, AlignedCode, CCode).

test_alignment(SrcOffset, NumBits, Offset, AlignedCode, CCode) ->
  [Tmp] = create_regs(1),  
  [AlignedLbl,CLbl] = create_lbls(2),
   [hipe_rtl:mk_alu(Tmp, SrcOffset, 'or', NumBits),
   hipe_rtl:mk_alu(Tmp, Tmp, 'or', Offset),
   hipe_rtl:mk_alub(Tmp, Tmp, 'and', ?LOW_BITS, 'eq', 
		    hipe_rtl:label_name(AlignedLbl),
		    hipe_rtl:label_name(CLbl)),
   AlignedLbl,
   AlignedCode,
   CLbl,
   CCode].
 
put_static_binary(NewOffset, Src, Size, Base, Offset, TLName, FLName) ->
  [SrcBase] = create_unsafe_regs(1), 
  [SrcOffset, SrcSize] = create_regs(2),
    case Size of
      0 ->
	get_base_offset_size(Src, SrcBase, SrcOffset, SrcSize, FLName) ++
	[hipe_rtl:mk_move(NewOffset, Offset), 
	 hipe_rtl:mk_goto(TLName)];
      _ ->
	SizeImm = hipe_rtl:mk_imm(Size),
	CCode = binary_c_code(NewOffset, Src, Base, Offset, SizeImm, TLName),
	AlignedCode = copy_aligned_bytes(SrcBase, SrcOffset, SizeImm, Base,
					 Offset, NewOffset, TLName),
	get_base_offset_size(Src, SrcBase, SrcOffset, SrcSize, FLName) ++
	  small_check(SizeImm, SrcSize, FLName) ++
	  test_alignment(SrcOffset, SizeImm, Offset, AlignedCode, CCode)
     end.

put_dynamic_binary(NewOffset, Src, SizeReg, Base, Offset, TLName, FLName) ->
  [SrcBase] = create_unsafe_regs(1), 
  [SrcOffset, SrcSize] = create_regs(2),
  CCode = binary_c_code(NewOffset, Src, Base, Offset, SizeReg, TLName),
  AlignedCode = copy_aligned_bytes(SrcBase, SrcOffset, SizeReg, Base, Offset, 
				   NewOffset, TLName),
  get_base_offset_size(Src, SrcBase, SrcOffset, SrcSize, FLName) ++
    small_check(SizeReg, SrcSize, FLName) ++
    test_alignment(SrcOffset, SizeReg, Offset, AlignedCode, CCode).

put_float(NewOffset, Src, Base, Offset, 64, CCode, Aligned, LittleEndian, 
	  ConstInfo, TrueLblName) ->
  [CLbl] = create_lbls(1),
  case {Aligned, LittleEndian} of
    {true, false} ->
      copy_float_big(Base, Offset, NewOffset, Src,
		     hipe_rtl:label_name(CLbl), TrueLblName, ConstInfo) ++
	[CLbl|CCode];
    {true, true} ->
      copy_float_little(Base, Offset, NewOffset, Src,
			hipe_rtl:label_name(CLbl), TrueLblName, ConstInfo) ++
	[CLbl|CCode];
    {false, _} ->
      CCode
  end;
put_float(_NewOffset, _Src, _Base, _Offset, _Size, CCode, _Aligned,
	  _LittleEndian, _ConstInfo, _TrueLblName) ->
  CCode.

put_static_int(NewOffset, Src, Base, Offset, Size, CCode, Aligned, 
	       LittleEndian, TrueLblName) ->
  {Init, End, UntaggedSrc} = make_init_end(Src, CCode, TrueLblName),
  case {Aligned, LittleEndian} of
    {true, true} ->
      Init ++
	copy_int_little(Base, Offset, NewOffset, Size, UntaggedSrc) ++
	End;
    {true, false} ->
      Init ++
	copy_int_big(Base, Offset, NewOffset, Size, UntaggedSrc) ++
	End;
    {false, true} ->
      CCode;
    {false, false} ->
       Init ++
	copy_offset_int_big(Base, Offset, NewOffset, Size, UntaggedSrc) ++
	End
  end.

put_unsafe_static_int(NewOffset, Src, Base, Offset, Size, CCode, Aligned, 
		      LittleEndian, TrueLblName) ->
  {Init, End, UntaggedSrc} = make_init_end(Src, TrueLblName),
  case {Aligned, LittleEndian} of
    {true, true} ->
      Init ++
	copy_int_little(Base, Offset, NewOffset, Size, UntaggedSrc) ++
	End;
    {true, false} ->
      Init ++
	copy_int_big(Base, Offset, NewOffset, Size, UntaggedSrc) ++
	End;
    {false, true} ->
      CCode;
    {false, false} ->
       Init ++
	copy_offset_int_big(Base, Offset, NewOffset, Size, UntaggedSrc) ++
	End
  end.

put_dynamic_int(NewOffset, Src, Base, Offset, SizeReg, CCode, Aligned, 
		LittleEndian, TrueLblName) ->
  {Init, End, UntaggedSrc} = make_init_end(Src, CCode, TrueLblName),
  case Aligned of
    true ->
      case LittleEndian of
	true ->
	   Init ++
	    copy_int_little(Base, Offset, NewOffset, SizeReg, UntaggedSrc) ++
	    End;
	false ->
	  Init ++
	    copy_int_big(Base, Offset, NewOffset, SizeReg, UntaggedSrc) ++
	    End
	end;
    false ->
      CCode
  end.

put_unsafe_dynamic_int(NewOffset, Src, Base, Offset, SizeReg, CCode, Aligned, 
		       LittleEndian, TrueLblName) ->
  {Init, End, UntaggedSrc} = make_init_end(Src, TrueLblName),
  case Aligned of
    true ->
      case LittleEndian of
	true ->
	   Init ++
	    copy_int_little(Base, Offset, NewOffset, SizeReg, UntaggedSrc) ++
	    End;
	false ->
	  Init ++
	    copy_int_big(Base, Offset, NewOffset, SizeReg, UntaggedSrc) ++
	    End
	end;
    false ->
      CCode
  end.
    

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Help functions used by the above
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

make_init_end(Src, CCode, TrueLblName) ->
  [CLbl, SuccessLbl] = create_lbls(2),
  [UntaggedSrc] = create_regs(1),
  Init = [hipe_tagscheme:test_fixnum(Src, hipe_rtl:label_name(SuccessLbl), 
				     hipe_rtl:label_name(CLbl), 0.99),
	  SuccessLbl,
	  hipe_tagscheme:untag_fixnum(UntaggedSrc,Src)],
  End = [hipe_rtl:mk_goto(TrueLblName), CLbl| CCode],
  {Init, End, UntaggedSrc}.

make_init_end(Src, TrueLblName) ->
  [UntaggedSrc] = create_regs(1),
  Init = [hipe_tagscheme:untag_fixnum(UntaggedSrc,Src)],
  End = [hipe_rtl:mk_goto(TrueLblName)],
  {Init, End, UntaggedSrc}.

get_base_offset_size(Binary, SrcBase, SrcOffset, SrcSize, FLName) -> 
  [JoinLbl, EndLbl, SuccessLbl, SubLbl, OtherLbl, HeapLbl, REFCLbl] =
    Lbls = create_lbls(7),
  [JoinLblName, EndLblName, SuccessLblName, SubLblName, 
   OtherLblName, HeapLblName, REFCLblName] = get_label_names(Lbls),
  [BitSize,BitOffset] = create_regs(2),
  [Orig] = create_vars(1),
  [hipe_tagscheme:test_bitstr(Binary, SuccessLblName, FLName, 0.99),
   SuccessLbl,
   get_field_from_term({sub_binary,binsize}, Binary, SrcSize),
   hipe_rtl:mk_alu(SrcSize, SrcSize, sll, ?BYTE_SHIFT),
   hipe_tagscheme:test_subbinary(Binary, SubLblName, OtherLblName),
   SubLbl,
   get_field_from_term({sub_binary,bitsize}, Binary, BitSize),
   get_field_from_term({sub_binary,offset}, Binary, SrcOffset),
   hipe_rtl:mk_alu(SrcSize, SrcSize, add, BitSize),
   get_field_from_term({sub_binary,bitoffset}, Binary, BitOffset),
   hipe_rtl:mk_alu(SrcOffset, SrcOffset, sll, ?BYTE_SHIFT),
   hipe_rtl:mk_alu(SrcOffset, SrcOffset, add, BitOffset),
   get_field_from_term({sub_binary,orig}, Binary, Orig),
   hipe_rtl:mk_goto(JoinLblName),
   OtherLbl, 
   hipe_rtl:mk_move(SrcOffset, hipe_rtl:mk_imm(0)),
   hipe_rtl:mk_move(Orig, Binary),
   JoinLbl,
   hipe_tagscheme:test_heap_binary(Orig, HeapLblName, REFCLblName),
   HeapLbl,
   hipe_rtl:mk_alu(SrcBase, Orig, add, hipe_rtl:mk_imm(?HEAP_BIN_DATA-2)),
   hipe_rtl:mk_goto(EndLblName),
   REFCLbl,
   get_field_from_term({proc_bin,bytes}, Orig, SrcBase),
   EndLbl].

copy_aligned_bytes(CopyBase, CopyOffset, Size, Base, Offset, NewOffset, TrueLblName) ->
  [BaseDst, BaseSrc] = create_unsafe_regs(2),
  [Iter, Extra, BothOffset] = create_regs(3),
  initializations(BaseSrc, BaseDst, BothOffset, CopyOffset, Offset, CopyBase, Base) ++
    [hipe_rtl:mk_alu(Extra, Size, 'and', ?LOW_BITS), 
     hipe_rtl:mk_alu(Iter, Size, srl, ?BYTE_SHIFT), 
     hipe_rtl:mk_alu(NewOffset, Offset, 'add', Size)] ++
    easy_loop(BaseSrc, BaseDst, BothOffset, Iter, Extra, TrueLblName).

copy_string(StringBase, StringSize, BinBase, BinOffset, NewOffset, TrueLblName) ->
  [TmpOffset,BothOffset,InitOffs] = create_regs(3),
  [NewBinBase] = create_unsafe_regs(1),
  [EasyLbl,HardLbl] = create_lbls(2), 
  [hipe_rtl:mk_alu(TmpOffset, BinOffset, srl, ?BYTE_SHIFT),
   hipe_rtl:mk_alu(NewBinBase, BinBase, add, TmpOffset),
   hipe_rtl:mk_move(BothOffset, hipe_rtl:mk_imm(0)),
   hipe_rtl:mk_alub(InitOffs, BinOffset, 'and', ?LOW_BITS, eq,
		    hipe_rtl:label_name(EasyLbl), hipe_rtl:label_name(HardLbl)),
   EasyLbl,
   hipe_rtl:mk_alu(NewOffset, BinOffset, add, 
		   hipe_rtl:mk_imm(?bytes_to_bits(StringSize)))] ++
   easy_loop(StringBase, NewBinBase, BothOffset,
	     hipe_rtl:mk_imm(StringSize), hipe_rtl:mk_imm(0), TrueLblName) ++
    [HardLbl,
     hipe_rtl:mk_alu(NewOffset, BinOffset, add,
		     hipe_rtl:mk_imm(?bytes_to_bits(StringSize)))] ++
    hard_loop(StringBase, NewBinBase, BothOffset, hipe_rtl:mk_imm(StringSize),
	      InitOffs, TrueLblName).

small_check(SizeVar, CopySize, FalseLblName) ->
  SuccessLbl = hipe_rtl:mk_new_label(),
  [hipe_rtl:mk_branch(SizeVar, le, CopySize,
		      hipe_rtl:label_name(SuccessLbl), FalseLblName),
   SuccessLbl].

easy_loop(BaseSrc, BaseDst, BothOffset, Iterations, Extra, TrueLblName) -> 
  [Tmp1,Shift] = create_regs(2),
  [LoopLbl,TopLbl,EndLbl,ExtraLbl] = create_lbls(4),
  [TopLbl,
   hipe_rtl:mk_branch(BothOffset, ne, Iterations, hipe_rtl:label_name(LoopLbl),
		      hipe_rtl:label_name(EndLbl), 0.99),
   LoopLbl,
   hipe_rtl:mk_load(Tmp1, BaseSrc, BothOffset, byte, unsigned),
   hipe_rtl:mk_store(BaseDst, BothOffset, Tmp1, byte),
   hipe_rtl:mk_alu(BothOffset, BothOffset, add, hipe_rtl:mk_imm(1)),
   hipe_rtl:mk_goto(hipe_rtl:label_name(TopLbl)),
   EndLbl,
   hipe_rtl:mk_branch(Extra, eq, hipe_rtl:mk_imm(0), TrueLblName,
		      hipe_rtl:label_name(ExtraLbl)),
   ExtraLbl,
   hipe_rtl:mk_load(Tmp1, BaseSrc, BothOffset, byte, unsigned),
   hipe_rtl:mk_alu(Shift, hipe_rtl:mk_imm(?BYTE_SIZE), sub, Extra),
   hipe_rtl:mk_alu(Tmp1, Tmp1, srl, Shift),
   hipe_rtl:mk_alu(Tmp1, Tmp1, sll, Shift),
   hipe_rtl:mk_store(BaseDst, BothOffset, Tmp1, byte),
   hipe_rtl:mk_goto(TrueLblName)].

hard_loop(BaseSrc, BaseDst, BothOffset, Iterations, 
	  InitOffset, TrueLblName) ->
  [Tmp1, Tmp2, OldByte, NewByte, SaveByte] = create_regs(5),
  [LoopLbl,EndLbl,TopLbl] = create_lbls(3),
  [hipe_rtl:mk_load(OldByte, BaseDst, BothOffset, byte, unsigned),
   hipe_rtl:mk_alu(Tmp1, hipe_rtl:mk_imm(?BYTE_SIZE), sub, InitOffset), 
   TopLbl,
   hipe_rtl:mk_branch(BothOffset, ne, Iterations, 
		      hipe_rtl:label_name(LoopLbl), 
		      hipe_rtl:label_name(EndLbl)),
   LoopLbl, 
   hipe_rtl:mk_load(NewByte, BaseSrc, BothOffset, byte, unsigned),
   hipe_rtl:mk_alu(Tmp2, NewByte, srl, InitOffset),
   hipe_rtl:mk_alu(SaveByte, OldByte, 'or', Tmp2),
   hipe_rtl:mk_store(BaseDst, BothOffset, SaveByte, byte),
   hipe_rtl:mk_alu(OldByte, NewByte, sll, Tmp1),
   hipe_rtl:mk_alu(BothOffset, BothOffset, 'add', hipe_rtl:mk_imm(1)),
   hipe_rtl:mk_goto(hipe_rtl:label_name(TopLbl)),
   EndLbl,
   hipe_rtl:mk_store(BaseDst, BothOffset, OldByte, byte),
   hipe_rtl:mk_goto(TrueLblName)].

initializations(BaseTmp1, BaseTmp2, BothOffset, CopyOffset, Offset, CopyBase, Base) ->
  [OffsetTmp1,OffsetTmp2] = create_regs(2),
  [hipe_rtl:mk_alu(OffsetTmp1, CopyOffset, srl, ?BYTE_SHIFT),
   hipe_rtl:mk_alu(OffsetTmp2, Offset, srl, ?BYTE_SHIFT),
   hipe_rtl:mk_alu(BaseTmp1, CopyBase, add, OffsetTmp1),
   hipe_rtl:mk_alu(BaseTmp2, Base, add, OffsetTmp2),
   hipe_rtl:mk_move(BothOffset, hipe_rtl:mk_imm(0))].

copy_int_little(Base, Offset, NewOffset, Size, Tmp1) when is_integer(Size) ->
  [Tmp2,TmpOffset] = create_regs(2),
  ByteSize = Size div ?BYTE_SIZE, 
    [hipe_rtl:mk_alu(TmpOffset, Offset, srl, ?BYTE_SHIFT),
     hipe_rtl:mk_alu(Tmp2, hipe_rtl:mk_imm(ByteSize), 'add', TmpOffset)] ++
    
    little_loop(Tmp1, Tmp2, TmpOffset, Base) ++
    
    case Size band 7 of
      0 ->
	[hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(Size))];
      Bits ->
	[hipe_rtl:mk_alu(Tmp1, Tmp1, sll, hipe_rtl:mk_imm(?BYTE_SIZE-Bits)),
	 hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte),
	 hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(Size))]
    end;
    
copy_int_little(Base, Offset, NewOffset, Size, Tmp1) ->
  [Tmp2, Tmp3, Tmp4, TmpOffset] = create_regs(4), 
  
  [hipe_rtl:mk_alu(Tmp2, Size, srl, ?BYTE_SHIFT),
   hipe_rtl:mk_alu(TmpOffset, Offset, srl, ?BYTE_SHIFT),
   hipe_rtl:mk_alu(Tmp3, Tmp2, 'add', TmpOffset)] ++
    
    little_loop(Tmp1, Tmp3, TmpOffset, Base) ++
  
    [hipe_rtl:mk_alu(Tmp4, Size, 'and', ?LOW_BITS),
     hipe_rtl:mk_alu(Tmp4, hipe_rtl:mk_imm(?BYTE_SIZE), 'sub', Tmp4), 
     hipe_rtl:mk_alu(Tmp1, Tmp1, sll, Tmp4),
     hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte),
     hipe_rtl:mk_alu(NewOffset, Offset, 'add', Size)].

little_loop(Tmp1, Tmp3, TmpOffset, Base) ->
  [BranchLbl, BodyLbl, EndLbl] = create_lbls(3),
  [BranchLbl,
   hipe_rtl:mk_branch(TmpOffset, 'ne', Tmp3,
		      hipe_rtl:label_name(BodyLbl),
		      hipe_rtl:label_name(EndLbl)),
   BodyLbl,
   hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte),
   hipe_rtl:mk_alu(Tmp1, Tmp1, 'sra', hipe_rtl:mk_imm(?BYTE_SIZE)),
   hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'add', hipe_rtl:mk_imm(1)),
   hipe_rtl:mk_goto(hipe_rtl:label_name(BranchLbl)),
   EndLbl].

big_loop(Tmp1, Tmp3, TmpOffset, Base) ->
  [BranchLbl, BodyLbl, EndLbl] = create_lbls(3),
  [BranchLbl,
   hipe_rtl:mk_branch(TmpOffset, 'ne', Tmp3,
		      hipe_rtl:label_name(BodyLbl),
		      hipe_rtl:label_name(EndLbl)),
   BodyLbl,
   hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'sub', hipe_rtl:mk_imm(1)),
   hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte),
   hipe_rtl:mk_alu(Tmp1, Tmp1, 'sra', hipe_rtl:mk_imm(?BYTE_SIZE)),
   hipe_rtl:mk_goto(hipe_rtl:label_name(BranchLbl)),
   EndLbl].

copy_int_big(_Base, Offset, NewOffset, 0, _Tmp1) ->
  [hipe_rtl:mk_move(NewOffset, Offset)];
copy_int_big(Base, Offset, NewOffset, ?BYTE_SIZE, Tmp1) ->
  TmpOffset = hipe_rtl:mk_new_reg(),
    [hipe_rtl:mk_alu(TmpOffset, Offset, 'srl', hipe_rtl:mk_imm(3)),
     hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte),
     hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(8))];
copy_int_big(Base, Offset, NewOffset, 2*?BYTE_SIZE, Tmp1) ->
  TmpOffset = hipe_rtl:mk_new_reg(),
    [hipe_rtl:mk_alu(TmpOffset, Offset, 'srl', hipe_rtl:mk_imm(3)),
     hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'add', hipe_rtl:mk_imm(1)),
     hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte),
     hipe_rtl:mk_alu(TmpOffset, TmpOffset, sub, hipe_rtl:mk_imm(1)),
     hipe_rtl:mk_alu(Tmp1, Tmp1, 'sra', hipe_rtl:mk_imm(8)),
     hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte),
     hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(16))];
copy_int_big(Base, Offset, NewOffset, 3*?BYTE_SIZE, Tmp1) ->
  TmpOffset = hipe_rtl:mk_new_reg(), 
    [hipe_rtl:mk_alu(TmpOffset, Offset, srl, hipe_rtl:mk_imm(3)),
     hipe_rtl:mk_alu(TmpOffset, TmpOffset, add, hipe_rtl:mk_imm(2)),
     hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte),
     hipe_rtl:mk_alu(TmpOffset, TmpOffset, sub, hipe_rtl:mk_imm(1)),
     hipe_rtl:mk_alu(Tmp1, Tmp1, sra, hipe_rtl:mk_imm(8)),
     hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte),
     hipe_rtl:mk_alu(TmpOffset, TmpOffset, sub, hipe_rtl:mk_imm(1)),
     hipe_rtl:mk_alu(Tmp1, Tmp1, sra, hipe_rtl:mk_imm(8)),
     hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte),
     hipe_rtl:mk_alu(NewOffset, Offset, add, hipe_rtl:mk_imm(24))];
copy_int_big(Base, Offset,NewOffset, 4*?BYTE_SIZE, Tmp1) ->
  copy_big_word(Base, Offset, NewOffset, Tmp1);
copy_int_big(Base, Offset, NewOffset, Size, Tmp1) when is_integer(Size) ->
  [OldOffset, TmpOffset, Bits] = create_regs(3),
  ByteSize = (Size + 7) div ?BYTE_SIZE,
  case Size band 7 of 
    0 -> 
      [hipe_rtl:mk_alu(OldOffset, Offset, sra, hipe_rtl:mk_imm(3)),
       hipe_rtl:mk_alu(TmpOffset, OldOffset, add, hipe_rtl:mk_imm(ByteSize))];
    Rest ->
      [hipe_rtl:mk_alu(OldOffset, Offset, sra, hipe_rtl:mk_imm(3)),
       hipe_rtl:mk_alu(TmpOffset, OldOffset, add, hipe_rtl:mk_imm(ByteSize-1)),
       hipe_rtl:mk_alu(Bits, Tmp1, sll, hipe_rtl:mk_imm(?BYTE_SIZE-Rest)),
       hipe_rtl:mk_store(Base, TmpOffset, Bits, byte),
       hipe_rtl:mk_alu(Tmp1, Tmp1, sra, hipe_rtl:mk_imm(Rest))]
  end ++
    big_loop(Tmp1, OldOffset, TmpOffset, Base) ++
    [hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(Size))];  
copy_int_big(Base, Offset, NewOffset, Size, Tmp1) ->
  Tmp2 = hipe_rtl:mk_new_reg(),
  Tmp3 = hipe_rtl:mk_new_reg(),
  Tmp4 = hipe_rtl:mk_new_reg(),
  Tmp5 = hipe_rtl:mk_new_reg(),
  Tmp6 = hipe_rtl:mk_new_reg(),
  TmpOffset = hipe_rtl:mk_new_reg(),
  EvenLbl = hipe_rtl:mk_new_label(),
  OddLbl = hipe_rtl:mk_new_label(),
  [hipe_rtl:mk_alu(Tmp2, Size, 'srl', hipe_rtl:mk_imm(3)),
   hipe_rtl:mk_alu(Tmp3, Offset, 'srl', hipe_rtl:mk_imm(3)),
   hipe_rtl:mk_alu(TmpOffset, Tmp2, 'add', Tmp3),
   hipe_rtl:mk_alub(Tmp4, Size, 'and', hipe_rtl:mk_imm(7), 'eq', 
		    hipe_rtl:label_name(EvenLbl), hipe_rtl:label_name(OddLbl)),
   OddLbl,
   hipe_rtl:mk_alu(Tmp6, hipe_rtl:mk_imm(8), 'sub', Tmp4),
   hipe_rtl:mk_alu(Tmp5, Tmp1, 'sll', Tmp6),
   hipe_rtl:mk_store(Base, TmpOffset, Tmp5, byte),
   EvenLbl,
   hipe_rtl:mk_alu(Tmp1, Tmp1, srl, Tmp4)] ++
    
    big_loop(Tmp1, Tmp3, TmpOffset, Base) ++
    
    [hipe_rtl:mk_alu(NewOffset, Offset, 'add', Size)].

copy_big_word(Base, Offset, NewOffset, Word) ->
  TmpOffset = hipe_rtl:mk_new_reg(),
  [hipe_rtl:mk_alu(TmpOffset, Offset, 'srl', hipe_rtl:mk_imm(3)),
   hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'add', hipe_rtl:mk_imm(3)),
   hipe_rtl:mk_store(Base, TmpOffset, Word, byte),
   hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'sub', hipe_rtl:mk_imm(1)),
   hipe_rtl:mk_alu(Word, Word, 'sra', hipe_rtl:mk_imm(8)),
   hipe_rtl:mk_store(Base, TmpOffset, Word, byte),
   hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'sub', hipe_rtl:mk_imm(1)),
   hipe_rtl:mk_alu(Word, Word, 'sra', hipe_rtl:mk_imm(8)),
   hipe_rtl:mk_store(Base, TmpOffset, Word, byte),
   hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'sub', hipe_rtl:mk_imm(1)),
   hipe_rtl:mk_alu(Word, Word, 'sra', hipe_rtl:mk_imm(8)),
   hipe_rtl:mk_store(Base, TmpOffset, Word, byte),
   hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(32))].

copy_little_word(Base, Offset, NewOffset, Word) ->
  TmpOffset = hipe_rtl:mk_new_reg(),
  [hipe_rtl:mk_alu(TmpOffset, Offset, 'srl', ?BYTE_SHIFT),
   hipe_rtl:mk_store(Base, TmpOffset, Word, byte),
   hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'add', hipe_rtl:mk_imm(1)),
   hipe_rtl:mk_alu(Word, Word, 'sra', hipe_rtl:mk_imm(?BYTE_SIZE)),
   hipe_rtl:mk_store(Base, TmpOffset, Word, byte),
   hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'add', hipe_rtl:mk_imm(1)),
   hipe_rtl:mk_alu(Word, Word, 'sra', hipe_rtl:mk_imm(?BYTE_SIZE)),
   hipe_rtl:mk_store(Base, TmpOffset, Word, byte),
   hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'add', hipe_rtl:mk_imm(1)),
   hipe_rtl:mk_alu(Word, Word, 'sra', hipe_rtl:mk_imm(?BYTE_SIZE)),
   hipe_rtl:mk_store(Base, TmpOffset, Word, byte),
   hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(32))].

copy_offset_int_big(Base, Offset, NewOffset, Size, Tmp1) when is_integer(Size) ->
  Tmp2 = hipe_rtl:mk_new_reg(),
  Tmp3 = hipe_rtl:mk_new_reg(),
  Tmp4 = hipe_rtl:mk_new_reg(),
  Tmp5 = hipe_rtl:mk_new_reg(),
  Tmp6 = hipe_rtl:mk_new_reg(),
  Tmp7 = hipe_rtl:mk_new_reg(),
  Tmp8 = hipe_rtl:mk_new_reg(),
  Tmp9 = hipe_rtl:mk_new_reg(),
  OldByte = hipe_rtl:mk_new_reg(),
  TmpOffset = hipe_rtl:mk_new_reg(),
  BranchLbl =  hipe_rtl:mk_new_label(),
  BodyLbl = hipe_rtl:mk_new_label(),
  EndLbl = hipe_rtl:mk_new_label(),
  NextLbl = hipe_rtl:mk_new_label(),
  WordSize = hipe_rtl_arch:word_size(),
  [hipe_rtl:mk_alu(Tmp2, Offset, 'and', ?LOW_BITS),
   hipe_rtl:mk_alu(Tmp3, Offset, srl, ?BYTE_SHIFT),
   hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(Size)),
   hipe_rtl:mk_alu(Tmp9, NewOffset, 'sub', hipe_rtl:mk_imm(1)),
   hipe_rtl:mk_alu(TmpOffset, Tmp9, srl, ?BYTE_SHIFT),
   hipe_rtl:mk_alu(Tmp4, NewOffset, 'and', ?LOW_BITS),
   hipe_rtl:mk_alu(Tmp6, hipe_rtl:mk_imm(?BYTE_SIZE), 'sub', Tmp4),
   hipe_rtl:mk_alu(Tmp6, Tmp6, 'and', ?LOW_BITS),
   hipe_rtl:mk_alu(Tmp4, hipe_rtl:mk_imm(?BYTE_SIZE), 'sub', Tmp6),
   hipe_rtl:mk_move(Tmp5, Tmp1),
   hipe_rtl:mk_alu(Tmp1, Tmp1, 'sll', Tmp6), 
   hipe_rtl:mk_branch(TmpOffset, 'ne', Tmp3, hipe_rtl:label_name(NextLbl), 
		      hipe_rtl:label_name(EndLbl)),
   NextLbl,
   hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte),
   hipe_rtl:mk_move(Tmp1, Tmp5),
   hipe_rtl:mk_alu(Tmp1, Tmp1, 'sra', Tmp4),
   hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'sub', hipe_rtl:mk_imm(1)),
   BranchLbl,
   hipe_rtl:mk_branch(TmpOffset, 'ne', Tmp3, hipe_rtl:label_name(BodyLbl),
		      hipe_rtl:label_name(EndLbl)),
   BodyLbl,
   hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte),
   hipe_rtl:mk_alu(Tmp1, Tmp1, 'sra', hipe_rtl:mk_imm(?BYTE_SIZE)),
   hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'sub', hipe_rtl:mk_imm(1)),
   hipe_rtl:mk_goto(hipe_rtl:label_name(BranchLbl)),
   EndLbl,
   hipe_rtl:mk_load(OldByte, Base, TmpOffset, byte, unsigned),
   hipe_rtl:mk_alu(Tmp8, hipe_rtl:mk_imm(?BYTE_SIZE), 'sub', Tmp2),
   hipe_rtl:mk_alu(OldByte, OldByte, 'srl', Tmp8),
   hipe_rtl:mk_alu(OldByte, OldByte, 'sll', Tmp8),
   hipe_rtl:mk_alu(Tmp7, Tmp2, 'add',
		   hipe_rtl:mk_imm(?bytes_to_bits(WordSize-1))),
   hipe_rtl:mk_alu(Tmp1, Tmp1, 'sll', Tmp7),
   hipe_rtl:mk_alu(Tmp1, Tmp1, 'srl', Tmp7),
   hipe_rtl:mk_alu(Tmp1, Tmp1, 'or', OldByte),
   hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte)].

copy_float_little(_Base, _Offset, _NewOffset, _Src, FalseLblName, _TrueLblName, fail) ->
  [hipe_rtl:mk_goto(FalseLblName)];
copy_float_little(Base, Offset, NewOffset, Src, _FalseLblName, TrueLblName, pass) ->
  FloatLo = hipe_rtl:mk_new_reg(),
  FloatHi = hipe_rtl:mk_new_reg(),
  TmpOffset = hipe_rtl:mk_new_reg(),
   hipe_tagscheme:unsafe_load_float(FloatLo, FloatHi, Src) ++
    copy_little_word(Base, Offset, TmpOffset, FloatLo) ++
    copy_little_word(Base, TmpOffset, NewOffset, FloatHi) ++
    [hipe_rtl:mk_goto(TrueLblName)];
copy_float_little(Base, Offset, NewOffset, Src, FalseLblName, TrueLblName, var) ->
  SuccessLbl = hipe_rtl:mk_new_label(),
  hipe_tagscheme:test_flonum(Src, hipe_rtl:label_name(SuccessLbl), FalseLblName, 0.99) ++
    [SuccessLbl|copy_float_little(Base, Offset, NewOffset, Src, FalseLblName, TrueLblName, pass)].

copy_float_big(_Base, _Offset, _NewOffset, _Src, FalseLblName, _TrueLblName, fail) ->
  [hipe_rtl:mk_goto(FalseLblName)];
copy_float_big(Base, Offset, NewOffset, Src, _FalseLblName, TrueLblName,pass) ->
  FloatLo = hipe_rtl:mk_new_reg(),
  FloatHi = hipe_rtl:mk_new_reg(),
  TmpOffset =hipe_rtl:mk_new_reg(),
  hipe_tagscheme:unsafe_load_float(FloatLo, FloatHi, Src) ++
    copy_big_word(Base, Offset, TmpOffset, FloatHi) ++
    copy_big_word(Base, TmpOffset, NewOffset, FloatLo) ++
    [hipe_rtl:mk_goto(TrueLblName)];
copy_float_big(Base, Offset, NewOffset, Src, FalseLblName, TrueLblName, var) ->
  SuccessLbl = hipe_rtl:mk_new_label(),
  hipe_tagscheme:test_flonum(Src, hipe_rtl:label_name(SuccessLbl), FalseLblName, 0.99) ++
    [SuccessLbl|copy_float_big(Base, Offset, NewOffset, Src, FalseLblName, TrueLblName, pass)].

make_size(1, BitsVar, FalseLblName) ->
  [DstReg] = create_regs(1),
  {first_part(BitsVar, DstReg, FalseLblName), DstReg};
make_size(?BYTE_SIZE, BitsVar, FalseLblName) ->
  [DstReg] = create_regs(1),
  Code = 
    first_part(BitsVar, DstReg, FalseLblName) ++
    [hipe_rtl:mk_alu(DstReg, DstReg, 'sll', ?BYTE_SHIFT)],
  {Code, DstReg};
make_size(UnitImm, BitsVar, FalseLblName) ->
  [DstReg] = create_regs(1),
  UnitList = number2list(UnitImm),
  Code = multiply_code(UnitList, BitsVar, DstReg, FalseLblName),
  {Code, DstReg}.

multiply_code(List=[Head|_Tail], Variable, Result, FalseLblName) ->
  Test = set_high(Head),
  Tmp1 = hipe_rtl:mk_new_reg(),
  SuccessLbl = hipe_rtl:mk_new_label(),
  Register = hipe_rtl:mk_new_reg(),
  Code = [hipe_rtl:mk_move(Result, hipe_rtl:mk_imm(0))|
	  first_part(Variable, Register, FalseLblName)]
    ++
    [hipe_rtl:mk_alub(Tmp1, Register, 'and', hipe_rtl:mk_imm(Test), 
		      'eq', hipe_rtl:label_name(SuccessLbl), 
		      FalseLblName, 0.99),
     SuccessLbl],
  multiply_code(List, Register, Result, FalseLblName, Tmp1, Code).

multiply_code([ShiftSize|Rest], Register, Result, FalseLblName, Tmp1, OldCode) ->
  SuccessLbl = hipe_rtl:mk_new_label(),
  Code = OldCode ++ [hipe_rtl:mk_alu(Tmp1, Register, 'sll',
				     hipe_rtl:mk_imm(ShiftSize)),
		     hipe_rtl:mk_alub(Result, Tmp1, 'add', Result, not_overflow, hipe_rtl:label_name(SuccessLbl), FalseLblName, 0.99),
		     SuccessLbl],
  multiply_code(Rest, Register, Result, FalseLblName, Tmp1, Code);
multiply_code([], _Register, _Result, _FalseLblName, _Tmp1, Code) ->
  Code.

number2list(X) when is_integer(X), X >= 0 ->
  number2list(X, []).

number2list(1, Acc) ->
  lists:reverse([0|Acc]);
number2list(0, Acc) ->
  lists:reverse(Acc);
number2list(X, Acc) ->
  F = floorlog2(X),
  number2list(X-(1 bsl F), [F|Acc]).

floorlog2(X) ->
  round(math:log(X)/math:log(2)-0.5). 

set_high(X) ->
  set_high(X, 0).

set_high(0, Y) ->
  Y;
set_high(X, Y) ->
  set_high(X-1, Y+(1 bsl (27-X))).

get_32_bit_value(Size, USize, SystemLimitLblName, NegLblName) ->
  Lbls = [FixLbl, BigLbl, OkLbl, PosBigLbl] = create_lbls(4),
  [FixLblName, BigLblName, OkLblName, PosBigLblName] = [hipe_rtl:label_name(Lbl) || Lbl <- Lbls],
  [hipe_tagscheme:test_fixnum(Size, FixLblName, BigLblName, 0.99),
   FixLbl,
   hipe_tagscheme:untag_fixnum(USize, Size),
   hipe_rtl:mk_branch(USize, ge, hipe_rtl:mk_imm(0), OkLblName, NegLblName), 
   BigLbl,
   hipe_tagscheme:test_pos_bignum(Size, PosBigLblName, NegLblName, 0.99),
   PosBigLbl,
   hipe_tagscheme:get_one_word_pos_bignum(USize, Size, SystemLimitLblName),
   OkLbl].  
   

first_part(Var, Register, FalseLblName) ->
  [SuccessLbl1, SuccessLbl2] = create_lbls(2),
  [hipe_tagscheme:test_fixnum(Var, hipe_rtl:label_name(SuccessLbl1),
			      FalseLblName, 0.99),
  SuccessLbl1,
  hipe_tagscheme:fixnum_ge(Var, hipe_rtl:mk_imm(hipe_tagscheme:mk_fixnum(0)), 
			   hipe_rtl:label_name(SuccessLbl2), FalseLblName, 0.99),
  SuccessLbl2,
  hipe_tagscheme:untag_fixnum(Register, Var)].


