From a58b8f518a00eacb08245ad54e29cd2b8baf9e05 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Thu, 31 Oct 2024 01:29:44 +0100 Subject: [PATCH] [EraVM] Fix: handle duplicating library symbols There may be an arbitrary number of 'Linkersymbol' instructions with the same argument. Map them to the unique MCSymbol. --- lld/unittests/EraVM/LLDTest.cpp | 120 ++++++++++++++++++ llvm/lib/MC/MCC/AssemblerC.cpp | 5 - llvm/lib/Target/EraVM/EraVMAsmPrinter.cpp | 29 +++-- .../EraVM/no-linker-symbols-duplication.ll | 22 ++++ 4 files changed, 163 insertions(+), 13 deletions(-) create mode 100644 llvm/test/CodeGen/EraVM/no-linker-symbols-duplication.ll diff --git a/lld/unittests/EraVM/LLDTest.cpp b/lld/unittests/EraVM/LLDTest.cpp index 705e74d2bdc8..79a6c1cf774b 100644 --- a/lld/unittests/EraVM/LLDTest.cpp +++ b/lld/unittests/EraVM/LLDTest.cpp @@ -564,6 +564,126 @@ define i256 @test() { \n\ LLVMDisposeMemoryBuffer(BinMemBuffer); } +TEST_F(LLDCTest, IterativeLinkageRepeatedLibraries) { + StringRef LLVMIr = "\ +target datalayout = \"E-p:256:256-i256:256:256-S32-a:256:256\" \n\ +target triple = \"eravm\" \n\ +declare i256 @llvm.eravm.linkersymbol(metadata) \n\ + \n\ +define i256 @foo() { \n\ + %res = call i256 @llvm.eravm.linkersymbol(metadata !1) \n\ + %res2 = call i256 @llvm.eravm.linkersymbol(metadata !3) \n\ + %res3 = add i256 %res, %res2 \n\ + ret i256 %res3 \n\ +} \n\ + \n\ +define i256 @bar() { \n\ + %res = call i256 @llvm.eravm.linkersymbol(metadata !1) \n\ + %res2 = call i256 @llvm.eravm.linkersymbol(metadata !2) \n\ + %res3 = add i256 %res, %res2 \n\ + ret i256 %res3 \n\ +} \n\ + \n\ +!1 = !{!\"library_id\"} \n\ +!2 = !{!\"library_id\"} \n\ +!3 = !{!\"library_id2\"}"; + + // Wrap Source in a MemoryBuffer + LLVMMemoryBufferRef IrMemBuffer = LLVMCreateMemoryBufferWithMemoryRange( + LLVMIr.data(), LLVMIr.size(), "test", 1); + char *ErrMsg = nullptr; + LLVMModuleRef M; + if (LLVMParseIRInContext(Context, IrMemBuffer, &M, &ErrMsg)) { + FAIL() << "Failed to parse llvm ir:" << ErrMsg; + LLVMDisposeMessage(ErrMsg); + return; + } + + // Run CodeGen to produce the buffer. + LLVMMemoryBufferRef ObjMemBuffer; + if (LLVMTargetMachineEmitToMemoryBuffer(TM, M, LLVMObjectFile, &ErrMsg, + &ObjMemBuffer)) { + FAIL() << "Failed to compile llvm ir:" << ErrMsg; + LLVMDisposeModule(M); + LLVMDisposeMessage(ErrMsg); + return; + } + LLVMDisposeModule(M); + + EXPECT_TRUE(LLVMIsELFEraVM(ObjMemBuffer)); + + uint64_t NumUndefLinkerSymbols = 0; + const char *LinkerSymbols[2] = {"library_id", "library_id2"}; + const char LinkerSymbolVals[2][20] = { + {1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}, + {6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 11, 12, 13}}; + + char **UndefLinkerSymbols = + LLVMGetUndefinedLinkerSymbolsEraVM(ObjMemBuffer, &NumUndefLinkerSymbols); + EXPECT_TRUE(NumUndefLinkerSymbols == 2); + EXPECT_TRUE((std::strcmp(UndefLinkerSymbols[0], LinkerSymbols[0]) == 0) || + (std::strcmp(UndefLinkerSymbols[0], LinkerSymbols[1]) == 0)); + EXPECT_TRUE((std::strcmp(UndefLinkerSymbols[1], LinkerSymbols[0]) == 0) || + (std::strcmp(UndefLinkerSymbols[1], LinkerSymbols[1]) == 0)); + + LLVMDisposeUndefinedLinkerSymbolsEraVM(UndefLinkerSymbols, + NumUndefLinkerSymbols); + + // Pass only the first linker symbol. + LLVMMemoryBufferRef Obj2MemBuffer; + if (LLVMLinkEraVM(ObjMemBuffer, &Obj2MemBuffer, LinkerSymbols, + LinkerSymbolVals, 1, &ErrMsg)) { + FAIL() << "Failed to link:" << ErrMsg; + LLVMDisposeMessage(ErrMsg); + return; + } + + EXPECT_TRUE(LLVMIsELFEraVM(Obj2MemBuffer)); + UndefLinkerSymbols = + LLVMGetUndefinedLinkerSymbolsEraVM(Obj2MemBuffer, &NumUndefLinkerSymbols); + EXPECT_TRUE(NumUndefLinkerSymbols == 1); + EXPECT_TRUE(std::strcmp(UndefLinkerSymbols[0], LinkerSymbols[1]) == 0); + + LLVMDisposeUndefinedLinkerSymbolsEraVM(UndefLinkerSymbols, + NumUndefLinkerSymbols); + + // Pass only the second linker symbol. This time + // the linker should emit the final bytecode, as all the + // symbols are resolved. + LLVMMemoryBufferRef BinMemBuffer; + if (LLVMLinkEraVM(Obj2MemBuffer, &BinMemBuffer, &LinkerSymbols[1], + &LinkerSymbolVals[1], 1, &ErrMsg)) { + FAIL() << "Failed to link:" << ErrMsg; + LLVMDisposeMessage(ErrMsg); + return; + } + + { + LLVMMemoryBufferRef Bin2MemBuffer; + EXPECT_TRUE(LLVMLinkEraVM(BinMemBuffer, &Bin2MemBuffer, nullptr, nullptr, 0, + &ErrMsg)); + EXPECT_TRUE( + StringRef(ErrMsg).contains("Input binary is not an EraVM ELF file")); + LLVMDisposeMessage(ErrMsg); + } + + EXPECT_FALSE(LLVMIsELFEraVM(BinMemBuffer)); + UndefLinkerSymbols = + LLVMGetUndefinedLinkerSymbolsEraVM(BinMemBuffer, &NumUndefLinkerSymbols); + EXPECT_TRUE(NumUndefLinkerSymbols == 0); + + StringRef Val1(LinkerSymbolVals[0], 20); + StringRef Val2(LinkerSymbolVals[1], 20); + StringRef Binary(LLVMGetBufferStart(BinMemBuffer), + LLVMGetBufferSize(BinMemBuffer)); + EXPECT_TRUE(Binary.count(Val1) == 1); + EXPECT_TRUE(Binary.count(Val2) == 1); + EXPECT_TRUE(LLVMGetBufferSize(BinMemBuffer) % 64 == 32); + LLVMDisposeMemoryBuffer(ObjMemBuffer); + LLVMDisposeMemoryBuffer(Obj2MemBuffer); + LLVMDisposeMemoryBuffer(BinMemBuffer); +} + TEST_F(LLDCTest, LinkError) { StringRef LLVMIr = "\ target datalayout = \"E-p:256:256-i256:256:256-S32-a:256:256\" \n\ diff --git a/llvm/lib/MC/MCC/AssemblerC.cpp b/llvm/lib/MC/MCC/AssemblerC.cpp index 2e0a740582f5..acbedb107cc5 100644 --- a/llvm/lib/MC/MCC/AssemblerC.cpp +++ b/llvm/lib/MC/MCC/AssemblerC.cpp @@ -301,11 +301,6 @@ LLVMBool LLVMDisassembleEraVM(LLVMTargetMachineRef T, break; } -#ifndef NDEBUG - if (ConstantSectionStart != std::numeric_limits::max()) - assert(PC == ConstantSectionStart * WordSize); -#endif - while (PC + WordSize <= BytesNum) { uint64_t Word = PC / WordSize; assert(PC % WordSize == 0); diff --git a/llvm/lib/Target/EraVM/EraVMAsmPrinter.cpp b/llvm/lib/Target/EraVM/EraVMAsmPrinter.cpp index e4bc94a9b0ce..b3e1a2904be4 100644 --- a/llvm/lib/Target/EraVM/EraVMAsmPrinter.cpp +++ b/llvm/lib/Target/EraVM/EraVMAsmPrinter.cpp @@ -18,6 +18,7 @@ #include "MCTargetDesc/EraVMInstPrinter.h" #include "MCTargetDesc/EraVMTargetStreamer.h" #include "TargetInfo/EraVMTargetInfo.h" +#include "llvm/ADT/StringMap.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineConstantPool.h" @@ -60,6 +61,8 @@ class EraVMAsmPrinter : public AsmPrinter { std::map>; std::vector UniqueConstants; ConstantPoolMapType ConstantPoolMap; + // Maps linker symbol name to corresponding MCSymbol. + StringMap LinkerSymbolMap; public: EraVMAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) @@ -146,7 +149,7 @@ void EraVMAsmPrinter::emitInstruction(const MachineInstr *MI) { EmitToStreamer(*OutStreamer, TmpInst); } -// The LinkerSymbol instruction is lowered in the following: +// The LinkerSymbol instruction is lowered to the following: // // .rodata // .linker_symbol0: @@ -158,8 +161,13 @@ void EraVMAsmPrinter::emitInstruction(const MachineInstr *MI) { void EraVMAsmPrinter::emitLibraryAddressSymbol(const MachineInstr *MI) { MCInst MCI; MCI.setOpcode(EraVM::ADDcrr_s); + // Code reference. - MCSymbol *Label = OutContext.createNamedTempSymbol("linker_symbol"); + MCSymbol *LinkerSymbol = MI->getOperand(1).getMCSymbol(); + StringRef LinkerSymbolName = LinkerSymbol->getName(); + MCSymbol *Label = LinkerSymbolMap.contains(LinkerSymbolName) + ? LinkerSymbolMap[LinkerSymbolName] + : OutContext.createNamedTempSymbol("linker_symbol"); const MCExpr *LabelExpr = MCSymbolRefExpr::create(Label, OutContext); auto *TS = static_cast(OutStreamer->getTargetStreamer()); @@ -173,9 +181,13 @@ void EraVMAsmPrinter::emitLibraryAddressSymbol(const MachineInstr *MI) { MCI.addOperand(MCOperand::createImm(0)); EmitToStreamer(*OutStreamer, MCI); - MCSymbol *LinkerSymbol = MI->getOperand(1).getMCSymbol(); - StringRef SymbolName = LinkerSymbol->getName(); - std::string SymbolNameHash = EraVM::getLinkerSymbolHash(SymbolName); + // The linker symbol and the related section already exist, so just exit. + if (LinkerSymbolMap.contains(LinkerSymbolName)) + return; + + LinkerSymbolMap[LinkerSymbolName] = Label; + + std::string SymbolNameHash = EraVM::getLinkerSymbolHash(LinkerSymbolName); MCSymbol *LinkerSymbolHash = OutContext.getOrCreateSymbol(SymbolNameHash); // Emit the .rodata entry. @@ -188,13 +200,13 @@ void EraVMAsmPrinter::emitLibraryAddressSymbol(const MachineInstr *MI) { // Emit the .linker_symbol_name section that contains the actual symbol // name. - std::string LinkerSymbolSectionName = - EraVM::getLinkerSymbolSectionName(EraVM::getLinkerSymbolHash(SymbolName)); + std::string LinkerSymbolSectionName = EraVM::getLinkerSymbolSectionName( + EraVM::getLinkerSymbolHash(LinkerSymbolName)); MCSection *LinkerSymbolSection = OutContext.getELFSection( LinkerSymbolSectionName, ELF::SHT_PROGBITS, ELF::SHF_STRINGS); OutStreamer->switchSection(LinkerSymbolSection); - OutStreamer->emitBytes(SymbolName); + OutStreamer->emitBytes(LinkerSymbolName); OutStreamer->switchSection(CurrentSection); } @@ -270,6 +282,7 @@ void EraVMAsmPrinter::emitEndOfAsmFile(Module &) { // after emitting all the things, we also need to clear symbol cache UniqueConstants.clear(); ConstantPoolMap.clear(); + LinkerSymbolMap.clear(); } void EraVMAsmPrinter::insertSymbolToConstantMap(const Constant *C, diff --git a/llvm/test/CodeGen/EraVM/no-linker-symbols-duplication.ll b/llvm/test/CodeGen/EraVM/no-linker-symbols-duplication.ll new file mode 100644 index 000000000000..5322684e9c9e --- /dev/null +++ b/llvm/test/CodeGen/EraVM/no-linker-symbols-duplication.ll @@ -0,0 +1,22 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S32-a:256:256" +target triple = "eravm" + +declare i256 @llvm.eravm.linkersymbol(metadata) + +; CHECK: .linker_symbol0: +; CHECK-NOT: .linker_symbol1: + +define i256 @foo() { + %res = call i256 @llvm.eravm.linkersymbol(metadata !1) + ret i256 %res +} + +define i256 @bar() { + %res = call i256 @llvm.eravm.linkersymbol(metadata !2) + ret i256 %res +} + +!1 = !{!"library_id"} +!2 = !{!"library_id"}