diff --git a/llvm/lib/Target/AVR/AVRInstrFormats.td b/llvm/lib/Target/AVR/AVRInstrFormats.td --- a/llvm/lib/Target/AVR/AVRInstrFormats.td +++ b/llvm/lib/Target/AVR/AVRInstrFormats.td @@ -188,19 +188,20 @@ // r = src/dst register // // Note that the bit labelled 'i' above does not follow a simple pattern, -// so there exists a post encoder method to set it manually. +// so it is an additional field in ptrreg which is encoded/decoded by +// LDSTPtrReg. //===---------------------------------------------------------------------===// class FSTLD mode, dag outs, dag ins, string asmstr, list pattern> : AVRInst16 { - bits<2> ptrreg; + bits<3> ptrreg; bits<5> reg; let Inst{15-13} = 0b100; - // This bit varies depending on the arguments and the mode. - // We have a post encoder method to set this bit manually. - let Inst{12} = 0; + // This bit varies depending on the pointer arguments and the mode. + // We have a operand encoder method to set this bit manually. + let Inst{12} = ptrreg{2}; let Inst{11-10} = 0b00; let Inst{9} = type; @@ -210,8 +211,6 @@ let Inst{3-2} = ptrreg{1-0}; let Inst{1-0} = mode{1-0}; - - let PostEncoderMethod = "loadStorePostEncoder"; } //===---------------------------------------------------------------------===// diff --git a/llvm/lib/Target/AVR/AVRInstrInfo.td b/llvm/lib/Target/AVR/AVRInstrInfo.td --- a/llvm/lib/Target/AVR/AVRInstrInfo.td +++ b/llvm/lib/Target/AVR/AVRInstrInfo.td @@ -167,6 +167,7 @@ let PrintMethod = "printMemri"; let EncoderMethod = "encodeMemri"; + let DecoderMethod = "decodeMemri"; let ParserMatchClass = MemriAsmOperand; } @@ -179,14 +180,18 @@ def relbrtarget_7 : Operand { + let OperandType = "OPERAND_PCREL"; let PrintMethod = "printPCRelImm"; let EncoderMethod = "encodeRelCondBrTarget"; + let DecoderMethod = "decodeRelCondBrTarget<7>"; } def brtarget_13 : Operand { + let OperandType = "OPERAND_PCREL"; let PrintMethod = "printPCRelImm"; let EncoderMethod = "encodeRelCondBrTarget"; + let DecoderMethod = "decodeRelCondBrTarget<13>"; } // The target of a 22 or 16-bit call/jmp instruction. @@ -246,6 +251,11 @@ { let MIOperandInfo = (ops PTRREGS); let EncoderMethod = "encodeLDSTPtrReg"; + let DecoderMethod = "decodeLDSTPtrReg"; + + // May try multiple instructions while figuring out the value of the + // inconsistent bit. + let hasCompleteDecoder = 0; let ParserMatchClass = PtrRegAsmOperand; } @@ -257,6 +267,7 @@ { let MIOperandInfo = (ops PTRDISPREGS); let EncoderMethod = "encodeLDSTPtrReg"; + let DecoderMethod = "decodeLDSTPtrReg"; let ParserMatchClass = PtrRegAsmOperand; } @@ -763,9 +774,9 @@ { def RJMPk : FBRk<0, (outs), - (ins brtarget_13:$target), - "rjmp\t$target", - [(br bb:$target)]>; + (ins brtarget_13:$k), + "rjmp\t$k", + [(br bb:$k)]>; let isIndirectBranch = 1, Uses = [R31R30] in @@ -803,8 +814,8 @@ let Uses = [SP] in def RCALLk : FBRk<1, (outs), - (ins brtarget_13:$target), - "rcall\t$target", + (ins brtarget_13:$k), + "rcall\t$k", []>; // SP is marked as a use to prevent stack-pointer assignments that appear @@ -1032,31 +1043,31 @@ def BREQk : FBRsk<0, 0b001, (outs), - (ins relbrtarget_7:$target), - "breq\t$target", - [(AVRbrcond bb:$target, AVR_COND_EQ)]>; + (ins relbrtarget_7:$k), + "breq\t$k", + [(AVRbrcond bb:$k, AVR_COND_EQ)]>; def BRNEk : FBRsk<1, 0b001, (outs), - (ins relbrtarget_7:$target), - "brne\t$target", - [(AVRbrcond bb:$target, AVR_COND_NE)]>; + (ins relbrtarget_7:$k), + "brne\t$k", + [(AVRbrcond bb:$k, AVR_COND_NE)]>; def BRSHk : FBRsk<1, 0b000, (outs), - (ins relbrtarget_7:$target), - "brsh\t$target", - [(AVRbrcond bb:$target, AVR_COND_SH)]>; + (ins relbrtarget_7:$k), + "brsh\t$k", + [(AVRbrcond bb:$k, AVR_COND_SH)]>; def BRLOk : FBRsk<0, 0b000, (outs), - (ins relbrtarget_7:$target), - "brlo\t$target", - [(AVRbrcond bb:$target, AVR_COND_LO)]>; + (ins relbrtarget_7:$k), + "brlo\t$k", + [(AVRbrcond bb:$k, AVR_COND_LO)]>; def BRMIk : FBRsk<0, 0b010, @@ -1068,23 +1079,23 @@ def BRPLk : FBRsk<1, 0b010, (outs), - (ins relbrtarget_7:$target), - "brpl\t$target", - [(AVRbrcond bb:$target, AVR_COND_PL)]>; + (ins relbrtarget_7:$k), + "brpl\t$k", + [(AVRbrcond bb:$k, AVR_COND_PL)]>; def BRGEk : FBRsk<1, 0b100, (outs), - (ins relbrtarget_7:$target), - "brge\t$target", - [(AVRbrcond bb:$target, AVR_COND_GE)]>; + (ins relbrtarget_7:$k), + "brge\t$k", + [(AVRbrcond bb:$k, AVR_COND_GE)]>; def BRLTk : FBRsk<0, 0b100, (outs), - (ins relbrtarget_7:$target), - "brlt\t$target", - [(AVRbrcond bb:$target, AVR_COND_LT)]>; + (ins relbrtarget_7:$k), + "brlt\t$k", + [(AVRbrcond bb:$k, AVR_COND_LT)]>; } //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/AVR/Disassembler/AVRDisassembler.cpp b/llvm/lib/Target/AVR/Disassembler/AVRDisassembler.cpp --- a/llvm/lib/Target/AVR/Disassembler/AVRDisassembler.cpp +++ b/llvm/lib/Target/AVR/Disassembler/AVRDisassembler.cpp @@ -90,11 +90,27 @@ static DecodeStatus DecodePTRREGSRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { - // Note: this function must be defined but does not seem to be called. - assert(false && "unimplemented: PTRREGS register class"); + unsigned Register; + switch (RegNo) { + case 0b00: + Register = AVR::R31R30; // Z + break; + case 0b10: + Register = AVR::R29R28; // Y + break; + case 0b11: + Register = AVR::R27R26; // X + break; + default: + return MCDisassembler::Fail; + } + Inst.addOperand(MCOperand::createReg(Register)); return MCDisassembler::Success; } +static DecodeStatus decodeMemri(MCInst &Inst, unsigned Insn, uint64_t Address, + const void *Decoder); + static DecodeStatus decodeFIOARr(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); @@ -104,9 +120,17 @@ static DecodeStatus decodeFIOBIT(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); +template +static DecodeStatus decodeRelCondBrTarget(MCInst &Inst, unsigned Insn, + uint64_t Address, + const void *Decoder); + static DecodeStatus decodeCallTarget(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); +static DecodeStatus decodeLDSTPtrReg(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); + static DecodeStatus decodeFRd(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); @@ -127,6 +151,21 @@ #include "AVRGenDisassemblerTables.inc" +static DecodeStatus decodeMemri(MCInst &Inst, unsigned Insn, uint64_t Address, + const void *Decoder) { + unsigned YOrZ = fieldFromInstruction(Insn, 6, 1); + unsigned Reg; + if (YOrZ == 0) { + Reg = AVR::R31R30; // Z + } else { + Reg = AVR::R29R28; // Y + } + unsigned displacement = fieldFromInstruction(Insn, 0, 6); + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createImm(displacement)); + return MCDisassembler::Success; +} + static DecodeStatus decodeFIOARr(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { unsigned addr = 0; @@ -160,6 +199,20 @@ return MCDisassembler::Success; } +template +static DecodeStatus decodeRelCondBrTarget(MCInst &Inst, unsigned Field, + uint64_t Address, + const void *Decoder) { + // Branch targets need to be shifted left by one + uint64_t Target = Field << 1; + // Sign extend + const uint64_t ValueMask = (1 << (Bits - 1)) - 1; + const uint64_t SignMask = static_cast(-1) - ValueMask; + Target = (Target & ValueMask) | ((Target & SignMask) ? SignMask : 0); + Inst.addOperand(MCOperand::createImm(static_cast(Target))); + return MCDisassembler::Success; +} + static DecodeStatus decodeCallTarget(MCInst &Inst, unsigned Field, uint64_t Address, const void *Decoder) { // Call targets need to be shifted left by one so this needs a custom @@ -168,6 +221,30 @@ return MCDisassembler::Success; } +// See AVRMCCodeEmitter::encodeLDSTPtrReg for more information. +static DecodeStatus decodeLDSTPtrReg(MCInst &Inst, unsigned Field, + uint64_t Address, const void *Decoder) { + unsigned RegNo = fieldFromInstruction(Field, 0, 2); + bool InconsistentBit = fieldFromInstruction(Field, 2, 1) != 0; + + unsigned Opcode = Inst.getOpcode(); + + // check whether the register is the X pointer register. + bool IsRegX = RegNo == 0x03; + + bool IsPredec = Opcode == AVR::LDRdPtrPd || Opcode == AVR::STPtrPdRr; + bool IsPostinc = Opcode == AVR::LDRdPtrPi || Opcode == AVR::STPtrPiRr; + + // Check if this is the right instruction for the value of the + // inconsistent bit. If it is not, fail and we will try to decode + // with a different opcode. + if ((IsRegX || IsPredec || IsPostinc) != InconsistentBit) { + return DecodeStatus::Fail; + } + + return DecodePTRREGSRegisterClass(Inst, RegNo, Address, Decoder); +} + static DecodeStatus decodeFRd(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { unsigned d = fieldFromInstruction(Insn, 4, 5); diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRInstrAnalysis.h b/llvm/lib/Target/AVR/MCTargetDesc/AVRInstrAnalysis.h new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRInstrAnalysis.h @@ -0,0 +1,31 @@ +//===- AVRInstrAnalysis.h - Analyze AVR MCInst ------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This class analyzes AVR MCInst. In particular, it implements evaluateBranch +// so llvm-objdump can show branch targets. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_AVR_INSTR_ANALYSIS_H +#define LLVM_AVR_INSTR_ANALYSIS_H + +#include "llvm/MC/MCInstrAnalysis.h" + +namespace llvm { + +class AVRMCInstrAnalysis : public MCInstrAnalysis { +public: + AVRMCInstrAnalysis(const MCInstrInfo *Info) : MCInstrAnalysis(Info) {} + + bool evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size, + uint64_t &Target) const override; +}; + +} // end namespace llvm + +#endif // LLVM_AVR_INSTR_ANALYSIS_H diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRInstrAnalysis.cpp b/llvm/lib/Target/AVR/MCTargetDesc/AVRInstrAnalysis.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRInstrAnalysis.cpp @@ -0,0 +1,52 @@ +//===- AVRInstrAnalysis.h - Analyze AVR MCInst ------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This class analyzes AVR MCInst. In particular, it implements evaluateBranch +// so llvm-objdump can show branch targets. +// +//===----------------------------------------------------------------------===// + +#include "AVRInstrAnalysis.h" + +#define GET_INSTRINFO_ENUM +#include "AVRGenInstrInfo.inc" + +namespace llvm { + +bool AVRMCInstrAnalysis::evaluateBranch(const MCInst &Inst, uint64_t Addr, + uint64_t Size, uint64_t &Target) const { + + switch (Inst.getOpcode()) { + case AVR::JMPk: + case AVR::CALLk: + Target = Inst.getOperand(0).getImm(); + break; + + case AVR::RJMPk: + case AVR::RCALLk: + case AVR::BRBSsk: + case AVR::BRBCsk: + case AVR::BREQk: + case AVR::BRNEk: + case AVR::BRSHk: + case AVR::BRLOk: + case AVR::BRMIk: + case AVR::BRPLk: + case AVR::BRGEk: + case AVR::BRLTk: + // Add 2 because it is relative to next executed instruction + Target = Addr + 2 + Inst.getOperand(0).getImm(); + break; + + default: + return false; + } + return true; +} + +} // namespace llvm \ No newline at end of file diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.h b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.h --- a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.h +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.h @@ -40,12 +40,6 @@ : MCII(MCII), Ctx(Ctx) {} private: - /// Finishes up encoding an LD/ST instruction. - /// The purpose of this function is to set an bit in the instruction - /// which follows no logical pattern. See the implementation for details. - unsigned loadStorePostEncoder(const MCInst &MI, unsigned EncodedValue, - const MCSubtargetInfo &STI) const; - /// Gets the encoding for a conditional branch target. template unsigned encodeRelCondBrTarget(const MCInst &MI, unsigned OpNo, diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp --- a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp @@ -36,7 +36,29 @@ namespace llvm { -/// Performs a post-encoding step on a `LD` or `ST` instruction. +template +unsigned +AVRMCCodeEmitter::encodeRelCondBrTarget(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + if (MO.isExpr()) { + Fixups.push_back( + MCFixup::create(0, MO.getExpr(), MCFixupKind(Fixup), MI.getLoc())); + return 0; + } + + assert(MO.isImm()); + + // Take the size of the current instruction away. + // With labels, this is implicitly done. + auto Target = MO.getImm(); + AVR::fixups::adjustBranchTarget(Target); + return Target; +} + +/// Encodes Operand for a `LD` or `ST` instruction. /// /// The encoding of the LD/ST family of instructions is inconsistent w.r.t /// the pointer register and the addressing mode. @@ -63,13 +85,31 @@ /// inconsistent_bit = is_predec OR is_postinc OR is_reg_x /// ``` // -/// We manually set this bit in this post encoder method. -unsigned -AVRMCCodeEmitter::loadStorePostEncoder(const MCInst &MI, unsigned EncodedValue, - const MCSubtargetInfo &STI) const { +/// We manually set this bit in this operand encoder. It is mapped +/// to bit 2 of the operand. +unsigned AVRMCCodeEmitter::encodeLDSTPtrReg(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + auto MO = MI.getOperand(OpNo); - assert(MI.getOperand(0).isReg() && MI.getOperand(1).isReg() && - "the load/store operands must be registers"); + // The operand should be a pointer register. + assert(MO.isReg()); + + unsigned EncodedValue; + + switch (MO.getReg()) { + case AVR::R27R26: + EncodedValue = 0x03; // X: 0b11 + break; + case AVR::R29R28: + EncodedValue = 0x02; // Y: 0b10 + break; + case AVR::R31R30: + EncodedValue = 0x00; // Z: 0b00 + break; + default: + llvm_unreachable("invalid pointer register"); + } unsigned Opcode = MI.getOpcode(); @@ -82,51 +122,12 @@ // Check if we need to set the inconsistent bit if (IsRegX || IsPredec || IsPostinc) { - EncodedValue |= (1 << 12); + EncodedValue |= (1 << 2); } return EncodedValue; } -template -unsigned -AVRMCCodeEmitter::encodeRelCondBrTarget(const MCInst &MI, unsigned OpNo, - SmallVectorImpl &Fixups, - const MCSubtargetInfo &STI) const { - const MCOperand &MO = MI.getOperand(OpNo); - - if (MO.isExpr()) { - Fixups.push_back(MCFixup::create(0, MO.getExpr(), - MCFixupKind(Fixup), MI.getLoc())); - return 0; - } - - assert(MO.isImm()); - - // Take the size of the current instruction away. - // With labels, this is implicitly done. - auto target = MO.getImm(); - AVR::fixups::adjustBranchTarget(target); - return target; -} - -unsigned AVRMCCodeEmitter::encodeLDSTPtrReg(const MCInst &MI, unsigned OpNo, - SmallVectorImpl &Fixups, - const MCSubtargetInfo &STI) const { - auto MO = MI.getOperand(OpNo); - - // The operand should be a pointer register. - assert(MO.isReg()); - - switch (MO.getReg()) { - case AVR::R27R26: return 0x03; // X: 0b11 - case AVR::R29R28: return 0x02; // Y: 0b10 - case AVR::R31R30: return 0x00; // Z: 0b00 - default: - llvm_unreachable("invalid pointer register"); - } -} - /// Encodes a `memri` operand. /// The operand is 7-bits. /// * The lower 6 bits is the immediate diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCTargetDesc.cpp b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCTargetDesc.cpp --- a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCTargetDesc.cpp +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCTargetDesc.cpp @@ -10,17 +10,19 @@ // //===----------------------------------------------------------------------===// +#include "AVRMCTargetDesc.h" #include "AVRELFStreamer.h" #include "AVRInstPrinter.h" +#include "AVRInstrAnalysis.h" #include "AVRMCAsmInfo.h" #include "AVRMCELFStreamer.h" -#include "AVRMCTargetDesc.h" #include "AVRTargetStreamer.h" #include "TargetInfo/AVRTargetInfo.h" #include "llvm/MC/MCAsmBackend.h" -#include "llvm/MC/MCELFStreamer.h" #include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCInstrAnalysis.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" @@ -44,6 +46,10 @@ return X; } +static MCInstrAnalysis *createAVRMCInstrAnalysis(const MCInstrInfo *Info) { + return new AVRMCInstrAnalysis(Info); +} + static MCRegisterInfo *createAVRMCRegisterInfo(const Triple &TT) { MCRegisterInfo *X = new MCRegisterInfo(); InitAVRMCRegisterInfo(X, 0); @@ -96,6 +102,10 @@ // Register the MC instruction info. TargetRegistry::RegisterMCInstrInfo(getTheAVRTarget(), createAVRMCInstrInfo); + // Register the MC instruction analysis + TargetRegistry::RegisterMCInstrAnalysis(getTheAVRTarget(), + createAVRMCInstrAnalysis); + // Register the MC register info. TargetRegistry::RegisterMCRegInfo(getTheAVRTarget(), createAVRMCRegisterInfo); diff --git a/llvm/lib/Target/AVR/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/AVR/MCTargetDesc/CMakeLists.txt --- a/llvm/lib/Target/AVR/MCTargetDesc/CMakeLists.txt +++ b/llvm/lib/Target/AVR/MCTargetDesc/CMakeLists.txt @@ -3,6 +3,7 @@ AVRELFObjectWriter.cpp AVRELFStreamer.cpp AVRInstPrinter.cpp + AVRInstrAnalysis.cpp AVRMCAsmInfo.cpp AVRMCCodeEmitter.cpp AVRMCELFStreamer.cpp