Add support of .nops on X86. This addresses llvm.org/PR45788.
Reviewed By: craig.topper
Differential Revision: https://reviews.llvm.org/D82826
///
virtual unsigned getMinimumNopSize() const { return 1; }
+ /// Returns the maximum size of a nop in bytes on this target.
+ ///
+ virtual unsigned getMaximumNopSize() const { return 0; }
+
/// Write an (optimal) nop sequence of Count bytes to the given output. If the
/// target cannot generate such a sequence, it should return an error.
///
FT_Data,
FT_CompactEncodedInst,
FT_Fill,
+ FT_Nops,
FT_Relaxable,
FT_Org,
FT_Dwarf,
}
};
+class MCNopsFragment : public MCFragment {
+ /// The number of bytes to insert.
+ int64_t Size;
+ /// Maximum number of bytes allowed in each NOP instruction.
+ int64_t ControlledNopLength;
+
+ /// Source location of the directive that this fragment was created for.
+ SMLoc Loc;
+
+public:
+ MCNopsFragment(int64_t NumBytes, int64_t ControlledNopLength, SMLoc L,
+ MCSection *Sec = nullptr)
+ : MCFragment(FT_Nops, false, Sec), Size(NumBytes),
+ ControlledNopLength(ControlledNopLength), Loc(L) {}
+
+ int64_t getNumBytes() const { return Size; }
+ int64_t getControlledNopLength() const { return ControlledNopLength; }
+
+ SMLoc getLoc() const { return Loc; }
+
+ static bool classof(const MCFragment *F) {
+ return F->getKind() == MCFragment::FT_Nops;
+ }
+};
+
class MCOrgFragment : public MCFragment {
/// Value to use for filling bytes.
int8_t Value;
SMLoc Loc = SMLoc()) override;
void emitFill(const MCExpr &NumValues, int64_t Size, int64_t Expr,
SMLoc Loc = SMLoc()) override;
+ void emitNops(int64_t NumBytes, int64_t ControlledNopLength,
+ SMLoc Loc) override;
void emitFileDirective(StringRef Filename) override;
void emitAddrsig() override;
virtual void emitFill(const MCExpr &NumValues, int64_t Size, int64_t Expr,
SMLoc Loc = SMLoc());
+ virtual void emitNops(int64_t NumBytes, int64_t ControlledNopLength,
+ SMLoc Loc);
+
/// Emit NumBytes worth of zeros.
/// This function properly handles data in virtual sections.
void emitZeros(uint64_t NumBytes);
"Number of emitted assembler fragments - align");
STATISTIC(EmittedFillFragments,
"Number of emitted assembler fragments - fill");
-STATISTIC(EmittedOrgFragments,
- "Number of emitted assembler fragments - org");
+STATISTIC(EmittedNopsFragments, "Number of emitted assembler fragments - nops");
+STATISTIC(EmittedOrgFragments, "Number of emitted assembler fragments - org");
STATISTIC(evaluateFixup, "Number of evaluated fixups");
STATISTIC(FragmentLayouts, "Number of fragment layouts");
STATISTIC(ObjectBytes, "Number of emitted object file bytes");
return Size;
}
+ case MCFragment::FT_Nops:
+ return cast<MCNopsFragment>(F).getNumBytes();
+
case MCFragment::FT_LEB:
return cast<MCLEBFragment>(F).getContents().size();
break;
}
+ case MCFragment::FT_Nops: {
+ ++stats::EmittedNopsFragments;
+ const MCNopsFragment &NF = cast<MCNopsFragment>(F);
+ int64_t NumBytes = NF.getNumBytes();
+ int64_t ControlledNopLength = NF.getControlledNopLength();
+ int64_t MaximumNopLength = Asm.getBackend().getMaximumNopSize();
+
+ assert(NumBytes > 0 && "Expected positive NOPs fragment size");
+ assert(ControlledNopLength >= 0 && "Expected non-negative NOP size");
+
+ if (ControlledNopLength > MaximumNopLength) {
+ Asm.getContext().reportError(NF.getLoc(),
+ "illegal NOP size " +
+ std::to_string(ControlledNopLength) +
+ ". (expected within [0, " +
+ std::to_string(MaximumNopLength) + "])");
+ // Clamp the NOP length as reportError does not stop the execution
+ // immediately.
+ ControlledNopLength = MaximumNopLength;
+ }
+
+ // Use maximum value if the size of each NOP is not specified
+ if (!ControlledNopLength)
+ ControlledNopLength = MaximumNopLength;
+
+ while (NumBytes) {
+ uint64_t NumBytesToEmit =
+ (uint64_t)std::min(NumBytes, ControlledNopLength);
+ assert(NumBytesToEmit && "try to emit empty NOP instruction");
+ if (!Asm.getBackend().writeNopData(OS, NumBytesToEmit)) {
+ report_fatal_error("unable to write nop sequence of the remaining " +
+ Twine(NumBytesToEmit) + " bytes");
+ break;
+ }
+ NumBytes -= NumBytesToEmit;
+ }
+ break;
+ }
+
case MCFragment::FT_LEB: {
const MCLEBFragment &LF = cast<MCLEBFragment>(F);
OS << LF.getContents();
case FT_Fill:
delete cast<MCFillFragment>(this);
return;
+ case FT_Nops:
+ delete cast<MCNopsFragment>(this);
+ return;
case FT_Relaxable:
delete cast<MCRelaxableFragment>(this);
return;
case MCFragment::FT_CompactEncodedInst:
OS << "MCCompactEncodedInstFragment"; break;
case MCFragment::FT_Fill: OS << "MCFillFragment"; break;
+ case MCFragment::FT_Nops:
+ OS << "MCFNopsFragment";
+ break;
case MCFragment::FT_Relaxable: OS << "MCRelaxableFragment"; break;
case MCFragment::FT_Org: OS << "MCOrgFragment"; break;
case MCFragment::FT_Dwarf: OS << "MCDwarfFragment"; break;
<< " NumValues:" << FF->getNumValues();
break;
}
+ case MCFragment::FT_Nops: {
+ const auto *NF = cast<MCNopsFragment>(this);
+ OS << " NumBytes:" << NF->getNumBytes()
+ << " ControlledNopLength:" << NF->getControlledNopLength();
+ break;
+ }
case MCFragment::FT_Relaxable: {
const auto *F = cast<MCRelaxableFragment>(this);
OS << "\n ";
insert(new MCFillFragment(Expr, Size, NumValues, Loc));
}
+void MCObjectStreamer::emitNops(int64_t NumBytes, int64_t ControlledNopLength,
+ SMLoc Loc) {
+ // Emit an NOP fragment.
+ MCDataFragment *DF = getOrCreateDataFragment();
+ flushPendingLabels(DF, DF->getContents().size());
+
+ assert(getCurrentSectionOnly() && "need a section");
+ insert(new MCNopsFragment(NumBytes, ControlledNopLength, Loc));
+}
+
void MCObjectStreamer::emitFileDirective(StringRef Filename) {
getAssembler().addFileName(Filename);
}
emitFill(*MCConstantExpr::create(NumBytes, getContext()), FillValue);
}
+void llvm::MCStreamer::emitNops(int64_t NumBytes, int64_t ControlledNopLen,
+ llvm::SMLoc) {}
+
/// The implementation in this class just redirects to emitFill.
void MCStreamer::emitZeros(uint64_t NumBytes) { emitFill(NumBytes, 0); }
OperandVector &Operands);
bool parseDirectiveArch();
+ bool parseDirectiveNops(SMLoc L);
bool parseDirectiveEven(SMLoc L);
bool ParseDirectiveCode(StringRef IDVal, SMLoc L);
"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());
return false;
}
+/// parseDirectiveNops
+/// ::= .nops size[, control]
+bool X86AsmParser::parseDirectiveNops(SMLoc L) {
+ int64_t NumBytes = 0, Control = 0;
+ SMLoc NumBytesLoc, ControlLoc;
+ const MCSubtargetInfo STI = getSTI();
+ NumBytesLoc = getTok().getLoc();
+ 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) {
+ Error(NumBytesLoc, "'.nops' directive with non-positive size");
+ return false;
+ }
+
+ if (Control < 0) {
+ Error(ControlLoc, "'.nops' directive with negative NOP size");
+ return false;
+ }
+
+ /// Emit nops
+ getParser().getStreamer().emitNops(NumBytes, Control, L);
+
+ return false;
+}
+
/// parseDirectiveEven
/// ::= .even
bool X86AsmParser::parseDirectiveEven(SMLoc L) {
void finishLayout(MCAssembler const &Asm, MCAsmLayout &Layout) const override;
+ unsigned getMaximumNopSize() const override;
+
bool writeNopData(raw_ostream &OS, uint64_t Count) const override;
};
} // end anonymous namespace
}
}
+unsigned X86AsmBackend::getMaximumNopSize() const {
+ if (!STI.hasFeature(X86::FeatureNOPL) && !STI.hasFeature(X86::Mode64Bit))
+ return 1;
+ if (STI.getFeatureBits()[X86::FeatureFast7ByteNOP])
+ return 7;
+ if (STI.getFeatureBits()[X86::FeatureFast15ByteNOP])
+ return 15;
+ if (STI.getFeatureBits()[X86::FeatureFast11ByteNOP])
+ return 11;
+ // FIXME: handle 32-bit mode
+ // 15-bytes is the longest single NOP instruction, but 10-bytes is
+ // commonly the longest that can be efficiently decoded.
+ return 10;
+}
+
/// Write a sequence of optimal nops to the output, covering \p Count
/// bytes.
/// \return - true on success, false on failure
"\x66\x2e\x0f\x1f\x84\x00\x00\x00\x00\x00",
};
- // This CPU doesn't support long nops. If needed add more.
- // FIXME: We could generated something better than plain 0x90.
- if (!STI.hasFeature(X86::FeatureNOPL) && !STI.hasFeature(X86::Mode64Bit)) {
- for (uint64_t i = 0; i < Count; ++i)
- OS << '\x90';
- return true;
- }
-
- // 15-bytes is the longest single NOP instruction, but 10-bytes is
- // commonly the longest that can be efficiently decoded.
- uint64_t MaxNopLength = 10;
- if (STI.getFeatureBits()[X86::FeatureFast7ByteNOP])
- MaxNopLength = 7;
- else if (STI.getFeatureBits()[X86::FeatureFast15ByteNOP])
- MaxNopLength = 15;
- else if (STI.getFeatureBits()[X86::FeatureFast11ByteNOP])
- MaxNopLength = 11;
+ uint64_t MaxNopLength = (uint64_t)getMaximumNopSize();
// Emit as many MaxNopLength NOPs as needed, then emit a NOP of the remaining
// length.
--- /dev/null
+# RUN: not llvm-mc -triple i386 %s -filetype=obj -o /dev/null 2>&1 | FileCheck --check-prefix=X86 %s
+# RUN: not llvm-mc -triple=x86_64 %s -filetype=obj -o /dev/null 2>&1 | FileCheck --check-prefix=X64 %s
+
+.nops 4, 3
+# X86: :[[@LINE-1]]:1: error: illegal NOP size 3.
+.nops 4, 4
+# X86: :[[@LINE-1]]:1: error: illegal NOP size 4.
+.nops 4, 5
+# X86: :[[@LINE-1]]:1: error: illegal NOP size 5.
+.nops 16, 15
+# X86: :[[@LINE-1]]:1: error: illegal NOP size 15.
+# X64: :[[@LINE-2]]:1: error: illegal NOP size 15.
--- /dev/null
+# RUN: llvm-mc -triple i386 %s -filetype=obj | llvm-objdump -d - | FileCheck %s
+
+.nops 4
+# CHECK: 0: 90 nop
+# CHECK-NEXT: 1: 90 nop
+# CHECK-NEXT: 2: 90 nop
+# CHECK-NEXT: 3: 90 nop
+.nops 4, 1
+# CHECK: 4: 90 nop
+# CHECK-NEXT: 5: 90 nop
+# CHECK-NEXT: 6: 90 nop
+# CHECK-NEXT: 7: 90 nop
--- /dev/null
+# RUN: llvm-mc -triple=x86_64 %s -filetype=obj | llvm-objdump -d - | FileCheck %s
+
+.nops 4, 1
+# CHECK: 0: 90 nop
+# CHECK-NEXT: 1: 90 nop
+# CHECK-NEXT: 2: 90 nop
+# CHECK-NEXT: 3: 90 nop
+.nops 4, 2
+# CHECK-NEXT: 4: 66 90 nop
+# CHECK-NEXT: 6: 66 90 nop
+.nops 4, 3
+# CHECK-NEXT: 8: 0f 1f 00 nopl (%rax)
+# CHECK-NEXT: b: 90 nop
+.nops 4, 4
+# CHECK-NEXT: c: 0f 1f 40 00 nopl (%rax)
+.nops 4, 5
+# CHECK-NEXT: 10: 0f 1f 40 00 nopl (%rax)
+.nops 4
+# CHECK-NEXT: 14: 0f 1f 40 00 nopl (%rax)