| //===-- AMDGPUAsmBackend.cpp - AMDGPU Assembler Backend -------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| /// \file | |
| //===----------------------------------------------------------------------===// | |
| #include "MCTargetDesc/AMDGPUFixupKinds.h" | |
| #include "MCTargetDesc/AMDGPUMCTargetDesc.h" | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/BinaryFormat/ELF.h" | |
| #include "llvm/MC/MCAsmBackend.h" | |
| #include "llvm/MC/MCAssembler.h" | |
| #include "llvm/MC/MCContext.h" | |
| #include "llvm/MC/MCFixupKindInfo.h" | |
| #include "llvm/MC/MCObjectWriter.h" | |
| #include "llvm/MC/MCValue.h" | |
| #include "llvm/Support/TargetRegistry.h" | |
| #include "Utils/AMDGPUBaseInfo.h" | |
| using namespace llvm; | |
| using namespace llvm::AMDGPU; | |
| namespace { | |
| class AMDGPUAsmBackend : public MCAsmBackend { | |
| public: | |
| AMDGPUAsmBackend(const Target &T) : MCAsmBackend(support::little) {} | |
| unsigned getNumFixupKinds() const override { return AMDGPU::NumTargetFixupKinds; }; | |
| void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, | |
| const MCValue &Target, MutableArrayRef<char> Data, | |
| uint64_t Value, bool IsResolved, | |
| const MCSubtargetInfo *STI) const override; | |
| bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, | |
| const MCRelaxableFragment *DF, | |
| const MCAsmLayout &Layout) const override; | |
| void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, | |
| MCInst &Res) const override; | |
| bool mayNeedRelaxation(const MCInst &Inst, | |
| const MCSubtargetInfo &STI) const override; | |
| unsigned getMinimumNopSize() const override; | |
| bool writeNopData(raw_ostream &OS, uint64_t Count) const override; | |
| const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; | |
| }; | |
| } //End anonymous namespace | |
| void AMDGPUAsmBackend::relaxInstruction(const MCInst &Inst, | |
| const MCSubtargetInfo &STI, | |
| MCInst &Res) const { | |
| unsigned RelaxedOpcode = AMDGPU::getSOPPWithRelaxation(Inst.getOpcode()); | |
| Res.setOpcode(RelaxedOpcode); | |
| Res.addOperand(Inst.getOperand(0)); | |
| return; | |
| } | |
| bool AMDGPUAsmBackend::fixupNeedsRelaxation(const MCFixup &Fixup, | |
| uint64_t Value, | |
| const MCRelaxableFragment *DF, | |
| const MCAsmLayout &Layout) const { | |
| // if the branch target has an offset of x3f this needs to be relaxed to | |
| // add a s_nop 0 immediately after branch to effectively increment offset | |
| // for hardware workaround in gfx1010 | |
| return (((int64_t(Value)/4)-1) == 0x3f); | |
| } | |
| bool AMDGPUAsmBackend::mayNeedRelaxation(const MCInst &Inst, | |
| const MCSubtargetInfo &STI) const { | |
| if (!STI.getFeatureBits()[AMDGPU::FeatureOffset3fBug]) | |
| return false; | |
| if (AMDGPU::getSOPPWithRelaxation(Inst.getOpcode()) >= 0) | |
| return true; | |
| return false; | |
| } | |
| static unsigned getFixupKindNumBytes(unsigned Kind) { | |
| switch (Kind) { | |
| case AMDGPU::fixup_si_sopp_br: | |
| return 2; | |
| case FK_SecRel_1: | |
| case FK_Data_1: | |
| return 1; | |
| case FK_SecRel_2: | |
| case FK_Data_2: | |
| return 2; | |
| case FK_SecRel_4: | |
| case FK_Data_4: | |
| case FK_PCRel_4: | |
| return 4; | |
| case FK_SecRel_8: | |
| case FK_Data_8: | |
| return 8; | |
| default: | |
| llvm_unreachable("Unknown fixup kind!"); | |
| } | |
| } | |
| static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, | |
| MCContext *Ctx) { | |
| int64_t SignedValue = static_cast<int64_t>(Value); | |
| switch (Fixup.getTargetKind()) { | |
| case AMDGPU::fixup_si_sopp_br: { | |
| int64_t BrImm = (SignedValue - 4) / 4; | |
| if (Ctx && !isInt<16>(BrImm)) | |
| Ctx->reportError(Fixup.getLoc(), "branch size exceeds simm16"); | |
| return BrImm; | |
| } | |
| case FK_Data_1: | |
| case FK_Data_2: | |
| case FK_Data_4: | |
| case FK_Data_8: | |
| case FK_PCRel_4: | |
| case FK_SecRel_4: | |
| return Value; | |
| default: | |
| llvm_unreachable("unhandled fixup kind"); | |
| } | |
| } | |
| void AMDGPUAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, | |
| const MCValue &Target, | |
| MutableArrayRef<char> Data, uint64_t Value, | |
| bool IsResolved, | |
| const MCSubtargetInfo *STI) const { | |
| Value = adjustFixupValue(Fixup, Value, &Asm.getContext()); | |
| if (!Value) | |
| return; // Doesn't change encoding. | |
| MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind()); | |
| // Shift the value into position. | |
| Value <<= Info.TargetOffset; | |
| unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind()); | |
| uint32_t Offset = Fixup.getOffset(); | |
| assert(Offset + NumBytes <= Data.size() && "Invalid fixup offset!"); | |
| // For each byte of the fragment that the fixup touches, mask in the bits from | |
| // the fixup value. | |
| for (unsigned i = 0; i != NumBytes; ++i) | |
| Data[Offset + i] |= static_cast<uint8_t>((Value >> (i * 8)) & 0xff); | |
| } | |
| const MCFixupKindInfo &AMDGPUAsmBackend::getFixupKindInfo( | |
| MCFixupKind Kind) const { | |
| const static MCFixupKindInfo Infos[AMDGPU::NumTargetFixupKinds] = { | |
| // name offset bits flags | |
| { "fixup_si_sopp_br", 0, 16, MCFixupKindInfo::FKF_IsPCRel }, | |
| }; | |
| if (Kind < FirstTargetFixupKind) | |
| return MCAsmBackend::getFixupKindInfo(Kind); | |
| return Infos[Kind - FirstTargetFixupKind]; | |
| } | |
| unsigned AMDGPUAsmBackend::getMinimumNopSize() const { | |
| return 4; | |
| } | |
| bool AMDGPUAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count) const { | |
| // If the count is not 4-byte aligned, we must be writing data into the text | |
| // section (otherwise we have unaligned instructions, and thus have far | |
| // bigger problems), so just write zeros instead. | |
| OS.write_zeros(Count % 4); | |
| // We are properly aligned, so write NOPs as requested. | |
| Count /= 4; | |
| // FIXME: R600 support. | |
| // s_nop 0 | |
| const uint32_t Encoded_S_NOP_0 = 0xbf800000; | |
| for (uint64_t I = 0; I != Count; ++I) | |
| support::endian::write<uint32_t>(OS, Encoded_S_NOP_0, Endian); | |
| return true; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // ELFAMDGPUAsmBackend class | |
| //===----------------------------------------------------------------------===// | |
| namespace { | |
| class ELFAMDGPUAsmBackend : public AMDGPUAsmBackend { | |
| bool Is64Bit; | |
| bool HasRelocationAddend; | |
| uint8_t OSABI = ELF::ELFOSABI_NONE; | |
| uint8_t ABIVersion = 0; | |
| public: | |
| ELFAMDGPUAsmBackend(const Target &T, const Triple &TT, uint8_t ABIVersion) : | |
| AMDGPUAsmBackend(T), Is64Bit(TT.getArch() == Triple::amdgcn), | |
| HasRelocationAddend(TT.getOS() == Triple::AMDHSA), | |
| ABIVersion(ABIVersion) { | |
| switch (TT.getOS()) { | |
| case Triple::AMDHSA: | |
| OSABI = ELF::ELFOSABI_AMDGPU_HSA; | |
| break; | |
| case Triple::AMDPAL: | |
| OSABI = ELF::ELFOSABI_AMDGPU_PAL; | |
| break; | |
| case Triple::Mesa3D: | |
| OSABI = ELF::ELFOSABI_AMDGPU_MESA3D; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| std::unique_ptr<MCObjectTargetWriter> | |
| createObjectTargetWriter() const override { | |
| return createAMDGPUELFObjectWriter(Is64Bit, OSABI, HasRelocationAddend, | |
| ABIVersion); | |
| } | |
| }; | |
| } // end anonymous namespace | |
| MCAsmBackend *llvm::createAMDGPUAsmBackend(const Target &T, | |
| const MCSubtargetInfo &STI, | |
| const MCRegisterInfo &MRI, | |
| const MCTargetOptions &Options) { | |
| // Use 64-bit ELF for amdgcn | |
| return new ELFAMDGPUAsmBackend(T, STI.getTargetTriple(), | |
| IsaInfo::hasCodeObjectV3(&STI) ? 1 : 0); | |
| } |