diff --git a/clang/test/CodeGen/cheri/vaddr-mode.c b/clang/test/CodeGen/cheri/vaddr-mode.c index 9293f60a2e209..a1f61e8236f02 100644 --- a/clang/test/CodeGen/cheri/vaddr-mode.c +++ b/clang/test/CodeGen/cheri/vaddr-mode.c @@ -48,7 +48,9 @@ long write_uintcap(__uintcap_t *cap) { // OFFSET-NEXT: entry: // OFFSET-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[CAP:%.*]]) // OFFSET-NEXT: [[AND:%.*]] = and i64 [[TMP0]], 3 -// OFFSET-NEXT: ret i64 [[AND]] +// OFFSET-NEXT: [[TMP1:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[CAP]], i64 [[AND]]) +// OFFSET-NEXT: [[TMP2:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[TMP1]]) +// OFFSET-NEXT: ret i64 [[TMP2]] // long get_low_bits(__uintcap_t cap) { return cap & 3; @@ -126,7 +128,9 @@ __uintcap_t xor_uintcap(__uintcap_t cap, __uintcap_t cap2) { // OFFSET-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[CAP:%.*]]) // OFFSET-NEXT: [[TMP1:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[CAP2:%.*]]) // OFFSET-NEXT: [[XOR:%.*]] = xor i64 [[TMP1]], [[TMP0]] -// OFFSET-NEXT: ret i64 [[XOR]] +// OFFSET-NEXT: [[TMP2:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[CAP]], i64 [[XOR]]) +// OFFSET-NEXT: [[TMP3:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[TMP2]]) +// OFFSET-NEXT: ret i64 [[TMP3]] // long xor_uintcap_return_long(__uintcap_t cap, __uintcap_t cap2) { return cap ^ cap2; @@ -160,7 +164,9 @@ __uintcap_t modulo_return_uintcap(__uintcap_t cap) { // OFFSET-NEXT: entry: // OFFSET-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[CAP:%.*]]) // OFFSET-NEXT: [[REM:%.*]] = and i64 [[TMP0]], 31 -// OFFSET-NEXT: ret i64 [[REM]] +// OFFSET-NEXT: [[TMP1:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[CAP]], i64 [[REM]]) +// OFFSET-NEXT: [[TMP2:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[TMP1]]) +// OFFSET-NEXT: ret i64 [[TMP2]] // long modulo_return_long(__uintcap_t cap) { return cap % 32; diff --git a/lld/ELF/Arch/Cheri.cpp b/lld/ELF/Arch/Cheri.cpp index 72a862f9008c5..3bd18741918dd 100644 --- a/lld/ELF/Arch/Cheri.cpp +++ b/lld/ELF/Arch/Cheri.cpp @@ -17,6 +17,17 @@ using namespace llvm::ELF; namespace lld { namespace elf { +bool isCheriAbi(const InputFile *f) { + switch (f->emachine) { + case EM_MIPS: + return (f->eflags & EF_MIPS_ABI) == EF_MIPS_ABI_CHERIABI; + case EM_RISCV: + return f->eflags & EF_RISCV_CHERIABI; + default: + return false; + } +} + bool hasDynamicLinker(Ctx &ctx) { return ctx.arg.shared || ctx.arg.pie || !ctx.sharedFiles.empty(); } @@ -43,10 +54,10 @@ struct InMemoryCapRelocEntry { CapRelocUint permissions; }; -CheriCapRelocsSection::CheriCapRelocsSection(Ctx &ctx) - : SyntheticSection(ctx, "__cap_relocs", SHT_PROGBITS, +CheriCapRelocsSection::CheriCapRelocsSection(Ctx &ctx, StringRef name) + : SyntheticSection(ctx, name, SHT_PROGBITS, (ctx.arg.isPic && !ctx.arg.relativeCapRelocsOnly) - ? SHF_ALLOC | SHF_WRITE /* XXX: actually RELRO */ + ? SHF_ALLOC | SHF_WRITE : SHF_ALLOC, ctx.arg.wordsize) { this->entsize = ctx.arg.wordsize * 5; @@ -592,16 +603,14 @@ void CheriCapRelocsSection::writeTo(uint8_t *buf) { invokeELFT(writeToImpl, buf); } -CheriCapTableSection::CheriCapTableSection(Ctx &ctx) - : SyntheticSection( - ctx, ".captable", SHT_PROGBITS, - SHF_ALLOC | SHF_WRITE, /* XXX: actually RELRO for BIND_NOW*/ - ctx.arg.capabilitySize) { +MipsCheriCapTableSection::MipsCheriCapTableSection(Ctx &ctx) + : SyntheticSection(ctx, ".captable", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE, + ctx.arg.capabilitySize) { assert(ctx.arg.capabilitySize > 0); this->entsize = ctx.arg.capabilitySize; } -void CheriCapTableSection::writeTo(uint8_t *buf) { +void MipsCheriCapTableSection::writeTo(uint8_t *buf) { // Capability part should be filled with all zeros and crt_init_globals fills // it in. For the TLS part, assignValuesAndAddCapTableSymbols adds any static // relocations needed, and should be procesed by relocateAlloc. @@ -614,8 +623,8 @@ static Defined *findMatchingFunction(const InputSectionBase *isec, return isec->getEnclosingFunction(symOffset); } -CheriCapTableSection::CaptableMap & -CheriCapTableSection::getCaptableMapForFileAndOffset( +MipsCheriCapTableSection::CaptableMap & +MipsCheriCapTableSection::getCaptableMapForFileAndOffset( const InputSectionBase *isec, uint64_t offset) { if (LLVM_LIKELY(ctx.arg.capTableScope == CapTableScopePolicy::All)) return globalEntries; @@ -637,8 +646,9 @@ CheriCapTableSection::getCaptableMapForFileAndOffset( return globalEntries; } -void CheriCapTableSection::addEntry(Symbol &sym, RelExpr expr, - InputSectionBase *isec, uint64_t offset) { +void MipsCheriCapTableSection::addEntry(Symbol &sym, RelExpr expr, + InputSectionBase *isec, + uint64_t offset) { // FIXME: can this be called from multiple threads? CapTableIndex idx; idx.needsSmallImm = false; @@ -646,12 +656,12 @@ void CheriCapTableSection::addEntry(Symbol &sym, RelExpr expr, idx.firstUse = SymbolAndOffset(isec, offset); assert(!idx.firstUse->symOrSec.isNull()); switch (expr) { - case R_CHERI_CAPABILITY_TABLE_INDEX_SMALL_IMMEDIATE: - case R_CHERI_CAPABILITY_TABLE_INDEX_CALL_SMALL_IMMEDIATE: - idx.needsSmallImm = true; - break; - default: - break; + case R_MIPS_CHERI_CAPTAB_INDEX_SMALL_IMMEDIATE: + case R_MIPS_CHERI_CAPTAB_INDEX_CALL_SMALL_IMMEDIATE: + idx.needsSmallImm = true; + break; + default: + break; } // If the symbol is only ever referenced by the captable call relocations we // can emit a capability call relocation instead of a normal capability @@ -659,25 +669,25 @@ void CheriCapTableSection::addEntry(Symbol &sym, RelExpr expr, // not used as a function pointer and therefore does not need a unique // address (plt stub) across all DSOs. switch (expr) { - case R_CHERI_CAPABILITY_TABLE_INDEX_CALL: - case R_CHERI_CAPABILITY_TABLE_INDEX_CALL_SMALL_IMMEDIATE: - if (!sym.isFunc() && !sym.isUndefWeak()) { - CheriCapRelocLocation loc{isec, offset}; - std::string msg = "call relocation against non-function symbol " + - verboseToString(ctx, &sym, 0) + "\n>>> referenced by " + - loc.toString(ctx); - if (sym.isUndefined() && - ctx.arg.unresolvedSymbolsInShlib == UnresolvedPolicy::Ignore) { - // Don't fail the build for shared libraries unless - nonFatalWarning(msg); - } else { - warn(msg); - } + case R_MIPS_CHERI_CAPTAB_INDEX_CALL: + case R_MIPS_CHERI_CAPTAB_INDEX_CALL_SMALL_IMMEDIATE: + if (!sym.isFunc() && !sym.isUndefWeak()) { + CheriCapRelocLocation loc{isec, offset}; + std::string msg = "call relocation against non-function symbol " + + verboseToString(ctx, &sym, 0) + "\n>>> referenced by " + + loc.toString(ctx); + if (sym.isUndefined() && + ctx.arg.unresolvedSymbolsInShlib == UnresolvedPolicy::Ignore) { + // Don't fail the build for shared libraries unless + nonFatalWarning(msg); + } else { + warn(msg); } - idx.usedInCallExpr = true; - break; - default: - break; + } + idx.usedInCallExpr = true; + break; + default: + break; } CaptableMap &entries = getCaptableMapForFileAndOffset(isec, offset); if (ctx.arg.zCapTableDebug) { @@ -700,25 +710,25 @@ void CheriCapTableSection::addEntry(Symbol &sym, RelExpr expr, } } -void CheriCapTableSection::addDynTlsEntry(Symbol &sym) { +void MipsCheriCapTableSection::addDynTlsEntry(Symbol &sym) { dynTlsEntries.map.insert(std::make_pair(&sym, CapTableIndex())); } -void CheriCapTableSection::addTlsIndex() { +void MipsCheriCapTableSection::addTlsIndex() { dynTlsEntries.map.insert(std::make_pair(nullptr, CapTableIndex())); } -void CheriCapTableSection::addTlsEntry(Symbol &sym) { +void MipsCheriCapTableSection::addTlsEntry(Symbol &sym) { tlsEntries.map.insert(std::make_pair(&sym, CapTableIndex())); } -uint32_t CheriCapTableSection::getIndex(const Symbol &sym, - const InputSectionBase *isec, - uint64_t offset) const { +uint32_t MipsCheriCapTableSection::getIndex(const Symbol &sym, + const InputSectionBase *isec, + uint64_t offset) const { assert(valuesAssigned && "getIndex called before index assignment"); const CaptableMap &entries = - const_cast(this)->getCaptableMapForFileAndOffset( - isec, offset); + const_cast(this) + ->getCaptableMapForFileAndOffset(isec, offset); auto it = entries.map.find(const_cast(&sym)); assert(entries.firstIndex != std::numeric_limits::max() && "First index not set yet?"); @@ -730,21 +740,21 @@ uint32_t CheriCapTableSection::getIndex(const Symbol &sym, return *it->second.index - entries.firstIndex; } -uint32_t CheriCapTableSection::getDynTlsOffset(const Symbol &sym) const { +uint32_t MipsCheriCapTableSection::getDynTlsOffset(const Symbol &sym) const { assert(valuesAssigned && "getDynTlsOffset called before index assignment"); auto it = dynTlsEntries.map.find(const_cast(&sym)); assert(it != dynTlsEntries.map.end()); return *it->second.index * ctx.arg.wordsize; } -uint32_t CheriCapTableSection::getTlsIndexOffset() const { +uint32_t MipsCheriCapTableSection::getTlsIndexOffset() const { assert(valuesAssigned && "getTlsIndexOffset called before index assignment"); auto it = dynTlsEntries.map.find(nullptr); assert(it != dynTlsEntries.map.end()); return *it->second.index * ctx.arg.wordsize; } -uint32_t CheriCapTableSection::getTlsOffset(const Symbol &sym) const { +uint32_t MipsCheriCapTableSection::getTlsOffset(const Symbol &sym) const { assert(valuesAssigned && "getTlsOffset called before index assignment"); auto it = tlsEntries.map.find(const_cast(&sym)); assert(it != tlsEntries.map.end()); @@ -752,9 +762,9 @@ uint32_t CheriCapTableSection::getTlsOffset(const Symbol &sym) const { } template -uint64_t CheriCapTableSection::assignIndices(uint64_t startIndex, - CaptableMap &entries, - const Twine &symContext) { +uint64_t MipsCheriCapTableSection::assignIndices(uint64_t startIndex, + CaptableMap &entries, + const Twine &symContext) { // Usually StartIndex will be zero (one global captable) but if we are // compiling with per-file/per-function uint64_t smallEntryCount = 0; @@ -864,7 +874,7 @@ uint64_t CheriCapTableSection::assignIndices(uint64_t startIndex, ? ctx.in.relaPlt.get() : ctx.mainPart->relaDyn.get(); addCapabilityRelocation( - ctx, targetSym, elfCapabilityReloc, ctx.in.cheriCapTable.get(), off, + ctx, targetSym, elfCapabilityReloc, ctx.in.mipsCheriCapTable.get(), off, R_CHERI_CAPABILITY, 0, it.second.usedInCallExpr, [&]() { return ("\n>>> referenced by " + refName + "\n>>> first used in " + @@ -878,7 +888,7 @@ uint64_t CheriCapTableSection::assignIndices(uint64_t startIndex, } template -void CheriCapTableSection::assignValuesAndAddCapTableSymbols() { +void MipsCheriCapTableSection::assignValuesAndAddCapTableSymbols() { // First assign the global indices (which will usually be the only ones) uint64_t assignedEntries = assignIndices(0, globalEntries, ""); if (LLVM_UNLIKELY(ctx.arg.capTableScope != CapTableScopePolicy::All)) { @@ -961,7 +971,6 @@ void CheriCapTableSection::assignValuesAndAddCapTableSymbols() { this->relocations.push_back( {R_TPREL, ctx.target->symbolicRel, offset, 0, s}); else - // FIXME: casting to GotSection here is a massive hack!! ctx.mainPart->relaDyn->addAddendOnlyRelocIfNonPreemptible( ctx.target->tlsGotRel, *this, offset, *s, ctx.target->symbolicRel); } @@ -970,22 +979,22 @@ void CheriCapTableSection::assignValuesAndAddCapTableSymbols() { } template void -CheriCapTableSection::assignValuesAndAddCapTableSymbols(); +MipsCheriCapTableSection::assignValuesAndAddCapTableSymbols(); template void -CheriCapTableSection::assignValuesAndAddCapTableSymbols(); +MipsCheriCapTableSection::assignValuesAndAddCapTableSymbols(); template void -CheriCapTableSection::assignValuesAndAddCapTableSymbols(); +MipsCheriCapTableSection::assignValuesAndAddCapTableSymbols(); template void -CheriCapTableSection::assignValuesAndAddCapTableSymbols(); +MipsCheriCapTableSection::assignValuesAndAddCapTableSymbols(); -CheriCapTableMappingSection::CheriCapTableMappingSection(Ctx &ctx) +MipsCheriCapTableMappingSection::MipsCheriCapTableMappingSection(Ctx &ctx) : SyntheticSection(ctx, ".captable_mapping", SHT_PROGBITS, SHF_ALLOC, 8) { assert(ctx.arg.capabilitySize > 0); this->entsize = sizeof(CaptableMappingEntry); static_assert(sizeof(CaptableMappingEntry) == 24, ""); } -size_t CheriCapTableMappingSection::getSize() const { +size_t MipsCheriCapTableMappingSection::getSize() const { assert(ctx.arg.capTableScope != CapTableScopePolicy::All); if (!isNeeded()) return 0; size_t count = 0; @@ -1000,9 +1009,10 @@ size_t CheriCapTableMappingSection::getSize() const { return count * sizeof(CaptableMappingEntry); } -void CheriCapTableMappingSection::writeTo(uint8_t *buf) { +void MipsCheriCapTableMappingSection::writeTo(uint8_t *buf) { assert(ctx.arg.capTableScope != CapTableScopePolicy::All); - if (!ctx.in.cheriCapTable) return; + if (!ctx.in.mipsCheriCapTable) + return; if (!ctx.in.symTab) { error("Cannot write " + this->name + " without .symtab section!"); return; @@ -1015,14 +1025,14 @@ void CheriCapTableMappingSection::writeTo(uint8_t *buf) { for (const SymbolTableEntry &ste : ctx.in.symTab->getSymbols()) { Symbol *sym = ste.sym; if (!sym->isDefined() || !sym->isFunc()) continue; - const CheriCapTableSection::CaptableMap *capTableMap = nullptr; + const MipsCheriCapTableSection::CaptableMap *capTableMap = nullptr; if (ctx.arg.capTableScope == CapTableScopePolicy::Function) { - auto it = ctx.in.cheriCapTable->perFunctionEntries.find(sym); - if (it != ctx.in.cheriCapTable->perFunctionEntries.end()) + auto it = ctx.in.mipsCheriCapTable->perFunctionEntries.find(sym); + if (it != ctx.in.mipsCheriCapTable->perFunctionEntries.end()) capTableMap = &it->second; } else if (ctx.arg.capTableScope == CapTableScopePolicy::File) { - auto it = ctx.in.cheriCapTable->perFileEntries.find(sym->file); - if (it != ctx.in.cheriCapTable->perFileEntries.end()) + auto it = ctx.in.mipsCheriCapTable->perFileEntries.find(sym->file); + if (it != ctx.in.mipsCheriCapTable->perFileEntries.end()) capTableMap = &it->second; } else { llvm_unreachable("Invalid mode!"); diff --git a/lld/ELF/Arch/Cheri.h b/lld/ELF/Arch/Cheri.h index 20f31d54e1661..a846273cd2e61 100644 --- a/lld/ELF/Arch/Cheri.h +++ b/lld/ELF/Arch/Cheri.h @@ -77,7 +77,7 @@ struct CheriCapReloc { // FIXME: should de-template this class properly class CheriCapRelocsSection : public SyntheticSection { public: - CheriCapRelocsSection(Ctx &ctx); + CheriCapRelocsSection(Ctx &ctx, StringRef name); // Add a __cap_relocs section from in input object file template void addSection(InputSectionBase *s); @@ -186,9 +186,9 @@ class CheriCapRelocsSection : public SyntheticSection { // +---------------------------------------+ // TODO: TLS caps also need to be per file/function -class CheriCapTableSection : public SyntheticSection { +class MipsCheriCapTableSection : public SyntheticSection { public: - CheriCapTableSection(Ctx &ctx); + MipsCheriCapTableSection(Ctx &ctx); // InputFile and Offset is needed in order to implement per-file/per-function // tables void addEntry(Symbol &sym, RelExpr expr, InputSectionBase *isec, @@ -266,7 +266,7 @@ class CheriCapTableSection : public SyntheticSection { CaptableMap dynTlsEntries; CaptableMap tlsEntries; bool valuesAssigned = false; - friend class CheriCapTableMappingSection; + friend class MipsCheriCapTableMappingSection; }; // TODO: could shrink these to reduce size overhead but this is experimental @@ -280,13 +280,13 @@ struct CaptableMappingEntry { // Map from symbol vaddr -> captable subset so that RTLD can setup the correct // trampolines to initialize $cgp to the correct subset -class CheriCapTableMappingSection : public SyntheticSection { +class MipsCheriCapTableMappingSection : public SyntheticSection { public: - CheriCapTableMappingSection(Ctx &ctx); + MipsCheriCapTableMappingSection(Ctx &ctx); bool isNeeded() const override { if (ctx.arg.capTableScope == CapTableScopePolicy::All) return false; - return ctx.in.cheriCapTable && ctx.in.cheriCapTable->isNeeded(); + return ctx.in.mipsCheriCapTable && ctx.in.mipsCheriCapTable->isNeeded(); } void writeTo(uint8_t *buf) override; size_t getSize() const override; diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp index 090767de495a2..93d6eb9ed904d 100644 --- a/lld/ELF/Arch/Mips.cpp +++ b/lld/ELF/Arch/Mips.cpp @@ -23,7 +23,6 @@ template class MIPS final : public TargetInfo { public: MIPS(Ctx &); uint32_t calcEFlags() const override; - bool calcIsCheriAbi() const override; int getCapabilitySize() const override; RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; @@ -79,18 +78,6 @@ template uint32_t MIPS::calcEFlags() const { return calcMipsEFlags(ctx); } -template bool MIPS::calcIsCheriAbi() const { - bool isCheriAbi = (ctx.arg.eflags & EF_MIPS_ABI) == EF_MIPS_ABI_CHERIABI; - - if (ctx.arg.isCheriAbi && !ctx.objectFiles.empty() && !isCheriAbi) { - auto diag = Err(ctx); - diag << ctx.objectFiles.front() - << ": object file is non-CheriABI but emulation forces it"; - } - - return isCheriAbi; -} - template int MIPS::getCapabilitySize() const { // Compute the size of a CHERI capability based on the MIPS ABI flags: if ((ctx.arg.eflags & EF_MIPS_MACH) == EF_MIPS_MACH_CHERI128) @@ -134,7 +121,7 @@ RelExpr MIPS::getRelExpr(RelType type, const Symbol &s, case R_MICROMIPS_GPREL7_S2: return RE_MIPS_GOTREL; case R_MIPS_CHERI_CAPTABLEREL16: - return R_CHERI_CAPABILITY_TABLE_REL; + return R_MIPS_CHERI_CAPTAB_REL; case R_MIPS_26: case R_MICROMIPS_26_S1: return R_PLT; @@ -223,16 +210,16 @@ RelExpr MIPS::getRelExpr(RelType type, const Symbol &s, return R_CHERI_CAPABILITY; case R_MIPS_CHERI_CAPTAB_LO16: case R_MIPS_CHERI_CAPTAB_HI16: - return R_CHERI_CAPABILITY_TABLE_INDEX; + return R_MIPS_CHERI_CAPTAB_INDEX; case R_MIPS_CHERI_CAPCALL_LO16: case R_MIPS_CHERI_CAPCALL_HI16: - return R_CHERI_CAPABILITY_TABLE_INDEX_CALL; + return R_MIPS_CHERI_CAPTAB_INDEX_CALL; case R_MIPS_CHERI_CAPTAB_CLC11: case R_MIPS_CHERI_CAPTAB20: - return R_CHERI_CAPABILITY_TABLE_INDEX_SMALL_IMMEDIATE; + return R_MIPS_CHERI_CAPTAB_INDEX_SMALL_IMMEDIATE; case R_MIPS_CHERI_CAPCALL_CLC11: case R_MIPS_CHERI_CAPCALL20: - return R_CHERI_CAPABILITY_TABLE_INDEX_CALL_SMALL_IMMEDIATE; + return R_MIPS_CHERI_CAPTAB_INDEX_CALL_SMALL_IMMEDIATE; case R_MIPS_CHERI_CAPTAB_TLS_GD_LO16: case R_MIPS_CHERI_CAPTAB_TLS_GD_HI16: return R_MIPS_CHERI_CAPTAB_TLSGD; diff --git a/lld/ELF/Arch/MipsArchTree.cpp b/lld/ELF/Arch/MipsArchTree.cpp index c6919380cb93a..4f648b093e8be 100644 --- a/lld/ELF/Arch/MipsArchTree.cpp +++ b/lld/ELF/Arch/MipsArchTree.cpp @@ -491,35 +491,24 @@ static Mips::AFL_EXT cheriFlagsToAFL_EXT(uint64_t cheriFlags) { return Mips::AFL_EXT::AFL_EXT_NONE; } -void elf::checkMipsShlibCompatible(Ctx &ctx, InputFile *f, uint64_t inputCheriFlags, +void elf::checkMipsShlibCompatible(Ctx &ctx, InputFile *f, + uint64_t inputCheriFlags, uint64_t targetCheriFlags) { - const uint32_t targetABI = ctx.arg.eflags & (EF_MIPS_ABI | EF_MIPS_ABI2); assert(f->emachine == ctx.arg.emachine); - uint32_t abi = f->eflags & (EF_MIPS_ABI | EF_MIPS_ABI2); - // Mips can't link against cheriabi and the other way around - if ((ctx.arg.isCheriAbi && abi != EF_MIPS_ABI_CHERIABI) || - (!ctx.arg.isCheriAbi && abi == EF_MIPS_ABI_CHERIABI)) { - // assert(errorCount() && "Should have already caused an errors"); - // llvm_unreachable("Should have been checked earlier!"); - if (!errorCount()) - error(toStr(ctx, f) + ": ABI '" + getAbiName(abi) + - "' is incompatible with target ABI: " + getAbiName(targetABI)); - } else { - uint64_t inputCheriAbi = inputCheriFlags & DF_MIPS_CHERI_ABI_MASK; - uint64_t targetCheriAbi = targetCheriFlags & DF_MIPS_CHERI_ABI_MASK; - if (inputCheriAbi != targetCheriAbi) { - std::string msg = "target pure-capability ABI " + - getMipsIsaExtName(cheriFlagsToAFL_EXT(targetCheriAbi)) + - " is incompatible with linked shared library\n>>> " + - toStr(ctx, f) + " uses " + - getMipsIsaExtName(cheriFlagsToAFL_EXT(inputCheriAbi)); - // mixing legacy/non-legacy is an error, anything a warning - if (inputCheriAbi == DF_MIPS_CHERI_ABI_LEGACY || - targetCheriAbi == DF_MIPS_CHERI_ABI_LEGACY) - error(msg); - else - warn(msg); - } + uint64_t inputCheriAbi = inputCheriFlags & DF_MIPS_CHERI_ABI_MASK; + uint64_t targetCheriAbi = targetCheriFlags & DF_MIPS_CHERI_ABI_MASK; + if (inputCheriAbi != targetCheriAbi) { + std::string msg = "target pure-capability ABI " + + getMipsIsaExtName(cheriFlagsToAFL_EXT(targetCheriAbi)) + + " is incompatible with linked shared library\n>>> " + + toStr(ctx, f) + " uses " + + getMipsIsaExtName(cheriFlagsToAFL_EXT(inputCheriAbi)); + // mixing legacy/non-legacy is an error, anything a warning + if (inputCheriAbi == DF_MIPS_CHERI_ABI_LEGACY || + targetCheriAbi == DF_MIPS_CHERI_ABI_LEGACY) + error(msg); + else + warn(msg); } } diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp index c5ee6a3f4e1f9..f4e04222a2e62 100644 --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -34,7 +34,6 @@ class RISCV final : public TargetInfo { public: RISCV(Ctx &); uint32_t calcEFlags() const override; - bool calcIsCheriAbi() const override; int getCapabilitySize() const override; int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; void writeGotHeader(uint8_t *buf) const override; @@ -144,11 +143,16 @@ RISCV::RISCV(Ctx &ctx) : TargetInfo(ctx) { tlsOffsetRel = R_RISCV_TLS_DTPREL32; tlsGotRel = R_RISCV_TLS_TPREL32; } - gotRel = symbolicRel; + if (ctx.arg.isCheriAbi) + gotRel = *cheriCapRel; + else + gotRel = symbolicRel; tlsDescRel = R_RISCV_TLSDESC; // .got[0] = _DYNAMIC gotHeaderEntriesNum = 1; + if (ctx.arg.isCheriAbi) + gotEntrySize = getCapabilitySize(); // .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map gotPltHeaderEntriesNum = 2; @@ -205,16 +209,6 @@ uint32_t RISCV::calcEFlags() const { return target; } -bool RISCV::calcIsCheriAbi() const { - bool isCheriAbi = ctx.arg.eflags & EF_RISCV_CHERIABI; - - if (ctx.arg.isCheriAbi && !ctx.objectFiles.empty() && !isCheriAbi) - error(toStr(ctx, ctx.objectFiles.front()) + - ": object file is non-CheriABI but emulation forces it"); - - return isCheriAbi; -} - int64_t RISCV::getImplicitAddend(const uint8_t *buf, RelType type) const { switch (type) { default: @@ -316,9 +310,8 @@ void RISCV::writePlt(uint8_t *buf, const Symbol &sym, // nop uint32_t ptrload = ctx.arg.isCheriAbi ? ctx.arg.is64 ? CLC_128 : CLC_64 : ctx.arg.is64 ? LD : LW; - uint32_t entryva = ctx.arg.isCheriAbi - ? sym.getCapTableVA(ctx, ctx.in.plt.get(), 0) - : sym.getGotPltVA(ctx); + uint32_t entryva = + ctx.arg.isCheriAbi ? sym.getGotVA(ctx) : sym.getGotPltVA(ctx); uint32_t offset = entryva - pltEntryAddr; write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset))); write32le(buf + 4, itype(ptrload, X_T3, X_T3, lo12(offset))); @@ -402,25 +395,27 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s, return RE_RISCV_LEB128; case R_RISCV_CHERI_CAPABILITY: return R_CHERI_CAPABILITY; - case R_RISCV_CHERI_CAPTAB_PCREL_HI20: - return R_CHERI_CAPABILITY_TABLE_ENTRY_PC; - case R_RISCV_CHERI_TLS_IE_CAPTAB_PCREL_HI20: - return R_CHERI_CAPABILITY_TABLE_TLSIE_ENTRY_PC; - case R_RISCV_CHERI_TLS_GD_CAPTAB_PCREL_HI20: - return R_CHERI_CAPABILITY_TABLE_TLSGD_ENTRY_PC; - case R_RISCV_CHERIOT_COMPARTMENT_HI: - return isPCCRelative(ctx, loc, &s) ? R_PC : R_CHERIOT_COMPARTMENT_CGPREL_HI; - case R_RISCV_CHERIOT_COMPARTMENT_LO_I: - return R_CHERIOT_COMPARTMENT_CGPREL_LO_I; - case R_RISCV_CHERIOT_COMPARTMENT_LO_S: - return R_CHERIOT_COMPARTMENT_CGPREL_LO_S; - case R_RISCV_CHERIOT_COMPARTMENT_SIZE: - return R_CHERIOT_COMPARTMENT_SIZE; - default: - Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v - << ") against symbol " << &s; - return R_NONE; - } + // TODO: Deprecate and eventually remove these + case R_RISCV_CHERI_CAPTAB_PCREL_HI20: + return R_GOT_PC; + case R_RISCV_CHERI_TLS_IE_CAPTAB_PCREL_HI20: + return R_GOT_PC; + case R_RISCV_CHERI_TLS_GD_CAPTAB_PCREL_HI20: + return R_TLSGD_PC; + case R_RISCV_CHERIOT_COMPARTMENT_HI: + return isPCCRelative(ctx, loc, &s) ? R_PC + : R_CHERIOT_COMPARTMENT_CGPREL_HI; + case R_RISCV_CHERIOT_COMPARTMENT_LO_I: + return R_CHERIOT_COMPARTMENT_CGPREL_LO_I; + case R_RISCV_CHERIOT_COMPARTMENT_LO_S: + return R_CHERIOT_COMPARTMENT_CGPREL_LO_S; + case R_RISCV_CHERIOT_COMPARTMENT_SIZE: + return R_CHERIOT_COMPARTMENT_SIZE; + default: + Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v + << ") against symbol " << &s; + return R_NONE; + } } void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 6a9de427b8597..49b9414ff0a53 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -78,9 +78,9 @@ class SymbolTableBaseSection; class SymtabShndxSection; class SyntheticSection; -class CheriCapTableSection; +class MipsCheriCapTableSection; class CheriCapRelocsSection; -class CheriCapTableMappingSection; +class MipsCheriCapTableMappingSection; enum ELFKind : uint8_t { ELFNoneKind, @@ -200,6 +200,8 @@ class LinkerDriver { Ctx &ctx; void createFiles(llvm::opt::InputArgList &args); void inferMachineType(); + void inferIsCheriAbi(); + void inferIsCheriot(); template void link(llvm::opt::InputArgList &args); template void compileBitcodeFiles(bool skipLinkedOutput); bool tryAddFatLTOFile(MemoryBufferRef mb, StringRef archiveName, @@ -622,9 +624,9 @@ struct InStruct { std::unique_ptr gotPlt; std::unique_ptr igotPlt; std::unique_ptr relroPadding; - std::unique_ptr cheriCapTable; + std::unique_ptr mipsCheriCapTable; std::unique_ptr capRelocs; - std::unique_ptr cheriCapTableMapping; + std::unique_ptr mipsCheriCapTableMapping; std::unique_ptr armCmseSGSection; std::unique_ptr ppc64LongBranchTarget; std::unique_ptr mipsAbiFlags; @@ -711,7 +713,7 @@ struct Ctx : CommonLinkerContext { // _TLS_MODULE_BASE_ on targets that support TLSDESC. Defined *tlsModuleBase; - Defined *cheriCapabilityTable; + Defined *mipsCheriCapabilityTable; }; ElfSym sym{}; std::unique_ptr symtab; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index ec59278a7c400..d4fd12cd581b1 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -741,6 +741,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { return; inferMachineType(); + inferIsCheriAbi(); + inferIsCheriot(); setConfigs(ctx, args); checkOptions(ctx); if (errCount(ctx)) @@ -2330,6 +2332,36 @@ static uint64_t getMaxPageSize(Ctx &ctx, opt::InputArgList &args) { return val; } +// If -m did not force a CheriABI emulation, infer it from +// object files. +void LinkerDriver::inferIsCheriAbi() { + if (ctx.arg.isCheriAbi) + return; + + for (const auto &f : files) { + if (f->ekind == ELFNoneKind) + continue; + ctx.arg.isCheriAbi = isCheriAbi(f.get()); + return; + } +} + +// If -m did not force CHERIoT, infer it from +// object files. +void LinkerDriver::inferIsCheriot() { + if (ctx.arg.isCheriot) + return; + if (!ctx.arg.isCheriAbi) + return; + + for (const auto &f : files) { + if (f->ekind == ELFNoneKind) + continue; + ctx.arg.isCheriot = f->eflags & EF_RISCV_CHERIOT; + return; + } +} + // Parse -z common-page-size=. The default value is defined by // each target. static uint64_t getCommonPageSize(Ctx &ctx, opt::InputArgList &args) { @@ -3495,10 +3527,6 @@ template void LinkerDriver::link(opt::InputArgList &args) { setTarget(ctx); ctx.arg.eflags = ctx.target->calcEFlags(); - ctx.arg.isCheriAbi = ctx.target->calcIsCheriAbi(); - if (ctx.arg.isCheriAbi) - ctx.arg.isCheriot = ctx.arg.eflags & EF_RISCV_CHERIOT; - // maxPageSize (sometimes called abi page size) is the maximum page size that // the output can be run on. For example if the OS can use 4k or 64k page // sizes then maxPageSize must be 64k for the output to be useable on both. diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 69834959e72ac..32dd4151bf7f1 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -272,16 +272,21 @@ static bool isCompatible(Ctx &ctx, InputFile *file) { if (!file->isElf() && !isa(file)) return true; + bool onlyCheriAbi = false; if (file->ekind == ctx.arg.ekind && file->emachine == ctx.arg.emachine) { - if (ctx.arg.emachine != EM_MIPS) - return true; - if (isMipsN32Abi(ctx, *file) == ctx.arg.mipsN32Abi) - return true; + if (ctx.arg.emachine != EM_MIPS || + isMipsN32Abi(ctx, *file) == ctx.arg.mipsN32Abi) { + if (isCheriAbi(file) == ctx.arg.isCheriAbi) + return true; + onlyCheriAbi = true; + } } StringRef target = !ctx.arg.bfdname.empty() ? ctx.arg.bfdname : ctx.arg.emulation; - if (!target.empty()) { + // NB: Don't print the target for isCheriABI mismatches if it doesn't force + // it, since it's a valid target for both. + if (!target.empty() && (!onlyCheriAbi || target.contains("_cheri"))) { Err(ctx) << file << " is incompatible with " << target; return false; } diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index ffe2d7408eae7..7fc673eb3ba43 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -1014,42 +1014,28 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r, return ctx.in.got->getTlsIndexVA() + a - p; case R_CHERI_CAPABILITY: llvm_unreachable("R_CHERI_CAPABILITY should not be handled here!"); - case R_CHERI_CAPABILITY_TABLE_INDEX: - case R_CHERI_CAPABILITY_TABLE_INDEX_SMALL_IMMEDIATE: - case R_CHERI_CAPABILITY_TABLE_INDEX_CALL: - case R_CHERI_CAPABILITY_TABLE_INDEX_CALL_SMALL_IMMEDIATE: + case R_MIPS_CHERI_CAPTAB_INDEX: + case R_MIPS_CHERI_CAPTAB_INDEX_SMALL_IMMEDIATE: + case R_MIPS_CHERI_CAPTAB_INDEX_CALL: + case R_MIPS_CHERI_CAPTAB_INDEX_CALL_SMALL_IMMEDIATE: assert(a == 0 && "capability table index relocs should not have addends"); - return r.sym->getCapTableOffset(ctx, this, r.offset); - case R_CHERI_CAPABILITY_TABLE_ENTRY_PC: { - assert(a == 0 && "capability table entry relocs should not have addends"); - return r.sym->getCapTableVA(ctx, this, r.offset) - p; - } - case R_CHERI_CAPABILITY_TABLE_TLSGD_ENTRY_PC: { - assert(a == 0 && "capability table index relocs should not have addends"); - uint64_t capTableOffset = ctx.in.cheriCapTable->getDynTlsOffset(*r.sym); - return ctx.sym.cheriCapabilityTable->getVA(ctx) + capTableOffset - p; - } - case R_CHERI_CAPABILITY_TABLE_TLSIE_ENTRY_PC: { - assert(a == 0 && "capability table index relocs should not have addends"); - uint64_t capTableOffset = ctx.in.cheriCapTable->getTlsOffset(*r.sym); - return ctx.sym.cheriCapabilityTable->getVA(ctx) + capTableOffset - p; - } - case R_CHERI_CAPABILITY_TABLE_REL: - if (!ctx.sym.cheriCapabilityTable) { + return r.sym->getMipsCheriCapTableOffset(ctx, this, r.offset); + case R_MIPS_CHERI_CAPTAB_REL: + if (!ctx.sym.mipsCheriCapabilityTable) { error("cannot compute difference between non-existent " "CheriCapabilityTable and symbol " + toStr(ctx, *r.sym)); return r.sym->getVA(ctx, a); } - return r.sym->getVA(ctx, a) - ctx.sym.cheriCapabilityTable->getVA(ctx); + return r.sym->getVA(ctx, a) - ctx.sym.mipsCheriCapabilityTable->getVA(ctx); case R_MIPS_CHERI_CAPTAB_TLSGD: assert(a == 0 && "capability table index relocs should not have addends"); - return ctx.in.cheriCapTable->getDynTlsOffset(*r.sym); + return ctx.in.mipsCheriCapTable->getDynTlsOffset(*r.sym); case R_MIPS_CHERI_CAPTAB_TLSLD: assert(a == 0 && "capability table index relocs should not have addends"); - return ctx.in.cheriCapTable->getTlsIndexOffset(); + return ctx.in.mipsCheriCapTable->getTlsIndexOffset(); case R_MIPS_CHERI_CAPTAB_TPREL: assert(a == 0 && "capability table index relocs should not have addends"); - return ctx.in.cheriCapTable->getTlsOffset(*r.sym); + return ctx.in.mipsCheriCapTable->getTlsOffset(*r.sym); // LO_I is used for both PCC and CGP-relative addresses. For backwards // compatibility, the symbol may be a CGP-relative symbol. In newer code, it // will always be the symbol containing the accompanying HI relocation. diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index d0683f904caef..267b4091c9b99 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -217,7 +217,7 @@ static bool isRelExpr(RelExpr expr) { return oneof(expr); + RE_LOONGARCH_PAGE_PC, R_MIPS_CHERI_CAPTAB_REL>(expr); } static RelExpr toPlt(RelExpr expr) { @@ -923,20 +923,21 @@ template static void addPltEntry(Ctx &ctx, PltSection &plt, GotPltSection &gotPlt, RelocationBaseSection &rel, RelType type, Symbol &sym) { plt.addEntry(sym); - if (ctx.arg.isCheriAbi) { - // TODO: More normal .got.plt rather than piggy-backing on .captable. We - // pass R_CHERI_CAPABILITY_TABLE_INDEX rather than the more obvious - // R_CHERI_CAPABILITY_TABLE_INDEX_CALL to force dynamic relocations into - // .rela.dyn rather than .rela.plt so no rtld changes are needed, as the - // latter doesn't really achieve anything without lazy binding. - ctx.in.cheriCapTable->addEntry(sym, R_CHERI_CAPABILITY_TABLE_INDEX, &plt, 0); - } else { - gotPlt.addEntry(sym); - rel.addReloc({type, &gotPlt, sym.getGotPltOffset(ctx), - sym.isPreemptible ? DynamicReloc::AgainstSymbol - : DynamicReloc::AddendOnlyWithTargetVA, - sym, 0, R_ABS}); - } + + // For CHERI-RISC-V we mark the symbol NEEDS_GOT so it will end up in .got as + // a function pointer, and uses .rela.dyn rather than .rela.plt, so no rtld + // changes are needed. + // + // TODO: More normal .got.plt with lazy-binding rather than piggy-backing on + // .got once rtld supports it. + if (ctx.arg.emachine == EM_RISCV && ctx.arg.isCheriAbi) + return; + + gotPlt.addEntry(sym); + rel.addReloc({type, &gotPlt, sym.getGotPltOffset(ctx), + sym.isPreemptible ? DynamicReloc::AgainstSymbol + : DynamicReloc::AddendOnlyWithTargetVA, + sym, 0, R_ABS}); } void elf::addGotEntry(Ctx &ctx, Symbol &sym) { @@ -952,8 +953,12 @@ void elf::addGotEntry(Ctx &ctx, Symbol &sym) { } // Otherwise, the value is either a link-time constant or the load base - // plus a constant. - if (!ctx.arg.isPic || isAbsolute(sym)) + // plus a constant. For CHERI it always requires run-time initialisation. + if (ctx.arg.isCheriAbi) { + invokeELFT(addCapabilityRelocation, ctx, &sym, *ctx.target->cheriCapRel, + ctx.in.got.get(), off, R_CHERI_CAPABILITY, 0, false, + [] { return ""; }); + } else if (!ctx.arg.isPic || isAbsolute(sym)) ctx.in.got->addConstant({R_ABS, ctx.target->symbolicRel, off, 0, &sym}); else addRelativeReloc(ctx, *ctx.in.got, off, sym, 0, R_ABS, @@ -1031,17 +1036,13 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type, R_GOTPLT_GOTREL, R_GOTPLT_PC, RE_PPC32_PLTREL, RE_PPC64_CALL_PLT, RE_PPC64_RELAX_TOC, RE_RISCV_ADD, RE_AARCH64_GOT_PAGE, RE_AARCH64_AUTH_GOT, RE_AARCH64_AUTH_GOT_PC, RE_LOONGARCH_PLT_PAGE_PC, - RE_LOONGARCH_GOT, RE_LOONGARCH_GOT_PAGE_PC, - R_CHERI_CAPABILITY_TABLE_INDEX, - R_CHERI_CAPABILITY_TABLE_INDEX_SMALL_IMMEDIATE, - R_CHERI_CAPABILITY_TABLE_INDEX_CALL, - R_CHERI_CAPABILITY_TABLE_INDEX_CALL_SMALL_IMMEDIATE, - R_CHERI_CAPABILITY_TABLE_ENTRY_PC, - R_CHERI_CAPABILITY_TABLE_REL, - R_CHERIOT_COMPARTMENT_CGPREL_HI, - R_CHERIOT_COMPARTMENT_CGPREL_LO_I, - R_CHERIOT_COMPARTMENT_CGPREL_LO_S, - R_CHERIOT_COMPARTMENT_SIZE>(e)) + RE_LOONGARCH_GOT, RE_LOONGARCH_GOT_PAGE_PC, R_MIPS_CHERI_CAPTAB_INDEX, + R_MIPS_CHERI_CAPTAB_INDEX_SMALL_IMMEDIATE, + R_MIPS_CHERI_CAPTAB_INDEX_CALL, + R_MIPS_CHERI_CAPTAB_INDEX_CALL_SMALL_IMMEDIATE, + R_MIPS_CHERI_CAPTAB_REL, R_CHERIOT_COMPARTMENT_CGPREL_HI, + R_CHERIOT_COMPARTMENT_CGPREL_LO_I, R_CHERIOT_COMPARTMENT_CGPREL_LO_S, + R_CHERIOT_COMPARTMENT_SIZE>(e)) return true; // Cheri capability relocations are never static link time constants since @@ -1158,25 +1159,12 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, return; } - if (oneof(expr)) { + if (oneof(expr)) { std::lock_guard lock(ctx.relocMutex); - ctx.in.cheriCapTable->addEntry(sym, expr, sec, offset); - // Write out the index into the instruction - sec->relocations.push_back({expr, type, offset, addend, &sym}); - return; - } - - if (oneof(expr)) { - std::lock_guard lock(ctx.relocMutex); - ctx.in.cheriCapTable->addEntry(sym, expr, sec, offset); + ctx.in.mipsCheriCapTable->addEntry(sym, expr, sec, offset); // Write out the index into the instruction sec->relocations.push_back({expr, type, offset, addend, &sym}); return; @@ -1203,6 +1191,9 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, } } else if (needsPlt(expr)) { sym.setFlags(NEEDS_PLT); + // See addPltEntry + if (ctx.arg.emachine == EM_RISCV && ctx.arg.isCheriAbi) + sym.setFlags(NEEDS_GOT); } else if (LLVM_UNLIKELY(isIfunc)) { sym.setFlags(HAS_DIRECT_RELOC); } @@ -1359,6 +1350,9 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, printLocation(diag, *sec, sym, offset); } sym.setFlags(NEEDS_COPY | NEEDS_PLT); + // See addPltEntry + if (ctx.arg.emachine == EM_RISCV && ctx.arg.isCheriAbi) + sym.setFlags(NEEDS_GOT); sec->addReloc({expr, type, offset, addend, &sym}); return; } @@ -1394,17 +1388,17 @@ static unsigned handleMipsTlsRelocation(Ctx &ctx, RelType type, Symbol &sym, return 1; } if (expr == R_MIPS_CHERI_CAPTAB_TLSLD) { - ctx.in.cheriCapTable->addTlsIndex(); + ctx.in.mipsCheriCapTable->addTlsIndex(); c.relocations.push_back({expr, type, offset, addend, &sym}); return 1; } if (expr == R_MIPS_CHERI_CAPTAB_TLSGD) { - ctx.in.cheriCapTable->addDynTlsEntry(sym); + ctx.in.mipsCheriCapTable->addDynTlsEntry(sym); c.relocations.push_back({expr, type, offset, addend, &sym}); return 1; } if (expr == R_MIPS_CHERI_CAPTAB_TPREL) { - ctx.in.cheriCapTable->addTlsEntry(sym); + ctx.in.mipsCheriCapTable->addTlsEntry(sym); c.relocations.push_back({expr, type, offset, addend, &sym}); return 1; } @@ -1501,36 +1495,6 @@ unsigned RelocationScanner::handleTlsRelocation(RelExpr expr, RelType type, !(isRISCV && expr != R_TLSDESC_PC && expr != R_TLSDESC_CALL) && !sec->file->ppc64DisableTLSRelax; - // No targets currently support TLS relaxation, so we can avoid duplicating - // much of the logic below for the captable. - if (expr == R_CHERI_CAPABILITY_TABLE_TLSGD_ENTRY_PC) { - std::lock_guard lock(ctx.relocMutex); - ctx.in.cheriCapTable->addDynTlsEntry(sym); - sec->relocations.push_back({expr, type, offset, addend, &sym}); - return 1; - } - if (expr == R_CHERI_CAPABILITY_TABLE_TLSIE_ENTRY_PC) { - std::lock_guard lock(ctx.relocMutex); - ctx.in.cheriCapTable->addTlsEntry(sym); - sec->relocations.push_back({expr, type, offset, addend, &sym}); - return 1; - } - - // No targets currently support TLS relaxation, so we can avoid duplicating - // much of the logic below for the captable. - if (expr == R_CHERI_CAPABILITY_TABLE_TLSGD_ENTRY_PC) { - std::lock_guard lock(ctx.relocMutex); - ctx.in.cheriCapTable->addDynTlsEntry(sym); - sec->relocations.push_back({expr, type, offset, addend, &sym}); - return 1; - } - if (expr == R_CHERI_CAPABILITY_TABLE_TLSIE_ENTRY_PC) { - std::lock_guard lock(ctx.relocMutex); - ctx.in.cheriCapTable->addTlsEntry(sym); - sec->relocations.push_back({expr, type, offset, addend, &sym}); - return 1; - } - // If we are producing an executable and the symbol is non-preemptable, it // must be defined and the code sequence can be optimized to use // Local-Exesec-> diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h index e1016758556f9..d64d2e235f817 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -122,14 +122,11 @@ enum RelExpr { RE_RISCV_ADD, RE_RISCV_LEB128, RE_RISCV_PC_INDIRECT, - R_CHERI_CAPABILITY_TABLE_INDEX, - R_CHERI_CAPABILITY_TABLE_INDEX_SMALL_IMMEDIATE, - R_CHERI_CAPABILITY_TABLE_INDEX_CALL, - R_CHERI_CAPABILITY_TABLE_INDEX_CALL_SMALL_IMMEDIATE, - R_CHERI_CAPABILITY_TABLE_ENTRY_PC, - R_CHERI_CAPABILITY_TABLE_TLSGD_ENTRY_PC, - R_CHERI_CAPABILITY_TABLE_TLSIE_ENTRY_PC, - R_CHERI_CAPABILITY_TABLE_REL, // relative offset to _CHERI_CAPABILITY_TABLE_ + R_MIPS_CHERI_CAPTAB_INDEX, + R_MIPS_CHERI_CAPTAB_INDEX_SMALL_IMMEDIATE, + R_MIPS_CHERI_CAPTAB_INDEX_CALL, + R_MIPS_CHERI_CAPTAB_INDEX_CALL_SMALL_IMMEDIATE, + R_MIPS_CHERI_CAPTAB_REL, // relative offset to _CHERI_CAPABILITY_TABLE_ R_MIPS_CHERI_CAPTAB_TLSGD, R_MIPS_CHERI_CAPTAB_TLSLD, R_MIPS_CHERI_CAPTAB_TPREL, diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index 9fa643dcbe14d..8271d1a5f2905 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -255,16 +255,17 @@ uint64_t Symbol::getPltVA(Ctx &ctx) const { return outVA; } -uint64_t Symbol::getCapTableVA(Ctx &ctx, const InputSectionBase *isec, - uint64_t offset) const { - return ctx.sym.cheriCapabilityTable->getVA(ctx) + - getCapTableOffset(ctx, isec, offset); +uint64_t Symbol::getMipsCheriCapTableVA(Ctx &ctx, const InputSectionBase *isec, + uint64_t offset) const { + return ctx.sym.mipsCheriCapabilityTable->getVA(ctx) + + getMipsCheriCapTableOffset(ctx, isec, offset); } -uint64_t Symbol::getCapTableOffset(Ctx &ctx, const InputSectionBase *isec, - uint64_t offset) const { +uint64_t Symbol::getMipsCheriCapTableOffset(Ctx &ctx, + const InputSectionBase *isec, + uint64_t offset) const { return ctx.arg.capabilitySize * - ctx.in.cheriCapTable->getIndex(*this, isec, offset); + ctx.in.mipsCheriCapTable->getIndex(*this, isec, offset); } uint64_t Defined::getSize(Ctx &ctx) const { diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index 843472b4d2cf1..ebf4325a0daf8 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -215,10 +215,11 @@ class Symbol { uint64_t getGotPltOffset(Ctx &) const; uint64_t getGotPltVA(Ctx &) const; uint64_t getPltVA(Ctx &) const; - uint64_t getCapTableVA(Ctx &ctx, const InputSectionBase *isec, - uint64_t offset) const; - uint64_t getCapTableOffset(Ctx &ctx, const InputSectionBase *isec, - uint64_t offset) const; + uint64_t getMipsCheriCapTableVA(Ctx &ctx, const InputSectionBase *isec, + + uint64_t offset) const; + uint64_t getMipsCheriCapTableOffset(Ctx &ctx, const InputSectionBase *isec, + uint64_t offset) const; uint64_t getSize(Ctx &ctx) const; OutputSection *getOutputSection() const; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 8ca05717afc5b..319dea0683a68 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -686,6 +686,9 @@ GotSection::GotSection(Ctx &ctx) void GotSection::addConstant(const Relocation &r) { relocations.push_back(r); } void GotSection::addEntry(const Symbol &sym) { + // TODO: Separate out TLS IE entries for CHERI so we can pack them more + // efficiently rather than consuming a whole capability-sized slot for an + // integer. assert(sym.auxIdx == ctx.symAux.size() - 1); ctx.symAux.back().gotIdx = numEntries++; } @@ -710,8 +713,12 @@ void GotSection::addTlsDescAuthEntry() { bool GotSection::addDynTlsEntry(const Symbol &sym) { assert(sym.auxIdx == ctx.symAux.size() - 1); ctx.symAux.back().tlsGdIdx = numEntries; - // Global Dynamic TLS entries take two GOT slots. - numEntries += 2; + // Global Dynamic TLS entries take two GOT slots, except on CHERI where they + // can be packed into one GOT slot. + if (ctx.arg.isCheriAbi) + ++numEntries; + else + numEntries += 2; return true; } @@ -1635,14 +1642,17 @@ DynamicSection::computeContents() { if (f->isNeeded) checkMipsShlibCompatible(ctx, f, f->cheriFlags, targetCheriFlags); } - if (ctx.in.cheriCapTable && ctx.in.cheriCapTable->isNeeded()) { - addInSec(DT_MIPS_CHERI_CAPTABLE, *ctx.in.cheriCapTable); - addInt(DT_MIPS_CHERI_CAPTABLESZ, ctx.in.cheriCapTable->getParent()->size); + if (ctx.in.mipsCheriCapTable && ctx.in.mipsCheriCapTable->isNeeded()) { + addInSec(DT_MIPS_CHERI_CAPTABLE, *ctx.in.mipsCheriCapTable); + addInt(DT_MIPS_CHERI_CAPTABLESZ, + ctx.in.mipsCheriCapTable->getParent()->size); } - if (ctx.in.cheriCapTableMapping && ctx.in.cheriCapTableMapping->isNeeded()) { - addInSec(DT_MIPS_CHERI_CAPTABLE_MAPPING, *ctx.in.cheriCapTableMapping); + if (ctx.in.mipsCheriCapTableMapping && + ctx.in.mipsCheriCapTableMapping->isNeeded()) { + addInSec(DT_MIPS_CHERI_CAPTABLE_MAPPING, + *ctx.in.mipsCheriCapTableMapping); addInt(DT_MIPS_CHERI_CAPTABLE_MAPPINGSZ, - ctx.in.cheriCapTableMapping->getParent()->size); + ctx.in.mipsCheriCapTableMapping->getParent()->size); } if (ctx.in.capRelocs && ctx.in.capRelocs->isNeeded()) { addInSec(DT_MIPS_CHERI___CAPRELOCS, *ctx.in.capRelocs); @@ -1791,10 +1801,10 @@ void RelocationBaseSection::finalizeContents() { if (ctx.in.relaPlt.get() == this && ctx.in.gotPlt->getParent()) { getParent()->flags |= ELF::SHF_INFO_LINK; // For CheriABI we use the captable as the sh_info value - if (ctx.arg.isCheriAbi && ctx.in.cheriCapTable && - ctx.in.cheriCapTable->isNeeded()) { - assert(ctx.in.cheriCapTable->getParent()->sectionIndex != UINT32_MAX); - getParent()->info = ctx.in.cheriCapTable->getParent()->sectionIndex; + if (ctx.arg.isCheriAbi && ctx.in.mipsCheriCapTable && + ctx.in.mipsCheriCapTable->isNeeded()) { + assert(ctx.in.mipsCheriCapTable->getParent()->sectionIndex != UINT32_MAX); + getParent()->info = ctx.in.mipsCheriCapTable->getParent()->sectionIndex; } else { getParent()->info = ctx.in.gotPlt->getParent()->sectionIndex; } @@ -4867,12 +4877,18 @@ template void elf::createSyntheticSections(Ctx &ctx) { add(*ctx.in.bssRelRo); if (ctx.arg.capabilitySize > 0) { - ctx.in.capRelocs = std::make_unique(ctx); - ctx.in.cheriCapTable = std::make_unique(ctx); - add(*ctx.in.cheriCapTable); - if (ctx.arg.capTableScope != CapTableScopePolicy::All) { - ctx.in.cheriCapTableMapping = std::make_unique(ctx); - add(*ctx.in.cheriCapTableMapping); + ctx.in.capRelocs = + std::make_unique(ctx, "__cap_relocs"); + + if (ctx.arg.emachine == EM_MIPS) { + ctx.in.mipsCheriCapTable = + std::make_unique(ctx); + add(*ctx.in.mipsCheriCapTable); + if (ctx.arg.capTableScope != CapTableScopePolicy::All) { + ctx.in.mipsCheriCapTableMapping = + std::make_unique(ctx); + add(*ctx.in.mipsCheriCapTableMapping); + } } } diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h index 5bad061a5ec59..f0a24973ec239 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -1426,8 +1426,8 @@ class PPC64LongBranchTargetSection final : public SyntheticSection { // Can only be forward declared here since it depends on SyntheticSection class CheriCapRelocsSection; -class CheriCapTableSection; -class CheriCapTableMappingSection; +class MipsCheriCapTableSection; +class MipsCheriCapTableMappingSection; template class PartitionElfHeaderSection final : public SyntheticSection { diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index 2d3ea59bd3854..ad7d57d30668d 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -117,13 +117,6 @@ ErrorPlace elf::getErrorPlace(Ctx &ctx, const uint8_t *loc) { TargetInfo::~TargetInfo() {} -bool TargetInfo::calcIsCheriAbi() const { - if (ctx.arg.isCheriAbi) - error("emulation forces CheriABI but not supported for the current target"); - - return false; -} - int64_t TargetInfo::getImplicitAddend(const uint8_t *buf, RelType type) const { InternalErr(ctx, buf) << "cannot read addend for relocation " << type; return 0; diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index 68c851294877f..45239d320f866 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -32,7 +32,6 @@ class TargetInfo { TargetInfo(Ctx &ctx) : ctx(ctx) {} virtual uint32_t calcEFlags() const { return 0; } virtual int getCapabilitySize() const { return 0; } - virtual bool calcIsCheriAbi() const; virtual RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const = 0; virtual RelType getDynRel(RelType type) const { return 0; } diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 0f7af3d8f03f4..f7fd98efda124 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -642,7 +642,8 @@ bool elf::isRelroSection(Ctx &ctx, const OutputSection *sec) { // Similarly the CHERI capability table is also relro since the capabilities // in the table need to be initialized at runtime to set the tag bits - if (ctx.in.cheriCapTable && sec == ctx.in.cheriCapTable->getParent()) { + if (ctx.in.mipsCheriCapTable && + sec == ctx.in.mipsCheriCapTable->getParent()) { // Without -z now, the PLT stubs can update the captable entries so we // can't mark it as relro. It can also be relro for static binaries: return ctx.arg.zNow || !ctx.arg.isPic; @@ -1910,13 +1911,13 @@ template void Writer::finalizeSections() { } } - if (ctx.in.cheriCapTable) { + if (ctx.arg.emachine == EM_MIPS && ctx.in.mipsCheriCapTable) { // When creating relocatable output we should not define the // _CHERI_CAPABILITY_TABLE_ symbol because otherwise we get duplicate // symbol errors when linking that into a final executable if (!ctx.arg.relocatable) - ctx.sym.cheriCapabilityTable = - addOptionalRegular(ctx, captableSym, ctx.in.cheriCapTable.get(), 0); + ctx.sym.mipsCheriCapabilityTable = addOptionalRegular( + ctx, captableSym, ctx.in.mipsCheriCapTable.get(), 0); } // This responsible for splitting up .eh_frame section into @@ -1962,18 +1963,19 @@ template void Writer::finalizeSections() { // Do the cap table index assignment // Must come before CapRelocs->finalizeContents() because it can add // __cap_relocs - if (ctx.in.cheriCapTable) { + if (ctx.in.mipsCheriCapTable) { // Ensure that we always have a _CHERI_CAPABILITY_TABLE_ symbol if the // cap table exists. This makes llvm-objdump more useful since it can now // print the target of a cap table load - if (!ctx.sym.cheriCapabilityTable && ctx.in.cheriCapTable->isNeeded()) { - ctx.sym.cheriCapabilityTable = cast( - ctx.symtab->addSymbol(Defined{ctx, nullptr, captableSym, STB_LOCAL, - STV_HIDDEN, STT_NOTYPE, 0, 0, ctx.in.cheriCapTable.get()})); - ctx.sym.cheriCapabilityTable->isSectionStartSymbol = true; - assert(!ctx.sym.cheriCapabilityTable->isPreemptible); + if (!ctx.sym.mipsCheriCapabilityTable && + ctx.in.mipsCheriCapTable->isNeeded()) { + ctx.sym.mipsCheriCapabilityTable = cast(ctx.symtab->addSymbol( + Defined{ctx, nullptr, captableSym, STB_LOCAL, STV_HIDDEN, + STT_NOTYPE, 0, 0, ctx.in.mipsCheriCapTable.get()})); + ctx.sym.mipsCheriCapabilityTable->isSectionStartSymbol = true; + assert(!ctx.sym.mipsCheriCapabilityTable->isPreemptible); } - ctx.in.cheriCapTable->assignValuesAndAddCapTableSymbols(); + ctx.in.mipsCheriCapTable->assignValuesAndAddCapTableSymbols(); } // Now handle __cap_relocs (must be before RelaDyn because it might @@ -2366,9 +2368,9 @@ template void Writer::addStartEndSymbols() { define("__fini_array_start", "__fini_array_end", ctx.out.finiArray); define("__ctors_start", "__ctors_end", findSection(ctx, ".ctors")); define("__dtors_start", "__dtors_end", findSection(ctx, ".dtors")); - if (ctx.in.cheriCapTable) + if (ctx.in.mipsCheriCapTable) define("__cap_table_start", "__cap_table_end", - ctx.in.cheriCapTable->getOutputSection()); + ctx.in.mipsCheriCapTable->getOutputSection()); // As a special case, don't unnecessarily retain .ARM.exidx, which would // create an empty PT_ARM_EXIDX. diff --git a/lld/ELF/Writer.h b/lld/ELF/Writer.h index a3695387722d3..c6f926b3f25dc 100644 --- a/lld/ELF/Writer.h +++ b/lld/ELF/Writer.h @@ -24,6 +24,7 @@ uint8_t getMipsIsaExt(uint64_t oldExt, llvm::StringRef oldFile, uint64_t newExt, llvm::StringRef newFile); void checkMipsShlibCompatible(Ctx &ctx, InputFile *f, uint64_t shlibCheriFlags, uint64_t targetCheriFlags); +bool isCheriAbi(const InputFile *f); bool isRelroSection(Ctx &ctx, const OutputSection *sec); } // namespace lld::elf diff --git a/lld/test/ELF/cheri/cheri-elf-flags-err.s b/lld/test/ELF/cheri/cheri-elf-flags-err.s index 01f5d2583855f..9d96081c033d5 100644 --- a/lld/test/ELF/cheri/cheri-elf-flags-err.s +++ b/lld/test/ELF/cheri/cheri-elf-flags-err.s @@ -12,7 +12,7 @@ # RUN: ld.lld %t-cheri128-hybrid-lib.o %t-cheri128-hybrid-main.o -o /dev/null # But with -melf64btsmip_cheri_fbsd the target ABI should be purecap (and therefore linking should fail)! # RUN: not ld.lld -melf64btsmip_cheri_fbsd %t-cheri128-hybrid-lib.o %t-cheri128-hybrid-main.o -o %t.exe 2>&1 | FileCheck -DCHERI_TYPE=cheri128 -check-prefix HYBRID-input-with-explicit-PURECAP %s -# HYBRID-input-with-explicit-PURECAP: error: {{.*}}-cheri128-hybrid-lib.o: object file is non-CheriABI but emulation forces it +# HYBRID-input-with-explicit-PURECAP: error: {{.*}}-cheri128-hybrid-lib.o is incompatible with elf64btsmip_cheri_fbsd # Similarly purecap input should work fine without a -m flag: # RUN: ld.lld %t-cheri128-lib.o %t-cheri128-main.o -o /dev/null @@ -94,8 +94,16 @@ __start: # CHERI128-HYBRID-FLAGS-NEXT: EF_MIPS_MACH_CHERI128 (0xC10000) # CHERI128-HYBRID-FLAGS-NEXT: ] -# CHERI128-vs-MIPS: {{.*}}/cheri-elf-flags-err.s.tmp-mips64.o: ABI 'n64' is incompatible with target ABI 'purecap' -# CHERI128-vs-CHERI128-HYBRID: {{.*}}/cheri-elf-flags-err.s.tmp-cheri128-hybrid-lib.o: ABI 'n64' is incompatible with target ABI 'purecap' +# CHERI256-vs-MIPS: {{.*}}/cheri-elf-flags-err.s.tmp-mips64.o is incompatible with {{.*}}/cheri-elf-flags-err.s.tmp-cheri256-main.o +# CHERI128-vs-MIPS: {{.*}}/cheri-elf-flags-err.s.tmp-mips64.o is incompatible with {{.*}}/cheri-elf-flags-err.s.tmp-cheri128-main.o +# CHERI256-vs-CHERI128: incompatible target ISA: +# CHERI256-vs-CHERI128-NEXT: {{.+}}-cheri256-main.o: mips4 (cheri256) +# CHERI256-vs-CHERI128-NEXT: {{.+}}-cheri128-lib.o: mips4 (cheri128) +# CHERI128-vs-CHERI256: incompatible target ISA: +# CHERI128-vs-CHERI256-NEXT: {{.+}}-cheri128-main.o: mips4 (cheri128) +# CHERI128-vs-CHERI256-NEXT: {{.+}}-cheri256-lib.o: mips4 (cheri256) +# CHERI256-vs-CHERI256-HYBRID: {{.*}}/cheri-elf-flags-err.s.tmp-cheri256-hybrid-lib.o is incompatible with {{.*}}/cheri-elf-flags-err.s.tmp-cheri256-main.o +# CHERI128-vs-CHERI128-HYBRID: {{.*}}/cheri-elf-flags-err.s.tmp-cheri128-hybrid-lib.o is incompatible with {{.*}}/cheri-elf-flags-err.s.tmp-cheri128-main.o # TODO: should those files actually link? # CHERI128-HYBRID-vs-MIPS: target ISA 'cheri128' is incompatible with 'mips64': {{.*}}/cheri-elf-flags-err.s.tmp-mips64.o diff --git a/lld/test/ELF/cheri/mix-abis-shlib.s b/lld/test/ELF/cheri/mix-abis-shlib.s index ee10a0cdd06e5..cc14ddc36de8a 100644 --- a/lld/test/ELF/cheri/mix-abis-shlib.s +++ b/lld/test/ELF/cheri/mix-abis-shlib.s @@ -50,9 +50,9 @@ # FNDESC: 0x000000007000C002 MIPS_CHERI_FLAGS ABI_FNDESC RELATIVE_CAPRELOCS # MIPS-NOT: MIPS_CHERI_FLAGS -# ERR-CHERIABI-LINKING-MIPS: ld.lld: error: {{.+}}mix-abis-shlib.s.tmp-lib-mips.so: ABI 'n64' is incompatible with target ABI: purecap +# ERR-CHERIABI-LINKING-MIPS: ld.lld: error: {{.+}}mix-abis-shlib.s.tmp-lib-mips.so is incompatible with {{.+}}mix-abis-shlib.s.tmp-{{.+}}.o # ERR-CHERIABI-LINKING-MIPS-EMPTY: -# ERR-MIPS-LINKING-CHERIABI: ld.lld: error: {{.+}}mix-abis-shlib.s.tmp-lib-{{.+}}.so: ABI 'purecap' is incompatible with target ABI: n64 +# ERR-MIPS-LINKING-CHERIABI: ld.lld: error: {{.+}}mix-abis-shlib.s.tmp-lib-{{.+}}.so is incompatible with {{.+}}mix-abis-shlib.s.tmp-mips.o # ERR-MIPS-LINKING-CHERIABI-EMPTY: # ERR-SHARED: ld.lld: error: target pure-capability ABI EXT_CHERI_ABI_[[IN_ABI]] is incompatible with linked shared library diff --git a/lld/test/ELF/cheri/riscv/plt.s b/lld/test/ELF/cheri/riscv/plt.s index 1a0e1dc7cf69f..29b52f282dacf 100644 --- a/lld/test/ELF/cheri/riscv/plt.s +++ b/lld/test/ELF/cheri/riscv/plt.s @@ -7,7 +7,7 @@ # RUN: ld.lld %t.32.o %t1.32.so -z separate-code -o %t.32 # RUN: llvm-readelf -S -s %t.32 | FileCheck --check-prefixes=SEC,NM %s # RUN: llvm-readobj -r %t.32 | FileCheck --check-prefix=RELOC32 %s -# RUN: llvm-readelf -x .captable %t.32 | FileCheck --check-prefix=CAPTAB32 %s +# RUN: llvm-readelf -x .got %t.32 | FileCheck --check-prefix=GOT32 %s # RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex=false %t.32 | FileCheck --check-prefixes=DIS,DIS32 %s # RUN: %riscv64_cheri_purecap_llvm-mc -filetype=obj %t1.s -o %t1.64.o @@ -16,7 +16,7 @@ # RUN: ld.lld %t.64.o %t1.64.so -z separate-code -o %t.64 # RUN: llvm-readelf -S -s %t.64 | FileCheck --check-prefixes=SEC,NM %s # RUN: llvm-readobj -r %t.64 | FileCheck --check-prefix=RELOC64 %s -# RUN: llvm-readelf -x .captable %t.64 | FileCheck --check-prefix=CAPTAB64 %s +# RUN: llvm-readelf -x .got %t.64 | FileCheck --check-prefix=GOT64 %s # RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex=false %t.64 | FileCheck --check-prefixes=DIS,DIS64 %s # SEC: .plt PROGBITS {{0*}}00011030 @@ -27,19 +27,21 @@ # NM: {{0*}}00000000 0 FUNC WEAK DEFAULT UND weak # RELOC32: .rela.dyn { -# RELOC32-NEXT: 0x12000 R_RISCV_CHERI_CAPABILITY bar 0x0 -# RELOC32-NEXT: 0x12008 R_RISCV_CHERI_CAPABILITY weak 0x0 +# RELOC32-NEXT: 0x12068 R_RISCV_CHERI_CAPABILITY bar 0x0 +# RELOC32-NEXT: 0x12070 R_RISCV_CHERI_CAPABILITY weak 0x0 # RELOC32-NEXT: } -# CAPTAB32: section '.captable' -# CAPTAB32-NEXT: 0x00012000 00000000 00000000 00000000 00000000 +# GOT32: section '.got' +# GOT32-NEXT: 0x00012060 00200100 00000000 00000000 00000000 +# GOT32-NEXT: 0x00012070 00000000 00000000 # RELOC64: .rela.dyn { -# RELOC64-NEXT: 0x12000 R_RISCV_CHERI_CAPABILITY bar 0x0 -# RELOC64-NEXT: 0x12010 R_RISCV_CHERI_CAPABILITY weak 0x0 +# RELOC64-NEXT: 0x120D0 R_RISCV_CHERI_CAPABILITY bar 0x0 +# RELOC64-NEXT: 0x120E0 R_RISCV_CHERI_CAPABILITY weak 0x0 # RELOC64-NEXT: } -# CAPTAB64: section '.captable' -# CAPTAB64-NEXT: 0x00012000 00000000 00000000 00000000 00000000 -# CAPTAB64-NEXT: 0x00012010 00000000 00000000 00000000 00000000 +# GOT64: section '.got' +# GOT64-NEXT: 0x000120c0 00200100 00000000 00000000 00000000 +# GOT64-NEXT: 0x000120d0 00000000 00000000 00000000 00000000 +# GOT64-NEXT: 0x000120e0 00000000 00000000 00000000 00000000 # DIS: <_start>: ## Direct call @@ -62,17 +64,19 @@ # DIS: <.plt>: # DIS-NEXT: ... -## 32-bit: &.captable[bar]-. = 0x12000-0x11050 = 4096*1-80 +## 32-bit: &.got[bar]-. = 0x12068-0x11050 = 4096*1+24 +## 64-bit: &.got[bar]-. = 0x120d0-0x11050 = 4096*1+128 # DIS: 11050: auipcc t3, 1 -# DIS32-NEXT: clc t3, -80(t3) -# DIS64-NEXT: clc t3, -80(t3) +# DIS32-NEXT: clc t3, 24(t3) +# DIS64-NEXT: clc t3, 128(t3) # DIS-NEXT: cjalr t1, t3 # DIS-NEXT: nop -## 32-bit: &.captable[weak]-. = 0x12008-0x11060 = 4096*1-88 +## 32-bit: &.got[weak]-. = 0x12070-0x11060 = 4096*1+16 +## 64-bit: &.got[weak]-. = 0x120e0-0x11060 = 4096*1+128 # DIS: 11060: auipcc t3, 1 -# DIS32-NEXT: clc t3, -88(t3) -# DIS64-NEXT: clc t3, -80(t3) +# DIS32-NEXT: clc t3, 16(t3) +# DIS64-NEXT: clc t3, 128(t3) # DIS-NEXT: cjalr t1, t3 # DIS-NEXT: nop diff --git a/lld/test/ELF/cheri/riscv/reloc-got.s b/lld/test/ELF/cheri/riscv/reloc-got.s new file mode 100644 index 0000000000000..0afec1fd772e0 --- /dev/null +++ b/lld/test/ELF/cheri/riscv/reloc-got.s @@ -0,0 +1,77 @@ +# REQUIRES: riscv +# RUN: echo '.globl b; b:' | %riscv32_cheri_purecap_llvm-mc -filetype=obj - -o %t1.o +# RUN: ld.lld -shared %t1.o -soname=t1.so -o %t1.so + +# RUN: %riscv32_cheri_purecap_llvm-mc -filetype=obj -position-independent %s -o %t.o +# RUN: ld.lld %t.o %t1.so -o %t +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC32 %s +# RUN: llvm-readobj -r --cap-relocs %t | FileCheck --check-prefix=RELOC32 %s +# RUN: llvm-nm %t | FileCheck --check-prefix=NM32 %s +# RUN: llvm-readobj -x .got %t | FileCheck --check-prefix=HEX32 %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=DIS32 %s + +# RUN: echo '.globl b; b:' | %riscv64_cheri_purecap_llvm-mc -filetype=obj - -o %t1.o +# RUN: ld.lld -shared %t1.o -soname=t1.so -o %t1.so + +# RUN: %riscv64_cheri_purecap_llvm-mc -filetype=obj -position-independent %s -o %t.o +# RUN: ld.lld %t.o %t1.so -o %t +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC64 %s +# RUN: llvm-readobj -r --cap-relocs %t | FileCheck --check-prefix=RELOC64 %s +# RUN: llvm-nm %t | FileCheck --check-prefix=NM64 %s +# RUN: llvm-readobj -x .got %t | FileCheck --check-prefix=HEX64 %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=DIS64 %s + +# SEC32: .got PROGBITS 00012230 000230 000018 +# SEC64: .got PROGBITS 00000000000123a0 0003a0 000030 + +# RELOC32: .rela.dyn { +# RELOC32-NEXT: 0x12238 R_RISCV_CHERI_CAPABILITY b 0x0 +# RELOC32-NEXT: } +# RELOC32: CHERI __cap_relocs [ +# RELOC32-NEXT: 0x012240 Base: 0x13248 (a+0) Length: 4 Perms: Object +# RELOC32-NEXT: ] + +# RELOC64: .rela.dyn { +# RELOC64-NEXT: 0x123B0 R_RISCV_CHERI_CAPABILITY b 0x0 +# RELOC64-NEXT: } +# RELOC64: CHERI __cap_relocs [ +# RELOC64-NEXT: 0x0123c0 Base: 0x133d0 (a+0) Length: 4 Perms: Object +# RELOC64-NEXT: ] + +# NM32: 00013248 d a +# NM64: 00000000000133d0 d a + +## .got[0] = _DYNAMIC +## .got[1] = 0 (relocated by R_RISCV_CHERI_CAPABILITY at run time) +## .got[2] = 0 (relocated by __cap_relocs at run time) +# HEX32: section '.got': +# HEX32: 0x00012230 c0210100 00000000 00000000 00000000 +# HEX32: 0x00012240 00000000 00000000 + +# HEX64: section '.got': +# HEX64: 0x000123a0 c0220100 00000000 00000000 00000000 +# HEX64: 0x000123b0 00000000 00000000 00000000 00000000 +# HEX64: 0x000123c0 00000000 00000000 00000000 00000000 + +## &.got[2]-. = 0x12240-0x111b0 = 4096*1+144 +# DIS32: 111b0: auipcc a0, 0x1 +# DIS32-NEXT: clc a0, 0x90(a0) +## &.got[1]-. = 0x12238-0x111b8 = 4096*1+128 +# DIS32: 111b8: auipcc a0, 0x1 +# DIS32-NEXT: clc a0, 0x80(a0) + +## &.got[2]-. = 0x123c0-0x112b0 = 4096*1+272 +# DIS64: 112b0: auipcc a0, 0x1 +# DIS64-NEXT: clc a0, 0x110(a0) +## &.got[1]-. = 0x123b0-0x112b8 = 4096*1+248 +# DIS64: 112b8: auipcc a0, 0x1 +# DIS64-NEXT: clc a0, 0xf8(a0) + +clgc a0, a +clgc a0, b + +.data +a: +## An undefined reference of _GLOBAL_OFFSET_TABLE_ causes .got[0] to be +## allocated to store _DYNAMIC. +.long _GLOBAL_OFFSET_TABLE_ - . diff --git a/lld/test/ELF/cheri/riscv/tls.s b/lld/test/ELF/cheri/riscv/tls.s index fbf5931f32fd4..f10bb3b149657 100644 --- a/lld/test/ELF/cheri/riscv/tls.s +++ b/lld/test/ELF/cheri/riscv/tls.s @@ -7,13 +7,13 @@ # RUN: %riscv32_cheri_purecap_llvm-mc --defsym PIC=0 -filetype=obj %s -o %t.32.o # RUN: ld.lld %t.32.o %t1.32.so -o %t.32 # RUN: llvm-readobj -r %t.32 | FileCheck --check-prefix=RV32-REL %s -# RUN: llvm-readelf -x .captable %t.32 | FileCheck --check-prefix=RV32-CAP %s +# RUN: llvm-readelf -x .got %t.32 | FileCheck --check-prefix=RV32-GOT %s # RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex=false %t.32 | FileCheck --check-prefix=RV32-DIS %s # RUN: %riscv32_cheri_purecap_llvm-mc --defsym PIC=1 -filetype=obj %s -o %t.32.pico # RUN: ld.lld -shared %t.32.pico %t1.32.so -o %t.32.so # RUN: llvm-readobj -r %t.32.so | FileCheck --check-prefix=RV32-SO-REL %s -# RUN: llvm-readelf -x .captable %t.32.so | FileCheck --check-prefix=RV32-SO-CAP %s +# RUN: llvm-readelf -x .got %t.32.so | FileCheck --check-prefix=RV32-SO-GOT %s # RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex=false %t.32.so | FileCheck --check-prefix=RV32-SO-DIS %s # RUN: %riscv64_cheri_purecap_llvm-mc -filetype=obj %t.s -o %t1.64.o @@ -22,132 +22,138 @@ # RUN: %riscv64_cheri_purecap_llvm-mc --defsym PIC=0 -filetype=obj %s -o %t.64.o # RUN: ld.lld %t.64.o %t1.64.so -o %t.64 # RUN: llvm-readobj -r %t.64 | FileCheck --check-prefix=RV64-REL %s -# RUN: llvm-readelf -x .captable %t.64 | FileCheck --check-prefix=RV64-CAP %s +# RUN: llvm-readelf -x .got %t.64 | FileCheck --check-prefix=RV64-GOT %s # RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex=false %t.64 | FileCheck --check-prefix=RV64-DIS %s # RUN: %riscv64_cheri_purecap_llvm-mc --defsym PIC=1 -filetype=obj %s -o %t.64.pico # RUN: ld.lld -shared %t.64.pico %t1.64.so -o %t.64.so # RUN: llvm-readobj -r %t.64.so | FileCheck --check-prefix=RV64-SO-REL %s -# RUN: llvm-readelf -x .captable %t.64.so | FileCheck --check-prefix=RV64-SO-CAP %s +# RUN: llvm-readelf -x .got %t.64.so | FileCheck --check-prefix=RV64-SO-GOT %s # RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex=false %t.64.so | FileCheck --check-prefix=RV64-SO-DIS %s # RV32-REL: .rela.dyn { -# RV32-REL-NEXT: 0x121E0 R_RISCV_TLS_DTPMOD32 evar 0x0 -# RV32-REL-NEXT: 0x121E4 R_RISCV_TLS_DTPREL32 evar 0x0 -# RV32-REL-NEXT: 0x121F0 R_RISCV_TLS_TPREL32 evar 0x0 +# RV32-REL-NEXT: 0x12248 R_RISCV_TLS_DTPMOD32 evar 0x0 +# RV32-REL-NEXT: 0x1224C R_RISCV_TLS_DTPREL32 evar 0x0 +# RV32-REL-NEXT: 0x12250 R_RISCV_TLS_TPREL32 evar 0x0 # RV32-REL-NEXT: } # RV32-SO-REL: .rela.dyn { -# RV32-SO-REL-NEXT: 0x3290 R_RISCV_TLS_DTPMOD32 - 0x0 -# RV32-SO-REL-NEXT: 0x329C R_RISCV_TLS_TPREL32 - 0x4 -# RV32-SO-REL-NEXT: 0x3288 R_RISCV_TLS_DTPMOD32 evar 0x0 -# RV32-SO-REL-NEXT: 0x328C R_RISCV_TLS_DTPREL32 evar 0x0 -# RV32-SO-REL-NEXT: 0x3298 R_RISCV_TLS_TPREL32 evar 0x0 +# RV32-SO-REL-NEXT: 0x2288 R_RISCV_TLS_DTPMOD32 - 0x0 +# RV32-SO-REL-NEXT: 0x2290 R_RISCV_TLS_TPREL32 - 0x4 +# RV32-SO-REL-NEXT: 0x2278 R_RISCV_TLS_DTPMOD32 evar 0x0 +# RV32-SO-REL-NEXT: 0x227C R_RISCV_TLS_DTPREL32 evar 0x0 +# RV32-SO-REL-NEXT: 0x2280 R_RISCV_TLS_TPREL32 evar 0x0 # RV32-SO-REL-NEXT: } -# RV32-CAP: section '.captable': -# RV32-CAP-NEXT: 0x000121e0 00000000 00000000 01000000 04000000 -# RV32-CAP-NEXT: 0x000121f0 00000000 04000000 +# RV32-GOT: section '.got': +# RV32-GOT-NEXT: 0x00012240 e0210100 00000000 00000000 00000000 +# RV32-GOT-NEXT: 0x00012250 00000000 00000000 01000000 04000000 +# RV32-GOT-NEXT: 0x00012260 04000000 00000000 -# RV32-SO-CAP: section '.captable': -# RV32-SO-CAP-NEXT: 0x00003288 00000000 00000000 00000000 04000000 -# RV32-SO-CAP-NEXT: 0x00003298 00000000 00000000 +# RV32-SO-GOT: section '.got': +# RV32-SO-GOT-NEXT: 0x00002270 10220000 00000000 00000000 00000000 +# RV32-SO-GOT-NEXT: 0x00002280 00000000 00000000 00000000 04000000 +# RV32-SO-GOT-NEXT: 0x00002290 00000000 00000000 -# 0x121e0 - 0x111b4 = 0x0102c (GD evar) +# 0x12248 - 0x111b4 = 0x1094 (GD evar) # RV32-DIS: 111b4: auipcc a0, 1 -# RV32-DIS-NEXT: cincoffset a0, a0, 44 +# RV32-DIS-NEXT: cincoffset a0, a0, 148 -# 0x121f0 - 0x111bc = 0x01034 (IE evar) +# 0x12250 - 0x111bc = 0x1094 (IE evar) # RV32-DIS: 111bc: auipcc a0, 1 -# RV32-DIS-NEXT: clw a0, 52(a0) +# RV32-DIS-NEXT: clw a0, 148(a0) -# 0x121e8 - 0x111c4 = 0x01024 (GD lvar) +# 0x12258 - 0x111c4 = 0x1094 (GD lvar) # RV32-DIS: 111c4: auipcc a0, 1 -# RV32-DIS-NEXT: cincoffset a0, a0, 36 +# RV32-DIS-NEXT: cincoffset a0, a0, 148 -# 0x121f4 - 0x111cc = 0x01028 (IE lvar) +# 0x12260 - 0x111cc = 0x1094 (IE lvar) # RV32-DIS: 111cc: auipcc a0, 1 -# RV32-DIS-NEXT: clw a0, 40(a0) +# RV32-DIS-NEXT: clw a0, 148(a0) # RV32-DIS: 111d4: lui a0, 0 # RV32-DIS-NEXT: cincoffset a0, tp, a0 # RV32-DIS-NEXT: cincoffset a0, a0, 4 -# 0x3288 - 0x1210 = 0x2078 (GD evar) -# RV32-SO-DIS: 1210: auipcc a0, 2 -# RV32-SO-DIS-NEXT: cincoffset a0, a0, 120 +# 0x2278 - 0x11f0 = 0x1088 (GD evar) +# RV32-SO-DIS: 11f0: auipcc a0, 1 +# RV32-SO-DIS-NEXT: cincoffset a0, a0, 136 -# 0x3298 - 0x1218 = 0x2080 (IE evar) -# RV32-SO-DIS: 1218: auipcc a0, 2 -# RV32-SO-DIS-NEXT: clw a0, 128(a0) +# 0x2280 - 0x11f8 = 0x1088 (IE evar) +# RV32-SO-DIS: 11f8: auipcc a0, 1 +# RV32-SO-DIS-NEXT: clw a0, 136(a0) -# 0x3290 - 0x1220 = 0x2070 (GD lvar) -# RV32-SO-DIS: 1220: auipcc a0, 2 -# RV32-SO-DIS-NEXT: cincoffset a0, a0, 112 +# 0x2288 - 0x1200 = 0x1088 (GD lvar) +# RV32-SO-DIS: 1200: auipcc a0, 1 +# RV32-SO-DIS-NEXT: cincoffset a0, a0, 136 -# 0x329c - 0x1228 = 0x2074 (IE lvar) -# RV32-SO-DIS: 1228: auipcc a0, 2 -# RV32-SO-DIS-NEXT: clw a0, 116(a0) +# 0x2290 - 0x1208 = 0x1088 (IE lvar) +# RV32-SO-DIS: 1208: auipcc a0, 1 +# RV32-SO-DIS-NEXT: clw a0, 136(a0) # RV64-REL: .rela.dyn { -# RV64-REL-NEXT: 0x122F0 R_RISCV_TLS_DTPMOD64 evar 0x0 -# RV64-REL-NEXT: 0x122F8 R_RISCV_TLS_DTPREL64 evar 0x0 -# RV64-REL-NEXT: 0x12310 R_RISCV_TLS_TPREL64 evar 0x0 +# RV64-REL-NEXT: 0x123C0 R_RISCV_TLS_DTPMOD64 evar 0x0 +# RV64-REL-NEXT: 0x123C8 R_RISCV_TLS_DTPREL64 evar 0x0 +# RV64-REL-NEXT: 0x123D0 R_RISCV_TLS_TPREL64 evar 0x0 # RV64-REL-NEXT: } # RV64-SO-REL: .rela.dyn { -# RV64-SO-REL-NEXT: 0x3430 R_RISCV_TLS_DTPMOD64 - 0x0 -# RV64-SO-REL-NEXT: 0x3448 R_RISCV_TLS_TPREL64 - 0x4 -# RV64-SO-REL-NEXT: 0x3420 R_RISCV_TLS_DTPMOD64 evar 0x0 -# RV64-SO-REL-NEXT: 0x3428 R_RISCV_TLS_DTPREL64 evar 0x0 -# RV64-SO-REL-NEXT: 0x3440 R_RISCV_TLS_TPREL64 evar 0x0 +# RV64-SO-REL-NEXT: 0x2430 R_RISCV_TLS_DTPMOD64 - 0x0 +# RV64-SO-REL-NEXT: 0x2440 R_RISCV_TLS_TPREL64 - 0x4 +# RV64-SO-REL-NEXT: 0x2410 R_RISCV_TLS_DTPMOD64 evar 0x0 +# RV64-SO-REL-NEXT: 0x2418 R_RISCV_TLS_DTPREL64 evar 0x0 +# RV64-SO-REL-NEXT: 0x2420 R_RISCV_TLS_TPREL64 evar 0x0 # RV64-SO-REL-NEXT: } -# RV64-CAP: section '.captable': -# RV64-CAP-NEXT: 0x000122f0 00000000 00000000 00000000 00000000 -# RV64-CAP-NEXT: 0x00012300 01000000 00000000 04000000 00000000 -# RV64-CAP-NEXT: 0x00012310 00000000 00000000 04000000 00000000 - -# RV64-SO-CAP: section '.captable': -# RV64-SO-CAP-NEXT: 0x00003420 00000000 00000000 00000000 00000000 -# RV64-SO-CAP-NEXT: 0x00003430 00000000 00000000 04000000 00000000 -# RV64-SO-CAP-NEXT: 0x00003440 00000000 00000000 00000000 00000000 - -# 0x122f0 - 0x112b8 = 0x01038 (GD evar) +# RV64-GOT: section '.got': +# RV64-GOT-NEXT: 0x000123b0 e8220100 00000000 00000000 00000000 +# RV64-GOT-NEXT: 0x000123c0 00000000 00000000 00000000 00000000 +# RV64-GOT-NEXT: 0x000123d0 00000000 00000000 00000000 00000000 +# RV64-GOT-NEXT: 0x000123e0 01000000 00000000 04000000 00000000 +# RV64-GOT-NEXT: 0x000123f0 04000000 00000000 00000000 00000000 + +# RV64-SO-GOT: section '.got': +# RV64-SO-GOT-NEXT: 0x00002400 38230000 00000000 00000000 00000000 +# RV64-SO-GOT-NEXT: 0x00002410 00000000 00000000 00000000 00000000 +# RV64-SO-GOT-NEXT: 0x00002420 00000000 00000000 00000000 00000000 +# RV64-SO-GOT-NEXT: 0x00002430 00000000 00000000 04000000 00000000 +# RV64-SO-GOT-NEXT: 0x00002440 00000000 00000000 00000000 00000000 + +# 0x123c0 - 0x112b8 = 0x1108 (GD evar) # RV64-DIS: 112b8: auipcc a0, 1 -# RV64-DIS-NEXT: cincoffset a0, a0, 56 +# RV64-DIS-NEXT: cincoffset a0, a0, 264 -# 0x12310 - 0x112c0 = 0x01050 (IE evar) +# 0x123d0 - 0x112c0 = 0x1110 (IE evar) # RV64-DIS: 112c0: auipcc a0, 1 -# RV64-DIS-NEXT: cld a0, 80(a0) +# RV64-DIS-NEXT: cld a0, 272(a0) -# 0x12300 - 0x112c8 = 0x01038 (GD lvar) +# 0x123e0 - 0x112c8 = 0x1118 (GD lvar) # RV64-DIS: 112c8: auipcc a0, 1 -# RV64-DIS-NEXT: cincoffset a0, a0, 56 +# RV64-DIS-NEXT: cincoffset a0, a0, 280 -# 0x12318 - 0x112d0 = 0x01048 (IE lvar) +# 0x123f0 - 0x112d0 = 0x1120 (IE lvar) # RV64-DIS: 112d0: auipcc a0, 1 -# RV64-DIS-NEXT: cld a0, 72(a0) +# RV64-DIS-NEXT: cld a0, 288(a0) # RV64-DIS: 112d8: lui a0, 0 # RV64-DIS-NEXT: cincoffset a0, tp, a0 # RV64-DIS-NEXT: cincoffset a0, a0, 4 -# 0x3420 - 0x1350 = 0x20d0 (GD evar) -# RV64-SO-DIS: 1350: auipcc a0, 2 -# RV64-SO-DIS-NEXT: cincoffset a0, a0, 208 +# 0x2410 - 0x1318 = 0x10f8 (GD evar) +# RV64-SO-DIS: 1318: auipcc a0, 1 +# RV64-SO-DIS-NEXT: cincoffset a0, a0, 248 -# 0x3440 - 0x1358 = 0x20e8 (IE evar) -# RV64-SO-DIS: 1358: auipcc a0, 2 -# RV64-SO-DIS-NEXT: cld a0, 232(a0) +# 0x2420 - 0x1320 = 0x1100 (IE evar) +# RV64-SO-DIS: 1320: auipcc a0, 1 +# RV64-SO-DIS-NEXT: cld a0, 256(a0) -# 0x3430 - 0x1360 = 0x20d0 (GD lvar) -# RV64-SO-DIS: 1360: auipcc a0, 2 -# RV64-SO-DIS-NEXT: cincoffset a0, a0, 208 +# 0x2430 - 0x1328 = 0x1108 (GD lvar) +# RV64-SO-DIS: 1328: auipcc a0, 1 +# RV64-SO-DIS-NEXT: cincoffset a0, a0, 264 -# 0x3448 - 0x1368 = 0x20e0 (IE lvar) -# RV64-SO-DIS: 1368: auipcc a0, 2 -# RV64-SO-DIS-NEXT: cld a0, 224(a0) +# 0x2440 - 0x1330 = 0x1110 (IE lvar) +# RV64-SO-DIS: 1330: auipcc a0, 1 +# RV64-SO-DIS-NEXT: cld a0, 272(a0) .global _start _start: diff --git a/llvm/include/llvm/IR/CheriIntrinsics.h b/llvm/include/llvm/IR/CheriIntrinsics.h index caa292981a72c..06f776f1af26b 100644 --- a/llvm/include/llvm/IR/CheriIntrinsics.h +++ b/llvm/include/llvm/IR/CheriIntrinsics.h @@ -21,7 +21,8 @@ namespace llvm { namespace cheri { -inline Intrinsic::ID correspondingSetIntrinsic(Intrinsic::ID GetIntrin) { +inline constexpr Intrinsic::ID +correspondingSetIntrinsic(Intrinsic::ID GetIntrin) { switch (GetIntrin) { case Intrinsic::cheri_cap_offset_get: return Intrinsic::cheri_cap_offset_set; @@ -29,11 +30,14 @@ inline Intrinsic::ID correspondingSetIntrinsic(Intrinsic::ID GetIntrin) { return Intrinsic::cheri_cap_address_set; case Intrinsic::cheri_cap_flags_get: return Intrinsic::cheri_cap_flags_set; + case Intrinsic::cheri_cap_high_get: + return Intrinsic::cheri_cap_high_set; default: llvm_unreachable("No matching set intrinsic"); } } -inline Intrinsic::ID correspondingGetIntrinsic(Intrinsic::ID SetIntrin) { +inline constexpr Intrinsic::ID +correspondingGetIntrinsic(Intrinsic::ID SetIntrin) { switch (SetIntrin) { case Intrinsic::cheri_cap_offset_set: return Intrinsic::cheri_cap_offset_get; @@ -41,6 +45,8 @@ inline Intrinsic::ID correspondingGetIntrinsic(Intrinsic::ID SetIntrin) { return Intrinsic::cheri_cap_address_get; case Intrinsic::cheri_cap_flags_set: return Intrinsic::cheri_cap_flags_get; + case Intrinsic::cheri_cap_high_set: + return Intrinsic::cheri_cap_high_get; default: llvm_unreachable("No matching get intrinsic"); } diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index c1e7dd98fa5f3..3462c01337cd6 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -6366,25 +6366,45 @@ stripAndAccumulateGEPsAndPointerCastsSameRepr(Value *V, const DataLayout &DL, return Result; } -template -static Value *inferCapabilityOffsetOrAddr(Value *V, Type *ResultTy, - const DataLayout &DL) { - // Try to infer the offset/address from a prior setoffset/setaddr value +// Common helper to fold llvm.cheri.get.foo intrinsics +template +static Value *foldCheriGetSetPair(Value *V, Type *ResultTy, + const DataLayout &DL) { + constexpr Intrinsic::ID SetIntrin = cheri::correspondingSetIntrinsic(Intrin); + // getFoo(setFoo(A, B)) -> B + // This is true for address and high, but not necessarily true for flags and + // offset since setflags truncates the value and setoffset may change the + // bounds representation (and thus the offset) if it is significantly OOB. + constexpr bool SetUpdatesAllGetBits = + SetIntrin != Intrinsic::cheri_cap_offset_set && + SetIntrin != Intrinsic::cheri_cap_flags_set; Value *IntrinArg = nullptr; - // getaddr(setaddr(A, B)) -> B and getoffset(setoffset(A, B)) -> B - if (match(V, m_Intrinsic(m_Value(), m_Value(IntrinArg)))) { - return IntrinArg; - } - // get{addr,offset}(setaddr(NULL, B)) -> B - if (match(V, m_Intrinsic( - m_Zero(), m_Value(IntrinArg)))) { + if (SetUpdatesAllGetBits && + match(V, m_Intrinsic(m_Value(), m_Value(IntrinArg)))) { return IntrinArg; } - // get{addr,offset}(setoffset(NULL, B)) -> B - if (match(V, m_Intrinsic( - m_Zero(), m_Value(IntrinArg)))) { - return IntrinArg; + // All intrinsics we call this function for return zero for NULL inputs. + // In fact, we can extend this to any input derived from NULL where the only + // operations have been address manipulations (unless we are retrieving a + // result depending on the address), since those cannot affect non-bounds + // capability metadata. + constexpr bool CanIgnoreAddressManipulation = + Intrin != Intrinsic::cheri_cap_address_get && + Intrin != Intrinsic::cheri_cap_offset_get; + Value *Base = CanIgnoreAddressManipulation + ? getBasePtrIgnoringCapabilityAddressManipulation(V, DL) + : V->stripPointerCastsSameRepresentation(); + if (isa(Base)) { + return Constant::getNullValue(ResultTy); } + return nullptr; +} + +template +static Value *inferCapabilityOffsetOrAddr(Value *V, Type *ResultTy, + const DataLayout &DL) { + if (Value *Ret = foldCheriGetSetPair(V, ResultTy, DL)) + return Ret; APInt OffsetAPInt(DL.getIndexTypeSizeInBits(V->getType()), 0); // Look through pointer casts and accumulate constant GEPs: @@ -6418,6 +6438,7 @@ static Value *inferCapabilityOffsetOrAddr(Value *V, Type *ResultTy, // We can also fold chains of constant GEPS: // For example: getoffset(GEP(setoffset(A, Const1), 100) -> Const1 + 100 ConstantInt *ConstSetArg; + constexpr Intrinsic::ID SetIntrin = cheri::correspondingSetIntrinsic(Intrin); if (match(BasePtr, m_Intrinsic(m_Value(), m_ConstantInt(ConstSetArg)))) { return ConstantInt::get(ResultTy, ConstSetArg->getValue() + OffsetAPInt); @@ -6427,20 +6448,6 @@ static Value *inferCapabilityOffsetOrAddr(Value *V, Type *ResultTy, return nullptr; } -Value *simplifyCapabilityGetOffset(Value *V, Type *ResultTy, - const DataLayout &DL) { - return inferCapabilityOffsetOrAddr( - V, ResultTy, DL); -} - -Value *simplifyCapabilityGetAddress(Value *V, Type *ResultTy, - const DataLayout &DL) { - return inferCapabilityOffsetOrAddr( - V, ResultTy, DL); -} - static Value *simplifyUnaryIntrinsic(Function *F, Value *Op0, const SimplifyQuery &Q, const CallBase *Call) { @@ -6485,7 +6492,6 @@ static Value *simplifyUnaryIntrinsic(Function *F, Value *Op0, return Constant::getNullValue(F->getReturnType()); break; case Intrinsic::cheri_cap_sealed_get: - case Intrinsic::cheri_cap_flags_get: case Intrinsic::cheri_cap_base_get: // Values derived from NULL and where the only operations that have been // applied are address manipulations, always have the following properties: @@ -6519,11 +6525,25 @@ static Value *simplifyUnaryIntrinsic(Function *F, Value *Op0, case Intrinsic::cheri_cap_length_get: break; case Intrinsic::cheri_cap_address_get: - if (Value *V = simplifyCapabilityGetAddress(Op0, F->getReturnType(), Q.DL)) + if (auto *V = inferCapabilityOffsetOrAddr( + Op0, F->getReturnType(), Q.DL)) return V; break; + case Intrinsic::cheri_cap_flags_get: { + if (auto *Ret = foldCheriGetSetPair( + Op0, F->getReturnType(), Q.DL)) + return Ret; + break; + } + case Intrinsic::cheri_cap_high_get: { + if (auto *Ret = foldCheriGetSetPair( + Op0, F->getReturnType(), Q.DL)) + return Ret; + break; + } case Intrinsic::cheri_cap_offset_get: - if (Value *V = simplifyCapabilityGetOffset(Op0, F->getReturnType(), Q.DL)) + if (auto *V = inferCapabilityOffsetOrAddr( + Op0, F->getReturnType(), Q.DL)) return V; break; diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp index 9241d93b5a506..cd019691d4ff1 100644 --- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -4175,8 +4175,7 @@ void RISCVAsmParser::emitAuipccInstPair(MCOperand DestReg, MCOperand TmpReg, // OP DestReg, TmpReg, %pcrel_lo(TmpLabel) MCContext &Ctx = getContext(); - MCSymbol *TmpLabel = - Ctx.createTempSymbol("pcrel_hi", /* AlwaysAddSuffix=*/true); + MCSymbol *TmpLabel = Ctx.createNamedTempSymbol("pcrel_hi"); Out.emitLabel(TmpLabel); const MCExpr *SymbolHi = MCSpecifierExpr::create(Symbol, VKHi, Ctx); diff --git a/llvm/test/CodeGen/CHERI-Generic/Inputs/cheri-intrinsics-folding-broken-module-regression.ll b/llvm/test/CodeGen/CHERI-Generic/Inputs/cheri-intrinsics-folding-broken-module-regression.ll index 5951969b570a7..ba8cf4c9fa6b3 100644 --- a/llvm/test/CodeGen/CHERI-Generic/Inputs/cheri-intrinsics-folding-broken-module-regression.ll +++ b/llvm/test/CodeGen/CHERI-Generic/Inputs/cheri-intrinsics-folding-broken-module-regression.ll @@ -1,6 +1,4 @@ ; This used to create a broken function. -; FIXME: the getoffset+add sequence should be folded to an increment -; REQUIRES: mips-registered-target ; RUN: opt @PURECAP_HARDFLOAT_ARGS@ -S -passes=instcombine %s -o - | FileCheck %s ; RUN: opt @PURECAP_HARDFLOAT_ARGS@ -S '-passes=default' %s | llc @PURECAP_HARDFLOAT_ARGS@ -O3 -o - | FileCheck %s --check-prefix ASM target datalayout = "@PURECAP_DATALAYOUT@" diff --git a/llvm/test/CodeGen/CHERI-Generic/MIPS/cheri-intrinsics-folding-broken-module-regression.ll b/llvm/test/CodeGen/CHERI-Generic/MIPS/cheri-intrinsics-folding-broken-module-regression.ll index 0fb65051c24d6..81b1f55b35d05 100644 --- a/llvm/test/CodeGen/CHERI-Generic/MIPS/cheri-intrinsics-folding-broken-module-regression.ll +++ b/llvm/test/CodeGen/CHERI-Generic/MIPS/cheri-intrinsics-folding-broken-module-regression.ll @@ -1,8 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --scrub-attributes --version 2 ; DO NOT EDIT -- This file was generated from test/CodeGen/CHERI-Generic/Inputs/cheri-intrinsics-folding-broken-module-regression.ll ; This used to create a broken function. -; FIXME: the getoffset+add sequence should be folded to an increment -; REQUIRES: mips-registered-target ; RUN: opt -mtriple=mips64 -mcpu=cheri128 -mattr=+cheri128 --relocation-model=pic -target-abi purecap -S -passes=instcombine %s -o - | FileCheck %s ; RUN: opt -mtriple=mips64 -mcpu=cheri128 -mattr=+cheri128 --relocation-model=pic -target-abi purecap -S '-passes=default' %s | llc -mtriple=mips64 -mcpu=cheri128 -mattr=+cheri128 --relocation-model=pic -target-abi purecap -O3 -o - | FileCheck %s --check-prefix ASM target datalayout = "E-m:e-pf200:128:128:128:64-i8:8:32-i16:16:32-i64:64-n32:64-S128-A200-P200-G200" @@ -23,13 +21,10 @@ define void @g(i64 %x, i64 %y) addrspace(200) nounwind { ; ASM-NEXT: lui $1, %pcrel_hi(_CHERI_CAPABILITY_TABLE_-8) ; ASM-NEXT: daddiu $1, $1, %pcrel_lo(_CHERI_CAPABILITY_TABLE_-4) ; ASM-NEXT: cgetpccincoffset $c1, $1 -; ASM-NEXT: daddu $2, $5, $4 ; ASM-NEXT: clcbi $c2, %captab20(d)($c1) ; ASM-NEXT: clcbi $c1, %captab20(e)($c1) -; ASM-NEXT: cgetoffset $1, $c2 ; ASM-NEXT: cincoffset $c2, $c2, $4 -; ASM-NEXT: daddu $1, $2, $1 -; ASM-NEXT: csetoffset $c2, $c2, $1 +; ASM-NEXT: cincoffset $c2, $c2, $5 ; ASM-NEXT: cjr $c17 ; ASM-NEXT: csc $c2, $zero, 0($c1) ; CHECK-LABEL: define void @g diff --git a/llvm/test/CodeGen/CHERI-Generic/RISCV32/cheri-intrinsics-folding-broken-module-regression.ll b/llvm/test/CodeGen/CHERI-Generic/RISCV32/cheri-intrinsics-folding-broken-module-regression.ll index cbaa2aab56786..5af50f984d024 100644 --- a/llvm/test/CodeGen/CHERI-Generic/RISCV32/cheri-intrinsics-folding-broken-module-regression.ll +++ b/llvm/test/CodeGen/CHERI-Generic/RISCV32/cheri-intrinsics-folding-broken-module-regression.ll @@ -1,8 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --scrub-attributes --version 2 ; DO NOT EDIT -- This file was generated from test/CodeGen/CHERI-Generic/Inputs/cheri-intrinsics-folding-broken-module-regression.ll ; This used to create a broken function. -; FIXME: the getoffset+add sequence should be folded to an increment -; REQUIRES: mips-registered-target ; RUN: opt -mtriple=riscv32 --relocation-model=pic -target-abi il32pc64f -mattr=+xcheri,+xcheripurecap,+f -S -passes=instcombine %s -o - | FileCheck %s ; RUN: opt -mtriple=riscv32 --relocation-model=pic -target-abi il32pc64f -mattr=+xcheri,+xcheripurecap,+f -S '-passes=default' %s | llc -mtriple=riscv32 --relocation-model=pic -target-abi il32pc64f -mattr=+xcheri,+xcheripurecap,+f -O3 -o - | FileCheck %s --check-prefix ASM target datalayout = "e-m:e-pf200:64:64:64:32-p:32:32-i64:64-n32-S128-A200-P200-G200" @@ -23,14 +21,11 @@ define void @g(i32 %x, i32 %y) addrspace(200) nounwind { ; ASM-NEXT: .LBB0_1: # Label of block must be emitted ; ASM-NEXT: auipcc a2, %captab_pcrel_hi(d) ; ASM-NEXT: clc a2, %pcrel_lo(.LBB0_1)(a2) -; ASM-NEXT: add a1, a1, a0 ; ASM-NEXT: .LBB0_2: # Label of block must be emitted ; ASM-NEXT: auipcc a3, %captab_pcrel_hi(e) ; ASM-NEXT: clc a3, %pcrel_lo(.LBB0_2)(a3) ; ASM-NEXT: cincoffset a0, a2, a0 -; ASM-NEXT: cgetoffset a2, a2 -; ASM-NEXT: add a1, a1, a2 -; ASM-NEXT: csetoffset a0, a0, a1 +; ASM-NEXT: cincoffset a0, a0, a1 ; ASM-NEXT: csc a0, 0(a3) ; ASM-NEXT: cret ; CHECK-LABEL: define void @g diff --git a/llvm/test/CodeGen/CHERI-Generic/RISCV64/cheri-intrinsics-folding-broken-module-regression.ll b/llvm/test/CodeGen/CHERI-Generic/RISCV64/cheri-intrinsics-folding-broken-module-regression.ll index 25f0998e2ab39..e714263a9d82d 100644 --- a/llvm/test/CodeGen/CHERI-Generic/RISCV64/cheri-intrinsics-folding-broken-module-regression.ll +++ b/llvm/test/CodeGen/CHERI-Generic/RISCV64/cheri-intrinsics-folding-broken-module-regression.ll @@ -1,8 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --scrub-attributes --version 2 ; DO NOT EDIT -- This file was generated from test/CodeGen/CHERI-Generic/Inputs/cheri-intrinsics-folding-broken-module-regression.ll ; This used to create a broken function. -; FIXME: the getoffset+add sequence should be folded to an increment -; REQUIRES: mips-registered-target ; RUN: opt -mtriple=riscv64 --relocation-model=pic -target-abi l64pc128d -mattr=+xcheri,+xcheripurecap,+f,+d -S -passes=instcombine %s -o - | FileCheck %s ; RUN: opt -mtriple=riscv64 --relocation-model=pic -target-abi l64pc128d -mattr=+xcheri,+xcheripurecap,+f,+d -S '-passes=default' %s | llc -mtriple=riscv64 --relocation-model=pic -target-abi l64pc128d -mattr=+xcheri,+xcheripurecap,+f,+d -O3 -o - | FileCheck %s --check-prefix ASM target datalayout = "e-m:e-pf200:128:128:128:64-p:64:64-i64:64-i128:128-n64-S128-A200-P200-G200" @@ -23,14 +21,11 @@ define void @g(i64 %x, i64 %y) addrspace(200) nounwind { ; ASM-NEXT: .LBB0_1: # Label of block must be emitted ; ASM-NEXT: auipcc a2, %captab_pcrel_hi(d) ; ASM-NEXT: clc a2, %pcrel_lo(.LBB0_1)(a2) -; ASM-NEXT: add a1, a1, a0 ; ASM-NEXT: .LBB0_2: # Label of block must be emitted ; ASM-NEXT: auipcc a3, %captab_pcrel_hi(e) ; ASM-NEXT: clc a3, %pcrel_lo(.LBB0_2)(a3) ; ASM-NEXT: cincoffset a0, a2, a0 -; ASM-NEXT: cgetoffset a2, a2 -; ASM-NEXT: add a1, a1, a2 -; ASM-NEXT: csetoffset a0, a0, a1 +; ASM-NEXT: cincoffset a0, a0, a1 ; ASM-NEXT: csc a0, 0(a3) ; ASM-NEXT: cret ; CHECK-LABEL: define void @g diff --git a/llvm/test/CodeGen/RISCV/cheri/double-imm.ll b/llvm/test/CodeGen/RISCV/cheri/double-imm.ll index a191d9581989f..380e4f7aa273a 100644 --- a/llvm/test/CodeGen/RISCV/cheri/double-imm.ll +++ b/llvm/test/CodeGen/RISCV/cheri/double-imm.ll @@ -1,10 +1,9 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; Check that .LCPI symbols have a size associated with them. -; TODO: Enable once BuildPairF64 works for purecap -; RUNNOT: %riscv32_cheri_purecap_llc -mattr=+d -verify-machineinstrs < %s \ -; RUNNOT: | FileCheck --check-prefix=CHECK,IL32PC64 %s +; RUN: %riscv32_cheri_purecap_llc -mattr=+d -verify-machineinstrs < %s \ +; RUN: | FileCheck %s ; RUN: %riscv64_cheri_purecap_llc -mattr=+d -verify-machineinstrs < %s \ -; RUN: | FileCheck --check-prefixes=CHECK,L64PC128 %s --allow-unused-prefixes +; RUN: | FileCheck %s ; CHECK-LABEL: .rodata.cst8,"aM",@progbits,8 ; CHECK-NEXT: .p2align 3 diff --git a/llvm/test/CodeGen/RISCV/cheri/machine-outliner.mir b/llvm/test/CodeGen/RISCV/cheri/machine-outliner.mir index bf249467e8a81..fae1b58f1b22b 100644 --- a/llvm/test/CodeGen/RISCV/cheri/machine-outliner.mir +++ b/llvm/test/CodeGen/RISCV/cheri/machine-outliner.mir @@ -1,6 +1,6 @@ # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py ## The MachineOutliner was creating non-purecap instructions (found while compiling PCRE) -# RUNs: %riscv64_cheri_purecap_llc --run-pass=machine-outliner %s -o - | FileCheck %s +# RUN: %riscv64_cheri_purecap_llc --run-pass=machine-outliner %s -o - | FileCheck %s # RUN: %riscv64_cheri_purecap_llc --start-before=machine-outliner %s -o - | FileCheck %s --check-prefix=ASM --- | target datalayout = "e-m:e-pf200:128:128:128:64-p:64:64-i64:64-i128:128-n64-S128-A200-P200-G200" @@ -49,7 +49,7 @@ body: | ; CHECK-NEXT: successors: %bb.2(0x40000000), %bb.3(0x40000000) ; CHECK-NEXT: liveins: $x10_y, $x12_y, $x13_y, $x11 ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: $x5_y = PseudoCCALLReg target-flags(riscv-ccall) @OUTLINED_FUNCTION_0, implicit-def $x5_y, implicit-def $x15_y, implicit-def $x14, implicit-def $x15, implicit $x10_y, implicit $x12_y + ; CHECK-NEXT: PseudoCCALLReg target-flags(riscv-ccall) @OUTLINED_FUNCTION_0, implicit-def $x14, implicit-def $x15, implicit-def $x5_y, implicit-def $x15_y, implicit $x10_y, implicit $x12_y ; CHECK-NEXT: renamable $x14 = ANDI killed renamable $x14, 1 ; CHECK-NEXT: BEQ killed renamable $x14, $x0, %bb.3 ; CHECK-NEXT: {{ $}} @@ -57,7 +57,7 @@ body: | ; CHECK-NEXT: successors: %bb.3(0x80000000) ; CHECK-NEXT: liveins: $x10_y, $x12_y, $x13_y, $x11 ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: $x5_y = PseudoCCALLReg target-flags(riscv-ccall) @OUTLINED_FUNCTION_0, implicit-def $x5_y, implicit-def $x15_y, implicit-def $x14, implicit-def $x15, implicit $x10_y, implicit $x12_y + ; CHECK-NEXT: PseudoCCALLReg target-flags(riscv-ccall) @OUTLINED_FUNCTION_0, implicit-def $x14, implicit-def $x15, implicit-def $x5_y, implicit-def $x15_y, implicit $x10_y, implicit $x12_y ; CHECK-NEXT: renamable $x15 = ANDI killed renamable $x14, 1 ; CHECK-NEXT: $x14_y = CMove $x0_y ; CHECK-NEXT: {{ $}} @@ -122,7 +122,7 @@ body: | # CHECK: liveins: [] # CHECK: body: | # CHECK-NEXT: bb.0: -# CHECK-NEXT: liveins: $x10_y, $x12_y, $x11, $x13_y, $x5_y +# CHECK-NEXT: liveins: $x10_y, $x12_y, $x13_y, $x11, $x5_y # CHECK-NEXT: {{ $}} # CHECK-NEXT: renamable $x14 = CLHU renamable $x10_y, 0 # CHECK-NEXT: renamable $x15 = SRLI renamable $x14, 3 @@ -132,7 +132,6 @@ body: | # CHECK-NEXT: renamable $x14 = SRLW killed renamable $x15, killed renamable $x14 # FIXME: This should not be a JALR: # CHECK-NEXT: $x0_y = CJALR $x5_y, 0 -# CHECK-EMPTY: # CHECK-NEXT: ... # ASM-LABEL: pcre2_match_16: diff --git a/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll b/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll new file mode 100644 index 0000000000000..9708890081aab --- /dev/null +++ b/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll @@ -0,0 +1,293 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes --force-update +;; Check folds for CHERI intrinsics that have both get and set variants +; RUN: sed 's/FIELD/address/' %s | opt -S -passes=instsimplify | FileCheck %s --check-prefixes=ADDRESS +; RUN: sed 's/FIELD/flags/' %s | opt -S -passes=instsimplify | FileCheck %s --check-prefixes=FLAGS +; RUN: sed 's/FIELD/high/' %s | opt -S -passes=instsimplify | FileCheck %s --check-prefixes=HIGH +; RUN: sed 's/FIELD/offset/' %s | opt -S -passes=instsimplify | FileCheck %s --check-prefixes=OFFSET +target datalayout = "pf200:128:128:128:64-A200-P200-G200" + +declare i64 @check_fold(i64) nounwind +declare i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200)) nounwind +declare ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200), i64) nounwind + + +;; This is a no-op and should be folded to ret %arg +define ptr addrspace(200) @fold_set_of_get(ptr addrspace(200) %arg) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_set_of_get +; ADDRESS-SAME: (ptr addrspace(200) [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { +; ADDRESS-NEXT: ret ptr addrspace(200) [[ARG]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_set_of_get +; FLAGS-SAME: (ptr addrspace(200) [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { +; FLAGS-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.flags.get.i64(ptr addrspace(200) [[ARG]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: ret ptr addrspace(200) [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_set_of_get +; HIGH-SAME: (ptr addrspace(200) [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { +; HIGH-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(ptr addrspace(200) [[ARG]]) +; HIGH-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; HIGH-NEXT: ret ptr addrspace(200) [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_set_of_get +; OFFSET-SAME: (ptr addrspace(200) [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { +; OFFSET-NEXT: ret ptr addrspace(200) [[ARG]] +; + %value = tail call i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200) %arg) + %ret = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %arg, i64 %value) + ret ptr addrspace(200) %ret +} + +;; negative test - get of a different value should not be folded to ret %arg +define ptr addrspace(200) @fold_set_of_get_bad(ptr addrspace(200) %arg, ptr addrspace(200) %other) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_set_of_get_bad +; ADDRESS-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.address.get.i64(ptr addrspace(200) [[OTHER]]) +; ADDRESS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.address.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; ADDRESS-NEXT: ret ptr addrspace(200) [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_set_of_get_bad +; FLAGS-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.flags.get.i64(ptr addrspace(200) [[OTHER]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: ret ptr addrspace(200) [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_set_of_get_bad +; HIGH-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(ptr addrspace(200) [[OTHER]]) +; HIGH-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; HIGH-NEXT: ret ptr addrspace(200) [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_set_of_get_bad +; OFFSET-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[OTHER]]) +; OFFSET-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; OFFSET-NEXT: ret ptr addrspace(200) [[RET]] +; + %value = tail call i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200) %other) + %ret = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %arg, i64 %value) + ret ptr addrspace(200) %ret +} + +;; For almost all intrinsics this get-of-set pair can be elided +;; NB: This is not true for flags where the value written by set could be truncated to another value +;; FIXME: high.get should be folded. +define i64 @fold_get_of_set(ptr addrspace(200) %arg, i64 %value) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_get_of_set +; ADDRESS-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: ret i64 [[VALUE]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_get_of_set +; FLAGS-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.flags.get.i64(ptr addrspace(200) [[MODIFIED]]) +; FLAGS-NEXT: ret i64 [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_get_of_set +; HIGH-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: ret i64 [[VALUE]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_get_of_set +; OFFSET-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; OFFSET-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[MODIFIED]]) +; OFFSET-NEXT: ret i64 [[RET]] +; + %modified = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %arg, i64 %value) + %ret = tail call i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200) %modified) + ret i64 %ret +} + +;; Negative test -- get is used on other intrinsic +define i64 @fold_get_of_set_bad(ptr addrspace(200) %arg, ptr addrspace(200) %other, i64 %value) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_get_of_set_bad +; ADDRESS-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.address.get.i64(ptr addrspace(200) [[ARG]]) +; ADDRESS-NEXT: ret i64 [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_get_of_set_bad +; FLAGS-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.flags.get.i64(ptr addrspace(200) [[ARG]]) +; FLAGS-NEXT: ret i64 [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_get_of_set_bad +; HIGH-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(ptr addrspace(200) [[ARG]]) +; HIGH-NEXT: ret i64 [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_get_of_set_bad +; OFFSET-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[ARG]]) +; OFFSET-NEXT: ret i64 [[RET]] +; + %modified = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %other, i64 %value) + %ret = tail call i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200) %arg) + ret i64 %ret +} + +define i64 @fold_get_on_null() nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_get_on_null +; ADDRESS-SAME: () addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: ret i64 0 +; +; FLAGS-LABEL: define {{[^@]+}}@fold_get_on_null +; FLAGS-SAME: () addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: ret i64 0 +; +; HIGH-LABEL: define {{[^@]+}}@fold_get_on_null +; HIGH-SAME: () addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: ret i64 0 +; +; OFFSET-LABEL: define {{[^@]+}}@fold_get_on_null +; OFFSET-SAME: () addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: ret i64 0 +; + %ret = tail call i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200) null) + ret i64 %ret +} + +define i64 @fold_get_on_null_with_gep(i64 %value, i64 %gepoff) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep +; ADDRESS-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: ret i64 [[GEPOFF]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep +; FLAGS-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: ret i64 0 +; +; HIGH-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep +; HIGH-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: ret i64 0 +; +; OFFSET-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep +; OFFSET-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: ret i64 [[GEPOFF]] +; + %tmp = getelementptr i8, ptr addrspace(200) null, i64 %gepoff + %ret = tail call i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200) %tmp) + ret i64 %ret +} + +;; TODO: Setting is idempotent, so the first call should be elided +define ptr addrspace(200) @set_idempotent(ptr addrspace(200) %arg, i64 %value) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@set_idempotent +; ADDRESS-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.address.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; ADDRESS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.address.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; ADDRESS-NEXT: ret ptr addrspace(200) [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@set_idempotent +; FLAGS-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; FLAGS-NEXT: ret ptr addrspace(200) [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@set_idempotent +; HIGH-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; HIGH-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; HIGH-NEXT: ret ptr addrspace(200) [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@set_idempotent +; OFFSET-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; OFFSET-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; OFFSET-NEXT: ret ptr addrspace(200) [[RET]] +; + %modified = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %arg, i64 %value) + %ret = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %modified, i64 %value) + ret ptr addrspace(200) %ret +} + + +;; Negative test - calls with the same base argument are not yet elided if the +;; second argument is different since they could have side-effects such as +;; clearing the tag. For example `setoffset(setoffset(x, ), )` +;; would result in an untagged value if the first setoffset went outside the +;; representable range but that detagging would not happen if we elide it. +;; TODO: Maybe we should do this fold since people should not be relying on +;; representability to clear tags of capabilities. +define ptr addrspace(200) @set_different_values(ptr addrspace(200) %arg, i64 %value, i64 %value2) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@set_different_values +; ADDRESS-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]], i64 [[VALUE2:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.address.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; ADDRESS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.address.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; ADDRESS-NEXT: ret ptr addrspace(200) [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@set_different_values +; FLAGS-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]], i64 [[VALUE2:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; FLAGS-NEXT: ret ptr addrspace(200) [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@set_different_values +; HIGH-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]], i64 [[VALUE2:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; HIGH-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; HIGH-NEXT: ret ptr addrspace(200) [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@set_different_values +; OFFSET-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]], i64 [[VALUE2:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; OFFSET-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; OFFSET-NEXT: ret ptr addrspace(200) [[RET]] +; + %modified = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %arg, i64 %value) + %ret = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %modified, i64 %value) + ret ptr addrspace(200) %ret +} + +define ptr addrspace(200) @fold_set_on_null(i64 %value) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_set_on_null +; ADDRESS-SAME: (i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.address.set.i64(ptr addrspace(200) null, i64 [[VALUE]]) +; ADDRESS-NEXT: ret ptr addrspace(200) [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_set_on_null +; FLAGS-SAME: (i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) null, i64 [[VALUE]]) +; FLAGS-NEXT: ret ptr addrspace(200) [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_set_on_null +; HIGH-SAME: (i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) null, i64 [[VALUE]]) +; HIGH-NEXT: ret ptr addrspace(200) [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_set_on_null +; OFFSET-SAME: (i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) null, i64 [[VALUE]]) +; OFFSET-NEXT: ret ptr addrspace(200) [[RET]] +; + %ret = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) null, i64 %value) + ret ptr addrspace(200) %ret +} + +define ptr addrspace(200) @fold_set_on_null_with_gep(i64 %value, i64 %gepoff) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_set_on_null_with_gep +; ADDRESS-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[TMP:%.*]] = getelementptr i8, ptr addrspace(200) null, i64 [[GEPOFF]] +; ADDRESS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.address.set.i64(ptr addrspace(200) [[TMP]], i64 [[VALUE]]) +; ADDRESS-NEXT: ret ptr addrspace(200) [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_set_on_null_with_gep +; FLAGS-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[TMP:%.*]] = getelementptr i8, ptr addrspace(200) null, i64 [[GEPOFF]] +; FLAGS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[TMP]], i64 [[VALUE]]) +; FLAGS-NEXT: ret ptr addrspace(200) [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_set_on_null_with_gep +; HIGH-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[TMP:%.*]] = getelementptr i8, ptr addrspace(200) null, i64 [[GEPOFF]] +; HIGH-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[TMP]], i64 [[VALUE]]) +; HIGH-NEXT: ret ptr addrspace(200) [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_set_on_null_with_gep +; OFFSET-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[TMP:%.*]] = getelementptr i8, ptr addrspace(200) null, i64 [[GEPOFF]] +; OFFSET-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[TMP]], i64 [[VALUE]]) +; OFFSET-NEXT: ret ptr addrspace(200) [[RET]] +; + %tmp = getelementptr i8, ptr addrspace(200) null, i64 %gepoff + %ret = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %tmp, i64 %value) + ret ptr addrspace(200) %ret +}