This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] x86: Add .nop directive to assembler


On Tue, Feb 13, 2018 at 12:50 AM, Jan Beulich <JBeulich@suse.com> wrote:
>>>> On 12.02.18 at 20:38, <hjl.tools@gmail.com> wrote:
>> On Mon, Feb 12, 2018 at 7:48 AM, Maciej W. Rozycki <macro@mips.com> wrote:
>>> On Mon, 12 Feb 2018, H.J. Lu wrote:
>>>
>>>> My implementation uses the existing relaxation frame work.
>>>> When we are processing .nop, we don't know exactly how big the
>>>> the NOP size will be.  We allocate a frag with the maximum size
>>>> and set the exact size after relaxation.  After relaxation is done,
>>>> all frags are converted to rs_fill.  We can add rs_fill_nop to
>>>> support arbitrary .nop directive size. But I don't know if it is
>>>> necessary.
>>>
>>>  Right, so this is needed for argument expressions using forward
>>> references.  Understood and accepted.  Thank your for patience.
>>>
>>
>> Implement the '.nop SIZE[, LIMIT]' directive for x86 assembler.  This
>> directive emits SIZE bytes filled with 'NOP' instructions.  SIZE is
>> absolute expression, which must be between 0 and 512.  LIMIT specifies
>> the size limit of a single 'NOP' instruction.  If the comma and LIMIT
>> are omitted, LIMIT is assumed to the maximum supported size of a single
>> 'NOP' instruction.  The valid values of LIMIT are between 1 and 8 for
>> 16-bit mode, between 1 and 10 for 32-bit mode, between 1 and 11 for
>> 64-bit mode.  This directive is only allowed in text sections.
>
> Did you consider generalizing .skip instead (e.g. by allowing its
> FILL to be "@NOP" alongside an absolute expression)? I have
> to admit that adding a new directive looks a little odd to me
> when all you want is some more flexibility with an existing one.

This requires much bigger changes.  Also I like ".not SIZE, LIMIT".
But if everyone agrees that we should extend .skip, we can do that.
This may also remove the size limit.

> Also I'm not sure I really follow what the upper bounds for
> LIMIT in the different modes are being derived from. Without
> a comment next to the patterns that's going to remain guesswork
> forever. For example, why would

I just reused what i386 has for aligning code.

> static const unsigned char alt64_12[] =
>   {0x67,0x66,0x2e,0x40,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
>
> not be a possibility? Or, like at least AMD suggests, multiple 0x66
> prefixes?

We can update NOP padding, independent of .nop directive.

>> This is implemented by adding a relax state, rs_space_nop, to enum
>> _relax_state, which is similar to rs_space, but it fills with NOPs,
>> instead of a single byte.  A pseudo relocation, BFD_RELOC_NOP_DIRECTIVE,
>> is added to fix up frag data with the proper number of NOPs.  The new
>> rs_space_nop state is processed only when TARGET_USE_NOP_DIRECTIVE is
>> defined.
>
> In your earlier reply to Maciej didn't you indicate you restrict
> LIMIT only because you don't want to go through the
> complexity of introducing a new relaxation state?

It turned that I need a new relaxation state to cover branches
which need its own relaxation.  But I keep the generic change
to minimum.

>>  To enable .nop directive, a target backend should
>>
>> 1. Define TARGET_USE_NOP_DIRECTIVE.
>
> With this, why is the new directive being added to i386's
> md_pseudo_table[], instead of the arch independent one in
> read.c?

This requires generic a s_nop function.  I don't think i386 s_nop
is suitable as a generic implementation.

>> 2. Create a rs_space_nop frag for .nop directive.
>> 3. Update md_convert_frag to create a fixup with BFD_RELOC_NOP_DIRECTIVE
>> for rs_space_nop frag.
>> 4. Update md_apply_fix to process fixup with BFD_RELOC_NOP_DIRECTIVE.
>>
>> OK for master?
>>
>> Andrew, please test my current users/hjl/nop branch.
>
> On the original thread Andrew had indicated that producing a single
> byte NOP at the end of a sequence of NOPs is undesirable. Your
> i386_output_nops() appears to do just that, however.

386_output_nops is extracted out of i386_align_code.  We can always
improve it.

> Additionally - what's wrong with emitting NOPs to a non-executable
> section?

Removed.

> Finally, would you mind making the diagnostic complaining about too
> large a LIMIT also report the upper bound (not the least because - as
> per above remark - this may change over time)?
>

Done.

Here is the updated patch.  OK for master?

Thanks.

-- 
H.J.
From 020c73ab3e94c9a8d077623726be952b9067ba1b Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Fri, 9 Feb 2018 03:46:06 -0800
Subject: [PATCH] x86: Add .nop directive to assembler

Implement the '.nop SIZE[, LIMIT]' directive for x86 assembler.  This
directive emits SIZE bytes filled with 'NOP' instructions.  SIZE is
absolute expression, which must be between 0 and 512.  LIMIT specifies
the size limit of a single 'NOP' instruction.  If the comma and LIMIT
are omitted, LIMIT is assumed to the maximum supported size of a single
'NOP' instruction.  The valid values of LIMIT are between 1 and 8 for
16-bit mode, between 1 and 10 for 32-bit mode, between 1 and 11 for
64-bit mode.  This directive is only allowed in text sections.

This is implemented by adding a relax state, rs_space_nop, to enum
_relax_state, which is similar to rs_space, but it fills with NOPs,
instead of a single byte.  A pseudo relocation, BFD_RELOC_NOP_DIRECTIVE,
is added to fix up frag data with the proper number of NOPs.  The new
rs_space_nop state is processed only when TARGET_USE_NOP_DIRECTIVE is
defined.  To enable .nop directive, a target backend should

1. Define TARGET_USE_NOP_DIRECTIVE.
2. Create a rs_space_nop frag for .nop directive.
3. Update md_convert_frag to create a fixup with BFD_RELOC_NOP_DIRECTIVE
for rs_space_nop frag.
4. Update md_apply_fix to process fixup with BFD_RELOC_NOP_DIRECTIVE.

bfd/

	* reloc.c (BFD_RELOC_NOP_DIRECTIVE): New pseudo relocation.
	* bfd-in2.h: Regenerated.
	* libbfd.h: Likewise.

gas/

	* as.h (_relax_state): Add rs_space_nop.
	* write.c (cvt_frag_to_fill): Handle rs_space_nop if
	TARGET_USE_NOP_DIRECTIVE is define.
	(relax_segment): Likewise.
	* config/tc-i386.c (MAX_NOP_DIRECTIVE_SIZE): New.
	(alt64_11): Likewise.
	(alt64_patt): Likewise.
	(md_convert_frag): Handle rs_space_nop.
	(md_apply_fix): Handle BFD_RELOC_NOP_DIRECTIVE.
	(s_nop): New function prototype.
	(md_pseudo_table): Add "nop".
	(i386_align_code): Call i386_output_nops.
	(i386_output_nops): New function.
	(s_nop): Likewise.
	* config/tc-i386.h (TARGET_USE_NOP_DIRECTIVE): New.
	* doc/as.texinfo: Document .nop directive for x86.
	* testsuite/gas/i386/i386.exp: Run .nop directive tests.
	* testsuite/gas/i386/nop-1.d: New file.
	* testsuite/gas/i386/nop-1.s: Likewise.
	* testsuite/gas/i386/nop-2.d: Likewise.
	* testsuite/gas/i386/nop-2.s: Likewise.
	* testsuite/gas/i386/nop-3.d: Likewise.
	* testsuite/gas/i386/nop-3.s: Likewise.
	* testsuite/gas/i386/nop-4.d: Likewise.
	* testsuite/gas/i386/nop-4.s: Likewise.
	* testsuite/gas/i386/nop-5.d: Likewise.
	* testsuite/gas/i386/nop-5.s: Likewise.
	* testsuite/gas/i386/nop-6.d: Likewise.
	* testsuite/gas/i386/nop-6.s: Likewise.
	* testsuite/gas/i386/nop-bad-1.l: Likewise.
	* testsuite/gas/i386/nop-bad-1.s: Likewise.
	* testsuite/gas/i386/x86-64-nop-1.d: Likewise.
	* testsuite/gas/i386/x86-64-nop-2.d: Likewise.
	* testsuite/gas/i386/x86-64-nop-3.d: Likewise.
	* testsuite/gas/i386/x86-64-nop-4.d: Likewise.
	* testsuite/gas/i386/x86-64-nop-5.d: Likewise.
	* testsuite/gas/i386/x86-64-nop-6.d: Likewise.
---
 bfd/bfd-in2.h                         |   4 +
 bfd/libbfd.h                          |   2 +
 bfd/reloc.c                           |   6 +
 gas/as.h                              |  10 +
 gas/config/tc-i386.c                  | 371 ++++++++++++++++++++++++----------
 gas/config/tc-i386.h                  |   3 +
 gas/doc/as.texinfo                    |  20 ++
 gas/testsuite/gas/i386/i386.exp       |  13 ++
 gas/testsuite/gas/i386/nop-1.d        |  31 +++
 gas/testsuite/gas/i386/nop-1.s        |  21 ++
 gas/testsuite/gas/i386/nop-2.d        |  40 ++++
 gas/testsuite/gas/i386/nop-2.s        |  22 ++
 gas/testsuite/gas/i386/nop-3.d        |  20 ++
 gas/testsuite/gas/i386/nop-3.s        |  15 ++
 gas/testsuite/gas/i386/nop-4.d        |  23 +++
 gas/testsuite/gas/i386/nop-4.s        |  18 ++
 gas/testsuite/gas/i386/nop-5.d        |  25 +++
 gas/testsuite/gas/i386/nop-5.s        |  19 ++
 gas/testsuite/gas/i386/nop-6.d        |  17 ++
 gas/testsuite/gas/i386/nop-6.s        |  25 +++
 gas/testsuite/gas/i386/nop-bad-1.l    |   5 +
 gas/testsuite/gas/i386/nop-bad-1.s    |   5 +
 gas/testsuite/gas/i386/x86-64-nop-1.d |  32 +++
 gas/testsuite/gas/i386/x86-64-nop-2.d |  41 ++++
 gas/testsuite/gas/i386/x86-64-nop-3.d |  21 ++
 gas/testsuite/gas/i386/x86-64-nop-4.d |  24 +++
 gas/testsuite/gas/i386/x86-64-nop-5.d |  26 +++
 gas/testsuite/gas/i386/x86-64-nop-6.d |  18 ++
 gas/write.c                           |  23 ++-
 29 files changed, 788 insertions(+), 112 deletions(-)
 create mode 100644 gas/testsuite/gas/i386/nop-1.d
 create mode 100644 gas/testsuite/gas/i386/nop-1.s
 create mode 100644 gas/testsuite/gas/i386/nop-2.d
 create mode 100644 gas/testsuite/gas/i386/nop-2.s
 create mode 100644 gas/testsuite/gas/i386/nop-3.d
 create mode 100644 gas/testsuite/gas/i386/nop-3.s
 create mode 100644 gas/testsuite/gas/i386/nop-4.d
 create mode 100644 gas/testsuite/gas/i386/nop-4.s
 create mode 100644 gas/testsuite/gas/i386/nop-5.d
 create mode 100644 gas/testsuite/gas/i386/nop-5.s
 create mode 100644 gas/testsuite/gas/i386/nop-6.d
 create mode 100644 gas/testsuite/gas/i386/nop-6.s
 create mode 100644 gas/testsuite/gas/i386/nop-bad-1.l
 create mode 100644 gas/testsuite/gas/i386/nop-bad-1.s
 create mode 100644 gas/testsuite/gas/i386/x86-64-nop-1.d
 create mode 100644 gas/testsuite/gas/i386/x86-64-nop-2.d
 create mode 100644 gas/testsuite/gas/i386/x86-64-nop-3.d
 create mode 100644 gas/testsuite/gas/i386/x86-64-nop-4.d
 create mode 100644 gas/testsuite/gas/i386/x86-64-nop-5.d
 create mode 100644 gas/testsuite/gas/i386/x86-64-nop-6.d

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 42991e7848..5d8f6412fe 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -2740,6 +2740,10 @@ The 24-bit relocation is used in some Intel 960 configurations.  */
   BFD_RELOC_SIZE32,
   BFD_RELOC_SIZE64,
 
+/* This is the pseudo NOP relocation for .nop directive.  */
+  BFD_RELOC_NOP_DIRECTIVE,
+
+
 /* Relocations used by 68K ELF.  */
   BFD_RELOC_68K_GLOB_DAT,
   BFD_RELOC_68K_JMP_SLOT,
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 935b5b49c9..54223a2d48 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -973,6 +973,8 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_8_PLTOFF",
   "BFD_RELOC_SIZE32",
   "BFD_RELOC_SIZE64",
+  "BFD_RELOC_NOP_DIRECTIVE",
+
   "BFD_RELOC_68K_GLOB_DAT",
   "BFD_RELOC_68K_JMP_SLOT",
   "BFD_RELOC_68K_RELATIVE",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 301199a742..6e20101bde 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -1798,6 +1798,12 @@ ENUMX
 ENUMDOC
   Size relocations.
 
+ENUM
+  BFD_RELOC_NOP_DIRECTIVE
+ENUMDOC
+  This is the pseudo NOP relocation for .nop directive.
+COMMENT
+
 ENUM
   BFD_RELOC_68K_GLOB_DAT
 ENUMX
diff --git a/gas/as.h b/gas/as.h
index c33353a9c5..9d5dba4e52 100644
--- a/gas/as.h
+++ b/gas/as.h
@@ -279,6 +279,16 @@ enum _relax_state
      1 variable char: fill character  */
   rs_space,
 
+  /* .nop directive with expression operand that needs to be computed
+     later.  Similar to rs_space, but different.  It fills with NOPs.
+     fr_symbol: operand
+     1 constant byte: NOP fill control byte.
+     NB: rs_space_nop is used only if TARGET_USE_NOP_DIRECTIVE is
+     defined in tc-XXX.h, which is included after enum _relax_state
+     has been defined.  It is harmless since there is no rs_space_nop
+     frag without TARGET_USE_NOP_DIRECTIVE.  */
+  rs_space_nop,
+
   /* A DWARF leb128 value; only ELF uses this.  The subtype is 0 for
      unsigned, 1 for signed.  */
   rs_leb128,
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 1a5be1bcda..087812e811 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -33,6 +33,9 @@
 #include "elf/x86-64.h"
 #include "opcodes/i386-init.h"
 
+/* The maximum size of .nop directive.  */
+#define MAX_NOP_DIRECTIVE_SIZE 512
+
 #ifndef REGISTER_WARNINGS
 #define REGISTER_WARNINGS 1
 #endif
@@ -186,6 +189,7 @@ static const seg_entry *build_modrm_byte (void);
 static void output_insn (void);
 static void output_imm (fragS *, offsetT);
 static void output_disp (fragS *, offsetT);
+static void s_nop (int);
 #ifndef I386COFF
 static void s_bss (int);
 #endif
@@ -1124,6 +1128,7 @@ const pseudo_typeS md_pseudo_table[] =
   {"disallow_index_reg", set_allow_index_reg, 0},
   {"sse_check", set_check, 0},
   {"operand_check", set_check, 1},
+  {"nop", s_nop, 0},
 #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
   {"largecomm", handle_large_common, 0},
 #else
@@ -1146,105 +1151,135 @@ static struct hash_control *op_hash;
 /* Hash table for register lookup.  */
 static struct hash_control *reg_hash;
 
-void
-i386_align_code (fragS *fragP, int count)
-{
   /* Various efficient no-op patterns for aligning code labels.
      Note: Don't try to assemble the instructions in the comments.
      0L and 0w are not legal.  */
-  static const unsigned char f32_1[] =
-    {0x90};					/* nop			*/
-  static const unsigned char f32_2[] =
-    {0x66,0x90};				/* xchg %ax,%ax */
-  static const unsigned char f32_3[] =
-    {0x8d,0x76,0x00};				/* leal 0(%esi),%esi	*/
-  static const unsigned char f32_4[] =
-    {0x8d,0x74,0x26,0x00};			/* leal 0(%esi,1),%esi	*/
-  static const unsigned char f32_5[] =
-    {0x90,					/* nop			*/
-     0x8d,0x74,0x26,0x00};			/* leal 0(%esi,1),%esi	*/
-  static const unsigned char f32_6[] =
-    {0x8d,0xb6,0x00,0x00,0x00,0x00};		/* leal 0L(%esi),%esi	*/
-  static const unsigned char f32_7[] =
-    {0x8d,0xb4,0x26,0x00,0x00,0x00,0x00};	/* leal 0L(%esi,1),%esi */
-  static const unsigned char f32_8[] =
-    {0x90,					/* nop			*/
-     0x8d,0xb4,0x26,0x00,0x00,0x00,0x00};	/* leal 0L(%esi,1),%esi */
-  static const unsigned char f32_9[] =
-    {0x89,0xf6,					/* movl %esi,%esi	*/
-     0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};	/* leal 0L(%edi,1),%edi */
-  static const unsigned char f32_10[] =
-    {0x8d,0x76,0x00,				/* leal 0(%esi),%esi	*/
-     0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};	/* leal 0L(%edi,1),%edi */
-  static const unsigned char f32_11[] =
-    {0x8d,0x74,0x26,0x00,			/* leal 0(%esi,1),%esi	*/
-     0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};	/* leal 0L(%edi,1),%edi */
-  static const unsigned char f32_12[] =
-    {0x8d,0xb6,0x00,0x00,0x00,0x00,		/* leal 0L(%esi),%esi	*/
-     0x8d,0xbf,0x00,0x00,0x00,0x00};		/* leal 0L(%edi),%edi	*/
-  static const unsigned char f32_13[] =
-    {0x8d,0xb6,0x00,0x00,0x00,0x00,		/* leal 0L(%esi),%esi	*/
-     0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};	/* leal 0L(%edi,1),%edi */
-  static const unsigned char f32_14[] =
-    {0x8d,0xb4,0x26,0x00,0x00,0x00,0x00,	/* leal 0L(%esi,1),%esi */
-     0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};	/* leal 0L(%edi,1),%edi */
-  static const unsigned char f16_3[] =
-    {0x8d,0x74,0x00};				/* lea 0(%esi),%esi	*/
-  static const unsigned char f16_4[] =
-    {0x8d,0xb4,0x00,0x00};			/* lea 0w(%si),%si	*/
-  static const unsigned char f16_5[] =
-    {0x90,					/* nop			*/
-     0x8d,0xb4,0x00,0x00};			/* lea 0w(%si),%si	*/
-  static const unsigned char f16_6[] =
-    {0x89,0xf6,					/* mov %si,%si		*/
-     0x8d,0xbd,0x00,0x00};			/* lea 0w(%di),%di	*/
-  static const unsigned char f16_7[] =
-    {0x8d,0x74,0x00,				/* lea 0(%si),%si	*/
-     0x8d,0xbd,0x00,0x00};			/* lea 0w(%di),%di	*/
-  static const unsigned char f16_8[] =
-    {0x8d,0xb4,0x00,0x00,			/* lea 0w(%si),%si	*/
-     0x8d,0xbd,0x00,0x00};			/* lea 0w(%di),%di	*/
-  static const unsigned char jump_31[] =
-    {0xeb,0x1d,0x90,0x90,0x90,0x90,0x90,	/* jmp .+31; lotsa nops	*/
-     0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,
-     0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,
-     0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90};
-  static const unsigned char *const f32_patt[] = {
-    f32_1, f32_2, f32_3, f32_4, f32_5, f32_6, f32_7, f32_8,
-    f32_9, f32_10, f32_11, f32_12, f32_13, f32_14
-  };
-  static const unsigned char *const f16_patt[] = {
-    f32_1, f32_2, f16_3, f16_4, f16_5, f16_6, f16_7, f16_8
-  };
-  /* nopl (%[re]ax) */
-  static const unsigned char alt_3[] =
-    {0x0f,0x1f,0x00};
-  /* nopl 0(%[re]ax) */
-  static const unsigned char alt_4[] =
-    {0x0f,0x1f,0x40,0x00};
-  /* nopl 0(%[re]ax,%[re]ax,1) */
-  static const unsigned char alt_5[] =
-    {0x0f,0x1f,0x44,0x00,0x00};
-  /* nopw 0(%[re]ax,%[re]ax,1) */
-  static const unsigned char alt_6[] =
-    {0x66,0x0f,0x1f,0x44,0x00,0x00};
-  /* nopl 0L(%[re]ax) */
-  static const unsigned char alt_7[] =
-    {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00};
-  /* nopl 0L(%[re]ax,%[re]ax,1) */
-  static const unsigned char alt_8[] =
-    {0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
-  /* nopw 0L(%[re]ax,%[re]ax,1) */
-  static const unsigned char alt_9[] =
-    {0x66,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
-  /* nopw %cs:0L(%[re]ax,%[re]ax,1) */
-  static const unsigned char alt_10[] =
-    {0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
-  static const unsigned char *const alt_patt[] = {
-    f32_1, f32_2, alt_3, alt_4, alt_5, alt_6, alt_7, alt_8,
-    alt_9, alt_10
-  };
+static const unsigned char f32_1[] =
+  {0x90};				/* nop			*/
+static const unsigned char f32_2[] =
+  {0x66,0x90};				/* xchg %ax,%ax		*/
+static const unsigned char f32_3[] =
+  {0x8d,0x76,0x00};			/* leal 0(%esi),%esi	*/
+static const unsigned char f32_4[] =
+  {0x8d,0x74,0x26,0x00};		/* leal 0(%esi,1),%esi	*/
+static const unsigned char f32_5[] =
+  {0x90,				/* nop			*/
+   0x8d,0x74,0x26,0x00};		/* leal 0(%esi,1),%esi	*/
+static const unsigned char f32_6[] =
+  {0x8d,0xb6,0x00,0x00,0x00,0x00};	/* leal 0L(%esi),%esi	*/
+static const unsigned char f32_7[] =
+  {0x8d,0xb4,0x26,0x00,0x00,0x00,0x00};	/* leal 0L(%esi,1),%esi */
+static const unsigned char f32_8[] =
+  {0x90,				/* nop			*/
+   0x8d,0xb4,0x26,0x00,0x00,0x00,0x00};	/* leal 0L(%esi,1),%esi */
+static const unsigned char f32_9[] =
+  {0x89,0xf6,				/* movl %esi,%esi	*/
+   0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};	/* leal 0L(%edi,1),%edi */
+static const unsigned char f32_10[] =
+  {0x8d,0x76,0x00,			/* leal 0(%esi),%esi	*/
+   0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};	/* leal 0L(%edi,1),%edi */
+static const unsigned char f32_11[] =
+  {0x8d,0x74,0x26,0x00,			/* leal 0(%esi,1),%esi	*/
+   0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};	/* leal 0L(%edi,1),%edi */
+static const unsigned char f32_12[] =
+  {0x8d,0xb6,0x00,0x00,0x00,0x00,	/* leal 0L(%esi),%esi	*/
+   0x8d,0xbf,0x00,0x00,0x00,0x00};	/* leal 0L(%edi),%edi	*/
+static const unsigned char f32_13[] =
+  {0x8d,0xb6,0x00,0x00,0x00,0x00,	/* leal 0L(%esi),%esi	*/
+   0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};	/* leal 0L(%edi,1),%edi */
+static const unsigned char f32_14[] =
+  {0x8d,0xb4,0x26,0x00,0x00,0x00,0x00,	/* leal 0L(%esi,1),%esi */
+   0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};	/* leal 0L(%edi,1),%edi */
+static const unsigned char f16_3[] =
+  {0x8d,0x74,0x00};			/* lea 0(%esi),%esi	*/
+static const unsigned char f16_4[] =
+  {0x8d,0xb4,0x00,0x00};		/* lea 0w(%si),%si	*/
+static const unsigned char f16_5[] =
+  {0x90,				/* nop			*/
+   0x8d,0xb4,0x00,0x00};		/* lea 0w(%si),%si	*/
+static const unsigned char f16_6[] =
+  {0x89,0xf6,				/* mov %si,%si		*/
+   0x8d,0xbd,0x00,0x00};		/* lea 0w(%di),%di	*/
+static const unsigned char f16_7[] =
+  {0x8d,0x74,0x00,			/* lea 0(%si),%si	*/
+   0x8d,0xbd,0x00,0x00};		/* lea 0w(%di),%di	*/
+static const unsigned char f16_8[] =
+  {0x8d,0xb4,0x00,0x00,			/* lea 0w(%si),%si	*/
+   0x8d,0xbd,0x00,0x00};		/* lea 0w(%di),%di	*/
+static const unsigned char jump_31[] =
+  {0xeb,0x1d,0x90,0x90,0x90,0x90,0x90,	/* jmp .+31; lotsa nops	*/
+   0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,
+   0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,
+   0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90};
+/* 32-bit NOPs patterns.  */
+static const unsigned char *const f32_patt[] = {
+  f32_1, f32_2, f32_3, f32_4, f32_5, f32_6, f32_7, f32_8,
+  f32_9, f32_10, f32_11, f32_12, f32_13, f32_14
+};
+/* 16-bit NOPs patterns.  */
+static const unsigned char *const f16_patt[] = {
+  f32_1, f32_2, f16_3, f16_4, f16_5, f16_6, f16_7, f16_8
+};
+/* nopl (%[re]ax) */
+static const unsigned char alt_3[] =
+  {0x0f,0x1f,0x00};
+/* nopl 0(%[re]ax) */
+static const unsigned char alt_4[] =
+  {0x0f,0x1f,0x40,0x00};
+/* nopl 0(%[re]ax,%[re]ax,1) */
+static const unsigned char alt_5[] =
+  {0x0f,0x1f,0x44,0x00,0x00};
+/* nopw 0(%[re]ax,%[re]ax,1) */
+static const unsigned char alt_6[] =
+  {0x66,0x0f,0x1f,0x44,0x00,0x00};
+/* nopl 0L(%[re]ax) */
+static const unsigned char alt_7[] =
+  {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00};
+/* nopl 0L(%[re]ax,%[re]ax,1) */
+static const unsigned char alt_8[] =
+  {0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
+/* nopw 0L(%[re]ax,%[re]ax,1) */
+static const unsigned char alt_9[] =
+  {0x66,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
+/* nopw %cs:0L(%[re]ax,%[re]ax,1) */
+static const unsigned char alt_10[] =
+  {0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
+/* 32-bit and 64-bit NOPs patterns.  */
+static const unsigned char *const alt_patt[] = {
+  f32_1, f32_2, alt_3, alt_4, alt_5, alt_6, alt_7, alt_8,
+  alt_9, alt_10
+};
+/* 64-bit only: nopw %cs:0L(%eax,%eax,1) */
+static const unsigned char alt64_11[] =
+  {0x67,0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
+/* 64-bit NOPs patterns.  */
+static const unsigned char *const alt64_patt[] = {
+  f32_1, f32_2, alt_3, alt_4, alt_5, alt_6, alt_7, alt_8,
+  alt_9, alt_10, alt64_11
+};
+
+/* Copy COUNT bytes of NOPs to WHERE from PATT with the maximum
+   size of a single NOP instruction MAX_SINGLE_NOP_SIZE.  */
+
+static void
+i386_output_nops (char *where, const unsigned char *const *patt,
+		  int count, int max_single_nop_size)
+
+{
+  while (count > max_single_nop_size)
+    {
+      count -= max_single_nop_size;
+      memcpy (where + count, patt[max_single_nop_size - 1],
+	      max_single_nop_size);
+    }
 
+  if (count)
+    memcpy (where, patt[count - 1], count);
+}
+
+void
+i386_align_code (fragS *fragP, int count)
+{
   /* Only align for at least a positive non-zero boundary. */
   if (count <= 0 || count > MAX_MEM_FOR_RS_ALIGN_CODE)
     return;
@@ -1397,17 +1432,8 @@ i386_align_code (fragS *fragP, int count)
 	  /* Maximum length of an instruction is 10 byte.  If the
 	     padding is greater than 10 bytes and we don't use jump,
 	     we have to break it into smaller pieces.  */
-	  int padding = count;
-	  while (padding > 10)
-	    {
-	      padding -= 10;
-	      memcpy (fragP->fr_literal + fragP->fr_fix + padding,
-		      patt [9], 10);
-	    }
-
-	  if (padding)
-	    memcpy (fragP->fr_literal + fragP->fr_fix,
-		    patt [padding - 1], padding);
+	  i386_output_nops (fragP->fr_literal + fragP->fr_fix,
+			    patt, count, 10);
 	}
     }
   fragP->fr_var = count;
@@ -9449,6 +9475,9 @@ md_estimate_size_before_relax (fragS *fragP, segT segment)
    In:	Address of frag.
 	fr_type == rs_machine_dependent.
 	fr_subtype is what the address relaxed to.
+	Or
+	fr_type == rs_space_nop.
+	fr_var is the size of .nop directive.
 
    Out:	Any fixSs and constants are set up.
 	Caller will turn frag into a ".space 0".  */
@@ -9458,12 +9487,42 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED,
                  fragS *fragP)
 {
   unsigned char *opcode;
-  unsigned char *where_to_put_displacement = NULL;
+  unsigned char *where_to_put_displacement;
   offsetT target_address;
   offsetT opcode_address;
-  unsigned int extension = 0;
+  unsigned int extension;
   offsetT displacement_from_opcode_start;
 
+  if (fragP->fr_type == rs_space_nop)
+    {
+      /* Get the size of .nop directive.  */
+      offsetT amount = fragP->fr_var;
+      if (amount < 0
+	  || amount > MAX_NOP_DIRECTIVE_SIZE)
+	{
+	  as_bad_where (fragP->fr_file, fragP->fr_line,
+			 _("invalid .nop directive size: %ld "
+			   "(expect less than %d)"),
+			 (long) amount, MAX_NOP_DIRECTIVE_SIZE);
+	  /* Prevent repeat of this error message.  */
+	  fragP->fr_symbol = NULL;
+	}
+      else if (amount != 0)
+	{
+	  fix_new (fragP, fragP->fr_fix, amount,
+		   fragP->fr_symbol,
+		   fragP->fr_offset, 0,
+		   BFD_RELOC_NOP_DIRECTIVE);
+	  fragP->fr_fix += amount;
+	}
+      frag_wane (fragP);
+      return;
+    }
+
+  gas_assert (fragP->fr_type == rs_machine_dependent);
+
+  where_to_put_displacement = NULL;
+  extension = 0;
   opcode = (unsigned char *) fragP->fr_opcode;
 
   /* Address we want to reach in file space.  */
@@ -9564,8 +9623,27 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED,
 void
 md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 {
-  char *p = fixP->fx_where + fixP->fx_frag->fr_literal;
-  valueT value = *valP;
+  fragS *fragP = fixP->fx_frag;
+  char *p = fragP->fr_literal + fixP->fx_where;
+  valueT value;
+
+  if (fixP->fx_r_type == BFD_RELOC_NOP_DIRECTIVE)
+    {
+      /* Output NOPs for .nop directive.  */
+      const unsigned char *const *patt
+	= (flag_code == CODE_16BIT
+	   ? f16_patt
+	   : (flag_code == CODE_64BIT
+	      ? alt64_patt
+	      : alt_patt));
+      /* The maximum size of a single NOP instruction is stored in
+         the first byte.  */
+      i386_output_nops (p, patt, fixP->fx_size, *p);
+      fixP->fx_done = 1;
+      return;
+    }
+
+  value = *valP;
 
 #if !defined (TE_Mach)
   if (fixP->fx_pcrel)
@@ -10830,6 +10908,79 @@ s_bss (int ignore ATTRIBUTE_UNUSED)
 
 #endif
 
+/* Implement .nop directive.  */
+
+void
+s_nop (int ignore ATTRIBUTE_UNUSED)
+{
+  expressionS exp;
+  expressionS val;
+  int max_single_nop_size;
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+#ifdef md_cons_align
+  md_cons_align (1);
+#endif
+
+  /* Get the longest single NOP size.  */
+  max_single_nop_size
+    = (flag_code == CODE_16BIT
+       ? sizeof (f16_patt) / sizeof (f16_patt[0])
+       : (flag_code == CODE_64BIT
+	  ? sizeof (alt64_patt) / sizeof (alt64_patt[0])
+	  : sizeof (alt_patt) / sizeof (alt_patt[0])));
+				     ;
+  expression (&exp);
+
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer == ',')
+    {
+      ++input_line_pointer;
+      expression (&val);
+    }
+  else
+    {
+      /* Default to the longest single NOP.  */
+      val.X_op = O_constant;
+      val.X_add_number = max_single_nop_size;
+    }
+
+  if (val.X_op == O_constant)
+    {
+      if (val.X_add_number <= 0
+	  || val.X_add_number > max_single_nop_size)
+	{
+	  as_bad (_("invalide single nop size: %ld "
+		    "(expect within [0, %d])"),
+		  (long) val.X_add_number, max_single_nop_size);
+	  goto getout;
+	}
+
+      /* Store the maximum single NOP size in fr_opcode.  */
+      if (!need_pass_2)
+	{
+	  char *p;
+	  symbolS *sym = make_expr_symbol (&exp);
+
+	  /* Start a new rs_space_nop frag for .nop directive with
+	     up to MAX_NOP_DIRECTIVE_SIZE bytes of NOPs.  Store the
+	     maximum size of a single NOP instruction in the first
+	     byte of NOP output.  */
+	  p = frag_var (rs_space_nop, MAX_NOP_DIRECTIVE_SIZE, 0,
+			(relax_substateT) 0, sym, (offsetT) 0, NULL);
+	  *p = val.X_add_number;
+	}
+    }
+  else
+    as_bad (_("unsupported variable single nop limit in .nop directive"));
+
+getout:
+  demand_empty_rest_of_line ();
+}
+
 void
 i386_validate_fix (fixS *fixp)
 {
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
index 6e4f440c09..6eb120389b 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -281,6 +281,9 @@ extern void sco_id (void);
 
 #define WORKING_DOT_WORD 1
 
+/* We want .nop direct directive.  */
+#define TARGET_USE_NOP_DIRECTIVE 1
+
 /* We want .cfi_* pseudo-ops for generating unwind info.  */
 #define TARGET_USE_CFIPOP 1
 
diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo
index f4e0fddefb..c693af159a 100644
--- a/gas/doc/as.texinfo
+++ b/gas/doc/as.texinfo
@@ -4523,6 +4523,9 @@ Some machine configurations provide additional directives.
 * Sleb128::			@code{.sleb128 @var{expressions}}
 @ifclear no-space-dir
 * Space::                       @code{.space @var{size} , @var{fill}}
+@ifset I80386
+* Nop::                         @code{.nop @var{size}[, @var{limit}]}
+@end ifset
 @end ifclear
 @ifset have-stabs
 * Stab::                        @code{.stabd, .stabn, .stabs}
@@ -6851,6 +6854,23 @@ Assembly Language Reference Manual} (HP 92432-90001) for the meaning of the
 for a summary.
 @end quotation
 @end ifset
+
+@ifset I80386
+@node Nop
+@section @code{.nop @var{size}[, @var{limit}]}
+
+@cindex @code{nop} directive
+@cindex filling memory with NOP
+This directive emits @var{size} bytes filled with @code{NOP}
+instructions.  @var{size} is absolute expression, which must be
+between 0 and 512.  @var{limit} specifies the size limit of a
+single @code{NOP} instruction.  If the comma and @var{limit} are
+omitted, @var{limit} is assumed to the maximum supported size of
+a single @code{NOP} instruction.  The valid values of @var{limit}
+are between 1 and 8 for 16-bit mode, between 1 and 10 for 32-bit mode,
+between 1 and 11 for 64-bit mode.  This directive is only allowed
+in text sections.
+@end ifset
 @end ifclear
 
 @ifset have-stabs
diff --git a/gas/testsuite/gas/i386/i386.exp b/gas/testsuite/gas/i386/i386.exp
index a21ef84997..0ca49ea46f 100644
--- a/gas/testsuite/gas/i386/i386.exp
+++ b/gas/testsuite/gas/i386/i386.exp
@@ -431,6 +431,8 @@ if [expr ([istarget "i*86-*-*"] ||  [istarget "x86_64-*-*"]) && [gas_32_check]]
     run_dump_test "align-1a"
     run_dump_test "align-1b"
     run_list_test "inval-pseudo" "-al"
+    run_dump_test "nop-1"
+    run_dump_test "nop-2"
 
     # These tests require support for 8 and 16 bit relocs,
     # so we only run them for ELF and COFF targets.
@@ -494,6 +496,10 @@ if [expr ([istarget "i*86-*-*"] ||  [istarget "x86_64-*-*"]) && [gas_32_check]]
 	run_dump_test "got-no-relax"
 
 	run_dump_test "addend"
+	run_dump_test "nop-3"
+	run_dump_test "nop-4"
+	run_dump_test "nop-5"
+	run_dump_test "nop-6"
 
 	if { [gas_64_check] } then {
 	    run_dump_test "att-regs"
@@ -538,6 +544,7 @@ if [expr [istarget "i*86-*-*"] || [istarget "x86_64-*-*"]] then {
     run_list_test "space1" "-al"
     run_dump_test rept
     run_dump_test pr19498
+    run_list_test "nop-bad-1" ""
     if [is_elf_format] then {
 	run_list_test_stdin "list-1" "-al"
 	run_list_test_stdin "list-2" "-al"
@@ -904,6 +911,8 @@ if [expr ([istarget "i*86-*-*"] || [istarget "x86_64-*-*"]) && [gas_64_check]] t
     run_list_test "x86-64-notrackbad" "-al"
     run_dump_test "x86-64-movd"
     run_dump_test "x86-64-movd-intel"
+    run_dump_test "x86-64-nop-1"
+    run_dump_test "x86-64-nop-2"
 
     if { ![istarget "*-*-aix*"]
       && ![istarget "*-*-beos*"]
@@ -961,6 +970,10 @@ if [expr ([istarget "i*86-*-*"] || [istarget "x86_64-*-*"]) && [gas_64_check]] t
 	run_dump_test "x86-64-gotpcrel-no-relax"
 
 	run_dump_test "x86-64-addend"
+	run_dump_test "x86-64-nop-3"
+	run_dump_test "x86-64-nop-4"
+	run_dump_test "x86-64-nop-5"
+	run_dump_test "x86-64-nop-6"
     }
 
     set ASFLAGS "$old_ASFLAGS"
diff --git a/gas/testsuite/gas/i386/nop-1.d b/gas/testsuite/gas/i386/nop-1.d
new file mode 100644
index 0000000000..46422c88db
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-1.d
@@ -0,0 +1,31 @@
+#objdump: -drw
+#name: i386 .nop 1
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <single>:
+ +[a-f0-9]+:	90                   	nop
+
+0+1 <pseudo_1>:
+ +[a-f0-9]+:	90                   	nop
+
+0+2 <pseudo_8>:
+ +[a-f0-9]+:	0f 1f 84 00 00 00 00 00 	nopl   0x0\(%eax,%eax,1\)
+
+0+a <pseudo_8_4>:
+ +[a-f0-9]+:	0f 1f 40 00          	nopl   0x0\(%eax\)
+ +[a-f0-9]+:	0f 1f 40 00          	nopl   0x0\(%eax\)
+
+0+12 <pseudo_20>:
+ +[a-f0-9]+:	66 2e 0f 1f 84 00 00 00 00 00 	nopw   %cs:0x0\(%eax,%eax,1\)
+ +[a-f0-9]+:	66 2e 0f 1f 84 00 00 00 00 00 	nopw   %cs:0x0\(%eax,%eax,1\)
+
+0+26 <pseudo_30>:
+ +[a-f0-9]+:	66 2e 0f 1f 84 00 00 00 00 00 	nopw   %cs:0x0\(%eax,%eax,1\)
+ +[a-f0-9]+:	66 2e 0f 1f 84 00 00 00 00 00 	nopw   %cs:0x0\(%eax,%eax,1\)
+ +[a-f0-9]+:	66 2e 0f 1f 84 00 00 00 00 00 	nopw   %cs:0x0\(%eax,%eax,1\)
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+#pass
diff --git a/gas/testsuite/gas/i386/nop-1.s b/gas/testsuite/gas/i386/nop-1.s
new file mode 100644
index 0000000000..891783dce8
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-1.s
@@ -0,0 +1,21 @@
+       .text
+single:
+	.nop 0
+	nop
+
+pseudo_1:
+	.nop 1
+
+pseudo_8:
+	.nop 8
+
+pseudo_8_4:
+	.nop 8, 4
+
+pseudo_20:
+	.nop 20
+
+pseudo_30:
+	.nop 30
+
+	xor %eax, %eax
diff --git a/gas/testsuite/gas/i386/nop-2.d b/gas/testsuite/gas/i386/nop-2.d
new file mode 100644
index 0000000000..332b990a97
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-2.d
@@ -0,0 +1,40 @@
+#objdump: -drw -Mi8086
+#name: i386 .nop 2
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <single>:
+ +[a-f0-9]+:	90                   	nop
+
+0+1 <pseudo_1>:
+ +[a-f0-9]+:	90                   	nop
+
+0+2 <pseudo_8>:
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d bd 00 00          	lea    0x0\(%di\),%di
+
+0+a <pseudo_8_4>:
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+
+0+12 <pseudo_20>:
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d bd 00 00          	lea    0x0\(%di\),%di
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d bd 00 00          	lea    0x0\(%di\),%di
+
+0+26 <pseudo_30>:
+ +[a-f0-9]+:	89 f6                	mov    %si,%si
+ +[a-f0-9]+:	8d bd 00 00          	lea    0x0\(%di\),%di
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d bd 00 00          	lea    0x0\(%di\),%di
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d bd 00 00          	lea    0x0\(%di\),%di
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d bd 00 00          	lea    0x0\(%di\),%di
+ +[a-f0-9]+:	66 31 c0             	xor    %eax,%eax
+#pass
diff --git a/gas/testsuite/gas/i386/nop-2.s b/gas/testsuite/gas/i386/nop-2.s
new file mode 100644
index 0000000000..2b71b9786d
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-2.s
@@ -0,0 +1,22 @@
+       .text
+       .code16
+single:
+	.nop 0
+	nop
+
+pseudo_1:
+	.nop 1
+
+pseudo_8:
+	.nop 8
+
+pseudo_8_4:
+	.nop 8, 4
+
+pseudo_20:
+	.nop 20
+
+pseudo_30:
+	.nop 30
+
+	xor %eax, %eax
diff --git a/gas/testsuite/gas/i386/nop-3.d b/gas/testsuite/gas/i386/nop-3.d
new file mode 100644
index 0000000000..bebd24bc08
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-3.d
@@ -0,0 +1,20 @@
+#objdump: -drw
+#name: i386 .nop 3
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <_start>:
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+ +[a-f0-9]+:	85 c0                	test   %eax,%eax
+ +[a-f0-9]+:	0f 1f 00             	nopl   \(%eax\)
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+
+Disassembly of section .altinstr_replacement:
+
+0+ <.altinstr_replacement>:
+ +[a-f0-9]+:	e9 fc ff ff ff       	jmp    1 <.altinstr_replacement\+0x1>	1: (R_386_PC)?(DISP)?32	foo
+#pass
diff --git a/gas/testsuite/gas/i386/nop-3.s b/gas/testsuite/gas/i386/nop-3.s
new file mode 100644
index 0000000000..57370ff579
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-3.s
@@ -0,0 +1,15 @@
+	.text
+_start:
+	xor %eax, %eax
+140:
+	testl %eax, %eax
+141:
+	.nop -(((144f-143f)-(141b-140b)) > 0)*((144f-143f)-(141b-140b)),7
+142:
+	xor %eax, %eax
+	.pushsection .altinstr_replacement,"ax"
+143:
+	jmp foo
+144:
+	.popsection
+	xor %eax, %eax
diff --git a/gas/testsuite/gas/i386/nop-4.d b/gas/testsuite/gas/i386/nop-4.d
new file mode 100644
index 0000000000..99ddcd3994
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-4.d
@@ -0,0 +1,23 @@
+#objdump: -drw
+#name: i386 .nop 4
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <_start>:
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+ +[a-f0-9]+:	85 c0                	test   %eax,%eax
+ +[a-f0-9]+:	66 0f 1f 84 00 00 00 00 00 	nopw   0x0\(%eax,%eax,1\)
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+
+Disassembly of section .altinstr_replacement:
+
+0+ <.altinstr_replacement>:
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	e9 fc ff ff ff       	jmp    7 <.altinstr_replacement\+0x7>	7: (R_386_PC)?(DISP)?32	foo
+#pass
diff --git a/gas/testsuite/gas/i386/nop-4.s b/gas/testsuite/gas/i386/nop-4.s
new file mode 100644
index 0000000000..f7aa11187e
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-4.s
@@ -0,0 +1,18 @@
+	.text
+_start:
+	xor %eax, %eax
+140:
+	testl %eax, %eax
+141:
+	.nop -(((144f-143f)-(141b-140b)) > 0)*((144f-143f)-(141b-140b))
+142:
+	xor %eax, %eax
+	.pushsection .altinstr_replacement,"ax"
+143:
+	mov %eax, %eax
+	mov %eax, %eax
+	mov %eax, %eax
+	jmp foo
+144:
+	.popsection
+	xor %eax, %eax
diff --git a/gas/testsuite/gas/i386/nop-5.d b/gas/testsuite/gas/i386/nop-5.d
new file mode 100644
index 0000000000..aab4258b19
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-5.d
@@ -0,0 +1,25 @@
+#objdump: -drw
+#name: i386 .nop 5
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <_start>:
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+ +[a-f0-9]+:	85 c0                	test   %eax,%eax
+ +[a-f0-9]+:	0f 1f 44 00 00       	nopl   0x0\(%eax,%eax,1\)
+ +[a-f0-9]+:	66 0f 1f 44 00 00    	nopw   0x0\(%eax,%eax,1\)
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+
+Disassembly of section .altinstr_replacement:
+
+0+ <.altinstr_replacement>:
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	e9 fc ff ff ff       	jmp    9 <.altinstr_replacement\+0x9>	9: (R_386_PC)?(DISP)?32	foo
+#pass
diff --git a/gas/testsuite/gas/i386/nop-5.s b/gas/testsuite/gas/i386/nop-5.s
new file mode 100644
index 0000000000..4f563ce82f
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-5.s
@@ -0,0 +1,19 @@
+	.text
+_start:
+	xor %eax, %eax
+140:
+	testl %eax, %eax
+141:
+	.nop -(((144f-143f)-(141b-140b)) > 0)*((144f-143f)-(141b-140b)),6
+142:
+	xor %eax, %eax
+	.pushsection .altinstr_replacement,"ax"
+143:
+	mov %eax, %eax
+	mov %eax, %eax
+	mov %eax, %eax
+	mov %eax, %eax
+	jmp foo
+144:
+	.popsection
+	xor %eax, %eax
diff --git a/gas/testsuite/gas/i386/nop-6.d b/gas/testsuite/gas/i386/nop-6.d
new file mode 100644
index 0000000000..93ee8def03
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-6.d
@@ -0,0 +1,17 @@
+#objdump: -drw
+#name: i386 .nop 6
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <_start>:
+ +[a-f0-9]+:	0f 1f 40 00          	nopl   0x0\(%eax\)
+
+Disassembly of section .altinstr_replacement:
+
+0+ <.altinstr_replacement>:
+ +[a-f0-9]+:	75 fe                	jne    0 <_start>
+ +[a-f0-9]+:	89 c4                	mov    %eax,%esp
+#pass
diff --git a/gas/testsuite/gas/i386/nop-6.s b/gas/testsuite/gas/i386/nop-6.s
new file mode 100644
index 0000000000..c7b1e2cbf0
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-6.s
@@ -0,0 +1,25 @@
+.macro mknops nr_bytes
+    .nop \nr_bytes, 9
+.endm
+
+.macro ALTERNATIVE
+.L\@_orig_s:
+.L\@_orig_e:
+     mknops (-(((.L\@_repl_e\()1 - .L\@_repl_s\()1) - (.L\@_orig_e - .L\@_orig_s)) > 0) * ((.L\@_repl_e\()1 - .L\@_repl_s\()1) - (.L\@_orig_e - .L\@_orig_s)))
+.L\@_orig_p:
+
+    .section .discard, "a", @progbits
+    .byte (.L\@_orig_p - .L\@_orig_s)
+    .byte 0xff + (.L\@_repl_e\()1 - .L\@_repl_s\()1) - (.L\@_orig_p - .L\@_orig_s)
+
+    .section .altinstr_replacement, "ax", @progbits
+.L\@_repl_s\()1:
+.L\@_fill_rsb_loop:
+    jnz .L\@_fill_rsb_loop
+    mov %eax, %esp
+.L\@_repl_e\()1:
+.endm
+
+	.text
+_start:
+ALTERNATIVE
diff --git a/gas/testsuite/gas/i386/nop-bad-1.l b/gas/testsuite/gas/i386/nop-bad-1.l
new file mode 100644
index 0000000000..8e6c9daac9
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-bad-1.l
@@ -0,0 +1,5 @@
+.*: Assembler messages:
+.*:2: Error: invalide single nop size: -2 \(expect within \[0, [0-9]+\]\)
+.*:3: Error: invalide single nop size: 20 \(expect within \[0, [0-9]+\]\)
+.*:5: Warning: .space or .fill with negative value, ignored
+.*:4: Error: invalid .nop directive size: 600 \(expect less than [0-9]+\)
diff --git a/gas/testsuite/gas/i386/nop-bad-1.s b/gas/testsuite/gas/i386/nop-bad-1.s
new file mode 100644
index 0000000000..0127e418da
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-bad-1.s
@@ -0,0 +1,5 @@
+	.text
+        .nop 100, -2
+        .nop 100, 20
+        .nop 600
+        .nop -1
diff --git a/gas/testsuite/gas/i386/x86-64-nop-1.d b/gas/testsuite/gas/i386/x86-64-nop-1.d
new file mode 100644
index 0000000000..f3edc7d346
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-nop-1.d
@@ -0,0 +1,32 @@
+#source: nop-1.s
+#objdump: -drw
+#name: x86-64 .nop 1
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <single>:
+ +[a-f0-9]+:	90                   	nop
+
+0+1 <pseudo_1>:
+ +[a-f0-9]+:	90                   	nop
+
+0+2 <pseudo_8>:
+ +[a-f0-9]+:	0f 1f 84 00 00 00 00 00 	nopl   0x0\(%rax,%rax,1\)
+
+0+a <pseudo_8_4>:
+ +[a-f0-9]+:	0f 1f 40 00          	nopl   0x0\(%rax\)
+ +[a-f0-9]+:	0f 1f 40 00          	nopl   0x0\(%rax\)
+
+0+12 <pseudo_20>:
+ +[a-f0-9]+:	66 0f 1f 84 00 00 00 00 00 	nopw   0x0\(%rax,%rax,1\)
+ +[a-f0-9]+:	67 66 2e 0f 1f 84 00 00 00 00 00 	nopw   %cs:0x0\(%eax,%eax,1\)
+
+0+26 <pseudo_30>:
+ +[a-f0-9]+:	0f 1f 84 00 00 00 00 00 	nopl   0x0\(%rax,%rax,1\)
+ +[a-f0-9]+:	67 66 2e 0f 1f 84 00 00 00 00 00 	nopw   %cs:0x0\(%eax,%eax,1\)
+ +[a-f0-9]+:	67 66 2e 0f 1f 84 00 00 00 00 00 	nopw   %cs:0x0\(%eax,%eax,1\)
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+#pass
diff --git a/gas/testsuite/gas/i386/x86-64-nop-2.d b/gas/testsuite/gas/i386/x86-64-nop-2.d
new file mode 100644
index 0000000000..e894d2c7bf
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-nop-2.d
@@ -0,0 +1,41 @@
+#source: nop-2.s
+#objdump: -drw -Mi8086
+#name: x86-64 .nop 2
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <single>:
+ +[a-f0-9]+:	90                   	nop
+
+0+1 <pseudo_1>:
+ +[a-f0-9]+:	90                   	nop
+
+0+2 <pseudo_8>:
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d bd 00 00          	lea    0x0\(%di\),%di
+
+0+a <pseudo_8_4>:
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+
+0+12 <pseudo_20>:
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d bd 00 00          	lea    0x0\(%di\),%di
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d bd 00 00          	lea    0x0\(%di\),%di
+
+0+26 <pseudo_30>:
+ +[a-f0-9]+:	89 f6                	mov    %si,%si
+ +[a-f0-9]+:	8d bd 00 00          	lea    0x0\(%di\),%di
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d bd 00 00          	lea    0x0\(%di\),%di
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d bd 00 00          	lea    0x0\(%di\),%di
+ +[a-f0-9]+:	8d b4 00 00          	lea    0x0\(%si\),%si
+ +[a-f0-9]+:	8d bd 00 00          	lea    0x0\(%di\),%di
+ +[a-f0-9]+:	66 31 c0             	xor    %eax,%eax
+#pass
diff --git a/gas/testsuite/gas/i386/x86-64-nop-3.d b/gas/testsuite/gas/i386/x86-64-nop-3.d
new file mode 100644
index 0000000000..b43239af6f
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-nop-3.d
@@ -0,0 +1,21 @@
+#source: nop-3.s
+#objdump: -drw
+#name: x86-64 .nop 3
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <_start>:
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+ +[a-f0-9]+:	85 c0                	test   %eax,%eax
+ +[a-f0-9]+:	0f 1f 00             	nopl   \(%rax\)
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+
+Disassembly of section .altinstr_replacement:
+
+0+ <.altinstr_replacement>:
+ +[a-f0-9]+:	e9 00 00 00 00       	jmpq   5 <_start\+0x5>	1: R_X86_64_PLT32	foo-0x4
+#pass
diff --git a/gas/testsuite/gas/i386/x86-64-nop-4.d b/gas/testsuite/gas/i386/x86-64-nop-4.d
new file mode 100644
index 0000000000..a910171303
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-nop-4.d
@@ -0,0 +1,24 @@
+#source: nop-4.s
+#objdump: -drw
+#name: x86-64 .nop 4
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <_start>:
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+ +[a-f0-9]+:	85 c0                	test   %eax,%eax
+ +[a-f0-9]+:	66 0f 1f 84 00 00 00 00 00 	nopw   0x0\(%rax,%rax,1\)
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+
+Disassembly of section .altinstr_replacement:
+
+0+ <.altinstr_replacement>:
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	e9 00 00 00 00       	jmpq   b <_start\+0xb>	7: R_X86_64_PLT32	foo-0x4
+#pass
diff --git a/gas/testsuite/gas/i386/x86-64-nop-5.d b/gas/testsuite/gas/i386/x86-64-nop-5.d
new file mode 100644
index 0000000000..57493cf6dc
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-nop-5.d
@@ -0,0 +1,26 @@
+#source: nop-5.s
+#objdump: -drw
+#name: x86-64 .nop 5
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <_start>:
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+ +[a-f0-9]+:	85 c0                	test   %eax,%eax
+ +[a-f0-9]+:	0f 1f 44 00 00       	nopl   0x0\(%rax,%rax,1\)
+ +[a-f0-9]+:	66 0f 1f 44 00 00    	nopw   0x0\(%rax,%rax,1\)
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+ +[a-f0-9]+:	31 c0                	xor    %eax,%eax
+
+Disassembly of section .altinstr_replacement:
+
+0+ <.altinstr_replacement>:
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	e9 00 00 00 00       	jmpq   d <_start\+0xd>	9: R_X86_64_PLT32	foo-0x4
+#pass
diff --git a/gas/testsuite/gas/i386/x86-64-nop-6.d b/gas/testsuite/gas/i386/x86-64-nop-6.d
new file mode 100644
index 0000000000..520f590945
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-nop-6.d
@@ -0,0 +1,18 @@
+#source: nop-6.s
+#objdump: -drw
+#name: x86-64 .nop 6
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <_start>:
+ +[a-f0-9]+:	0f 1f 40 00          	nopl   0x0\(%rax\)
+
+Disassembly of section .altinstr_replacement:
+
+0+ <.altinstr_replacement>:
+ +[a-f0-9]+:	75 fe                	jne    0 <_start>
+ +[a-f0-9]+:	89 c4                	mov    %eax,%esp
+#pass
diff --git a/gas/write.c b/gas/write.c
index 2869660cfe..9e2ef5767d 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -457,6 +457,12 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
       fragP->fr_type = rs_fill;
       break;
 
+#ifdef TARGET_USE_NOP_DIRECTIVE
+    case rs_space_nop:
+      md_convert_frag (stdoutput, sec, fragP);
+      break;
+#endif
+
     case rs_fill:
       break;
 
@@ -2461,6 +2467,9 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 	  break;
 
 	case rs_space:
+#ifdef TARGET_USE_NOP_DIRECTIVE
+	case rs_space_nop:
+#endif
 	  break;
 
 	case rs_machine_dependent:
@@ -2765,6 +2774,9 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 		break;
 
 	      case rs_space:
+#ifdef TARGET_USE_NOP_DIRECTIVE
+	      case rs_space_nop:
+#endif
 		growth = 0;
 		if (symbolP)
 		  {
@@ -2795,8 +2807,15 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 			fragP->fr_symbol = 0;
 		      }
 		    else
-		      growth = (was_address + fragP->fr_fix + amount
-				- fragP->fr_next->fr_address);
+		      {
+			growth = (was_address + fragP->fr_fix + amount
+				  - fragP->fr_next->fr_address);
+#ifdef TARGET_USE_NOP_DIRECTIVE
+			if (growth != 0
+			    && fragP->fr_type == rs_space_nop)
+			  fragP->fr_var = growth;
+#endif
+		      }
 		  }
 		break;
 
-- 
2.14.3


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]