|  | // Copyright 2020 The SwiftShader Authors. 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. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #include "LLVMAsm.hpp" | 
|  |  | 
|  | #ifdef ENABLE_RR_EMIT_ASM_FILE | 
|  |  | 
|  | #	include "Debug.hpp" | 
|  | #	include "llvm/IR/LegacyPassManager.h" | 
|  | #	include "llvm/Support/FileSystem.h" | 
|  | #	include <fstream> | 
|  | #	include <iomanip> | 
|  | #	include <regex> | 
|  | #	include <sstream> | 
|  |  | 
|  | namespace rr { | 
|  | namespace AsmFile { | 
|  |  | 
|  | std::string generateFilename(const std::string &emitDir, std::string routineName) | 
|  | { | 
|  | // Names from gtests sometimes have invalid file name characters | 
|  | std::replace(routineName.begin(), routineName.end(), '/', '_'); | 
|  |  | 
|  | static size_t counter = 0; | 
|  | std::stringstream f; | 
|  | f << emitDir << "reactor_jit_llvm_" << std::setfill('0') << std::setw(4) << counter++ << "_" << routineName << ".asm"; | 
|  | return f.str(); | 
|  | } | 
|  |  | 
|  | bool emitAsmFile(const std::string &filename, llvm::orc::JITTargetMachineBuilder builder, llvm::Module &module) | 
|  | { | 
|  | auto targetMachine = builder.createTargetMachine(); | 
|  | if(!targetMachine) | 
|  | return false; | 
|  |  | 
|  | auto fileType = llvm::CGFT_AssemblyFile; | 
|  | std::error_code EC; | 
|  | llvm::raw_fd_ostream dest(filename, EC, llvm::sys::fs::OF_None); | 
|  | ASSERT(!EC); | 
|  | llvm::legacy::PassManager pm; | 
|  | auto &options = targetMachine.get()->Options.MCOptions; | 
|  | options.ShowMCEncoding = true; | 
|  | options.AsmVerbose = true; | 
|  | targetMachine.get()->addPassesToEmitFile(pm, dest, nullptr, fileType); | 
|  | pm.run(module); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void fixupAsmFile(const std::string &filename, std::vector<const void *> addresses) | 
|  | { | 
|  | // Read input asm file into memory so we can overwrite it. This also allows us to merge multiline | 
|  | // comments into a single line for easier parsing below. | 
|  | std::vector<std::string> lines; | 
|  | { | 
|  | std::ifstream fin(filename); | 
|  | std::string line; | 
|  | while(std::getline(fin, line)) | 
|  | { | 
|  | auto firstChar = [&] { | 
|  | auto index = line.find_first_not_of(" \t"); | 
|  | if(index == std::string::npos) | 
|  | return '\n'; | 
|  | return line[index]; | 
|  | }; | 
|  |  | 
|  | if(!lines.empty() && firstChar() == '#') | 
|  | { | 
|  | lines.back() += line; | 
|  | } | 
|  | else | 
|  | { | 
|  | lines.push_back(line); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | std::ofstream fout(filename); | 
|  |  | 
|  | // Output function table | 
|  | fout << "\nFunction Addresses:\n"; | 
|  | for(size_t i = 0; i < addresses.size(); i++) | 
|  | { | 
|  | fout << "f" << i << ": " << addresses[i] << "\n"; | 
|  | } | 
|  | fout << "\n"; | 
|  |  | 
|  | size_t functionIndex = ~0; | 
|  | size_t instructionAddress = 0; | 
|  |  | 
|  | for(auto &line : lines) | 
|  | { | 
|  | size_t pos{}; | 
|  |  | 
|  | if(line.find("# -- Begin function") != std::string::npos) | 
|  | { | 
|  | ++functionIndex; | 
|  |  | 
|  | if(functionIndex < addresses.size()) | 
|  | { | 
|  | instructionAddress = (size_t)addresses[functionIndex]; | 
|  | } | 
|  | else | 
|  | { | 
|  | // For coroutines, more functions are compiled than the top-level three. | 
|  | // For now, just output 0-based instructions. | 
|  | instructionAddress = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Handle alignment directives by aligning the instruction address. When lowered, these actually | 
|  | // map to a nops to pad to the next aligned address. | 
|  | pos = line.find(".p2align"); | 
|  | if(pos != std::string::npos) | 
|  | { | 
|  | // This assumes GNU asm format (https://sourceware.org/binutils/docs/as/P2align.html#P2align) | 
|  | static std::regex reAlign(R"(.*\.p2align.*([0-9]+).*)"); | 
|  | std::smatch matches; | 
|  | auto found = std::regex_search(line, matches, reAlign); | 
|  | ASSERT(found); | 
|  | auto alignPow2 = std::stoi(matches[1]); | 
|  | auto align = 1 << alignPow2; | 
|  | instructionAddress = (instructionAddress + align - 1) & ~(align - 1); | 
|  | } | 
|  |  | 
|  | // Detect instruction lines and prepend the location (address) | 
|  | pos = line.find("encoding: ["); | 
|  | if(pos != std::string::npos) | 
|  | { | 
|  | // Determine offset of next instruction (size of this instruction in bytes) | 
|  | // e.g. # encoding: [0x48,0x89,0x4c,0x24,0x40] | 
|  | // Count number of commas in the array + 1 | 
|  | auto endPos = line.find("]", pos); | 
|  | auto instructionSize = 1 + std::count_if(line.begin() + pos, line.begin() + endPos, [](char c) { return c == ','; }); | 
|  |  | 
|  | // Prepend current location to instruction line | 
|  | std::stringstream location; | 
|  | location << "[0x" << std::uppercase << std::hex << instructionAddress << "] "; | 
|  | line = location.str() + line; | 
|  |  | 
|  | instructionAddress += instructionSize; | 
|  | } | 
|  |  | 
|  | fout << line + "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace AsmFile | 
|  | }  // namespace rr | 
|  |  | 
|  | #endif  // ENABLE_RR_EMIT_ASM_FILE |