x86: Add .nop directive to assembler

Message ID CAMe9rOr9n4XwM4wMhR3RNP4EXUE37nu0ui3SL4-nUvLAF_T9yA@mail.gmail.com
State New
Headers show
Series
  • x86: Add .nop directive to assembler
Related show

Commit Message

H.J. Lu Feb. 12, 2018, 7:38 p.m.
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.

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.

OK for master?

Andrew, please test my current users/hjl/nop branch.

Thanks.


-- 
H.J.

Comments

Jan Beulich Feb. 13, 2018, 8:50 a.m. | #1
>>> 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.

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

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?

> 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?

>  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?

> 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.

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

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)?

Jan
H.J. Lu Feb. 14, 2018, 2:03 p.m. | #2
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;
Michael Matz Feb. 14, 2018, 3:55 p.m. | #3
Hi,

On Wed, 14 Feb 2018, H.J. Lu wrote:

> > 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.


If I may chime in, it didn't occur to me before Jan mentioned it, but yes, 
I also think extending .fill feels more natural.


Ciao,
Michael.
H.J. Lu Feb. 14, 2018, 4:19 p.m. | #4
On Wed, Feb 14, 2018 at 7:55 AM, Michael Matz <matz@suse.de> wrote:
> Hi,

>

> On Wed, 14 Feb 2018, H.J. Lu wrote:

>

>> > 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.

>

> If I may chime in, it didn't occur to me before Jan mentioned it, but yes,

> I also think extending .fill feels more natural.

>


No, .fill isn't what we want.  We need to extend

.skip SIZE , FILL

So what should FILL look like?

BTW, to implement that, we need to extend both rs_fill and rs_space relax
states.

-- 
H.J.
Michael Matz Feb. 14, 2018, 4:48 p.m. | #5
Hi,

On Wed, 14 Feb 2018, H.J. Lu wrote:

> >> 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.

> >

> > If I may chime in, it didn't occur to me before Jan mentioned it, but yes,

> > I also think extending .fill feels more natural.

> >

> 

> No, .fill isn't what we want.


I fat-fingered that one, yes I meant .fill.

> We need to extend

> 

> .skip SIZE , FILL

> 

> So what should FILL look like?


@nop, $nop, %nop?  (I think only 'nop' without prefix would interact with 
uses in macros?  The '@' prefix resembles a prefix for relocations, so 
that might be the natural choice).  Perhaps also 'nop' should be 'anynop' 
or so, to indicate it's not just the single-byte 0x90 filler).


Ciao,
Michael.
Andrew Cooper Feb. 14, 2018, 5:03 p.m. | #6
On 14/02/18 14:03, H.J. Lu wrote:
> On Tue, Feb 13, 2018 at 12:50 AM, Jan Beulich <JBeulich@suse.com> wrote:

>

>> 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?


Sorry for the delay.  This patch does work correctly.

As for exactly how this functionality gets implemented, I am not fussed
at all.  I'll defer that to the binutils community - its very easy for
me to tweak my patch series to start using it when the eventual
implementation is decided upon.

~Andrew
Jan Beulich Feb. 15, 2018, 8:42 a.m. | #7
>>> On 14.02.18 at 17:48, <matz@suse.de> wrote:

> On Wed, 14 Feb 2018, H.J. Lu wrote:

>> >> 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.

>> >

>> > If I may chime in, it didn't occur to me before Jan mentioned it, but yes,

>> > I also think extending .fill feels more natural.

>> >

>> 

>> No, .fill isn't what we want.

> 

> I fat-fingered that one, yes I meant .fill.


.fill again? Didn't you mean to write .skip this time?

>> We need to extend

>> 

>> .skip SIZE , FILL

>> 

>> So what should FILL look like?

> 

> @nop, $nop, %nop?  (I think only 'nop' without prefix would interact with 

> uses in macros?  The '@' prefix resembles a prefix for relocations, so 

> that might be the natural choice).  Perhaps also 'nop' should be 'anynop' 

> or so, to indicate it's not just the single-byte 0x90 filler).


Having thought about this some more - since currently FILL is
expected to be an expression, none of the above is really suitable.
How about "nop" (including the quotes) instead?

Jan
Michael Matz Feb. 15, 2018, 3:02 p.m. | #8
Hi,

On Thu, 15 Feb 2018, Jan Beulich wrote:

> >> > I also think extending .fill feels more natural.

> >> >

> >> 

> >> No, .fill isn't what we want.

> > 

> > I fat-fingered that one, yes I meant .fill.

> 

> .fill again? Didn't you mean to write .skip this time?


Sigh.  It seems my fingers are unusually fat in this thread.  .skip, 
dammit! .skip, .skip, .skip!

> Having thought about this some more - since currently FILL is expected 

> to be an expression, none of the above is really suitable.


Well, that could be easily rectified.  Something along the lines of:

diff --git a/gas/read.c b/gas/read.c
index 7bf52f1..8730318 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -3326,6 +3326,7 @@ s_space (int mult)
   char *stop = NULL;
   char stopc = 0;
   int bytes;
+  bfd_boolean do_nops = FALSE;
 
 #ifdef md_flush_pending_output
   md_flush_pending_output ();
@@ -3386,7 +3387,12 @@ s_space (int mult)
   if (*input_line_pointer == ',')
     {
       ++input_line_pointer;
-      expression (&val);
+      SKIP_WHITESPACE ();
+      if (!strncasecmp (input_line_pointer, "@NOP", 4)
+         && (input_line_pointer[4] == ' ' || !input_line_pointer[4]))
+       do_nops = TRUE, input_line_pointer += 4;
+      else
+       expression (&val);
     }
   else
     {

> How about "nop" (including the quotes) instead?


Also possible, yeah.


Ciao,
Michael.
Jan Beulich Feb. 15, 2018, 3:26 p.m. | #9
>>> On 15.02.18 at 16:02, <matz@suse.de> wrote:

> Hi,

> 

> On Thu, 15 Feb 2018, Jan Beulich wrote:

> 

>> >> > I also think extending .fill feels more natural.

>> >> >

>> >> 

>> >> No, .fill isn't what we want.

>> > 

>> > I fat-fingered that one, yes I meant .fill.

>> 

>> .fill again? Didn't you mean to write .skip this time?

> 

> Sigh.  It seems my fingers are unusually fat in this thread.  .skip, 

> dammit! .skip, .skip, .skip!

> 

>> Having thought about this some more - since currently FILL is expected 

>> to be an expression, none of the above is really suitable.

> 

> Well, that could be easily rectified.  Something along the lines of:


I didn't make the remark because of foreseeing any issues with
coding this up, but because @NOP (or any of your other
suggestions) could actually be a valid symbol name, which could be
valid to use here even if it's not part of any expression in case it's
an absolute one (e.g. an equate). The leading @ is undesirable
anyway because of ARM's use of it as a comment char.

Jan

> diff --git a/gas/read.c b/gas/read.c

> index 7bf52f1..8730318 100644

> --- a/gas/read.c

> +++ b/gas/read.c

> @@ -3326,6 +3326,7 @@ s_space (int mult)

>    char *stop = NULL;

>    char stopc = 0;

>    int bytes;

> +  bfd_boolean do_nops = FALSE;

>  

>  #ifdef md_flush_pending_output

>    md_flush_pending_output ();

> @@ -3386,7 +3387,12 @@ s_space (int mult)

>    if (*input_line_pointer == ',')

>      {

>        ++input_line_pointer;

> -      expression (&val);

> +      SKIP_WHITESPACE ();

> +      if (!strncasecmp (input_line_pointer, "@NOP", 4)

> +         && (input_line_pointer[4] == ' ' || !input_line_pointer[4]))

> +       do_nops = TRUE, input_line_pointer += 4;

> +      else

> +       expression (&val);

>      }

>    else

>      {

> 

>> How about "nop" (including the quotes) instead?

> 

> Also possible, yeah.

> 

> 

> Ciao,

> Michael.
Michael Matz Feb. 15, 2018, 3:50 p.m. | #10
Hi,

On Thu, 15 Feb 2018, Jan Beulich wrote:

> >> to be an expression, none of the above is really suitable.

> > 

> > Well, that could be easily rectified.  Something along the lines of:

> 

> I didn't make the remark because of foreseeing any issues with

> coding this up, but because @NOP (or any of your other

> suggestions) could actually be a valid symbol name, which could be

> valid to use here even if it's not part of any expression in case it's

> an absolute one (e.g. an equate).


Well, then I don't see other ways of special casing something that also is 
a valid expression right now, like your string suggestion.  The problem of 
course being that this would reinterpret currently valid (though 
strange) directives into something else.  Right now
  .skip 10, "foo"
is equivalent to
  .skip 10, foo
and results in 10 one-byte relocs against symbol 'foo'.  We'd reinterpret 
this, which may be fine, but needs to be a conscious decision.

> The leading @ is undesirable anyway because of ARM's use of it as a 

> comment char.


Sure, other prefix chars could be used, e.g. one of the binary operators 
in expressions.  But in light of the above it might not be such a bright 
idea to extend .skip after all.


Ciao,
Michael.
Jan Beulich Feb. 15, 2018, 4:08 p.m. | #11
>>> On 15.02.18 at 16:50, <matz@suse.de> wrote:

> Hi,

> 

> On Thu, 15 Feb 2018, Jan Beulich wrote:

> 

>> >> to be an expression, none of the above is really suitable.

>> > 

>> > Well, that could be easily rectified.  Something along the lines of:

>> 

>> I didn't make the remark because of foreseeing any issues with

>> coding this up, but because @NOP (or any of your other

>> suggestions) could actually be a valid symbol name, which could be

>> valid to use here even if it's not part of any expression in case it's

>> an absolute one (e.g. an equate).

> 

> Well, then I don't see other ways of special casing something that also is 

> a valid expression right now, like your string suggestion.  The problem of 

> course being that this would reinterpret currently valid (though 

> strange) directives into something else.  Right now

>   .skip 10, "foo"

> is equivalent to

>   .skip 10, foo

> and results in 10 one-byte relocs against symbol 'foo'.  We'd reinterpret 

> this, which may be fine, but needs to be a conscious decision.


Oh, right, symbol names may be quoted now.

>> The leading @ is undesirable anyway because of ARM's use of it as a 

>> comment char.

> 

> Sure, other prefix chars could be used, e.g. one of the binary operators 

> in expressions.  But in light of the above it might not be such a bright 

> idea to extend .skip after all.


Well, if we can fine a universally usable escape character, things
ought to be fine this way. Apart from binary operator chars which
aren't also unary operator ones, \ would come to mind.

Jan
H.J. Lu Feb. 15, 2018, 5:11 p.m. | #12
On Thu, Feb 15, 2018 at 8:08 AM, Jan Beulich <JBeulich@suse.com> wrote:
>>>> On 15.02.18 at 16:50, <matz@suse.de> wrote:

>> Hi,

>>

>> On Thu, 15 Feb 2018, Jan Beulich wrote:

>>

>>> >> to be an expression, none of the above is really suitable.

>>> >

>>> > Well, that could be easily rectified.  Something along the lines of:

>>>

>>> I didn't make the remark because of foreseeing any issues with

>>> coding this up, but because @NOP (or any of your other

>>> suggestions) could actually be a valid symbol name, which could be

>>> valid to use here even if it's not part of any expression in case it's

>>> an absolute one (e.g. an equate).

>>

>> Well, then I don't see other ways of special casing something that also is

>> a valid expression right now, like your string suggestion.  The problem of

>> course being that this would reinterpret currently valid (though

>> strange) directives into something else.  Right now

>>   .skip 10, "foo"

>> is equivalent to

>>   .skip 10, foo

>> and results in 10 one-byte relocs against symbol 'foo'.  We'd reinterpret

>> this, which may be fine, but needs to be a conscious decision.

>

> Oh, right, symbol names may be quoted now.

>

>>> The leading @ is undesirable anyway because of ARM's use of it as a

>>> comment char.

>>

>> Sure, other prefix chars could be used, e.g. one of the binary operators

>> in expressions.  But in light of the above it might not be such a bright

>> idea to extend .skip after all.

>

> Well, if we can fine a universally usable escape character, things

> ought to be fine this way. Apart from binary operator chars which

> aren't also unary operator ones, \ would come to mind.


I think we should simply go with

.nop SIZE


-- 
H.J.
H.J. Lu Feb. 15, 2018, 9:21 p.m. | #13
On Thu, Feb 15, 2018 at 9:11 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Thu, Feb 15, 2018 at 8:08 AM, Jan Beulich <JBeulich@suse.com> wrote:

>>>>> On 15.02.18 at 16:50, <matz@suse.de> wrote:

>>> Hi,

>>>

>>> On Thu, 15 Feb 2018, Jan Beulich wrote:

>>>

>>>> >> to be an expression, none of the above is really suitable.

>>>> >

>>>> > Well, that could be easily rectified.  Something along the lines of:

>>>>

>>>> I didn't make the remark because of foreseeing any issues with

>>>> coding this up, but because @NOP (or any of your other

>>>> suggestions) could actually be a valid symbol name, which could be

>>>> valid to use here even if it's not part of any expression in case it's

>>>> an absolute one (e.g. an equate).

>>>

>>> Well, then I don't see other ways of special casing something that also is

>>> a valid expression right now, like your string suggestion.  The problem of

>>> course being that this would reinterpret currently valid (though

>>> strange) directives into something else.  Right now

>>>   .skip 10, "foo"

>>> is equivalent to

>>>   .skip 10, foo

>>> and results in 10 one-byte relocs against symbol 'foo'.  We'd reinterpret

>>> this, which may be fine, but needs to be a conscious decision.

>>

>> Oh, right, symbol names may be quoted now.

>>

>>>> The leading @ is undesirable anyway because of ARM's use of it as a

>>>> comment char.

>>>

>>> Sure, other prefix chars could be used, e.g. one of the binary operators

>>> in expressions.  But in light of the above it might not be such a bright

>>> idea to extend .skip after all.

>>

>> Well, if we can fine a universally usable escape character, things

>> ought to be fine this way. Apart from binary operator chars which

>> aren't also unary operator ones, \ would come to mind.

>

> I think we should simply go with

>

> .nop SIZE

>


Here is a patch for

'.nop SIZE[, CONTROL]'

OK for master?

Andrew, please try users/hjl/nop/master branch at

https://github.com/hjl-tools/binutils-gdb

There is no usage change.

-- 
H.J.
From 8c3b68bc7c5531e35a14f3e4ec46e430f55815f5 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] Add .nop assembler directive

Implement the '.nop SIZE[, CONTROL]' assembler directive.  This directive
emits SIZE bytes filled with 'NOP' instructions.  SIZE is absolute
expression.  The optional CONTROL how 'NOP' instructions should be
generated.  If the comma and @var{control} are omitted, CONTROL is
assumed to be zero.

For Intel 80386 and AMD x86-64 targets, CONTROL specifies the
size limit of a single 'NOP' instruction.  The valid values of CONTROL
are between 0 and 8 for 16-bit mode, between 0 and 10 for 32-bit mode,
between 0 and 11 for 64-bit mode.  When 0 is used, the 'NOP' size limit
is set to the maximum supported size.

This is implemented by adding 2 relax states, rs_space_nop and
rs_fill_nop , to enum _relax_state, which is similar to rs_space and
rs_fill, but they fill with NOPs, instead of a single byte.  The default
NOP fill is zero.  A target backend should override md_generate_nops
to generate proper NOPs.

gas/

	* as.h (_relax_state): Add rs_space_nop and rs_fill_nop.
	* read.c (potable): Add .nop.
	(s_nop): New function.
	* read.h (s_nop): New prototype.
	* write.c (cvt_frag_to_fill): Handle rs_space_nop and
	rs_fill_nop.
	(md_generate_nops): New function.
	(relax_segment): Likewise.
	(write_contents): Use md_generate_nops for rs_fill_nop.
	* config/tc-i386.c (alt64_11): New.
	(alt64_patt): Likewise.
	(md_convert_frag): Handle rs_space_nop.
	(i386_output_nops): New function.
	(i386_generate_nops): Likewise.
	(i386_align_code): Call i386_output_nops.
	* config/tc-i386.h (i386_generate_nops): New.
	(md_generate_nops): Likewise.
	* doc/as.texinfo: Document .nop directive.
	* 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.
---
 gas/as.h                              |   9 ++
 gas/config/tc-i386.c                  | 272 +++++++++++++++++++++-------------
 gas/config/tc-i386.h                  |   5 +
 gas/doc/as.texinfo                    |  18 +++
 gas/read.c                            |  52 +++++++
 gas/read.h                            |   1 +
 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    |   4 +
 gas/testsuite/gas/i386/nop-bad-1.s    |   4 +
 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                           |  57 ++++++-
 28 files changed, 762 insertions(+), 111 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/gas/as.h b/gas/as.h
index c33353a9c5..e9b1947f33 100644
--- a/gas/as.h
+++ b/gas/as.h
@@ -279,6 +279,15 @@ 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.  */
+  rs_space_nop,
+
+  /* Similar to rs_fill.  It is used to implement .nop directive .  */
+  rs_fill_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..c67ea1f224 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -1146,105 +1146,174 @@ 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
+};
+
+/* Genenerate 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);
+}
+
+
+/* Genenerate COUNT bytes of NOPs to WHERE with the maximum size of a
+   single NOP instruction LIMIT.  */
+
+void
+i386_generate_nops (fragS *f, char *where, offsetT count, int limit)
+{
+  /* Output NOPs for .nop directive.  */
+  int max_single_nop_size;
+  const unsigned char *const *patt;
+
+  if (flag_code == CODE_16BIT)
+    {
+      patt = f16_patt;
+      max_single_nop_size = sizeof (f16_patt) / sizeof (f16_patt[0]);
+    }
+  else if (flag_code == CODE_64BIT)
+    {
+      patt = alt64_patt;
+      max_single_nop_size = sizeof (alt64_patt) / sizeof (alt64_patt[0]);
+    }
+  else
+    {
+      patt = alt_patt;
+      max_single_nop_size = sizeof (alt_patt) / sizeof (alt_patt[0]);
+    }
+  if (limit == 0)
+    limit = max_single_nop_size;
+  else if (limit > max_single_nop_size)
+    {
+      as_bad_where (f->fr_file, f->fr_line,
+		    _("invalide single nop size: %d (expect within [0, %d])"),
+		    limit, max_single_nop_size);
+      return;
+    }
+
+  i386_output_nops (where, patt, count, limit);
+}
+
+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 +1466,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;
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
index 6e4f440c09..1250bc25f5 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -281,6 +281,11 @@ extern void sco_id (void);
 
 #define WORKING_DOT_WORD 1
 
+/* How to generate NOPs for .nop direct directive.  */
+extern void i386_generate_nops (fragS *, char *, offsetT, int);
+#define md_generate_nops(frag, where, amount, control) \
+  i386_generate_nops ((frag), (where), (amount), (control))
+
 /* 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..96738808ea 100644
--- a/gas/doc/as.texinfo
+++ b/gas/doc/as.texinfo
@@ -4523,6 +4523,7 @@ Some machine configurations provide additional directives.
 * Sleb128::			@code{.sleb128 @var{expressions}}
 @ifclear no-space-dir
 * Space::                       @code{.space @var{size} , @var{fill}}
+* Nop::                         @code{.nop @var{size}[, @var{limit}]}
 @end ifclear
 @ifset have-stabs
 * Stab::                        @code{.stabd, .stabn, .stabs}
@@ -6851,6 +6852,23 @@ Assembly Language Reference Manual} (HP 92432-90001) for the meaning of the
 for a summary.
 @end quotation
 @end ifset
+
+@node Nop
+@section @code{.nop @var{size}[, @var{control}]}
+
+@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
+a positve value.  @var{control} controls how @code{NOP} should be
+generated.  If the comma and @var{control} are omitted, @var{control}
+is assumed to be zero.
+
+Note: For Intel 80386 and AMD x86-64 targets, @var{control} specifies
+the size limit of a single @code{NOP} instruction.  The valid values
+of @var{control} are between 0 and 8 for 16-bit mode, between 0 and
+10 for 32-bit mode, between 0 and 11 for 64-bit mode.  When 0 is used,
+the @code{NOP} size limit is set to the maximum supported size.
 @end ifclear
 
 @ifset have-stabs
diff --git a/gas/read.c b/gas/read.c
index 7bf52f1121..23bd9aefef 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -442,6 +442,7 @@ static const pseudo_typeS potable[] = {
 /* size  */
   {"space", s_space, 0},
   {"skip", s_space, 0},
+  {"nop", s_nop, 0},
   {"sleb128", s_leb128, 1},
   {"spc", s_ignore, 0},
   {"stabd", s_stab, 'd'},
@@ -3508,6 +3509,57 @@ s_space (int mult)
     mri_comment_end (stop, stopc);
 }
 
+void
+s_nop (int ignore ATTRIBUTE_UNUSED)
+{
+  expressionS exp;
+  expressionS val;
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+#ifdef md_cons_align
+  md_cons_align (1);
+#endif
+
+  expression (&exp);
+
+  if (*input_line_pointer == ',')
+    {
+      ++input_line_pointer;
+      expression (&val);
+    }
+  else
+    {
+      val.X_op = O_constant;
+      val.X_add_number = 0;
+    }
+
+  if (val.X_op == O_constant)
+    {
+      if (val.X_add_number < 0)
+	{
+	  as_warn (_("negative nop control byte, ignored"));
+	  val.X_add_number = 0;
+	}
+
+      if (!need_pass_2)
+	{
+	  /* Store the NOP control byte in the first byte of frag.  */
+	  char *p;
+	  symbolS *sym = make_expr_symbol (&exp);
+	  p = frag_var (rs_space_nop, 1, 1, (relax_substateT) 0,
+			sym, (offsetT) 0, (char *) 0);
+	  *p = val.X_add_number;
+	}
+    }
+  else
+    as_bad (_("unsupported variable nop control in .nop directive"));
+
+  demand_empty_rest_of_line ();
+}
+
 /* This is like s_space, but the value is a floating point number with
    the given precision.  This is for the MRI dcb.s pseudo-op and
    friends.  */
diff --git a/gas/read.h b/gas/read.h
index eebdc4e745..3f0927bc9e 100644
--- a/gas/read.h
+++ b/gas/read.h
@@ -206,6 +206,7 @@ extern void s_purgem (int);
 extern void s_rept (int);
 extern void s_set (int);
 extern void s_space (int mult);
+extern void s_nop (int);
 extern void s_stab (int what);
 extern void s_struct (int);
 extern void s_text (int);
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..34be496b4b
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-bad-1.l
@@ -0,0 +1,4 @@
+.*: Assembler messages:
+.*:2: Warning: negative nop control byte, ignored
+.*:4: Warning: \.space, \.nop or \.fill with negative value, ignored
+.*:3: Error: invalide single nop size: 20 \(expect within \[0, [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..53cc7d554a
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-bad-1.s
@@ -0,0 +1,4 @@
+	.text
+        .nop 100, -2
+        .nop 100, 20
+        .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..3e4a9d4e38 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -435,6 +435,8 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
 {
   switch (fragP->fr_type)
     {
+    case rs_space_nop:
+      goto skip_align;
     case rs_align:
     case rs_align_code:
     case rs_align_test:
@@ -443,6 +445,7 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
 #ifdef HANDLE_ALIGN
       HANDLE_ALIGN (fragP);
 #endif
+skip_align:
       know (fragP->fr_next != NULL);
       fragP->fr_offset = (fragP->fr_next->fr_address
 			  - fragP->fr_address
@@ -450,14 +453,18 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
       if (fragP->fr_offset < 0)
 	{
 	  as_bad_where (fragP->fr_file, fragP->fr_line,
-			_("attempt to .org/.space backwards? (%ld)"),
+			_("attempt to .org/.space/,nop backwards? (%ld)"),
 			(long) fragP->fr_offset);
 	  fragP->fr_offset = 0;
 	}
-      fragP->fr_type = rs_fill;
+      if (fragP->fr_type == rs_space_nop)
+	fragP->fr_type = rs_fill_nop;
+      else
+	fragP->fr_type = rs_fill;
       break;
 
     case rs_fill:
+    case rs_fill_nop:
       break;
 
     case rs_leb128:
@@ -1570,6 +1577,18 @@ compress_debug (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
     }
 }
 
+#ifndef md_generate_nops
+/* Genenerate COUNT bytes of NOPs to WHERE.  Default NOP fill is zero.
+   A target backend should override this with proper NOPs.   */
+
+static void
+md_generate_nops (fragS *f ATTRIBUTE_UNUSED, char *where,
+		  offsetT count, int control ATTRIBUTE_UNUSED)
+{
+  memset (where, 0, count);
+}
+#endif
+
 static void
 write_contents (bfd *abfd ATTRIBUTE_UNUSED,
 		asection *sec,
@@ -1593,7 +1612,7 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
       char *fill_literal;
       offsetT count;
 
-      gas_assert (f->fr_type == rs_fill);
+      gas_assert (f->fr_type == rs_fill || f->fr_type == rs_fill_nop);
       if (f->fr_fix)
 	{
 	  x = bfd_set_section_contents (stdoutput, sec,
@@ -1610,9 +1629,35 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
 		      bfd_errmsg (bfd_get_error ()));
 	  offset += f->fr_fix;
 	}
-      fill_literal = f->fr_literal + f->fr_fix;
+
       fill_size = f->fr_var;
       count = f->fr_offset;
+      fill_literal = f->fr_literal + f->fr_fix;
+
+      if (f->fr_type == rs_fill_nop)
+	{
+	  gas_assert (count >= 0 && fill_size == 1);
+	  if (count > 0)
+	    {
+	      char *buf = xmalloc (count);
+	      md_generate_nops (f, buf, count, *fill_literal);
+	      x = bfd_set_section_contents
+		(stdoutput, sec, buf, (file_ptr) offset,
+		 (bfd_size_type) count);
+	      if (!x)
+		as_fatal (ngettext ("can't fill %ld byte "
+				    "in section %s of %s: '%s'",
+				    "can't fill %ld bytes "
+				    "in section %s of %s: '%s'",
+				    (long) count), (long) count,
+				    sec->name, stdoutput->filename,
+				    bfd_errmsg (bfd_get_error ()));
+	      offset += count;
+	      free (buf);
+	    }
+	  continue;
+	}
+
       gas_assert (count >= 0);
       if (fill_size && count)
 	{
@@ -2461,6 +2506,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 	  break;
 
 	case rs_space:
+	case rs_space_nop:
 	  break;
 
 	case rs_machine_dependent:
@@ -2765,6 +2811,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 		break;
 
 	      case rs_space:
+	      case rs_space_nop:
 		growth = 0;
 		if (symbolP)
 		  {
@@ -2791,7 +2838,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 			  }
 
 			as_warn_where (fragP->fr_file, fragP->fr_line,
-				       _(".space or .fill with negative value, ignored"));
+				       _(".space, .nop or .fill with negative value, ignored"));
 			fragP->fr_symbol = 0;
 		      }
 		    else
Michael Matz Feb. 16, 2018, 1:58 p.m. | #14
Hi,

On Thu, 15 Feb 2018, H.J. Lu wrote:

> OK for master?


@@ -4523,6 +4523,7 @@ Some machine configurations provide additional directives.
 * Sleb128::                    @code{.sleb128 @var{expressions}}
 @ifclear no-space-dir
 * Space::                       @code{.space @var{size} , @var{fill}}
+* Nop::                         @code{.nop @var{size}[, @var{limit}]}
 @end ifclear
 @ifset have-stabs
 * Stab::                        @code{.stabd, .stabn, .stabs}

You probably don't want to have the Nop entry conditional on no-space-dir. 
The index is also supposed to be sorted alphabetical.  And the description 
of above item still says "limit" instead of "control".

@@ -450,14 +453,18 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
       if (fragP->fr_offset < 0)
        {
          as_bad_where (fragP->fr_file, fragP->fr_line,
-                       _("attempt to .org/.space backwards? (%ld)"),
+                       _("attempt to .org/.space/,nop backwards? (%ld)"),

".nop", not ",nop".


Ciao,
Michael.
H.J. Lu Feb. 16, 2018, 2:34 p.m. | #15
On Fri, Feb 16, 2018 at 5:58 AM, Michael Matz <matz@suse.de> wrote:
> Hi,

>

> On Thu, 15 Feb 2018, H.J. Lu wrote:

>

>> OK for master?

>

> @@ -4523,6 +4523,7 @@ Some machine configurations provide additional directives.

>  * Sleb128::                    @code{.sleb128 @var{expressions}}

>  @ifclear no-space-dir

>  * Space::                       @code{.space @var{size} , @var{fill}}

> +* Nop::                         @code{.nop @var{size}[, @var{limit}]}

>  @end ifclear

>  @ifset have-stabs

>  * Stab::                        @code{.stabd, .stabn, .stabs}

>

> You probably don't want to have the Nop entry conditional on no-space-dir.

> The index is also supposed to be sorted alphabetical.  And the description

> of above item still says "limit" instead of "control".

>

> @@ -450,14 +453,18 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)

>        if (fragP->fr_offset < 0)

>         {

>           as_bad_where (fragP->fr_file, fragP->fr_line,

> -                       _("attempt to .org/.space backwards? (%ld)"),

> +                       _("attempt to .org/.space/,nop backwards? (%ld)"),

>

> ".nop", not ",nop".

>


All fixed.  Here is the updated patch.  OK for master?


-- 
H.J.
From 944d2486925312bc4f9d062018bc37c3f3f66dc3 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] Add .nop assembler directive

Implement the '.nop SIZE[, CONTROL]' assembler directive.  This directive
emits SIZE bytes filled with 'NOP' instructions.  SIZE is absolute
expression.  The optional CONTROL how 'NOP' instructions should be
generated.  If the comma and @var{control} are omitted, CONTROL is
assumed to be zero.

For Intel 80386 and AMD x86-64 targets, CONTROL specifies the
size limit of a single 'NOP' instruction.  The valid values of CONTROL
are between 0 and 8 for 16-bit mode, between 0 and 10 for 32-bit mode,
between 0 and 11 for 64-bit mode.  When 0 is used, the 'NOP' size limit
is set to the maximum supported size.

This is implemented by adding 2 relax states, rs_space_nop and
rs_fill_nop , to enum _relax_state, which is similar to rs_space and
rs_fill, but they fill with NOPs, instead of a single byte.  The default
NOP fill is zero.  A target backend should override md_generate_nops
to generate proper NOPs.

gas/

	* as.h (_relax_state): Add rs_space_nop and rs_fill_nop.
	* read.c (potable): Add .nop.
	(s_nop): New function.
	* read.h (s_nop): New prototype.
	* write.c (cvt_frag_to_fill): Handle rs_space_nop and
	rs_fill_nop.
	(md_generate_nops): New function.
	(relax_segment): Likewise.
	(write_contents): Use md_generate_nops for rs_fill_nop.
	* config/tc-i386.c (alt64_11): New.
	(alt64_patt): Likewise.
	(md_convert_frag): Handle rs_space_nop.
	(i386_output_nops): New function.
	(i386_generate_nops): Likewise.
	(i386_align_code): Call i386_output_nops.
	* config/tc-i386.h (i386_generate_nops): New.
	(md_generate_nops): Likewise.
	* doc/as.texinfo: Document .nop directive.
	* 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.
---
 gas/as.h                              |   9 ++
 gas/config/tc-i386.c                  | 272 +++++++++++++++++++++-------------
 gas/config/tc-i386.h                  |   5 +
 gas/doc/as.texinfo                    |  18 +++
 gas/read.c                            |  52 +++++++
 gas/read.h                            |   1 +
 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    |   4 +
 gas/testsuite/gas/i386/nop-bad-1.s    |   4 +
 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                           |  57 ++++++-
 28 files changed, 762 insertions(+), 111 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/gas/as.h b/gas/as.h
index c33353a9c5..e9b1947f33 100644
--- a/gas/as.h
+++ b/gas/as.h
@@ -279,6 +279,15 @@ 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.  */
+  rs_space_nop,
+
+  /* Similar to rs_fill.  It is used to implement .nop directive .  */
+  rs_fill_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..c67ea1f224 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -1146,105 +1146,174 @@ 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
+};
+
+/* Genenerate 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);
+}
+
+
+/* Genenerate COUNT bytes of NOPs to WHERE with the maximum size of a
+   single NOP instruction LIMIT.  */
+
+void
+i386_generate_nops (fragS *f, char *where, offsetT count, int limit)
+{
+  /* Output NOPs for .nop directive.  */
+  int max_single_nop_size;
+  const unsigned char *const *patt;
+
+  if (flag_code == CODE_16BIT)
+    {
+      patt = f16_patt;
+      max_single_nop_size = sizeof (f16_patt) / sizeof (f16_patt[0]);
+    }
+  else if (flag_code == CODE_64BIT)
+    {
+      patt = alt64_patt;
+      max_single_nop_size = sizeof (alt64_patt) / sizeof (alt64_patt[0]);
+    }
+  else
+    {
+      patt = alt_patt;
+      max_single_nop_size = sizeof (alt_patt) / sizeof (alt_patt[0]);
+    }
+  if (limit == 0)
+    limit = max_single_nop_size;
+  else if (limit > max_single_nop_size)
+    {
+      as_bad_where (f->fr_file, f->fr_line,
+		    _("invalide single nop size: %d (expect within [0, %d])"),
+		    limit, max_single_nop_size);
+      return;
+    }
+
+  i386_output_nops (where, patt, count, limit);
+}
+
+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 +1466,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;
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
index 6e4f440c09..1250bc25f5 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -281,6 +281,11 @@ extern void sco_id (void);
 
 #define WORKING_DOT_WORD 1
 
+/* How to generate NOPs for .nop direct directive.  */
+extern void i386_generate_nops (fragS *, char *, offsetT, int);
+#define md_generate_nops(frag, where, amount, control) \
+  i386_generate_nops ((frag), (where), (amount), (control))
+
 /* 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..a96c04aea8 100644
--- a/gas/doc/as.texinfo
+++ b/gas/doc/as.texinfo
@@ -4479,6 +4479,7 @@ Some machine configurations provide additional directives.
 * MRI::				@code{.mri @var{val}}
 * Noaltmacro::                  @code{.noaltmacro}
 * Nolist::                      @code{.nolist}
+* Nop::                         @code{.nop @var{size}[, @var{control}]}
 * Octa::                        @code{.octa @var{bignums}}
 * Offset::			@code{.offset @var{loc}}
 * Org::                         @code{.org @var{new-lc}, @var{fill}}
@@ -6101,6 +6102,23 @@ internal counter (which is zero initially).   @code{.list} increments the
 counter, and @code{.nolist} decrements it.  Assembly listings are
 generated whenever the counter is greater than zero.
 
+@node Nop
+@section @code{.nop @var{size}[, @var{control}]}
+
+@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
+a positve value.  @var{control} controls how @code{NOP} should be
+generated.  If the comma and @var{control} are omitted, @var{control}
+is assumed to be zero.
+
+Note: For Intel 80386 and AMD x86-64 targets, @var{control} specifies
+the size limit of a single @code{NOP} instruction.  The valid values
+of @var{control} are between 0 and 8 for 16-bit mode, between 0 and
+10 for 32-bit mode, between 0 and 11 for 64-bit mode.  When 0 is used,
+the @code{NOP} size limit is set to the maximum supported size.
+
 @node Octa
 @section @code{.octa @var{bignums}}
 
diff --git a/gas/read.c b/gas/read.c
index 7bf52f1121..23bd9aefef 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -442,6 +442,7 @@ static const pseudo_typeS potable[] = {
 /* size  */
   {"space", s_space, 0},
   {"skip", s_space, 0},
+  {"nop", s_nop, 0},
   {"sleb128", s_leb128, 1},
   {"spc", s_ignore, 0},
   {"stabd", s_stab, 'd'},
@@ -3508,6 +3509,57 @@ s_space (int mult)
     mri_comment_end (stop, stopc);
 }
 
+void
+s_nop (int ignore ATTRIBUTE_UNUSED)
+{
+  expressionS exp;
+  expressionS val;
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+#ifdef md_cons_align
+  md_cons_align (1);
+#endif
+
+  expression (&exp);
+
+  if (*input_line_pointer == ',')
+    {
+      ++input_line_pointer;
+      expression (&val);
+    }
+  else
+    {
+      val.X_op = O_constant;
+      val.X_add_number = 0;
+    }
+
+  if (val.X_op == O_constant)
+    {
+      if (val.X_add_number < 0)
+	{
+	  as_warn (_("negative nop control byte, ignored"));
+	  val.X_add_number = 0;
+	}
+
+      if (!need_pass_2)
+	{
+	  /* Store the NOP control byte in the first byte of frag.  */
+	  char *p;
+	  symbolS *sym = make_expr_symbol (&exp);
+	  p = frag_var (rs_space_nop, 1, 1, (relax_substateT) 0,
+			sym, (offsetT) 0, (char *) 0);
+	  *p = val.X_add_number;
+	}
+    }
+  else
+    as_bad (_("unsupported variable nop control in .nop directive"));
+
+  demand_empty_rest_of_line ();
+}
+
 /* This is like s_space, but the value is a floating point number with
    the given precision.  This is for the MRI dcb.s pseudo-op and
    friends.  */
diff --git a/gas/read.h b/gas/read.h
index eebdc4e745..3f0927bc9e 100644
--- a/gas/read.h
+++ b/gas/read.h
@@ -206,6 +206,7 @@ extern void s_purgem (int);
 extern void s_rept (int);
 extern void s_set (int);
 extern void s_space (int mult);
+extern void s_nop (int);
 extern void s_stab (int what);
 extern void s_struct (int);
 extern void s_text (int);
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..34be496b4b
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-bad-1.l
@@ -0,0 +1,4 @@
+.*: Assembler messages:
+.*:2: Warning: negative nop control byte, ignored
+.*:4: Warning: \.space, \.nop or \.fill with negative value, ignored
+.*:3: Error: invalide single nop size: 20 \(expect within \[0, [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..53cc7d554a
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-bad-1.s
@@ -0,0 +1,4 @@
+	.text
+        .nop 100, -2
+        .nop 100, 20
+        .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..f6db96ea66 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -435,6 +435,8 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
 {
   switch (fragP->fr_type)
     {
+    case rs_space_nop:
+      goto skip_align;
     case rs_align:
     case rs_align_code:
     case rs_align_test:
@@ -443,6 +445,7 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
 #ifdef HANDLE_ALIGN
       HANDLE_ALIGN (fragP);
 #endif
+skip_align:
       know (fragP->fr_next != NULL);
       fragP->fr_offset = (fragP->fr_next->fr_address
 			  - fragP->fr_address
@@ -450,14 +453,18 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
       if (fragP->fr_offset < 0)
 	{
 	  as_bad_where (fragP->fr_file, fragP->fr_line,
-			_("attempt to .org/.space backwards? (%ld)"),
+			_("attempt to .org/.space/.nop backwards? (%ld)"),
 			(long) fragP->fr_offset);
 	  fragP->fr_offset = 0;
 	}
-      fragP->fr_type = rs_fill;
+      if (fragP->fr_type == rs_space_nop)
+	fragP->fr_type = rs_fill_nop;
+      else
+	fragP->fr_type = rs_fill;
       break;
 
     case rs_fill:
+    case rs_fill_nop:
       break;
 
     case rs_leb128:
@@ -1570,6 +1577,18 @@ compress_debug (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
     }
 }
 
+#ifndef md_generate_nops
+/* Genenerate COUNT bytes of NOPs to WHERE.  Default NOP fill is zero.
+   A target backend should override this with proper NOPs.   */
+
+static void
+md_generate_nops (fragS *f ATTRIBUTE_UNUSED, char *where,
+		  offsetT count, int control ATTRIBUTE_UNUSED)
+{
+  memset (where, 0, count);
+}
+#endif
+
 static void
 write_contents (bfd *abfd ATTRIBUTE_UNUSED,
 		asection *sec,
@@ -1593,7 +1612,7 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
       char *fill_literal;
       offsetT count;
 
-      gas_assert (f->fr_type == rs_fill);
+      gas_assert (f->fr_type == rs_fill || f->fr_type == rs_fill_nop);
       if (f->fr_fix)
 	{
 	  x = bfd_set_section_contents (stdoutput, sec,
@@ -1610,9 +1629,35 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
 		      bfd_errmsg (bfd_get_error ()));
 	  offset += f->fr_fix;
 	}
-      fill_literal = f->fr_literal + f->fr_fix;
+
       fill_size = f->fr_var;
       count = f->fr_offset;
+      fill_literal = f->fr_literal + f->fr_fix;
+
+      if (f->fr_type == rs_fill_nop)
+	{
+	  gas_assert (count >= 0 && fill_size == 1);
+	  if (count > 0)
+	    {
+	      char *buf = xmalloc (count);
+	      md_generate_nops (f, buf, count, *fill_literal);
+	      x = bfd_set_section_contents
+		(stdoutput, sec, buf, (file_ptr) offset,
+		 (bfd_size_type) count);
+	      if (!x)
+		as_fatal (ngettext ("can't fill %ld byte "
+				    "in section %s of %s: '%s'",
+				    "can't fill %ld bytes "
+				    "in section %s of %s: '%s'",
+				    (long) count), (long) count,
+				    sec->name, stdoutput->filename,
+				    bfd_errmsg (bfd_get_error ()));
+	      offset += count;
+	      free (buf);
+	    }
+	  continue;
+	}
+
       gas_assert (count >= 0);
       if (fill_size && count)
 	{
@@ -2461,6 +2506,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 	  break;
 
 	case rs_space:
+	case rs_space_nop:
 	  break;
 
 	case rs_machine_dependent:
@@ -2765,6 +2811,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 		break;
 
 	      case rs_space:
+	      case rs_space_nop:
 		growth = 0;
 		if (symbolP)
 		  {
@@ -2791,7 +2838,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 			  }
 
 			as_warn_where (fragP->fr_file, fragP->fr_line,
-				       _(".space or .fill with negative value, ignored"));
+				       _(".space, .nop or .fill with negative value, ignored"));
 			fragP->fr_symbol = 0;
 		      }
 		    else
Maciej W. Rozycki Feb. 16, 2018, 3:41 p.m. | #16
On Fri, 16 Feb 2018, H.J. Lu wrote:

> All fixed.  Here is the updated patch.  OK for master?


 I skimmed over briefly only.  I think the convention for the manual is to 
use the generic term `no-op instruction' rather than `@code{NOP}'.  

 Firstly, not all targets we support have an actual NOP mnemonic.  And 
even with x86 the encodings you choose are not true architectural NOP 
instructions, but just machine code encodings which happen to execute 
quickly and with no visible effects (and e.g. for MIPS `addiu $0, $3, -1' 
yields no observable effect, however nobody calls that `nop'; there are 
millions of such MIPS machine code encodings).

  Maciej
H.J. Lu Feb. 16, 2018, 4:07 p.m. | #17
On Fri, Feb 16, 2018 at 7:41 AM, Maciej W. Rozycki <macro@mips.com> wrote:
> On Fri, 16 Feb 2018, H.J. Lu wrote:

>

>> All fixed.  Here is the updated patch.  OK for master?

>

>  I skimmed over briefly only.  I think the convention for the manual is to

> use the generic term `no-op instruction' rather than `@code{NOP}'.

>

>  Firstly, not all targets we support have an actual NOP mnemonic.  And

> even with x86 the encodings you choose are not true architectural NOP

> instructions, but just machine code encodings which happen to execute

> quickly and with no visible effects (and e.g. for MIPS `addiu $0, $3, -1'

> yields no observable effect, however nobody calls that `nop'; there are

> millions of such MIPS machine code encodings).


Good points.  Here is the updated patch.   OK for master?

Thanks.

-- 
H.J.
From 586723cdbb374ceeaf5553d665ea60d8ae6b758f 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] Add .nop assembler directive

Implement the '.nop SIZE[, CONTROL]' assembler directive.  This directive
emits SIZE bytes filled with no-op instructions.  SIZE is absolute
expression.  The optional CONTROL how no-op instructions should be
generated.  If the comma and @var{control} are omitted, CONTROL is
assumed to be zero.

For Intel 80386 and AMD x86-64 targets, CONTROL specifies the size limit
of a single no-op instruction.  The valid values of CONTROL are between
0 and 8 for 16-bit mode, between 0 and 10 for 32-bit mode, between 0 and
11 for 64-bit mode.  When 0 is used, the no-op size limit is set to the
maximum supported size.

2 new relax states, rs_space_nop and rs_fill_nop, are added to enum
_relax_state, which are similar to rs_space and rs_fill, respectively,
but they fill with no-op instructions, instead of a single byte.  The
default no-op instruction fill is zero.  A target backend should override
md_generate_nops to generate proper no-op instructions.

gas/

	* as.h (_relax_state): Add rs_space_nop and rs_fill_nop.
	* read.c (potable): Add .nop.
	(s_nop): New function.
	* read.h (s_nop): New prototype.
	* write.c (cvt_frag_to_fill): Handle rs_space_nop and
	rs_fill_nop.
	(md_generate_nops): New function.
	(relax_segment): Likewise.
	(write_contents): Use md_generate_nops for rs_fill_nop.
	* config/tc-i386.c (alt64_11): New.
	(alt64_patt): Likewise.
	(md_convert_frag): Handle rs_space_nop.
	(i386_output_nops): New function.
	(i386_generate_nops): Likewise.
	(i386_align_code): Call i386_output_nops.
	* config/tc-i386.h (i386_generate_nops): New.
	(md_generate_nops): Likewise.
	* doc/as.texinfo: Document .nop directive.
	* 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.
---
 gas/as.h                              |   9 ++
 gas/config/tc-i386.c                  | 272 +++++++++++++++++++++-------------
 gas/config/tc-i386.h                  |   5 +
 gas/doc/as.texinfo                    |  18 +++
 gas/read.c                            |  52 +++++++
 gas/read.h                            |   1 +
 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    |   4 +
 gas/testsuite/gas/i386/nop-bad-1.s    |   4 +
 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                           |  57 ++++++-
 28 files changed, 762 insertions(+), 111 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/gas/as.h b/gas/as.h
index c33353a9c5..e9b1947f33 100644
--- a/gas/as.h
+++ b/gas/as.h
@@ -279,6 +279,15 @@ 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.  */
+  rs_space_nop,
+
+  /* Similar to rs_fill.  It is used to implement .nop directive .  */
+  rs_fill_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..c67ea1f224 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -1146,105 +1146,174 @@ 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
+};
+
+/* Genenerate 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);
+}
+
+
+/* Genenerate COUNT bytes of NOPs to WHERE with the maximum size of a
+   single NOP instruction LIMIT.  */
+
+void
+i386_generate_nops (fragS *f, char *where, offsetT count, int limit)
+{
+  /* Output NOPs for .nop directive.  */
+  int max_single_nop_size;
+  const unsigned char *const *patt;
+
+  if (flag_code == CODE_16BIT)
+    {
+      patt = f16_patt;
+      max_single_nop_size = sizeof (f16_patt) / sizeof (f16_patt[0]);
+    }
+  else if (flag_code == CODE_64BIT)
+    {
+      patt = alt64_patt;
+      max_single_nop_size = sizeof (alt64_patt) / sizeof (alt64_patt[0]);
+    }
+  else
+    {
+      patt = alt_patt;
+      max_single_nop_size = sizeof (alt_patt) / sizeof (alt_patt[0]);
+    }
+  if (limit == 0)
+    limit = max_single_nop_size;
+  else if (limit > max_single_nop_size)
+    {
+      as_bad_where (f->fr_file, f->fr_line,
+		    _("invalide single nop size: %d (expect within [0, %d])"),
+		    limit, max_single_nop_size);
+      return;
+    }
+
+  i386_output_nops (where, patt, count, limit);
+}
+
+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 +1466,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;
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
index 6e4f440c09..1250bc25f5 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -281,6 +281,11 @@ extern void sco_id (void);
 
 #define WORKING_DOT_WORD 1
 
+/* How to generate NOPs for .nop direct directive.  */
+extern void i386_generate_nops (fragS *, char *, offsetT, int);
+#define md_generate_nops(frag, where, amount, control) \
+  i386_generate_nops ((frag), (where), (amount), (control))
+
 /* 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..3084bcb871 100644
--- a/gas/doc/as.texinfo
+++ b/gas/doc/as.texinfo
@@ -4479,6 +4479,7 @@ Some machine configurations provide additional directives.
 * MRI::				@code{.mri @var{val}}
 * Noaltmacro::                  @code{.noaltmacro}
 * Nolist::                      @code{.nolist}
+* Nop::                         @code{.nop @var{size}[, @var{control}]}
 * Octa::                        @code{.octa @var{bignums}}
 * Offset::			@code{.offset @var{loc}}
 * Org::                         @code{.org @var{new-lc}, @var{fill}}
@@ -6101,6 +6102,23 @@ internal counter (which is zero initially).   @code{.list} increments the
 counter, and @code{.nolist} decrements it.  Assembly listings are
 generated whenever the counter is greater than zero.
 
+@node Nop
+@section @code{.nop @var{size}[, @var{control}]}
+
+@cindex @code{nop} directive
+@cindex filling memory with no-op instructions
+This directive emits @var{size} bytes filled with no-op instructions.
+@var{size} is absolute expression, which must be a positve value.
+@var{control} controls how no-op instructions should be generated.  If
+the comma and @var{control} are omitted, @var{control} is assumed to be
+zero.
+
+Note: For Intel 80386 and AMD x86-64 targets, @var{control} specifies
+the size limit of a no-op instruction.  The valid values of @var{control}
+are between 0 and 8 for 16-bit mode, between 0 and 10 for 32-bit mode,
+between 0 and 11 for 64-bit mode.  When 0 is used, the no-op instruction
+size limit is set to the maximum supported size.
+
 @node Octa
 @section @code{.octa @var{bignums}}
 
diff --git a/gas/read.c b/gas/read.c
index 7bf52f1121..23bd9aefef 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -442,6 +442,7 @@ static const pseudo_typeS potable[] = {
 /* size  */
   {"space", s_space, 0},
   {"skip", s_space, 0},
+  {"nop", s_nop, 0},
   {"sleb128", s_leb128, 1},
   {"spc", s_ignore, 0},
   {"stabd", s_stab, 'd'},
@@ -3508,6 +3509,57 @@ s_space (int mult)
     mri_comment_end (stop, stopc);
 }
 
+void
+s_nop (int ignore ATTRIBUTE_UNUSED)
+{
+  expressionS exp;
+  expressionS val;
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+#ifdef md_cons_align
+  md_cons_align (1);
+#endif
+
+  expression (&exp);
+
+  if (*input_line_pointer == ',')
+    {
+      ++input_line_pointer;
+      expression (&val);
+    }
+  else
+    {
+      val.X_op = O_constant;
+      val.X_add_number = 0;
+    }
+
+  if (val.X_op == O_constant)
+    {
+      if (val.X_add_number < 0)
+	{
+	  as_warn (_("negative nop control byte, ignored"));
+	  val.X_add_number = 0;
+	}
+
+      if (!need_pass_2)
+	{
+	  /* Store the NOP control byte in the first byte of frag.  */
+	  char *p;
+	  symbolS *sym = make_expr_symbol (&exp);
+	  p = frag_var (rs_space_nop, 1, 1, (relax_substateT) 0,
+			sym, (offsetT) 0, (char *) 0);
+	  *p = val.X_add_number;
+	}
+    }
+  else
+    as_bad (_("unsupported variable nop control in .nop directive"));
+
+  demand_empty_rest_of_line ();
+}
+
 /* This is like s_space, but the value is a floating point number with
    the given precision.  This is for the MRI dcb.s pseudo-op and
    friends.  */
diff --git a/gas/read.h b/gas/read.h
index eebdc4e745..3f0927bc9e 100644
--- a/gas/read.h
+++ b/gas/read.h
@@ -206,6 +206,7 @@ extern void s_purgem (int);
 extern void s_rept (int);
 extern void s_set (int);
 extern void s_space (int mult);
+extern void s_nop (int);
 extern void s_stab (int what);
 extern void s_struct (int);
 extern void s_text (int);
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..34be496b4b
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-bad-1.l
@@ -0,0 +1,4 @@
+.*: Assembler messages:
+.*:2: Warning: negative nop control byte, ignored
+.*:4: Warning: \.space, \.nop or \.fill with negative value, ignored
+.*:3: Error: invalide single nop size: 20 \(expect within \[0, [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..53cc7d554a
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-bad-1.s
@@ -0,0 +1,4 @@
+	.text
+        .nop 100, -2
+        .nop 100, 20
+        .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..f6db96ea66 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -435,6 +435,8 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
 {
   switch (fragP->fr_type)
     {
+    case rs_space_nop:
+      goto skip_align;
     case rs_align:
     case rs_align_code:
     case rs_align_test:
@@ -443,6 +445,7 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
 #ifdef HANDLE_ALIGN
       HANDLE_ALIGN (fragP);
 #endif
+skip_align:
       know (fragP->fr_next != NULL);
       fragP->fr_offset = (fragP->fr_next->fr_address
 			  - fragP->fr_address
@@ -450,14 +453,18 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
       if (fragP->fr_offset < 0)
 	{
 	  as_bad_where (fragP->fr_file, fragP->fr_line,
-			_("attempt to .org/.space backwards? (%ld)"),
+			_("attempt to .org/.space/.nop backwards? (%ld)"),
 			(long) fragP->fr_offset);
 	  fragP->fr_offset = 0;
 	}
-      fragP->fr_type = rs_fill;
+      if (fragP->fr_type == rs_space_nop)
+	fragP->fr_type = rs_fill_nop;
+      else
+	fragP->fr_type = rs_fill;
       break;
 
     case rs_fill:
+    case rs_fill_nop:
       break;
 
     case rs_leb128:
@@ -1570,6 +1577,18 @@ compress_debug (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
     }
 }
 
+#ifndef md_generate_nops
+/* Genenerate COUNT bytes of NOPs to WHERE.  Default NOP fill is zero.
+   A target backend should override this with proper NOPs.   */
+
+static void
+md_generate_nops (fragS *f ATTRIBUTE_UNUSED, char *where,
+		  offsetT count, int control ATTRIBUTE_UNUSED)
+{
+  memset (where, 0, count);
+}
+#endif
+
 static void
 write_contents (bfd *abfd ATTRIBUTE_UNUSED,
 		asection *sec,
@@ -1593,7 +1612,7 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
       char *fill_literal;
       offsetT count;
 
-      gas_assert (f->fr_type == rs_fill);
+      gas_assert (f->fr_type == rs_fill || f->fr_type == rs_fill_nop);
       if (f->fr_fix)
 	{
 	  x = bfd_set_section_contents (stdoutput, sec,
@@ -1610,9 +1629,35 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
 		      bfd_errmsg (bfd_get_error ()));
 	  offset += f->fr_fix;
 	}
-      fill_literal = f->fr_literal + f->fr_fix;
+
       fill_size = f->fr_var;
       count = f->fr_offset;
+      fill_literal = f->fr_literal + f->fr_fix;
+
+      if (f->fr_type == rs_fill_nop)
+	{
+	  gas_assert (count >= 0 && fill_size == 1);
+	  if (count > 0)
+	    {
+	      char *buf = xmalloc (count);
+	      md_generate_nops (f, buf, count, *fill_literal);
+	      x = bfd_set_section_contents
+		(stdoutput, sec, buf, (file_ptr) offset,
+		 (bfd_size_type) count);
+	      if (!x)
+		as_fatal (ngettext ("can't fill %ld byte "
+				    "in section %s of %s: '%s'",
+				    "can't fill %ld bytes "
+				    "in section %s of %s: '%s'",
+				    (long) count), (long) count,
+				    sec->name, stdoutput->filename,
+				    bfd_errmsg (bfd_get_error ()));
+	      offset += count;
+	      free (buf);
+	    }
+	  continue;
+	}
+
       gas_assert (count >= 0);
       if (fill_size && count)
 	{
@@ -2461,6 +2506,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 	  break;
 
 	case rs_space:
+	case rs_space_nop:
 	  break;
 
 	case rs_machine_dependent:
@@ -2765,6 +2811,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 		break;
 
 	      case rs_space:
+	      case rs_space_nop:
 		growth = 0;
 		if (symbolP)
 		  {
@@ -2791,7 +2838,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 			  }
 
 			as_warn_where (fragP->fr_file, fragP->fr_line,
-				       _(".space or .fill with negative value, ignored"));
+				       _(".space, .nop or .fill with negative value, ignored"));
 			fragP->fr_symbol = 0;
 		      }
 		    else
Andrew Cooper Feb. 16, 2018, 6 p.m. | #18
On 16/02/18 16:07, H.J. Lu wrote:
> On Fri, Feb 16, 2018 at 7:41 AM, Maciej W. Rozycki <macro@mips.com> wrote:

>> On Fri, 16 Feb 2018, H.J. Lu wrote:

>>

>>> All fixed.  Here is the updated patch.  OK for master?

>>  I skimmed over briefly only.  I think the convention for the manual is to

>> use the generic term `no-op instruction' rather than `@code{NOP}'.

>>

>>  Firstly, not all targets we support have an actual NOP mnemonic.  And

>> even with x86 the encodings you choose are not true architectural NOP

>> instructions, but just machine code encodings which happen to execute

>> quickly and with no visible effects (and e.g. for MIPS `addiu $0, $3, -1'

>> yields no observable effect, however nobody calls that `nop'; there are

>> millions of such MIPS machine code encodings).

> Good points.  Here is the updated patch.   OK for master?


I've tested this latest patch, and everything looks ok.

~Andrew
Alan Modra Feb. 17, 2018, 12:49 a.m. | #19
On Fri, Feb 16, 2018 at 08:07:50AM -0800, H.J. Lu wrote:
> Good points.  Here is the updated patch.   OK for master?


OK, except for one quibble with the default md_generate_nops.  I think
that should error rather than filling with zeros (which is likely to
be an illegal instruction or trap).

-- 
Alan Modra
Australia Development Lab, IBM
H.J. Lu Feb. 17, 2018, 2:59 a.m. | #20
On Fri, Feb 16, 2018 at 4:49 PM, Alan Modra <amodra@gmail.com> wrote:
> On Fri, Feb 16, 2018 at 08:07:50AM -0800, H.J. Lu wrote:

>> Good points.  Here is the updated patch.   OK for master?

>

> OK, except for one quibble with the default md_generate_nops.  I think

> that should error rather than filling with zeros (which is likely to

> be an illegal instruction or trap).

>


Fixed.  This is the patch I will check in tomorrow.

Thanks.

-- 
H.J.
From 2fe9da0602ad115c02a1164b3237f12df3d5b130 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] Add .nop assembler directive

Implement the '.nop SIZE[, CONTROL]' assembler directive, which emits
SIZE bytes filled with no-op instructions.  SIZE is absolute expression.
The optional CONTROL byte controls how no-op instructions should be
generated.  If the comma and @var{control} are omitted, CONTROL is
assumed to be zero.

For Intel 80386 and AMD x86-64 targets, CONTROL byte specifies the size
limit of a single no-op instruction.  The valid values of CONTROL byte
are between 0 and 8 for 16-bit mode, between 0 and 10 for 32-bit mode,
between 0 and 11 for 64-bit mode.  When 0 is used, the no-op size limit
is set to the maximum supported size.

2 new relax states, rs_space_nop and rs_fill_nop, are added to enum
_relax_state, which are similar to rs_space and rs_fill, respectively,
but they fill with no-op instructions, instead of a single byte.  A
target backend must override the default md_generate_nops to generate
proper no-op instructions.  Otherwise, an error of unimplemented .nop
directive will be issued whenever .nop directive is used.

gas/

	* as.h (_relax_state): Add rs_space_nop and rs_fill_nop.
	* read.c (potable): Add .nop.
	(s_nop): New function.
	* read.h (s_nop): New prototype.
	* write.c (cvt_frag_to_fill): Handle rs_space_nop and
	rs_fill_nop.
	(md_generate_nops): New function.
	(relax_segment): Likewise.
	(write_contents): Use md_generate_nops for rs_fill_nop.
	* config/tc-i386.c (alt64_11): New.
	(alt64_patt): Likewise.
	(md_convert_frag): Handle rs_space_nop.
	(i386_output_nops): New function.
	(i386_generate_nops): Likewise.
	(i386_align_code): Call i386_output_nops.
	* config/tc-i386.h (i386_generate_nops): New.
	(md_generate_nops): Likewise.
	* doc/as.texinfo: Document .nop directive.
	* 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.
---
 gas/as.h                              |  10 ++
 gas/config/tc-i386.c                  | 272 +++++++++++++++++++++-------------
 gas/config/tc-i386.h                  |   5 +
 gas/doc/as.texinfo                    |  18 +++
 gas/read.c                            |  53 +++++++
 gas/read.h                            |   1 +
 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    |   4 +
 gas/testsuite/gas/i386/nop-bad-1.s    |   4 +
 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                           |  59 +++++++-
 28 files changed, 766 insertions(+), 111 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/gas/as.h b/gas/as.h
index c33353a9c5..d75ff42523 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 no-op
+     instructions.
+     fr_symbol: operand
+     1 constant byte: no-op fill control byte.  */
+  rs_space_nop,
+
+  /* Similar to rs_fill.  It is used to implement .nop directive .  */
+  rs_fill_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..c67ea1f224 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -1146,105 +1146,174 @@ 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
+};
+
+/* Genenerate 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);
+}
+
+
+/* Genenerate COUNT bytes of NOPs to WHERE with the maximum size of a
+   single NOP instruction LIMIT.  */
+
+void
+i386_generate_nops (fragS *f, char *where, offsetT count, int limit)
+{
+  /* Output NOPs for .nop directive.  */
+  int max_single_nop_size;
+  const unsigned char *const *patt;
+
+  if (flag_code == CODE_16BIT)
+    {
+      patt = f16_patt;
+      max_single_nop_size = sizeof (f16_patt) / sizeof (f16_patt[0]);
+    }
+  else if (flag_code == CODE_64BIT)
+    {
+      patt = alt64_patt;
+      max_single_nop_size = sizeof (alt64_patt) / sizeof (alt64_patt[0]);
+    }
+  else
+    {
+      patt = alt_patt;
+      max_single_nop_size = sizeof (alt_patt) / sizeof (alt_patt[0]);
+    }
+  if (limit == 0)
+    limit = max_single_nop_size;
+  else if (limit > max_single_nop_size)
+    {
+      as_bad_where (f->fr_file, f->fr_line,
+		    _("invalide single nop size: %d (expect within [0, %d])"),
+		    limit, max_single_nop_size);
+      return;
+    }
+
+  i386_output_nops (where, patt, count, limit);
+}
+
+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 +1466,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;
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
index 6e4f440c09..1250bc25f5 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -281,6 +281,11 @@ extern void sco_id (void);
 
 #define WORKING_DOT_WORD 1
 
+/* How to generate NOPs for .nop direct directive.  */
+extern void i386_generate_nops (fragS *, char *, offsetT, int);
+#define md_generate_nops(frag, where, amount, control) \
+  i386_generate_nops ((frag), (where), (amount), (control))
+
 /* 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..3084bcb871 100644
--- a/gas/doc/as.texinfo
+++ b/gas/doc/as.texinfo
@@ -4479,6 +4479,7 @@ Some machine configurations provide additional directives.
 * MRI::				@code{.mri @var{val}}
 * Noaltmacro::                  @code{.noaltmacro}
 * Nolist::                      @code{.nolist}
+* Nop::                         @code{.nop @var{size}[, @var{control}]}
 * Octa::                        @code{.octa @var{bignums}}
 * Offset::			@code{.offset @var{loc}}
 * Org::                         @code{.org @var{new-lc}, @var{fill}}
@@ -6101,6 +6102,23 @@ internal counter (which is zero initially).   @code{.list} increments the
 counter, and @code{.nolist} decrements it.  Assembly listings are
 generated whenever the counter is greater than zero.
 
+@node Nop
+@section @code{.nop @var{size}[, @var{control}]}
+
+@cindex @code{nop} directive
+@cindex filling memory with no-op instructions
+This directive emits @var{size} bytes filled with no-op instructions.
+@var{size} is absolute expression, which must be a positve value.
+@var{control} controls how no-op instructions should be generated.  If
+the comma and @var{control} are omitted, @var{control} is assumed to be
+zero.
+
+Note: For Intel 80386 and AMD x86-64 targets, @var{control} specifies
+the size limit of a no-op instruction.  The valid values of @var{control}
+are between 0 and 8 for 16-bit mode, between 0 and 10 for 32-bit mode,
+between 0 and 11 for 64-bit mode.  When 0 is used, the no-op instruction
+size limit is set to the maximum supported size.
+
 @node Octa
 @section @code{.octa @var{bignums}}
 
diff --git a/gas/read.c b/gas/read.c
index 7bf52f1121..9ab88f8962 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -442,6 +442,7 @@ static const pseudo_typeS potable[] = {
 /* size  */
   {"space", s_space, 0},
   {"skip", s_space, 0},
+  {"nop", s_nop, 0},
   {"sleb128", s_leb128, 1},
   {"spc", s_ignore, 0},
   {"stabd", s_stab, 'd'},
@@ -3508,6 +3509,58 @@ s_space (int mult)
     mri_comment_end (stop, stopc);
 }
 
+void
+s_nop (int ignore ATTRIBUTE_UNUSED)
+{
+  expressionS exp;
+  expressionS val;
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+#ifdef md_cons_align
+  md_cons_align (1);
+#endif
+
+  expression (&exp);
+
+  if (*input_line_pointer == ',')
+    {
+      ++input_line_pointer;
+      expression (&val);
+    }
+  else
+    {
+      val.X_op = O_constant;
+      val.X_add_number = 0;
+    }
+
+  if (val.X_op == O_constant)
+    {
+      if (val.X_add_number < 0)
+	{
+	  as_warn (_("negative nop control byte, ignored"));
+	  val.X_add_number = 0;
+	}
+
+      if (!need_pass_2)
+	{
+	  /* Store the no-op instruction control byte in the first byte
+	     of frag.  */
+	  char *p;
+	  symbolS *sym = make_expr_symbol (&exp);
+	  p = frag_var (rs_space_nop, 1, 1, (relax_substateT) 0,
+			sym, (offsetT) 0, (char *) 0);
+	  *p = val.X_add_number;
+	}
+    }
+  else
+    as_bad (_("unsupported variable nop control in .nop directive"));
+
+  demand_empty_rest_of_line ();
+}
+
 /* This is like s_space, but the value is a floating point number with
    the given precision.  This is for the MRI dcb.s pseudo-op and
    friends.  */
diff --git a/gas/read.h b/gas/read.h
index eebdc4e745..3f0927bc9e 100644
--- a/gas/read.h
+++ b/gas/read.h
@@ -206,6 +206,7 @@ extern void s_purgem (int);
 extern void s_rept (int);
 extern void s_set (int);
 extern void s_space (int mult);
+extern void s_nop (int);
 extern void s_stab (int what);
 extern void s_struct (int);
 extern void s_text (int);
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..34be496b4b
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-bad-1.l
@@ -0,0 +1,4 @@
+.*: Assembler messages:
+.*:2: Warning: negative nop control byte, ignored
+.*:4: Warning: \.space, \.nop or \.fill with negative value, ignored
+.*:3: Error: invalide single nop size: 20 \(expect within \[0, [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..53cc7d554a
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-bad-1.s
@@ -0,0 +1,4 @@
+	.text
+        .nop 100, -2
+        .nop 100, 20
+        .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..9b14fda156 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -435,6 +435,8 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
 {
   switch (fragP->fr_type)
     {
+    case rs_space_nop:
+      goto skip_align;
     case rs_align:
     case rs_align_code:
     case rs_align_test:
@@ -443,6 +445,7 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
 #ifdef HANDLE_ALIGN
       HANDLE_ALIGN (fragP);
 #endif
+skip_align:
       know (fragP->fr_next != NULL);
       fragP->fr_offset = (fragP->fr_next->fr_address
 			  - fragP->fr_address
@@ -450,14 +453,18 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
       if (fragP->fr_offset < 0)
 	{
 	  as_bad_where (fragP->fr_file, fragP->fr_line,
-			_("attempt to .org/.space backwards? (%ld)"),
+			_("attempt to .org/.space/.nop backwards? (%ld)"),
 			(long) fragP->fr_offset);
 	  fragP->fr_offset = 0;
 	}
-      fragP->fr_type = rs_fill;
+      if (fragP->fr_type == rs_space_nop)
+	fragP->fr_type = rs_fill_nop;
+      else
+	fragP->fr_type = rs_fill;
       break;
 
     case rs_fill:
+    case rs_fill_nop:
       break;
 
     case rs_leb128:
@@ -1570,6 +1577,20 @@ compress_debug (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
     }
 }
 
+#ifndef md_generate_nops
+/* Genenerate COUNT bytes of no-op instructions to WHERE.  A target
+   backend must override this with proper no-op instructions.   */
+
+static void
+md_generate_nops (fragS *f ATTRIBUTE_UNUSED,
+		  char *where ATTRIBUTE_UNUSED,
+		  offsetT count ATTRIBUTE_UNUSED,
+		  int control ATTRIBUTE_UNUSED)
+{
+  as_bad (_("unimplemented .nop directive"));
+}
+#endif
+
 static void
 write_contents (bfd *abfd ATTRIBUTE_UNUSED,
 		asection *sec,
@@ -1593,7 +1614,7 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
       char *fill_literal;
       offsetT count;
 
-      gas_assert (f->fr_type == rs_fill);
+      gas_assert (f->fr_type == rs_fill || f->fr_type == rs_fill_nop);
       if (f->fr_fix)
 	{
 	  x = bfd_set_section_contents (stdoutput, sec,
@@ -1610,9 +1631,35 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
 		      bfd_errmsg (bfd_get_error ()));
 	  offset += f->fr_fix;
 	}
-      fill_literal = f->fr_literal + f->fr_fix;
+
       fill_size = f->fr_var;
       count = f->fr_offset;
+      fill_literal = f->fr_literal + f->fr_fix;
+
+      if (f->fr_type == rs_fill_nop)
+	{
+	  gas_assert (count >= 0 && fill_size == 1);
+	  if (count > 0)
+	    {
+	      char *buf = xmalloc (count);
+	      md_generate_nops (f, buf, count, *fill_literal);
+	      x = bfd_set_section_contents
+		(stdoutput, sec, buf, (file_ptr) offset,
+		 (bfd_size_type) count);
+	      if (!x)
+		as_fatal (ngettext ("can't fill %ld byte "
+				    "in section %s of %s: '%s'",
+				    "can't fill %ld bytes "
+				    "in section %s of %s: '%s'",
+				    (long) count), (long) count,
+				    sec->name, stdoutput->filename,
+				    bfd_errmsg (bfd_get_error ()));
+	      offset += count;
+	      free (buf);
+	    }
+	  continue;
+	}
+
       gas_assert (count >= 0);
       if (fill_size && count)
 	{
@@ -2461,6 +2508,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 	  break;
 
 	case rs_space:
+	case rs_space_nop:
 	  break;
 
 	case rs_machine_dependent:
@@ -2765,6 +2813,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 		break;
 
 	      case rs_space:
+	      case rs_space_nop:
 		growth = 0;
 		if (symbolP)
 		  {
@@ -2791,7 +2840,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 			  }
 
 			as_warn_where (fragP->fr_file, fragP->fr_line,
-				       _(".space or .fill with negative value, ignored"));
+				       _(".space, .nop or .fill with negative value, ignored"));
 			fragP->fr_symbol = 0;
 		      }
 		    else
H.J. Lu Feb. 17, 2018, 3:04 a.m. | #21
On Fri, Feb 16, 2018 at 6:59 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Fri, Feb 16, 2018 at 4:49 PM, Alan Modra <amodra@gmail.com> wrote:

>> On Fri, Feb 16, 2018 at 08:07:50AM -0800, H.J. Lu wrote:

>>> Good points.  Here is the updated patch.   OK for master?

>>

>> OK, except for one quibble with the default md_generate_nops.  I think

>> that should error rather than filling with zeros (which is likely to

>> be an illegal instruction or trap).

>>

>

> Fixed.  This is the patch I will check in tomorrow.

>


Here is a patch for gas/NEWS.

diff --git a/gas/NEWS b/gas/NEWS
index a591a7b200..27ee306f2f 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -1,5 +1,8 @@
 -*- text -*-

+* Add support for .nop directive.  It is currently supported only for
+  x86 targets.
+
 Changes in 2.30:

 * Add support for loaction views in DWARF debug line information.


-- 
H.J.
Florian Weimer Feb. 19, 2018, 1:41 p.m. | #22
On 02/12/2018 08:38 PM, H.J. Lu wrote:
> +@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.


The documentation doesn't say if it is safe to execute those NOPs on all 
future CPUs, or if these NOPs can only be used for non-executing 
padding.  I think it's worth clarifying that.

Thanks,
Florian
Andrew Cooper Feb. 19, 2018, 1:49 p.m. | #23
On 19/02/18 13:41, Florian Weimer wrote:
> On 02/12/2018 08:38 PM, H.J. Lu wrote:

>> +@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.

>

> The documentation doesn't say if it is safe to execute those NOPs on

> all future CPUs, or if these NOPs can only be used for non-executing

> padding.  I think it's worth clarifying that.


Ah - the usecase I proposed requires them to be usable for executable
padding.  Certainly in the 64bit case, the nop sequences do match the
recommended nops from the Intel and AMD optimisation manuals.

~Andrew
Maciej W. Rozycki Feb. 19, 2018, 2:07 p.m. | #24
On Fri, 16 Feb 2018, Alan Modra wrote:

> > Good points.  Here is the updated patch.   OK for master?

> 

> OK, except for one quibble with the default md_generate_nops.  I think

> that should error rather than filling with zeros (which is likely to

> be an illegal instruction or trap).


 I have a feeling the default should wire in HANDLE_ALIGN somehow.  For 
many targets, such as fixed instruction width RISC processors, there is 
only going to be one way of filling a given amount of space with no-op 
instructions, so I think there's no point in having code duplication 
here.  And even if there are more ways possible for a given target, then 
the `.align' way is still right even if suboptimal.

  Maciej
Michael Matz Feb. 19, 2018, 2:17 p.m. | #25
Hi,

On Mon, 19 Feb 2018, Maciej W. Rozycki wrote:

> > OK, except for one quibble with the default md_generate_nops.  I think

> > that should error rather than filling with zeros (which is likely to

> > be an illegal instruction or trap).

> 

>  I have a feeling the default should wire in HANDLE_ALIGN somehow.  For 

> many targets, such as fixed instruction width RISC processors, there is 

> only going to be one way of filling a given amount of space with no-op 

> instructions, so I think there's no point in having code duplication 

> here.  And even if there are more ways possible for a given target, then 

> the `.align' way is still right even if suboptimal.


How would you propose to express "I need three nop 
instructions aka 12 bytes from here" with just .align?  (Note: no matter 
how "here" is currently aligned!)


Ciao,
Michael.
H.J. Lu Feb. 19, 2018, 2:21 p.m. | #26
On Mon, Feb 19, 2018 at 6:17 AM, Michael Matz <matz@suse.de> wrote:
> Hi,

>

> On Mon, 19 Feb 2018, Maciej W. Rozycki wrote:

>

>> > OK, except for one quibble with the default md_generate_nops.  I think

>> > that should error rather than filling with zeros (which is likely to

>> > be an illegal instruction or trap).

>>

>>  I have a feeling the default should wire in HANDLE_ALIGN somehow.  For

>> many targets, such as fixed instruction width RISC processors, there is

>> only going to be one way of filling a given amount of space with no-op

>> instructions, so I think there's no point in having code duplication

>> here.  And even if there are more ways possible for a given target, then

>> the `.align' way is still right even if suboptimal.

>

> How would you propose to express "I need three nop

> instructions aka 12 bytes from here" with just .align?  (Note: no matter

> how "here" is currently aligned!)

>


.align isn't the same as .nop.   However, HANDLE_ALIGN can be updated
to shared the same code with md_generate_nops, like what I did for x86.


H.J.
H.J. Lu Feb. 19, 2018, 2:24 p.m. | #27
On Mon, Feb 19, 2018 at 5:41 AM, Florian Weimer <fweimer@redhat.com> wrote:
> On 02/12/2018 08:38 PM, H.J. Lu wrote:

>>

>> +@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.

>

>

> The documentation doesn't say if it is safe to execute those NOPs on all


These NOPs are no different from other x86 instructions, like CMOVxx.

> future CPUs, or if these NOPs can only be used for non-executing padding.  I

> think it's worth clarifying that.

>


.nop is similar to .skip, except that it fills with NOPs.  Yes, it can be used
wherever .skip can be used.

-- 
H.J.
Maciej W. Rozycki Feb. 19, 2018, 2:44 p.m. | #28
On Mon, 19 Feb 2018, H.J. Lu wrote:

> >>  I have a feeling the default should wire in HANDLE_ALIGN somehow.  For

> >> many targets, such as fixed instruction width RISC processors, there is

> >> only going to be one way of filling a given amount of space with no-op

> >> instructions, so I think there's no point in having code duplication

> >> here.  And even if there are more ways possible for a given target, then

> >> the `.align' way is still right even if suboptimal.

> >

> > How would you propose to express "I need three nop

> > instructions aka 12 bytes from here" with just .align?  (Note: no matter

> > how "here" is currently aligned!)

> >

> 

> .align isn't the same as .nop.   However, HANDLE_ALIGN can be updated

> to shared the same code with md_generate_nops, like what I did for x86.


 Yes, that's what I meant.  It's an internal code wiring thing, not to be 
something observable by the user.

  Maciej
H.J. Lu Feb. 19, 2018, 2:47 p.m. | #29
On Mon, Feb 19, 2018 at 6:44 AM, Maciej W. Rozycki <macro@mips.com> wrote:
> On Mon, 19 Feb 2018, H.J. Lu wrote:

>

>> >>  I have a feeling the default should wire in HANDLE_ALIGN somehow.  For

>> >> many targets, such as fixed instruction width RISC processors, there is

>> >> only going to be one way of filling a given amount of space with no-op

>> >> instructions, so I think there's no point in having code duplication

>> >> here.  And even if there are more ways possible for a given target, then

>> >> the `.align' way is still right even if suboptimal.

>> >

>> > How would you propose to express "I need three nop

>> > instructions aka 12 bytes from here" with just .align?  (Note: no matter

>> > how "here" is currently aligned!)

>> >

>>

>> .align isn't the same as .nop.   However, HANDLE_ALIGN can be updated

>> to shared the same code with md_generate_nops, like what I did for x86.

>

>  Yes, that's what I meant.  It's an internal code wiring thing, not to be

> something observable by the user.

>


Let me work on it.


-- 
H.J.
H.J. Lu Feb. 19, 2018, 4:36 p.m. | #30
On Mon, Feb 19, 2018 at 6:47 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Mon, Feb 19, 2018 at 6:44 AM, Maciej W. Rozycki <macro@mips.com> wrote:

>> On Mon, 19 Feb 2018, H.J. Lu wrote:

>>

>>> >>  I have a feeling the default should wire in HANDLE_ALIGN somehow.  For

>>> >> many targets, such as fixed instruction width RISC processors, there is

>>> >> only going to be one way of filling a given amount of space with no-op

>>> >> instructions, so I think there's no point in having code duplication

>>> >> here.  And even if there are more ways possible for a given target, then

>>> >> the `.align' way is still right even if suboptimal.

>>> >

>>> > How would you propose to express "I need three nop

>>> > instructions aka 12 bytes from here" with just .align?  (Note: no matter

>>> > how "here" is currently aligned!)

>>> >

>>>

>>> .align isn't the same as .nop.   However, HANDLE_ALIGN can be updated

>>> to shared the same code with md_generate_nops, like what I did for x86.

>>

>>  Yes, that's what I meant.  It's an internal code wiring thing, not to be

>> something observable by the user.

>>

>

> Let me work on it.


Here is the patch to implement HANDLE_ALIGN with i386_generate_nops.
Other targets can do something similar.


-- 
H.J.
From 4a1a555eb09bc9fab822b0099ce048d205a1d400 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Mon, 19 Feb 2018 07:32:28 -0800
Subject: [PATCH] x86: Implement HANDLE_ALIGN with i386_generate_nops

HANDLE_ALIGN can be implemented with i386_generate_nops.

	* write.c (HANDLE_ALIGN): Define with md_generate_nops if not
	defined.
	* config/tc-i386.c (i386_align_code): Merged with ...
	(i386_generate_nops): This.
	* config/tc-i386.h (i386_align_code): Removed.
	(HANDLE_ALIGN): Rewritten with i386_generate_nops.
---
 gas/config/tc-i386.c | 68 +++++++++++++++++++++++++---------------------------
 gas/config/tc-i386.h | 19 ++++++++-------
 2 files changed, 43 insertions(+), 44 deletions(-)

diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index c67ea1f224..09b3694135 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -1277,46 +1277,43 @@ i386_output_nops (char *where, const unsigned char *const *patt,
    single NOP instruction LIMIT.  */
 
 void
-i386_generate_nops (fragS *f, char *where, offsetT count, int limit)
+i386_generate_nops (fragS *fragP, char *where, offsetT count, int limit)
 {
   /* Output NOPs for .nop directive.  */
-  int max_single_nop_size;
-  const unsigned char *const *patt;
-
-  if (flag_code == CODE_16BIT)
-    {
-      patt = f16_patt;
-      max_single_nop_size = sizeof (f16_patt) / sizeof (f16_patt[0]);
-    }
-  else if (flag_code == CODE_64BIT)
-    {
-      patt = alt64_patt;
-      max_single_nop_size = sizeof (alt64_patt) / sizeof (alt64_patt[0]);
-    }
-  else
+  if (fragP->fr_type == rs_fill_nop)
     {
-      patt = alt_patt;
-      max_single_nop_size = sizeof (alt_patt) / sizeof (alt_patt[0]);
-    }
-  if (limit == 0)
-    limit = max_single_nop_size;
-  else if (limit > max_single_nop_size)
-    {
-      as_bad_where (f->fr_file, f->fr_line,
-		    _("invalide single nop size: %d (expect within [0, %d])"),
-		    limit, max_single_nop_size);
-      return;
-    }
+      int max_single_nop_size;
+      const unsigned char *const *patt;
 
-  i386_output_nops (where, patt, count, limit);
-}
+      if (flag_code == CODE_16BIT)
+	{
+	  patt = f16_patt;
+	  max_single_nop_size = sizeof (f16_patt) / sizeof (f16_patt[0]);
+	}
+      else if (flag_code == CODE_64BIT)
+	{
+	  patt = alt64_patt;
+	  max_single_nop_size = sizeof (alt64_patt) / sizeof (alt64_patt[0]);
+	}
+      else
+	{
+	  patt = alt_patt;
+	  max_single_nop_size = sizeof (alt_patt) / sizeof (alt_patt[0]);
+	}
+      if (limit == 0)
+	limit = max_single_nop_size;
+      else if (limit > max_single_nop_size)
+	{
+	  as_bad_where (fragP->fr_file, fragP->fr_line,
+			_("invalide single nop size: %d "
+			  "(expect within [0, %d])"),
+			limit, max_single_nop_size);
+	  return;
+	}
 
-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;
+      i386_output_nops (where, patt, count, limit);
+      return;
+    }
 
   /* We need to decide which NOP sequence to use for 32bit and
      64bit. When -mtune= is used:
@@ -1442,7 +1439,6 @@ i386_align_code (fragS *fragP, int count)
 	  /* If the padding is less than 15 bytes, we use the normal
 	     ones.  Otherwise, we use a jump instruction and adjust
 	     its offset.   */
-	  int limit;
 
 	  /* For 64bit, the limit is 3 bytes.  */
 	  if (flag_code == CODE_64BIT
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
index 1250bc25f5..72484b2537 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -207,14 +207,6 @@ if ((n)									\
 
 #define MAX_MEM_FOR_RS_ALIGN_CODE  31
 
-extern void i386_align_code (fragS *, int);
-
-#define HANDLE_ALIGN(fragP)						\
-if (fragP->fr_type == rs_align_code) 					\
-  i386_align_code (fragP, (fragP->fr_next->fr_address			\
-			   - fragP->fr_address				\
-			   - fragP->fr_fix));
-
 void i386_print_statistics (FILE *);
 #define tc_print_statistics i386_print_statistics
 
@@ -286,6 +278,17 @@ extern void i386_generate_nops (fragS *, char *, offsetT, int);
 #define md_generate_nops(frag, where, amount, control) \
   i386_generate_nops ((frag), (where), (amount), (control))
 
+#define HANDLE_ALIGN(fragP)						\
+if (fragP->fr_type == rs_align_code) 					\
+  {									\
+    offsetT __count = (fragP->fr_next->fr_address			\
+		       - fragP->fr_address				\
+		       - fragP->fr_fix);				\
+    if (__count > 0 && __count <= MAX_MEM_FOR_RS_ALIGN_CODE)		\
+      md_generate_nops (fragP, fragP->fr_literal + fragP->fr_fix,	\
+			__count, 0);					\
+  }
+
 /* We want .cfi_* pseudo-ops for generating unwind info.  */
 #define TARGET_USE_CFIPOP 1
Alan Modra Feb. 19, 2018, 9:54 p.m. | #31
On Mon, Feb 19, 2018 at 06:24:04AM -0800, H.J. Lu wrote:
> .nop is similar to .skip, except that it fills with NOPs.  Yes, it can be used

> wherever .skip can be used.


Given https://sourceware.org/ml/binutils/2018-02/msg00322.html I'm
inclined to think the name of the directive should change.
.skipnops perhaps?

-- 
Alan Modra
Australia Development Lab, IBM
Maciej W. Rozycki Feb. 19, 2018, 10:08 p.m. | #32
On Mon, 19 Feb 2018, Alan Modra wrote:

> > .nop is similar to .skip, except that it fills with NOPs.  Yes, it can be used

> > wherever .skip can be used.

> 

> Given https://sourceware.org/ml/binutils/2018-02/msg00322.html I'm

> inclined to think the name of the directive should change.

> .skipnops perhaps?


 Just `.nops' maybe?  There doesn't appear to be any matching mnemonic in 
opcodes/.

  Maciej
Alan Modra Feb. 19, 2018, 11:19 p.m. | #33
On Mon, Feb 19, 2018 at 10:08:38PM +0000, Maciej W. Rozycki wrote:
> On Mon, 19 Feb 2018, Alan Modra wrote:

> 

> > > .nop is similar to .skip, except that it fills with NOPs.  Yes, it can be used

> > > wherever .skip can be used.

> > 

> > Given https://sourceware.org/ml/binutils/2018-02/msg00322.html I'm

> > inclined to think the name of the directive should change.

> > .skipnops perhaps?

> 

>  Just `.nops' maybe?  There doesn't appear to be any matching mnemonic in 

> opcodes/.


I'd be happy with that too.

-- 
Alan Modra
Australia Development Lab, IBM
H.J. Lu Feb. 19, 2018, 11:24 p.m. | #34
On Mon, Feb 19, 2018 at 3:19 PM, Alan Modra <amodra@gmail.com> wrote:
> On Mon, Feb 19, 2018 at 10:08:38PM +0000, Maciej W. Rozycki wrote:

>> On Mon, 19 Feb 2018, Alan Modra wrote:

>>

>> > > .nop is similar to .skip, except that it fills with NOPs.  Yes, it can be used

>> > > wherever .skip can be used.

>> >

>> > Given https://sourceware.org/ml/binutils/2018-02/msg00322.html I'm

>> > inclined to think the name of the directive should change.

>> > .skipnops perhaps?

>>

>>  Just `.nops' maybe?  There doesn't appear to be any matching mnemonic in

>> opcodes/.

>

> I'd be happy with that too.


There is no guarantee that one of those NO_PSEUDO_DOT targets or the new
NO_PSEUDO_DOT target won't have an instruction called nops or skipnops in
the future.

-- 
H.J.
Alan Modra Feb. 20, 2018, 12:05 a.m. | #35
On Mon, Feb 19, 2018 at 03:24:17PM -0800, H.J. Lu wrote:
> On Mon, Feb 19, 2018 at 3:19 PM, Alan Modra <amodra@gmail.com> wrote:

> > On Mon, Feb 19, 2018 at 10:08:38PM +0000, Maciej W. Rozycki wrote:

> >> On Mon, 19 Feb 2018, Alan Modra wrote:

> >>

> >> > > .nop is similar to .skip, except that it fills with NOPs.  Yes, it can be used

> >> > > wherever .skip can be used.

> >> >

> >> > Given https://sourceware.org/ml/binutils/2018-02/msg00322.html I'm

> >> > inclined to think the name of the directive should change.

> >> > .skipnops perhaps?

> >>

> >>  Just `.nops' maybe?  There doesn't appear to be any matching mnemonic in

> >> opcodes/.

> >

> > I'd be happy with that too.

> 

> There is no guarantee that one of those NO_PSEUDO_DOT targets or the new

> NO_PSEUDO_DOT target won't have an instruction called nops or skipnops in

> the future.


If a target adds an instruction like that, then the target will need
to deal with it.  For example, as the spu target deals with "set" and
"equ" which existed as directives before the spu defined them as
instructions.

In this case the use of "nop" as an instruction existed before you
decided to define ".nop" as a directive, and lack of testing resulted
in not discovering the NO_PSEUDO_DOT clash.  I suspect we wouldn't be
having this conversation if you had run a full test suite regression,
rather than just testing x86.  You yourself would have chosen
something other than ".nop" as a directive!

-- 
Alan Modra
Australia Development Lab, IBM
H.J. Lu Feb. 20, 2018, 1:03 a.m. | #36
On Mon, Feb 19, 2018 at 4:05 PM, Alan Modra <amodra@gmail.com> wrote:
> On Mon, Feb 19, 2018 at 03:24:17PM -0800, H.J. Lu wrote:

>> On Mon, Feb 19, 2018 at 3:19 PM, Alan Modra <amodra@gmail.com> wrote:

>> > On Mon, Feb 19, 2018 at 10:08:38PM +0000, Maciej W. Rozycki wrote:

>> >> On Mon, 19 Feb 2018, Alan Modra wrote:

>> >>

>> >> > > .nop is similar to .skip, except that it fills with NOPs.  Yes, it can be used

>> >> > > wherever .skip can be used.

>> >> >

>> >> > Given https://sourceware.org/ml/binutils/2018-02/msg00322.html I'm

>> >> > inclined to think the name of the directive should change.

>> >> > .skipnops perhaps?

>> >>

>> >>  Just `.nops' maybe?  There doesn't appear to be any matching mnemonic in

>> >> opcodes/.

>> >

>> > I'd be happy with that too.

>>

>> There is no guarantee that one of those NO_PSEUDO_DOT targets or the new

>> NO_PSEUDO_DOT target won't have an instruction called nops or skipnops in

>> the future.

>

> If a target adds an instruction like that, then the target will need

> to deal with it.  For example, as the spu target deals with "set" and

> "equ" which existed as directives before the spu defined them as

> instructions.

>

> In this case the use of "nop" as an instruction existed before you

> decided to define ".nop" as a directive, and lack of testing resulted

> in not discovering the NO_PSEUDO_DOT clash.  I suspect we wouldn't be

> having this conversation if you had run a full test suite regression,

> rather than just testing x86.  You yourself would have chosen

> something other than ".nop" as a directive!


I would have chosen .nop and handled it for NO_PSEUDO_DOT targets.

Here is a patch to rename .nop to .nops.  OK for master?

-- 
H.J.
From eb81600ebb30f513732e395c524bcc9dca4de00a Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Mon, 19 Feb 2018 16:53:28 -0800
Subject: [PATCH] Rename .nop directive to .nops

Since directives of NO_PSEUDO_DOT targets don't have the leading '.'
and "nop" can be a valid instruction, rename .nop directive to .nops
to avoid conflict.

	* NEWS: Rename .nop to .nops.
	* doc/as.texinfo: Likewise.
	* read.c (potable): Add "nops".  Remove "nop".
	(s_nop): Renamed to ...
	(s_nops): This.
	* read.h (s_nop): Renamed to ...
	(s_nops): This.
	* write.c (cvt_frag_to_fill): Rename .nop to .nops.
	(md_generate_nops): Likewise.
	(relax_segment): Likewise.
	* testsuite/gas/i386/nop-1.d: Updated.
	* 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.
---
 gas/NEWS                              |  2 +-
 gas/doc/as.texinfo                    |  8 ++++----
 gas/read.c                            |  6 +++---
 gas/read.h                            |  2 +-
 gas/testsuite/gas/i386/nop-1.d        |  2 +-
 gas/testsuite/gas/i386/nop-1.s        | 12 ++++++------
 gas/testsuite/gas/i386/nop-2.d        |  2 +-
 gas/testsuite/gas/i386/nop-2.s        | 12 ++++++------
 gas/testsuite/gas/i386/nop-3.d        |  2 +-
 gas/testsuite/gas/i386/nop-3.s        |  2 +-
 gas/testsuite/gas/i386/nop-4.d        |  2 +-
 gas/testsuite/gas/i386/nop-4.s        |  2 +-
 gas/testsuite/gas/i386/nop-5.d        |  2 +-
 gas/testsuite/gas/i386/nop-5.s        |  2 +-
 gas/testsuite/gas/i386/nop-6.d        |  2 +-
 gas/testsuite/gas/i386/nop-6.s        |  2 +-
 gas/testsuite/gas/i386/nop-bad-1.l    |  2 +-
 gas/testsuite/gas/i386/nop-bad-1.s    |  6 +++---
 gas/testsuite/gas/i386/x86-64-nop-1.d |  2 +-
 gas/testsuite/gas/i386/x86-64-nop-2.d |  2 +-
 gas/testsuite/gas/i386/x86-64-nop-3.d |  2 +-
 gas/testsuite/gas/i386/x86-64-nop-4.d |  2 +-
 gas/testsuite/gas/i386/x86-64-nop-5.d |  2 +-
 gas/testsuite/gas/i386/x86-64-nop-6.d |  2 +-
 gas/write.c                           |  6 +++---
 25 files changed, 44 insertions(+), 44 deletions(-)

diff --git a/gas/NEWS b/gas/NEWS
index 27ee306f2f..c5b5e1c141 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -1,6 +1,6 @@
 -*- text -*-
 
-* Add support for .nop directive.  It is currently supported only for
+* Add support for .nops directive.  It is currently supported only for
   x86 targets.
 
 Changes in 2.30:
diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo
index 3084bcb871..5bae3fd643 100644
--- a/gas/doc/as.texinfo
+++ b/gas/doc/as.texinfo
@@ -4479,7 +4479,7 @@ Some machine configurations provide additional directives.
 * MRI::				@code{.mri @var{val}}
 * Noaltmacro::                  @code{.noaltmacro}
 * Nolist::                      @code{.nolist}
-* Nop::                         @code{.nop @var{size}[, @var{control}]}
+* Nops::                        @code{.nops @var{size}[, @var{control}]}
 * Octa::                        @code{.octa @var{bignums}}
 * Offset::			@code{.offset @var{loc}}
 * Org::                         @code{.org @var{new-lc}, @var{fill}}
@@ -6102,10 +6102,10 @@ internal counter (which is zero initially).   @code{.list} increments the
 counter, and @code{.nolist} decrements it.  Assembly listings are
 generated whenever the counter is greater than zero.
 
-@node Nop
-@section @code{.nop @var{size}[, @var{control}]}
+@node Nops
+@section @code{.nops @var{size}[, @var{control}]}
 
-@cindex @code{nop} directive
+@cindex @code{nops} directive
 @cindex filling memory with no-op instructions
 This directive emits @var{size} bytes filled with no-op instructions.
 @var{size} is absolute expression, which must be a positve value.
diff --git a/gas/read.c b/gas/read.c
index 9ab88f8962..ff78caef38 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -417,6 +417,7 @@ static const pseudo_typeS potable[] = {
   {"noformat", s_ignore, 0},
   {"nolist", listing_list, 0},	/* Turn listing off.  */
   {"nopage", listing_nopage, 0},
+  {"nops", s_nops, 0},
   {"octa", cons, 16},
   {"offset", s_struct, 0},
   {"org", s_org, 0},
@@ -442,7 +443,6 @@ static const pseudo_typeS potable[] = {
 /* size  */
   {"space", s_space, 0},
   {"skip", s_space, 0},
-  {"nop", s_nop, 0},
   {"sleb128", s_leb128, 1},
   {"spc", s_ignore, 0},
   {"stabd", s_stab, 'd'},
@@ -3510,7 +3510,7 @@ s_space (int mult)
 }
 
 void
-s_nop (int ignore ATTRIBUTE_UNUSED)
+s_nops (int ignore ATTRIBUTE_UNUSED)
 {
   expressionS exp;
   expressionS val;
@@ -3556,7 +3556,7 @@ s_nop (int ignore ATTRIBUTE_UNUSED)
 	}
     }
   else
-    as_bad (_("unsupported variable nop control in .nop directive"));
+    as_bad (_("unsupported variable nop control in .nops directive"));
 
   demand_empty_rest_of_line ();
 }
diff --git a/gas/read.h b/gas/read.h
index 3f0927bc9e..352b802d4a 100644
--- a/gas/read.h
+++ b/gas/read.h
@@ -206,7 +206,7 @@ extern void s_purgem (int);
 extern void s_rept (int);
 extern void s_set (int);
 extern void s_space (int mult);
-extern void s_nop (int);
+extern void s_nops (int);
 extern void s_stab (int what);
 extern void s_struct (int);
 extern void s_text (int);
diff --git a/gas/testsuite/gas/i386/nop-1.d b/gas/testsuite/gas/i386/nop-1.d
index 46422c88db..5a9e03ef19 100644
--- a/gas/testsuite/gas/i386/nop-1.d
+++ b/gas/testsuite/gas/i386/nop-1.d
@@ -1,5 +1,5 @@
 #objdump: -drw
-#name: i386 .nop 1
+#name: i386 .nops 1
 
 .*: +file format .*
 
diff --git a/gas/testsuite/gas/i386/nop-1.s b/gas/testsuite/gas/i386/nop-1.s
index 891783dce8..94ec28e815 100644
--- a/gas/testsuite/gas/i386/nop-1.s
+++ b/gas/testsuite/gas/i386/nop-1.s
@@ -1,21 +1,21 @@
        .text
 single:
-	.nop 0
+	.nops 0
 	nop
 
 pseudo_1:
-	.nop 1
+	.nops 1
 
 pseudo_8:
-	.nop 8
+	.nops 8
 
 pseudo_8_4:
-	.nop 8, 4
+	.nops 8, 4
 
 pseudo_20:
-	.nop 20
+	.nops 20
 
 pseudo_30:
-	.nop 30
+	.nops 30
 
 	xor %eax, %eax
diff --git a/gas/testsuite/gas/i386/nop-2.d b/gas/testsuite/gas/i386/nop-2.d
index 332b990a97..d248fd1df7 100644
--- a/gas/testsuite/gas/i386/nop-2.d
+++ b/gas/testsuite/gas/i386/nop-2.d
@@ -1,5 +1,5 @@
 #objdump: -drw -Mi8086
-#name: i386 .nop 2
+#name: i386 .nops 2
 
 .*: +file format .*
 
diff --git a/gas/testsuite/gas/i386/nop-2.s b/gas/testsuite/gas/i386/nop-2.s
index 2b71b9786d..9f7a3729c0 100644
--- a/gas/testsuite/gas/i386/nop-2.s
+++ b/gas/testsuite/gas/i386/nop-2.s
@@ -1,22 +1,22 @@
        .text
        .code16
 single:
-	.nop 0
+	.nops 0
 	nop
 
 pseudo_1:
-	.nop 1
+	.nops 1
 
 pseudo_8:
-	.nop 8
+	.nops 8
 
 pseudo_8_4:
-	.nop 8, 4
+	.nops 8, 4
 
 pseudo_20:
-	.nop 20
+	.nops 20
 
 pseudo_30:
-	.nop 30
+	.nops 30
 
 	xor %eax, %eax
diff --git a/gas/testsuite/gas/i386/nop-3.d b/gas/testsuite/gas/i386/nop-3.d
index bebd24bc08..b2b4577b55 100644
--- a/gas/testsuite/gas/i386/nop-3.d
+++ b/gas/testsuite/gas/i386/nop-3.d
@@ -1,5 +1,5 @@
 #objdump: -drw
-#name: i386 .nop 3
+#name: i386 .nops 3
 
 .*: +file format .*
 
diff --git a/gas/testsuite/gas/i386/nop-3.s b/gas/testsuite/gas/i386/nop-3.s
index 57370ff579..0a6bb58fdf 100644
--- a/gas/testsuite/gas/i386/nop-3.s
+++ b/gas/testsuite/gas/i386/nop-3.s
@@ -4,7 +4,7 @@ _start:
 140:
 	testl %eax, %eax
 141:
-	.nop -(((144f-143f)-(141b-140b)) > 0)*((144f-143f)-(141b-140b)),7
+	.nops -(((144f-143f)-(141b-140b)) > 0)*((144f-143f)-(141b-140b)),7
 142:
 	xor %eax, %eax
 	.pushsection .altinstr_replacement,"ax"
diff --git a/gas/testsuite/gas/i386/nop-4.d b/gas/testsuite/gas/i386/nop-4.d
index 99ddcd3994..b548d079a3 100644
--- a/gas/testsuite/gas/i386/nop-4.d
+++ b/gas/testsuite/gas/i386/nop-4.d
@@ -1,5 +1,5 @@
 #objdump: -drw
-#name: i386 .nop 4
+#name: i386 .nops 4
 
 .*: +file format .*
 
diff --git a/gas/testsuite/gas/i386/nop-4.s b/gas/testsuite/gas/i386/nop-4.s
index f7aa11187e..4f0bfe6dc3 100644
--- a/gas/testsuite/gas/i386/nop-4.s
+++ b/gas/testsuite/gas/i386/nop-4.s
@@ -4,7 +4,7 @@ _start:
 140:
 	testl %eax, %eax
 141:
-	.nop -(((144f-143f)-(141b-140b)) > 0)*((144f-143f)-(141b-140b))
+	.nops -(((144f-143f)-(141b-140b)) > 0)*((144f-143f)-(141b-140b))
 142:
 	xor %eax, %eax
 	.pushsection .altinstr_replacement,"ax"
diff --git a/gas/testsuite/gas/i386/nop-5.d b/gas/testsuite/gas/i386/nop-5.d
index aab4258b19..6a4791344f 100644
--- a/gas/testsuite/gas/i386/nop-5.d
+++ b/gas/testsuite/gas/i386/nop-5.d
@@ -1,5 +1,5 @@
 #objdump: -drw
-#name: i386 .nop 5
+#name: i386 .nops 5
 
 .*: +file format .*
 
diff --git a/gas/testsuite/gas/i386/nop-5.s b/gas/testsuite/gas/i386/nop-5.s
index 4f563ce82f..d91f81fb09 100644
--- a/gas/testsuite/gas/i386/nop-5.s
+++ b/gas/testsuite/gas/i386/nop-5.s
@@ -4,7 +4,7 @@ _start:
 140:
 	testl %eax, %eax
 141:
-	.nop -(((144f-143f)-(141b-140b)) > 0)*((144f-143f)-(141b-140b)),6
+	.nops -(((144f-143f)-(141b-140b)) > 0)*((144f-143f)-(141b-140b)),6
 142:
 	xor %eax, %eax
 	.pushsection .altinstr_replacement,"ax"
diff --git a/gas/testsuite/gas/i386/nop-6.d b/gas/testsuite/gas/i386/nop-6.d
index 93ee8def03..851c129c4a 100644
--- a/gas/testsuite/gas/i386/nop-6.d
+++ b/gas/testsuite/gas/i386/nop-6.d
@@ -1,5 +1,5 @@
 #objdump: -drw
-#name: i386 .nop 6
+#name: i386 .nops 6
 
 .*: +file format .*
 
diff --git a/gas/testsuite/gas/i386/nop-6.s b/gas/testsuite/gas/i386/nop-6.s
index c7b1e2cbf0..d8952c9a43 100644
--- a/gas/testsuite/gas/i386/nop-6.s
+++ b/gas/testsuite/gas/i386/nop-6.s
@@ -1,5 +1,5 @@
 .macro mknops nr_bytes
-    .nop \nr_bytes, 9
+    .nops \nr_bytes, 9
 .endm
 
 .macro ALTERNATIVE
diff --git a/gas/testsuite/gas/i386/nop-bad-1.l b/gas/testsuite/gas/i386/nop-bad-1.l
index 34be496b4b..2ae5b48453 100644
--- a/gas/testsuite/gas/i386/nop-bad-1.l
+++ b/gas/testsuite/gas/i386/nop-bad-1.l
@@ -1,4 +1,4 @@
 .*: Assembler messages:
 .*:2: Warning: negative nop control byte, ignored
-.*:4: Warning: \.space, \.nop or \.fill with negative value, ignored
+.*:4: Warning: \.space, \.nops or \.fill with negative value, ignored
 .*:3: Error: invalide single nop size: 20 \(expect within \[0, [0-9]+\]\)
diff --git a/gas/testsuite/gas/i386/nop-bad-1.s b/gas/testsuite/gas/i386/nop-bad-1.s
index 53cc7d554a..2f2bbfb9fd 100644
--- a/gas/testsuite/gas/i386/nop-bad-1.s
+++ b/gas/testsuite/gas/i386/nop-bad-1.s
@@ -1,4 +1,4 @@
 	.text
-        .nop 100, -2
-        .nop 100, 20
-        .nop -1
+        .nops 100, -2
+        .nops 100, 20
+        .nops -1
diff --git a/gas/testsuite/gas/i386/x86-64-nop-1.d b/gas/testsuite/gas/i386/x86-64-nop-1.d
index f3edc7d346..ec6263b49d 100644
--- a/gas/testsuite/gas/i386/x86-64-nop-1.d
+++ b/gas/testsuite/gas/i386/x86-64-nop-1.d
@@ -1,6 +1,6 @@
 #source: nop-1.s
 #objdump: -drw
-#name: x86-64 .nop 1
+#name: x86-64 .nops 1
 
 .*: +file format .*
 
diff --git a/gas/testsuite/gas/i386/x86-64-nop-2.d b/gas/testsuite/gas/i386/x86-64-nop-2.d
index e894d2c7bf..438e4f84f2 100644
--- a/gas/testsuite/gas/i386/x86-64-nop-2.d
+++ b/gas/testsuite/gas/i386/x86-64-nop-2.d
@@ -1,6 +1,6 @@
 #source: nop-2.s
 #objdump: -drw -Mi8086
-#name: x86-64 .nop 2
+#name: x86-64 .nops 2
 
 .*: +file format .*
 
diff --git a/gas/testsuite/gas/i386/x86-64-nop-3.d b/gas/testsuite/gas/i386/x86-64-nop-3.d
index b43239af6f..1dc9505b4f 100644
--- a/gas/testsuite/gas/i386/x86-64-nop-3.d
+++ b/gas/testsuite/gas/i386/x86-64-nop-3.d
@@ -1,6 +1,6 @@
 #source: nop-3.s
 #objdump: -drw
-#name: x86-64 .nop 3
+#name: x86-64 .nops 3
 
 .*: +file format .*
 
diff --git a/gas/testsuite/gas/i386/x86-64-nop-4.d b/gas/testsuite/gas/i386/x86-64-nop-4.d
index a910171303..25927ca222 100644
--- a/gas/testsuite/gas/i386/x86-64-nop-4.d
+++ b/gas/testsuite/gas/i386/x86-64-nop-4.d
@@ -1,6 +1,6 @@
 #source: nop-4.s
 #objdump: -drw
-#name: x86-64 .nop 4
+#name: x86-64 .nops 4
 
 .*: +file format .*
 
diff --git a/gas/testsuite/gas/i386/x86-64-nop-5.d b/gas/testsuite/gas/i386/x86-64-nop-5.d
index 57493cf6dc..a609a1254b 100644
--- a/gas/testsuite/gas/i386/x86-64-nop-5.d
+++ b/gas/testsuite/gas/i386/x86-64-nop-5.d
@@ -1,6 +1,6 @@
 #source: nop-5.s
 #objdump: -drw
-#name: x86-64 .nop 5
+#name: x86-64 .nops 5
 
 .*: +file format .*
 
diff --git a/gas/testsuite/gas/i386/x86-64-nop-6.d b/gas/testsuite/gas/i386/x86-64-nop-6.d
index 520f590945..63f3817148 100644
--- a/gas/testsuite/gas/i386/x86-64-nop-6.d
+++ b/gas/testsuite/gas/i386/x86-64-nop-6.d
@@ -1,6 +1,6 @@
 #source: nop-6.s
 #objdump: -drw
-#name: x86-64 .nop 6
+#name: x86-64 .nops 6
 
 .*: +file format .*
 
diff --git a/gas/write.c b/gas/write.c
index 9b14fda156..4c8e42b4b4 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -453,7 +453,7 @@ skip_align:
       if (fragP->fr_offset < 0)
 	{
 	  as_bad_where (fragP->fr_file, fragP->fr_line,
-			_("attempt to .org/.space/.nop backwards? (%ld)"),
+			_("attempt to .org/.space/.nops backwards? (%ld)"),
 			(long) fragP->fr_offset);
 	  fragP->fr_offset = 0;
 	}
@@ -1587,7 +1587,7 @@ md_generate_nops (fragS *f ATTRIBUTE_UNUSED,
 		  offsetT count ATTRIBUTE_UNUSED,
 		  int control ATTRIBUTE_UNUSED)
 {
-  as_bad (_("unimplemented .nop directive"));
+  as_bad (_("unimplemented .nops directive"));
 }
 #endif
 
@@ -2840,7 +2840,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 			  }
 
 			as_warn_where (fragP->fr_file, fragP->fr_line,
-				       _(".space, .nop or .fill with negative value, ignored"));
+				       _(".space, .nops or .fill with negative value, ignored"));
 			fragP->fr_symbol = 0;
 		      }
 		    else
H.J. Lu Feb. 20, 2018, 1:24 p.m. | #37
On Mon, Feb 19, 2018 at 5:03 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Mon, Feb 19, 2018 at 4:05 PM, Alan Modra <amodra@gmail.com> wrote:

>> On Mon, Feb 19, 2018 at 03:24:17PM -0800, H.J. Lu wrote:

>>> On Mon, Feb 19, 2018 at 3:19 PM, Alan Modra <amodra@gmail.com> wrote:

>>> > On Mon, Feb 19, 2018 at 10:08:38PM +0000, Maciej W. Rozycki wrote:

>>> >> On Mon, 19 Feb 2018, Alan Modra wrote:

>>> >>

>>> >> > > .nop is similar to .skip, except that it fills with NOPs.  Yes, it can be used

>>> >> > > wherever .skip can be used.

>>> >> >

>>> >> > Given https://sourceware.org/ml/binutils/2018-02/msg00322.html I'm

>>> >> > inclined to think the name of the directive should change.

>>> >> > .skipnops perhaps?

>>> >>

>>> >>  Just `.nops' maybe?  There doesn't appear to be any matching mnemonic in

>>> >> opcodes/.

>>> >

>>> > I'd be happy with that too.

>>>

>>> There is no guarantee that one of those NO_PSEUDO_DOT targets or the new

>>> NO_PSEUDO_DOT target won't have an instruction called nops or skipnops in

>>> the future.

>>

>> If a target adds an instruction like that, then the target will need

>> to deal with it.  For example, as the spu target deals with "set" and

>> "equ" which existed as directives before the spu defined them as

>> instructions.

>>

>> In this case the use of "nop" as an instruction existed before you

>> decided to define ".nop" as a directive, and lack of testing resulted

>> in not discovering the NO_PSEUDO_DOT clash.  I suspect we wouldn't be

>> having this conversation if you had run a full test suite regression,

>> rather than just testing x86.  You yourself would have chosen

>> something other than ".nop" as a directive!

>

> I would have chosen .nop and handled it for NO_PSEUDO_DOT targets.

>

> Here is a patch to rename .nop to .nops.  OK for master?

>


Just for the record, I found it is extremely odd that .nop has to be
renamed to .nops just because of NO_PSEUDO_DOT targets.

-- 
H.J.
H.J. Lu Feb. 23, 2018, 11:56 p.m. | #38
On Tue, Feb 20, 2018 at 5:24 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Mon, Feb 19, 2018 at 5:03 PM, H.J. Lu <hjl.tools@gmail.com> wrote:

>> On Mon, Feb 19, 2018 at 4:05 PM, Alan Modra <amodra@gmail.com> wrote:

>>> On Mon, Feb 19, 2018 at 03:24:17PM -0800, H.J. Lu wrote:

>>>> On Mon, Feb 19, 2018 at 3:19 PM, Alan Modra <amodra@gmail.com> wrote:

>>>> > On Mon, Feb 19, 2018 at 10:08:38PM +0000, Maciej W. Rozycki wrote:

>>>> >> On Mon, 19 Feb 2018, Alan Modra wrote:

>>>> >>

>>>> >> > > .nop is similar to .skip, except that it fills with NOPs.  Yes, it can be used

>>>> >> > > wherever .skip can be used.

>>>> >> >

>>>> >> > Given https://sourceware.org/ml/binutils/2018-02/msg00322.html I'm

>>>> >> > inclined to think the name of the directive should change.

>>>> >> > .skipnops perhaps?

>>>> >>

>>>> >>  Just `.nops' maybe?  There doesn't appear to be any matching mnemonic in

>>>> >> opcodes/.

>>>> >

>>>> > I'd be happy with that too.

>>>>

>>>> There is no guarantee that one of those NO_PSEUDO_DOT targets or the new

>>>> NO_PSEUDO_DOT target won't have an instruction called nops or skipnops in

>>>> the future.

>>>

>>> If a target adds an instruction like that, then the target will need

>>> to deal with it.  For example, as the spu target deals with "set" and

>>> "equ" which existed as directives before the spu defined them as

>>> instructions.

>>>

>>> In this case the use of "nop" as an instruction existed before you

>>> decided to define ".nop" as a directive, and lack of testing resulted

>>> in not discovering the NO_PSEUDO_DOT clash.  I suspect we wouldn't be

>>> having this conversation if you had run a full test suite regression,

>>> rather than just testing x86.  You yourself would have chosen

>>> something other than ".nop" as a directive!

>>

>> I would have chosen .nop and handled it for NO_PSEUDO_DOT targets.

>>

>> Here is a patch to rename .nop to .nops.  OK for master?

>>

>

> Just for the record, I found it is extremely odd that .nop has to be

> renamed to .nops just because of NO_PSEUDO_DOT targets.

>


How about this patch?

-- 
H.J.
From 56fb57ef6b66d805df88b65a800f844cbbb59cbd Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Fri, 23 Feb 2018 15:41:56 -0800
Subject: [PATCH] Skip pseudo-op for instruction of the same name

Pseudo-ops on NO_PSEUDO_DOT targets don't require a leading dot.  But
there may be an instruction which has the same name as a pseudo-op.
This patch adds md_pseudo_insn_table for NO_PSEUDO_DOT targets, which
contains generic pseudo-ops which are also machine specific instructions,
and it consults md_pseudo_insn_table to skip pseudo-op which matches
a machine specific instruction.

	* read.c (po_pseudo_insn_hash): New if md_pseudo_insn_table is
	defined.
	(pop_pseudo_insn_insert): New.
	(pop_pseudo_insn_find): Likewise.
	(pobegin): Call pop_pseudo_insn_insert.
	(read_a_source_file): Skip pseudo-op without dot if there is an
	instruction of the same name.
	* config/tc-m68hc11.c (md_pseudo_insn_table): New.
	* config/tc-m68hc11.h (md_pseudo_insn_table): Likewise.
	* config/tc-m68k.c (md_pseudo_insn_table): Likewise.
	* config/tc-m68k.h (md_pseudo_insn_table): Likewise.
	* config/tc-spu.h (md_pseudo_insn_table): Likewise.
	* config/tc-xgate.c (md_pseudo_insn_table): Likewise.
	* config/tc-xgate.h (md_pseudo_insn_table): Likewise.
	* config/tc-xtensa.c (md_pseudo_insn_table): Likewise.
	* config/tc-xtensa.h (md_pseudo_insn_table): Likewise.
	* config/tc-z80.h (md_pseudo_insn_table): Likewise.
	* config/tc-spu.c (md_pseudo_table): Remove set, .set,
	eqv and .eqv.
	(md_pseudo_insn_table): New.
	* config/tc-z80.c (md_pseudo_table): Remove set.
	(md_pseudo_insn_table): New.
---
 gas/config/tc-m68hc11.c |  8 ++++++++
 gas/config/tc-m68hc11.h |  3 +++
 gas/config/tc-m68k.c    |  8 ++++++++
 gas/config/tc-m68k.h    |  3 +++
 gas/config/tc-spu.c     | 16 ++++++++++------
 gas/config/tc-spu.h     |  3 +++
 gas/config/tc-xgate.c   |  8 ++++++++
 gas/config/tc-xgate.h   |  3 +++
 gas/config/tc-xtensa.c  |  8 ++++++++
 gas/config/tc-xtensa.h  |  3 +++
 gas/config/tc-z80.c     | 10 +++++++++-
 gas/config/tc-z80.h     |  3 +++
 gas/read.c              | 36 ++++++++++++++++++++++++++++++++++--
 13 files changed, 103 insertions(+), 9 deletions(-)

diff --git a/gas/config/tc-m68hc11.c b/gas/config/tc-m68hc11.c
index 31ff9815b0..f93b2b3262 100644
--- a/gas/config/tc-m68hc11.c
+++ b/gas/config/tc-m68hc11.c
@@ -320,6 +320,14 @@ const pseudo_typeS md_pseudo_table[] =
 
   {0, 0, 0}
 };
+
+/* This table describes all generic pseudo-ops which are also machine
+   specific instructions.  */
+const pseudo_typeS md_pseudo_insn_table[] =
+{
+  {"nop", NULL, 0},
+  {NULL, NULL, 0}			/* End sentinel.  */
+};
 
 /* Options and initialization.  */
 
diff --git a/gas/config/tc-m68hc11.h b/gas/config/tc-m68hc11.h
index e6bfd74e96..96764de5bd 100644
--- a/gas/config/tc-m68hc11.h
+++ b/gas/config/tc-m68hc11.h
@@ -31,6 +31,9 @@ struct fix;
 /* Motorola assembler specs does not require '.' before pseudo-ops.  */
 #define NO_PSEUDO_DOT 1
 
+extern const pseudo_typeS md_pseudo_insn_table[];
+#define md_pseudo_insn_table md_pseudo_insn_table
+
 /* The target BFD architecture.  */
 #define TARGET_ARCH (m68hc11_arch ())
 extern enum bfd_architecture m68hc11_arch (void);
diff --git a/gas/config/tc-m68k.c b/gas/config/tc-m68k.c
index 13fb897012..6ca01abd9c 100644
--- a/gas/config/tc-m68k.c
+++ b/gas/config/tc-m68k.c
@@ -979,6 +979,14 @@ const pseudo_typeS mote_pseudo_table[] =
   {0, 0, 0}
 };
 
+/* This table describes all generic pseudo-ops which are also machine
+   specific instructions.  */
+const pseudo_typeS md_pseudo_insn_table[] =
+{
+  {"nop", NULL, 0},
+  {NULL, NULL, 0}			/* End sentinel.  */
+};
+
 /* Truncate and sign-extend at 32 bits, so that building on a 64-bit host
    gives identical results to a 32-bit host.  */
 #define TRUNC(X)	((valueT) (X) & 0xffffffff)
diff --git a/gas/config/tc-m68k.h b/gas/config/tc-m68k.h
index 42f69845e7..901c164f41 100644
--- a/gas/config/tc-m68k.h
+++ b/gas/config/tc-m68k.h
@@ -93,6 +93,9 @@ extern const char *m68k_comment_chars;
 #define NO_PSEUDO_DOT 1
 #endif
 
+extern const pseudo_typeS md_pseudo_insn_table[];
+#define md_pseudo_insn_table md_pseudo_insn_table
+
 extern void m68k_mri_mode_change (int);
 #define MRI_MODE_CHANGE(i) m68k_mri_mode_change (i)
 
diff --git a/gas/config/tc-spu.c b/gas/config/tc-spu.c
index 474805bc26..9388b9829f 100644
--- a/gas/config/tc-spu.c
+++ b/gas/config/tc-spu.c
@@ -95,15 +95,19 @@ const pseudo_typeS md_pseudo_table[] =
   {"quad", spu_cons, 8},
   {"string", stringer, 8 + 1},
   {"word", spu_cons, 4},
-  /* Force set to be treated as an instruction.  */
-  {"set", NULL, 0},
-  {".set", s_set, 0},
-  /* Likewise for eqv.  */
-  {"eqv", NULL, 0},
-  {".eqv", s_set, -1},
   {0,0,0}
 };
 
+/* This table describes all generic pseudo-ops which are also machine
+   specific instructions.  */
+const pseudo_typeS md_pseudo_insn_table[] =
+{
+  {"eqv", NULL, 0},
+  {"nop", NULL, 0},
+  {"set", NULL, 0},
+  {NULL, NULL, 0}			/* End sentinel.  */
+};
+
 /* Bits plugged into branch instruction offset field.  */
 unsigned int brinfo;
 
diff --git a/gas/config/tc-spu.h b/gas/config/tc-spu.h
index b163cf0557..acfb3fa41c 100644
--- a/gas/config/tc-spu.h
+++ b/gas/config/tc-spu.h
@@ -73,6 +73,9 @@ struct tc_fix_info {
 /* The spu uses pseudo-ops with no leading period.  */
 #define NO_PSEUDO_DOT 1
 
+extern const pseudo_typeS md_pseudo_insn_table[];
+#define md_pseudo_insn_table md_pseudo_insn_table
+
 /* Don't warn on word overflow; it happens on %hi relocs.  */
 #undef WARN_SIGNED_OVERFLOW_WORD
 
diff --git a/gas/config/tc-xgate.c b/gas/config/tc-xgate.c
index 20dbbedcc7..dad00c634a 100644
--- a/gas/config/tc-xgate.c
+++ b/gas/config/tc-xgate.c
@@ -163,6 +163,14 @@ const pseudo_typeS md_pseudo_table[] =
   {0, 0, 0}
 };
 
+/* This table describes all generic pseudo-ops which are also machine
+   specific instructions.  */
+const pseudo_typeS md_pseudo_insn_table[] =
+{
+  {"nop", NULL, 0},
+  {NULL, NULL, 0}			/* End sentinel.  */
+};
+
 const char *md_shortopts = "m:";
 
 struct option md_longopts[] =
diff --git a/gas/config/tc-xgate.h b/gas/config/tc-xgate.h
index 1b65f042d1..d86befe8bd 100644
--- a/gas/config/tc-xgate.h
+++ b/gas/config/tc-xgate.h
@@ -31,6 +31,9 @@ struct fix;
 /* Motorola assembler specs does not require '.' before pseudo-ops.  */
 #define NO_PSEUDO_DOT 1
 
+extern const pseudo_typeS md_pseudo_insn_table[];
+#define md_pseudo_insn_table md_pseudo_insn_table
+
 /* The target BFD architecture.  */
 #define TARGET_ARCH (xgate_arch ())
 extern enum bfd_architecture xgate_arch (void);
diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
index 4db7ef57e8..1f89ae61f9 100644
--- a/gas/config/tc-xtensa.c
+++ b/gas/config/tc-xtensa.c
@@ -1136,6 +1136,14 @@ const pseudo_typeS md_pseudo_table[] =
   { NULL, 0, 0 },
 };
 
+/* This table describes all generic pseudo-ops which are also machine
+   specific instructions.  */
+const pseudo_typeS md_pseudo_insn_table[] =
+{
+  {"nop", NULL, 0},
+  {NULL, NULL, 0}			/* End sentinel.  */
+};
+
 
 static bfd_boolean
 use_transform (void)
diff --git a/gas/config/tc-xtensa.h b/gas/config/tc-xtensa.h
index d423776eb6..81dddfccfe 100644
--- a/gas/config/tc-xtensa.h
+++ b/gas/config/tc-xtensa.h
@@ -381,6 +381,9 @@ extern void xtensa_init (int, char **);
 #define HANDLE_ALIGN(fragP)		xtensa_handle_align (fragP)
 #define MAX_MEM_FOR_RS_ALIGN_CODE	1
 
+extern const pseudo_typeS md_pseudo_insn_table[];
+#define md_pseudo_insn_table md_pseudo_insn_table
+
 
 /* The renumber_section function must be mapped over all the sections
    after calling xtensa_post_relax_hook.  That function is static in
diff --git a/gas/config/tc-z80.c b/gas/config/tc-z80.c
index 5a4fd38fce..470fecd487 100644
--- a/gas/config/tc-z80.c
+++ b/gas/config/tc-z80.c
@@ -1832,10 +1832,18 @@ const pseudo_typeS md_pseudo_table[] =
   { "ds",   s_space, 1}, /* Fill with bytes rather than words.  */
   { "dw", cons, 2},
   { "psect", obj_coff_section, 0}, /* TODO: Translate attributes.  */
-  { "set", 0, 0}, 		/* Real instruction on z80.  */
   { NULL, 0, 0 }
 } ;
 
+/* This table describes all generic pseudo-ops which are also machine
+   specific instructions.  */
+const pseudo_typeS md_pseudo_insn_table[] =
+{
+  {"nop", NULL, 0},
+  {"set", NULL, 0}, 		/* Real instruction on z80.  */
+  {NULL, NULL, 0}		/* End sentinel.  */
+};
+
 static table_t instab[] =
 {
   { "adc",  0x88, 0x4A, emit_adc },
diff --git a/gas/config/tc-z80.h b/gas/config/tc-z80.h
index 75f379a793..3f7d122477 100644
--- a/gas/config/tc-z80.h
+++ b/gas/config/tc-z80.h
@@ -93,6 +93,9 @@ extern void z80_cons_fix_new (fragS *, int, int, expressionS *);
 #define SINGLE_QUOTE_STRINGS
 #define NO_STRING_ESCAPES
 
+extern const pseudo_typeS md_pseudo_insn_table[];
+#define md_pseudo_insn_table md_pseudo_insn_table
+
 /* An `.lcomm' directive with no explicit alignment parameter will
    use this macro to set P2VAR to the alignment that a request for
    SIZE bytes will have.  The alignment is expressed as a power of
diff --git a/gas/read.c b/gas/read.c
index 9ab88f8962..06c82aeda2 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -535,6 +535,31 @@ pop_insert (const pseudo_typeS *table)
 #define cfi_pop_insert()	pop_insert(cfi_pseudo_table)
 #endif
 
+#ifdef md_pseudo_insn_table
+static struct hash_control *po_pseudo_insn_hash;
+
+static void
+pop_pseudo_insn_insert (void)
+{
+  const char *errtxt;
+  const pseudo_typeS *pop;
+  po_pseudo_insn_hash = hash_new ();
+  for (pop = md_pseudo_insn_table; pop->poc_name; pop++)
+    {
+      errtxt = hash_insert (po_pseudo_insn_hash, pop->poc_name, (char *) pop);
+      if (errtxt && (!pop_override_ok || strcmp (errtxt, "exists")))
+	as_fatal (_("error constructing md insn-op table: %s"),
+		  errtxt);
+    }
+}
+
+#define pop_pseudo_insn_find(s) \
+  ((pseudo_typeS *) hash_find (po_pseudo_insn_hash, (s)))
+#else
+#define pop_pseudo_insn_insert()
+#define pop_pseudo_insn_find(s) NULL
+#endif
+
 static void
 pobegin (void)
 {
@@ -557,6 +582,8 @@ pobegin (void)
   pop_table_name = "cfi";
   pop_override_ok = 1;
   cfi_pop_insert ();
+
+  pop_pseudo_insn_insert ();
 }
 
 #define HANDLE_CONDITIONAL_ASSEMBLY(num_read)				\
@@ -1067,8 +1094,13 @@ read_a_source_file (const char *name)
 		      /* The MRI assembler uses pseudo-ops without
 			 a period.  */
 		      pop = (pseudo_typeS *) hash_find (po_hash, s);
-		      if (pop != NULL && pop->poc_handler == NULL)
-			pop = NULL;
+		      if (pop != NULL)
+			{
+			  pseudo_typeS *insn_pop
+			    = pop_pseudo_insn_find (s);
+			  if (insn_pop != NULL)
+			    pop = NULL;
+			}
 		    }
 
 		  if (pop != NULL
John Darrington Feb. 26, 2018, 7 p.m. | #39
I haven't read every post in this thread, nor do I pretend to understand all the
issues.

But it this change seems to have the effect that any instruction called "NOP" 
in the .s file are completely ignored :(

Many cpus have  a NOP instruction. It's often used in realtime systems to
implement short delays.

J'


On Fri, Feb 23, 2018 at 03:56:41PM -0800, H.J. Lu wrote:
     On Tue, Feb 20, 2018 at 5:24 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
     > On Mon, Feb 19, 2018 at 5:03 PM, H.J. Lu <hjl.tools@gmail.com> wrote:

     >> On Mon, Feb 19, 2018 at 4:05 PM, Alan Modra <amodra@gmail.com> wrote:

     >>> On Mon, Feb 19, 2018 at 03:24:17PM -0800, H.J. Lu wrote:

     >>>> On Mon, Feb 19, 2018 at 3:19 PM, Alan Modra <amodra@gmail.com> wrote:

     >>>> > On Mon, Feb 19, 2018 at 10:08:38PM +0000, Maciej W. Rozycki wrote:

     >>>> >> On Mon, 19 Feb 2018, Alan Modra wrote:

     >>>> >>

     >>>> >> > > .nop is similar to .skip, except that it fills with NOPs.  Yes, it can be used

     >>>> >> > > wherever .skip can be used.

     >>>> >> >

     >>>> >> > Given https://sourceware.org/ml/binutils/2018-02/msg00322.html I'm

     >>>> >> > inclined to think the name of the directive should change.

     >>>> >> > .skipnops perhaps?

     >>>> >>

     >>>> >>  Just `.nops' maybe?  There doesn't appear to be any matching mnemonic in

     >>>> >> opcodes/.

     >>>> >

     >>>> > I'd be happy with that too.

     >>>>

     >>>> There is no guarantee that one of those NO_PSEUDO_DOT targets or the new

     >>>> NO_PSEUDO_DOT target won't have an instruction called nops or skipnops in

     >>>> the future.

     >>>

     >>> If a target adds an instruction like that, then the target will need

     >>> to deal with it.  For example, as the spu target deals with "set" and

     >>> "equ" which existed as directives before the spu defined them as

     >>> instructions.

     >>>

     >>> In this case the use of "nop" as an instruction existed before you

     >>> decided to define ".nop" as a directive, and lack of testing resulted

     >>> in not discovering the NO_PSEUDO_DOT clash.  I suspect we wouldn't be

     >>> having this conversation if you had run a full test suite regression,

     >>> rather than just testing x86.  You yourself would have chosen

     >>> something other than ".nop" as a directive!

     >>

     >> I would have chosen .nop and handled it for NO_PSEUDO_DOT targets.

     >>

     >> Here is a patch to rename .nop to .nops.  OK for master?

     >>

     >

     > Just for the record, I found it is extremely odd that .nop has to be

     > renamed to .nops just because of NO_PSEUDO_DOT targets.

     >

     
     How about this patch?
     
     -- 
     H.J.

     From 56fb57ef6b66d805df88b65a800f844cbbb59cbd Mon Sep 17 00:00:00 2001
     From: "H.J. Lu" <hjl.tools@gmail.com>

     Date: Fri, 23 Feb 2018 15:41:56 -0800
     Subject: [PATCH] Skip pseudo-op for instruction of the same name
     
     Pseudo-ops on NO_PSEUDO_DOT targets don't require a leading dot.  But
     there may be an instruction which has the same name as a pseudo-op.
     This patch adds md_pseudo_insn_table for NO_PSEUDO_DOT targets, which
     contains generic pseudo-ops which are also machine specific instructions,
     and it consults md_pseudo_insn_table to skip pseudo-op which matches
     a machine specific instruction.
     
     	* read.c (po_pseudo_insn_hash): New if md_pseudo_insn_table is
     	defined.
     	(pop_pseudo_insn_insert): New.
     	(pop_pseudo_insn_find): Likewise.
     	(pobegin): Call pop_pseudo_insn_insert.
     	(read_a_source_file): Skip pseudo-op without dot if there is an
     	instruction of the same name.
     	* config/tc-m68hc11.c (md_pseudo_insn_table): New.
     	* config/tc-m68hc11.h (md_pseudo_insn_table): Likewise.
     	* config/tc-m68k.c (md_pseudo_insn_table): Likewise.
     	* config/tc-m68k.h (md_pseudo_insn_table): Likewise.
     	* config/tc-spu.h (md_pseudo_insn_table): Likewise.
     	* config/tc-xgate.c (md_pseudo_insn_table): Likewise.
     	* config/tc-xgate.h (md_pseudo_insn_table): Likewise.
     	* config/tc-xtensa.c (md_pseudo_insn_table): Likewise.
     	* config/tc-xtensa.h (md_pseudo_insn_table): Likewise.
     	* config/tc-z80.h (md_pseudo_insn_table): Likewise.
     	* config/tc-spu.c (md_pseudo_table): Remove set, .set,
     	eqv and .eqv.
     	(md_pseudo_insn_table): New.
     	* config/tc-z80.c (md_pseudo_table): Remove set.
     	(md_pseudo_insn_table): New.
     ---
      gas/config/tc-m68hc11.c |  8 ++++++++
      gas/config/tc-m68hc11.h |  3 +++
      gas/config/tc-m68k.c    |  8 ++++++++
      gas/config/tc-m68k.h    |  3 +++
      gas/config/tc-spu.c     | 16 ++++++++++------
      gas/config/tc-spu.h     |  3 +++
      gas/config/tc-xgate.c   |  8 ++++++++
      gas/config/tc-xgate.h   |  3 +++
      gas/config/tc-xtensa.c  |  8 ++++++++
      gas/config/tc-xtensa.h  |  3 +++
      gas/config/tc-z80.c     | 10 +++++++++-
      gas/config/tc-z80.h     |  3 +++
      gas/read.c              | 36 ++++++++++++++++++++++++++++++++++--
      13 files changed, 103 insertions(+), 9 deletions(-)
     
     diff --git a/gas/config/tc-m68hc11.c b/gas/config/tc-m68hc11.c
     index 31ff9815b0..f93b2b3262 100644
     --- a/gas/config/tc-m68hc11.c
     +++ b/gas/config/tc-m68hc11.c
     @@ -320,6 +320,14 @@ const pseudo_typeS md_pseudo_table[] =
      
        {0, 0, 0}
      };
     +
     +/* This table describes all generic pseudo-ops which are also machine
     +   specific instructions.  */
     +const pseudo_typeS md_pseudo_insn_table[] =
     +{
     +  {"nop", NULL, 0},
     +  {NULL, NULL, 0}			/* End sentinel.  */
     +};
      
      /* Options and initialization.  */
      
     diff --git a/gas/config/tc-m68hc11.h b/gas/config/tc-m68hc11.h
     index e6bfd74e96..96764de5bd 100644
     --- a/gas/config/tc-m68hc11.h
     +++ b/gas/config/tc-m68hc11.h
     @@ -31,6 +31,9 @@ struct fix;
      /* Motorola assembler specs does not require '.' before pseudo-ops.  */
      #define NO_PSEUDO_DOT 1
      
     +extern const pseudo_typeS md_pseudo_insn_table[];
     +#define md_pseudo_insn_table md_pseudo_insn_table
     +
      /* The target BFD architecture.  */
      #define TARGET_ARCH (m68hc11_arch ())
      extern enum bfd_architecture m68hc11_arch (void);
     diff --git a/gas/config/tc-m68k.c b/gas/config/tc-m68k.c
     index 13fb897012..6ca01abd9c 100644
     --- a/gas/config/tc-m68k.c
     +++ b/gas/config/tc-m68k.c
     @@ -979,6 +979,14 @@ const pseudo_typeS mote_pseudo_table[] =
        {0, 0, 0}
      };
      
     +/* This table describes all generic pseudo-ops which are also machine
     +   specific instructions.  */
     +const pseudo_typeS md_pseudo_insn_table[] =
     +{
     +  {"nop", NULL, 0},
     +  {NULL, NULL, 0}			/* End sentinel.  */
     +};
     +
      /* Truncate and sign-extend at 32 bits, so that building on a 64-bit host
         gives identical results to a 32-bit host.  */
      #define TRUNC(X)	((valueT) (X) & 0xffffffff)
     diff --git a/gas/config/tc-m68k.h b/gas/config/tc-m68k.h
     index 42f69845e7..901c164f41 100644
     --- a/gas/config/tc-m68k.h
     +++ b/gas/config/tc-m68k.h
     @@ -93,6 +93,9 @@ extern const char *m68k_comment_chars;
      #define NO_PSEUDO_DOT 1
      #endif
      
     +extern const pseudo_typeS md_pseudo_insn_table[];
     +#define md_pseudo_insn_table md_pseudo_insn_table
     +
      extern void m68k_mri_mode_change (int);
      #define MRI_MODE_CHANGE(i) m68k_mri_mode_change (i)
      
     diff --git a/gas/config/tc-spu.c b/gas/config/tc-spu.c
     index 474805bc26..9388b9829f 100644
     --- a/gas/config/tc-spu.c
     +++ b/gas/config/tc-spu.c
     @@ -95,15 +95,19 @@ const pseudo_typeS md_pseudo_table[] =
        {"quad", spu_cons, 8},
        {"string", stringer, 8 + 1},
        {"word", spu_cons, 4},
     -  /* Force set to be treated as an instruction.  */
     -  {"set", NULL, 0},
     -  {".set", s_set, 0},
     -  /* Likewise for eqv.  */
     -  {"eqv", NULL, 0},
     -  {".eqv", s_set, -1},
        {0,0,0}
      };
      
     +/* This table describes all generic pseudo-ops which are also machine
     +   specific instructions.  */
     +const pseudo_typeS md_pseudo_insn_table[] =
     +{
     +  {"eqv", NULL, 0},
     +  {"nop", NULL, 0},
     +  {"set", NULL, 0},
     +  {NULL, NULL, 0}			/* End sentinel.  */
     +};
     +
      /* Bits plugged into branch instruction offset field.  */
      unsigned int brinfo;
      
     diff --git a/gas/config/tc-spu.h b/gas/config/tc-spu.h
     index b163cf0557..acfb3fa41c 100644
     --- a/gas/config/tc-spu.h
     +++ b/gas/config/tc-spu.h
     @@ -73,6 +73,9 @@ struct tc_fix_info {
      /* The spu uses pseudo-ops with no leading period.  */
      #define NO_PSEUDO_DOT 1
      
     +extern const pseudo_typeS md_pseudo_insn_table[];
     +#define md_pseudo_insn_table md_pseudo_insn_table
     +
      /* Don't warn on word overflow; it happens on %hi relocs.  */
      #undef WARN_SIGNED_OVERFLOW_WORD
      
     diff --git a/gas/config/tc-xgate.c b/gas/config/tc-xgate.c
     index 20dbbedcc7..dad00c634a 100644
     --- a/gas/config/tc-xgate.c
     +++ b/gas/config/tc-xgate.c
     @@ -163,6 +163,14 @@ const pseudo_typeS md_pseudo_table[] =
        {0, 0, 0}
      };
      
     +/* This table describes all generic pseudo-ops which are also machine
     +   specific instructions.  */
     +const pseudo_typeS md_pseudo_insn_table[] =
     +{
     +  {"nop", NULL, 0},
     +  {NULL, NULL, 0}			/* End sentinel.  */
     +};
     +
      const char *md_shortopts = "m:";
      
      struct option md_longopts[] =
     diff --git a/gas/config/tc-xgate.h b/gas/config/tc-xgate.h
     index 1b65f042d1..d86befe8bd 100644
     --- a/gas/config/tc-xgate.h
     +++ b/gas/config/tc-xgate.h
     @@ -31,6 +31,9 @@ struct fix;
      /* Motorola assembler specs does not require '.' before pseudo-ops.  */
      #define NO_PSEUDO_DOT 1
      
     +extern const pseudo_typeS md_pseudo_insn_table[];
     +#define md_pseudo_insn_table md_pseudo_insn_table
     +
      /* The target BFD architecture.  */
      #define TARGET_ARCH (xgate_arch ())
      extern enum bfd_architecture xgate_arch (void);
     diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
     index 4db7ef57e8..1f89ae61f9 100644
     --- a/gas/config/tc-xtensa.c
     +++ b/gas/config/tc-xtensa.c
     @@ -1136,6 +1136,14 @@ const pseudo_typeS md_pseudo_table[] =
        { NULL, 0, 0 },
      };
      
     +/* This table describes all generic pseudo-ops which are also machine
     +   specific instructions.  */
     +const pseudo_typeS md_pseudo_insn_table[] =
     +{
     +  {"nop", NULL, 0},
     +  {NULL, NULL, 0}			/* End sentinel.  */
     +};
     +
      
      static bfd_boolean
      use_transform (void)
     diff --git a/gas/config/tc-xtensa.h b/gas/config/tc-xtensa.h
     index d423776eb6..81dddfccfe 100644
     --- a/gas/config/tc-xtensa.h
     +++ b/gas/config/tc-xtensa.h
     @@ -381,6 +381,9 @@ extern void xtensa_init (int, char **);
      #define HANDLE_ALIGN(fragP)		xtensa_handle_align (fragP)
      #define MAX_MEM_FOR_RS_ALIGN_CODE	1
      
     +extern const pseudo_typeS md_pseudo_insn_table[];
     +#define md_pseudo_insn_table md_pseudo_insn_table
     +
      
      /* The renumber_section function must be mapped over all the sections
         after calling xtensa_post_relax_hook.  That function is static in
     diff --git a/gas/config/tc-z80.c b/gas/config/tc-z80.c
     index 5a4fd38fce..470fecd487 100644
     --- a/gas/config/tc-z80.c
     +++ b/gas/config/tc-z80.c
     @@ -1832,10 +1832,18 @@ const pseudo_typeS md_pseudo_table[] =
        { "ds",   s_space, 1}, /* Fill with bytes rather than words.  */
        { "dw", cons, 2},
        { "psect", obj_coff_section, 0}, /* TODO: Translate attributes.  */
     -  { "set", 0, 0}, 		/* Real instruction on z80.  */
        { NULL, 0, 0 }
      } ;
      
     +/* This table describes all generic pseudo-ops which are also machine
     +   specific instructions.  */
     +const pseudo_typeS md_pseudo_insn_table[] =
     +{
     +  {"nop", NULL, 0},
     +  {"set", NULL, 0}, 		/* Real instruction on z80.  */
     +  {NULL, NULL, 0}		/* End sentinel.  */
     +};
     +
      static table_t instab[] =
      {
        { "adc",  0x88, 0x4A, emit_adc },
     diff --git a/gas/config/tc-z80.h b/gas/config/tc-z80.h
     index 75f379a793..3f7d122477 100644
     --- a/gas/config/tc-z80.h
     +++ b/gas/config/tc-z80.h
     @@ -93,6 +93,9 @@ extern void z80_cons_fix_new (fragS *, int, int, expressionS *);
      #define SINGLE_QUOTE_STRINGS
      #define NO_STRING_ESCAPES
      
     +extern const pseudo_typeS md_pseudo_insn_table[];
     +#define md_pseudo_insn_table md_pseudo_insn_table
     +
      /* An `.lcomm' directive with no explicit alignment parameter will
         use this macro to set P2VAR to the alignment that a request for
         SIZE bytes will have.  The alignment is expressed as a power of
     diff --git a/gas/read.c b/gas/read.c
     index 9ab88f8962..06c82aeda2 100644
     --- a/gas/read.c
     +++ b/gas/read.c
     @@ -535,6 +535,31 @@ pop_insert (const pseudo_typeS *table)
      #define cfi_pop_insert()	pop_insert(cfi_pseudo_table)
      #endif
      
     +#ifdef md_pseudo_insn_table
     +static struct hash_control *po_pseudo_insn_hash;
     +
     +static void
     +pop_pseudo_insn_insert (void)
     +{
     +  const char *errtxt;
     +  const pseudo_typeS *pop;
     +  po_pseudo_insn_hash = hash_new ();
     +  for (pop = md_pseudo_insn_table; pop->poc_name; pop++)
     +    {
     +      errtxt = hash_insert (po_pseudo_insn_hash, pop->poc_name, (char *) pop);
     +      if (errtxt && (!pop_override_ok || strcmp (errtxt, "exists")))
     +	as_fatal (_("error constructing md insn-op table: %s"),
     +		  errtxt);
     +    }
     +}
     +
     +#define pop_pseudo_insn_find(s) \
     +  ((pseudo_typeS *) hash_find (po_pseudo_insn_hash, (s)))
     +#else
     +#define pop_pseudo_insn_insert()
     +#define pop_pseudo_insn_find(s) NULL
     +#endif
     +
      static void
      pobegin (void)
      {
     @@ -557,6 +582,8 @@ pobegin (void)
        pop_table_name = "cfi";
        pop_override_ok = 1;
        cfi_pop_insert ();
     +
     +  pop_pseudo_insn_insert ();
      }
      
      #define HANDLE_CONDITIONAL_ASSEMBLY(num_read)				\
     @@ -1067,8 +1094,13 @@ read_a_source_file (const char *name)
      		      /* The MRI assembler uses pseudo-ops without
      			 a period.  */
      		      pop = (pseudo_typeS *) hash_find (po_hash, s);
     -		      if (pop != NULL && pop->poc_handler == NULL)
     -			pop = NULL;
     +		      if (pop != NULL)
     +			{
     +			  pseudo_typeS *insn_pop
     +			    = pop_pseudo_insn_find (s);
     +			  if (insn_pop != NULL)
     +			    pop = NULL;
     +			}
      		    }
      
      		  if (pop != NULL
     -- 
     2.14.3
     


-- 
Avoid eavesdropping.  Send strong encrypted email.
PGP Public key ID: 1024D/2DE827B3 
fingerprint = 8797 A26D 0854 2EAB 0285  A290 8A67 719C 2DE8 27B3
See http://sks-keyservers.net or any PGP keyserver for public key.
H.J. Lu Feb. 26, 2018, 7:03 p.m. | #40
On Mon, Feb 26, 2018 at 11:00 AM, John Darrington
<john@darrington.wattle.id.au> wrote:
> I haven't read every post in this thread, nor do I pretend to understand all the

> issues.

>

> But it this change seems to have the effect that any instruction called "NOP"

> in the .s file are completely ignored :(

>

> Many cpus have  a NOP instruction. It's often used in realtime systems to

> implement short delays.


Which target have you tried?

-- 
H.J.
John Darrington Feb. 26, 2018, 7:17 p.m. | #41
On Mon, Feb 26, 2018 at 11:03:12AM -0800, H.J. Lu wrote:
     On Mon, Feb 26, 2018 at 11:00 AM, John Darrington
     <john@darrington.wattle.id.au> wrote:
     > I haven't read every post in this thread, nor do I pretend to understand all the

     > issues.

     >

     > But it this change seems to have the effect that any instruction called "NOP"

     > in the .s file are completely ignored :(

     >

     > Many cpus have  a NOP instruction. It's often used in realtime systems to

     > implement short delays.

     
     Which target have you tried?
     

hcs12z

J'

-- 
Avoid eavesdropping.  Send strong encrypted email.
PGP Public key ID: 1024D/2DE827B3 
fingerprint = 8797 A26D 0854 2EAB 0285  A290 8A67 719C 2DE8 27B3
See http://sks-keyservers.net or any PGP keyserver for public key.
Petr Ovtchenkov Feb. 26, 2018, 8:04 p.m. | #42
On Mon, 26 Feb 2018 20:17:30 +0100
John Darrington <john@darrington.wattle.id.au> wrote:

> On Mon, Feb 26, 2018 at 11:03:12AM -0800, H.J. Lu wrote:

>      On Mon, Feb 26, 2018 at 11:00 AM, John Darrington

>      <john@darrington.wattle.id.au> wrote:

>      > I haven't read every post in this thread, nor do I pretend to understand all the

>      > issues.

>      >

>      > But it this change seems to have the effect that any instruction called "NOP"

>      > in the .s file are completely ignored :(

>      >

>      > Many cpus have  a NOP instruction. It's often used in realtime systems to

>      > implement short delays.

>      

>      Which target have you tried?

>      

> 

> hcs12z


+1 to John Darrington.

nop indeed used as delay (for RISC processors, like SPARC, it is well specified delay)
on programming over bare metal. Ignoring it will lead to a catastrophic effects.

--

  - ptr
H.J. Lu Feb. 26, 2018, 8:08 p.m. | #43
On Mon, Feb 26, 2018 at 11:17 AM, John Darrington
<john@darrington.wattle.id.au> wrote:
> On Mon, Feb 26, 2018 at 11:03:12AM -0800, H.J. Lu wrote:

>      On Mon, Feb 26, 2018 at 11:00 AM, John Darrington

>      <john@darrington.wattle.id.au> wrote:

>      > I haven't read every post in this thread, nor do I pretend to understand all the

>      > issues.

>      >

>      > But it this change seems to have the effect that any instruction called "NOP"

>      > in the .s file are completely ignored :(

>      >

>      > Many cpus have  a NOP instruction. It's often used in realtime systems to

>      > implement short delays.

>

>      Which target have you tried?

>

>

> hcs12z

>


I got

[hjl@gnu-cfl-1 gas]$ cat x.s
nop
[hjl@gnu-cfl-1 gas]$ ./as-new -o x.o x.s -m68hcs12
[hjl@gnu-cfl-1 gas]$ ../binutils/objdump -dw x.o

x.o:     file format elf32-m68hc12


Disassembly of section .text:

00000000 <.text>:
   0: a7          nop
[hjl@gnu-cfl-1 gas]$

Am I missing something?

-- 
H.J.
Alan Modra Feb. 27, 2018, 10:25 p.m. | #44
On Mon, Feb 19, 2018 at 05:03:43PM -0800, H.J. Lu wrote:
> On Mon, Feb 19, 2018 at 4:05 PM, Alan Modra <amodra@gmail.com> wrote:

> > On Mon, Feb 19, 2018 at 03:24:17PM -0800, H.J. Lu wrote:

> >> On Mon, Feb 19, 2018 at 3:19 PM, Alan Modra <amodra@gmail.com> wrote:

> >> > On Mon, Feb 19, 2018 at 10:08:38PM +0000, Maciej W. Rozycki wrote:

> >> >> On Mon, 19 Feb 2018, Alan Modra wrote:

> >> >>

> >> >> > > .nop is similar to .skip, except that it fills with NOPs.  Yes, it can be used

> >> >> > > wherever .skip can be used.

> >> >> >

> >> >> > Given https://sourceware.org/ml/binutils/2018-02/msg00322.html I'm

> >> >> > inclined to think the name of the directive should change.

> >> >> > .skipnops perhaps?

> >> >>

> >> >>  Just `.nops' maybe?  There doesn't appear to be any matching mnemonic in

> >> >> opcodes/.

> >> >

> >> > I'd be happy with that too.

> >>

> >> There is no guarantee that one of those NO_PSEUDO_DOT targets or the new

> >> NO_PSEUDO_DOT target won't have an instruction called nops or skipnops in

> >> the future.

> >

> > If a target adds an instruction like that, then the target will need

> > to deal with it.  For example, as the spu target deals with "set" and

> > "equ" which existed as directives before the spu defined them as

> > instructions.

> >

> > In this case the use of "nop" as an instruction existed before you

> > decided to define ".nop" as a directive, and lack of testing resulted

> > in not discovering the NO_PSEUDO_DOT clash.  I suspect we wouldn't be

> > having this conversation if you had run a full test suite regression,

> > rather than just testing x86.  You yourself would have chosen

> > something other than ".nop" as a directive!

> 

> I would have chosen .nop and handled it for NO_PSEUDO_DOT targets.

> 

> Here is a patch to rename .nop to .nops.  OK for master?

> 

> -- 

> H.J.


> From eb81600ebb30f513732e395c524bcc9dca4de00a Mon Sep 17 00:00:00 2001

> From: "H.J. Lu" <hjl.tools@gmail.com>

> Date: Mon, 19 Feb 2018 16:53:28 -0800

> Subject: [PATCH] Rename .nop directive to .nops

> 

> Since directives of NO_PSEUDO_DOT targets don't have the leading '.'

> and "nop" can be a valid instruction, rename .nop directive to .nops

> to avoid conflict.

> 

> 	* NEWS: Rename .nop to .nops.

> 	* doc/as.texinfo: Likewise.

> 	* read.c (potable): Add "nops".  Remove "nop".

> 	(s_nop): Renamed to ...

> 	(s_nops): This.

> 	* read.h (s_nop): Renamed to ...

> 	(s_nops): This.

> 	* write.c (cvt_frag_to_fill): Rename .nop to .nops.

> 	(md_generate_nops): Likewise.

> 	(relax_segment): Likewise.

> 	* testsuite/gas/i386/nop-1.d: Updated.

> 	* 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.


OK.  I think this is the best solution from a maintenance point of
view.

-- 
Alan Modra
Australia Development Lab, IBM
H.J. Lu Feb. 27, 2018, 10:33 p.m. | #45
On Tue, Feb 27, 2018 at 2:25 PM, Alan Modra <amodra@gmail.com> wrote:
> On Mon, Feb 19, 2018 at 05:03:43PM -0800, H.J. Lu wrote:

>> On Mon, Feb 19, 2018 at 4:05 PM, Alan Modra <amodra@gmail.com> wrote:

>> > On Mon, Feb 19, 2018 at 03:24:17PM -0800, H.J. Lu wrote:

>> >> On Mon, Feb 19, 2018 at 3:19 PM, Alan Modra <amodra@gmail.com> wrote:

>> >> > On Mon, Feb 19, 2018 at 10:08:38PM +0000, Maciej W. Rozycki wrote:

>> >> >> On Mon, 19 Feb 2018, Alan Modra wrote:

>> >> >>

>> >> >> > > .nop is similar to .skip, except that it fills with NOPs.  Yes, it can be used

>> >> >> > > wherever .skip can be used.

>> >> >> >

>> >> >> > Given https://sourceware.org/ml/binutils/2018-02/msg00322.html I'm

>> >> >> > inclined to think the name of the directive should change.

>> >> >> > .skipnops perhaps?

>> >> >>

>> >> >>  Just `.nops' maybe?  There doesn't appear to be any matching mnemonic in

>> >> >> opcodes/.

>> >> >

>> >> > I'd be happy with that too.

>> >>

>> >> There is no guarantee that one of those NO_PSEUDO_DOT targets or the new

>> >> NO_PSEUDO_DOT target won't have an instruction called nops or skipnops in

>> >> the future.

>> >

>> > If a target adds an instruction like that, then the target will need

>> > to deal with it.  For example, as the spu target deals with "set" and

>> > "equ" which existed as directives before the spu defined them as

>> > instructions.

>> >

>> > In this case the use of "nop" as an instruction existed before you

>> > decided to define ".nop" as a directive, and lack of testing resulted

>> > in not discovering the NO_PSEUDO_DOT clash.  I suspect we wouldn't be

>> > having this conversation if you had run a full test suite regression,

>> > rather than just testing x86.  You yourself would have chosen

>> > something other than ".nop" as a directive!

>>

>> I would have chosen .nop and handled it for NO_PSEUDO_DOT targets.

>>

>> Here is a patch to rename .nop to .nops.  OK for master?

>>

>> --

>> H.J.

>

>> From eb81600ebb30f513732e395c524bcc9dca4de00a Mon Sep 17 00:00:00 2001

>> From: "H.J. Lu" <hjl.tools@gmail.com>

>> Date: Mon, 19 Feb 2018 16:53:28 -0800

>> Subject: [PATCH] Rename .nop directive to .nops

>>

>> Since directives of NO_PSEUDO_DOT targets don't have the leading '.'

>> and "nop" can be a valid instruction, rename .nop directive to .nops

>> to avoid conflict.

>>

>>       * NEWS: Rename .nop to .nops.

>>       * doc/as.texinfo: Likewise.

>>       * read.c (potable): Add "nops".  Remove "nop".

>>       (s_nop): Renamed to ...

>>       (s_nops): This.

>>       * read.h (s_nop): Renamed to ...

>>       (s_nops): This.

>>       * write.c (cvt_frag_to_fill): Rename .nop to .nops.

>>       (md_generate_nops): Likewise.

>>       (relax_segment): Likewise.

>>       * testsuite/gas/i386/nop-1.d: Updated.

>>       * 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.

>

> OK.  I think this is the best solution from a maintenance point of

> view.

>


Andrew, I am going to check in this patch.

-- 
H.J.
John Darrington Feb. 28, 2018, 7:38 p.m. | #46
On Mon, Feb 26, 2018 at 12:08:18PM -0800, H.J. Lu wrote:
     On Mon, Feb 26, 2018 at 11:17 AM, John Darrington
     <john@darrington.wattle.id.au> wrote:
     > On Mon, Feb 26, 2018 at 11:03:12AM -0800, H.J. Lu wrote:

     >      On Mon, Feb 26, 2018 at 11:00 AM, John Darrington

     >      <john@darrington.wattle.id.au> wrote:

     >      > I haven't read every post in this thread, nor do I pretend to understand all the

     >      > issues.

     >      >

     >      > But it this change seems to have the effect that any instruction called "NOP"

     >      > in the .s file are completely ignored :(

     >      >

     >      > Many cpus have  a NOP instruction. It's often used in realtime systems to

     >      > implement short delays.

     >

     >      Which target have you tried?

     >

     >

     > hcs12z

     >

     
     I got
     
     [hjl@gnu-cfl-1 gas]$ cat x.s
     nop
     [hjl@gnu-cfl-1 gas]$ ./as-new -o x.o x.s -m68hcs12
     [hjl@gnu-cfl-1 gas]$ ../binutils/objdump -dw x.o
     
     x.o:     file format elf32-m68hc12
     
     
     Disassembly of section .text:
     
     00000000 <.text>:
        0: a7          nop
     [hjl@gnu-cfl-1 gas]$
     
     Am I missing something?
     
After applying commit 8f065d3b4a814b5a69b3f39700fea7034c79c492dd
the problem seems to have been fixed.

Thanks.


-- 
Avoid eavesdropping.  Send strong encrypted email.
PGP Public key ID: 1024D/2DE827B3 
fingerprint = 8797 A26D 0854 2EAB 0285  A290 8A67 719C 2DE8 27B3
See http://sks-keyservers.net or any PGP keyserver for public key.

Patch

From f661ca327b7230f53eddcca2844894d62e1201d4 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                  | 377 ++++++++++++++++++++++++----------
 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    |   6 +
 gas/testsuite/gas/i386/nop-bad-1.s    |   8 +
 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, 798 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 8746d9ca75..bc8ee14dee 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -959,6 +959,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..c0741c78a9 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 md_relax_space_frag 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 md_relax_space_frag.  */
+  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 32a8b31d30..e9f96eeabd 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;
@@ -9402,6 +9428,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".  */
@@ -9411,12 +9440,41 @@  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"),
+			 (long) amount);
+	  /* 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.  */
@@ -9517,8 +9575,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)
@@ -10783,6 +10860,86 @@  s_bss (int ignore ATTRIBUTE_UNUSED)
 
 #endif
 
+/* Implement .nop directive.  */
+
+void
+s_nop (int ignore ATTRIBUTE_UNUSED)
+{
+  expressionS exp;
+  expressionS val;
+  offsetT max_single_nop_size;
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+#ifdef md_cons_align
+  md_cons_align (1);
+#endif
+
+  if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) == 0)
+    {
+      as_bad (_("unexpected .nop directive in section `%s'"),
+	      segment_name (now_seg));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  /* 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"),
+		  (long) val.X_add_number);
+	  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..33b8c58254
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-bad-1.l
@@ -0,0 +1,6 @@ 
+.*: Assembler messages:
+.*:2: Error: invalide single nop size: -2
+.*:3: Error: invalide single nop size: 20
+.*:8: Error: unexpected .nop directive in section `.data'
+.*:5: Warning: .space or .fill with negative value, ignored
+.*:4: Error: invalid .nop directive size: 600
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..81f81c01df
--- /dev/null
+++ b/gas/testsuite/gas/i386/nop-bad-1.s
@@ -0,0 +1,8 @@ 
+	.text
+        .nop 100, -2
+        .nop 100, 20
+        .nop 600
+        .nop -1
+
+	.data
+	.nop 6
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..39f5d08029
--- /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_PC32	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..3c508850e1
--- /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_PC32	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..bb35fbcd29
--- /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_PC32	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