xref: /aosp_15_r20/external/mesa3d/src/gallium/frontends/clover/llvm/codegen/native.cpp (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 //
2 // Copyright 2012-2016 Francisco Jerez
3 // Copyright 2012-2016 Advanced Micro Devices, Inc.
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a
6 // copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the
10 // Software is furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 // OTHER DEALINGS IN THE SOFTWARE.
22 //
23 
24 ///
25 /// \file
26 /// Generate code using an arbitrary LLVM back-end capable of emitting
27 /// executable code as an ELF object file.
28 ///
29 
30 #include <llvm/Target/TargetMachine.h>
31 #include <llvm/Transforms/Utils/Cloning.h>
32 
33 #include "llvm/codegen.hpp"
34 #include "llvm/compat.hpp"
35 #include "llvm/util.hpp"
36 #include "core/error.hpp"
37 
38 using clover::binary;
39 using clover::build_error;
40 using namespace clover::llvm;
41 using ::llvm::TargetMachine;
42 
43 #if defined(USE_LIBELF)
44 
45 #include <libelf.h>
46 #include <gelf.h>
47 
48 namespace {
49    namespace elf {
50       std::unique_ptr<Elf, int (*)(Elf *)>
get(const std::vector<char> & code)51       get(const std::vector<char> &code) {
52          // One of the libelf implementations
53          // (http://www.mr511.de/software/english.htm) requires calling
54          // elf_version() before elf_memory().
55          elf_version(EV_CURRENT);
56          return { elf_memory(const_cast<char *>(code.data()), code.size()),
57                   elf_end };
58       }
59 
60       Elf_Scn *
get_symbol_table(Elf * elf)61       get_symbol_table(Elf *elf) {
62          size_t section_str_index;
63          elf_getshdrstrndx(elf, &section_str_index);
64 
65          for (Elf_Scn *s = elf_nextscn(elf, NULL); s; s = elf_nextscn(elf, s)) {
66             GElf_Shdr header;
67             if (gelf_getshdr(s, &header) != &header)
68                return nullptr;
69 
70             if (!std::strcmp(elf_strptr(elf, section_str_index, header.sh_name),
71                              ".symtab"))
72                return s;
73          }
74 
75          return nullptr;
76       }
77 
78       std::map<std::string, unsigned>
get_symbol_offsets(Elf * elf,Elf_Scn * symtab)79       get_symbol_offsets(Elf *elf, Elf_Scn *symtab) {
80          Elf_Data *const symtab_data = elf_getdata(symtab, NULL);
81          GElf_Shdr header;
82          if (gelf_getshdr(symtab, &header) != &header)
83             return {};
84 
85          std::map<std::string, unsigned> symbol_offsets;
86          GElf_Sym symbol;
87          unsigned i = 0;
88 
89          while (GElf_Sym *s = gelf_getsym(symtab_data, i++, &symbol)) {
90             const char *name = elf_strptr(elf, header.sh_link, s->st_name);
91             symbol_offsets[name] = s->st_value;
92          }
93 
94          return symbol_offsets;
95       }
96    }
97 
98    std::map<std::string, unsigned>
get_symbol_offsets(const std::vector<char> & code,std::string & r_log)99    get_symbol_offsets(const std::vector<char> &code, std::string &r_log) {
100       const auto elf = elf::get(code);
101       const auto symtab = elf::get_symbol_table(elf.get());
102       if (!symtab)
103          fail(r_log, build_error(), "Unable to find symbol table.");
104 
105       return elf::get_symbol_offsets(elf.get(), symtab);
106    }
107 
108    std::vector<char>
emit_code(::llvm::Module & mod,const target & target,compat::CodeGenFileType ft,std::string & r_log)109    emit_code(::llvm::Module &mod, const target &target,
110              compat::CodeGenFileType ft,
111              std::string &r_log) {
112       std::string err;
113       auto t = ::llvm::TargetRegistry::lookupTarget(target.triple, err);
114       if (!t)
115          fail(r_log, build_error(), err);
116 
117       std::unique_ptr<TargetMachine> tm {
118          t->createTargetMachine(target.triple, target.cpu, "", {},
119 #if LLVM_VERSION_MAJOR >= 16
120                                 std::nullopt, std::nullopt,
121 #else
122                                 ::llvm::None, ::llvm::None,
123 #endif
124 #if LLVM_VERSION_MAJOR >= 18
125                                 ::llvm::CodeGenOptLevel::Default) };
126 #else
127                                 ::llvm::CodeGenOpt::Default) };
128 #endif
129       if (!tm)
130          fail(r_log, build_error(),
131               "Could not create TargetMachine: " + target.triple);
132 
133       ::llvm::SmallVector<char, 1024> data;
134 
135       {
136          ::llvm::legacy::PassManager pm;
137          ::llvm::raw_svector_ostream os { data };
138 
139          mod.setDataLayout(tm->createDataLayout());
140          tm->Options.MCOptions.AsmVerbose =
141             (ft == compat::CGFT_AssemblyFile);
142 
143          if (tm->addPassesToEmitFile(pm, os, nullptr, ft))
144             fail(r_log, build_error(), "TargetMachine can't emit this file");
145 
146          pm.run(mod);
147       }
148 
149       return { data.begin(), data.end() };
150    }
151 }
152 
153 binary
build_module_native(::llvm::Module & mod,const target & target,const clang::CompilerInstance & c,std::string & r_log)154 clover::llvm::build_module_native(::llvm::Module &mod, const target &target,
155                                   const clang::CompilerInstance &c,
156                                   std::string &r_log) {
157    const auto code = emit_code(mod, target,
158                                compat::CGFT_ObjectFile, r_log);
159    return build_module_common(mod, code, get_symbol_offsets(code, r_log), c);
160 }
161 
162 std::string
print_module_native(const::llvm::Module & mod,const target & target)163 clover::llvm::print_module_native(const ::llvm::Module &mod,
164                                   const target &target) {
165    std::string log;
166    try {
167       std::unique_ptr< ::llvm::Module> cmod { ::llvm::CloneModule(mod) };
168       return as_string(emit_code(*cmod, target,
169                                  compat::CGFT_AssemblyFile, log));
170    } catch (...) {
171       return "Couldn't output native disassembly: " + log;
172    }
173 }
174 
175 #else
176 
177 binary
build_module_native(::llvm::Module & mod,const target & target,const clang::CompilerInstance & c,std::string & r_log)178 clover::llvm::build_module_native(::llvm::Module &mod, const target &target,
179                                   const clang::CompilerInstance &c,
180                                   std::string &r_log) {
181    unreachable("Native codegen support disabled at build time");
182 }
183 
184 std::string
print_module_native(const::llvm::Module & mod,const target & target)185 clover::llvm::print_module_native(const ::llvm::Module &mod,
186                                   const target &target) {
187    unreachable("Native codegen support disabled at build time");
188 }
189 
190 #endif
191