// 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 # include # include # include 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 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 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