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 bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, 47 const MCRelaxableFragment *Fragment, 48 const MCAsmLayout &Layout) const override; 49 void relaxInstruction(MCInst &Inst, 50 const MCSubtargetInfo &STI) const override; 51 bool writeNopData(raw_ostream &OS, uint64_t Count, 52 const MCSubtargetInfo *STI) const override; 53 54 std::unique_ptr<MCObjectTargetWriter> createObjectTargetWriter() const override { 55 return createXtensaObjectWriter(OSABI, IsLittleEndian); 56 } 57 }; 58 } // namespace llvm 59 60 const MCFixupKindInfo & 61 XtensaMCAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { 62 const static MCFixupKindInfo Infos[Xtensa::NumTargetFixupKinds] = { 63 // name offset bits flags 64 {"fixup_xtensa_branch_6", 0, 16, MCFixupKindInfo::FKF_IsPCRel}, 65 {"fixup_xtensa_branch_8", 16, 8, MCFixupKindInfo::FKF_IsPCRel}, 66 {"fixup_xtensa_branch_12", 12, 12, MCFixupKindInfo::FKF_IsPCRel}, 67 {"fixup_xtensa_jump_18", 6, 18, MCFixupKindInfo::FKF_IsPCRel}, 68 {"fixup_xtensa_call_18", 6, 18, 69 MCFixupKindInfo::FKF_IsPCRel | 70 MCFixupKindInfo::FKF_IsAlignedDownTo32Bits}, 71 {"fixup_xtensa_l32r_16", 8, 16, 72 MCFixupKindInfo::FKF_IsPCRel | 73 MCFixupKindInfo::FKF_IsAlignedDownTo32Bits}}; 74 75 if (Kind < FirstTargetFixupKind) 76 return MCAsmBackend::getFixupKindInfo(Kind); 77 assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && 78 "Invalid kind!"); 79 return Infos[Kind - FirstTargetFixupKind]; 80 } 81 82 static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, 83 MCContext &Ctx) { 84 unsigned Kind = Fixup.getKind(); 85 switch (Kind) { 86 default: 87 llvm_unreachable("Unknown fixup kind!"); 88 case FK_Data_1: 89 case FK_Data_2: 90 case FK_Data_4: 91 case FK_Data_8: 92 return Value; 93 case Xtensa::fixup_xtensa_branch_6: { 94 Value -= 4; 95 if (!isInt<6>(Value)) 96 Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); 97 unsigned Hi2 = (Value >> 4) & 0x3; 98 unsigned Lo4 = Value & 0xf; 99 return (Hi2 << 4) | (Lo4 << 12); 100 } 101 case Xtensa::fixup_xtensa_branch_8: 102 Value -= 4; 103 if (!isInt<8>(Value)) 104 Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); 105 return (Value & 0xff); 106 case Xtensa::fixup_xtensa_branch_12: 107 Value -= 4; 108 if (!isInt<12>(Value)) 109 Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); 110 return (Value & 0xfff); 111 case Xtensa::fixup_xtensa_jump_18: 112 Value -= 4; 113 if (!isInt<18>(Value)) 114 Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); 115 return (Value & 0x3ffff); 116 case Xtensa::fixup_xtensa_call_18: 117 Value -= 4; 118 if (!isInt<20>(Value)) 119 Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); 120 if (Value & 0x3) 121 Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned"); 122 return (Value & 0xffffc) >> 2; 123 case Xtensa::fixup_xtensa_l32r_16: 124 unsigned Offset = Fixup.getOffset(); 125 if (Offset & 0x3) 126 Value -= 4; 127 if (!isInt<18>(Value) && (Value & 0x20000)) 128 Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); 129 if (Value & 0x3) 130 Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned"); 131 return (Value & 0x3fffc) >> 2; 132 } 133 } 134 135 static unsigned getSize(unsigned Kind) { 136 switch (Kind) { 137 default: 138 return 3; 139 case MCFixupKind::FK_Data_4: 140 return 4; 141 case Xtensa::fixup_xtensa_branch_6: 142 return 2; 143 } 144 } 145 146 void XtensaMCAsmBackend::applyFixup(const MCAssembler &Asm, 147 const MCFixup &Fixup, const MCValue &Target, 148 MutableArrayRef<char> Data, uint64_t Value, 149 bool IsResolved, 150 const MCSubtargetInfo *STI) const { 151 MCContext &Ctx = Asm.getContext(); 152 MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind()); 153 154 Value = adjustFixupValue(Fixup, Value, Ctx); 155 156 // Shift the value into position. 157 Value <<= Info.TargetOffset; 158 159 if (!Value) 160 return; // Doesn't change encoding. 161 162 unsigned Offset = Fixup.getOffset(); 163 unsigned FullSize = getSize(Fixup.getKind()); 164 165 for (unsigned i = 0; i != FullSize; ++i) { 166 Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff); 167 } 168 } 169 170 bool XtensaMCAsmBackend::mayNeedRelaxation(const MCInst &Inst, 171 const MCSubtargetInfo &STI) const { 172 return false; 173 } 174 175 bool XtensaMCAsmBackend::fixupNeedsRelaxation( 176 const MCFixup &Fixup, uint64_t Value, const MCRelaxableFragment *Fragment, 177 const MCAsmLayout &Layout) const { 178 return false; 179 } 180 181 void XtensaMCAsmBackend::relaxInstruction(MCInst &Inst, 182 const MCSubtargetInfo &STI) const {} 183 184 bool XtensaMCAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count, 185 const MCSubtargetInfo *STI) const { 186 uint64_t NumNops24b = Count / 3; 187 188 for (uint64_t i = 0; i != NumNops24b; ++i) { 189 // Currently just little-endian machine supported, 190 // but probably big-endian will be also implemented in future 191 if (IsLittleEndian) { 192 OS.write("\xf0", 1); 193 OS.write("\x20", 1); 194 OS.write("\0x00", 1); 195 } else { 196 report_fatal_error("Big-endian mode currently is not supported!"); 197 } 198 Count -= 3; 199 } 200 201 // TODO maybe function should return error if (Count > 0) 202 switch (Count) { 203 default: 204 break; 205 case 1: 206 OS.write("\0", 1); 207 break; 208 case 2: 209 // NOP.N instruction 210 OS.write("\x3d", 1); 211 OS.write("\xf0", 1); 212 break; 213 } 214 215 return true; 216 } 217 218 MCAsmBackend *llvm::createXtensaMCAsmBackend(const Target &T, 219 const MCSubtargetInfo &STI, 220 const MCRegisterInfo &MRI, 221 const MCTargetOptions &Options) { 222 uint8_t OSABI = 223 MCELFObjectTargetWriter::getOSABI(STI.getTargetTriple().getOS()); 224 return new llvm::XtensaMCAsmBackend(OSABI, true); 225 } 226