1%% -*- erlang-indent-level: 2 -*-
2-module(hipe_llvm_main).
3
4-export([rtl_to_native/4]).
5
6-include("../../kernel/src/hipe_ext_format.hrl").
7-include("hipe_llvm_arch.hrl").
8-include("elf_format.hrl").
9
10%% @doc Translation of RTL to a loadable object. This function takes the RTL
11%%      code and calls hipe_rtl_to_llvm:translate/2 to translate the RTL code to
12%%      LLVM code. After this, LLVM asm is printed to a file and the LLVM tool
13%%      chain is invoked in order to produce an object file.
14rtl_to_native(MFA, RTL, Roots, Options) ->
15  %% Compile to LLVM and get Instruction List (along with infos)
16  {LLVMCode, RelocsDict0, ConstTab0} =
17    hipe_rtl_to_llvm:translate(RTL, Roots),
18  %% Fix function name to an acceptable LLVM identifier (needed for closures)
19  {_Module, Fun, Arity} = hipe_rtl_to_llvm:fix_mfa_name(MFA),
20  %% Write LLVM Assembly to intermediate file (on disk)
21  {ok, Dir, ObjectFile} =
22    compile_with_llvm(Fun, Arity, LLVMCode, Options, false),
23  %%
24  %% Extract information from object file
25  %%
26  ObjBin = open_object_file(ObjectFile),
27  Obj = elf_format:read(ObjBin),
28  %% Get labels info (for switches and jump tables)
29  Labels = elf_format:extract_rela(Obj, ?RODATA),
30  Tables = get_tables(Obj),
31  %% Associate Labels with Switches and Closures with stack args
32  {SwitchInfos, ExposedClosures} = correlate_labels(Tables, Labels),
33  %% SwitchInfos:     [{"table_50", [Labels]}]
34  %% ExposedClosures: [{"table_closures", [Labels]}]
35
36  %% Labelmap contains the offsets of the labels in the code that are
37  %% used for switch's jump tables
38  LabelMap = create_labelmap(MFA, SwitchInfos, RelocsDict0),
39  {RelocsDict, ConstTab} = extract_constants(RelocsDict0, ConstTab0, Obj),
40  %% Get relocation info
41  TextRelocs = elf_format:extract_rela(Obj, ?TEXT),
42  %% AccRefs contains the offsets of all references to relocatable symbols in
43  %% the code:
44  AccRefs = fix_relocations(TextRelocs, RelocsDict, MFA),
45  %% Get stack descriptors
46  SDescs = get_sdescs(Obj),
47  %% FixedSDescs are the stack descriptors after correcting calls that have
48  %% arguments in the stack
49  FixedSDescs =
50    fix_stack_descriptors(RelocsDict, AccRefs, SDescs, ExposedClosures),
51  Refs = AccRefs ++ FixedSDescs,
52  %% Get binary code from object file
53  BinCode = elf_format:extract_text(Obj),
54  %% Remove temp files (if needed)
55  ok = remove_temp_folder(Dir, Options),
56  %% Return the code together with information that will be used in the
57  %% hipe_llvm_merge module to produce the final binary that will be loaded
58  %% by the hipe unified loader.
59  {MFA, BinCode, byte_size(BinCode), ConstTab, Refs, LabelMap}.
60
61%%------------------------------------------------------------------------------
62%% LLVM tool chain
63%%------------------------------------------------------------------------------
64
65%% @doc Compile function FunName/Arity to LLVM. Return Dir (in order to remove
66%%      it if we do not want to store temporary files) and ObjectFile name that
67%%      is created by the LLVM tools.
68compile_with_llvm(FunName, Arity, LLVMCode, Options, UseBuffer) ->
69  Filename = atom_to_list(FunName) ++ "_" ++ integer_to_list(Arity),
70  %% Save temp files in a unique folder
71  Dir = unique_folder(FunName, Arity, Options),
72  ok = file:make_dir(Dir),
73  %% Print LLVM assembly to file
74  OpenOpts = [append, raw] ++
75    case UseBuffer of
76      %% true  -> [delayed_write]; % Use delayed_write!
77      false -> []
78    end,
79  {ok, File_llvm} = file:open(Dir ++ Filename ++ ".ll", OpenOpts),
80  Ver = hipe:get_llvm_version(), %% Should probably cache this
81  hipe_llvm:pp_ins_list(File_llvm, Ver, LLVMCode),
82  %% delayed_write can cause file:close not to do a close, hence the two calls
83  ok = file:close(File_llvm),
84  __ = file:close(File_llvm),
85  %% Invoke LLVM compiler tools to produce an object file
86  llvm_opt(Dir, Filename, Options),
87  llvm_llc(Dir, Filename, Ver, Options),
88  compile(Dir, Filename, "gcc"), %%FIXME: use llc -filetype=obj and skip this!
89  {ok, Dir, Dir ++ Filename ++ ".o"}.
90
91%% @doc Invoke opt tool to optimize the bitcode (_name.ll -> _name.bc).
92llvm_opt(Dir, Filename, Options) ->
93  Source   = Dir ++ Filename ++ ".ll",
94  Dest     = Dir ++ Filename ++ ".bc",
95  OptLevel = trans_optlev_flag(opt, Options),
96  OptFlags = [OptLevel, "-mem2reg", "-strip"],
97  Command  = "opt " ++ fix_opts(OptFlags) ++ " " ++ Source ++ " -o " ++ Dest,
98  %% io:format("OPT: ~s~n", [Command]),
99  case os:cmd(Command) of
100    "" -> ok;
101    Error -> exit({?MODULE, opt, Error})
102  end.
103
104%% @doc Invoke llc tool to compile the bitcode to object file
105%%      (_name.bc -> _name.o).
106llvm_llc(Dir, Filename, Ver, Options) ->
107  Source   = Dir ++ Filename ++ ".bc",
108  OptLevel = trans_optlev_flag(llc, Options),
109  VerFlags = llc_ver_flags(Ver),
110  Align    = find_stack_alignment(),
111  Target   = llc_target_opt(),
112  LlcFlags = [OptLevel, "-code-model=medium", "-stack-alignment=" ++ Align
113             , "-tailcallopt", "-filetype=asm" %FIXME
114             , Target
115             | VerFlags],
116  Command  = "llc " ++ fix_opts(LlcFlags) ++ " " ++ Source,
117  %% io:format("LLC: ~s~n", [Command]),
118  case os:cmd(Command) of
119    "" -> ok;
120    Error -> exit({?MODULE, llc, Error})
121  end.
122
123%% @doc Invoke the compiler tool ("gcc", "llvmc", etc.) to generate an object
124%%      file from native assembly.
125compile(Dir, Fun_Name, Compiler) ->
126  Source  = Dir ++ Fun_Name ++ ".s",
127  Dest    = Dir ++ Fun_Name ++ ".o",
128  Target  = compiler_target_opt(),
129  Command = Compiler ++ " " ++ Target ++ " -c " ++ Source ++ " -o " ++ Dest,
130  %% io:format("~s: ~s~n", [Compiler, Command]),
131  case os:cmd(Command) of
132    "" -> ok;
133    Error -> exit({?MODULE, cc, Error})
134  end.
135
136find_stack_alignment() ->
137  case get(hipe_target_arch) of
138    x86 -> "4";
139    amd64 -> "8";
140    _ -> exit({?MODULE, find_stack_alignment, "Unimplemented architecture"})
141  end.
142
143llc_target_opt() ->
144  case get(hipe_target_arch) of
145    x86 -> "-march=x86";
146    amd64 -> "-march=x86-64"
147  end.
148
149compiler_target_opt() ->
150  case get(hipe_target_arch) of
151    x86 -> "-m32";
152    amd64 -> "-m64"
153  end.
154
155%% @doc Join options.
156fix_opts(Opts) ->
157  lists:flatten(lists:join(" ", Opts)).
158
159%% @doc Translate optimization-level flag (default is "O2").
160trans_optlev_flag(Tool, Options) ->
161  Flag = case Tool of
162	   opt -> llvm_opt;
163	   llc -> llvm_llc
164         end,
165  case proplists:get_value(Flag, Options) of
166    o0 -> ""; % "-O0" does not exist in opt tool
167    o1 -> "-O1";
168    o2 -> "-O2";
169    o3 -> "-O3";
170    undefined -> "-O2"
171  end.
172
173llc_ver_flags(Ver = {_, _}) when Ver >= {3,9} ->
174  %% Works around a bug in the x86-call-frame-opt pass (as of LLVM 3.9) that
175  %% break the garbage collection stack descriptors.
176  ["-no-x86-call-frame-opt"];
177llc_ver_flags({_, _}) -> [].
178
179%%------------------------------------------------------------------------------
180%% Functions to manage Relocations
181%%------------------------------------------------------------------------------
182
183%% @doc Get switch table and closure table.
184-spec get_tables(elf_format:elf()) -> [elf_sym()].
185get_tables(Elf) ->
186  %% Search Symbol Table for entries where name is prefixed with "table_":
187  [S || S=#elf_sym{name="table_" ++ _} <- elf_format:elf_symbols(Elf)].
188
189%% @doc This function associates symbols who point to some table of labels with
190%%      the corresponding offsets of the labels in the code. These tables can
191%%      either be jump tables for switches or a table which contains the labels
192%%      of blocks that contain closure calls with more than ?NR_ARG_REGS.
193correlate_labels([], _L) -> {[], []};
194correlate_labels(Tables, Labels) ->
195  %% Assumes that the relocations are sorted
196  RelocTree = gb_trees:from_orddict(
197		[{Rel#elf_rel.offset, Rel#elf_rel.addend} || Rel <- Labels]),
198  %% Lookup all relocations pertaining to each symbol
199  NamesValues = [{Name, lookup_range(Value, Value+Size, RelocTree)}
200		 || #elf_sym{name=Name, value=Value, size=Size} <- Tables],
201  case lists:keytake("table_closures", 1, NamesValues) of
202    false ->  %% No closures in the code, no closure table
203      {NamesValues, []};
204    {value, ClosureTableNV, SwitchesNV} ->
205      {SwitchesNV, ClosureTableNV}
206  end.
207
208%% Fetches all values with a key in [Low, Hi)
209-spec lookup_range(_::K, _::K, gb_trees:tree(K,V)) -> [_::V].
210lookup_range(Low, Hi, Tree) ->
211  lookup_range_1(Hi, gb_trees:iterator_from(Low, Tree)).
212
213lookup_range_1(Hi, Iter0) ->
214  case gb_trees:next(Iter0) of
215    {Key, Value, Iter} when Key < Hi -> [Value | lookup_range_1(Hi, Iter)];
216    _ -> []
217  end.
218
219%% @doc Create a gb_tree which contains information about the labels that used
220%%      for switch's jump tables. The keys of the gb_tree are of the form
221%%      {MFA, Label} and the values are the actual Offsets.
222create_labelmap(MFA, SwitchInfos, RelocsDict) ->
223  create_labelmap(MFA, SwitchInfos, RelocsDict, gb_trees:empty()).
224
225create_labelmap(_, [], _, LabelMap) -> LabelMap;
226create_labelmap(MFA, [{Name, Offsets} | Rest], RelocsDict, LabelMap) ->
227  case dict:fetch(Name, RelocsDict) of
228    {switch, {_TableType, LabelList, _NrLabels, _SortOrder}, _JTabLab} ->
229      KVDict = lists:ukeysort(1, lists:zip(LabelList, Offsets)),
230      NewLabelMap = insert_to_labelmap(KVDict, LabelMap),
231      create_labelmap(MFA, Rest, RelocsDict, NewLabelMap);
232    _ ->
233      exit({?MODULE, create_labelmap, "Not a jump table!"})
234  end.
235
236%% @doc Insert a list of [{Key,Value}] to a LabelMap (gb_tree).
237insert_to_labelmap([], LabelMap) -> LabelMap;
238insert_to_labelmap([{Key, Value}|Rest], LabelMap) ->
239  case gb_trees:lookup(Key, LabelMap) of
240    none ->
241      insert_to_labelmap(Rest, gb_trees:insert(Key, Value, LabelMap));
242    {value, Value} -> %% Exists with the *exact* same Value.
243      insert_to_labelmap(Rest, LabelMap)
244  end.
245
246%% Find any LLVM-generated constants and add them to the constant table
247extract_constants(RelocsDict0, ConstTab0, Obj) ->
248  TextRelocs = elf_format:extract_rela(Obj, ?TEXT),
249  AnonConstSections =
250    lists:usort([{Sec, Offset}
251		 || #elf_rel{symbol=#elf_sym{type=section, section=Sec},
252			     addend=Offset} <- TextRelocs]),
253  lists:foldl(
254    fun({#elf_shdr{name=Name, type=progbits, addralign=Align, entsize=EntSize,
255		   size=Size} = Section, Offset}, {RelocsDict1, ConstTab1})
256	when EntSize > 0, 0 =:= Size rem EntSize, 0 =:= Offset rem EntSize ->
257	SectionBin = elf_format:section_contents(Section, Obj),
258	Constant = binary:part(SectionBin, Offset, EntSize),
259	{ConstTab, ConstLbl} =
260	  hipe_consttab:insert_binary_const(ConstTab1, Align, Constant),
261	{dict:store({anon, Name, Offset}, {constant, ConstLbl}, RelocsDict1),
262	 ConstTab}
263    end, {RelocsDict0, ConstTab0}, AnonConstSections).
264
265%% @doc Correlate object file relocation symbols with info from translation to
266%%      llvm code.
267fix_relocations(Relocs, RelocsDict, MFA) ->
268  lists:map(fun(Reloc) -> fix_reloc(Reloc, RelocsDict, MFA) end, Relocs).
269
270%% Relocation types and expected addends for x86 and amd64
271-define(PCREL_T, 'pc32').
272-define(PCREL_A, -4). %% Hard-coded in hipe_x86.c and hipe_amd64.c
273-ifdef(BIT32).
274-define(ABS_T, '32').
275-define(ABS_A, _). %% We support any addend
276-else.
277-define(ABS_T, '64').
278-define(ABS_A, 0).
279-endif.
280
281fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=undefined, type=notype},
282		   offset=Offset, type=?PCREL_T, addend=?PCREL_A},
283	  RelocsDict, {_,_,_}) when Name =/= "" ->
284  case dict:fetch(Name, RelocsDict) of
285    {call, _, {bif, BifName, _}} -> {?CALL_LOCAL, Offset, BifName};
286    {call, not_remote,  CallMFA} -> {?CALL_LOCAL,  Offset, CallMFA};
287    {call, remote, CallMFA} -> {?CALL_REMOTE, Offset, CallMFA}
288  end;
289fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=undefined, type=notype},
290		   offset=Offset, type=?ABS_T, addend=?ABS_A},
291	  RelocsDict, _) when Name =/= "" ->
292  case dict:fetch(Name, RelocsDict) of
293    {atom, AtomName}     -> {?LOAD_ATOM,    Offset, AtomName};
294    {constant, Label}    -> {?LOAD_ADDRESS, Offset, {constant, Label}};
295    {closure, _}=Closure -> {?LOAD_ADDRESS, Offset, Closure}
296  end;
297fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=#elf_shdr{name=?TEXT},
298				   type=func},
299		   offset=Offset, type=?PCREL_T, addend=?PCREL_A},
300	  RelocsDict, MFA) when Name =/= "" ->
301  case dict:fetch(Name, RelocsDict) of
302    {call, not_remote, MFA} -> {?CALL_LOCAL, Offset, MFA}
303  end;
304fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=#elf_shdr{name=?RODATA},
305				   type=object},
306		   offset=Offset, type=?ABS_T, addend=?ABS_A},
307	  RelocsDict, _) when Name =/= "" ->
308  case dict:fetch(Name, RelocsDict) of
309    {switch, _, JTabLab} -> %% Treat switch exactly as constant
310      {?LOAD_ADDRESS, Offset, {constant, JTabLab}}
311  end;
312fix_reloc(#elf_rel{symbol=#elf_sym{type=section, section=#elf_shdr{name=Name}},
313		   offset=Offset, type=?ABS_T, addend=Addend}, RelocsDict, _) ->
314  case dict:fetch({anon, Name, Addend}, RelocsDict) of
315    {constant, Label} -> {?LOAD_ADDRESS, Offset, {constant, Label}}
316  end.
317
318%%------------------------------------------------------------------------------
319%% Functions to manage Stack Descriptors
320%%------------------------------------------------------------------------------
321
322%% @doc This function takes an ELF Object File binary and returns a proper sdesc
323%%      list for Erlang/OTP System's loader. The return value should be of the
324%%      form:
325%%        {
326%%          4, Safepoint Address,
327%%          {ExnLabel OR [], FrameSize, StackArity, {Liveroot stack frame indexes}},
328%%        }
329get_sdescs(Elf) ->
330  case elf_format:extract_note(Elf, ?NOTE_ERLGC_NAME) of
331    <<>> -> % Object file has no ".note.gc" section!
332      [];
333    NoteGC_bin ->
334      %% Get safe point addresses (stored in ".rela.note.gc" section):
335      RelaNoteGC = elf_format:extract_rela(Elf, ?NOTE(?NOTE_ERLGC_NAME)),
336      SPCount = length(RelaNoteGC),
337      T = SPCount * ?SP_ADDR_SIZE,
338      %% Pattern match fields of ".note.gc":
339      <<SPCount:(?bits(?SP_COUNT_SIZE))/integer-little, % Sanity check!
340	_SPAddrs:T/binary, % NOTE: In 64bit they are relocs!
341        StkFrameSize:(?bits(?SP_STKFRAME_SIZE))/integer-little,
342        StkArity:(?bits(?SP_STKARITY_SIZE))/integer-little,
343        _LiveRootCount:(?bits(?SP_LIVEROOTCNT_SIZE))/integer-little, % Skip
344        Roots/binary>> = NoteGC_bin,
345      LiveRoots = get_liveroots(Roots, []),
346      %% Extract the safe point offsets:
347      SPOffs = [A || #elf_rel{addend=A} <- RelaNoteGC],
348      %% Extract Exception Handler labels:
349      ExnHandlers = elf_format:get_exn_handlers(Elf),
350      %% Combine ExnHandlers and Safe point addresses (return addresses):
351      ExnAndSPOffs = combine_ras_and_exns(ExnHandlers, SPOffs, []),
352      create_sdesc_list(ExnAndSPOffs, StkFrameSize, StkArity, LiveRoots, [])
353  end.
354
355%% @doc Extracts a bunch of integers (live roots) from a binary. Returns a tuple
356%%      as need for stack descriptors.
357get_liveroots(<<>>, Acc) ->
358  list_to_tuple(Acc);
359get_liveroots(<<Root:?bits(?LR_STKINDEX_SIZE)/integer-little,
360                MoreRoots/binary>>, Acc) ->
361  get_liveroots(MoreRoots, [Root | Acc]).
362
363combine_ras_and_exns(_, [], Acc) ->
364  lists:reverse(Acc);
365combine_ras_and_exns(ExnHandlers, [RA | MoreRAs], Acc) ->
366  %% FIXME: do something better than O(n^2) by taking advantage of the property
367  %% ||ExnHandlers|| <= ||RAs||
368  Handler = find_exn_handler(RA, ExnHandlers),
369  combine_ras_and_exns(ExnHandlers, MoreRAs, [{Handler, RA} | Acc]).
370
371find_exn_handler(_, []) ->
372  [];
373find_exn_handler(RA, [{Start, End, Handler} | MoreExnHandlers]) ->
374  case (RA >= Start andalso RA =< End) of
375    true ->
376      Handler;
377    false ->
378      find_exn_handler(RA, MoreExnHandlers)
379  end.
380
381create_sdesc_list([], _, _, _, Acc) ->
382  lists:reverse(Acc);
383create_sdesc_list([{ExnLbl, SPOff} | MoreExnAndSPOffs],
384		  StkFrameSize, StkArity, LiveRoots, Acc) ->
385  Hdlr = case ExnLbl of
386           0 -> [];
387           N -> N
388         end,
389  create_sdesc_list(MoreExnAndSPOffs, StkFrameSize, StkArity, LiveRoots,
390                    [{?SDESC, SPOff, {Hdlr, StkFrameSize, StkArity, LiveRoots}}
391                     | Acc]).
392
393%% @doc This function is responsible for correcting the stack descriptors of
394%%      the calls that are found in the code and have more than NR_ARG_REGS
395%%      (thus, some of their arguments are passed to the stack). Because of the
396%%      Reserved Call Frame feature that the LLVM uses, the stack descriptors
397%%      are not correct since at the point of call the frame size is reduced
398%%      by the number of arguments that are passed on the stack. Also, the
399%%      offsets of the roots need to be re-adjusted.
400fix_stack_descriptors(_, _, [], _) ->
401  [];
402fix_stack_descriptors(RelocsDict, Relocs, SDescs, ExposedClosures) ->
403  %% NamedCalls are MFA and BIF calls that need fix
404  NamedCalls       = calls_with_stack_args(RelocsDict),
405  NamedCallsOffs   = calls_offsets_arity(Relocs, NamedCalls),
406  ExposedClosures1 =
407    case dict:is_key("table_closures", RelocsDict) of
408      true -> %% A Table with closures exists
409        {table_closures, ArityList} = dict:fetch("table_closures", RelocsDict),
410        case ExposedClosures of
411          {_,  Offsets} ->
412            lists:zip(Offsets, ArityList);
413          _ ->
414            exit({?MODULE, fix_stack_descriptors,
415                 {"Wrong exposed closures", ExposedClosures}})
416        end;
417      false ->
418        []
419    end,
420  ClosuresOffs = closures_offsets_arity(ExposedClosures1, SDescs),
421  fix_sdescs(NamedCallsOffs ++ ClosuresOffs, SDescs).
422
423%% @doc This function takes as argument the relocation dictionary as produced by
424%%      the translation of RTL code to LLVM and finds the names of the calls
425%%      (MFA and BIF calls) that have more than NR_ARG_REGS.
426calls_with_stack_args(Dict) ->
427  calls_with_stack_args(dict:to_list(Dict), []).
428
429calls_with_stack_args([], Calls) -> Calls;
430calls_with_stack_args([ {_Name, {call, _, {M, F, A}}} | Rest], Calls)
431  when A > ?NR_ARG_REGS ->
432  Call =
433    case M of
434      bif -> {F,A};
435      _ -> {M,F,A}
436    end,
437  calls_with_stack_args(Rest, [Call|Calls]);
438calls_with_stack_args([_|Rest], Calls) ->
439  calls_with_stack_args(Rest, Calls).
440
441%% @doc This function extracts the stack arity and the offset in the code of
442%%      the named calls (MFAs, BIFs) that have stack arguments.
443calls_offsets_arity(AccRefs, CallsWithStackArgs) ->
444  calls_offsets_arity(AccRefs, CallsWithStackArgs, []).
445
446calls_offsets_arity([], _, Acc) -> Acc;
447calls_offsets_arity([{Type, Offset, Term} | Rest], CallsWithStackArgs, Acc)
448  when Type =:= ?CALL_REMOTE orelse Type =:= ?CALL_LOCAL ->
449  case lists:member(Term, CallsWithStackArgs) of
450    true ->
451      Arity =
452        case Term of
453          {_M, _F, A} -> A;
454          {_F, A} -> A
455        end,
456      calls_offsets_arity(Rest, CallsWithStackArgs,
457                          [{Offset + 4, Arity - ?NR_ARG_REGS} | Acc]);
458    false ->
459      calls_offsets_arity(Rest, CallsWithStackArgs, Acc)
460  end;
461calls_offsets_arity([_|Rest], CallsWithStackArgs, Acc) ->
462  calls_offsets_arity(Rest, CallsWithStackArgs, Acc).
463
464%% @doc This function extracts the stack arity and the offsets of closures that
465%%      have stack arity. The Closures argument represents the
466%%      hipe_bifs:llvm_exposure_closure/0 calls in the code. The actual closure
467%%      is the next call in the code, so the offset of the next call must be
468%%      calculated from the stack descriptors.
469closures_offsets_arity([], _) ->
470  [];
471closures_offsets_arity(ExposedClosures, SDescs) ->
472  Offsets = [Offset || {_, Offset, _} <- SDescs],
473  %% Offsets and closures must be sorted in order for find_offsets/3 to work
474  SortedOffsets = lists:sort(Offsets),
475  SortedExposedClosures = lists:keysort(1, ExposedClosures),
476  find_offsets(SortedExposedClosures, SortedOffsets, []).
477
478find_offsets([], _, Acc) -> Acc;
479find_offsets([{Off,Arity}|Rest], Offsets, Acc) ->
480  [I | RestOffsets] = lists:dropwhile(fun (Y) -> Y<Off end, Offsets),
481  find_offsets(Rest, RestOffsets, [{I, Arity}|Acc]).
482
483%% The function below corrects the stack descriptors of calls with arguments
484%% that are passed on the stack (more than NR_ARG_REGS) by subtracting the
485%% number of stacked arguments from the frame size and from the offset of the
486%% roots.
487fix_sdescs([], SDescs) -> SDescs;
488fix_sdescs([{Offset, Arity} | Rest], SDescs) ->
489  case lists:keyfind(Offset, 2, SDescs) of
490    false ->
491      fix_sdescs(Rest, SDescs);
492    {?SDESC, Offset, {ExnHandler, FrameSize, StkArity, Roots}} ->
493      FixedRoots = list_to_tuple([Ri - Arity || Ri <- tuple_to_list(Roots)]),
494      FixedSDesc =
495        {?SDESC, Offset, {ExnHandler, FrameSize - Arity, StkArity, FixedRoots}},
496      fix_sdescs(Rest, [FixedSDesc | lists:keydelete(Offset, 2, SDescs)])
497  end.
498
499%%------------------------------------------------------------------------------
500%% Miscellaneous functions
501%%------------------------------------------------------------------------------
502
503%% @doc A function that opens a file as binary. The function takes as argument
504%%      the name of the file and returns an Erlang binary.
505-spec open_object_file(string()) -> binary().
506open_object_file(ObjFile) ->
507  case file:read_file(ObjFile) of
508    {ok, Binary} ->
509      Binary;
510    {error, Reason} ->
511      exit({?MODULE, open_file, Reason})
512  end.
513
514remove_temp_folder(Dir, Options) ->
515  case proplists:get_bool(llvm_save_temps, Options) of
516    true -> ok;
517    false -> spawn(fun () -> "" = os:cmd("rm -rf " ++ Dir) end), ok
518  end.
519
520unique_id(FunName, Arity) ->
521  integer_to_list(erlang:phash2({FunName, Arity, erlang:unique_integer()})).
522
523unique_folder(FunName, Arity, Options) ->
524  DirName = "llvm_" ++ unique_id(FunName, Arity) ++ "/",
525  Dir =
526    case proplists:get_bool(llvm_save_temps, Options) of
527      true ->  %% Store folder in current directory
528        DirName;
529      false -> %% Temporarily store folder in tempfs (/dev/shm/)
530        "/tmp/" ++ DirName
531    end,
532  %% Make sure it does not exist
533  case dir_exists(Dir) of
534    true -> %% Dir already exists! Generate again.
535      unique_folder(FunName, Arity, Options);
536    false ->
537      Dir
538  end.
539
540%% @doc Function that checks that a given Filename is an existing Directory
541%%      Name (from http://rosettacode.org/wiki/Ensure_that_a_file_exists#Erlang)
542dir_exists(Filename) ->
543  {Flag, Info} = file:read_file_info(Filename),
544  (Flag =:= ok) andalso (element(3, Info) =:= directory).
545