blob: c4d831ab79ab18b3d95711bcbbde3841563976cd [file] [log] [blame] [edit]
// 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(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 << "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