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