diff --git a/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp b/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp --- a/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp +++ b/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "MCTargetDesc/CSKYMCExpr.h" #include "MCTargetDesc/CSKYMCTargetDesc.h" #include "TargetInfo/CSKYTargetInfo.h" #include "llvm/ADT/STLExtras.h" @@ -58,6 +59,8 @@ OperandMatchResultTy parseImmediate(OperandVector &Operands); OperandMatchResultTy parseRegister(OperandVector &Operands); OperandMatchResultTy parseBaseRegImm(OperandVector &Operands); + OperandMatchResultTy parseCSKYSymbol(OperandVector &Operands); + OperandMatchResultTy parseConstpoolSymbol(OperandVector &Operands); bool parseOperand(OperandVector &Operands, StringRef Mnemonic); @@ -171,6 +174,20 @@ bool isUImm12Shift1() { return isUImm<12, 1>(); } bool isUImm12Shift2() { return isUImm<12, 2>(); } + bool isSImm16Shift1() { return isSImm<16, 1>(); } + + bool isCSKYSymbol() const { + int64_t Imm; + // Must be of 'immediate' type but not a constant. + return isImm() && !evaluateConstantImm(getImm(), Imm); + } + + bool isConstpoolSymbol() const { + int64_t Imm; + // Must be of 'immediate' type but not a constant. + return isImm() && !evaluateConstantImm(getImm(), Imm); + } + /// Gets location of the first token of this operand. SMLoc getStartLoc() const override { return StartLoc; } /// Gets location of the last token of this operand. @@ -350,6 +367,14 @@ "immediate must be a multiple of 4 bytes in the range"); case Match_InvalidUImm16: return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 16) - 1); + case Match_InvalidCSKYSymbol: { + SMLoc ErrorLoc = ((CSKYOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, "operand must be a symbol name"); + } + case Match_InvalidConstpool: { + SMLoc ErrorLoc = ((CSKYOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, "operand must be a constpool symbol name"); + } } llvm_unreachable("Unknown match type detected!"); @@ -482,6 +507,15 @@ /// information, adding to Operands. If operand was parsed, returns false, else /// true. bool CSKYAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic) { + // Check if the current operand has a custom associated parser, if so, try to + // custom parse the operand, or fallback to the general approach. + OperandMatchResultTy Result = + MatchOperandParserImpl(Operands, Mnemonic, /*ParseForAllFeatures=*/true); + if (Result == MatchOperand_Success) + return false; + if (Result == MatchOperand_ParseFail) + return true; + // Attempt to parse token as register if (parseRegister(Operands) == MatchOperand_Success) return false; @@ -500,6 +534,68 @@ return true; } +OperandMatchResultTy CSKYAsmParser::parseCSKYSymbol(OperandVector &Operands) { + SMLoc S = getLoc(); + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + + if (getLexer().getKind() != AsmToken::Identifier) + return MatchOperand_NoMatch; + + StringRef Identifier; + if (getParser().parseIdentifier(Identifier)) + return MatchOperand_ParseFail; + + CSKYMCExpr::VariantKind Kind = CSKYMCExpr::VK_CSKY_None; + + if (Identifier.consume_back("@GOT")) + Kind = CSKYMCExpr::VK_CSKY_GOT; + else if (Identifier.consume_back("@GOTOFF")) + Kind = CSKYMCExpr::VK_CSKY_GOTOFF; + else if (Identifier.consume_back("@PLT")) + Kind = CSKYMCExpr::VK_CSKY_PLT; + else if (Identifier.consume_back("@GOTPC")) + Kind = CSKYMCExpr::VK_CSKY_GOTPC; + + MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier); + const MCExpr *Res = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); + + if (Kind != CSKYMCExpr::VK_CSKY_None) + Res = CSKYMCExpr::create(Res, Kind, getContext()); + + Operands.push_back(CSKYOperand::createImm(Res, S, E)); + return MatchOperand_Success; +} + +OperandMatchResultTy +CSKYAsmParser::parseConstpoolSymbol(OperandVector &Operands) { + SMLoc S = getLoc(); + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + + if (getLexer().getKind() != AsmToken::LBrac) + return MatchOperand_NoMatch; + + getLexer().Lex(); // Eat '['. + + if (getLexer().getKind() != AsmToken::Identifier) + return MatchOperand_NoMatch; + + StringRef Identifier; + if (getParser().parseIdentifier(Identifier)) + return MatchOperand_ParseFail; + + if (getLexer().getKind() != AsmToken::RBrac) + return MatchOperand_NoMatch; + + getLexer().Lex(); // Eat ']'. + + MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier); + const MCExpr *Res = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); + Operands.push_back(CSKYOperand::createImm(Res, S, E)); + return MatchOperand_Success; +} + bool CSKYAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands) { // First operand is token for instruction. diff --git a/llvm/lib/Target/CSKY/CSKYInstrFormats.td b/llvm/lib/Target/CSKY/CSKYInstrFormats.td --- a/llvm/lib/Target/CSKY/CSKYInstrFormats.td +++ b/llvm/lib/Target/CSKY/CSKYInstrFormats.td @@ -54,13 +54,14 @@ pattern> { bits<26> offset; let Inst{25 - 0} = offset; + let isCall = 1; + let Defs = [ R15 ]; } // Format< OP[6] | RZ[5] | SOP[3] | OFFSET[18] > // Instructions(7): grs, lrs32.b, lrs32.h, lrs32.w, srs32.b, srs32.h, srs32.w -class I_18_Z_L sop, string op, dag outs, dag ins, list pattern> - : CSKY32Inst { +class I_18_Z_L sop, string asm, dag outs, dag ins, list pattern> + : CSKY32Inst { bits<5> rz; bits<18> offset; let Inst{25 - 21} = rz; @@ -102,7 +103,7 @@ // Instructions(1): lrw32 class I_16_Z_L sop, string op, dag ins, list pattern> : CSKY32Inst { + !strconcat(op, "\t$rz, $imm16"), pattern> { bits<5> rz; bits<16> imm16; let Inst{25 - 21} = sop; @@ -112,9 +113,8 @@ // Format< OP[6] | SOP[5] | 00000[5] | OFFSET[16] > // Instructions(5): bt32, bf32, br32, jmpi32, jsri32 -class I_16_L sop, dag outs, dag ins, string op, list pattern> - : CSKY32Inst { +class I_16_L sop, dag outs, dag ins, string asm, list pattern> + : CSKY32Inst { bits<16> imm16; let Inst{25 - 21} = sop; let Inst{20 - 16} = 0; @@ -159,6 +159,19 @@ let isBarrier = 1; } +// Instructions(1): rte32 +class I_16_RET_I sop, bits<5> pcode, string op, list pattern> + : CSKY32Inst { + let Inst{25 - 21} = sop; + let Inst{20 - 16} = pcode; + let Inst{15 - 10} = 0x10; + let Inst{9 - 5} = 1; + let Inst{4 - 0} = 0; + let isTerminator = 1; + let isReturn = 1; + let isBarrier = 1; +} + // Format< OP[6] | SOP[5] | RX[5] | IMM16[16] > // Instructions(3): cmpnei32, cmphsi32, cmplti32 class I_16_X sop, string op, Operand operand> diff --git a/llvm/lib/Target/CSKY/CSKYInstrInfo.td b/llvm/lib/Target/CSKY/CSKYInstrInfo.td --- a/llvm/lib/Target/CSKY/CSKYInstrInfo.td +++ b/llvm/lib/Target/CSKY/CSKYInstrInfo.td @@ -15,7 +15,9 @@ // CSKY specific DAG Nodes. //===----------------------------------------------------------------------===// -// TODO: Add CSKY specific DAG Nodes. +// Target-dependent nodes. +def CSKY_RET : SDNode<"CSKYISD::RET", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; //===----------------------------------------------------------------------===// // Operand and SDNode transformation definitions. @@ -81,6 +83,41 @@ let ParserMatchClass = UImmAsmOperand<2>; } +def CSKYSymbol : AsmOperandClass { + let Name = "CSKYSymbol"; + let RenderMethod = "addImmOperands"; + let DiagnosticType = "InvalidCSKYSymbol"; + let ParserMethod = "parseCSKYSymbol"; +} + +def br_symbol : Operand { + let EncoderMethod = + "getBranchSymbolOpValue"; + let ParserMatchClass = CSKYSymbol; +} + +def call_symbol : Operand { + let ParserMatchClass = CSKYSymbol; + let EncoderMethod = "getCallSymbolOpValue"; +} + +def Constpool : AsmOperandClass { + let Name = "ConstpoolSymbol"; + let RenderMethod = "addImmOperands"; + let DiagnosticType = "InvalidConstpool"; + let ParserMethod = "parseConstpoolSymbol"; +} + +def constpool_symbol : Operand { + let ParserMatchClass = Constpool; + let EncoderMethod = + "getConstpoolSymbolOpValue"; +} + +def bare_symbol : Operand { + let ParserMatchClass = CSKYSymbol; + let EncoderMethod = "getBareSymbolOpValue"; +} def oimm12 : oimm<12>; def oimm16 : oimm<16>; @@ -229,3 +266,69 @@ def MVC32 : R_Z_1<0x1, 0x8, "mvc32">; def MVCV32 : R_Z_1<0x1, 0x10, "mvcv32">; + +//===----------------------------------------------------------------------===// +// Branch and call instructions. +//===----------------------------------------------------------------------===// + +let isBranch = 1, isTerminator = 1 in { + let isBarrier = 1, isPredicable = 1 in + def BR32 : I_16_L<0x0, (outs), (ins br_symbol:$imm16), "br32\t$imm16", + [(br bb:$imm16)]>; + + def BT32 : I_16_L<0x3, (outs), (ins CARRY:$ca, br_symbol:$imm16), + "bt32\t$imm16", [(brcond CARRY:$ca, bb:$imm16)]>; + def BF32 : I_16_L<0x2, (outs), (ins CARRY:$ca, br_symbol:$imm16), + "bf32\t$imm16", []>; +} + + +def BEZ32 : I_16_X_L<0x8, "bez32", br_symbol>; +def BNEZ32 : I_16_X_L<0x9, "bnez32", br_symbol>; +def BHZ32 : I_16_X_L<0xA, "bhz32", br_symbol>; +def BLSZ32 : I_16_X_L<0xB, "blsz32", br_symbol>; +def BLZ32 : I_16_X_L<0xC, "blz32", br_symbol>; +def BHSZ32 : I_16_X_L<0xD, "bhsz32", br_symbol>; + +let isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 1 in { + def JMP32 : I_16_JX<0x6, "jmp32", [(brind GPR:$rx)]>; // jmp to register + def JMPI32 : I_16_L<0x16, (outs), (ins constpool_symbol:$imm16), + "jmpi32\t$imm16", []>; +} + +let isCall = 1, Defs = [ R15 ] in + def JSR32 : I_16_JX<0x7, "jsr32", []>; + +let isCall = 1, Defs = [ R15 ] , mayLoad = 1 in + def JSRI32: I_16_L<0x17, (outs), + (ins constpool_symbol:$imm16), "jsri32\t$imm16", []>; + + +def BSR32 : J<0x38, (outs), (ins call_symbol:$offset), "bsr32", []>; + +def BSR32_BR : J<0x38, (outs), (ins call_symbol:$offset), "bsr32", []>{ + let isCodeGenOnly = 1; + let isBranch = 1; + let isTerminator = 1; + let isBarrier = 1; + let isPredicable = 1; + let Defs = [ R15 ]; +} + +def RTS32 : I_16_RET<0x6, 0xF, "rts32", [(CSKY_RET)]>; + + +def RTE32 : I_16_RET_I<0, 0, "rte32", []>; + +//===----------------------------------------------------------------------===// +// Symbol address instructions. +//===----------------------------------------------------------------------===// + +def GRS32 : I_18_Z_L<0x3, "grs32\t$rz, $offset", + (outs GPR:$rz), (ins bare_symbol:$offset), []>; + +let mayLoad = 1, mayStore = 0 in { +def LRW32 : I_16_Z_L<0x14, "lrw32", (ins constpool_symbol:$imm16), []>; +let isCodeGenOnly = 1 in +def LRW32_Gen : I_16_Z_L<0x14, "lrw32", (ins bare_symbol:$src1, constpool_symbol:$imm16), []>; +} \ No newline at end of file diff --git a/llvm/lib/Target/CSKY/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/CSKY/MCTargetDesc/CMakeLists.txt --- a/llvm/lib/Target/CSKY/MCTargetDesc/CMakeLists.txt +++ b/llvm/lib/Target/CSKY/MCTargetDesc/CMakeLists.txt @@ -3,6 +3,7 @@ CSKYELFObjectWriter.cpp CSKYInstPrinter.cpp CSKYMCAsmInfo.cpp + CSKYMCExpr.cpp CSKYMCTargetDesc.cpp CSKYMCCodeEmitter.cpp diff --git a/llvm/lib/Target/CSKY/MCTargetDesc/CSKYAsmBackend.h b/llvm/lib/Target/CSKY/MCTargetDesc/CSKYAsmBackend.h --- a/llvm/lib/Target/CSKY/MCTargetDesc/CSKYAsmBackend.h +++ b/llvm/lib/Target/CSKY/MCTargetDesc/CSKYAsmBackend.h @@ -9,6 +9,7 @@ #ifndef LLVM_LIB_TARGET_CSKY_MCTARGETDESC_CSKYASMBACKEND_H #define LLVM_LIB_TARGET_CSKY_MCTARGETDESC_CSKYASMBACKEND_H +#include "MCTargetDesc/CSKYFixupKinds.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCTargetOptions.h" @@ -20,17 +21,26 @@ CSKYAsmBackend(const MCSubtargetInfo &STI, const MCTargetOptions &OP) : MCAsmBackend(support::little) {} - unsigned int getNumFixupKinds() const override; + unsigned int getNumFixupKinds() const override { + return CSKY::NumTargetFixupKinds; + } + void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef Data, uint64_t Value, bool IsResolved, const MCSubtargetInfo *STI) const override; + + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; + bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, const MCRelaxableFragment *DF, const MCAsmLayout &Layout) const override; + void relaxInstruction(MCInst &Inst, const MCSubtargetInfo &STI) const override; + bool writeNopData(raw_ostream &OS, uint64_t Count) const override; + std::unique_ptr createObjectTargetWriter() const override; }; diff --git a/llvm/lib/Target/CSKY/MCTargetDesc/CSKYAsmBackend.cpp b/llvm/lib/Target/CSKY/MCTargetDesc/CSKYAsmBackend.cpp --- a/llvm/lib/Target/CSKY/MCTargetDesc/CSKYAsmBackend.cpp +++ b/llvm/lib/Target/CSKY/MCTargetDesc/CSKYAsmBackend.cpp @@ -8,6 +8,7 @@ #include "CSKYAsmBackend.h" #include "MCTargetDesc/CSKYMCTargetDesc.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" @@ -24,14 +25,113 @@ return createCSKYELFObjectWriter(); } -unsigned int CSKYAsmBackend::getNumFixupKinds() const { return 1; } +const MCFixupKindInfo & +CSKYAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { + + static llvm::DenseMap Infos = { + {CSKY::Fixups::fixup_csky_addr32, {"fixup_csky_addr32", 0, 32, 0}}, + {CSKY::Fixups::fixup_csky_pcrel_imm16_scale2, + {"fixup_csky_pcrel_imm16_scale2", 0, 32, MCFixupKindInfo::FKF_IsPCRel}}, + {CSKY::Fixups::fixup_csky_pcrel_uimm16_scale4, + {"fixup_csky_pcrel_uimm16_scale4", 0, 32, MCFixupKindInfo::FKF_IsPCRel}}, + {CSKY::Fixups::fixup_csky_pcrel_imm26_scale2, + {"fixup_csky_pcrel_imm26_scale2", 0, 32, MCFixupKindInfo::FKF_IsPCRel}}, + {CSKY::Fixups::fixup_csky_pcrel_imm18_scale2, + {"fixup_csky_pcrel_imm18_scale2", 0, 32, MCFixupKindInfo::FKF_IsPCRel}}}; + assert(Infos.size() == CSKY::NumTargetFixupKinds && + "Not all fixup kinds added to Infos array"); + + assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && + "Invalid kind!"); + if (FirstTargetFixupKind <= Kind && Kind < FirstLiteralRelocationKind) + return Infos[Kind]; + else if (Kind < FirstTargetFixupKind) + return MCAsmBackend::getFixupKindInfo(Kind); + else + return MCAsmBackend::getFixupKindInfo(FK_NONE); +} + +static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, + MCContext &Ctx) { + switch (Fixup.getTargetKind()) { + default: + llvm_unreachable("Unknown fixup kind!"); + case FK_Data_1: + case FK_Data_2: + case FK_Data_4: + case FK_Data_8: + return Value; + case CSKY::fixup_csky_addr32: + return Value & 0xffffffff; + case CSKY::fixup_csky_pcrel_imm16_scale2: + if (!isIntN(17, Value)) + Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value."); + if (Value & 0x1) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned."); + + return (Value >> 1) & 0xffff; + case CSKY::fixup_csky_pcrel_uimm16_scale4: + if (!isUIntN(18, Value)) + Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value."); + if (Value & 0x3) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned."); + + return (Value >> 2) & 0xffff; + case CSKY::fixup_csky_pcrel_imm26_scale2: + if (!isIntN(27, Value)) + Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value."); + if (Value & 0x1) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned."); + + return (Value >> 1) & 0x3ffffff; + case CSKY::fixup_csky_pcrel_imm18_scale2: + if (!isIntN(19, Value)) + Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value."); + if (Value & 0x1) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned."); + + return (Value >> 1) & 0x3ffff; + } +} void CSKYAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef Data, uint64_t Value, bool IsResolved, const MCSubtargetInfo *STI) const { - return; + MCFixupKind Kind = Fixup.getKind(); + if (Kind >= FirstLiteralRelocationKind) + return; + MCContext &Ctx = Asm.getContext(); + MCFixupKindInfo Info = getFixupKindInfo(Kind); + if (!Value) + return; // Doesn't change encoding. + // Apply any target-specific value adjustments. + Value = adjustFixupValue(Fixup, Value, Ctx); + + // Shift the value into position. + Value <<= Info.TargetOffset; + + unsigned Offset = Fixup.getOffset(); + unsigned NumBytes = alignTo(Info.TargetSize + Info.TargetOffset, 8) / 8; + + 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. + bool IsLittleEndian = (Endian == support::little); + + if (IsLittleEndian && (NumBytes == 4)) { + Data[Offset + 0] |= uint8_t((Value >> 16) & 0xff); + Data[Offset + 1] |= uint8_t((Value >> 24) & 0xff); + Data[Offset + 2] |= uint8_t(Value & 0xff); + Data[Offset + 3] |= uint8_t((Value >> 8) & 0xff); + } else { + for (unsigned I = 0; I != NumBytes; I++) { + unsigned Idx = IsLittleEndian ? I : (NumBytes - 1 - I); + Data[Offset + Idx] |= uint8_t((Value >> (I * 8)) & 0xff); + } + } } bool CSKYAsmBackend::fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, diff --git a/llvm/lib/Target/CSKY/MCTargetDesc/CSKYFixupKinds.h b/llvm/lib/Target/CSKY/MCTargetDesc/CSKYFixupKinds.h new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/CSKY/MCTargetDesc/CSKYFixupKinds.h @@ -0,0 +1,34 @@ +//===-- CSKYFixupKinds.h - CSKY Specific Fixup Entries ----------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_CSKY_MCTARGETDESC_CSKYFIXUPKINDS_H +#define LLVM_LIB_TARGET_CSKY_MCTARGETDESC_CSKYFIXUPKINDS_H + +#include "llvm/MC/MCFixup.h" + +namespace llvm { +namespace CSKY { +enum Fixups { + fixup_csky_addr32 = FirstTargetFixupKind, + + fixup_csky_pcrel_imm16_scale2, + + fixup_csky_pcrel_uimm16_scale4, + + fixup_csky_pcrel_imm26_scale2, + + fixup_csky_pcrel_imm18_scale2, + + // Marker + fixup_csky_invalid, + NumTargetFixupKinds = fixup_csky_invalid - FirstTargetFixupKind +}; +} // end namespace CSKY +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_CSKY_MCTARGETDESC_CSKYFIXUPKINDS_H diff --git a/llvm/lib/Target/CSKY/MCTargetDesc/CSKYMCCodeEmitter.h b/llvm/lib/Target/CSKY/MCTargetDesc/CSKYMCCodeEmitter.h --- a/llvm/lib/Target/CSKY/MCTargetDesc/CSKYMCCodeEmitter.h +++ b/llvm/lib/Target/CSKY/MCTargetDesc/CSKYMCCodeEmitter.h @@ -13,6 +13,8 @@ #ifndef LLVM_LIB_TARGET_CSKY_MCTARGETDESC_CSKYMCCODEEMITTER_H #define LLVM_LIB_TARGET_CSKY_MCTARGETDESC_CSKYMCCODEEMITTER_H +#include "CSKYMCExpr.h" +#include "MCTargetDesc/CSKYFixupKinds.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" @@ -62,6 +64,70 @@ assert(MO.isImm() && "Unexpected MO type."); return 1 << MO.getImm(); } + + MCFixupKind getTargetFixup(const MCExpr *Expr) const; + + template + unsigned getBranchSymbolOpValue(const MCInst &MI, unsigned Idx, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(Idx); + + if (MO.isImm()) + return MO.getImm() >> 1; + + assert(MO.isExpr() && "Unexpected MO type."); + + MCFixupKind Kind = MCFixupKind(FIXUP); + if (MO.getExpr()->getKind() == MCExpr::Target) + Kind = getTargetFixup(MO.getExpr()); + + Fixups.push_back(MCFixup::create(0, MO.getExpr(), Kind, MI.getLoc())); + return 0; + } + + template + unsigned getConstpoolSymbolOpValue(const MCInst &MI, unsigned Idx, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(Idx); + assert(MO.isExpr() && "Unexpected MO type."); + + MCFixupKind Kind = MCFixupKind(FIXUP); + if (MO.getExpr()->getKind() == MCExpr::Target) + Kind = getTargetFixup(MO.getExpr()); + + Fixups.push_back(MCFixup::create(0, MO.getExpr(), Kind, MI.getLoc())); + return 0; + } + + unsigned getCallSymbolOpValue(const MCInst &MI, unsigned Idx, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(Idx); + assert(MO.isExpr() && "Unexpected MO type."); + + MCFixupKind Kind = MCFixupKind(CSKY::fixup_csky_pcrel_imm26_scale2); + if (MO.getExpr()->getKind() == MCExpr::Target) + Kind = getTargetFixup(MO.getExpr()); + + Fixups.push_back(MCFixup::create(0, MO.getExpr(), Kind, MI.getLoc())); + return 0; + } + + unsigned getBareSymbolOpValue(const MCInst &MI, unsigned Idx, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(Idx); + assert(MO.isExpr() && "Unexpected MO type."); + + MCFixupKind Kind = MCFixupKind(CSKY::fixup_csky_pcrel_imm18_scale2); + if (MO.getExpr()->getKind() == MCExpr::Target) + Kind = getTargetFixup(MO.getExpr()); + + Fixups.push_back(MCFixup::create(0, MO.getExpr(), Kind, MI.getLoc())); + return 0; + } }; } // namespace llvm diff --git a/llvm/lib/Target/CSKY/MCTargetDesc/CSKYMCCodeEmitter.cpp b/llvm/lib/Target/CSKY/MCTargetDesc/CSKYMCCodeEmitter.cpp --- a/llvm/lib/Target/CSKY/MCTargetDesc/CSKYMCCodeEmitter.cpp +++ b/llvm/lib/Target/CSKY/MCTargetDesc/CSKYMCCodeEmitter.cpp @@ -62,6 +62,17 @@ return 0; } +MCFixupKind CSKYMCCodeEmitter::getTargetFixup(const MCExpr *Expr) const { + const CSKYMCExpr *CSKYExpr = cast(Expr); + + switch (CSKYExpr->getKind()) { + default: + llvm_unreachable("Unhandled fixup kind!"); + case CSKYMCExpr::VK_CSKY_ADDR: + return MCFixupKind(CSKY::fixup_csky_addr32); + } +} + MCCodeEmitter *llvm::createCSKYMCCodeEmitter(const MCInstrInfo &MCII, const MCRegisterInfo &MRI, MCContext &Ctx) { diff --git a/llvm/lib/Target/CSKY/MCTargetDesc/CSKYMCExpr.h b/llvm/lib/Target/CSKY/MCTargetDesc/CSKYMCExpr.h new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/CSKY/MCTargetDesc/CSKYMCExpr.h @@ -0,0 +1,69 @@ +//===-- CSKYMCExpr.h - CSKY specific MC expression classes -*- C++ -*----===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_LANAI_MCTARGETDESC_LANAIMCEXPR_H +#define LLVM_LIB_TARGET_LANAI_MCTARGETDESC_LANAIMCEXPR_H + +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCValue.h" + +namespace llvm { + +class CSKYMCExpr : public MCTargetExpr { +public: + enum VariantKind { + VK_CSKY_None, + VK_CSKY_ADDR, + VK_CSKY_PCREL, + VK_CSKY_GOT, + VK_CSKY_GOTPC, + VK_CSKY_GOTOFF, + VK_CSKY_PLT, + VK_CSKY_TPOFF, + VK_CSKY_TLSGD, + VK_CSKY_Invalid + }; + +private: + const VariantKind Kind; + const MCExpr *Expr; + + explicit CSKYMCExpr(VariantKind Kind, const MCExpr *Expr) + : Kind(Kind), Expr(Expr) {} + +public: + static const CSKYMCExpr *create(const MCExpr *Expr, VariantKind Kind, + MCContext &Ctx); + + // Returns the kind of this expression. + VariantKind getKind() const { return Kind; } + + // Returns the child of this expression. + const MCExpr *getSubExpr() const { return Expr; } + + void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; + + bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, + const MCFixup *Fixup) const override; + void visitUsedExpr(MCStreamer &Streamer) const override; + + MCFragment *findAssociatedFragment() const override { + return getSubExpr()->findAssociatedFragment(); + } + + void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const override; + + static bool classof(const MCExpr *E) { + return E->getKind() == MCExpr::Target; + } + + static StringRef getVariantKindName(VariantKind Kind); +}; +} // end namespace llvm + +#endif diff --git a/llvm/lib/Target/CSKY/MCTargetDesc/CSKYMCExpr.cpp b/llvm/lib/Target/CSKY/MCTargetDesc/CSKYMCExpr.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/CSKY/MCTargetDesc/CSKYMCExpr.cpp @@ -0,0 +1,122 @@ +//===-- CSKYMCExpr.cpp - CSKY specific MC expression classes -*- C++ -*----===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "CSKYMCExpr.h" +#include "CSKYFixupKinds.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbolELF.h" + +using namespace llvm; + +#define DEBUG_TYPE "csky-mc-expr" + +const CSKYMCExpr *CSKYMCExpr::create(const MCExpr *Expr, VariantKind Kind, + MCContext &Ctx) { + return new (Ctx) CSKYMCExpr(Kind, Expr); +} + +StringRef CSKYMCExpr::getVariantKindName(VariantKind Kind) { + switch (Kind) { + default: + llvm_unreachable("Invalid ELF symbol kind"); + case VK_CSKY_ADDR: + return ""; + case VK_CSKY_PCREL: + return ""; + case VK_CSKY_GOT: + return "@GOT"; + case VK_CSKY_GOTPC: + return "@GOTPC"; + case VK_CSKY_GOTOFF: + return "@GOTOFF"; + case VK_CSKY_PLT: + return "@PLT"; + case VK_CSKY_TPOFF: + return "@TPOFF"; + case VK_CSKY_TLSGD: + return "@TLSGD"; + } +} + +void CSKYMCExpr::visitUsedExpr(MCStreamer &Streamer) const { + Streamer.visitUsedExpr(*getSubExpr()); +} + +void CSKYMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { + Expr->print(OS, MAI); + OS << getVariantKindName(getKind()); +} + +static void fixELFSymbolsInTLSFixupsImpl(const MCExpr *Expr, MCAssembler &Asm) { + switch (Expr->getKind()) { + case MCExpr::Target: + llvm_unreachable("Can't handle nested target expression"); + break; + case MCExpr::Constant: + break; + + case MCExpr::Binary: { + const MCBinaryExpr *BE = cast(Expr); + fixELFSymbolsInTLSFixupsImpl(BE->getLHS(), Asm); + fixELFSymbolsInTLSFixupsImpl(BE->getRHS(), Asm); + break; + } + + case MCExpr::SymbolRef: { + // We're known to be under a TLS fixup, so any symbol should be + // modified. There should be only one. + const MCSymbolRefExpr &SymRef = *cast(Expr); + cast(SymRef.getSymbol()).setType(ELF::STT_TLS); + break; + } + + case MCExpr::Unary: + fixELFSymbolsInTLSFixupsImpl(cast(Expr)->getSubExpr(), Asm); + break; + } +} + +void CSKYMCExpr::fixELFSymbolsInTLSFixups(MCAssembler &Asm) const { + switch (getKind()) { + default: + return; + case VK_CSKY_TPOFF: + case VK_CSKY_TLSGD: + break; + } + + fixELFSymbolsInTLSFixupsImpl(getSubExpr(), Asm); +} + +bool CSKYMCExpr::evaluateAsRelocatableImpl(MCValue &Res, + const MCAsmLayout *Layout, + const MCFixup *Fixup) const { + if (!getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup)) + return false; + + // Some custom fixup types are not valid with symbol difference expressions + if (Res.getSymA() && Res.getSymB()) { + switch (getKind()) { + default: + return true; + + case VK_CSKY_ADDR: + case VK_CSKY_PCREL: + case VK_CSKY_GOT: + case VK_CSKY_GOTPC: + case VK_CSKY_GOTOFF: + case VK_CSKY_TPOFF: + case VK_CSKY_TLSGD: + return false; + } + } + + return true; +} \ No newline at end of file diff --git a/llvm/test/MC/CSKY/basic.s b/llvm/test/MC/CSKY/basic.s --- a/llvm/test/MC/CSKY/basic.s +++ b/llvm/test/MC/CSKY/basic.s @@ -240,6 +240,102 @@ # CHECK-ASM: encoding: [0x04,0xc4,0xe3,0x58] sext32 a3, l0, 7, 0 +# CHECK-ASM: br32 .L.test +# CHECK-ASM: encoding: [A,0xe8'A',A,A] +# CHECK-ASM: fixup A - offset: 0, value: .L.test, kind: fixup_csky_pcrel_imm16_scale2 +.L.test: +br32 .L.test + +# CHECK-ASM: bt32 .L.test2 +# CHECK-ASM: encoding: [0x60'A',0xe8'A',A,A] +# CHECK-ASM: fixup A - offset: 0, value: .L.test2, kind: fixup_csky_pcrel_imm16_scale2 +.L.test2: +bt32 .L.test2 + +# CHECK-ASM: bf32 .L.test3 +# CHECK-ASM: encoding: [0x40'A',0xe8'A',A,A] +# CHECK-ASM: fixup A - offset: 0, value: .L.test3, kind: fixup_csky_pcrel_imm16_scale2 +.L.test3: +bf32 .L.test3 + +# CHECK-ASM: bez32 a0, .L.test4 +# CHECK-ASM: encoding: [A,0xe9'A',A,A] +# CHECK-ASM: fixup A - offset: 0, value: .L.test4, kind: fixup_csky_pcrel_imm16_scale2 +.L.test4: +bez32 a0, .L.test4 + +# CHECK-ASM: bnez32 a0, .L.test5 +# CHECK-ASM: encoding: [0x20'A',0xe9'A',A,A] +# CHECK-ASM: fixup A - offset: 0, value: .L.test5, kind: fixup_csky_pcrel_imm16_scale2 +.L.test5: +bnez32 a0, .L.test5 + +# CHECK-ASM: bhz32 a0, .L.test6 +# CHECK-ASM: encoding: [0x40'A',0xe9'A',A,A] +# CHECK-ASM: fixup A - offset: 0, value: .L.test6, kind: fixup_csky_pcrel_imm16_scale2 +.L.test6: +bhz32 a0, .L.test6 + +# CHECK-ASM: blsz32 a0, .L.test7 +# CHECK-ASM: encoding: [0x60'A',0xe9'A',A,A] +# CHECK-ASM: fixup A - offset: 0, value: .L.test7, kind: fixup_csky_pcrel_imm16_scale2 +.L.test7: +blsz32 a0, .L.test7 + +# CHECK-ASM: blz32 a0, .L.test8 +# CHECK-ASM: encoding: [0x80'A',0xe9'A',A,A] +# CHECK-ASM: fixup A - offset: 0, value: .L.test8, kind: fixup_csky_pcrel_imm16_scale2 +.L.test8: +blz32 a0, .L.test8 + +# CHECK-ASM: bhsz32 a0, .L.test9 +# CHECK-ASM: encoding: [0xa0'A',0xe9'A',A,A] +# CHECK-ASM: fixup A - offset: 0, value: .L.test9, kind: fixup_csky_pcrel_imm16_scale2 +.L.test9: +bhsz32 a0, .L.test9 + +# CHECK-ASM: jmp32 a3 +# CHECK-ASM: encoding: [0xc3,0xe8,0x00,0x00] +jmp32 a3 + +# CHECK-ASM: jmpi32 .L.test10 +# CHECK-ASM: encoding: [0xc0'A',0xea'A',A,A] +# CHECK-ASM: fixup A - offset: 0, value: .L.test10, kind: fixup_csky_pcrel_uimm16_scale4 +.L.test10: +jmpi32 [.L.test10] + +# CHECK-ASM: bsr32 .L.test11 +# CHECK-ASM: encoding: [A,0xe0'A',A,A] +# CHECK-ASM: fixup A - offset: 0, value: .L.test11, kind: fixup_csky_pcrel_imm26_scale2 +.L.test11: +bsr32 .L.test11 + +# CHECK-ASM: jsr32 a3 +# CHECK-ASM: encoding: [0xe3,0xe8,0x00,0x00] +jsr32 a3 + +# CHECK-ASM: jsri32 .L.test12 +# CHECK-ASM: encoding: [0xe0'A',0xea'A',A,A] +# CHECK-ASM: fixup A - offset: 0, value: .L.test12, kind: fixup_csky_pcrel_uimm16_scale4 +.L.test12: +jsri32 [.L.test12] + +# CHECK-ASM: rts32 +# CHECK-ASM: encoding: [0xcf,0xe8,0x00,0x00] +rts32 + +# CHECK-ASM: grs32 a0, .L.test13 +# CHECK-ASM: encoding: [0x0c'A',0xcc'A',A,A] +# CHECK-ASM: fixup A - offset: 0, value: .L.test13, kind: fixup_csky_pcrel_imm18_scale2 +.L.test13: +grs32 a0, .L.test13 + +# CHECK-ASM: lrw32 a0, .L.test14 +# CHECK-ASM: encoding: [0x80'A',0xea'A',A,A] +# CHECK-ASM: fixup A - offset: 0, value: .L.test14, kind: fixup_csky_pcrel_uimm16_scale4 +.L.test14: +lrw32 a0, [.L.test14] + # RUN: not llvm-mc -triple csky --defsym=ERR=1 < %s 2>&1 | FileCheck %s .ifdef ERR @@ -308,4 +404,9 @@ xori32 a0, a1 # CHECK: :[[#@LINE]]:1: error: too few operands for instruction xor32 a0, a2 # CHECK: :[[#@LINE]]:1: error: too few operands for instruction +# Need label +br32 0x100 # CHECK: :[[@LINE]]:6: error: operand must be a symbol name +jmpi32 0x100 # CHECK: :[[@LINE]]:8: error: operand must be a constpool symbol name +bsr32 0x100 # CHECK: :[[@LINE]]:7: error: operand must be a symbol name + .endif diff --git a/llvm/test/MC/CSKY/csky-error.s b/llvm/test/MC/CSKY/csky-error.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/CSKY/csky-error.s @@ -0,0 +1,80 @@ +# RUN: not llvm-mc -triple=csky %s -filetype=obj -o %t.o 2>&1 | FileCheck %s + +# Out of PC range + +# br/bt/bf + +.L.test1: +.space 0x10001 +br32 .L.test1 # CHECK: :[[@LINE]]:1: error: out of range pc-relative fixup value + # CHECK: :[[@LINE-1]]:1: error: fixup value must be 2-byte aligned + +br32 .L.test2 # CHECK: :[[@LINE]]:1: error: out of range pc-relative fixup value + # CHECK: :[[@LINE-1]]:1: error: fixup value must be 2-byte aligned +.space 0x10001 +.L.test2: + +.L.test3: +.space 0xFFFF +br32 .L.test3 # CHECK: :[[@LINE]]:1: error: fixup value must be 2-byte aligned + +.L.test4: +.space 0x10002 +br32 .L.test4 # CHECK: :[[@LINE]]:1: error: out of range pc-relative fixup value + +# bsr +.L.test5: +.space 0x4000001 +bsr32 .L.test5 # CHECK: :[[@LINE]]:1: error: out of range pc-relative fixup value + # CHECK: :[[@LINE-1]]:1: error: fixup value must be 2-byte aligned + +bsr32 .L.test6 # CHECK: :[[@LINE]]:1: error: out of range pc-relative fixup value + # CHECK: :[[@LINE-1]]:1: error: fixup value must be 2-byte aligned +.space 0x4000001 +.L.test6: + +.L.test7: +.space 0x3FFFFFF +bsr32 .L.test7 # CHECK: :[[@LINE]]:1: error: fixup value must be 2-byte aligned + +.L.test8: +.space 0x4000002 +bsr32 .L.test8 # CHECK: :[[@LINE]]:1: error: out of range pc-relative fixup value + +# grs +.L.test9: +.space 0x40001 +grs32 a0, .L.test9 # CHECK: :[[@LINE]]:1: error: out of range pc-relative fixup value + # CHECK: :[[@LINE-1]]:1: error: fixup value must be 2-byte aligned + +grs32 a0, .L.test10 # CHECK: :[[@LINE]]:1: error: out of range pc-relative fixup value + # CHECK: :[[@LINE-1]]:1: error: fixup value must be 2-byte aligned +.space 0x40001 +.L.test10: + +.L.test11: +.space 0x3FFFF +grs32 a0, .L.test11 # CHECK: :[[@LINE]]:1: error: fixup value must be 2-byte aligned + +.L.test12: +.space 0x40002 +grs32 a0, .L.test12 # CHECK: :[[@LINE]]:1: error: out of range pc-relative fixup value + + +# TODO: Fixup +lrw32 a0, [.L.test15] # CHECK: :[[@LINE]]:1: error: out of range pc-relative fixup value + # CHECK: :[[@LINE-1]]:1: error: fixup value must be 4-byte aligned +.space 0x40001 +.L.test15: + +# TODO: Fixup +jsri32 [.L.test16] # CHECK: :[[@LINE]]:1: error: out of range pc-relative fixup value + # CHECK: :[[@LINE-1]]:1: error: fixup value must be 4-byte aligned +.space 0x40001 +.L.test16: + +# TODO: Fixup +jmpi32 [.L.test17] # CHECK: :[[@LINE]]:1: error: out of range pc-relative fixup value + # CHECK: :[[@LINE-1]]:1: error: fixup value must be 4-byte aligned +.space 0x40001 +.L.test17: