diff options
Diffstat (limited to 'lib/hipe/llvm')
-rw-r--r-- | lib/hipe/llvm/Makefile | 21 | ||||
-rw-r--r-- | lib/hipe/llvm/elf_format.erl | 654 | ||||
-rw-r--r-- | lib/hipe/llvm/elf_format.hrl | 40 | ||||
-rw-r--r-- | lib/hipe/llvm/hipe_llvm.erl | 98 | ||||
-rw-r--r-- | lib/hipe/llvm/hipe_llvm_main.erl | 203 | ||||
-rw-r--r-- | lib/hipe/llvm/hipe_rtl_to_llvm.erl | 148 |
6 files changed, 560 insertions, 604 deletions
diff --git a/lib/hipe/llvm/Makefile b/lib/hipe/llvm/Makefile index f6f6f6be1c..88016a7d8b 100644 --- a/lib/hipe/llvm/Makefile +++ b/lib/hipe/llvm/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2014. All Rights Reserved. +# Copyright Ericsson AB 2001-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -40,20 +40,19 @@ RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN) # Target Specs # ---------------------------------------------------- ifdef HIPE_ENABLED -HIPE_MODULES = hipe_rtl_to_llvm \ +HIPE_MODULES = elf_format \ hipe_llvm \ - elf_format \ + hipe_llvm_liveness \ hipe_llvm_main \ hipe_llvm_merge \ - hipe_llvm_liveness + hipe_rtl_to_llvm else HIPE_MODULES = endif MODULES = $(HIPE_MODULES) -HRL_FILES= elf_format.hrl elf32_format.hrl elf64_format.hrl \ - hipe_llvm_arch.hrl +HRL_FILES= elf_format.hrl elf32_format.hrl elf64_format.hrl hipe_llvm_arch.hrl ERL_FILES= $(MODULES:%=%.erl) TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) @@ -71,7 +70,7 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) include ../native.mk -ERL_COMPILE_FLAGS += +inline #+warn_missing_spec +ERL_COMPILE_FLAGS += -Werror +inline +warn_export_vars #+warn_missing_spec # if in 32 bit backend define BIT32 symbol ARCH = $(shell echo $(TARGET) | sed 's/^\(x86_64\)-.*/64bit/') @@ -108,3 +107,11 @@ release_spec: opt $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin release_docs_spec: + +$(EBIN)/elf_format.beam: elf_format.hrl elf32_format.hrl elf64_format.hrl +$(EBIN)/hipe_llvm_main.beam: ../../kernel/src/hipe_ext_format.hrl \ + hipe_llvm_arch.hrl elf_format.hrl elf32_format.hrl elf64_format.hrl +$(EBIN)/hipe_llvm_merge.beam: ../../kernel/src/hipe_ext_format.hrl \ + hipe_llvm_arch.hrl ../rtl/hipe_literals.hrl ../main/hipe.hrl +$(EBIN)/hipe_rtl_to_llvm.beam: ../rtl/hipe_rtl.hrl ../rtl/hipe_literals.hrl \ + hipe_llvm_arch.hrl diff --git a/lib/hipe/llvm/elf_format.erl b/lib/hipe/llvm/elf_format.erl index 260da9b5e6..8cf6ea6250 100644 --- a/lib/hipe/llvm/elf_format.erl +++ b/lib/hipe/llvm/elf_format.erl @@ -13,21 +13,20 @@ -module(elf_format). --export([get_tab_entries/1, - %% Relocations - get_rodata_relocs/1, - get_text_relocs/1, +-export([%% Relocations extract_rela/2, - get_rela_addends/1, %% Note extract_note/2, %% Executable code extract_text/1, %% GCC Exception Table - get_exn_handlers/1, - %% Misc. - set_architecture_flag/1, - is64bit/0 + get_exn_handlers/1, + %% Symbols + elf_symbols/1, + %% Sections + section_contents/2, + %% Main interface + read/1 ]). -include("elf_format.hrl"). @@ -36,27 +35,57 @@ %% Types %%------------------------------------------------------------------------------ --type elf() :: binary(). - --type lp() :: non_neg_integer(). % landing pad --type num() :: non_neg_integer(). --type index() :: non_neg_integer(). --type offset() :: non_neg_integer(). --type size() :: non_neg_integer(). --type start() :: non_neg_integer(). - --type info() :: index(). --type nameoff() :: offset(). --type valueoff() :: offset(). - --type name() :: string(). --type name_size() :: {name(), size()}. --type name_sizes() :: [name_size()]. +-export_type([elf/0 + ,addend/0 + ,bitflags/0 + ,name/0 + ,offset/0 + ,reloc_type/0 + ,shdr_type/0 + ,size/0 + ,sym_bind/0 + ,sym_type/0 + ,valueoff/0 + ]). + +-type bitflags() :: non_neg_integer(). +-type index() :: non_neg_integer(). +-type lp() :: non_neg_integer(). % landing pad +-type num() :: non_neg_integer(). +-type offset() :: non_neg_integer(). +-type size() :: non_neg_integer(). +-type start() :: non_neg_integer(). + +-type addend() :: integer() | undefined. +-type name() :: string(). +-type shdr_type() :: 'null' | 'progbits' | 'symtab' | 'strtab' | 'rela' + | 'hash' | 'dynamic' | 'note' | 'nobits' | 'rel' | 'shlib' + | 'dynsym' | {os, ?SHT_LOOS..?SHT_HIOS} + | {proc, ?SHT_LOPROC..?SHT_HIPROC}. +-type sym_bind() :: 'local' | 'global' | 'weak' | {os, ?STB_LOOS..?STB_HIOS} + | {proc, ?STB_LOPROC..?STB_HIPROC}. +-type sym_type() :: 'notype' | 'object' | 'func' | 'section' | 'file' + | {os, ?STT_LOOS..?STT_HIOS} + | {proc, ?STT_LOPROC..?STT_HIPROC}. +-type valueoff() :: offset(). + +-ifdef(BIT32). % 386 +-type reloc_type() :: '32' | 'pc32'. +-else. % X86_64 +-type reloc_type() :: '64' | 'pc32' | '32'. +-endif. %%------------------------------------------------------------------------------ %% Abstract Data Types and Accessors for ELF Structures. %%------------------------------------------------------------------------------ +-record(elf, {file :: binary() + ,sections :: [elf_shdr()] + ,sec_nam :: #{string() => elf_shdr()} + ,symbols :: undefined | [elf_sym()] + }). +-opaque elf() :: #elf{}. + %% File header -record(elf_ehdr, {ident, % ELF identification type, % Object file type @@ -85,42 +114,6 @@ }). %% -type elf_ehdr_ident() :: #elf_ehdr_ident{}. -%% Section header entries --record(elf_shdr, {name, % Section name - type, % Section type - flags, % Section attributes - addr, % Virtual address in memory - offset :: offset(), % Offset in file - size :: size(), % Size of section - link, % Link to other section - info, % Miscellaneous information - addralign, % Address align boundary - entsize % Size of entries, if section has table - }). -%% -type elf_shdr() :: #elf_shdr{}. - -%% Symbol table entries --record(elf_sym, {name :: nameoff(), % Symbol name - info, % Type and Binding attributes - other, % Reserved - shndx, % Section table index - value :: valueoff(), % Symbol value - size :: size() % Size of object - }). --type elf_sym() :: #elf_sym{}. - -%% Relocations --record(elf_rel, {r_offset :: offset(), % Address of reference - r_info :: info() % Symbol index and type of relocation - }). --type elf_rel() :: #elf_rel{}. - --record(elf_rela, {r_offset :: offset(), % Address of reference - r_info :: info(), % Symbol index and type of relocation - r_addend :: offset() % Constant part of expression - }). --type elf_rela() :: #elf_rela{}. - %% %% Program header table %% -record(elf_phdr, {type, % Type of segment %% flags, % Segment attributes @@ -199,44 +192,19 @@ mk_shdr(Name, Type, Flags, Addr, Offset, Size, Link, Info, AddrAlign, EntSize) - %%%------------------------- %%% Symbol Table Entries %%%------------------------- -mk_sym(Name, Info, Other, Shndx, Value, Size) -> - #elf_sym{name = Name, info = Info, other = Other, - shndx = Shndx, value = Value, size = Size}. - --spec sym_name(elf_sym()) -> nameoff(). -sym_name(#elf_sym{name = Name}) -> Name. +mk_sym(Name, Bind, Type, Section, Value, Size) -> + #elf_sym{name = Name, bind = Bind, type = Type, + section = Section, value = Value, size = Size}. +%% -spec sym_name(elf_sym()) -> string(). +%% sym_name(#elf_sym{name = Name}) -> Name. +%% %% -spec sym_value(elf_sym()) -> valueoff(). %% sym_value(#elf_sym{value = Value}) -> Value. %% %% -spec sym_size(elf_sym()) -> size(). %% sym_size(#elf_sym{size = Size}) -> Size. -%%%------------------------- -%%% Relocations -%%%------------------------- --spec mk_rel(offset(), info()) -> elf_rel(). -mk_rel(Offset, Info) -> - #elf_rel{r_offset = Offset, r_info = Info}. - -%% The following two functions capitalize on the fact that the two kinds of -%% relocation records (for 32- and 64-bit architectures have similar structure. - --spec r_offset(elf_rel() | elf_rela()) -> offset(). -r_offset(#elf_rel{r_offset = Offset}) -> Offset; -r_offset(#elf_rela{r_offset = Offset}) -> Offset. - --spec r_info(elf_rel() | elf_rela()) -> info(). -r_info(#elf_rel{r_info = Info}) -> Info; -r_info(#elf_rela{r_info = Info}) -> Info. - --spec mk_rela(offset(), info(), offset()) -> elf_rela(). -mk_rela(Offset, Info, Addend) -> - #elf_rela{r_offset = Offset, r_info = Info, r_addend = Addend}. - --spec rela_addend(elf_rela()) -> offset(). -rela_addend(#elf_rela{r_addend = Addend}) -> Addend. - %% %%%------------------------- %% %%% GCC exception table %% %%%------------------------- @@ -263,15 +231,30 @@ mk_gccexntab_callsite(Start, Size, LP, Action) -> %% gccexntab_callsite_lp(#elf_gccexntab_callsite{lp = LP}) -> LP. %%------------------------------------------------------------------------------ +%% Main interface function +%%------------------------------------------------------------------------------ + +%% @doc Parses an ELF file. +-spec read(binary()) -> elf(). +read(ElfBin) -> + Header = extract_header(ElfBin), + [_UndefinedSec|Sections] = extract_shdrtab(ElfBin, Header), + SecNam = maps:from_list( + [{Name, Sec} || Sec = #elf_shdr{name=Name} <- Sections]), + Elf0 = #elf{file=ElfBin, sections=Sections, sec_nam=SecNam}, + [_UndefinedSym|Symbols] = extract_symtab(Elf0, extract_strtab(Elf0)), + Elf0#elf{symbols=Symbols}. + +%%------------------------------------------------------------------------------ %% Functions to manipulate the ELF File Header %%------------------------------------------------------------------------------ %% @doc Extracts the File Header from an ELF formatted object file. Also sets %% the ELF class variable in the process dictionary (used by many functions %% in this and hipe_llvm_main modules). --spec extract_header(elf()) -> elf_ehdr(). -extract_header(Elf) -> - Ehdr_bin = get_binary_segment(Elf, 0, ?ELF_EHDR_SIZE), +-spec extract_header(binary()) -> elf_ehdr(). +extract_header(ElfBin) -> + Ehdr_bin = get_binary_segment(ElfBin, 0, ?ELF_EHDR_SIZE), << %% Structural pattern matching on fields. Ident_bin:?E_IDENT_SIZE/binary, Type:?bits(?E_TYPE_SIZE)/integer-little, @@ -300,19 +283,28 @@ extract_header(Elf) -> %% Functions to manipulate Section Header Entries %%------------------------------------------------------------------------------ +-type shdrtab() :: [elf_shdr()]. + %% @doc Extracts the Section Header Table from an ELF formated Object File. -extract_shdrtab(Elf) -> - %% Extract File Header to get info about Section Header Offset (in bytes), - %% Entry Size (in bytes) and Number of entries - #elf_ehdr{shoff = ShOff, shentsize = ShEntsize, shnum = ShNum} = - extract_header(Elf), +-spec extract_shdrtab(binary(), elf_ehdr()) -> shdrtab(). +extract_shdrtab(ElfBin, #elf_ehdr{shoff=ShOff, shentsize=?ELF_SHDRENTRY_SIZE, + shnum=ShNum, shstrndx=ShStrNdx}) -> %% Get actual Section header table (binary) - ShdrBin = get_binary_segment(Elf, ShOff, ShNum * ShEntsize), - get_shdrtab_entries(ShdrBin, []). - -get_shdrtab_entries(<<>>, Acc) -> - lists:reverse(Acc); -get_shdrtab_entries(ShdrBin, Acc) -> + ShdrBin = get_binary_segment(ElfBin, ShOff, ShNum * ?ELF_SHDRENTRY_SIZE), + %% We need to lookup the offset and size of the section header string table + %% before we can fully parse the section table. We compute its offset and + %% extract the fields we need here. + ShStrEntryOffset = ShStrNdx * ?ELF_SHDRENTRY_SIZE, + <<_:ShStrEntryOffset/binary, _:?SH_NAME_SIZE/binary, + _:?SH_TYPE_SIZE/binary, _:?SH_FLAGS_SIZE/binary, _:?SH_ADDR_SIZE/binary, + ShStrOffset:?bits(?SH_OFFSET_SIZE)/little, + ShStrSize:?bits(?SH_SIZE_SIZE)/little, + _/binary>> = ShdrBin, + ShStrTab = parse_strtab(get_binary_segment(ElfBin, ShStrOffset, ShStrSize)), + get_shdrtab_entries(ShdrBin, ShStrTab). + +get_shdrtab_entries(<<>>, _ShStrTab) -> []; +get_shdrtab_entries(ShdrTab, ShStrTab) -> <<%% Structural pattern matching on fields. Name:?bits(?SH_NAME_SIZE)/integer-little, Type:?bits(?SH_TYPE_SIZE)/integer-little, @@ -324,205 +316,166 @@ get_shdrtab_entries(ShdrBin, Acc) -> Info:?bits(?SH_INFO_SIZE)/integer-little, Addralign:?bits(?SH_ADDRALIGN_SIZE)/integer-little, Entsize:?bits(?SH_ENTSIZE_SIZE)/integer-little, - MoreShdrE/binary - >> = ShdrBin, - ShdrE = mk_shdr(Name, Type, Flags, Addr, Offset, - Size, Link, Info, Addralign, Entsize), - get_shdrtab_entries(MoreShdrE, [ShdrE | Acc]). - -%% @doc Extracts a specific Entry of a Section Header Table. This function -%% takes as argument the Section Header Table (`SHdrTab') and the entry's -%% serial number (`EntryNum') and returns the entry (`shdr'). -get_shdrtab_entry(SHdrTab, EntryNum) -> - lists:nth(EntryNum + 1, SHdrTab). - -%%------------------------------------------------------------------------------ -%% Functions to manipulate Section Header String Table -%%------------------------------------------------------------------------------ - -%% @doc Extracts the Section Header String Table. This section is not a known -%% ELF Object File section. It is just a "hidden" table storing the -%% names of all sections that exist in current object file. --spec extract_shstrtab(elf()) -> [name()]. -extract_shstrtab(Elf) -> - %% Extract Section Name String Table Index - #elf_ehdr{shstrndx = ShStrNdx} = extract_header(Elf), - ShHdrTab = extract_shdrtab(Elf), - %% Extract Section header entry and get actual Section-header String Table - #elf_shdr{offset = ShStrOffset, size = ShStrSize} = - get_shdrtab_entry(ShHdrTab, ShStrNdx), - case get_binary_segment(Elf, ShStrOffset, ShStrSize) of - <<>> -> %% Segment empty - []; - ShStrTab -> %% Convert to string table - [Name || {Name, _Size} <- get_names(ShStrTab)] - end. - -%%------------------------------------------------------------------------------ - --spec get_tab_entries(elf()) -> [{name(), valueoff(), size()}]. -get_tab_entries(Elf) -> - SymTab = extract_symtab(Elf), - Ts = [{Name, Value, Size div ?ELF_XWORD_SIZE} - || #elf_sym{name = Name, value = Value, size = Size} <- SymTab, - Name =/= 0], - {NameIndices, ValueOffs, Sizes} = lists:unzip3(Ts), - %% Find the names of the symbols. - %% Get string table entries ([{Name, Offset in strtab section}]). Keep only - %% relevant entries: - StrTab = extract_strtab(Elf), - Relevant = [get_strtab_entry(StrTab, Off) || Off <- NameIndices], - %% Zip back to {Name, ValueOff, Size} - lists:zip3(Relevant, ValueOffs, Sizes). + Rest/binary + >> = ShdrTab, + Entry = mk_shdr(get_strtab_entry(Name, ShStrTab), decode_shdr_type(Type), + Flags, Addr, Offset, Size, Link, Info, Addralign, Entsize), + [Entry | get_shdrtab_entries(Rest, ShStrTab)]. + +decode_shdr_type(?SHT_NULL) -> 'null'; +decode_shdr_type(?SHT_PROGBITS) -> 'progbits'; +decode_shdr_type(?SHT_SYMTAB) -> 'symtab'; +decode_shdr_type(?SHT_STRTAB) -> 'strtab'; +decode_shdr_type(?SHT_RELA) -> 'rela'; +decode_shdr_type(?SHT_HASH) -> 'hash'; %unused +decode_shdr_type(?SHT_DYNAMIC) -> 'dynamic'; %unused +decode_shdr_type(?SHT_NOTE) -> 'note'; %unused +decode_shdr_type(?SHT_NOBITS) -> 'nobits'; +decode_shdr_type(?SHT_REL) -> 'rel'; +decode_shdr_type(?SHT_SHLIB) -> 'shlib'; %unused +decode_shdr_type(?SHT_DYNSYM) -> 'dynsym'; %unused +decode_shdr_type(OS) when ?SHT_LOOS =< OS, OS =< ?SHT_HIOS -> {os, OS}; +decode_shdr_type(Proc) when ?SHT_LOPROC =< Proc, Proc =< ?SHT_HIPROC -> + {proc, Proc}. + +-spec elf_section(non_neg_integer(), elf()) -> undefined | abs | elf_shdr(). +elf_section(0, #elf{}) -> undefined; +elf_section(?SHN_ABS, #elf{}) -> abs; +elf_section(Index, #elf{sections=SecIdx}) -> + lists:nth(Index, SecIdx). + +%% Reads the contents of a section from an object +-spec section_contents(elf_shdr(), elf()) -> binary(). +section_contents(#elf_shdr{offset=Offset, size=Size}, #elf{file=ElfBin}) -> + get_binary_segment(ElfBin, Offset, Size). %%------------------------------------------------------------------------------ %% Functions to manipulate Symbol Table %%------------------------------------------------------------------------------ %% @doc Function that extracts Symbol Table from an ELF Object file. -extract_symtab(Elf) -> - Symtab_bin = extract_segment_by_name(Elf, ?SYMTAB), - get_symtab_entries(Symtab_bin, []). - -get_symtab_entries(<<>>, Acc) -> - lists:reverse(Acc); -get_symtab_entries(Symtab_bin, Acc) -> - <<SymE_bin:?ELF_SYM_SIZE/binary, MoreSymE/binary>> = Symtab_bin, - case is64bit() of - true -> - <<%% Structural pattern matching on fields. - Name:?bits(?ST_NAME_SIZE)/integer-little, - Info:?bits(?ST_INFO_SIZE)/integer-little, - Other:?bits(?ST_OTHER_SIZE)/integer-little, - Shndx:?bits(?ST_SHNDX_SIZE)/integer-little, - Value:?bits(?ST_VALUE_SIZE)/integer-little, - Size:?bits(?ST_SIZE_SIZE)/integer-little - >> = SymE_bin; - false -> - << %% Same fields in different order: - Name:?bits(?ST_NAME_SIZE)/integer-little, - Value:?bits(?ST_VALUE_SIZE)/integer-little, - Size:?bits(?ST_SIZE_SIZE)/integer-little, - Info:?bits(?ST_INFO_SIZE)/integer-little, - Other:?bits(?ST_OTHER_SIZE)/integer-little, - Shndx:?bits(?ST_SHNDX_SIZE)/integer-little - >> = SymE_bin - end, - SymE = mk_sym(Name, Info, Other, Shndx, Value, Size), - get_symtab_entries(MoreSymE, [SymE | Acc]). - -%% @doc Extracts a specific entry from the Symbol Table (as binary). -%% This function takes as arguments the Symbol Table (`SymTab') -%% and the entry's serial number and returns that entry (`sym'). -get_symtab_entry(SymTab, EntryNum) -> - lists:nth(EntryNum + 1, SymTab). +extract_symtab(Elf, StrTab) -> + Symtab = extract_segment_by_name(Elf, ?SYMTAB), + [parse_sym(Sym, Elf, StrTab) || <<Sym:?ELF_SYM_SIZE/binary>> <= Symtab]. + +-ifdef(BIT32). +parse_sym(<<%% Structural pattern matching on fields. + Name:?bits(?ST_NAME_SIZE)/integer-little, + Value:?bits(?ST_VALUE_SIZE)/integer-little, + Size:?bits(?ST_SIZE_SIZE)/integer-little, + Info:?bits(?ST_INFO_SIZE)/integer-little, + _Other:?bits(?ST_OTHER_SIZE)/integer-little, + Shndx:?bits(?ST_SHNDX_SIZE)/integer-little>>, + Elf, StrTab) -> + mk_sym(get_strtab_entry(Name, StrTab), decode_symbol_bind(?ELF_ST_BIND(Info)), + decode_symbol_type(?ELF_ST_TYPE(Info)), elf_section(Shndx, Elf), Value, + Size). +-else. +parse_sym(<<%% Same fields in different order: + Name:?bits(?ST_NAME_SIZE)/integer-little, + Info:?bits(?ST_INFO_SIZE)/integer-little, + _Other:?bits(?ST_OTHER_SIZE)/integer-little, + Shndx:?bits(?ST_SHNDX_SIZE)/integer-little, + Value:?bits(?ST_VALUE_SIZE)/integer-little, + Size:?bits(?ST_SIZE_SIZE)/integer-little>>, + Elf, StrTab) -> + mk_sym(get_strtab_entry(Name, StrTab), decode_symbol_bind(?ELF_ST_BIND(Info)), + decode_symbol_type(?ELF_ST_TYPE(Info)), elf_section(Shndx, Elf), Value, + Size). +-endif. + +decode_symbol_bind(?STB_LOCAL) -> 'local'; +decode_symbol_bind(?STB_GLOBAL) -> 'global'; +decode_symbol_bind(?STB_WEAK) -> 'weak'; %unused +decode_symbol_bind(OS) when ?STB_LOOS =< OS, OS =< ?STB_HIOS -> {os, OS}; +decode_symbol_bind(Proc) when ?STB_LOPROC =< Proc, Proc =< ?STB_HIPROC -> + {proc, Proc}. + +decode_symbol_type(?STT_NOTYPE) -> 'notype'; +decode_symbol_type(?STT_OBJECT) -> 'object'; +decode_symbol_type(?STT_FUNC) -> 'func'; +decode_symbol_type(?STT_SECTION) -> 'section'; +decode_symbol_type(?STT_FILE) -> 'file'; +decode_symbol_type(OS) when ?STT_LOOS =< OS, OS =< ?STT_HIOS -> {os, OS}; +decode_symbol_type(Proc) when ?STT_LOPROC =< Proc, Proc =< ?STT_HIPROC -> + {proc, Proc}. + +%% @doc Extracts a specific entry from the Symbol Table. +-spec elf_symbol(0, elf()) -> undefined; + (pos_integer(), elf()) -> elf_sym(). +elf_symbol(0, #elf{}) -> undefined; +elf_symbol(Index, #elf{symbols=Symbols}) -> lists:nth(Index, Symbols). + +-spec elf_symbols(elf()) -> [elf_sym()]. +elf_symbols(#elf{symbols=Symbols}) -> Symbols. %%------------------------------------------------------------------------------ %% Functions to manipulate String Table %%------------------------------------------------------------------------------ +%% ADT: get_strtab_entry/1 must be used to consume this type. +-type strtab() :: binary(). + %% @doc Extracts String Table from an ELF formated Object File. --spec extract_strtab(elf()) -> [{string(), offset()}]. +-spec extract_strtab(elf()) -> strtab(). extract_strtab(Elf) -> - Strtab_bin = extract_segment_by_name(Elf, ?STRTAB), - NamesSizes = get_names(Strtab_bin), - make_offsets(NamesSizes). - -%% @doc Returns the name of the symbol at the given offset. The string table -%% contains entries of the form {Name, Offset}. If no such offset exists -%% returns the empty string (`""'). -%% XXX: There might be a bug here because of the "compact" saving the ELF -%% format uses: e.g. only stores ".rela.text" for ".rela.text" and ".text". -get_strtab_entry(Strtab, Offset) -> - case lists:keyfind(Offset, 2, Strtab) of - {Name, Offset} -> Name; - false -> "" - end. + parse_strtab(extract_segment_by_name(Elf, ?STRTAB)). + +-spec parse_strtab(binary()) -> strtab(). +parse_strtab(StrTabSectionBin) -> StrTabSectionBin. + +%% @doc Returns the name of the symbol at the given offset. +-spec get_strtab_entry(non_neg_integer(), strtab()) -> string(). +get_strtab_entry(Offset, StrTab) -> + <<_:Offset/binary, StrBin/binary>> = StrTab, + bin_get_string(StrBin). + +%% @doc Extracts a null-terminated string from a binary. +-spec bin_get_string(binary()) -> string(). +%% FIXME: No regard for encoding (just happens to work for ASCII and Latin-1) +bin_get_string(<<0, _/binary>>) -> []; +bin_get_string(<<Char, Rest/binary>>) -> [Char|bin_get_string(Rest)]. %%------------------------------------------------------------------------------ %% Functions to manipulate Relocations %%------------------------------------------------------------------------------ -%% @doc This function gets as argument an ELF binary file and returns a list -%% with all .rela.rodata labels (i.e. constants and literals in code) -%% or an empty list if no ".rela.rodata" section exists in code. --spec get_rodata_relocs(elf()) -> [offset()]. -get_rodata_relocs(Elf) -> - case is64bit() of - true -> - %% Only care about the addends (== offsets): - get_rela_addends(extract_rela(Elf, ?RODATA)); - false -> - %% Find offsets hardcoded in ".rodata" entry - %%XXX: Treat all 0s as padding and skip them! - [SkipPadding || SkipPadding <- extract_rodata(Elf), SkipPadding =/= 0] - end. - --spec get_rela_addends([elf_rela()]) -> [offset()]. -get_rela_addends(RelaEntries) -> - [rela_addend(E) || E <- RelaEntries]. - -%% @doc Extract a list of the form `[{SymbolName, Offset}]' with all relocatable -%% symbols and their offsets in the code from the ".text" section. --spec get_text_relocs(elf()) -> [{name(), offset()}]. -get_text_relocs(Elf) -> - %% Only care about the symbol table index and the offset: - NameOffsetTemp = [{?ELF_R_SYM(r_info(E)), r_offset(E)} - || E <- extract_rela(Elf, ?TEXT)], - {NameIndices, ActualOffsets} = lists:unzip(NameOffsetTemp), - %% Find the names of the symbols: - %% - %% Get those symbol table entries that are related to Text relocs: - Symtab = extract_symtab(Elf), - SymtabEs = [get_symtab_entry(Symtab, Index) || Index <- NameIndices], - %XXX: not zero-indexed! - %% Symbol table entries contain the offset of the name of the symbol in - %% String Table: - SymtabEs2 = [sym_name(E) || E <- SymtabEs], %XXX: Do we need to sort SymtabE? - %% Get string table entries ([{Name, Offset in strtab section}]). Keep only - %% relevant entries: - Strtab = extract_strtab(Elf), - Relevant = [get_strtab_entry(Strtab, Off) || Off <- SymtabEs2], - %% Zip back with actual offsets: - lists:zip(Relevant, ActualOffsets). - %% @doc Extract the Relocations segment for section `Name' (that is passed %% as second argument) from an ELF formated Object file binary. --spec extract_rela(elf(), name()) -> [elf_rel() | elf_rela()]. +-spec extract_rela(elf(), name()) -> [elf_rel()]. + +-ifdef(BIT32). extract_rela(Elf, Name) -> - SegName = - case is64bit() of - true -> ?RELA(Name); % ELF-64 uses ".rela" - false -> ?REL(Name) % ...while ELF-32 uses ".rel" - end, - Rela_bin = extract_segment_by_name(Elf, SegName), - get_rela_entries(Rela_bin, []). - -get_rela_entries(<<>>, Acc) -> - lists:reverse(Acc); -get_rela_entries(Bin, Acc) -> - E = case is64bit() of - true -> - <<%% Structural pattern matching on fields of a Rela Entry. - Offset:?bits(?R_OFFSET_SIZE)/integer-little, - Info:?bits(?R_INFO_SIZE)/integer-little, - Addend:?bits(?R_ADDEND_SIZE)/integer-little, - Rest/binary - >> = Bin, - mk_rela(Offset, Info, Addend); - false -> - <<%% Structural pattern matching on fields of a Rel Entry. - Offset:?bits(?R_OFFSET_SIZE)/integer-little, - Info:?bits(?R_INFO_SIZE)/integer-little, - Rest/binary - >> = Bin, - mk_rel(Offset, Info) - end, - get_rela_entries(Rest, [E | Acc]). - -%% %% @doc Extract the `EntryNum' (serial number) Relocation Entry. -%% get_rela_entry(Rela, EntryNum) -> -%% lists:nth(EntryNum + 1, Rela). + SecData = extract_segment_by_name(Elf, Name), + [#elf_rel{offset=Offset, symbol=elf_symbol(?ELF_R_SYM(Info), Elf), + type=decode_reloc_type(?ELF_R_TYPE(Info)), + addend=read_implicit_addend(Offset, SecData)} + || <<Offset:?bits(?R_OFFSET_SIZE)/little, + Info:?bits(?R_INFO_SIZE)/little % 386 uses ".rel" + >> <= extract_segment_by_name(Elf, ?REL(Name))]. + +%% The only types HiPE knows how to patch +decode_reloc_type(1) -> '32'; +decode_reloc_type(2) -> 'pc32'. + +read_implicit_addend(Offset, Section) -> + %% All x86 relocation types uses 'word32' relocation fields; i.e. 32-bit LE. + <<_:Offset/binary, Addend:32/signed-little, _/binary>> = Section, + Addend. + +-else. %% BIT32 +extract_rela(Elf, Name) -> + [#elf_rel{offset=Offset, symbol=elf_symbol(?ELF_R_SYM(Info), Elf), + type=decode_reloc_type(?ELF_R_TYPE(Info)), addend=Addend} + || <<Offset:?bits(?R_OFFSET_SIZE)/little, + Info:?bits(?R_INFO_SIZE)/little, + Addend:?bits(?R_ADDEND_SIZE)/signed-little % X86_64 uses ".rela" + >> <= extract_segment_by_name(Elf, ?RELA(Name))]. + +decode_reloc_type(1) -> '64'; +decode_reloc_type(2) -> 'pc32'; +decode_reloc_type(10) -> '32'. +-endif. %% BIT32 %%------------------------------------------------------------------------------ %% Functions to manipulate Executable Code segment @@ -615,19 +568,6 @@ get_gccexntab_callsites(CSTab, Acc) -> get_gccexntab_callsites(More, [GccCS | Acc]). %%------------------------------------------------------------------------------ -%% Functions to manipulate Read-only Data (.rodata) -%%------------------------------------------------------------------------------ -extract_rodata(Elf) -> - Rodata_bin = extract_segment_by_name(Elf, ?RODATA), - get_rodata_entries(Rodata_bin, []). - -get_rodata_entries(<<>>, Acc) -> - lists:reverse(Acc); -get_rodata_entries(Rodata_bin, Acc) -> - <<Num:?bits(?ELF_ADDR_SIZE)/integer-little, More/binary>> = Rodata_bin, - get_rodata_entries(More, [Num | Acc]). - -%%------------------------------------------------------------------------------ %% Helper functions %%------------------------------------------------------------------------------ @@ -647,107 +587,15 @@ get_binary_segment(Bin, Offset, Size) -> %% There are handy macros defined in elf_format.hrl for all Standard %% Section Names. -spec extract_segment_by_name(elf(), string()) -> binary(). -extract_segment_by_name(Elf, SectionName) -> - %% Extract Section Header Table and Section Header String Table from binary - SHdrTable = extract_shdrtab(Elf), - Names = extract_shstrtab(Elf), - %% Zip to a list of (Name,ShdrE) - [_Zero | ShdrEs] = lists:keysort(2, SHdrTable), % Skip first entry (zeros). - L = lists:zip(Names, ShdrEs), +extract_segment_by_name(#elf{file=ElfBin, sec_nam=SecNam}, SectionName) -> %% Find Section Header Table entry by name - case lists:keyfind(SectionName, 1, L) of - {SectionName, ShdrE} -> %% Note: Same name. - #elf_shdr{offset = Offset, size = Size} = ShdrE, - get_binary_segment(Elf, Offset, Size); - false -> %% Not found. + case SecNam of + #{SectionName := #elf_shdr{offset=Offset, size=Size}} -> + get_binary_segment(ElfBin, Offset, Size); + #{} -> %% Not found. <<>> end. -%% @doc Extracts a list of strings with (zero-separated) names from a binary. -%% Returns tuples of `{Name, Size}'. -%% XXX: Skip trailing 0. --spec get_names(<<_:8,_:_*8>>) -> name_sizes(). -get_names(<<0, Bin/binary>>) -> - NamesSizes = get_names(Bin, []), - fix_names(NamesSizes, []). - -get_names(<<>>, Acc) -> - lists:reverse(Acc); -get_names(Bin, Acc) -> - {Name, MoreNames} = bin_get_string(Bin), - get_names(MoreNames, [{Name, length(Name)} | Acc]). - -%% @doc Fix names: -%% e.g. If ".rela.text" exists, ".text" does not. Same goes for -%% ".rel.text". In that way, the Section Header String Table is more -%% compact. Add ".text" just *before* the corresponding rela-field, -%% etc. --spec fix_names(name_sizes(), name_sizes()) -> name_sizes(). -fix_names([], Acc) -> - lists:reverse(Acc); -fix_names([{Name, Size}=T | Names], Acc) -> - case is64bit() of - true -> - case string:str(Name, ".rela") =:= 1 of - true -> %% Name starts with ".rela": - Section = string:substr(Name, 6), - fix_names(Names, [{Section, Size - 5} - | [T | Acc]]); % XXX: Is order ok? (".text" - % always before ".rela.text") - false -> %% Name does not start with ".rela": - fix_names(Names, [T | Acc]) - end; - false -> - case string:str(Name, ".rel") =:= 1 of - true -> %% Name starts with ".rel": - Section = string:substr(Name, 5), - fix_names(Names, [{Section, Size - 4} - | [T | Acc]]); % XXX: Is order ok? (".text" - % always before ".rela.text") - false -> %% Name does not start with ".rel": - fix_names(Names, [T | Acc]) - end - end. - - -%% @doc A function that byte-reverses a binary. This might be needed because of -%% little (fucking!) endianess. --spec bin_reverse(binary()) -> binary(). -bin_reverse(Bin) when is_binary(Bin) -> - bin_reverse(Bin, <<>>). - --spec bin_reverse(binary(), binary()) -> binary(). -bin_reverse(<<>>, Acc) -> - Acc; -bin_reverse(<<Head, More/binary>>, Acc) -> - bin_reverse(More, <<Head, Acc/binary>>). - -%% @doc A function that extracts a null-terminated string from a binary. It -%% returns the found string along with the rest of the binary. --spec bin_get_string(binary()) -> {string(), binary()}. -bin_get_string(Bin) -> - bin_get_string(Bin, <<>>). - -bin_get_string(<<>>, BinAcc) -> - Bin = bin_reverse(BinAcc), % little endian! - {binary_to_list(Bin), <<>>}; -bin_get_string(<<0, MoreBin/binary>>, BinAcc) -> - Bin = bin_reverse(BinAcc), % little endian! - {binary_to_list(Bin), MoreBin}; -bin_get_string(<<Letter, Tail/binary>>, BinAcc) -> - bin_get_string(Tail, <<Letter, BinAcc/binary>>). - -%% @doc -make_offsets(NamesSizes) -> - {Names, Sizes} = lists:unzip(NamesSizes), - Offsets = make_offsets_from_sizes(Sizes, 1, []), - lists:zip(Names, Offsets). - -make_offsets_from_sizes([], _, Acc) -> - lists:reverse(Acc); -make_offsets_from_sizes([Size | Sizes], Cur, Acc) -> - make_offsets_from_sizes(Sizes, Size+Cur+1, [Cur | Acc]). % For the "."! - %% @doc Little-Endian Base 128 (LEB128) Decoder %% This function extracts the <b>first</b> LEB128-encoded integer in a %% binary and returns that integer along with the remaining binary. This is @@ -770,21 +618,3 @@ leb128_decode(LebNum, NoOfBits, Acc) -> <<Num:Size/integer>> = <<NextBundle:7/bits, Acc/bits>>, {Num, MoreLebNums} end. - -%% @doc Extract ELF Class from ELF header and export symbol to process -%% dictionary. --spec set_architecture_flag(elf()) -> 'ok'. -set_architecture_flag(Elf) -> - %% Extract information about ELF Class from ELF Header - <<16#7f, $E, $L, $F, EI_Class, _MoreHeader/binary>> - = get_binary_segment(Elf, 0, ?ELF_EHDR_SIZE), - put(elf_class, EI_Class), - ok. - -%% @doc Read from object file header if the file class is ELF32 or ELF64. --spec is64bit() -> boolean(). -is64bit() -> - case get(elf_class) of - ?ELFCLASS64 -> true; - ?ELFCLASS32 -> false - end. diff --git a/lib/hipe/llvm/elf_format.hrl b/lib/hipe/llvm/elf_format.hrl index 7a3cdfead6..57a36f0c3e 100644 --- a/lib/hipe/llvm/elf_format.hrl +++ b/lib/hipe/llvm/elf_format.hrl @@ -486,3 +486,43 @@ %% Misc. %%------------------------------------------------------------------------------ -define(bits(Bytes), ((Bytes) bsl 3)). + +%%------------------------------------------------------------------------------ +%% Exported record and type declarations for 'elf_format' module +%%------------------------------------------------------------------------------ + +%% Section header entries +-record(elf_shdr, + {name :: elf_format:name() % Section name + ,type :: elf_format:shdr_type() % Section type + ,flags :: elf_format:bitflags() % Section attributes + ,addr :: elf_format:offset() % Virtual address in memory + ,offset :: elf_format:offset() % Offset in file + ,size :: elf_format:size() % Size of section + ,link :: non_neg_integer() % Link to other section + ,info :: non_neg_integer() % Miscellaneous information + ,addralign :: elf_format:size() % Address align boundary + ,entsize :: elf_format:size() % Size of entries, if section has + % table + }). +-type elf_shdr() :: #elf_shdr{}. + +%% Symbol table entries +-record(elf_sym, + {name :: elf_format:name() % Symbol name + ,bind :: elf_format:sym_bind() % Symbol binding + ,type :: elf_format:sym_type() % Symbol type + ,value :: elf_format:valueoff() % Symbol value + ,size :: elf_format:size() % Size of object + ,section :: undefined | abs | elf_shdr() + }). +-type elf_sym() :: #elf_sym{}. + +%% Relocations +-record(elf_rel, + {offset :: elf_format:offset() + ,type :: elf_format:reloc_type() + ,addend :: elf_format:addend() + ,symbol :: elf_sym() + }). +-type elf_rel() :: #elf_rel{}. diff --git a/lib/hipe/llvm/hipe_llvm.erl b/lib/hipe/llvm/hipe_llvm.erl index 5e33731a2b..b22f8fb320 100644 --- a/lib/hipe/llvm/hipe_llvm.erl +++ b/lib/hipe/llvm/hipe_llvm.erl @@ -199,10 +199,9 @@ adj_stack_register/1, adj_stack_type/1, - mk_branch_meta/3, - branch_meta_id/1, - branch_meta_true_weight/1, - branch_meta_false_weight/1 + mk_meta/2, + meta_id/1, + meta_operands/1 ]). -export([ @@ -234,7 +233,7 @@ function_arg_type_list/1 ]). --export([pp_ins_list/2, pp_ins/2]). +-export([pp_ins_list/3, pp_ins/3]). %%----------------------------------------------------------------------------- @@ -343,8 +342,9 @@ -record(llvm_adj_stack, {offset, 'register', type}). -type llvm_adj_stack() :: #llvm_adj_stack{}. --record(llvm_branch_meta, {id, true_weight, false_weight}). --type llvm_branch_meta() :: #llvm_branch_meta{}. +-record(llvm_meta, {id :: string(), + operands :: [string() | integer() | llvm_meta()]}). +-type llvm_meta() :: #llvm_meta{}. %% A type for any LLVM instruction -type llvm_instr() :: llvm_ret() | llvm_br() | llvm_br_cond() @@ -357,7 +357,7 @@ | llvm_call() | llvm_fun_def() | llvm_fun_decl() | llvm_landingpad() | llvm_comment() | llvm_label() | llvm_const_decl() | llvm_asm() | llvm_adj_stack() - | llvm_branch_meta(). + | llvm_meta(). %% Types -record(llvm_void, {}). @@ -701,7 +701,7 @@ is_label(#llvm_comment{}) -> false; is_label(#llvm_const_decl{}) -> false; is_label(#llvm_asm{}) -> false; is_label(#llvm_adj_stack{}) -> false; -is_label(#llvm_branch_meta{}) -> false. +is_label(#llvm_meta{}) -> false. %% const_decl mk_const_decl(Dst, Decl_type, Type, Value) -> @@ -722,14 +722,11 @@ adj_stack_offset(#llvm_adj_stack{offset=Offset}) -> Offset. adj_stack_register(#llvm_adj_stack{'register'=Register}) -> Register. adj_stack_type(#llvm_adj_stack{type=Type}) -> Type. -%% branch meta-data -mk_branch_meta(Id, True_weight, False_weight) -> - #llvm_branch_meta{id=Id, true_weight=True_weight, false_weight=False_weight}. -branch_meta_id(#llvm_branch_meta{id=Id}) -> Id. -branch_meta_true_weight(#llvm_branch_meta{true_weight=True_weight}) -> - True_weight. -branch_meta_false_weight(#llvm_branch_meta{false_weight=False_weight}) -> - False_weight. +%% meta-data +mk_meta(Id, Operands) -> + #llvm_meta{id=Id, operands=Operands}. +meta_id(#llvm_meta{id=Id}) -> Id. +meta_operands(#llvm_meta{operands=Operands}) -> Operands. %% types mk_void() -> #llvm_void{}. @@ -765,13 +762,17 @@ function_arg_type_list(#llvm_fun{arg_type_list=Arg_type_list}) -> %% Pretty-printer Functions %%---------------------------------------------------------------------------- -%% @doc Pretty-print a list of LLVM instructions to a Device. -pp_ins_list(_Dev, []) -> ok; -pp_ins_list(Dev, [I|Is]) -> - pp_ins(Dev, I), - pp_ins_list(Dev, Is). +-type llvm_version() :: {Major :: integer(), Minor :: integer()}. -pp_ins(Dev, I) -> +%% @doc Pretty-print a list of LLVM instructions to a Device, using syntax +%% compatible with LLVM v. Major.Minor +-spec pp_ins_list(file:io_device(), llvm_version(), [llvm_instr()]) -> ok. +pp_ins_list(_Dev, _Ver, []) -> ok; +pp_ins_list(Dev, Ver={_,_}, [I|Is]) -> + pp_ins(Dev, Ver, I), + pp_ins_list(Dev, Ver, Is). + +pp_ins(Dev, Ver, I) -> case indent(I) of true -> write(Dev, " "); false -> ok @@ -861,7 +862,7 @@ pp_ins(Dev, I) -> true -> write(Dev, "volatile "); false -> ok end, - pp_type(Dev, load_p_type(I)), + pp_dereference_type(Dev, Ver, load_p_type(I)), write(Dev, [" ", load_pointer(I), " "]), case load_alignment(I) of [] -> ok; @@ -897,7 +898,7 @@ pp_ins(Dev, I) -> true -> write(Dev, "inbounds "); false -> ok end, - pp_type(Dev, getelementptr_p_type(I)), + pp_dereference_type(Dev, Ver, getelementptr_p_type(I)), write(Dev, [" ", getelementptr_value(I)]), pp_typed_idxs(Dev, getelementptr_typed_idxs(I)), write(Dev, "\n"); @@ -958,12 +959,16 @@ pp_ins(Dev, I) -> pp_args(Dev, fun_def_arglist(I)), write(Dev, ") "), pp_options(Dev, fun_def_fn_attrs(I)), + case Ver >= {3,7} of false -> ok; true -> + write(Dev, "personality i32 (i32, i64, i8*,i8*)* " + "@__gcc_personality_v0 ") + end, case fun_def_align(I) of [] -> ok; N -> write(Dev, ["align ", N]) end, write(Dev, "{\n"), - pp_ins_list(Dev, fun_def_body(I)), + pp_ins_list(Dev, Ver, fun_def_body(I)), write(Dev, "}\n"); #llvm_fun_decl{} -> write(Dev, "declare "), @@ -992,8 +997,12 @@ pp_ins(Dev, I) -> pp_type(Dev, const_decl_type(I)), write(Dev, [" ", const_decl_value(I), "\n"]); #llvm_landingpad{} -> - write(Dev, "landingpad { i8*, i32 } personality i32 (i32, i64, i8*,i8*)* - @__gcc_personality_v0 cleanup\n"); + write(Dev, "landingpad { i8*, i32 } "), + case Ver < {3,7} of false -> ok; true -> + write(Dev, "personality i32 (i32, i64, i8*,i8*)* " + "@__gcc_personality_v0 ") + end, + write(Dev, "cleanup\n"); #llvm_asm{} -> write(Dev, [asm_instruction(I), "\n"]); #llvm_adj_stack{} -> @@ -1001,14 +1010,37 @@ pp_ins(Dev, I) -> adj_stack_register(I), "\", \"r\"("]), pp_type(Dev, adj_stack_type(I)), write(Dev, [" ", adj_stack_offset(I),")\n"]); - #llvm_branch_meta{} -> - write(Dev, ["!", branch_meta_id(I), " = metadata !{metadata !\"branch_weights\", - i32 ", branch_meta_true_weight(I), ", i32 ", - branch_meta_false_weight(I), "}\n"]); + #llvm_meta{} -> + write(Dev, ["!", meta_id(I), " = "]), + Named = case string:to_integer(meta_id(I)) of + {_, ""} -> false; + _ -> true + end, + case Ver < {3,6} andalso not Named of + true -> write(Dev, "metadata !{metadata "); + false -> write(Dev, "!{ ") + end, + write(Dev, string:join([if is_list(Op) -> ["!\"", Op, "\""]; + is_integer(Op) -> ["i32 ", integer_to_list(Op)]; + is_record(Op, llvm_meta) -> + ["!", meta_id(Op)] + end || Op <- meta_operands(I)], ", ")), + write(Dev, " }\n"); Other -> exit({?MODULE, pp_ins, {"Unknown LLVM instruction", Other}}) end. +%% @doc Print the type of a dereference in an LLVM instruction using syntax +%% parsable by the specified LLVM version. +pp_dereference_type(Dev, Ver, Type) -> + case Ver >= {3,7} of + false -> ok; + true -> + pp_type(Dev, pointer_type(Type)), + write(Dev, ", ") + end, + pp_type(Dev, Type). + %% @doc Pretty-print a list of types pp_type_list(_Dev, []) -> ok; pp_type_list(Dev, [T]) -> @@ -1114,7 +1146,7 @@ indent(I) -> #llvm_fun_def{} -> false; #llvm_fun_decl{} -> false; #llvm_const_decl{} -> false; - #llvm_branch_meta{} -> false; + #llvm_meta{} -> false; _ -> true end. diff --git a/lib/hipe/llvm/hipe_llvm_main.erl b/lib/hipe/llvm/hipe_llvm_main.erl index 3c24425828..7f70826046 100644 --- a/lib/hipe/llvm/hipe_llvm_main.erl +++ b/lib/hipe/llvm/hipe_llvm_main.erl @@ -13,7 +13,7 @@ %% chain is invoked in order to produce an object file. rtl_to_native(MFA, RTL, Roots, Options) -> %% Compile to LLVM and get Instruction List (along with infos) - {LLVMCode, RelocsDict, ConstTab} = + {LLVMCode, RelocsDict0, ConstTab0} = hipe_rtl_to_llvm:translate(RTL, Roots), %% Fix function name to an acceptable LLVM identifier (needed for closures) {_Module, Fun, Arity} = hipe_rtl_to_llvm:fix_mfa_name(MFA), @@ -24,34 +24,33 @@ rtl_to_native(MFA, RTL, Roots, Options) -> %% Extract information from object file %% ObjBin = open_object_file(ObjectFile), - %% Read and set the ELF class - elf_format:set_architecture_flag(ObjBin), + Obj = elf_format:read(ObjBin), %% Get labels info (for switches and jump tables) - Labels = elf_format:get_rodata_relocs(ObjBin), - {Switches, Closures} = get_tables(ObjBin), + Labels = elf_format:extract_rela(Obj, ?RODATA), + Tables = get_tables(Obj), %% Associate Labels with Switches and Closures with stack args - {SwitchInfos, ExposedClosures} = - correlate_labels(Switches ++ Closures, Labels), + {SwitchInfos, ExposedClosures} = correlate_labels(Tables, Labels), %% SwitchInfos: [{"table_50", [Labels]}] %% ExposedClosures: [{"table_closures", [Labels]}] - + %% Labelmap contains the offsets of the labels in the code that are %% used for switch's jump tables - LabelMap = create_labelmap(MFA, SwitchInfos, RelocsDict), + LabelMap = create_labelmap(MFA, SwitchInfos, RelocsDict0), + {RelocsDict, ConstTab} = extract_constants(RelocsDict0, ConstTab0, Obj), %% Get relocation info - TextRelocs = elf_format:get_text_relocs(ObjBin), + TextRelocs = elf_format:extract_rela(Obj, ?TEXT), %% AccRefs contains the offsets of all references to relocatable symbols in %% the code: AccRefs = fix_relocations(TextRelocs, RelocsDict, MFA), %% Get stack descriptors - SDescs = get_sdescs(ObjBin), + SDescs = get_sdescs(Obj), %% FixedSDescs are the stack descriptors after correcting calls that have %% arguments in the stack FixedSDescs = fix_stack_descriptors(RelocsDict, AccRefs, SDescs, ExposedClosures), Refs = AccRefs ++ FixedSDescs, %% Get binary code from object file - BinCode = elf_format:extract_text(ObjBin), + BinCode = elf_format:extract_text(Obj), %% Remove temp files (if needed) ok = remove_temp_folder(Dir, Options), %% Return the code together with information that will be used in the @@ -78,13 +77,14 @@ compile_with_llvm(FunName, Arity, LLVMCode, Options, UseBuffer) -> false -> [] end, {ok, File_llvm} = file:open(Dir ++ Filename ++ ".ll", OpenOpts), - hipe_llvm:pp_ins_list(File_llvm, LLVMCode), + Ver = hipe:get_llvm_version(), %% Should probably cache this + hipe_llvm:pp_ins_list(File_llvm, Ver, LLVMCode), %% delayed_write can cause file:close not to do a close, hence the two calls ok = file:close(File_llvm), __ = file:close(File_llvm), %% Invoke LLVM compiler tools to produce an object file llvm_opt(Dir, Filename, Options), - llvm_llc(Dir, Filename, Options), + llvm_llc(Dir, Filename, Ver, Options), compile(Dir, Filename, "gcc"), %%FIXME: use llc -filetype=obj and skip this! {ok, Dir, Dir ++ Filename ++ ".o"}. @@ -103,12 +103,14 @@ llvm_opt(Dir, Filename, Options) -> %% @doc Invoke llc tool to compile the bitcode to object file %% (_name.bc -> _name.o). -llvm_llc(Dir, Filename, Options) -> +llvm_llc(Dir, Filename, Ver, Options) -> Source = Dir ++ Filename ++ ".bc", OptLevel = trans_optlev_flag(llc, Options), + VerFlags = llc_ver_flags(Ver), Align = find_stack_alignment(), LlcFlags = [OptLevel, "-code-model=medium", "-stack-alignment=" ++ Align - , "-tailcallopt", "-filetype=asm"], %%FIXME + , "-tailcallopt", "-filetype=asm" %FIXME + | VerFlags], Command = "llc " ++ fix_opts(LlcFlags) ++ " " ++ Source, %% io:format("LLC: ~s~n", [Command]), case os:cmd(Command) of @@ -153,17 +155,21 @@ trans_optlev_flag(Tool, Options) -> undefined -> "-O2" end. +llc_ver_flags(Ver = {_, _}) when Ver >= {3,9} -> + %% Works around a bug in the x86-call-frame-opt pass (as of LLVM 3.9) that + %% break the garbage collection stack descriptors. + ["-no-x86-call-frame-opt"]; +llc_ver_flags({_, _}) -> []. + %%------------------------------------------------------------------------------ %% Functions to manage Relocations %%------------------------------------------------------------------------------ %% @doc Get switch table and closure table. +-spec get_tables(elf_format:elf()) -> [elf_sym()]. get_tables(Elf) -> - %% Search Symbol Table for an entry with name prefixed with "table_": - Triples = elf_format:get_tab_entries(Elf), - Switches = [T || T={"table_" ++ _, _, _} <- Triples], - Closures = [T || T={"table_closures" ++ _, _, _} <- Switches], - {Switches, Closures}. + %% Search Symbol Table for entries where name is prefixed with "table_": + [S || S=#elf_sym{name="table_" ++ _} <- elf_format:elf_symbols(Elf)]. %% @doc This function associates symbols who point to some table of labels with %% the corresponding offsets of the labels in the code. These tables can @@ -171,14 +177,12 @@ get_tables(Elf) -> %% of blocks that contain closure calls with more than ?NR_ARG_REGS. correlate_labels([], _L) -> {[], []}; correlate_labels(Tables, Labels) -> - %% Sort "Tables" based on "ValueOffsets" - OffsetSortedTb = lists:ukeysort(2, Tables), - %% Unzip offset-sorted list of "Switches" - {Names, _Offsets, TablesSizeList} = lists:unzip3(OffsetSortedTb), - %% Associate switch names with labels - L = split_list(Labels, TablesSizeList), - %% Zip back! (to [{SwitchName, Values}]) - NamesValues = lists:zip(Names, L), + %% Assumes that the relocations are sorted + RelocTree = gb_trees:from_orddict( + [{Rel#elf_rel.offset, Rel#elf_rel.addend} || Rel <- Labels]), + %% Lookup all relocations pertaining to each symbol + NamesValues = [{Name, lookup_range(Value, Value+Size, RelocTree)} + || #elf_sym{name=Name, value=Value, size=Size} <- Tables], case lists:keytake("table_closures", 1, NamesValues) of false -> %% No closures in the code, no closure table {NamesValues, []}; @@ -186,6 +190,17 @@ correlate_labels(Tables, Labels) -> {SwitchesNV, ClosureTableNV} end. +%% Fetches all values with a key in [Low, Hi) +-spec lookup_range(_::K, _::K, gb_trees:tree(K,V)) -> [_::V]. +lookup_range(Low, Hi, Tree) -> + lookup_range_1(Hi, gb_trees:iterator_from(Low, Tree)). + +lookup_range_1(Hi, Iter0) -> + case gb_trees:next(Iter0) of + {Key, Value, Iter} when Key < Hi -> [Value | lookup_range_1(Hi, Iter)]; + _ -> [] + end. + %% @doc Create a gb_tree which contains information about the labels that used %% for switch's jump tables. The keys of the gb_tree are of the form %% {MFA, Label} and the values are the actual Offsets. @@ -213,40 +228,80 @@ insert_to_labelmap([{Key, Value}|Rest], LabelMap) -> insert_to_labelmap(Rest, LabelMap) end. +%% Find any LLVM-generated constants and add them to the constant table +extract_constants(RelocsDict0, ConstTab0, Obj) -> + TextRelocs = elf_format:extract_rela(Obj, ?TEXT), + AnonConstSections = + lists:usort([{Sec, Offset} + || #elf_rel{symbol=#elf_sym{type=section, section=Sec}, + addend=Offset} <- TextRelocs]), + lists:foldl( + fun({#elf_shdr{name=Name, type=progbits, addralign=Align, entsize=EntSize, + size=Size} = Section, Offset}, {RelocsDict1, ConstTab1}) + when EntSize > 0, 0 =:= Size rem EntSize, 0 =:= Offset rem EntSize -> + SectionBin = elf_format:section_contents(Section, Obj), + Constant = binary:part(SectionBin, Offset, EntSize), + {ConstTab, ConstLbl} = + hipe_consttab:insert_binary_const(ConstTab1, Align, Constant), + {dict:store({anon, Name, Offset}, {constant, ConstLbl}, RelocsDict1), + ConstTab} + end, {RelocsDict0, ConstTab0}, AnonConstSections). + %% @doc Correlate object file relocation symbols with info from translation to %% llvm code. fix_relocations(Relocs, RelocsDict, MFA) -> - fix_relocs(Relocs, RelocsDict, MFA, []). - -fix_relocs([], _, _, RelocAcc) -> RelocAcc; -fix_relocs([{Name, Offset}|Rs], RelocsDict, {ModName,_,_}=MFA, RelocAcc) -> + lists:map(fun(Reloc) -> fix_reloc(Reloc, RelocsDict, MFA) end, Relocs). + +%% Relocation types and expected addends for x86 and amd64 +-define(PCREL_T, 'pc32'). +-define(PCREL_A, -4). %% Hard-coded in hipe_x86.c and hipe_amd64.c +-ifdef(BIT32). +-define(ABS_T, '32'). +-define(ABS_A, _). %% We support any addend +-else. +-define(ABS_T, '64'). +-define(ABS_A, 0). +-endif. + +fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=undefined, type=notype}, + offset=Offset, type=?PCREL_T, addend=?PCREL_A}, + RelocsDict, {ModName,_,_}) when Name =/= "" -> case dict:fetch(Name, RelocsDict) of - {atom, AtomName} -> - fix_relocs(Rs, RelocsDict, MFA, - [{?LOAD_ATOM, Offset, AtomName}|RelocAcc]); - {constant, Label} -> - fix_relocs(Rs, RelocsDict, MFA, - [{?LOAD_ADDRESS, Offset, {constant, Label}}|RelocAcc]); - {switch, _, JTabLab} -> %% Treat switch exactly as constant - fix_relocs(Rs, RelocsDict, MFA, - [{?LOAD_ADDRESS, Offset, {constant, JTabLab}}|RelocAcc]); - {closure, _}=Closure -> - fix_relocs(Rs, RelocsDict, MFA, - [{?LOAD_ADDRESS, Offset, Closure}|RelocAcc]); - {call, {bif, BifName, _}} -> - fix_relocs(Rs, RelocsDict, MFA, - [{?CALL_LOCAL, Offset, BifName}|RelocAcc]); + {call, {bif, BifName, _}} -> {?CALL_LOCAL, Offset, BifName}; %% MFA calls to functions in the same module are of type 3, while all %% other MFA calls are of type 2. - {call, {ModName,_F,_A}=CallMFA} -> - fix_relocs(Rs, RelocsDict, MFA, - [{?CALL_LOCAL, Offset, CallMFA}|RelocAcc]); - {call, CallMFA} -> - fix_relocs(Rs, RelocsDict, MFA, - [{?CALL_REMOTE, Offset, CallMFA}|RelocAcc]); - Other -> - exit({?MODULE, fix_relocs, - {"Relocation not in relocation dictionary", Other}}) + %% XXX: Does this code break hot code loading (by transforming external + %% calls into local calls?) + {call, {ModName,_F,_A}=CallMFA} -> {?CALL_LOCAL, Offset, CallMFA}; + {call, CallMFA} -> {?CALL_REMOTE, Offset, CallMFA} + end; +fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=undefined, type=notype}, + offset=Offset, type=?ABS_T, addend=?ABS_A}, + RelocsDict, _) when Name =/= "" -> + case dict:fetch(Name, RelocsDict) of + {atom, AtomName} -> {?LOAD_ATOM, Offset, AtomName}; + {constant, Label} -> {?LOAD_ADDRESS, Offset, {constant, Label}}; + {closure, _}=Closure -> {?LOAD_ADDRESS, Offset, Closure} + end; +fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=#elf_shdr{name=?TEXT}, + type=func}, + offset=Offset, type=?PCREL_T, addend=?PCREL_A}, + RelocsDict, MFA) when Name =/= "" -> + case dict:fetch(Name, RelocsDict) of + {call, MFA} -> {?CALL_LOCAL, Offset, MFA} + end; +fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=#elf_shdr{name=?RODATA}, + type=object}, + offset=Offset, type=?ABS_T, addend=?ABS_A}, + RelocsDict, _) when Name =/= "" -> + case dict:fetch(Name, RelocsDict) of + {switch, _, JTabLab} -> %% Treat switch exactly as constant + {?LOAD_ADDRESS, Offset, {constant, JTabLab}} + end; +fix_reloc(#elf_rel{symbol=#elf_sym{type=section, section=#elf_shdr{name=Name}}, + offset=Offset, type=?ABS_T, addend=Addend}, RelocsDict, _) -> + case dict:fetch({anon, Name, Addend}, RelocsDict) of + {constant, Label} -> {?LOAD_ADDRESS, Offset, {constant, Label}} end. %%------------------------------------------------------------------------------ @@ -271,20 +326,14 @@ get_sdescs(Elf) -> T = SPCount * ?SP_ADDR_SIZE, %% Pattern match fields of ".note.gc": <<SPCount:(?bits(?SP_COUNT_SIZE))/integer-little, % Sanity check! - SPAddrs:T/binary, % NOTE: In 64bit they are relocs! + _SPAddrs:T/binary, % NOTE: In 64bit they are relocs! StkFrameSize:(?bits(?SP_STKFRAME_SIZE))/integer-little, StkArity:(?bits(?SP_STKARITY_SIZE))/integer-little, _LiveRootCount:(?bits(?SP_LIVEROOTCNT_SIZE))/integer-little, % Skip Roots/binary>> = NoteGC_bin, LiveRoots = get_liveroots(Roots, []), - %% Extract information about the safe point addresses: - SPOffs = - case elf_format:is64bit() of - true -> %% Find offsets in ".rela.note.gc": - elf_format:get_rela_addends(RelaNoteGC); - false -> %% Find offsets in SPAddrs (in ".note.gc"): - get_spoffs(SPAddrs, []) - end, + %% Extract the safe point offsets: + SPOffs = [A || #elf_rel{addend=A} <- RelaNoteGC], %% Extract Exception Handler labels: ExnHandlers = elf_format:get_exn_handlers(Elf), %% Combine ExnHandlers and Safe point addresses (return addresses): @@ -300,13 +349,6 @@ get_liveroots(<<Root:?bits(?LR_STKINDEX_SIZE)/integer-little, MoreRoots/binary>>, Acc) -> get_liveroots(MoreRoots, [Root | Acc]). -%% @doc Extracts a bunch of integers (safepoint offsets) from a binary. Returns -%% a tuple as need for stack descriptors. -get_spoffs(<<>>, Acc) -> - lists:reverse(Acc); -get_spoffs(<<SPOff:?bits(?SP_ADDR_SIZE)/integer-little, More/binary>>, Acc) -> - get_spoffs(More, [SPOff | Acc]). - combine_ras_and_exns(_, [], Acc) -> lists:reverse(Acc); combine_ras_and_exns(ExnHandlers, [RA | MoreRAs], Acc) -> @@ -489,18 +531,3 @@ unique_folder(FunName, Arity, Options) -> dir_exists(Filename) -> {Flag, Info} = file:read_file_info(Filename), (Flag =:= ok) andalso (element(3, Info) =:= directory). - -%% @doc Function that takes as arguments a list of integers and a list with -%% numbers indicating how many items should each tuple have and splits -%% the original list to a list of lists of integers (with the specified -%% number of elements), i.e. [ [...], [...] ]. --spec split_list([integer()], [integer()]) -> [ [integer()] ]. -split_list(List, ElemsPerTuple) -> - split_list(List, ElemsPerTuple, []). - --spec split_list([integer()], [integer()], [ [integer()] ]) -> [ [integer()] ]. -split_list([], [], Acc) -> - lists:reverse(Acc); -split_list(List, [NumOfElems | MoreNums], Acc) -> - {L1, L2} = lists:split(NumOfElems, List), - split_list(L2, MoreNums, [ L1 | Acc]). diff --git a/lib/hipe/llvm/hipe_rtl_to_llvm.erl b/lib/hipe/llvm/hipe_rtl_to_llvm.erl index d7d8d1b049..2179f7f765 100644 --- a/lib/hipe/llvm/hipe_rtl_to_llvm.erl +++ b/lib/hipe/llvm/hipe_rtl_to_llvm.erl @@ -10,9 +10,12 @@ -include("../rtl/hipe_literals.hrl"). -include("hipe_llvm_arch.hrl"). --define(WORD_WIDTH, (?bytes_to_bits(hipe_rtl_arch:word_size()))). +-define(BITS_IN_WORD, (?bytes_to_bits(hipe_rtl_arch:word_size()))). +-define(BITS_IN_BYTE, (?bytes_to_bits(1))). -define(BRANCH_META_TAKEN, "0"). -define(BRANCH_META_NOT_TAKEN, "1"). +-define(FIRST_FREE_META_NO, 2). +-define(HIPE_LITERALS_META, "hipe.literals"). %%------------------------------------------------------------------------------ %% @doc Main function for translating an RTL function to LLVM Assembly. Takes as @@ -51,8 +54,9 @@ translate(RTL, Roots) -> translate_instr_list(Code1, [], Relocs, Data), %% Create LLVM code to declare relocation symbols as external symbols along %% with local variables in order to use them as just any other variable - {FinalRelocs, ExternalDecl, LocalVars} = + {FinalRelocs, ExternalDecl0, LocalVars} = handle_relocations(Relocs1, Data, Fun), + ExternalDecl = add_literals_metadata(ExternalDecl0), %% Pass on LLVM code in order to create Fail blocks and a landingpad %% instruction to each one LLVM_Code2 = add_landingpads(LLVM_Code1, FailLabels), @@ -92,9 +96,9 @@ do_alloca_stack([], _, _, Acc) -> Acc; do_alloca_stack([D|Ds], Params, Roots, Acc) -> {Name, _I} = trans_dst(D), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), WordTyPtr = hipe_llvm:mk_pointer(WordTy), - ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)), + ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(?BITS_IN_BYTE)), case hipe_rtl:is_var(D) of true -> Num = hipe_rtl:var_index(D), @@ -230,7 +234,7 @@ trans_alu(I, Relocs) -> {Src1, I1} = trans_src(hipe_rtl:alu_src1(I)), {Src2, I2} = trans_src(hipe_rtl:alu_src2(I)), Op = trans_op(hipe_rtl:alu_op(I)), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), I3 = hipe_llvm:mk_operation(TmpDst, Op, WordTy, Src1, Src2, []), I4 = store_stack_dst(TmpDst, RtlDst), {[I4, I3, I2, I1], Relocs}. @@ -255,7 +259,7 @@ trans_alub_overflow(I, Sign, Relocs) -> TmpDst = mk_temp(), Name = trans_alub_op(I, Sign), NewRelocs = relocs_store(Name, {call, {llvm, Name, 2}}, Relocs), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), ReturnType = hipe_llvm:mk_struct([WordTy, hipe_llvm:mk_int(1)]), T1 = mk_temp(), I3 = hipe_llvm:mk_call(T1, false, [], [], ReturnType, "@" ++ Name, @@ -266,17 +270,18 @@ trans_alub_overflow(I, Sign, Relocs) -> T2 = mk_temp(), %% T1{1}: Boolean variable indicating overflow I6 = hipe_llvm:mk_extractvalue(T2, ReturnType, T1, "1", []), - case hipe_rtl:alub_cond(I) of - Op when Op =:= overflow orelse Op =:= ltu -> - True_label = mk_jump_label(hipe_rtl:alub_true_label(I)), - False_label = mk_jump_label(hipe_rtl:alub_false_label(I)), - MetaData = branch_metadata(hipe_rtl:alub_pred(I)); - not_overflow -> - True_label = mk_jump_label(hipe_rtl:alub_false_label(I)), - False_label = mk_jump_label(hipe_rtl:alub_true_label(I)), - MetaData = branch_metadata(1 - hipe_rtl:alub_pred(I)) - end, - I7 = hipe_llvm:mk_br_cond(T2, True_label, False_label, MetaData), + {TrueLabel, FalseLabel, MetaData} = + case hipe_rtl:alub_cond(I) of + Op when Op =:= overflow orelse Op =:= ltu -> + {mk_jump_label(hipe_rtl:alub_true_label(I)), + mk_jump_label(hipe_rtl:alub_false_label(I)), + branch_metadata(hipe_rtl:alub_pred(I))}; + not_overflow -> + {mk_jump_label(hipe_rtl:alub_false_label(I)), + mk_jump_label(hipe_rtl:alub_true_label(I)), + branch_metadata(1 - hipe_rtl:alub_pred(I))} + end, + I7 = hipe_llvm:mk_br_cond(T2, TrueLabel, FalseLabel, MetaData), {[I7, I6, I5, I4, I3, I2, I1], NewRelocs}. trans_alub_op(I, Sign) -> @@ -316,7 +321,7 @@ trans_alub_no_overflow(I, Relocs) -> {Dst, I2} = trans_src(hipe_rtl:alub_dst(I)), Cond = trans_rel_op(hipe_rtl:alub_cond(I)), T3 = mk_temp(), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), I5 = hipe_llvm:mk_icmp(T3, Cond, WordTy, Dst, "0"), %% br Metadata = branch_metadata(hipe_rtl:alub_pred(I)), @@ -334,7 +339,7 @@ trans_branch(I, Relocs) -> Cond = trans_rel_op(hipe_rtl:branch_cond(I)), %% icmp T1 = mk_temp(), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), I3 = hipe_llvm:mk_icmp(T1, Cond, WordTy, Src1, Src2), %% br True_label = mk_jump_label(hipe_rtl:branch_true_label(I)), @@ -362,7 +367,7 @@ trans_call(I, Relocs) -> {Name, I3, Relocs2} = trans_call_name(RtlCallName, Relocs1, CallArgs, FinalArgs), T1 = mk_temp(), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)), I4 = case hipe_rtl:call_fail(I) of @@ -446,7 +451,7 @@ trans_call_name(RtlCallName, Relocs, CallArgs, FinalArgs) -> %% order to make the call TT1 = mk_temp(), {RegName, II1} = trans_src(Reg), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), WordTyPtr = hipe_llvm:mk_pointer(WordTy), II2 = hipe_llvm:mk_conversion(TT1, inttoptr, WordTy, RegName, WordTyPtr), @@ -499,7 +504,7 @@ trans_enter(I, Relocs) -> {Name, I2, NewRelocs} = trans_call_name(hipe_rtl:enter_fun(I), Relocs, CallArgs, FinalArgs), T1 = mk_temp(), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)), I3 = hipe_llvm:mk_call(T1, true, "cc 11", [], FunRetTy, Name, FinalArgs, []), I4 = hipe_llvm:mk_ret([{FunRetTy, T1}]), @@ -514,7 +519,7 @@ trans_fconv(I, Relocs) -> TmpDst = mk_temp(), {Src, I1} = trans_float_src(hipe_rtl:fconv_src(I)), FloatTy = hipe_llvm:mk_double(), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), I2 = hipe_llvm:mk_conversion(TmpDst, sitofp, WordTy, Src, FloatTy), I3 = store_float_stack(TmpDst, RtlDst), {[I3, I2, I1], Relocs}. @@ -534,7 +539,7 @@ trans_fload(I, Relocs) -> {Src, I1} = trans_float_src(RtlSrc), {Offset, I2} = trans_float_src(_Offset), T1 = mk_temp(), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), FloatTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_double()), I3 = hipe_llvm:mk_operation(T1, add, WordTy, Src, Offset, []), T2 = mk_temp(), @@ -615,7 +620,7 @@ trans_fstore(I, Relocs) -> trans_fstore_reg(I, Relocs) -> {Base, I0} = trans_reg(hipe_rtl:fstore_base(I), dst), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), WordTyPtr = hipe_llvm:mk_pointer(WordTy), FloatTy = hipe_llvm:mk_double(), FloatTyPtr = hipe_llvm:mk_pointer(FloatTy), @@ -655,7 +660,7 @@ trans_load(I, Relocs) -> {Src, I1} = trans_src(hipe_rtl:load_src(I)), {Offset, I2} = trans_src(hipe_rtl:load_offset(I)), T1 = mk_temp(), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), WordTyPtr = hipe_llvm:mk_pointer(WordTy), I3 = hipe_llvm:mk_operation(T1, add, WordTy, Src, Offset, []), %%---------------------------------------------------------------- @@ -733,7 +738,7 @@ trans_move(I, Relocs) -> %% return %% trans_return(I, Relocs) -> - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), {VarRet, I1} = case hipe_rtl:return_varlist(I) of [] -> @@ -773,7 +778,7 @@ trans_store(I, Relocs) -> {Offset, I2} = trans_src(hipe_rtl:store_offset(I)), {Value, I3} = trans_src(hipe_rtl:store_src(I)), T1 = mk_temp(), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), WordTyPtr = hipe_llvm:mk_pointer(WordTy), I4 = hipe_llvm:mk_operation(T1, add, WordTy, Base, Offset, []), I5 = @@ -807,14 +812,14 @@ trans_switch(I, Relocs, Data) -> JumpLabels = [mk_jump_label(L) || L <- Labels], SortOrder = hipe_rtl:switch_sort_order(I), NrLabels = length(Labels), - ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)), + ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(?BITS_IN_BYTE)), TableType = hipe_llvm:mk_array(NrLabels, ByteTyPtr), TableTypeP = hipe_llvm:mk_pointer(TableType), TypedJumpLabels = [{hipe_llvm:mk_label_type(), X} || X <- JumpLabels], T1 = mk_temp(), {Src2, []} = trans_dst(RtlSrc), TableName = "table_" ++ tl(Src2), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), I2 = hipe_llvm:mk_getelementptr(T1, TableTypeP, "@"++TableName, [{WordTy, "0"}, {WordTy, Src}], false), T2 = mk_temp(), @@ -929,7 +934,7 @@ create_fail_blocks(Label, FailLabels, Acc) -> false -> Acc; {value, {Label, FailLabel, SpAdj}, RestFailLabels} -> - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), I1 = hipe_llvm:mk_label(FailLabel), LP = hipe_llvm:mk_landingpad(), I2 = @@ -958,7 +963,7 @@ create_fail_blocks(Label, FailLabels, Acc) -> %% @doc Convert RTL argument list to LLVM argument list. trans_args(ArgList) -> - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), MakeArg = fun(A) -> {Name, I1} = trans_src(A), @@ -968,13 +973,13 @@ trans_args(ArgList) -> %% @doc Convert a list of Precoloured registers to LLVM argument list. fix_reg_args(ArgList) -> - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), [{WordTy, A} || A <- ArgList]. %% @doc Load Precoloured registers. load_fixed_regs(RegList) -> Names = [mk_temp_reg(R) || R <- RegList], - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), WordTyPtr = hipe_llvm:mk_pointer(WordTy), Fun1 = fun (X, Y) -> @@ -987,7 +992,7 @@ load_fixed_regs(RegList) -> store_fixed_regs(RegList, Name) -> Names = [mk_temp_reg(R) || R <- RegList], Indexes = lists:seq(0, erlang:length(RegList) - 1), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), WordTyPtr = hipe_llvm:mk_pointer(WordTy), FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)), Fun1 = @@ -1056,7 +1061,7 @@ mk_temp_reg(Name) -> store_stack_dst(TempDst, Dst) -> {Dst2, II1} = trans_dst(Dst), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), WordTyPtr = hipe_llvm:mk_pointer(WordTy), II2 = hipe_llvm:mk_store(WordTy, TempDst, WordTyPtr, Dst2, [], [], false), [II2, II1]. @@ -1074,7 +1079,7 @@ trans_float_src(Src) -> Name = "@DL" ++ integer_to_list(hipe_rtl:const_label_label(Src)), T1 = mk_temp(), %% XXX: Hardcoded offset - ByteTy = hipe_llvm:mk_int(8), + ByteTy = hipe_llvm:mk_int(?BITS_IN_BYTE), ByteTyPtr = hipe_llvm:mk_pointer(ByteTy), I1 = hipe_llvm:mk_getelementptr(T1, ByteTyPtr, Name, [{ByteTy, integer_to_list(?FLOAT_OFFSET)}], true), @@ -1090,7 +1095,7 @@ trans_float_src(Src) -> end. trans_src(A) -> - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), WordTyPtr = hipe_llvm:mk_pointer(WordTy), case hipe_rtl:is_imm(A) of true -> @@ -1193,7 +1198,7 @@ map_precoloured_reg(Index) -> fix_reg_dst(Register) -> case Register of {Name, Offset} -> %% Case of %fcalls, %hplim - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), pointer_from_reg(Name, WordTy, Offset); Name -> %% Case of %p and %hp {Name, []} @@ -1201,7 +1206,7 @@ fix_reg_dst(Register) -> %% @doc Load precoloured src register. fix_reg_src(Register) -> - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), WordTyPtr = hipe_llvm:mk_pointer(WordTy), case Register of {Name, Offset} -> %% Case of %fcalls, %hplim @@ -1323,10 +1328,10 @@ insn_dst(I) -> llvm_type_from_size(Size) -> case Size of - byte -> hipe_llvm:mk_int(8); + byte -> hipe_llvm:mk_int(?BITS_IN_BYTE); int16 -> hipe_llvm:mk_int(16); int32 -> hipe_llvm:mk_int(32); - word -> hipe_llvm:mk_int(64) + word -> hipe_llvm:mk_int(?BITS_IN_WORD) end. %% @doc Create definition for the compiled function. The parameters that are @@ -1356,13 +1361,13 @@ create_function_definition(Fun, Params, Code, LocalVars) -> lists:flatten([EntryLabel, ExceptionSync, I2, LocalVars, StoredParams, I3]), Final_Code = EntryBlock ++ Code, FunctionOptions = [nounwind, noredzone, list_to_atom("gc \"erlang\"")], - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)), hipe_llvm:mk_fun_def([], [], "cc 11", [], FunRetTy, FunctionName, Args, FunctionOptions, [], Final_Code). header_params(Params) -> - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), [{WordTy, "%v" ++ integer_to_list(hipe_rtl:var_index(P))} || P <- Params]. store_params(Params) -> @@ -1371,7 +1376,7 @@ store_params(Params) -> Index = hipe_rtl:var_index(X), {Name, _} = trans_dst(X), ParamName = "%v" ++ integer_to_list(Index), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), WordTyPtr = hipe_llvm:mk_pointer(WordTy), hipe_llvm:mk_store(WordTy, ParamName, WordTyPtr, Name, [], [], false) end, @@ -1388,11 +1393,11 @@ fixed_registers() -> end. header_regs(Registers) -> - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), [{WordTy, "%" ++ X ++ "_in"} || X <- Registers]. load_regs(Registers) -> - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), WordTyPtr = hipe_llvm:mk_pointer(WordTy), Fun1 = fun(X) -> @@ -1457,8 +1462,8 @@ handle_relocations(Relocs, Data, Fun) -> Relocs4 = dict:store("hipe_bifs.llvm_fix_pinned_regs.0", {call, {hipe_bifs, llvm_fix_pinned_regs, 0}}, Relocs3), BranchMetaData = [ - hipe_llvm:mk_branch_meta(?BRANCH_META_TAKEN, "99", "1") - , hipe_llvm:mk_branch_meta(?BRANCH_META_NOT_TAKEN, "1", "99") + hipe_llvm:mk_meta(?BRANCH_META_TAKEN, ["branch_weights", 99, 1]) + , hipe_llvm:mk_meta(?BRANCH_META_NOT_TAKEN, ["branch_weights", 1, 99]) ], ExternalDeclarations = AtomDecl ++ ClosureDecl ++ ConstDecl ++ FunDecl ++ ClosureLabelDecl ++ SwitchDecl ++ BranchMetaData, @@ -1492,28 +1497,30 @@ seperate_relocs([R|Rs], CallAcc, AtomAcc, ClosureAcc, LabelAcc, JmpTableAcc) -> %% @doc External declaration of an atom. declare_atom({AtomName, _}) -> - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), - hipe_llvm:mk_const_decl("@" ++ AtomName, "external constant", WordTy, ""). + %% The type has to be byte, or a backend might assume the constant is aligned + %% and incorrectly optimise away type tests + ByteTy = hipe_llvm:mk_int(?BITS_IN_BYTE), + hipe_llvm:mk_const_decl("@" ++ AtomName, "external constant", ByteTy, ""). %% @doc Creation of local variable for an atom. load_atom({AtomName, _}) -> Dst = "%" ++ AtomName ++ "_var", Name = "@" ++ AtomName, - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), - WordTyPtr = hipe_llvm:mk_pointer(WordTy), - hipe_llvm:mk_conversion(Dst, ptrtoint, WordTyPtr, Name, WordTy). + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), + ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(?BITS_IN_BYTE)), + hipe_llvm:mk_conversion(Dst, ptrtoint, ByteTyPtr, Name, WordTy). %% @doc External declaration of a closure. declare_closure({ClosureName, _})-> - ByteTy = hipe_llvm:mk_int(8), + ByteTy = hipe_llvm:mk_int(?BITS_IN_BYTE), hipe_llvm:mk_const_decl("@" ++ ClosureName, "external constant", ByteTy, ""). %% @doc Creation of local variable for a closure. load_closure({ClosureName, _})-> Dst = "%" ++ ClosureName ++ "_var", Name = "@" ++ ClosureName, - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), - ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), + ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(?BITS_IN_BYTE)), hipe_llvm:mk_conversion(Dst, ptrtoint, ByteTyPtr, Name, WordTy). %% @doc Declaration of a local variable for a switch jump table. @@ -1544,7 +1551,7 @@ declare_closure_labels(ClosureLabels, Relocs, Fun) -> List3 = string:join(List2, ",\n"), List4 = "[\n" ++ List3 ++ "\n]\n", NrLabels = length(LabelList), - ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)), + ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(?BITS_IN_BYTE)), TableType = hipe_llvm:mk_array(NrLabels, ByteTyPtr), ConstDecl = hipe_llvm:mk_const_decl("@table_closures", "constant", TableType, List4), @@ -1559,7 +1566,7 @@ is_external_call(_, _) -> true. call_to_decl({Name, {call, MFA}}) -> {M, _F, A} = MFA, CConv = "cc 11", - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)), {Type, Args} = case M of @@ -1574,14 +1581,14 @@ call_to_decl({Name, {call, MFA}}) -> %% @doc These functions are always declared, even if not used. fixed_fun_decl() -> - ByteTy = hipe_llvm:mk_int(8), + ByteTy = hipe_llvm:mk_int(?BITS_IN_BYTE), ByteTyPtr = hipe_llvm:mk_pointer(ByteTy), LandPad = hipe_llvm:mk_fun_decl([], [], [], [], hipe_llvm:mk_int(32), "@__gcc_personality_v0", [hipe_llvm:mk_int(32), hipe_llvm:mk_int(64), ByteTyPtr, ByteTyPtr], []), GCROOTDecl = hipe_llvm:mk_fun_decl([], [], [], [], hipe_llvm:mk_void(), "@llvm.gcroot", [hipe_llvm:mk_pointer(ByteTyPtr), ByteTyPtr], []), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), FunRetTy = hipe_llvm:mk_struct(lists:duplicate(?NR_PINNED_REGS + 1, WordTy)), FixPinnedRegs = hipe_llvm:mk_fun_decl([], [], [], [], FunRetTy, "@hipe_bifs.llvm_fix_pinned_regs.0", [], []), @@ -1595,7 +1602,7 @@ fixed_fun_decl() -> %% values, add the offset and convert them again to pointers. declare_constant(Label) -> Name = "@DL" ++ integer_to_list(Label), - ByteTy = hipe_llvm:mk_int(8), + ByteTy = hipe_llvm:mk_int(?BITS_IN_BYTE), hipe_llvm:mk_const_decl(Name, "external constant", ByteTy, ""). %% @doc Load a constant is achieved by converting a pointer to an integer of @@ -1603,11 +1610,24 @@ declare_constant(Label) -> load_constant(Label) -> Dst = "%DL" ++ integer_to_list(Label) ++ "_var", Name = "@DL" ++ integer_to_list(Label), - WordTy = hipe_llvm:mk_int(?WORD_WIDTH), - ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(8)), + WordTy = hipe_llvm:mk_int(?BITS_IN_WORD), + ByteTyPtr = hipe_llvm:mk_pointer(hipe_llvm:mk_int(?BITS_IN_BYTE)), hipe_llvm:mk_conversion(Dst, ptrtoint, ByteTyPtr, Name, WordTy). %% @doc Store external constants and calls to dictionary. const_to_dict(Elem, Dict) -> Name = "DL" ++ integer_to_list(Elem), dict:store(Name, {'constant', Elem}, Dict). + +%% @doc Export the hipe literals that LLVM needs to generate the prologue as +%% metadata. +add_literals_metadata(ExternalDecls) -> + Pairs = [hipe_llvm:mk_meta(integer_to_list(?FIRST_FREE_META_NO), + ["P_NSP_LIMIT", ?P_NSP_LIMIT]) + ,hipe_llvm:mk_meta(integer_to_list(?FIRST_FREE_META_NO + 1), + ["X86_LEAF_WORDS", ?X86_LEAF_WORDS]) + ,hipe_llvm:mk_meta(integer_to_list(?FIRST_FREE_META_NO + 2), + ["AMD64_LEAF_WORDS", ?AMD64_LEAF_WORDS]) + ], + [hipe_llvm:mk_meta(?HIPE_LITERALS_META, Pairs) | + Pairs ++ ExternalDecls]. |