1%%% -*- erlang-indent-level: 2 -*-
2-module(hipe_llvm_merge).
3
4-export([finalize/3]).
5
6-include("hipe_llvm_arch.hrl").
7-include("../../kernel/src/hipe_ext_format.hrl").
8-include("../rtl/hipe_literals.hrl").
9-include("../main/hipe.hrl").
10
11finalize(CompiledCode, Closures, Exports) ->
12  CompiledCode1 = [CodePack || {_, CodePack} <- CompiledCode],
13  Code = [{MFA, [], ConstTab}
14	  || {MFA, _, _ , ConstTab, _, _} <- CompiledCode1],
15  {ConstAlign, ConstSize, ConstMap, RefsFromConsts} =
16    hipe_pack_constants:pack_constants(Code),
17  %% Compute total code size separately as a sanity check for alignment
18  CodeSize = compute_code_size(CompiledCode1, 0),
19  %% io:format("Code Size (pre-computed): ~w~n", [CodeSize]),
20  {CodeBinary, ExportMap} = merge_mfas(CompiledCode1, 0, <<>>, []),
21  %% io:format("Code Size (post-computed): ~w~n", [byte_size(CodeBinary)]),
22  ?VERBOSE_ASSERT(CodeSize =:= byte_size(CodeBinary)),
23  AccRefs = merge_refs(CompiledCode1, ConstMap, 0, []),
24  %% Bring CompiledCode to a combine_label_maps-acceptable form.
25  LabelMap = combine_label_maps(CompiledCode1, 0, gb_trees:empty()),
26  SC = hipe_pack_constants:slim_constmap(ConstMap),
27  DataRelocs = hipe_pack_constants:mk_data_relocs(RefsFromConsts, LabelMap),
28  SSE = hipe_pack_constants:slim_sorted_exportmap(ExportMap, Closures, Exports),
29  SlimRefs = hipe_pack_constants:slim_refs(AccRefs),
30  term_to_binary([{?VERSION_STRING(),?HIPE_ERTS_CHECKSUM},
31		  ConstAlign, ConstSize,
32		  SC,  % ConstMap
33		  DataRelocs, % LabelMap
34		  SSE, % ExportMap
35		  CodeSize, CodeBinary, SlimRefs,
36		  0,[] % ColdCodeSize, SlimColdRefs
37		 ]).
38
39%% Copied from hipe_x86_assemble.erl
40nr_pad_bytes(Address) ->
41  (4 - (Address rem 4)) rem 4. % XXX: 16 or 32 instead?
42
43align_entry(Address) ->
44  Address + nr_pad_bytes(Address).
45
46compute_code_size([{_MFA, _BinaryCode, CodeSize, _, _, _}|Code], Size) ->
47  compute_code_size(Code, align_entry(Size+CodeSize));
48compute_code_size([], Size) -> Size.
49
50combine_label_maps([{MFA, _, CodeSize, _, _, LabelMap}|Code], Address, CLM) ->
51  NewCLM = merge_label_map(gb_trees:to_list(LabelMap), MFA, Address, CLM),
52  combine_label_maps(Code, align_entry(Address+CodeSize), NewCLM);
53combine_label_maps([], _Address, CLM) -> CLM.
54
55merge_label_map([{Label,Offset}|Rest], MFA, Address, CLM) ->
56  NewCLM = gb_trees:insert({MFA,Label}, Address+Offset, CLM),
57  merge_label_map(Rest, MFA, Address, NewCLM);
58merge_label_map([], _MFA, _Address, CLM) -> CLM.
59
60%% @doc Merge the MFAs' binary code to one continuous binary and compute the
61%%      size of this binary. At the same time create an exportmap in a form
62%%      of {Address, M, F, A}.
63%% XXX: Is alignment correct/optimal for X86/AMD64?
64merge_mfas([{{M,F,A}, CodeBinary, CodeSize, _, _, _}|Code],
65           Address, AccCode, AccExportMap) ->
66  ?VERBOSE_ASSERT(CodeSize =:= byte_size(CodeBinary)),
67  {Address1, Code1} =
68    case nr_pad_bytes(Address + CodeSize) of
69      0 -> %% Retains alignment:
70        {Address + CodeSize, CodeBinary};
71      NrPadBytes -> %% Needs padding!
72        Padding = list_to_binary(lists:duplicate(NrPadBytes, 0)),
73        {Address + CodeSize + NrPadBytes, % =:= align_entry(Address+CodeSize)
74         <<CodeBinary/binary, Padding/binary>>}
75    end,
76  ?VERBOSE_ASSERT(Address1 =:=
77        align_entry(Address + CodeSize)), %XXX: Should address be aligned?
78  AccCode1 = <<AccCode/binary, Code1/binary>>,
79  merge_mfas(Code, Address1, AccCode1, [{Address, M, F, A}|AccExportMap]);
80merge_mfas([], _Address, AccCode, AccExportMap) ->
81  {AccCode, AccExportMap}.
82
83%% @doc Merge the references of relocatable symbols in the binary code. The
84%%      offsets must be updated because of the merging of the code binaries!
85merge_refs([], _ConstMap, _Addr, AccRefs) -> AccRefs;
86merge_refs([{MFA, _, CodeSize, _, Refs, _}|Rest], ConstMap, Address, AccRefs) ->
87  %% Important!: The hipe_pack_constants:pack_constants/2 function assignes
88  %% unique numbers to constants (ConstNo). This numbers are used from now on,
89  %% instead of labels that were used before. So, in order to be compatible, we
90  %% must change all the constant labels in the Refs to the corresponding
91  %% ConstNo, that can be found in the ConstMap (#pcm_entry{}).
92  UpdatedRefs = [update_ref(label_to_constno(Ref, MFA, ConstMap), Address)
93                 || Ref <- Refs],
94  merge_refs(Rest, ConstMap, align_entry(Address+CodeSize),
95             UpdatedRefs++AccRefs).
96
97label_to_constno({Type, Offset, {constant, Label}}, MFA, ConstMap) ->
98  ConstNo = hipe_pack_constants:find_const({MFA, Label}, ConstMap),
99  {Type, Offset, {constant, ConstNo}};
100label_to_constno(Other, _MFA, _ConstMap) ->
101  Other.
102
103%% @doc Update offset to a reference. In case of stack descriptors we must check
104%%      if there exists an exception handler, because it must also be updated.
105update_ref({?SDESC, Offset, SDesc}, CodeAddr) ->
106  NewRefAddr = Offset+CodeAddr,
107  case SDesc of
108    {[], _, _, _} -> % No handler; only update offset
109      {?SDESC, NewRefAddr, SDesc};
110    {ExnHandler, FrameSize, StackArity, Roots} -> % Update exception handler
111      {?SDESC, NewRefAddr, {ExnHandler+CodeAddr, FrameSize, StackArity, Roots}}
112  end;
113update_ref({Type, Offset, Term}, CodeAddr) ->
114  {Type, Offset+CodeAddr, Term}.
115