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