Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 86 additions & 57 deletions lib/Target/RISCV/RISCVLDBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,23 @@ void RISCVLDBackend::reportMissedRelaxation(StringRef Name,
recordRelaxationStats(Section, 0, NumBytes);
}

bool RISCVLDBackend::doRelaxationCall(Relocation *reloc, bool DoCompressed) {
bool RISCVLDBackend::doRelaxationCall(Relocation *reloc) {
/* Three similar relaxations can be applied here, in order of preference:
* -- auipc;jalr -> c.j/c.jal (saves 6 bytes)
* -- auipc;jalr -> jal (saves 4 bytes)
* -- auipc;jalr -> qc.e.j/qc.e.jal (saves 2 bytes) (Xqci only)
*/

Fragment *frag = reloc->targetRef()->frag();
RegionFragmentEx *region = llvm::dyn_cast<RegionFragmentEx>(frag);
if (!region)
return true;

Relocator::DWord S = getSymbolValuePLT(*reloc);
Relocator::DWord A = reloc->addend();
Relocator::DWord P = reloc->place(m_Module);
Relocator::DWord X = S + A - P;

uint64_t offset = reloc->targetRef()->offset();

// extract the next instruction
Expand All @@ -238,68 +250,87 @@ bool RISCVLDBackend::doRelaxationCall(Relocation *reloc, bool DoCompressed) {
return false;

unsigned rd = (jalr_instr >> 7) & 0x1fu;
bool canCompress = (rd == 0 || (rd == 1 && config().targets().is32Bits()));

// test if it can fall into 21bits
Relocator::DWord S = getSymbolValuePLT(*reloc);
Relocator::DWord A = reloc->addend();
Relocator::DWord P = reloc->place(m_Module);
Relocator::DWord X = S + A - P;
bool canRelax = config().options().getRISCVRelax() && llvm::isInt<21>(X);
// For `C.J`/`C.JAL`, we have to have C relaxations enabled,
// and be within 12 bits, and have a destination of `x0` or `ra`
bool canRelaxCJ = config().options().getRISCVRelax() &&
config().options().getRISCVRelaxToC() && isInt<12>(X) &&
(rd == 0 || (rd == 1 && config().targets().is32Bits()));

if (!canRelax) {
reportMissedRelaxation("RISCV_CALL", *region, offset, canCompress ? 6 : 4,
reloc->symInfo()->name());
return false;
// For `JAL`, the offset needs to be 21 bits.
bool canRelaxJal = config().options().getRISCVRelax() && llvm::isInt<21>(X);

// For `QC.E.J` and `QC.E.JAL`, we have to have xqci relaxations enabled,
// and be on 32-bit, and the destination must be either `x0` or `ra`.
bool canRelaxXqci = config().options().getRISCVRelax() &&
config().targets().is32Bits() &&
config().options().getRISCVRelaxXqci();
bool canRelaxQcEJ = canRelaxXqci && (rd == 0 || rd == 1);

const char *msgC = (rd == 1) ? "RISCV_CALL_JAL" : "RISCV_CALL_J";
if (canRelaxCJ) {
uint16_t c_j = (rd == 1) ? 0x2001u : 0xa001;

if (m_Module.getPrinter()->isVerbose())
config().raise(Diag::relax_to_compress)
<< msgC
<< llvm::utohexstr(reloc->target(), true, 8) + "," +
llvm::utohexstr(jalr_instr, true, 8)
<< llvm::utohexstr(c_j, true, 4) << reloc->symInfo()->name()
<< region->getOwningSection()->name() << llvm::utohexstr(offset)
<< region->getOwningSection()
->getInputFile()
->getInput()
->decoratedPath();

region->replaceInstruction(offset, reloc, c_j, 2);
reloc->setTargetData(c_j);
reloc->setType(llvm::ELF::R_RISCV_RVC_JUMP);
relaxDeleteBytes("RISCV_CALL_C", *region, offset + 2, 6,
reloc->symInfo()->name());

return true;
}

// Test if we can use C.JAL or C.J.
if (canCompress) {
bool canRelaxToCompressed = config().options().getRISCVRelax() &&
DoCompressed && llvm::isInt<12>(X);
if (canRelaxToCompressed) {
// C.J uses x0 as return register (writes are ignored)
// C.JAL uses x1 as return register (ABI link register)
uint32_t compressed = rd == 1 ? 0x2001 : 0xa001;
const char *msg = rd == 1 ? "RISCV_CALL_JAL" : "RISCV_CALL_J";
if (m_Module.getPrinter()->isVerbose())
config().raise(Diag::relax_to_compress)
<< msg
<< llvm::utohexstr(reloc->target(), true, 8) + "," +
llvm::utohexstr(jalr_instr, true, 8)
<< llvm::utohexstr(compressed, true, 4) << reloc->symInfo()->name()
<< region->getOwningSection()->name() << llvm::utohexstr(offset)
<< region->getOwningSection()
->getInputFile()
->getInput()
->decoratedPath();
if (canRelaxJal) {
// Replace the instruction to JAL
uint32_t jal = 0x6fu | rd << 7;

region->replaceInstruction(offset, reloc, compressed, 2);
// Replace the reloc to R_RISCV_RVC_JUMP
reloc->setType(llvm::ELF::R_RISCV_RVC_JUMP);
reloc->setTargetData(compressed);
// Delete the next instruction
relaxDeleteBytes("RISCV_CALL_C", *region, offset + 2, 6,
reloc->symInfo()->name());
return true;
}
region->replaceInstruction(offset, reloc, jal, 4 /* Replace bytes */);
reloc->setTargetData(jal);
reloc->setType(llvm::ELF::R_RISCV_JAL);
// Delete the next instruction
relaxDeleteBytes("RISCV_CALL", *region, offset + 4, 4,
reloc->symInfo()->name());

// Report missed relaxation as we could still do a `C.J`/`C.JAL`
reportMissedRelaxation("RISCV_CALL_C", *region, offset, 2,
reloc->symInfo()->name());

return true;
}

// Replace the instruction to JAL
uint32_t instr = 0x6fu | rd << 7;
region->replaceInstruction(offset, reloc, instr, 4 /* Replace bytes */);
// Replace the reloc to R_RISCV_JAL
reloc->setType(llvm::ELF::R_RISCV_JAL);
reloc->setTargetData(instr);
// Delete the next instruction
relaxDeleteBytes("RISCV_CALL", *region, offset + 4, 4,
reloc->symInfo()->name());
return true;
if (canRelaxQcEJ) {
uint64_t qc_e_j = (rd == 1) ? 0xc01ful : 0x401ful;
const char *msg =
(rd == 1) ? "R_RISCV_CALL_QC_E_JAL" : "R_RISCV_CALL_QC_E_J";

region->replaceInstruction(offset, reloc, qc_e_j, 6);
reloc->setTargetData(qc_e_j);
reloc->setType(ELF::riscv::internal::R_RISCV_QC_E_CALL_PLT);
relaxDeleteBytes(msg, *region, offset + 6, 2, reloc->symInfo()->name());

reportMissedRelaxation(msg, *region, offset, 4, reloc->symInfo()->name());

return true;
}

reportMissedRelaxation("RISCV_CALL", *region, offset, 6,
reloc->symInfo()->name());
return false;
}

bool RISCVLDBackend::doRelaxationQCCall(Relocation *reloc, bool DoCompressed) {
bool RISCVLDBackend::doRelaxationQCCall(Relocation *reloc) {
// This function performs the relaxation to replace: QC.E.JAL or QC.E.J with
// one of JAL, C.J, or C.JAL.

Expand All @@ -318,6 +349,7 @@ bool RISCVLDBackend::doRelaxationQCCall(Relocation *reloc, bool DoCompressed) {
Relocator::DWord P = reloc->place(m_Module);
Relocator::DWord X = S + A - P;

bool DoCompressed = config().options().getRISCVRelaxToC();
bool canRelaxXqci =
config().targets().is32Bits() && config().options().getRISCVRelaxXqci();
bool canRelax =
Expand Down Expand Up @@ -900,9 +932,6 @@ void RISCVLDBackend::mayBeRelax(int relaxation_pass, bool &pFinished) {
if (m_pGlobalPointer)
GP = m_pGlobalPointer->value();

// Compress
bool DoCompressed = config().options().getRISCVRelaxToC();

// start relocation relaxation
for (auto &input : m_Module.getObjectList()) {
ELFObjectFile *ObjFile = llvm::dyn_cast<ELFObjectFile>(input);
Expand Down Expand Up @@ -934,7 +963,7 @@ void RISCVLDBackend::mayBeRelax(int relaxation_pass, bool &pFinished) {
case llvm::ELF::R_RISCV_CALL:
case llvm::ELF::R_RISCV_CALL_PLT: {
if (nextRelax && relaxation_pass == RELAXATION_CALL)
doRelaxationCall(relocation, DoCompressed);
doRelaxationCall(relocation);
break;
}
case llvm::ELF::R_RISCV_PCREL_HI20:
Expand All @@ -959,7 +988,7 @@ void RISCVLDBackend::mayBeRelax(int relaxation_pass, bool &pFinished) {
}
case ELF::riscv::internal::R_RISCV_QC_E_CALL_PLT: {
if (nextRelax && relaxation_pass == RELAXATION_CALL)
doRelaxationQCCall(relocation, DoCompressed);
doRelaxationQCCall(relocation);
break;
}
case ELF::riscv::internal::R_RISCV_QC_E_32:
Expand Down
4 changes: 2 additions & 2 deletions lib/Target/RISCV/RISCVLDBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@ class RISCVLDBackend : public GNULDBackend {

bool isGOTReloc(Relocation *reloc) const;

bool doRelaxationCall(Relocation *R, bool DoCompressed);
bool doRelaxationQCCall(Relocation *R, bool DoCompressed);
bool doRelaxationCall(Relocation *R);
bool doRelaxationQCCall(Relocation *R);

bool doRelaxationLui(Relocation *R, Relocation::DWord G);
bool doRelaxationQCLi(Relocation *R, Relocation::DWord G);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ VERBOSE_RELAX: RISCV_CALL_C : Deleting 6 bytes for symbol 'foo' in section .text
DUMP_RELAX: b011 j 0x{{[0-9a-f]+}} <foo>

## The third jump is never compressed because it's too far
VERBOSE_RELAX: RISCV_CALL_C : Cannot relax 2 bytes for symbol 'foo' in section .text+0xff6 file {{.*}}.o
VERBOSE_RELAX: RISCV_CALL : Deleting 4 bytes for symbol 'foo' in section .text+0xffa file {{.*}}.o
VERBOSE_RELAX: RISCV_CALL_C : Cannot relax 2 bytes for symbol 'foo' in section .text+0xff6 file {{.*}}.o
DUMP_RELAX: 80aff06f j 0x{{[0-9a-f]+}} <foo>

RUN: %link %linkopts %t.o -o %t.2.out --verbose --no-relax 2>&1 | %filecheck %s --check-prefix=VERBOSE_NORELAX
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ VERBOSE_RELAX_C: RISCV_CALL_JAL : relaxing instruction 0x00000097,000080e7 to co
VERBOSE_RELAX_C: RISCV_CALL_C : Deleting 6 bytes for symbol 'f' in section .text+0x4 file {{.*}}.o
VERBOSE_RELAX_C: RISCV_CALL_JAL : relaxing instruction 0x00000097,000080e7 to compressed instruction 0x2001 for symbol f in section .text+0x77A file {{.*}}.o
VERBOSE_RELAX_C: RISCV_CALL_C : Deleting 6 bytes for symbol 'f' in section .text+0x77c file {{.*}}.o
VERBOSE_RELAX_C: RISCV_CALL_C : Cannot relax 2 bytes for symbol 'f' in section .text+0x874 file {{.*}}.o
VERBOSE_RELAX_C: RISCV_CALL : Deleting 4 bytes for symbol 'f' in section .text+0x878 file {{.*}}.o
VERBOSE_RELAX_C: RISCV_CALL_C : Cannot relax 2 bytes for symbol 'f' in section .text+0xffef0 file {{.*}}.o
VERBOSE_RELAX_C: RISCV_CALL_C : Cannot relax 2 bytes for symbol 'f' in section .text+0x874 file {{.*}}.o
VERBOSE_RELAX_C: RISCV_CALL : Deleting 4 bytes for symbol 'f' in section .text+0xffef4 file {{.*}}.o
VERBOSE_RELAX_C: RISCV_CALL_C : Cannot relax 2 bytes for symbol 'f' in section .text+0xffef0 file {{.*}}.o
VERBOSE_RELAX_C: RISCV_CALL : Cannot relax 6 bytes for symbol 'f' in section .text+0x1000ec file {{.*}}.o

RUN: %objdump -d %t.1.out 2>&1 | %filecheck %s --check-prefix=DUMP_RELAX_C
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ VERBOSE_RELAX_C: RISCV_CALL : Deleting 4 bytes for symbol 'f' in section .text+0
VERBOSE_RELAX_C: RISCV_CALL : Deleting 4 bytes for symbol 'f' in section .text+0x780 file {{.*}}.o
VERBOSE_RELAX_C: RISCV_CALL : Deleting 4 bytes for symbol 'f' in section .text+0x87c file {{.*}}.o
VERBOSE_RELAX_C: RISCV_CALL : Deleting 4 bytes for symbol 'f' in section .text+0xffef8 file {{.*}}.o
VERBOSE_RELAX_C: RISCV_CALL : Cannot relax 4 bytes for symbol 'f' in section .text+0x1000f0 file {{.*}}.o
VERBOSE_RELAX_C: RISCV_CALL : Cannot relax 6 bytes for symbol 'f' in section .text+0x1000f0 file {{.*}}.o
VERBOSE_RELAX_C-NOT: Cannot relax

RUN: %objdump -d %t.1.out 2>&1 | %filecheck %s --check-prefix=DUMP_RELAX_C
Expand All @@ -26,35 +26,37 @@ DUMP_RELAX_C: f10080e7 jalr
RUN: %filecheck %s --input-file=%t.1.map --check-prefix=MAP_RELAX_C
MAP_RELAX_C: # LinkStats Begin
MAP_RELAX_C: # RelaxationBytesDeleted : 16
MAP_RELAX_C: # RelaxationBytesMissed : 4
MAP_RELAX_C: # RelaxationBytesMissed : 14
MAP_RELAX_C: # LinkStats End

MAP_RELAX_C: .text {{.+}}, Alignment: 0x2, Flags: SHF_ALLOC|SHF_EXECINSTR, Type: SHT_PROGBITS
MAP_RELAX_C: # RelaxationBytesDeleted : 16
MAP_RELAX_C: # RelaxationBytesMissed : 4
MAP_RELAX_C: # RelaxationBytesMissed : 14
MAP_RELAX_C: .text {{.+}}.o #SHT_PROGBITS,SHF_ALLOC|SHF_EXECINSTR,2

# Only non-compressed relaxations are enabled.
RUN: %link %linkopts --no-relax-c %t.o -o %t.2.out -MapStyle txt -Map %t.2.map --verbose 2>&1 | %filecheck %s --check-prefix=VERBOSE_RELAX
VERBOSE_RELAX: RISCV_CALL : Cannot relax 4 bytes for symbol 'f' in section .text+0x1000f0 file {{.*}}.o
VERBOSE_RELAX: RISCV_CALL : Cannot relax 6 bytes for symbol 'f' in section .text+0x1000f0 file {{.*}}.o

RUN: %filecheck %s --input-file=%t.2.map --check-prefix=MAP_RELAX
MAP_RELAX: # LinkStats Begin
MAP_RELAX: # RelaxationBytesMissed : 4
MAP_RELAX: # RelaxationBytesDeleted : 16
MAP_RELAX: # RelaxationBytesMissed : 14
MAP_RELAX: # LinkStats End

MAP_RELAX: .text {{.+}}, Alignment: 0x2, Flags: SHF_ALLOC|SHF_EXECINSTR, Type: SHT_PROGBITS
MAP_RELAX: # RelaxationBytesMissed : 4
MAP_RELAX: # RelaxationBytesDeleted : 16
MAP_RELAX: # RelaxationBytesMissed : 14
MAP_RELAX: .text {{.+}}.o #SHT_PROGBITS,SHF_ALLOC|SHF_EXECINSTR,2

### Relaxations are disabled.
RUN: %link %linkopts --no-relax %t.o -o %t.3.out -MapStyle txt -Map %t.3.map

RUN: %filecheck %s --input-file=%t.3.map --check-prefix=MAP_NORELAX
MAP_NORELAX: # LinkStats Begin
MAP_NORELAX: # RelaxationBytesMissed : 20
MAP_NORELAX: # RelaxationBytesMissed : 30
MAP_NORELAX: # LinkStats End

MAP_NORELAX: .text {{.+}}, Alignment: 0x2, Flags: SHF_ALLOC|SHF_EXECINSTR, Type: SHT_PROGBITS
MAP_NORELAX: # RelaxationBytesMissed : 20
MAP_NORELAX: # RelaxationBytesMissed : 30
MAP_NORELAX: .text {{.+}}.o #SHT_PROGBITS,SHF_ALLOC|SHF_EXECINSTR,2
Loading
Loading