diff --git a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp --- a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp +++ b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp @@ -21,6 +21,7 @@ #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCAsmParser.h" @@ -910,6 +911,7 @@ SMLoc End, unsigned Size, StringRef Identifier, const InlineAsmIdentifierInfo &Info); + bool parseDirectiveNops(SMLoc L); bool parseDirectiveEven(SMLoc L); bool ParseDirectiveCode(StringRef IDVal, SMLoc L); @@ -3846,7 +3848,9 @@ "a '%' prefix in .intel_syntax"); } return false; - } else if (IDVal == ".even") + } else if (IDVal == ".nops") + return parseDirectiveNops(DirectiveID.getLoc()); + else if (IDVal == ".even") return parseDirectiveEven(DirectiveID.getLoc()); else if (IDVal == ".cv_fpo_proc") return parseDirectiveFPOProc(DirectiveID.getLoc()); @@ -3876,6 +3880,170 @@ return true; } +/// Return the longest nop which can be efficiently decoded for the given +/// target cpu. 15-bytes is the longest single NOP instruction, but some +/// platforms can't decode the longest forms efficiently. +static unsigned maxLongNopLength(const MCSubtargetInfo &STI) { + if (STI.getFeatureBits()[X86::ProcIntelSLM]) + return 7; + if (STI.getFeatureBits()[X86::FeatureFast15ByteNOP]) + return 15; + if (STI.getFeatureBits()[X86::FeatureFast11ByteNOP]) + return 11; + if (STI.getFeatureBits()[X86::FeatureNOPL] || + STI.getFeatureBits()[X86::Mode64Bit]) + return 10; + if (STI.getFeatureBits()[X86::Mode32Bit]) + return 2; + return 1; +} + +/// Emit the largest nop instruction smaller than or equal to \p NumBytes +/// bytes. Return the size of nop emitted. +static unsigned emitNop(MCStreamer &OS, unsigned NumBytes, + const MCSubtargetInfo &STI) { + // Cap a single nop emission at the profitable value for the target + + unsigned NopSize; + unsigned Opc, BaseReg, ScaleVal, IndexReg, Displacement, SegmentReg; + IndexReg = Displacement = SegmentReg = 0; + BaseReg = X86::RAX; + ScaleVal = 1; + switch (NumBytes) { + case 0: + llvm_unreachable("Zero nops?"); + break; + case 1: + NopSize = 1; + Opc = X86::NOOP; + break; + case 2: + NopSize = 2; + Opc = X86::XCHG16ar; + break; + case 3: + NopSize = 3; + Opc = X86::NOOPL; + break; + case 4: + NopSize = 4; + Opc = X86::NOOPL; + Displacement = 8; + break; + case 5: + NopSize = 5; + Opc = X86::NOOPL; + Displacement = 8; + IndexReg = X86::RAX; + break; + case 6: + NopSize = 6; + Opc = X86::NOOPW; + Displacement = 8; + IndexReg = X86::RAX; + break; + case 7: + NopSize = 7; + Opc = X86::NOOPL; + Displacement = 512; + break; + case 8: + NopSize = 8; + Opc = X86::NOOPL; + Displacement = 512; + IndexReg = X86::RAX; + break; + case 9: + NopSize = 9; + Opc = X86::NOOPW; + Displacement = 512; + IndexReg = X86::RAX; + break; + default: + NopSize = 10; + Opc = X86::NOOPW; + Displacement = 512; + IndexReg = X86::RAX; + SegmentReg = X86::CS; + break; + } + + unsigned NumPrefixes = std::min(NumBytes - NopSize, 5U); + NopSize += NumPrefixes; + for (unsigned i = 0; i != NumPrefixes; ++i) + OS.emitBytes("\x66"); + + switch (Opc) { + default: + llvm_unreachable("Unexpected opcode"); + case X86::NOOP: + OS.emitInstruction(MCInstBuilder(Opc), STI); + break; + case X86::XCHG16ar: + OS.emitInstruction(MCInstBuilder(Opc).addReg(X86::AX).addReg(X86::AX), STI); + break; + case X86::NOOPL: + case X86::NOOPW: + OS.emitInstruction(MCInstBuilder(Opc) + .addReg(BaseReg) + .addImm(ScaleVal) + .addReg(IndexReg) + .addImm(Displacement) + .addReg(SegmentReg), + STI); + break; + } + assert(NopSize <= NumBytes && "We overemitted?"); + return NopSize; +} + +/// parseDirectiveNops +/// ::= .nops size[, control] +bool X86AsmParser::parseDirectiveNops(SMLoc L) { + int64_t NumBytes = 0, Control = 0; + SMLoc ControlLoc; + const MCSubtargetInfo STI = getSTI(); + if (getParser().checkForValidSection() || + getParser().parseAbsoluteExpression(NumBytes)) + return true; + + if (parseOptionalToken(AsmToken::Comma)) { + ControlLoc = getTok().getLoc(); + if (getParser().parseAbsoluteExpression(Control)) + return true; + } + if (getParser().parseToken(AsmToken::EndOfStatement, + "unexpected token in '.nops' directive")) + return true; + + if (NumBytes <= 0) { + Warning(L, "'.nops' directive with non-positive size has no effect"); + return false; + } + + if (Control < 0 || Control > maxLongNopLength(STI)) { + Warning(ControlLoc, "'.nops' directive with invalid single nop size " + + std::to_string(Control) + + ". (expected within [0, " + + std::to_string(maxLongNopLength(STI)) + "])"); + return true; + } + + if (!Control) + Control = maxLongNopLength(STI); + + /// Emit nops + unsigned NopsToEmit = NumBytes; + //(void)NopsToEmit; + while (NumBytes) { + NumBytes -= + emitNop(getParser().getStreamer(), std::min(NumBytes, Control), STI); + assert(NopsToEmit >= NumBytes && "Emitted more than I asked for!"); + } + + return false; +} + /// parseDirectiveEven /// ::= .even bool X86AsmParser::parseDirectiveEven(SMLoc L) { diff --git a/llvm/test/MC/X86/x86-directive-nops.s b/llvm/test/MC/X86/x86-directive-nops.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/X86/x86-directive-nops.s @@ -0,0 +1,32 @@ +// RUN: llvm-mc -triple i386 %s -filetype=obj 2>&1 -o %t | FileCheck --check-prefix=X32-WARNINGS %s +// RUN: llvm-objdump -d %t | FileCheck --check-prefix=X32 %s +// RUN: llvm-mc -triple x86_64 %s -filetype=obj 2>&1 -o %t | FileCheck --check-prefix=X64-WARNINGS %s +// RUN: llvm-objdump -d %t | FileCheck --check-prefix=X64 %s + +.nops 4, 1 +#X32: 0: 90 nop +#X32-NEXT: 1: 90 nop +#X32-NEXT: 2: 90 nop +#X32-NEXT: 3: 90 nop +#X64: 0: 90 nop +#X64-NEXT: 1: 90 nop +#X64-NEXT: 2: 90 nop +#X64-NEXT: 3: 90 nop +.nops 4, 2 +#X32-NEXT: 4: 66 90 nop +#X32-NEXT: 6: 66 90 nop +#X64-NEXT: 4: 66 90 nop +#X64-NEXT: 6: 66 90 nop +.nops 4, 3 +#X32-WARNINGS: :[[@LINE-1]]:10: warning: '.nops' directive with invalid single nop size 3. +#X64-NEXT: 8: 0f 1f 00 nopl (%rax) +#X64-NEXT: b: 90 nop +.nops 4, 4 +#X32-WARNINGS: :[[@LINE-1]]:10: warning: '.nops' directive with invalid single nop size 4. +#X64: c: 0f 1f 40 08 nopl 8(%rax) +.nops 4, 5 +#X32-WARNINGS: :[[@LINE-1]]:10: warning: '.nops' directive with invalid single nop size 5. +#X64: 10: 0f 1f 40 08 nopl 8(%rax) +.nops 16, 15 +#X32-WARNINGS: :[[@LINE-1]]:11: warning: '.nops' directive with invalid single nop size 15. +#X64-WARNINGS: :[[@LINE-2]]:11: warning: '.nops' directive with invalid single nop size 15.