[llvm] c6334db - [X86] support .nops directive
Jian Cai via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 3 11:51:16 PDT 2020
Author: Jian Cai
Date: 2020-08-03T11:50:56-07:00
New Revision: c6334db577e7049fe4868b1647c9f937f68ff1f5
URL: https://github.com/llvm/llvm-project/commit/c6334db577e7049fe4868b1647c9f937f68ff1f5
DIFF: https://github.com/llvm/llvm-project/commit/c6334db577e7049fe4868b1647c9f937f68ff1f5.diff
LOG: [X86] support .nops directive
Add support of .nops on X86. This addresses llvm.org/PR45788.
Reviewed By: craig.topper
Differential Revision: https://reviews.llvm.org/D82826
Added:
llvm/test/MC/X86/x86-directive-nops-errors.s
llvm/test/MC/X86/x86-directive-nops.s
llvm/test/MC/X86/x86_64-directive-nops.s
Modified:
llvm/include/llvm/MC/MCAsmBackend.h
llvm/include/llvm/MC/MCFragment.h
llvm/include/llvm/MC/MCObjectStreamer.h
llvm/include/llvm/MC/MCStreamer.h
llvm/lib/MC/MCAssembler.cpp
llvm/lib/MC/MCFragment.cpp
llvm/lib/MC/MCObjectStreamer.cpp
llvm/lib/MC/MCStreamer.cpp
llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/MC/MCAsmBackend.h b/llvm/include/llvm/MC/MCAsmBackend.h
index 8f95cfd55a3d..94ed3d27e785 100644
--- a/llvm/include/llvm/MC/MCAsmBackend.h
+++ b/llvm/include/llvm/MC/MCAsmBackend.h
@@ -177,6 +177,10 @@ class MCAsmBackend {
///
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.
///
diff --git a/llvm/include/llvm/MC/MCFragment.h b/llvm/include/llvm/MC/MCFragment.h
index fb7166e82c09..87338ab46cc2 100644
--- a/llvm/include/llvm/MC/MCFragment.h
+++ b/llvm/include/llvm/MC/MCFragment.h
@@ -37,6 +37,7 @@ class MCFragment : public ilist_node_with_parent<MCFragment, MCSection> {
FT_Data,
FT_CompactEncodedInst,
FT_Fill,
+ FT_Nops,
FT_Relaxable,
FT_Org,
FT_Dwarf,
@@ -350,6 +351,31 @@ class MCFillFragment : public MCFragment {
}
};
+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;
diff --git a/llvm/include/llvm/MC/MCObjectStreamer.h b/llvm/include/llvm/MC/MCObjectStreamer.h
index c3f3ae5de921..a00000bc11b6 100644
--- a/llvm/include/llvm/MC/MCObjectStreamer.h
+++ b/llvm/include/llvm/MC/MCObjectStreamer.h
@@ -179,6 +179,8 @@ class MCObjectStreamer : public MCStreamer {
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;
diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h
index 484c62538366..63a4c1d190ac 100644
--- a/llvm/include/llvm/MC/MCStreamer.h
+++ b/llvm/include/llvm/MC/MCStreamer.h
@@ -777,6 +777,9 @@ class MCStreamer {
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);
diff --git a/llvm/lib/MC/MCAssembler.cpp b/llvm/lib/MC/MCAssembler.cpp
index 3ca8714b7817..9515b7e2642b 100644
--- a/llvm/lib/MC/MCAssembler.cpp
+++ b/llvm/lib/MC/MCAssembler.cpp
@@ -62,8 +62,8 @@ STATISTIC(EmittedAlignFragments,
"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");
@@ -312,6 +312,9 @@ uint64_t MCAssembler::computeFragmentSize(const MCAsmLayout &Layout,
return Size;
}
+ case MCFragment::FT_Nops:
+ return cast<MCNopsFragment>(F).getNumBytes();
+
case MCFragment::FT_LEB:
return cast<MCLEBFragment>(F).getContents().size();
@@ -613,6 +616,45 @@ static void writeFragment(raw_ostream &OS, const MCAssembler &Asm,
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();
diff --git a/llvm/lib/MC/MCFragment.cpp b/llvm/lib/MC/MCFragment.cpp
index 8e90e07a4dbf..e9cea9d18f2e 100644
--- a/llvm/lib/MC/MCFragment.cpp
+++ b/llvm/lib/MC/MCFragment.cpp
@@ -279,6 +279,9 @@ void MCFragment::destroy() {
case FT_Fill:
delete cast<MCFillFragment>(this);
return;
+ case FT_Nops:
+ delete cast<MCNopsFragment>(this);
+ return;
case FT_Relaxable:
delete cast<MCRelaxableFragment>(this);
return;
@@ -336,6 +339,9 @@ LLVM_DUMP_METHOD void MCFragment::dump() const {
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;
@@ -408,6 +414,12 @@ LLVM_DUMP_METHOD void MCFragment::dump() const {
<< " 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 ";
diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp
index 78ee215b59aa..f9e0d858cf70 100644
--- a/llvm/lib/MC/MCObjectStreamer.cpp
+++ b/llvm/lib/MC/MCObjectStreamer.cpp
@@ -819,6 +819,16 @@ void MCObjectStreamer::emitFill(const MCExpr &NumValues, int64_t Size,
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);
}
diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp
index 6d3a933c96a3..df08c343f69f 100644
--- a/llvm/lib/MC/MCStreamer.cpp
+++ b/llvm/lib/MC/MCStreamer.cpp
@@ -202,6 +202,9 @@ void MCStreamer::emitFill(uint64_t NumBytes, uint8_t FillValue) {
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); }
diff --git a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
index 49c01d7b9ef0..ddb13e46e930 100644
--- a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
+++ b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
@@ -934,6 +934,7 @@ class X86AsmParser : public MCTargetAsmParser {
OperandVector &Operands);
bool parseDirectiveArch();
+ bool parseDirectiveNops(SMLoc L);
bool parseDirectiveEven(SMLoc L);
bool ParseDirectiveCode(StringRef IDVal, SMLoc L);
@@ -4037,7 +4038,9 @@ bool X86AsmParser::ParseDirective(AsmToken DirectiveID) {
"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());
@@ -4073,6 +4076,42 @@ bool X86AsmParser::parseDirectiveArch() {
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) {
diff --git a/llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp b/llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp
index bf3b6bcb5463..31bc54f53d02 100644
--- a/llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp
+++ b/llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp
@@ -207,6 +207,8 @@ class X86AsmBackend : public MCAsmBackend {
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
@@ -1067,6 +1069,21 @@ void X86AsmBackend::finishLayout(MCAssembler const &Asm,
}
}
+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
@@ -1094,23 +1111,7 @@ bool X86AsmBackend::writeNopData(raw_ostream &OS, uint64_t Count) const {
"\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.
diff --git a/llvm/test/MC/X86/x86-directive-nops-errors.s b/llvm/test/MC/X86/x86-directive-nops-errors.s
new file mode 100644
index 000000000000..473cb509442c
--- /dev/null
+++ b/llvm/test/MC/X86/x86-directive-nops-errors.s
@@ -0,0 +1,12 @@
+# 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.
diff --git a/llvm/test/MC/X86/x86-directive-nops.s b/llvm/test/MC/X86/x86-directive-nops.s
new file mode 100644
index 000000000000..786a029d503a
--- /dev/null
+++ b/llvm/test/MC/X86/x86-directive-nops.s
@@ -0,0 +1,12 @@
+# 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
diff --git a/llvm/test/MC/X86/x86_64-directive-nops.s b/llvm/test/MC/X86/x86_64-directive-nops.s
new file mode 100644
index 000000000000..2255cd3a2efd
--- /dev/null
+++ b/llvm/test/MC/X86/x86_64-directive-nops.s
@@ -0,0 +1,19 @@
+# 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)
More information about the llvm-commits
mailing list