diff --git a/llvm/lib/Target/PowerPC/AsmParser/PPCAsmParser.cpp b/llvm/lib/Target/PowerPC/AsmParser/PPCAsmParser.cpp --- a/llvm/lib/Target/PowerPC/AsmParser/PPCAsmParser.cpp +++ b/llvm/lib/Target/PowerPC/AsmParser/PPCAsmParser.cpp @@ -356,6 +356,12 @@ bool isS16ImmX16() const { return Kind == Expression || (Kind == Immediate && isInt<16>(getImm()) && (getImm() & 15) == 0); } + bool isS34Imm() const { + // Once the PC-Rel ABI is finalized, evaluate whether a 34-bit + // ContextImmediate is needed. + return Kind == Expression || (Kind == Immediate && isInt<34>(getImm())); + } + bool isS17Imm() const { switch (Kind) { case Expression: @@ -388,6 +394,7 @@ bool isCondBr() const { return Kind == Expression || (Kind == Immediate && isInt<16>(getImm()) && (getImm() & 3) == 0); } + bool isImmZero() const { return Kind == Immediate && getImm() == 0; } bool isRegNumber() const { return Kind == Immediate && isUInt<5>(getImm()); } bool isVSRegNumber() const { return Kind == Immediate && isUInt<6>(getImm()); diff --git a/llvm/lib/Target/PowerPC/Disassembler/PPCDisassembler.cpp b/llvm/lib/Target/PowerPC/Disassembler/PPCDisassembler.cpp --- a/llvm/lib/Target/PowerPC/Disassembler/PPCDisassembler.cpp +++ b/llvm/lib/Target/PowerPC/Disassembler/PPCDisassembler.cpp @@ -191,6 +191,14 @@ return MCDisassembler::Success; } +static DecodeStatus decodeImmZeroOperand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + if (Imm != 0) + return MCDisassembler::Fail; + Inst.addOperand(MCOperand::createImm(Imm)); + return MCDisassembler::Success; +} + static DecodeStatus decodeMemRIOperands(MCInst &Inst, uint64_t Imm, int64_t Address, const void *Decoder) { // Decode the memri field (imm, reg), which has the low 16-bits as the @@ -324,6 +332,29 @@ ArrayRef Bytes, uint64_t Address, raw_ostream &CS) const { + auto *ReadFunc = IsLittleEndian ? support::endian::read32le + : support::endian::read32be; + + // If this is an 8-byte prefixed instruction, handle it here. + // Note: prefixed instructions aren't technically 8-byte entities - the prefix + // appears in memory at an address 4 bytes prior to that of the base + // instruction regardless of endianness. So we read the two pieces and + // rebuild the 8-byte instruction. + // TODO: In this function we call decodeInstruction several times with + // different decoder tables. It may be possible to only call once by + // looking at the top 6 bits of the instruction. + if (STI.getFeatureBits()[PPC::FeaturePrefixInstrs] && Bytes.size() >= 8) { + uint32_t Prefix = ReadFunc(Bytes.data()); + uint32_t BaseInst = ReadFunc(Bytes.data() + 4); + uint64_t Inst = BaseInst | (uint64_t)Prefix << 32; + DecodeStatus result = decodeInstruction(DecoderTable64, MI, Inst, Address, + this, STI); + if (result != MCDisassembler::Fail) { + Size = 8; + return result; + } + } + // Get the four bytes of the instruction. Size = 4; if (Bytes.size() < 4) { @@ -332,8 +363,7 @@ } // Read the instruction in the proper endianness. - uint32_t Inst = IsLittleEndian ? support::endian::read32le(Bytes.data()) - : support::endian::read32be(Bytes.data()); + uint64_t Inst = ReadFunc(Bytes.data()); if (STI.getFeatureBits()[PPC::FeatureQPX]) { DecodeStatus result = diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.h b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.h --- a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.h +++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.h @@ -61,7 +61,9 @@ void printU10ImmOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O); void printU12ImmOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O); void printS16ImmOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O); + void printS34ImmOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O); void printU16ImmOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O); + void printImmZeroOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O); void printBranchOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O); void printAbsBranchOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O); void printTLSCall(const MCInst *MI, unsigned OpNo, raw_ostream &O); diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.cpp b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.cpp --- a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.cpp +++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.cpp @@ -339,6 +339,13 @@ O << (int)Value; } +void PPCInstPrinter::printImmZeroOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + unsigned int Value = MI->getOperand(OpNo).getImm(); + assert(Value == 0 && "Operand must be zero"); + O << (unsigned int)Value; +} + void PPCInstPrinter::printU5ImmOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O) { unsigned int Value = MI->getOperand(OpNo).getImm(); @@ -391,6 +398,13 @@ printOperand(MI, OpNo, O); } +void PPCInstPrinter::printS34ImmOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + long long Value = MI->getOperand(OpNo).getImm(); + assert(isInt<34>(Value) && "Invalid s34imm argument!"); + O << (long long)Value; +} + void PPCInstPrinter::printU16ImmOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O) { if (MI->getOperand(OpNo).isImm()) diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.h b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.h --- a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.h +++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.h @@ -80,7 +80,7 @@ /// getMachineOpValue - Return binary encoding of operand. If the machine /// operand requires relocation, record the relocation and return zero. - unsigned getMachineOpValue(const MCInst &MI, const MCOperand &MO, + uint64_t getMachineOpValue(const MCInst &MI, const MCOperand &MO, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.cpp b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.cpp --- a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.cpp +++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.cpp @@ -257,7 +257,7 @@ return ~0U; // Silence any warnings about no return. } -unsigned PPCMCCodeEmitter:: +uint64_t PPCMCCodeEmitter:: getMachineOpValue(const MCInst &MI, const MCOperand &MO, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { diff --git a/llvm/lib/Target/PowerPC/PPC.td b/llvm/lib/Target/PowerPC/PPC.td --- a/llvm/lib/Target/PowerPC/PPC.td +++ b/llvm/lib/Target/PowerPC/PPC.td @@ -209,6 +209,11 @@ "VectorsUseTwoUnits", "true", "Vectors use two units">; +def FeaturePrefixInstrs : SubtargetFeature<"prefix-instrs", "HasPrefixInstrs", + "true", + "Enable prefixed instructions", + [FeatureISA3_0, FeatureP8Vector, + FeatureP9Altivec]>; // Since new processors generally contain a superset of features of those that // came before them, the idea is to make implementations of new processors @@ -298,7 +303,7 @@ // For future CPU we assume that all of the existing features from Power 9 // still exist with the exception of those we know are Power 9 specific. list FutureAdditionalFeatures = []; - list FutureSpecificFeatures = []; + list FutureSpecificFeatures = [FeaturePrefixInstrs]; list FutureInheritableFeatures = !listconcat(P9InheritableFeatures, FutureAdditionalFeatures); list FutureFeatures = diff --git a/llvm/lib/Target/PowerPC/PPCInstrFormats.td b/llvm/lib/Target/PowerPC/PPCInstrFormats.td --- a/llvm/lib/Target/PowerPC/PPCInstrFormats.td +++ b/llvm/lib/Target/PowerPC/PPCInstrFormats.td @@ -41,6 +41,10 @@ bits<1> XFormMemOp = 0; let TSFlags{6} = XFormMemOp; + // Indicate that this instruction is prefixed. + bits<1> Prefixed = 0; + let TSFlags{7} = Prefixed; + // Fields used for relation models. string BaseName = ""; diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.h b/llvm/lib/Target/PowerPC/PPCInstrInfo.h --- a/llvm/lib/Target/PowerPC/PPCInstrInfo.h +++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.h @@ -65,7 +65,9 @@ NewDef_Shift = 6, /// This instruction is an X-Form memory operation. - XFormMemOp = 0x1 << NewDef_Shift + XFormMemOp = 0x1 << NewDef_Shift, + /// This instruction is prefixed. + Prefixed = 0x1 << (NewDef_Shift+1) }; } // end namespace PPCII diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.td b/llvm/lib/Target/PowerPC/PPCInstrInfo.td --- a/llvm/lib/Target/PowerPC/PPCInstrInfo.td +++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.td @@ -723,6 +723,26 @@ let ParserMatchClass = PPCS17ImmAsmOperand; let DecoderMethod = "decodeSImmOperand<16>"; } +def PPCS34ImmAsmOperand : AsmOperandClass { + let Name = "S34Imm"; + let PredicateMethod = "isS34Imm"; + let RenderMethod = "addImmOperands"; +} +def s34imm : Operand { + let PrintMethod = "printS34ImmOperand"; + let ParserMatchClass = PPCS34ImmAsmOperand; + let DecoderMethod = "decodeSImmOperand<34>"; +} +def PPCImmZeroAsmOperand : AsmOperandClass { + let Name = "ImmZero"; + let PredicateMethod = "isImmZero"; + let RenderMethod = "addImmOperands"; +} +def immZero : Operand { + let PrintMethod = "printImmZeroOperand"; + let ParserMatchClass = PPCImmZeroAsmOperand; + let DecoderMethod = "decodeImmZeroOperand"; +} def fpimm0 : PatLeaf<(fpimm), [{ return N->isExactlyValue(+0.0); }]>; @@ -3331,6 +3351,10 @@ def : Pat<(not i1:$in), (crnot $in)>; +// Prefixed instructions may require access to the above defs at a later +// time so we include this after the def. +include "PPCInstrPrefix.td" + // Patterns for arithmetic i1 operations. def : Pat<(add i1:$a, i1:$b), (CRXOR $a, $b)>; diff --git a/llvm/lib/Target/PowerPC/PPCInstrPrefix.td b/llvm/lib/Target/PowerPC/PPCInstrPrefix.td new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/PowerPC/PPCInstrPrefix.td @@ -0,0 +1,123 @@ +// PC Relative flag (for instructions that use the address of the prefix for +// address computations). +class isPCRel { bit PCRel = 1; } + +// Top-level class for prefixed instructions. +class PI pref, bits<6> opcode, dag OOL, dag IOL, string asmstr, + InstrItinClass itin> : Instruction { + field bits<64> Inst; + field bits<64> SoftFail = 0; + bit PCRel = 0; // Default value, set by isPCRel. + let Size = 8; + + let Namespace = "PPC"; + let OutOperandList = OOL; + let InOperandList = IOL; + let AsmString = asmstr; + let Itinerary = itin; + let Inst{0-5} = pref; + let Inst{32-37} = opcode; + + bits<1> PPC970_First = 0; + bits<1> PPC970_Single = 0; + bits<1> PPC970_Cracked = 0; + bits<3> PPC970_Unit = 0; + + /// These fields correspond to the fields in PPCInstrInfo.h. Any changes to + /// these must be reflected there! See comments there for what these are. + let TSFlags{0} = PPC970_First; + let TSFlags{1} = PPC970_Single; + let TSFlags{2} = PPC970_Cracked; + let TSFlags{5-3} = PPC970_Unit; + + bits<1> Prefixed = 1; // This is a prefixed instruction. + let TSFlags{7} = Prefixed; + + // For cases where multiple instruction definitions really represent the + // same underlying instruction but with one definition for 64-bit arguments + // and one for 32-bit arguments, this bit breaks the degeneracy between + // the two forms and allows TableGen to generate mapping tables. + bit Interpretation64Bit = 0; + + // Fields used for relation models. + string BaseName = ""; +} + +class MLS_DForm_R_SI34_RTA5 opcode, dag OOL, dag IOL, string asmstr, + InstrItinClass itin, list pattern> + : PI<1, opcode, OOL, IOL, asmstr, itin> { + bits<5> RT; + bits<5> RA; + bits<34> SI; + + let Pattern = pattern; + + // The prefix. + let Inst{6-7} = 2; + let Inst{8-10} = 0; + let Inst{11} = PCRel; + let Inst{12-13} = 0; + let Inst{14-31} = SI{33-16}; + + // The instruction. + let Inst{38-42} = RT; + let Inst{43-47} = RA; + let Inst{48-63} = SI{15-0}; +} + +class MLS_DForm2_r0 opcode, dag OOL, dag IOL, string asmstr, + InstrItinClass itin, list pattern> + : PI<1, opcode, OOL, IOL, asmstr, itin> { + bits<5> RT; + bits<34> SI; + + let Pattern = pattern; + + // The prefix. + let Inst{6-7} = 2; + let Inst{8-10} = 0; + let Inst{11} = 0; + let Inst{12-13} = 0; + let Inst{14-31} = SI{33-16}; + + // The instruction. + let Inst{38-42} = RT; + let Inst{43-47} = 0; + let Inst{48-63} = SI{15-0}; +} + +multiclass MLS_DForm_R_SI34_RTA5_p opcode, dag OOL, dag IOL, + dag PCRel_IOL, string asmstr, + InstrItinClass itin> { + def NAME : MLS_DForm_R_SI34_RTA5; + def pc : MLS_DForm_R_SI34_RTA5, isPCRel; +} + + +def PrefixInstrs : Predicate<"PPCSubTarget->hasPrefixInstrs()">; + +let Predicates = [PrefixInstrs] in { + let Interpretation64Bit = 1, isCodeGenOnly = 1 in { + defm PADDI8 : + MLS_DForm_R_SI34_RTA5_p<14, (outs g8rc:$RT), (ins g8rc:$RA, s34imm:$SI), + (ins immZero:$RA, s34imm:$SI), + "paddi $RT, $RA, $SI", IIC_LdStLFD>; + let isReMaterializable = 1, isAsCheapAsAMove = 1, isMoveImm = 1 in { + def PLI8 : MLS_DForm2_r0<14, (outs g8rc:$RT), + (ins s34imm:$SI), + "pli $RT, $SI", IIC_IntSimple, []>; + } + } + defm PADDI : + MLS_DForm_R_SI34_RTA5_p<14, (outs gprc:$RT), (ins gprc:$RA, s34imm:$SI), + (ins immZero:$RA, s34imm:$SI), + "paddi $RT, $RA, $SI", IIC_LdStLFD>; + let isReMaterializable = 1, isAsCheapAsAMove = 1, isMoveImm = 1 in { + def PLI : MLS_DForm2_r0<14, (outs gprc:$RT), + (ins s34imm:$SI), + "pli $RT, $SI", IIC_IntSimple, []>; + } +} + diff --git a/llvm/lib/Target/PowerPC/PPCScheduleP9.td b/llvm/lib/Target/PowerPC/PPCScheduleP9.td --- a/llvm/lib/Target/PowerPC/PPCScheduleP9.td +++ b/llvm/lib/Target/PowerPC/PPCScheduleP9.td @@ -40,9 +40,9 @@ let CompleteModel = 1; - // Do not support QPX (Quad Processing eXtension) or SPE (Signal Procesing - // Engine) on Power 9. - let UnsupportedFeatures = [HasQPX, HasSPE]; + // Do not support QPX (Quad Processing eXtension), SPE (Signal Procesing + // Engine) or prefixed instructions on Power 9. + let UnsupportedFeatures = [HasQPX, HasSPE, PrefixInstrs]; } diff --git a/llvm/lib/Target/PowerPC/PPCSubtarget.h b/llvm/lib/Target/PowerPC/PPCSubtarget.h --- a/llvm/lib/Target/PowerPC/PPCSubtarget.h +++ b/llvm/lib/Target/PowerPC/PPCSubtarget.h @@ -105,6 +105,7 @@ bool HasP8Crypto; bool HasP9Vector; bool HasP9Altivec; + bool HasPrefixInstrs; bool HasFCPSGN; bool HasFSQRT; bool HasFRE, HasFRES, HasFRSQRTE, HasFRSQRTES; @@ -255,6 +256,7 @@ bool hasP8Crypto() const { return HasP8Crypto; } bool hasP9Vector() const { return HasP9Vector; } bool hasP9Altivec() const { return HasP9Altivec; } + bool hasPrefixInstrs() const { return HasPrefixInstrs; } bool hasMFOCRF() const { return HasMFOCRF; } bool hasISEL() const { return HasISEL; } bool hasBPERMD() const { return HasBPERMD; } diff --git a/llvm/lib/Target/PowerPC/PPCSubtarget.cpp b/llvm/lib/Target/PowerPC/PPCSubtarget.cpp --- a/llvm/lib/Target/PowerPC/PPCSubtarget.cpp +++ b/llvm/lib/Target/PowerPC/PPCSubtarget.cpp @@ -78,6 +78,7 @@ HasP8Crypto = false; HasP9Vector = false; HasP9Altivec = false; + HasPrefixInstrs = false; HasFCPSGN = false; HasFSQRT = false; HasFRE = false; diff --git a/llvm/test/CodeGen/PowerPC/future-check-features.ll b/llvm/test/CodeGen/PowerPC/future-check-features.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/future-check-features.ll @@ -0,0 +1,19 @@ +; RUN: llc -mattr=prefix-instrs -verify-machineinstrs \ +; RUN: -mtriple=powerpc64le-unknown-unknown -ppc-asm-full-reg-names \ +; RUN: %s -o - 2>&1 | FileCheck %s +; RUN: llc -mattr=prefix-instrs -verify-machineinstrs \ +; RUN: -mtriple=powerpc64-unknown-unknown -ppc-asm-full-reg-names \ +; RUN: %s -o - 2>&1 | FileCheck %s + +define dso_local signext i32 @f() { +entry: + ret i32 0 +} + +; Make sure that all of the features listed are recognized. +; CHECK-NOT: is not a recognized feature for this target + +; Make sure that the test was actually compiled. +; CHECK: li r3, 0 +; CHECK-NEXT: blr + diff --git a/llvm/test/MC/Disassembler/PowerPC/future-invalid.txt b/llvm/test/MC/Disassembler/PowerPC/future-invalid.txt new file mode 100644 --- /dev/null +++ b/llvm/test/MC/Disassembler/PowerPC/future-invalid.txt @@ -0,0 +1,5 @@ +# RUN: llvm-mc --disassemble %s -mcpu=future -triple powerpc64-unknown-linux-gnu < %s 2>&1 | FileCheck %s + +# paddi 1, 2, 8589934591, 1. However, RA is not zero with R=1 +# CHECK: warning: invalid instruction encoding +0x06 0x11 0xff 0xff 0x38 0x22 0xff 0xff diff --git a/llvm/test/MC/Disassembler/PowerPC/futureinsts.txt b/llvm/test/MC/Disassembler/PowerPC/futureinsts.txt new file mode 100644 --- /dev/null +++ b/llvm/test/MC/Disassembler/PowerPC/futureinsts.txt @@ -0,0 +1,9 @@ +# RUN: llvm-mc --disassemble %s -triple powerpc64-unknown-linux-gnu \ +# RUN: -mcpu=future | FileCheck %s + +# CHECK: paddi 1, 2, 8589934591, 0 +0x06 0x01 0xff 0xff 0x38 0x22 0xff 0xff + +# CHECK: paddi 1, 0, -8589934592, 1 +0x06 0x12 0x00 0x00 0x38 0x20 0x00 0x00 + diff --git a/llvm/test/MC/PowerPC/future.s b/llvm/test/MC/PowerPC/future.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/PowerPC/future.s @@ -0,0 +1,27 @@ +# RUN: llvm-mc -triple powerpc64-unknown-linux-gnu --show-encoding %s | \ +# RUN: FileCheck -check-prefix=CHECK-BE %s +# RUN: llvm-mc -triple powerpc64le-unknown-linux-gnu --show-encoding %s | \ +# RUN: FileCheck -check-prefix=CHECK-LE %s + +# CHECK-BE: paddi 1, 2, 8589934591, 0 # encoding: [0x06,0x01,0xff,0xff +# CHECK-BE-SAME: 0x38,0x22,0xff,0xff] +# CHECK-LE: paddi 1, 2, 8589934591, 0 # encoding: [0xff,0xff,0x01,0x06 +# CHECK-LE-SAME: 0xff,0xff,0x22,0x38] + paddi 1, 2, 8589934591, 0 +# CHECK-BE: paddi 1, 0, -8589934592, 1 # encoding: [0x06,0x12,0x00,0x00 +# CHECK-BE-SAME: 0x38,0x20,0x00,0x00] +# CHECK-LE: paddi 1, 0, -8589934592, 1 # encoding: [0x00,0x00,0x12,0x06 +# CHECK-LE-SAME: 0x00,0x00,0x20,0x38] + paddi 1, 0, -8589934592, 1 +# CHECK-BE: pli 1, -8589934592 # encoding: [0x06,0x02,0x00,0x00 +# CHECK-BE-SAME: 0x38,0x20,0x00,0x00] +# CHECK-LE: pli 1, -8589934592 # encoding: [0x00,0x00,0x02,0x06 +# CHECK-LE-SAME: 0x00,0x00,0x20,0x38] + pli 1, -8589934592 +# CHECK-BE: pli 1, 8589934591 # encoding: [0x06,0x01,0xff,0xff +# CHECK-BE-SAME: 0x38,0x20,0xff,0xff] +# CHECK-LE: pli 1, 8589934591 # encoding: [0xff,0xff,0x01,0x06 +# CHECK-LE-SAME: 0xff,0xff,0x20,0x38] + pli 1, 8589934591 + +