From dbfdfa816d0ccb7d3eac7a5f7f31e1a51a42ae9c Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko Date: Tue, 7 May 2024 11:39:39 +0300 Subject: [PATCH] [EraVM][ELF] Fix emission of jump tables The generic implementation tries to emit jump table entries as 256-bit pointers, which triggers an assertion in MCFixupKind::getKindForSize. This commit adds custom implementation of emitJumpTableInfo for EraVM, based on emitJumpTableInfo and emitJumpTableEntry functions from the AsmPrinter class that emits 16-bit labels and takes scaling by 8 into account. --- llvm/lib/Target/EraVM/EraVMAsmPrinter.cpp | 44 +++++++++++++ .../EraVM/MCTargetDesc/EraVMELFStreamer.cpp | 28 +++++++++ .../EraVM/MCTargetDesc/EraVMTargetStreamer.h | 3 + llvm/test/MC/EraVM/encoding/jump-table.ll | 61 +++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 llvm/test/MC/EraVM/encoding/jump-table.ll diff --git a/llvm/lib/Target/EraVM/EraVMAsmPrinter.cpp b/llvm/lib/Target/EraVM/EraVMAsmPrinter.cpp index 7d37775a3ad2..4fa8523adef4 100644 --- a/llvm/lib/Target/EraVM/EraVMAsmPrinter.cpp +++ b/llvm/lib/Target/EraVM/EraVMAsmPrinter.cpp @@ -22,6 +22,7 @@ #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" @@ -81,6 +82,7 @@ class EraVMAsmPrinter : public AsmPrinter { return MCInstLowering.lowerOperand(MO, MCOp); } + void emitJumpTableInfo() override; void emitConstantPool() override; void emitEndOfAsmFile(Module &) override; }; @@ -174,6 +176,48 @@ void EraVMAsmPrinter::emitInstruction(const MachineInstr *MI) { EmitToStreamer(*OutStreamer, TmpInst); } +void EraVMAsmPrinter::emitJumpTableInfo() { + // The default implementation would try to emit 256-bit fixup, so provide + // custom implementation based on emitJumpTableInfo and emitJumpTableEntry + // from AsmPrinter (the latter is not virtual) that emits 16-bit relocation + // and takes scaling into account. + + auto *TS = + static_cast(OutStreamer->getTargetStreamer()); + const DataLayout &DL = MF->getDataLayout(); + const MachineJumpTableInfo *MJTI = MF->getJumpTableInfo(); + if (!MJTI) + return; + assert(MJTI->getEntryKind() == MachineJumpTableInfo::EK_BlockAddress); + const std::vector &JT = MJTI->getJumpTables(); + if (JT.empty()) + return; + + // Switch section. + const Function &F = MF->getFunction(); + MCSection *Section = getObjFileLowering().getSectionForJumpTable(F, TM); + OutStreamer->switchSection(Section); + + emitAlignment(Align(MJTI->getEntryAlignment(DL))); + + for (unsigned JTI = 0, e = JT.size(); JTI != e; ++JTI) { + const std::vector &JTBBs = JT[JTI].MBBs; + + // If this jump table was deleted, ignore it. + if (JTBBs.empty()) + continue; + + OutStreamer->emitLabel(GetJTISymbol(JTI)); + + for (const MachineBasicBlock *MBB : JTBBs) { + assert(MBB && MBB->getNumber() >= 0 && "Invalid basic block"); + const MCExpr *Value = + MCSymbolRefExpr::create(MBB->getSymbol(), OutContext); + TS->emitJumpTarget(Value); + } + } +} + bool EraVMAsmPrinter::runOnMachineFunction(MachineFunction &MF) { SetupMachineFunction(MF); emitFunctionBody(); diff --git a/llvm/lib/Target/EraVM/MCTargetDesc/EraVMELFStreamer.cpp b/llvm/lib/Target/EraVM/MCTargetDesc/EraVMELFStreamer.cpp index 52b31b803779..1d4c049cc474 100644 --- a/llvm/lib/Target/EraVM/MCTargetDesc/EraVMELFStreamer.cpp +++ b/llvm/lib/Target/EraVM/MCTargetDesc/EraVMELFStreamer.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "EraVMFixupKinds.h" #include "EraVMMCTargetDesc.h" #include "EraVMTargetStreamer.h" #include "llvm/BinaryFormat/ELF.h" @@ -18,6 +19,7 @@ #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/Casting.h" using namespace llvm; @@ -28,6 +30,7 @@ class EraVMTargetELFStreamer : public EraVMTargetStreamer { MCELFStreamer &getStreamer(); EraVMTargetELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI); void emitCell(const APInt &Value) override; + void emitJumpTarget(const MCExpr *Expr) override; }; // This part is for ELF object output. @@ -40,6 +43,7 @@ class EraVMTargetAsmStreamer : public EraVMTargetStreamer { EraVMTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS, MCInstPrinter &InstPrinter, bool VerboseAsm); void emitCell(const APInt &Value) override; + void emitJumpTarget(const MCExpr *Expr) override; }; void EraVMTargetELFStreamer::emitCell(const APInt &Value) { @@ -49,6 +53,26 @@ void EraVMTargetELFStreamer::emitCell(const APInt &Value) { Streamer.emitIntValue(Value.sext(EraVM::CellBitWidth)); } +void EraVMTargetELFStreamer::emitJumpTarget(const MCExpr *Expr) { + // The code is similar to MCObjectStreamer::emitValueImpl, but takes the + // specifics of code labels into account: the instruction index is actually + // only 16 bits in size and is counted in 8-byte units. + + constexpr auto FK = static_cast(EraVM::fixup_16_scale_8); + auto &S = static_cast(Streamer); + + S.visitUsedExpr(*Expr); + + // Emit the placeholder. + emitCell(APInt::getZero(EraVM::CellBitWidth)); + + // Emit the fixup. + auto *DF = cast(S.getCurrentFragment()); + // Offset of the 16 least significant bits of 256-bit value. + unsigned Offset = DF->getContents().size() - 2; + DF->getFixups().push_back(MCFixup::create(Offset, Expr, FK)); +} + void EraVMTargetAsmStreamer::emitCell(const APInt &Value) { assert(Value.getBitWidth() <= EraVM::CellBitWidth); @@ -59,6 +83,10 @@ void EraVMTargetAsmStreamer::emitCell(const APInt &Value) { Streamer.emitRawText(OS.str()); } +void EraVMTargetAsmStreamer::emitJumpTarget(const MCExpr *Expr) { + Streamer.emitValue(Expr, EraVM::CellBitWidth / 8); +} + EraVMTargetAsmStreamer::EraVMTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS, MCInstPrinter &InstPrinter, diff --git a/llvm/lib/Target/EraVM/MCTargetDesc/EraVMTargetStreamer.h b/llvm/lib/Target/EraVM/MCTargetDesc/EraVMTargetStreamer.h index 6cd52f148f23..87c999a9ad0a 100644 --- a/llvm/lib/Target/EraVM/MCTargetDesc/EraVMTargetStreamer.h +++ b/llvm/lib/Target/EraVM/MCTargetDesc/EraVMTargetStreamer.h @@ -27,6 +27,9 @@ class EraVMTargetStreamer : public MCTargetStreamer { /// Emit .cell N (naturally-aligned 256-bit value). virtual void emitCell(const APInt &Value) {} + + /// Emit `.cell @tgt` where `@tgt` is an instruction address. + virtual void emitJumpTarget(const MCExpr *Expr) {} }; } // namespace llvm diff --git a/llvm/test/MC/EraVM/encoding/jump-table.ll b/llvm/test/MC/EraVM/encoding/jump-table.ll new file mode 100644 index 000000000000..ac495d7ea63d --- /dev/null +++ b/llvm/test/MC/EraVM/encoding/jump-table.ll @@ -0,0 +1,61 @@ +; RUN: llc < %s | FileCheck %s +; RUN: llc -filetype=obj -o %t.o < %s +; RUN: llvm-readelf --sections --relocs --syms %t.o | FileCheck --check-prefix=ELF %s + +target datalayout = "E-p:256:256-i8:256:256:256-i256:256:256-S32-a:256:256" +target triple = "eravm" + +define i256 @foo(i256 %arg) { +entry: + switch i256 %arg, label %default [ + i256 1, label %l1 + i256 2, label %l2 + i256 3, label %l3 + i256 4, label %l4 + ] +l1: + ret i256 123 +l2: + ret i256 234 +l3: + ret i256 345 +l4: + ret i256 456 +default: + ret i256 42 +} + +; CHECK: foo: +; Make sure raw values from the jump table are used as-is, so R_ERAVM_16_SCALE_8 +; is the right relocation to use for jump table entry emission. +; CHECK: jump.le @JTI0_0[r1] + +; CHECK: .rodata +; CHECK-NEXT: .p2align 5, 0x0 +; CHECK-NEXT:JTI0_0: +; CHECK-NEXT: .cell @.BB0_1 +; CHECK-NEXT: .cell @.BB0_2 +; CHECK-NEXT: .cell @.BB0_3 +; CHECK-NEXT: .cell @.BB0_4 + +; Capture the index of .rodata section +; ELF: Section Headers: +; ELF: [ [[RODATA:[0-9]+]]] .rodata + +; JTI0_0 is mentioned as a const operand of *some* instruction +; ELF: Relocation section '.rela.text' at offset {{0x[0-9a-f]+}} contains {{[0-9]+}} entries: +; ELF-NEXT: Offset Info Type Sym. Value Symbol's Name + Addend +; ELF: {{[0-9a-f]+}} 00000401 R_ERAVM_16_SCALE_32 00000000 .rodata + 0 + +; JTI0_0 is filled by R_ERAVM_16_SCALE_8 relocations +; ELF: Relocation section '.rela.rodata' at offset {{0x[0-9a-f]+}} contains 4 entries: +; ELF-NEXT: Offset Info Type Sym. Value Symbol's Name + Addend +; ELF-NEXT: 0000001e 00000202 R_ERAVM_16_SCALE_8 00000000 .text + 20 +; ELF-NEXT: 0000003e 00000202 R_ERAVM_16_SCALE_8 00000000 .text + 40 +; ELF-NEXT: 0000005e 00000202 R_ERAVM_16_SCALE_8 00000000 .text + 50 +; ELF-NEXT: 0000007e 00000202 R_ERAVM_16_SCALE_8 00000000 .text + 60 + +; JTI0_0 starts at zero offset inside .rodata, as expected by the above checks +; ELF: Symbol table '.symtab' contains {{[0-9]+}} entries: +; ELF: Num: Value Size Type Bind Vis Ndx Name +; ELF: {{[0-9]+}}: 00000000 0 NOTYPE LOCAL DEFAULT [[RODATA]] JTI0_0