1 // Copyright 2020 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "LLVMAsm.hpp"
16
17 #ifdef ENABLE_RR_EMIT_ASM_FILE
18
19 # include "Debug.hpp"
20 # include "llvm/IR/LegacyPassManager.h"
21 # include "llvm/Support/FileSystem.h"
22 # include <fstream>
23 # include <iomanip>
24 # include <regex>
25 # include <sstream>
26
27 namespace rr {
28 namespace AsmFile {
29
generateFilename(std::string routineName)30 std::string generateFilename(std::string routineName)
31 {
32 // Names from gtests sometimes have invalid file name characters
33 std::replace(routineName.begin(), routineName.end(), '/', '_');
34
35 static size_t counter = 0;
36 std::stringstream f;
37 f << "reactor_jit_llvm_" << std::setfill('0') << std::setw(4) << counter++ << "_" << routineName << ".asm";
38 return f.str();
39 }
40
emitAsmFile(const std::string & filename,llvm::orc::JITTargetMachineBuilder builder,llvm::Module & module)41 bool emitAsmFile(const std::string &filename, llvm::orc::JITTargetMachineBuilder builder, llvm::Module &module)
42 {
43 auto targetMachine = builder.createTargetMachine();
44 if(!targetMachine)
45 return false;
46
47 auto fileType = llvm::CGFT_AssemblyFile;
48 std::error_code EC;
49 llvm::raw_fd_ostream dest(filename, EC, llvm::sys::fs::OF_None);
50 ASSERT(!EC);
51 llvm::legacy::PassManager pm;
52 auto &options = targetMachine.get()->Options.MCOptions;
53 options.ShowMCEncoding = true;
54 options.AsmVerbose = true;
55 targetMachine.get()->addPassesToEmitFile(pm, dest, nullptr, fileType);
56 pm.run(module);
57 return true;
58 }
59
fixupAsmFile(const std::string & filename,std::vector<const void * > addresses)60 void fixupAsmFile(const std::string &filename, std::vector<const void *> addresses)
61 {
62 // Read input asm file into memory so we can overwrite it. This also allows us to merge multiline
63 // comments into a single line for easier parsing below.
64 std::vector<std::string> lines;
65 {
66 std::ifstream fin(filename);
67 std::string line;
68 while(std::getline(fin, line))
69 {
70 auto firstChar = [&] {
71 auto index = line.find_first_not_of(" \t");
72 if(index == std::string::npos)
73 return '\n';
74 return line[index];
75 };
76
77 if(!lines.empty() && firstChar() == '#')
78 {
79 lines.back() += line;
80 }
81 else
82 {
83 lines.push_back(line);
84 }
85 }
86 }
87
88 std::ofstream fout(filename);
89
90 // Output function table
91 fout << "\nFunction Addresses:\n";
92 for(size_t i = 0; i < addresses.size(); i++)
93 {
94 fout << "f" << i << ": " << addresses[i] << "\n";
95 }
96 fout << "\n";
97
98 size_t functionIndex = ~0;
99 size_t instructionAddress = 0;
100
101 for(auto &line : lines)
102 {
103 size_t pos{};
104
105 if(line.find("# -- Begin function") != std::string::npos)
106 {
107 ++functionIndex;
108
109 if(functionIndex < addresses.size())
110 {
111 instructionAddress = (size_t)addresses[functionIndex];
112 }
113 else
114 {
115 // For coroutines, more functions are compiled than the top-level three.
116 // For now, just output 0-based instructions.
117 instructionAddress = 0;
118 }
119 }
120
121 // Handle alignment directives by aligning the instruction address. When lowered, these actually
122 // map to a nops to pad to the next aligned address.
123 pos = line.find(".p2align");
124 if(pos != std::string::npos)
125 {
126 // This assumes GNU asm format (https://sourceware.org/binutils/docs/as/P2align.html#P2align)
127 static std::regex reAlign(R"(.*\.p2align.*([0-9]+).*)");
128 std::smatch matches;
129 auto found = std::regex_search(line, matches, reAlign);
130 ASSERT(found);
131 auto alignPow2 = std::stoi(matches[1]);
132 auto align = 1 << alignPow2;
133 instructionAddress = (instructionAddress + align - 1) & ~(align - 1);
134 }
135
136 // Detect instruction lines and prepend the location (address)
137 pos = line.find("encoding: [");
138 if(pos != std::string::npos)
139 {
140 // Determine offset of next instruction (size of this instruction in bytes)
141 // e.g. # encoding: [0x48,0x89,0x4c,0x24,0x40]
142 // Count number of commas in the array + 1
143 auto endPos = line.find("]", pos);
144 auto instructionSize = 1 + std::count_if(line.begin() + pos, line.begin() + endPos, [](char c) { return c == ','; });
145
146 // Prepend current location to instruction line
147 std::stringstream location;
148 location << "[0x" << std::uppercase << std::hex << instructionAddress << "] ";
149 line = location.str() + line;
150
151 instructionAddress += instructionSize;
152 }
153
154 fout << line + "\n";
155 }
156 }
157
158 } // namespace AsmFile
159 } // namespace rr
160
161 #endif // ENABLE_RR_EMIT_ASM_FILE
162