forked from OSchip/llvm-project
[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
This commit is contained in:
parent
1e392fc445
commit
c6334db577
|
@ -177,6 +177,10 @@ public:
|
|||
///
|
||||
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.
|
||||
///
|
||||
|
|
|
@ -37,6 +37,7 @@ public:
|
|||
FT_Data,
|
||||
FT_CompactEncodedInst,
|
||||
FT_Fill,
|
||||
FT_Nops,
|
||||
FT_Relaxable,
|
||||
FT_Org,
|
||||
FT_Dwarf,
|
||||
|
@ -350,6 +351,31 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
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;
|
||||
|
|
|
@ -179,6 +179,8 @@ public:
|
|||
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;
|
||||
|
|
|
@ -777,6 +777,9 @@ public:
|
|||
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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 ";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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); }
|
||||
|
||||
|
|
|
@ -934,6 +934,7 @@ private:
|
|||
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) {
|
||||
|
|
|
@ -207,6 +207,8 @@ public:
|
|||
|
||||
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.
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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)
|
Loading…
Reference in New Issue