This patch is essentially an adaption of LLD's algorithm to JITLink. Currently, only relaxing R_RISCV_CALL(_PLT) and R_RISCV_ALIGN is implemented, other relocations can follow later. From a high level, the algorithm works as follows. In the first phase (relaxBlock), we iteratively try to relax all instructions that have a R_RISCV_RELAX relocation: - If, based on the current symbol values, an instruction sequence can be relaxed (i.e., replaced by a shorter instruction), we record how many bytes would be removed, the new instruction (Writes), and the new relocation type (EdgeKinds). - We keep track of the total number of bytes that got removed up to each relocation in the RelocDeltas array. This is the cumulative sum of the number of bytes removed for each relocation. - Symbol values and sizes are updated based on the number of removed bytes. - If for any relocation, the current RelocDeltas value doesn't match the one from the previous iteration, something changed and we need to run another iteration as some symbols might now have different values. In the second phase (finalizeBlockRelax), all code is moved based on RelocDeltas, the relaxed instructions are rewritten using Writes, and R_RISCV_ALIGN is handled (moving instructions to ensure alignment and inserting the correct NOP-sequence if needed). Finally, edge kinds and offsets are updated and all R_RISCV_RELAX and R_RISCV_ALIGN edges are removed (they are not needed anymore for the fixup linking stage). Linker relaxation is implemented as a pass and added to PreFixupPasses in the default configuration on RISC-V. Since linker relaxation removes instructions, the memory for blocks should ideally be reallocated. However, I believe this is currently not possible in JITLink. Therefore, relaxation directly modifies the memory of blocks, reducing the number of instructions but not the size of blocks. I'm not very familiar with JITLink's memory allocators so I might be overlooking something here, though. Note on testing: some of the tests rely on the debug output of llvm-jitlink. The main reason for this is the verification of symbol sizes (which may change due to relaxation). I don't believe this can be done using jitlink-check checks alone. Note that there is a slightly unrelated change that makes Symbol::setOffset public to be able to update symbol offsets during relaxation. Reviewed By: lhames Differential Revision: https://reviews.llvm.org/D149526
91 lines
2.4 KiB
C++
91 lines
2.4 KiB
C++
//===------ riscv.cpp - Generic JITLink riscv edge kinds, utilities -------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Generic utilities for graphs representing riscv objects.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ExecutionEngine/JITLink/riscv.h"
|
|
|
|
#define DEBUG_TYPE "jitlink"
|
|
|
|
namespace llvm {
|
|
namespace jitlink {
|
|
namespace riscv {
|
|
|
|
const char *getEdgeKindName(Edge::Kind K) {
|
|
switch (K) {
|
|
case R_RISCV_32:
|
|
return "R_RISCV_32";
|
|
case R_RISCV_64:
|
|
return "R_RISCV_64";
|
|
case R_RISCV_BRANCH:
|
|
return "R_RISCV_BRANCH";
|
|
case R_RISCV_JAL:
|
|
return "R_RISCV_JAL";
|
|
case R_RISCV_CALL:
|
|
return "R_RISCV_CALL";
|
|
case R_RISCV_CALL_PLT:
|
|
return "R_RISCV_CALL_PLT";
|
|
case R_RISCV_GOT_HI20:
|
|
return "R_RISCV_GOT_HI20";
|
|
case R_RISCV_PCREL_HI20:
|
|
return "R_RISCV_PCREL_HI20";
|
|
case R_RISCV_PCREL_LO12_I:
|
|
return "R_RISCV_PCREL_LO12_I";
|
|
case R_RISCV_PCREL_LO12_S:
|
|
return "R_RISCV_PCREL_LO12_S";
|
|
case R_RISCV_HI20:
|
|
return "R_RISCV_HI20";
|
|
case R_RISCV_LO12_I:
|
|
return "R_RISCV_LO12_I";
|
|
case R_RISCV_LO12_S:
|
|
return "R_RISCV_LO12_S";
|
|
case R_RISCV_ADD8:
|
|
return "R_RISCV_ADD8";
|
|
case R_RISCV_ADD16:
|
|
return "R_RISCV_ADD16";
|
|
case R_RISCV_ADD32:
|
|
return "R_RISCV_ADD32";
|
|
case R_RISCV_ADD64:
|
|
return "R_RISCV_ADD64";
|
|
case R_RISCV_SUB8:
|
|
return "R_RISCV_SUB8";
|
|
case R_RISCV_SUB16:
|
|
return "R_RISCV_SUB16";
|
|
case R_RISCV_SUB32:
|
|
return "R_RISCV_SUB32";
|
|
case R_RISCV_SUB64:
|
|
return "R_RISCV_SUB64";
|
|
case R_RISCV_RVC_BRANCH:
|
|
return "R_RISCV_RVC_BRANCH";
|
|
case R_RISCV_RVC_JUMP:
|
|
return "R_RISCV_RVC_JUMP";
|
|
case R_RISCV_SUB6:
|
|
return "R_RISCV_SUB6";
|
|
case R_RISCV_SET6:
|
|
return "R_RISCV_SET6";
|
|
case R_RISCV_SET8:
|
|
return "R_RISCV_SET8";
|
|
case R_RISCV_SET16:
|
|
return "R_RISCV_SET16";
|
|
case R_RISCV_SET32:
|
|
return "R_RISCV_SET32";
|
|
case R_RISCV_32_PCREL:
|
|
return "R_RISCV_32_PCREL";
|
|
case CallRelaxable:
|
|
return "CallRelaxable";
|
|
case AlignRelaxable:
|
|
return "AlignRelaxable";
|
|
}
|
|
return getGenericEdgeKindName(K);
|
|
}
|
|
} // namespace riscv
|
|
} // namespace jitlink
|
|
} // namespace llvm
|