1 //===-- XtensaMCAsmBackend.cpp - Xtensa assembler backend -----------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 6 // See https://llvm.org/LICENSE.txt for license information. 7 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 8 // 9 //===----------------------------------------------------------------------===// 10 11 #include "MCTargetDesc/XtensaFixupKinds.h" 12 #include "MCTargetDesc/XtensaMCTargetDesc.h" 13 #include "llvm/MC/MCAsmBackend.h" 14 #include "llvm/MC/MCAssembler.h" 15 #include "llvm/MC/MCContext.h" 16 #include "llvm/MC/MCELFObjectWriter.h" 17 #include "llvm/MC/MCFixupKindInfo.h" 18 #include "llvm/MC/MCInst.h" 19 #include "llvm/MC/MCObjectWriter.h" 20 #include "llvm/MC/MCSubtargetInfo.h" 21 #include "llvm/Support/raw_ostream.h" 22 23 using namespace llvm; 24 25 namespace llvm { 26 class MCObjectTargetWriter; 27 class XtensaMCAsmBackend : public MCAsmBackend { 28 uint8_t OSABI; 29 bool IsLittleEndian; 30 31 public: 32 XtensaMCAsmBackend(uint8_t osABI, bool isLE) 33 : MCAsmBackend(llvm::endianness::little), OSABI(osABI), 34 IsLittleEndian(isLE) {} 35 36 unsigned getNumFixupKinds() const override { 37 return Xtensa::NumTargetFixupKinds; 38 } 39 const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; 40 void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, 41 const MCValue &Target, MutableArrayRef<char> Data, 42 uint64_t Value, bool IsResolved, 43 const MCSubtargetInfo *STI) const override; 44 bool mayNeedRelaxation(const MCInst &Inst, 45 const MCSubtargetInfo &STI) const override; 46 void relaxInstruction(MCInst &Inst, 47 const MCSubtargetInfo &STI) const override; 48 bool writeNopData(raw_ostream &OS, uint64_t Count, 49 const MCSubtargetInfo *STI) const override; 50 51 std::unique_ptr<MCObjectTargetWriter> createObjectTargetWriter() const override { 52 return createXtensaObjectWriter(OSABI, IsLittleEndian); 53 } 54 }; 55 } // namespace llvm 56 57 const MCFixupKindInfo & 58 XtensaMCAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { 59 const static MCFixupKindInfo Infos[Xtensa::NumTargetFixupKinds] = { 60 // name offset bits flags 61 {"fixup_xtensa_branch_6", 0, 16, MCFixupKindInfo::FKF_IsPCRel}, 62 {"fixup_xtensa_branch_8", 16, 8, MCFixupKindInfo::FKF_IsPCRel}, 63 {"fixup_xtensa_branch_12", 12, 12, MCFixupKindInfo::FKF_IsPCRel}, 64 {"fixup_xtensa_jump_18", 6, 18, MCFixupKindInfo::FKF_IsPCRel}, 65 {"fixup_xtensa_call_18", 6, 18, 66 MCFixupKindInfo::FKF_IsPCRel | 67 MCFixupKindInfo::FKF_IsAlignedDownTo32Bits}, 68 {"fixup_xtensa_l32r_16", 8, 16, 69 MCFixupKindInfo::FKF_IsPCRel | 70 MCFixupKindInfo::FKF_IsAlignedDownTo32Bits}}; 71 72 if (Kind < FirstTargetFixupKind) 73 return MCAsmBackend::getFixupKindInfo(Kind); 74 assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && 75 "Invalid kind!"); 76 return Infos[Kind - FirstTargetFixupKind]; 77 } 78 79 static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, 80 MCContext &Ctx) { 81 unsigned Kind = Fixup.getKind(); 82 switch (Kind) { 83 default: 84 llvm_unreachable("Unknown fixup kind!"); 85 case FK_Data_1: 86 case FK_Data_2: 87 case FK_Data_4: 88 case FK_Data_8: 89 return Value; 90 case Xtensa::fixup_xtensa_branch_6: { 91 Value -= 4; 92 if (!isInt<6>(Value)) 93 Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); 94 unsigned Hi2 = (Value >> 4) & 0x3; 95 unsigned Lo4 = Value & 0xf; 96 return (Hi2 << 4) | (Lo4 << 12); 97 } 98 case Xtensa::fixup_xtensa_branch_8: 99 Value -= 4; 100 if (!isInt<8>(Value)) 101 Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); 102 return (Value & 0xff); 103 case Xtensa::fixup_xtensa_branch_12: 104 Value -= 4; 105 if (!isInt<12>(Value)) 106 Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); 107 return (Value & 0xfff); 108 case Xtensa::fixup_xtensa_jump_18: 109 Value -= 4; 110 if (!isInt<18>(Value)) 111 Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); 112 return (Value & 0x3ffff); 113 case Xtensa::fixup_xtensa_call_18: 114 Value -= 4; 115 if (!isInt<20>(Value)) 116 Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); 117 if (Value & 0x3) 118 Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned"); 119 return (Value & 0xffffc) >> 2; 120 case Xtensa::fixup_xtensa_l32r_16: 121 unsigned Offset = Fixup.getOffset(); 122 if (Offset & 0x3) 123 Value -= 4; 124 if (!isInt<18>(Value) && (Value & 0x20000)) 125 Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); 126 if (Value & 0x3) 127 Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned"); 128 return (Value & 0x3fffc) >> 2; 129 } 130 } 131 132 static unsigned getSize(unsigned Kind) { 133 switch (Kind) { 134 default: 135 return 3; 136 case MCFixupKind::FK_Data_4: 137 return 4; 138 case Xtensa::fixup_xtensa_branch_6: 139 return 2; 140 } 141 } 142 143 void XtensaMCAsmBackend::applyFixup(const MCAssembler &Asm, 144 const MCFixup &Fixup, const MCValue &Target, 145 MutableArrayRef<char> Data, uint64_t Value, 146 bool IsResolved, 147 const MCSubtargetInfo *STI) const { 148 MCContext &Ctx = Asm.getContext(); 149 MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind()); 150 151 Value = adjustFixupValue(Fixup, Value, Ctx); 152 153 // Shift the value into position. 154 Value <<= Info.TargetOffset; 155 156 if (!Value) 157 return; // Doesn't change encoding. 158 159 unsigned Offset = Fixup.getOffset(); 160 unsigned FullSize = getSize(Fixup.getKind()); 161 162 for (unsigned i = 0; i != FullSize; ++i) { 163 Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff); 164 } 165 } 166 167 bool XtensaMCAsmBackend::mayNeedRelaxation(const MCInst &Inst, 168 const MCSubtargetInfo &STI) const { 169 return false; 170 } 171 172 void XtensaMCAsmBackend::relaxInstruction(MCInst &Inst, 173 const MCSubtargetInfo &STI) const {} 174 175 bool XtensaMCAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count, 176 const MCSubtargetInfo *STI) const { 177 uint64_t NumNops24b = Count / 3; 178 179 for (uint64_t i = 0; i != NumNops24b; ++i) { 180 // Currently just little-endian machine supported, 181 // but probably big-endian will be also implemented in future 182 if (IsLittleEndian) { 183 OS.write("\xf0", 1); 184 OS.write("\x20", 1); 185 OS.write("\0x00", 1); 186 } else { 187 report_fatal_error("Big-endian mode currently is not supported!"); 188 } 189 Count -= 3; 190 } 191 192 // TODO maybe function should return error if (Count > 0) 193 switch (Count) { 194 default: 195 break; 196 case 1: 197 OS.write("\0", 1); 198 break; 199 case 2: 200 // NOP.N instruction 201 OS.write("\x3d", 1); 202 OS.write("\xf0", 1); 203 break; 204 } 205 206 return true; 207 } 208 209 MCAsmBackend *llvm::createXtensaMCAsmBackend(const Target &T, 210 const MCSubtargetInfo &STI, 211 const MCRegisterInfo &MRI, 212 const MCTargetOptions &Options) { 213 uint8_t OSABI = 214 MCELFObjectTargetWriter::getOSABI(STI.getTargetTriple().getOS()); 215 return new llvm::XtensaMCAsmBackend(OSABI, true); 216 } 217