public inbox for binutils-cvs@sourceware.org
 help / color / mirror / Atom feed
* [binutils-gdb] DesCGENization of the BPF binutils port
@ 2023-07-21 10:24 Jose E. Marchesi
  0 siblings, 0 replies; only message in thread
From: Jose E. Marchesi @ 2023-07-21 10:24 UTC (permalink / raw)
  To: bfd-cvs

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=d218e7fedc74d67837d2134120917f4ac877454c

commit d218e7fedc74d67837d2134120917f4ac877454c
Author: Jose E. Marchesi <jose.marchesi@oracle.com>
Date:   Sat Jul 15 00:50:14 2023 +0200

    DesCGENization of the BPF binutils port
    
    CGEN is cool, but the BPF architecture is simply too bizarre for it.
    
    The weird way of BPF to handle endianness in instruction encoding, the
    weird C-like alternative assembly syntax, the weird abuse of
    multi-byte (or infra-byte) instruction fields as opcodes, the unusual
    presence of opcodes beyond the first 32-bits of some instructions, are
    all examples of what makes it a PITA to continue using CGEN for this
    port.  The bpf.cpu file is becoming so complex and so nested with
    p-macros that it is very difficult to read, and quite challenging to
    update.  Also, every time we are forced to change something in CGEN to
    accommodate BPF requirements (which is often) we have to do extensive
    testing to make sure we do not break any other target using CGEN.
    
    This is getting un-maintenable.
    
    So I have decided to bite the bullet and revamp/rewrite the port so it
    no longer uses CGEN.  Overall, this involved:
    
    * To remove the cpu/bpf.{cpu,opc} descriptions.
    
    * To remove the CGEN generated files.
    
    * To replace the CGEN generated opcodes table with a new hand-written
      opcodes table for BPF.
    
    * To replace the CGEN generated disassembler wih a new disassembler
      that uses the new opcodes.
    
    * To replace the CGEN generated assembler with a new assembler that uses the
      new opcodes.
    
    * To replace the CGEN generated simulator with a new simulator that uses the
      new opcodes. [This is pushed in GDB in another patch.]
    
    * To adapt the build systems to the new situation.
    
    Additionally, this patch introduces some extensions and improvements:
    
    * A new BPF relocation BPF_RELOC_BPF_DISP16 plus corresponding ELF
      relocation R_BPF_GNU_64_16 are added to the BPF BFD port.  These
      relocations are used for section-relative 16-bit offsets used in
      load/store instructions.
    
    * The disassembler now has support for the "pseudo-c" assembly syntax of
      BPF.  What dialect to use when disassembling is controlled by a command
      line option.
    
    * The disassembler now has support for dumping instruction immediates in
      either octal, hexadecimal or decimal.  The used output base is controlled
      by a new command-line option.
    
    * The GAS BPF test suite has been re-structured and expanded in order to
      test the disassembler pseudoc syntax support.  Minor bugs have been also
      fixed there.  The assembler generic tests that were disabled for bpf-*-*
      targets due to the previous implementation of pseudoc syntax are now
      re-enabled.  Additional tests have been added to test the new features of
      the assembler.  .dump files are no longer used.
    
    * The linker BPF test suite has been adapted to the command line options
      used by the new disassembler.
    
    The result is very satisfactory.  This patchs adds 3448 lines of code
    and removes 10542 lines of code.
    
    Tested in:
    
    * Target bpf-unknown-none with 64-bit little-endian host and 32-bit
      little-endian host.
    
    * Target x86-64-linux-gnu with --enable-targets=all
    
    Note that I have not tested in a big-endian host yet.  I will do so
    once this lands upstream so I can use the GCC compiler farm.
    
    I have not included ChangeLog entries in this patch: these would be
    massive and not very useful, considering this is pretty much a rewrite
    of the port.  I beg the indulgence of the global maintainers.

Diff:
---
 bfd/bfd-in2.h                                |    1 +
 bfd/bpf-reloc.def                            |   15 +
 bfd/elf64-bpf.c                              |    2 +
 bfd/libbfd.h                                 |    1 +
 bfd/reloc.c                                  |    2 +
 cpu/bpf.cpu                                  |  855 ---------
 cpu/bpf.opc                                  |  191 --
 gas/config/tc-bpf.c                          | 2519 +++++++++-----------------
 gas/config/tc-bpf.h                          |    4 +-
 gas/configure                                |    1 -
 gas/configure.ac                             |    1 -
 gas/doc/c-bpf.texi                           |  790 +++++---
 gas/testsuite/gas/all/assign-bad-recursive.d |    1 -
 gas/testsuite/gas/all/eqv-dot.d              |    2 +-
 gas/testsuite/gas/all/gas.exp                |    5 +-
 gas/testsuite/gas/bpf/alu-be-pseudoc.d       |   66 +-
 gas/testsuite/gas/bpf/alu-be.d               |   68 +-
 gas/testsuite/gas/bpf/alu-be.dump            |   54 -
 gas/testsuite/gas/bpf/alu-pseudoc.d          |   68 +-
 gas/testsuite/gas/bpf/alu-pseudoc.s          |    8 +-
 gas/testsuite/gas/bpf/alu-xbpf.d             |   17 -
 gas/testsuite/gas/bpf/alu-xbpf.s             |   11 -
 gas/testsuite/gas/bpf/alu.d                  |   68 +-
 gas/testsuite/gas/bpf/alu.dump               |   54 -
 gas/testsuite/gas/bpf/alu.s                  |    8 +-
 gas/testsuite/gas/bpf/alu32-be-pseudoc.d     |   62 +-
 gas/testsuite/gas/bpf/alu32-be.d             |   64 +-
 gas/testsuite/gas/bpf/alu32-be.dump          |   60 -
 gas/testsuite/gas/bpf/alu32-pseudoc.d        |   62 +-
 gas/testsuite/gas/bpf/alu32-pseudoc.s        |   30 +-
 gas/testsuite/gas/bpf/alu32-xbpf.d           |   17 -
 gas/testsuite/gas/bpf/alu32-xbpf.s           |   11 -
 gas/testsuite/gas/bpf/alu32.d                |   62 +-
 gas/testsuite/gas/bpf/alu32.dump             |   60 -
 gas/testsuite/gas/bpf/alu32.s                |    8 +-
 gas/testsuite/gas/bpf/atomic-be-pseudoc.d    |   12 +
 gas/testsuite/gas/bpf/atomic-be.d            |    5 +-
 gas/testsuite/gas/bpf/atomic-pseudoc.d       |   15 +-
 gas/testsuite/gas/bpf/atomic-pseudoc.s       |    4 +-
 gas/testsuite/gas/bpf/atomic.d               |   13 +-
 gas/testsuite/gas/bpf/atomic.dump            |    7 -
 gas/testsuite/gas/bpf/atomic.s               |    4 +-
 gas/testsuite/gas/bpf/bpf.exp                |   30 +-
 gas/testsuite/gas/bpf/call-be.d              |    4 +-
 gas/testsuite/gas/bpf/call.d                 |    4 +-
 gas/testsuite/gas/bpf/data-be.d              |    2 +-
 gas/testsuite/gas/bpf/data.d                 |    2 +-
 gas/testsuite/gas/bpf/exit-be.d              |    4 +-
 gas/testsuite/gas/bpf/exit.d                 |    4 +-
 gas/testsuite/gas/bpf/indcall-1-pseudoc.d    |   24 +-
 gas/testsuite/gas/bpf/indcall-1.d            |   24 +-
 gas/testsuite/gas/bpf/indcall-1.dump         |   18 -
 gas/testsuite/gas/bpf/indcall-bad-1.l        |    5 -
 gas/testsuite/gas/bpf/indcall-bad-1.s        |    1 -
 gas/testsuite/gas/bpf/jump-be-pseudoc.d      |   32 +
 gas/testsuite/gas/bpf/jump-be.d              |    5 +-
 gas/testsuite/gas/bpf/jump-pseudoc.d         |   33 +-
 gas/testsuite/gas/bpf/jump.d                 |   33 +-
 gas/testsuite/gas/bpf/jump.dump              |   27 -
 gas/testsuite/gas/bpf/jump32-be-pseudoc.d    |   32 +
 gas/testsuite/gas/bpf/jump32-be.d            |   32 +
 gas/testsuite/gas/bpf/jump32-pseudoc.d       |   33 +-
 gas/testsuite/gas/bpf/jump32.d               |   33 +-
 gas/testsuite/gas/bpf/jump32.dump            |   27 -
 gas/testsuite/gas/bpf/lddw-be-pseudoc.d      |   19 +-
 gas/testsuite/gas/bpf/lddw-be.d              |   19 +-
 gas/testsuite/gas/bpf/lddw-be.dump           |   13 -
 gas/testsuite/gas/bpf/lddw-pseudoc.d         |   19 +-
 gas/testsuite/gas/bpf/lddw.d                 |   19 +-
 gas/testsuite/gas/bpf/lddw.dump              |   13 -
 gas/testsuite/gas/bpf/mem-be-pseudoc.d       |   30 +
 gas/testsuite/gas/bpf/mem-be.d               |   11 +-
 gas/testsuite/gas/bpf/mem-pseudoc.d          |   31 +-
 gas/testsuite/gas/bpf/mem-pseudoc.s          |    8 +-
 gas/testsuite/gas/bpf/mem.d                  |   31 +-
 gas/testsuite/gas/bpf/mem.dump               |   25 -
 gas/testsuite/gas/bpf/mem.s                  |    2 +-
 gas/testsuite/gas/bpf/pseudoc-normal-be.d    |  214 ---
 gas/testsuite/gas/bpf/pseudoc-normal.d       |  214 ---
 gas/testsuite/gas/bpf/pseudoc-normal.s       |  196 --
 gas/testsuite/gas/bpf/spacing-pseudoc.d      |   18 +
 gas/testsuite/gas/bpf/spacing-pseudoc.s      |    9 +
 include/dis-asm.h                            |    1 +
 include/elf/bpf.h                            |    1 +
 include/opcode/bpf.h                         |  306 ++++
 ld/testsuite/ld-bpf/call-1.d                 |    4 +-
 ld/testsuite/ld-bpf/call-2.d                 |    2 +-
 ld/testsuite/ld-bpf/reloc-insn-external-be.d |    4 +-
 ld/testsuite/ld-bpf/reloc-insn-external-le.d |    4 +-
 opcodes/Makefile.am                          |   16 -
 opcodes/Makefile.in                          |   20 -
 opcodes/bpf-asm.c                            |  590 ------
 opcodes/bpf-desc.c                           | 1939 --------------------
 opcodes/bpf-desc.h                           |  268 ---
 opcodes/bpf-dis.c                            |  795 +++-----
 opcodes/bpf-ibld.c                           |  961 ----------
 opcodes/bpf-opc.c                            | 2271 +++++------------------
 opcodes/bpf-opc.h                            |  166 --
 opcodes/configure                            |    2 +-
 opcodes/configure.ac                         |    2 +-
 opcodes/disassemble.c                        |   30 +-
 101 files changed, 3448 insertions(+), 10542 deletions(-)

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 1b9a801966c..ba7440c2768 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -7148,6 +7148,7 @@ assembler and not (currently) written to any object files.  */
 /* Linux eBPF relocations.  */
   BFD_RELOC_BPF_64,
   BFD_RELOC_BPF_DISP32,
+  BFD_RELOC_BPF_DISP16,
 
 /* Adapteva EPIPHANY - 8 bit signed pc-relative displacement  */
   BFD_RELOC_EPIPHANY_SIMM8,
diff --git a/bfd/bpf-reloc.def b/bfd/bpf-reloc.def
index b1be2eb66f6..31f761d291d 100644
--- a/bfd/bpf-reloc.def
+++ b/bfd/bpf-reloc.def
@@ -72,3 +72,18 @@
         0xffffffff,            /* src_mask */
         0xffffffff,            /* dst_mask */
         true)                  /* pcrel_offset */
+
+  /* 16-bit PC-relative address in load instructions.  */
+  BPF_HOWTO (R_BPF_GNU_64_16,      /* type */
+        0,                     /* rightshift */
+        2,                     /* size */
+        16,                    /* bitsize */
+        true,                  /* pc_relative */
+        16,                    /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        bpf_elf_generic_reloc, /* special_function */
+        "R_BPF_GNU_64_16",     /* name */
+        true,                  /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        true)                  /* pcrel_offset */
diff --git a/bfd/elf64-bpf.c b/bfd/elf64-bpf.c
index 65418d1d740..23ede4e5d96 100644
--- a/bfd/elf64-bpf.c
+++ b/bfd/elf64-bpf.c
@@ -89,6 +89,8 @@ bpf_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED,
       return &bpf_elf_howto_table[ (int) R_BPF_64_64_IDX];
     case BFD_RELOC_BPF_DISP32:
       return &bpf_elf_howto_table[ (int) R_BPF_64_32_IDX];
+    case BFD_RELOC_BPF_DISP16:
+      return &bpf_elf_howto_table[ (int) R_BPF_GNU_64_16_IDX];
 
     default:
       /* Pacify gcc -Wall.  */
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index d4fb3107597..5dbb0871607 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -3346,6 +3346,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_TILEGX_IMM8_Y1_TLS_ADD",
   "BFD_RELOC_BPF_64",
   "BFD_RELOC_BPF_DISP32",
+  "BFD_RELOC_BPF_DISP16",
   "BFD_RELOC_EPIPHANY_SIMM8",
   "BFD_RELOC_EPIPHANY_SIMM24",
   "BFD_RELOC_EPIPHANY_HIGH",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index fbc67ac7280..e71a510e26c 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -7753,6 +7753,8 @@ ENUM
   BFD_RELOC_BPF_64
 ENUMX
   BFD_RELOC_BPF_DISP32
+ENUMX
+  BFD_RELOC_BPF_DISP16
 ENUMDOC
   Linux eBPF relocations.
 
diff --git a/cpu/bpf.cpu b/cpu/bpf.cpu
deleted file mode 100644
index 2ae74fc4c18..00000000000
--- a/cpu/bpf.cpu
+++ /dev/null
@@ -1,855 +0,0 @@
-;; Linux BPF CPU description  -*- Scheme -*-
-;; Copyright (C) 2019 Free Software Foundation, Inc.
-;;
-;; Contributed by Oracle Inc.
-;;
-;; This file is part of the GNU Binutils and of GDB.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU General Public License as
-;; published by the Free Software Foundation; either version 3 of the
-;; License, or (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful, but
-;; WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;; General Public License for more details.
-;;
-;; You should have received a copy of the GNU General Public License
-;; along with this program; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
-;; 02110-1301, USA.
-
-;; This file contains a CGEN CPU description for the Linux kernel eBPF
-;; instruction set.  eBPF is documented in the linux kernel source
-;; tree.  See linux/Documentation/networking/filter.txt, and also the
-;; sources in the networking subsystem, notably
-;; linux/net/core/filter.c.
-
-(include "simplify.inc")
-
-(define-arch
-  (name bpf)
-  (comment "Linux kernel BPF")
-  (insn-lsb0? #t)
-  ;; XXX explain the default-alignment setting is for the simulator.
-  ;; It is confusing that the simulator follows the emulated memory
-  ;; access conventions for fetching instructions by pieces...
-  (default-alignment unaligned)
-  (machs bpf xbpf)
-  (isas ebpfle ebpfbe xbpfle xbpfbe))
-
-;;;; The ISAs
-
-;; Logically, eBPF comforms a single instruction set featuring two
-;; kind of instructions: 64-bit instructions and 128-bit instructions.
-;;
-;; The 64-bit instructions have the form:
-;;
-;;      code:8 regs:8 offset:16 imm:32
-;;
-;; Whereas the 128-bit instructions (at the moment there is only one
-;; of such instructions, lddw) have the form:
-;;
-;;      code:8 regs:8 offset:16 imm:32 unused:32 imm:32 
-;;
-;; In both formats `regs' is itself composed by two fields:
-;;
-;;      dst:4 src:4
-;;
-;; The ISA is supposed to be orthogonal to endianness: the endianness
-;; of the instruction fields follow the endianness of the host running
-;; the eBPF program, and that's all.  However, this is not entirely
-;; true.  The definition of an eBPF code in the Linux kernel is:
-;;
-;; struct bpf_insn {
-;;	__u8	code;		/* opcode */
-;;	__u8	dst_reg:4;	/* dest register */
-;;	__u8	src_reg:4;	/* source register */
-;;	__s16	off;		/* signed offset */
-;;	__s32	imm;		/* signed immediate constant */
-;; };
-;;
-;; Since the ordering of fields in C bitmaps is defined by the
-;; implementation, the impact of endianness in the encoding of eBPF
-;; instructions is effectively defined by GCC.  In particular, GCC
-;; places dst_reg before src_reg in little-endian code, and the other
-;; way around in big-endian code.
-;;
-;; So, in reality, eBPF comprises two instruction sets: one for
-;; little-endian with instructions like:
-;;
-;;   code:8 src:4 dst:4 offset:16 imm:32 [unused:32 imm:32]
-;;
-;; and another for big-endian with instructions like:
-;;
-;;   code:8 dst:4 src:4 offset:16 imm:32 [unused:32 imm:32]
-;;
-;; where `offset' and the immediate fields are encoded in
-;; little-endian and big-endian byte-order, respectively.
-
-(define-pmacro (define-bpf-isa x-endian)
-  (define-isa
-    (name (.sym ebpf x-endian))
-    (comment "The eBPF instruction set")
-    ;; Default length to record in ifields.  This is used in
-    ;; calculations involving bit numbers.
-    (default-insn-word-bitsize 64)
-    ;; Length of an unknown instruction.  Used by disassembly and by the
-    ;; simulator's invalid insn handler.
-    (default-insn-bitsize 64)
-    ;; Number of bits of insn that can be initially fetched.  This is
-    ;; the size of the smallest insn.
-    (base-insn-bitsize 64)))
-
-(define-bpf-isa le)
-(define-bpf-isa be)
-
-(define-pmacro (define-xbpf-isa x-endian)
-  (define-isa
-    (name (.sym xbpf x-endian))
-    (comment "The xBPF instruction set")
-    (default-insn-word-bitsize 64)
-    (default-insn-bitsize 64)
-    (base-insn-bitsize 64)))
-
-(define-xbpf-isa le)
-(define-xbpf-isa be)
-
-(define-pmacro all-isas () (ISA ebpfle,ebpfbe,xbpfle,xbpfbe))
-(define-pmacro xbpf-isas () (ISA xbpfle,xbpfbe))
-
-(define-pmacro (endian-isas x-endian)
-  ((ISA (.sym ebpf x-endian) (.sym xbpf x-endian))))
-
-;;;; Hardware Hierarchy
-
-;;
-;;         bpf            architecture
-;;          |
-;;        bpfbf           cpu-family
-;;      /       \
-;;     bpf     xbpf       machine
-;;      |       |
-;;   bpf-def  xbpf-def    model
-
-(define-cpu
-  (name bpfbf)
-  (comment "Linux kernel eBPF virtual CPU")
-  (insn-endian big)
-  (word-bitsize 64))
-
-(define-mach
-  (name bpf)
-  (comment "Linux eBPF")
-  (cpu bpfbf)
-  (isas ebpfle ebpfbe))
-
-(define-model
-  (name bpf-def)
-  (comment "Linux eBPF default model")
-  (mach bpf)
-  (unit u-exec "execution unit" ()
-    1 ; issue
-    1 ; done
-    () ; state
-    () ; inputs
-    () ; outputs
-    () ; profile action (default)
-    ))
-
-(define-mach
-  (name xbpf)
-  (comment "Experimental BPF")
-  (cpu bpfbf)
-  (isas ebpfle ebpfbe xbpfle xbpfbe))
-
-(define-model
-  (name xbpf-def)
-  (comment "xBPF default model")
-  (mach xbpf)
-  (unit u-exec "execution unit" ()
-    1 ; issue
-    1 ; done
-    () ; state
-    () ; inputs
-    () ; outputs
-    () ; profile action (default)
-    ))
-
-;;;; Hardware Elements
-
-;; eBPF programs can access 10 general-purpose registers which are
-;; 64-bit.
-
-(define-hardware
-  (name h-gpr)
-  (comment "General Purpose Registers")
-  (attrs all-isas (MACH bpf xbpf))
-  (type register DI (16))
-  (indices keyword "%"
-           ;; XXX the frame pointer fp is read-only, so it should
-           ;; go in a different hardware.
-           (;; ABI names.  Take priority when disassembling.
-            (r0 0) (r1 1) (r2 2) (r3 3) (r4 4) (r5 5) (r6 6)
-            (r7 7) (r8 8) (r9 9) (fp 10)
-            ;; Additional names recognized when assembling.
-            (r0 0) (r6 6) (r10 10))))
-
-;; The program counter.  CGEN requires it, even if it is not visible
-;; to eBPF programs.
-
-(define-hardware
-  (name h-pc)
-  (comment "program counter")
-  (attrs PC PROFILE all-isas)
-  (type pc UDI)
-  (get () (raw-reg h-pc))
-  (set (newval) (set (raw-reg h-pc) newval)))
-  
-;; A 64-bit h-sint to be used by the imm64 operand below.  XXX this
-;; shouldn't be needed, as h-sint is supposed to be able to hold
-;; 64-bit values.  However, in practice CGEN limits h-sint to 32 bits
-;; in 32-bit hosts.  To be fixed in CGEN.
-
-(dnh h-sint64 "signed 64-bit integer" (all-isas) (immediate DI)
-     () () ())
-
-;;;; The Instruction Sets
-
-;;; Fields and Opcodes
-
-;; Convenience macro to shorten the definition of the fields below.
-(define-pmacro (dwf x-name x-comment x-attrs
-                    x-word-offset x-word-length x-start x-length
-                    x-mode)
-  "Define a field including its containing word."
-  (define-ifield
-    (name x-name)
-    (comment x-comment)
-    (.splice attrs (.unsplice x-attrs))
-    (word-offset x-word-offset)
-    (word-length x-word-length)
-    (start x-start)
-    (length x-length)
-    (mode x-mode)))
-
-;; For arithmetic and jump instructions the 8-bit code field is
-;; subdivided in:
-;;
-;;  op-code:4 op-src:1 op-class:3
-
-(dwf f-op-code "eBPF opcode code" (all-isas) 0 8 7 4 UINT)
-(dwf f-op-src "eBPF opcode source" (all-isas) 0 8 3 1 UINT)
-(dwf f-op-class "eBPF opcode instruction class" (all-isas) 0 8 2 3 UINT)
-
-(define-normal-insn-enum insn-op-code-alu "eBPF instruction codes"
-  (all-isas) OP_CODE_ f-op-code
-  (;; Codes for OP_CLASS_ALU and OP_CLASS_ALU64
-   (ADD #x0) (SUB #x1) (MUL #x2) (DIV #x3) (OR #x4) (AND #x5)
-   (LSH #x6) (RSH #x7) (NEG #x8) (MOD #x9) (XOR #xa) (MOV #xb)
-   (ARSH #xc) (END #xd)
-   ;; xBPF-only: signed div, signed mod
-   (SDIV #xe) (SMOD #xf)
-   ;; Codes for OP_CLASS_JMP
-   (JA #x0) (JEQ #x1) (JGT #x2) (JGE #x3) (JSET #x4)
-   (JNE #x5) (JSGT #x6) (JSGE #x7) (CALL #x8) (EXIT #x9)
-   (JLT #xa) (JLE #xb) (JSLT #xc) (JSLE #xd)))
-
-(define-normal-insn-enum insn-op-src "eBPF instruction source"
-  (all-isas) OP_SRC_ f-op-src
-  ;; X => use `src' as source operand.
-  ;; K => use `imm32' as source operand.
-  ((K #b0) (X #b1)))
-
-(define-normal-insn-enum insn-op-class "eBPF instruction class"
-  (all-isas) OP_CLASS_ f-op-class
-  ((LD    #b000) (LDX   #b001) (ST    #b010) (STX   #b011)
-   (ALU   #b100) (JMP   #b101) (JMP32 #b110) (ALU64 #b111)))
-
-;; For load/store instructions, the 8-bit code field is subdivided in:
-;;
-;; op-mode:3 op-size:2 op-class:3
-
-(dwf f-op-mode "eBPF opcode mode" (all-isas) 0 8 7 3 UINT)
-(dwf f-op-size "eBPF opcode size" (all-isas) 0 8 4 2 UINT)
-
-(define-normal-insn-enum insn-op-mode "eBPF load/store instruction modes"
-  (all-isas) OP_MODE_ f-op-mode
-  ((IMM #b000) (ABS #b001) (IND #b010) (MEM #b011)
-   ;; #b100 and #b101 are used in classic BPF only, reserved in eBPF.
-   (XADD #b110)))
-
-(define-normal-insn-enum insn-op-size "eBPF load/store instruction sizes"
-  (all-isas) OP_SIZE_ f-op-size
-  ((W  #b00)   ;; Word:        4 byte
-   (H  #b01)   ;; Half-word:   2 byte
-   (B  #b10)   ;; Byte:        1 byte
-   (DW #b11))) ;; Double-word: 8 byte
-
-;; The fields for the source and destination registers are a bit
-;; tricky.  Due to the bizarre nibble swap between little-endian and
-;; big-endian ISAs we need to keep different variants of the fields.
-;;
-;; Note that f-regs is used in the format spec of instructions that do
-;; NOT use registers, where endianness is irrelevant i.e. f-regs is a
-;; constant 0 opcode.
-
-(dwf f-dstle "eBPF dst register field" ((ISA ebpfle xbpfle)) 8 8 3 4 UINT)
-(dwf f-srcle "eBPF source register field" ((ISA ebpfle xbpfle)) 8 8 7 4 UINT)
-
-(dwf f-dstbe "eBPF dst register field" ((ISA ebpfbe xbpfbe)) 8 8 7 4 UINT)
-(dwf f-srcbe "eBPF source register field" ((ISA ebpfbe xbpfbe)) 8 8 3 4 UINT)
-
-(dwf f-regs "eBPF registers field" (all-isas) 8 8 7 8 UINT)
-
-;; Finally, the fields for the immediates.
-;;
-;; The 16-bit offsets and 32-bit immediates do not present any special
-;; difficulty: we put them in their own instruction word so the
-;; byte-endianness will be properly applied.
-
-(dwf f-offset16 "eBPF offset field" (all-isas) 16 16 15 16 HI)
-(dwf f-imm32 "eBPF 32-bit immediate field" (all-isas) 32 32 31 32 INT)
-
-;; For the disjoint 64-bit signed immediate, however, we need to use a
-;; multi-ifield.
-
-(dwf f-imm64-a "eBPF 64-bit immediate a" (all-isas) 32 32 31 32 UINT)
-(dwf f-imm64-b "eBPF 64-bit immediate b" (all-isas) 64 32 31 32 UINT)
-(dwf f-imm64-c "eBPF 64-bit immediate c" (all-isas) 96 32 31 32 UINT)
-
-(define-multi-ifield
-  (name f-imm64)
-  (comment "eBPF 64-bit immediate field")
-  (attrs all-isas)
-  (mode DI)
-  (subfields f-imm64-a f-imm64-b f-imm64-c)
-  (insert (sequence ()
-                    (set (ifield f-imm64-b) (const 0))
-                    (set (ifield f-imm64-c) (srl (ifield f-imm64) (const 32)))
-                    (set (ifield f-imm64-a) (and (ifield f-imm64) (const #xffffffff)))))
-  (extract (sequence ()
-                     (set (ifield f-imm64)
-                          (or (sll UDI (zext UDI (ifield f-imm64-c)) (const 32))
-                              (zext UDI (ifield f-imm64-a)))))))
-
-;;; Operands
-
-;; A couple of source and destination register operands are defined
-;; for each ISA: ebpfle and ebpfbe.
-
-(dno dstle "destination register" ((ISA ebpfle xbpfle)) h-gpr f-dstle)
-(dno srcle "source register" ((ISA ebpfle xbpfle)) h-gpr f-srcle)
-
-(dno dstbe "destination register" ((ISA ebpfbe xbpfbe)) h-gpr f-dstbe)
-(dno srcbe "source register" ((ISA ebpfbe xbpfbe)) h-gpr f-srcbe)
-
-;; Jump instructions have a 16-bit PC-relative address.
-;; CALL instructions have a 32-bit PC-relative address.
-
-(dno disp16 "16-bit PC-relative address" (all-isas PCREL-ADDR) h-sint
-     f-offset16)
-(dno disp32 "32-bit PC-relative address" (all-isas PCREL-ADDR) h-sint
-     f-imm32)
-
-;; Immediate operands in eBPF are signed, and we want the disassembler
-;; to print negative values in a sane way.  Therefore we use the macro
-;; below to register a printer, which is itself defined as a C
-;; function in bpf.opc.
-
-;; define-normal-signed-immediate-operand
-(define-pmacro (dnsio x-name x-comment x-attrs x-type x-index)
-  (define-operand
-    (name x-name)
-    (comment x-comment)
-    (.splice attrs (.unsplice x-attrs))
-    (type x-type)
-    (index x-index)
-    (handlers (print "immediate"))))
-
-(dnsio imm32 "32-bit immediate" (all-isas) h-sint f-imm32)
-(dnsio offset16 "16-bit offset" (all-isas) h-sint f-offset16)
-
-;; The 64-bit immediate cannot use the default
-;; cgen_parse_signed_integer, because it assumes operands are at much
-;; 32-bit wide.  Use our own.
-
-(define-operand
-  (name imm64)
-  (comment "64-bit immediate")
-  (attrs all-isas)
-  (type h-sint64)
-  (index f-imm64)
-  (handlers (parse "imm64") (print "immediate")))
-
-;; The endle/endbe instructions take an operand to specify the word
-;; width in endianness conversions.  We use both a parser and printer,
-;; which are defined as C functions in bpf.opc.
-
-(define-operand
-  (name endsize)
-  (comment "endianness size immediate: 16, 32 or 64")
-  (attrs all-isas)
-  (type h-uint)
-  (index f-imm32)
-  (handlers (parse "endsize") (print "endsize")))
-
-;;; ALU instructions
-
-;; For each opcode in insn-op-code-alu representing and integer
-;; arithmetic instruction (ADD, SUB, etc) we define a bunch of
-;; instruction variants:
-;;
-;;   ADD[32]{i,r}le for the little-endian ISA
-;;   ADD[32]{i,r}be for the big-endian ISA
-;;
-;; The `i' variants perform `dst OP imm32 -> dst' operations.
-;; The `r' variants perform `dst OP src -> dst' operations.
-;;
-;; The variants with 32 in their name are of ALU class.  Otherwise
-;; they are ALU64 class.
-
-(define-pmacro (define-alu-insn-un x-basename x-suffix x-op-class x-op-code
-                 x-endian x-mode x-semop)
-  (dni (.sym x-basename x-suffix x-endian)
-       (.str x-basename x-suffix)
-       (endian-isas x-endian)
-       (.str x-basename x-suffix " $dst" x-endian)
-       (+ (f-imm32 0) (f-offset16 0) ((.sym f-src x-endian) 0) (.sym dst x-endian)
-          x-op-class OP_SRC_K x-op-code)
-       (set x-mode (.sym dst x-endian) (x-semop x-mode (.sym dst x-endian)))
-       ()))
-
-(define-pmacro (define-alu-insn-bin x-basename x-suffix x-op-class x-op-code
-                 x-endian x-mode x-semop x-isas)
-  (begin
-    ;; dst = dst OP immediate
-    (dni (.sym x-basename x-suffix "i" x-endian)
-         (.str x-basename x-suffix " immediate")
-         (.splice (.unsplice x-isas))
-         (.str x-basename x-suffix " $dst" x-endian ",$imm32")
-         (+ imm32 (f-offset16 0) ((.sym f-src x-endian) 0) (.sym dst x-endian)
-            x-op-class OP_SRC_K x-op-code)
-         (set x-mode (.sym dst x-endian) (x-semop x-mode (.sym dst x-endian) imm32))
-         ())
-    ;; dst = dst OP src
-    (dni (.sym x-basename x-suffix "r" x-endian)
-         (.str x-basename x-suffix " register")
-         (.splice (.unsplice x-isas))
-         (.str x-basename x-suffix " $dst" x-endian ",$src" x-endian)
-         (+ (f-imm32 0) (f-offset16 0) (.sym src x-endian) (.sym dst x-endian)
-            x-op-class OP_SRC_X x-op-code)
-         (set x-mode (.sym dst x-endian)
-                      (x-semop x-mode (.sym dst x-endian) (.sym src x-endian)))
-         ())))
-
-(define-pmacro (define-alu-insn-mov x-basename x-suffix x-op-class x-op-code
-                 x-endian x-mode)
-  (begin
-    (dni (.sym mov x-suffix "i" x-endian)
-         (.str mov x-suffix " immediate")
-         (endian-isas x-endian)
-         (.str x-basename x-suffix " $dst" x-endian ",$imm32")
-         (+ imm32 (f-offset16 0) ((.sym f-src x-endian) 0) (.sym dst x-endian)
-            x-op-class OP_SRC_K x-op-code)
-         (set x-mode (.sym dst x-endian) imm32)
-         ())
-    (dni (.sym mov x-suffix "r" x-endian)
-         (.str mov x-suffix " register")
-         (endian-isas x-endian)
-         (.str x-basename x-suffix " $dst" x-endian ",$src" x-endian)
-         (+ (f-imm32 0) (f-offset16 0) (.sym src x-endian) (.sym dst x-endian)
-            x-op-class OP_SRC_X x-op-code)
-         (set x-mode (.sym dst x-endian) (.sym src x-endian))
-         ())))
-
-
-;; Unary ALU instructions (neg)
-(define-pmacro (daiu x-basename x-op-code x-endian x-semop)
-  (begin
-    (define-alu-insn-un x-basename "" OP_CLASS_ALU64 x-op-code x-endian DI x-semop)
-    (define-alu-insn-un x-basename "32" OP_CLASS_ALU x-op-code x-endian USI x-semop)))
-
-;; Binary ALU instructions (all the others)
-;; For ALU32: DST = (u32) DST OP (u32) SRC is correct semantics
-(define-pmacro (daib x-basename x-op-code x-endian x-semop x-isas)
-  (begin
-    (define-alu-insn-bin x-basename "" OP_CLASS_ALU64 x-op-code x-endian DI x-semop x-isas)
-    (define-alu-insn-bin x-basename "32" OP_CLASS_ALU x-op-code x-endian USI x-semop x-isas)))
-
-;; Move ALU instructions (mov)
-(define-pmacro (daim x-basename x-op-code x-endian)
-  (begin
-    (define-alu-insn-mov x-basename "" OP_CLASS_ALU64 x-op-code x-endian DI)
-    (define-alu-insn-mov x-basename "32" OP_CLASS_ALU x-op-code x-endian USI)))
-
-(define-pmacro (define-alu-instructions x-endian)
-  (begin
-    (daib add OP_CODE_ADD x-endian add (endian-isas x-endian))
-    (daib sub OP_CODE_SUB x-endian sub (endian-isas x-endian))
-    (daib mul OP_CODE_MUL x-endian mul (endian-isas x-endian))
-    (daib div OP_CODE_DIV x-endian udiv (endian-isas x-endian))
-    (daib or  OP_CODE_OR x-endian or (endian-isas x-endian))
-    (daib and OP_CODE_AND x-endian and (endian-isas x-endian))
-    (daib lsh OP_CODE_LSH x-endian sll (endian-isas x-endian))
-    (daib rsh OP_CODE_RSH x-endian srl (endian-isas x-endian))
-    (daib mod OP_CODE_MOD x-endian umod (endian-isas x-endian))
-    (daib xor OP_CODE_XOR x-endian xor (endian-isas x-endian))
-    (daib arsh OP_CODE_ARSH x-endian sra (endian-isas x-endian))
-    (daib sdiv OP_CODE_SDIV x-endian div ((ISA (.sym xbpf x-endian))))
-    (daib smod OP_CODE_SMOD x-endian mod ((ISA (.sym xbpf x-endian))))
-    (daiu neg OP_CODE_NEG x-endian neg)
-    (daim mov OP_CODE_MOV x-endian)))
-
-(define-alu-instructions le)
-(define-alu-instructions be)
-
-;;; Endianness conversion instructions
-
-;; The endianness conversion instructions come in several variants:
-;;
-;;  END{le,be}le for the little-endian ISA
-;;  END{le,be}be for the big-endian ISA
-;;
-;; Please do not be confused by the repeated `be' and `le' here.  Each
-;; ISA has both endle and endbe instructions.  It is the disposition
-;; of the source and destination register fields that change between
-;; ISAs, not the semantics of the instructions themselves (see section
-;; "The ISAs" above in this very file.)
-
-(define-pmacro (define-endian-insn x-suffix x-op-src x-endian)
-  (dni (.sym "end" x-suffix x-endian)
-       (.str "end" x-suffix " register")
-       (endian-isas x-endian)
-       (.str "end" x-suffix " $dst" x-endian ",$endsize")
-       (+  (f-offset16 0) ((.sym f-src x-endian) 0) (.sym dst x-endian) endsize
-           OP_CLASS_ALU x-op-src OP_CODE_END)
-       (set (.sym dst x-endian)
-            (c-call DI (.str "bpfbf_end" x-suffix) (.sym dst x-endian) endsize))
-       ()))
-
-(define-endian-insn "le" OP_SRC_K le)
-(define-endian-insn "be" OP_SRC_X le)
-(define-endian-insn "le" OP_SRC_K be)
-(define-endian-insn "be" OP_SRC_X be)
-
-;;; Load/Store instructions
-
-;; The lddw instruction takes a 64-bit immediate as an operand.  Since
-;; this instruction also takes a `dst' operand, we need to define a
-;; variant for each ISA:
-;;
-;;  LDDWle for the little-endian ISA
-;;  LDDWbe for the big-endian ISA  
-
-(define-pmacro (define-lddw x-endian)
-  (dni (.sym lddw x-endian)
-       (.str "lddw" x-endian)
-       (endian-isas x-endian)
-       (.str "lddw $dst" x-endian ",$imm64")
-       (+ imm64 (f-offset16 0) ((.sym f-src x-endian) 0)
-          (.sym dst x-endian)
-          OP_CLASS_LD OP_SIZE_DW OP_MODE_IMM)
-       (set DI (.sym dst x-endian) imm64)
-       ()))
-
-(define-lddw le)
-(define-lddw be)
-
-;; The absolute load instructions are non-generic loads designed to be
-;; used in socket filters.  They come in several variants:
-;;
-;; LDABS{w,h,b,dw}
-
-(define-pmacro (dlabs x-suffix x-size x-smode)
-  (dni (.sym "ldabs" x-suffix)
-       (.str "ldabs" x-suffix)
-       (all-isas)
-       (.str "ldabs" x-suffix " $imm32")
-       (+ imm32 (f-offset16 0) (f-regs 0)
-          OP_CLASS_LD OP_MODE_ABS (.sym OP_SIZE_ x-size))
-       (set x-smode
-            (reg x-smode h-gpr 0)
-            (mem x-smode
-                 (add DI
-                      (mem DI
-                           (add DI
-                                (reg DI h-gpr 6) ;; Pointer to struct sk_buff
-                                (c-call "bpfbf_skb_data_offset")))
-                      imm32)))
-       ;; XXX this clobbers R1-R5
-       ()))
-
-(dlabs "w" W SI)
-(dlabs "h" H HI)
-(dlabs "b" B QI)
-(dlabs "dw" DW DI)
-
-;; The indirect load instructions are non-generic loads designed to be
-;; used in socket filters.  They come in several variants:
-;;
-;; LDIND{w,h,b,dw}le for the little-endian ISA
-;; LDIND[w,h,b,dw}be for the big-endian ISA
-
-(define-pmacro (dlind x-suffix x-size x-endian x-smode)
-  (dni (.sym "ldind" x-suffix x-endian)
-       (.str "ldind" x-suffix)
-       (endian-isas x-endian)
-       (.str "ldind" x-suffix " $src" x-endian ",$imm32")
-       (+ imm32 (f-offset16 0) ((.sym f-dst x-endian) 0) (.sym src x-endian)
-          OP_CLASS_LD OP_MODE_IND (.sym OP_SIZE_ x-size))
-       (set x-smode
-            (reg x-smode h-gpr 0)
-            (mem x-smode
-                 (add DI
-                      (mem DI
-                           (add DI
-                                (reg DI h-gpr 6) ;; Pointer to struct sk_buff
-                                (c-call "bpfbf_skb_data_offset")))
-                      (add DI
-                           (.sym src x-endian)
-                           imm32))))
-       ;; XXX this clobbers R1-R5
-       ()))
-
-(define-pmacro (define-ldind x-endian)
-  (begin    
-    (dlind "w" W x-endian SI)
-    (dlind "h" H x-endian HI)
-    (dlind "b" B x-endian QI)
-    (dlind "dw" DW x-endian DI)))
-
-(define-ldind le)
-(define-ldind be)
-
-;; Generic load and store instructions are provided for several word
-;; sizes.  They come in several variants:
-;;
-;;  LDX{b,h,w,dw}le, STX{b,h,w,dw}le for the little-endian ISA
-;;
-;;  LDX{b,h,w,dw}be, STX{b,h,w,dw}be for the big-endian ISA
-;;
-;; Loads operate on [$SRC+-OFFSET] -> $DST
-;; Stores operate on $SRC -> [$DST+-OFFSET]
-
-(define-pmacro (dxli x-basename x-suffix x-size x-endian x-mode)
-  (dni (.sym x-basename x-suffix x-endian)
-       (.str x-basename x-suffix)
-       (endian-isas x-endian)
-       (.str x-basename x-suffix " $dst" x-endian ",[$src" x-endian "+$offset16]")
-       (+ (f-imm32 0) offset16 (.sym src x-endian) (.sym dst x-endian)
-          OP_CLASS_LDX (.sym OP_SIZE_ x-size) OP_MODE_MEM)
-       (set x-mode
-            (.sym dst x-endian)
-            (mem x-mode (add DI (.sym src x-endian) offset16)))
-       ()))
-
-(define-pmacro (dxsi x-basename x-suffix x-size x-endian x-mode)
-  (dni (.sym x-basename x-suffix x-endian)
-       (.str x-basename x-suffix)
-       (endian-isas x-endian)
-       (.str x-basename x-suffix " [$dst" x-endian "+$offset16],$src" x-endian)
-       (+ (f-imm32 0) offset16 (.sym src x-endian) (.sym dst x-endian)
-          OP_CLASS_STX (.sym OP_SIZE_ x-size) OP_MODE_MEM)
-       (set x-mode
-            (mem x-mode (add DI (.sym dst x-endian) offset16))
-            (.sym src x-endian)) ;; XXX address is section-relative
-       ()))
-
-(define-pmacro (define-ldstx-insns x-endian)
-  (begin
-    (dxli "ldx" "w" W x-endian SI)
-    (dxli "ldx" "h" H x-endian HI)
-    (dxli "ldx" "b" B x-endian QI)
-    (dxli "ldx" "dw" DW x-endian DI)
-
-    (dxsi "stx" "w" W x-endian SI)
-    (dxsi "stx" "h" H x-endian HI)
-    (dxsi "stx" "b" B x-endian QI)
-    (dxsi "stx" "dw" DW x-endian DI)))
-
-(define-ldstx-insns le)
-(define-ldstx-insns be)
-
-;; Generic store instructions of the form IMM32 -> [$DST+OFFSET] are
-;; provided in several variants:
-;;
-;;  ST{b,h,w,dw}le for the little-endian ISA
-;;  ST{b,h,w,dw}be for the big-endian ISA
-
-(define-pmacro (dsti x-suffix x-size x-endian x-mode)
-  (dni (.sym "st" x-suffix x-endian)
-       (.str "st" x-suffix)
-       (endian-isas x-endian)
-       (.str "st" x-suffix " [$dst" x-endian "+$offset16],$imm32")
-       (+ imm32 offset16 ((.sym f-src x-endian) 0) (.sym dst x-endian)
-          OP_CLASS_ST (.sym OP_SIZE_ x-size) OP_MODE_MEM)
-       (set x-mode
-            (mem x-mode (add DI (.sym dst x-endian) offset16))
-            imm32) ;; XXX address is section-relative
-       ()))
-
-(define-pmacro (define-st-insns x-endian)
-  (begin
-    (dsti "b" B x-endian QI)
-    (dsti "h" H x-endian HI)
-    (dsti "w" W x-endian SI)
-    (dsti "dw" DW x-endian DI)))
-
-(define-st-insns le)
-(define-st-insns be)
-
-;;; Jump instructions
-
-;; Compare-and-jump instructions, on the other hand, make use of
-;; registers.  Therefore, we need to define several variants in both
-;; ISAs:
-;;
-;;   J{eq,gt,ge,lt,le,set,ne,sgt,sge,slt,sle}[32]{i,r}le for the
-;;   little-endian ISA.
-;;   J{eq,gt,ge,lt,le,set,ne.sgt,sge,slt,sle}[32]{i,r}be for the
-;;   big-endian ISA.
-
-(define-pmacro (define-cond-jump-insn x-cond x-suffix x-op-class x-op-code x-endian x-mode x-semop)
-  (begin
-    (dni (.sym j x-cond x-suffix i x-endian)
-         (.str j x-cond x-suffix " i")
-         (endian-isas x-endian)
-         (.str "j" x-cond x-suffix " $dst" x-endian ",$imm32,$disp16")
-         (+ imm32 disp16 ((.sym f-src x-endian) 0) (.sym dst x-endian)
-            x-op-class OP_SRC_K (.sym OP_CODE_ x-op-code))
-         (if VOID (x-semop x-mode (.sym dst x-endian) imm32)
-             (set DI
-                  (reg DI h-pc) (add DI (reg DI h-pc)
-                                     (mul DI (add HI disp16 1) 8))))
-         ())
-    (dni (.sym j x-cond x-suffix r x-endian)
-         (.str j x-cond x-suffix " r")
-         (endian-isas x-endian)
-         (.str "j" x-cond x-suffix " $dst" x-endian ",$src" x-endian ",$disp16")
-         (+ (f-imm32 0) disp16 (.sym src x-endian) (.sym dst x-endian)
-            x-op-class OP_SRC_X (.sym OP_CODE_ x-op-code))
-         (if VOID (x-semop x-mode (.sym dst x-endian) (.sym src x-endian))
-             (set DI
-                  (reg DI h-pc) (add DI (reg DI h-pc)
-                                     (mul DI (add HI disp16 1) 8))))
-         ())))
-
-(define-pmacro (dcji x-cond x-op-code x-endian x-semop)
-  (begin
-    (define-cond-jump-insn x-cond "" OP_CLASS_JMP x-op-code x-endian DI x-semop)
-    (define-cond-jump-insn x-cond "32" OP_CLASS_JMP32 x-op-code x-endian SI x-semop )))
-
-(define-pmacro (define-condjump-insns x-endian)
-  (begin
-    (dcji "eq" JEQ x-endian eq)
-    (dcji "gt" JGT x-endian gtu)
-    (dcji "ge" JGE x-endian geu)
-    (dcji "lt" JLT x-endian ltu)
-    (dcji "le" JLE x-endian leu)
-    (dcji "set" JSET x-endian and)
-    (dcji "ne" JNE x-endian ne)
-    (dcji "sgt" JSGT x-endian gt)
-    (dcji "sge" JSGE x-endian ge)
-    (dcji "slt" JSLT x-endian lt)
-    (dcji "sle" JSLE x-endian le)))
-
-(define-condjump-insns le)
-(define-condjump-insns be)
-
-;; The `call' instruction doesn't make use of registers, but the
-;; semantic routine should have access to the src register in order to
-;; properly interpret the meaning of disp32.  Therefore we need one
-;; version per ISA.
-
-(define-pmacro (define-call-insn x-endian)
-  (dni (.sym call x-endian)
-       "call"
-       (endian-isas x-endian)
-       "call $disp32"
-       (+ disp32 (f-offset16 0) (.sym src x-endian) ((.sym f-dst x-endian) 0)
-          OP_CLASS_JMP OP_SRC_K OP_CODE_CALL)
-       (c-call VOID
-               "bpfbf_call" disp32 (ifield (.sym f-src x-endian)))
-       ()))
-
-(define-call-insn le)
-(define-call-insn be)
-
-(define-pmacro (define-callr-insn x-endian)
-  (dni (.sym callr x-endian)
-       "callr"
-       ((ISA (.sym xbpf x-endian)))
-       (.str "call $dst" x-endian)
-       (+ (f-imm32 0) (f-offset16 0) ((.sym f-src x-endian) 0) (.sym dst x-endian)
-          OP_CLASS_JMP OP_SRC_X OP_CODE_CALL)
-       (c-call VOID
-               "bpfbf_callr" (ifield (.sym f-dst x-endian)))
-       ()))
-
-(define-callr-insn le)
-(define-callr-insn be)
-
-;; The jump-always and `exit' instructions dont make use of either
-;; source nor destination registers, so only one variant per
-;; instruction is defined.
-
-(dni ja "ja" (all-isas) "ja $disp16"
-     (+ (f-imm32 0) disp16 (f-regs 0)
-        OP_CLASS_JMP OP_SRC_K OP_CODE_JA)
-     (set DI (reg DI h-pc) (add DI (reg DI h-pc)
-                                (mul DI (add HI disp16 1) 8)))
-     ())
-
-(dni "exit" "exit" (all-isas) "exit"
-     (+ (f-imm32 0) (f-offset16 0) (f-regs 0)
-        OP_CLASS_JMP (f-op-src 0) OP_CODE_EXIT)
-     (c-call VOID "bpfbf_exit")
-     ())
-
-;;; Atomic instructions
-
-;; The atomic exchange-and-add instructions come in two flavors: one
-;; for swapping 64-bit quantities and another for 32-bit quantities.
-
-(define-pmacro (sem-exchange-and-add x-endian x-mode)
-  (sequence VOID ((x-mode tmp))
-            ;; XXX acquire lock in simulator...  as a hardware element?
-            (set x-mode tmp (mem x-mode (add DI (.sym dst x-endian) offset16)))
-            (set x-mode
-                 (mem x-mode (add DI (.sym dst x-endian) offset16))
-                 (add x-mode tmp (.sym src x-endian)))))
-
-(define-pmacro (define-atomic-insns x-endian)
-  (begin
-    (dni (.str "xadddw" x-endian)
-         "xadddw"
-         (endian-isas x-endian)
-         (.str "xadddw [$dst" x-endian "+$offset16],$src" x-endian)
-         (+ (f-imm32 0) (.sym src x-endian) (.sym dst x-endian)
-            offset16 OP_MODE_XADD OP_SIZE_DW OP_CLASS_STX)
-         (sem-exchange-and-add x-endian DI)
-         ())
-    (dni (.str "xaddw" x-endian)
-         "xaddw"
-         (endian-isas x-endian)
-         (.str "xaddw [$dst" x-endian "+$offset16],$src" x-endian)
-         (+ (f-imm32 0) (.sym src x-endian) (.sym dst x-endian)
-            offset16 OP_MODE_XADD OP_SIZE_W OP_CLASS_STX)
-         (sem-exchange-and-add x-endian SI)
-         ())))
-
-(define-atomic-insns le)
-(define-atomic-insns be)
-
-;;; Breakpoint instruction
-
-;; The brkpt instruction is used by the BPF simulator and it doesn't
-;; really belong to the eBPF instruction set.
-
-(dni "brkpt" "brkpt" (all-isas)  "brkpt"
-     (+ (f-imm32 0) (f-offset16 0) (f-regs 0)
-        OP_CLASS_ALU OP_SRC_X OP_CODE_NEG)
-     (c-call VOID "bpfbf_breakpoint")
-     ())
diff --git a/cpu/bpf.opc b/cpu/bpf.opc
deleted file mode 100644
index e70ee04841d..00000000000
--- a/cpu/bpf.opc
+++ /dev/null
@@ -1,191 +0,0 @@
-/* EBPF opcode support.  -*- c -*-
-
-   Copyright (C) 2019 Free Software Foundation, Inc.
-
-   Contributed by Oracle, Inc.
-
-   This file is part of the GNU Binutils and of GDB.
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
-   MA 02110-1301, USA.  */
-
-/*
-   Each section is delimited with start and end markers.
-
-   <arch>-opc.h additions use: "-- opc.h"
-   <arch>-opc.c additions use: "-- opc.c"
-   <arch>-asm.c additions use: "-- asm.c"
-   <arch>-dis.c additions use: "-- dis.c"
-   <arch>-ibd.h additions use: "-- ibd.h".  */
-\f
-/* -- opc.h */
-
-#undef CGEN_DIS_HASH_SIZE
-#define CGEN_DIS_HASH_SIZE 1
-
-#undef CGEN_DIS_HASH
-#define CGEN_DIS_HASH(buffer, value) 0
-
-/* Allows reason codes to be output when assembler errors occur.  */
-#define CGEN_VERBOSE_ASSEMBLER_ERRORS
-
-#define CGEN_VALIDATE_INSN_SUPPORTED
-extern int bpf_cgen_insn_supported (CGEN_CPU_DESC, const CGEN_INSN *);
-
-\f
-/* -- opc.c */
-\f
-/* -- asm.c */
-
-/* Parse a signed 64-bit immediate.  */
-
-static const char *
-parse_imm64 (CGEN_CPU_DESC cd,
-             const char **strp,
-             int opindex,
-             int64_t *valuep)
-{
-  bfd_vma value;
-  enum cgen_parse_operand_result result;
-  const char *errmsg;
-
-  errmsg = (* cd->parse_operand_fn)
-    (cd, CGEN_PARSE_OPERAND_INTEGER, strp, opindex, BFD_RELOC_NONE,
-     &result, &value);
-  if (!errmsg)
-    *valuep = value;
-
-  return errmsg;
-}
-
-/* Endianness size operands are integer immediates whose values can be
-   16, 32 or 64.  */
-
-static const char *
-parse_endsize (CGEN_CPU_DESC cd,
-               const char **strp,
-               int opindex,
-               unsigned long *valuep)
-{
-  const char *errmsg;
-
-  errmsg = cgen_parse_unsigned_integer (cd, strp, opindex, valuep);
-  if (errmsg)
-    return errmsg;
-
-  switch (*valuep)
-    {
-    case 16:
-    case 32:
-    case 64:
-      break;
-    default:
-      return _("expected 16, 32 or 64 in");
-    }
-
-  return NULL;
-}
-
-/* Special check to ensure that the right instruction variant is used
-   for the given endianness induced by the ISA selected in the CPU.
-   See bpf.cpu for a discussion on how eBPF is really two instruction
-   sets.  */
-
-int
-bpf_cgen_insn_supported (CGEN_CPU_DESC cd, const CGEN_INSN *insn)
-{
-  CGEN_BITSET isas = CGEN_INSN_BITSET_ATTR_VALUE (insn, CGEN_INSN_ISA);
-
-  return cgen_bitset_intersect_p (&isas, cd->isas);
-}
-
-\f
-/* -- dis.c */
-
-/* We need to customize the disassembler a bit:
-   - Use 8 bytes per line by default.
-*/
-
-#define CGEN_PRINT_INSN bpf_print_insn
-
-static int
-bpf_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info)
-{
-  bfd_byte buf[CGEN_MAX_INSN_SIZE];
-  int buflen;
-  int status;
-
-  info->bytes_per_chunk = 1;
-  info->bytes_per_line = 8;
-
-  /* Attempt to read the base part of the insn.  */
-  buflen = cd->base_insn_bitsize / 8;
-  status = (*info->read_memory_func) (pc, buf, buflen, info);
-
-  /* Try again with the minimum part, if min < base.  */
-  if (status != 0 && (cd->min_insn_bitsize < cd->base_insn_bitsize))
-    {
-      buflen = cd->min_insn_bitsize / 8;
-      status = (*info->read_memory_func) (pc, buf, buflen, info);
-    }
-
-  if (status != 0)
-    {
-      (*info->memory_error_func) (status, pc, info);
-      return -1;
-    }
-
-  return print_insn (cd, pc, info, buf, buflen);
-}
-
-/* Signed immediates should be printed in hexadecimal.  */
-
-static void
-print_immediate (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
-                 void *dis_info,
-                 int64_t value,
-                 unsigned int attrs ATTRIBUTE_UNUSED,
-                 bfd_vma pc ATTRIBUTE_UNUSED,
-                 int length ATTRIBUTE_UNUSED)
-{
-  disassemble_info *info = (disassemble_info *) dis_info;
-
-  if (value <= 9)
-    (*info->fprintf_func) (info->stream, "%" PRId64, value);
-  else
-    (*info->fprintf_func) (info->stream, "%#" PRIx64, value);
-
-  /* This is to avoid -Wunused-function for print_normal.  */
-  if (0)
-    print_normal (cd, dis_info, value, attrs, pc, length);
-}
-
-/* Endianness bit sizes should be printed in decimal.  */
-
-static void
-print_endsize (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
-               void *dis_info,
-               unsigned long value,
-               unsigned int attrs ATTRIBUTE_UNUSED,
-               bfd_vma pc ATTRIBUTE_UNUSED,
-               int length ATTRIBUTE_UNUSED)
-{
-  disassemble_info *info = (disassemble_info *) dis_info;
-  (*info->fprintf_func) (info->stream, "%lu", value);
-}
-
-\f
-/* -- */
-
diff --git a/gas/config/tc-bpf.c b/gas/config/tc-bpf.c
index 3b86f9c89cb..7a54faccb59 100644
--- a/gas/config/tc-bpf.c
+++ b/gas/config/tc-bpf.c
@@ -22,42 +22,42 @@
 #include "as.h"
 #include "subsegs.h"
 #include "symcat.h"
-#include "opcodes/bpf-desc.h"
-#include "opcodes/bpf-opc.h"
-#include "cgen.h"
+#include "opcode/bpf.h"
 #include "elf/common.h"
 #include "elf/bpf.h"
 #include "dwarf2dbg.h"
+#include "libiberty.h"
 #include <ctype.h>
 
+/* Data structure representing a parsed BPF instruction.  */
+
+struct bpf_insn
+{
+  int size; /* Instruction size in bytes.  */
+  bpf_insn_word opcode;
+  uint8_t dst;
+  uint8_t src;
+  expressionS offset16;
+  expressionS imm32;
+  expressionS imm64;
+  expressionS disp16;
+  expressionS disp32;
+
+  unsigned int has_dst : 1;
+  unsigned int has_src : 1;
+  unsigned int has_offset16 : 1;
+  unsigned int has_disp16 : 1;
+  unsigned int has_disp32 : 1;
+  unsigned int has_imm32 : 1;
+  unsigned int has_imm64 : 1;
+};
+
 const char comment_chars[]        = ";";
 const char line_comment_chars[]   = "#";
 const char line_separator_chars[] = "`";
 const char EXP_CHARS[]            = "eE";
 const char FLT_CHARS[]            = "fFdD";
 
-static const char *invalid_expression;
-static char pseudoc_lex[256];
-static const char symbol_chars[] =
-"_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-
-static const char arithm_op[] = "+-/<>%&|^";
-
-static void init_pseudoc_lex (void);
-
-#define LEX_IS_SYMBOL_COMPONENT  1
-#define LEX_IS_WHITESPACE        2
-#define LEX_IS_NEWLINE           3
-#define LEX_IS_ARITHM_OP         4
-#define LEX_IS_STAR              6
-#define LEX_IS_CLSE_BR           7
-#define LEX_IS_OPEN_BR           8
-#define LEX_IS_EQUAL             9
-#define LEX_IS_EXCLA             10
-
-#define ST_EOI        100
-#define MAX_TOKEN_SZ  100
-
 /* Like s_lcomm_internal in gas/read.c but the alignment string
    is allowed to be optional.  */
 
@@ -110,18 +110,15 @@ const pseudo_typeS md_pseudo_table[] =
 
 \f
 
-/* ISA handling.  */
-static CGEN_BITSET *bpf_isa;
-
-\f
-
 /* Command-line options processing.  */
 
 enum options
 {
   OPTION_LITTLE_ENDIAN = OPTION_MD_BASE,
   OPTION_BIG_ENDIAN,
-  OPTION_XBPF
+  OPTION_XBPF,
+  OPTION_DIALECT,
+  OPTION_ISA_SPEC,
 };
 
 struct option md_longopts[] =
@@ -129,6 +126,8 @@ struct option md_longopts[] =
   { "EL", no_argument, NULL, OPTION_LITTLE_ENDIAN },
   { "EB", no_argument, NULL, OPTION_BIG_ENDIAN },
   { "mxbpf", no_argument, NULL, OPTION_XBPF },
+  { "mdialect", required_argument, NULL, OPTION_DIALECT},
+  { "misa-spec", required_argument, NULL, OPTION_ISA_SPEC},
   { NULL,          no_argument, NULL, 0 },
 };
 
@@ -136,18 +135,34 @@ size_t md_longopts_size = sizeof (md_longopts);
 
 const char * md_shortopts = "";
 
-extern int target_big_endian;
+/* BPF supports little-endian and big-endian variants.  The following
+   global records what endianness to use.  It can be configured using
+   command-line options.  It defaults to the host endianness
+   initialized in md_begin.  */
 
-/* Whether target_big_endian has been set while parsing command-line
-   arguments.  */
 static int set_target_endian = 0;
+extern int target_big_endian;
+
+/* The ISA specification can be one of BPF_V1, BPF_V2, BPF_V3, BPF_V4
+   or BPF_XPBF.  The ISA spec to use can be configured using
+   command-line options.  It defaults to the latest BPF spec.  */
+
+static int isa_spec = BPF_V4;
 
-static int target_xbpf = 0;
+/* The assembler supports two different dialects: "normal" syntax and
+   "pseudoc" syntax.  The dialect to use can be configured using
+   command-line options.  */
 
-static int set_xbpf = 0;
+enum target_asm_dialect
+{
+  DIALECT_NORMAL,
+  DIALECT_PSEUDOC
+};
+
+static int asm_dialect = DIALECT_NORMAL;
 
 int
-md_parse_option (int c, const char * arg ATTRIBUTE_UNUSED)
+md_parse_option (int c, const char * arg)
 {
   switch (c)
     {
@@ -156,12 +171,36 @@ md_parse_option (int c, const char * arg ATTRIBUTE_UNUSED)
       target_big_endian = 1;
       break;
     case OPTION_LITTLE_ENDIAN:
-      set_target_endian = 1;
+      set_target_endian = 0;
       target_big_endian = 0;
       break;
+    case OPTION_DIALECT:
+      if (strcmp (arg, "normal") == 0)
+        asm_dialect = DIALECT_NORMAL;
+      else if (strcmp (arg, "pseudoc") == 0)
+        asm_dialect = DIALECT_PSEUDOC;
+      else
+        as_fatal (_("-mdialect=%s is not valid.  Expected normal or pseudoc"),
+                  arg);
+      break;
+    case OPTION_ISA_SPEC:
+      if (strcmp (arg, "v1") == 0)
+        isa_spec = BPF_V1;
+      else if (strcmp (arg, "v2") == 0)
+        isa_spec = BPF_V2;
+      else if (strcmp (arg, "v3") == 0)
+        isa_spec = BPF_V3;
+      else if (strcmp (arg, "v4") == 0)
+        isa_spec = BPF_V4;
+      else if (strcmp (arg, "xbpf") == 0)
+        isa_spec = BPF_XBPF;
+      else
+        as_fatal (_("-misa-spec=%s is not valid.  Expected v1, v2, v3, v4 o xbpf"),
+                  arg);
+      break;
     case OPTION_XBPF:
-      set_xbpf = 1;
-      target_xbpf = 1;
+      /* This is an alias for -misa-spec=xbpf.  */
+      isa_spec = BPF_XBPF;
       break;
     default:
       return 0;
@@ -175,43 +214,22 @@ md_show_usage (FILE * stream)
 {
   fprintf (stream, _("\nBPF options:\n"));
   fprintf (stream, _("\
-  --EL			generate code for a little endian machine\n\
-  --EB			generate code for a big endian machine\n\
-  -mxbpf                generate xBPF instructions\n"));
+BPF options:\n\
+  -EL                         generate code for a little endian machine\n\
+  -EB                         generate code for a big endian machine\n\
+  -mdialect=DIALECT           set the assembly dialect (normal, pseudoc)\n\
+  -misa-spec                  set the BPF ISA spec (v1, v2, v3, v4, xbpf)\n\
+  -mxbpf                      alias for -misa-spec=xbpf\n"));
 }
 
 \f
-
-static void
-init_pseudoc_lex (void)
-{
-  const char *p;
-
-  for (p = symbol_chars; *p; ++p)
-    pseudoc_lex[(unsigned char) *p] = LEX_IS_SYMBOL_COMPONENT;
-
-  pseudoc_lex[' '] = LEX_IS_WHITESPACE;
-  pseudoc_lex['\t'] = LEX_IS_WHITESPACE;
-  pseudoc_lex['\r'] = LEX_IS_WHITESPACE;
-  pseudoc_lex['\n'] = LEX_IS_NEWLINE;
-  pseudoc_lex['*'] = LEX_IS_STAR;
-  pseudoc_lex[')'] = LEX_IS_CLSE_BR;
-  pseudoc_lex['('] = LEX_IS_OPEN_BR;
-  pseudoc_lex[']'] = LEX_IS_CLSE_BR;
-  pseudoc_lex['['] = LEX_IS_OPEN_BR;
-
-  for (p = arithm_op; *p; ++p)
-    pseudoc_lex[(unsigned char) *p] = LEX_IS_ARITHM_OP;
-
-  pseudoc_lex['='] = LEX_IS_EQUAL;
-  pseudoc_lex['!'] = LEX_IS_EXCLA;
-}
+/* This function is called once, at assembler startup time.  This
+   should set up all the tables, etc that the MD part of the assembler
+   needs.  */
 
 void
 md_begin (void)
 {
-  /* Initialize the `cgen' interface.  */
-
   /* If not specified in the command line, use the host
      endianness.  */
   if (!set_target_endian)
@@ -223,50 +241,15 @@ md_begin (void)
 #endif
     }
 
-  /* If not specified in the command line, use eBPF rather
-     than xBPF.  */
-  if (!set_xbpf)
-      target_xbpf = 0;
-
-  /* Set the ISA, which depends on the target endianness. */
-  bpf_isa = cgen_bitset_create (ISA_MAX);
-  if (target_big_endian)
-    {
-      if (target_xbpf)
-	cgen_bitset_set (bpf_isa, ISA_XBPFBE);
-      else
-	cgen_bitset_set (bpf_isa, ISA_EBPFBE);
-    }
-  else
-    {
-      if (target_xbpf)
-	cgen_bitset_set (bpf_isa, ISA_XBPFLE);
-      else
-	cgen_bitset_set (bpf_isa, ISA_EBPFLE);
-    }
-
   /* Ensure that lines can begin with '*' in BPF store pseudoc instruction.  */
   lex_type['*'] |= LEX_BEGIN_NAME;
 
-  /* Set the machine number and endian.  */
-  gas_cgen_cpu_desc = bpf_cgen_cpu_open (CGEN_CPU_OPEN_ENDIAN,
-                                         target_big_endian ?
-                                         CGEN_ENDIAN_BIG : CGEN_ENDIAN_LITTLE,
-                                         CGEN_CPU_OPEN_INSN_ENDIAN,
-                                         CGEN_ENDIAN_LITTLE,
-                                         CGEN_CPU_OPEN_ISAS,
-                                         bpf_isa,
-                                         CGEN_CPU_OPEN_END);
-  bpf_cgen_init_asm (gas_cgen_cpu_desc);
-
-  /* This is a callback from cgen to gas to parse operands.  */
-  cgen_set_parse_operand_fn (gas_cgen_cpu_desc, gas_cgen_parse_operand);
-
   /* Set the machine type. */
   bfd_default_set_arch_mach (stdoutput, bfd_arch_bpf, bfd_mach_bpf);
-  init_pseudoc_lex();
 }
 
+/* Round up a section size to the appropriate boundary.  */
+
 valueT
 md_section_align (segT segment, valueT size)
 {
@@ -310,33 +293,48 @@ md_number_to_chars (char * buf, valueT val, int n)
 }
 
 arelent *
-tc_gen_reloc (asection *sec, fixS *fix)
+tc_gen_reloc (asection *sec ATTRIBUTE_UNUSED, fixS *fixP)
 {
-  return gas_cgen_tc_gen_reloc (sec, fix);
-}
+  bfd_reloc_code_real_type r_type = fixP->fx_r_type;
+  arelent *reloc;
 
-/* Return the bfd reloc type for OPERAND of INSN at fixup FIXP.  This
-   is called when the operand is an expression that couldn't be fully
-   resolved.  Returns BFD_RELOC_NONE if no reloc type can be found.
-   *FIXP may be modified if desired.  */
+  reloc = XNEW (arelent);
 
-bfd_reloc_code_real_type
-md_cgen_lookup_reloc (const CGEN_INSN *insn ATTRIBUTE_UNUSED,
-		      const CGEN_OPERAND *operand,
-		      fixS *fixP)
-{
-  switch (operand->type)
+  if (fixP->fx_pcrel)
+   {
+      r_type = (r_type == BFD_RELOC_8 ? BFD_RELOC_8_PCREL
+                : r_type == BFD_RELOC_16 ? BFD_RELOC_16_PCREL
+                : r_type == BFD_RELOC_24 ? BFD_RELOC_24_PCREL
+                : r_type == BFD_RELOC_32 ? BFD_RELOC_32_PCREL
+                : r_type == BFD_RELOC_64 ? BFD_RELOC_64_PCREL
+                : r_type);
+   }
+
+  reloc->howto = bfd_reloc_type_lookup (stdoutput, r_type);
+
+  if (reloc->howto == (reloc_howto_type *) NULL)
     {
-    case BPF_OPERAND_IMM64:
-      return BFD_RELOC_BPF_64;
-    case BPF_OPERAND_DISP32:
-      fixP->fx_pcrel = 1;
-      return BFD_RELOC_BPF_DISP32;
-    default:
-      break;
+      as_bad_where (fixP->fx_file, fixP->fx_line,
+		    _("relocation is not supported"));
+      return NULL;
     }
-  return BFD_RELOC_NONE;
+
+  //XXX  gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+
+  reloc->sym_ptr_ptr = XNEW (asymbol *);
+  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+
+  /* Use fx_offset for these cases.  */
+  if (fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+      || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT)
+    reloc->addend = fixP->fx_offset;
+  else
+    reloc->addend = fixP->fx_addnumber;
+
+  reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
+  return reloc;
 }
+
 \f
 /* *FRAGP has been relaxed to its final size, and now needs to have
    the bytes inside it modified to conform to the new size.
@@ -362,1556 +360,821 @@ md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
 }
 
 \f
+/* Apply a fixS (fixup of an instruction or data that we didn't have
+   enough info to complete immediately) to the data in a frag.  */
+
 void
-md_apply_fix (fixS *fixP, valueT *valP, segT seg)
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 {
-  /* Some fixups for instructions require special attention.  This is
-     handled in the code block below.  */
-  if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+  char *where = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+  switch (fixP->fx_r_type)
     {
-      int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
-      const CGEN_OPERAND *operand = cgen_operand_lookup_by_num (gas_cgen_cpu_desc,
-                                                                opindex);
-      char *where;
+    case BFD_RELOC_BPF_DISP16:
+      /* Convert from bytes to number of 64-bit words to the target,
+         minus one.  */
+      *valP = (((long) (*valP)) - 8) / 8;
+      break;
+    case BFD_RELOC_BPF_DISP32:
+      /* eBPF supports two kind of CALL instructions: the so called
+         pseudo calls ("bpf to bpf") and external calls ("bpf to
+         kernel").
+
+         Both kind of calls use the same instruction (CALL).  However,
+         external calls are constructed by passing a constant argument
+         to the instruction, whereas pseudo calls result from
+         expressions involving symbols.  In practice, instructions
+         requiring a fixup are interpreted as pseudo-calls.  If we are
+         executing this code, this is a pseudo call.
+
+         The kernel expects for pseudo-calls to be annotated by having
+         BPF_PSEUDO_CALL in the SRC field of the instruction.  But
+         beware the infamous nibble-swapping of eBPF and take
+         endianness into account here.
+
+         Note that the CALL instruction has only one operand, so
+         this code is executed only once per instruction.  */
+      md_number_to_chars (where + 1, target_big_endian ? 0x01 : 0x10, 1);
+
+      /* Convert from bytes to number of 64-bit words to the target,
+         minus one.  */
+      *valP = (((long) (*valP)) - 8) / 8;
+      break;
+    case BFD_RELOC_16_PCREL:
+      /* Convert from bytes to number of 64-bit words to the target,
+         minus one.  */
+      *valP = (((long) (*valP)) - 8) / 8;
+      break;
+    default:
+      break;
+    }
 
-      switch (operand->type)
-        {
-        case BPF_OPERAND_DISP32:
-          /* eBPF supports two kind of CALL instructions: the so
-             called pseudo calls ("bpf to bpf") and external calls
-             ("bpf to kernel").
-
-             Both kind of calls use the same instruction (CALL).
-             However, external calls are constructed by passing a
-             constant argument to the instruction, whereas pseudo
-             calls result from expressions involving symbols.  In
-             practice, instructions requiring a fixup are interpreted
-             as pseudo-calls.  If we are executing this code, this is
-             a pseudo call.
-
-             The kernel expects for pseudo-calls to be annotated by
-             having BPF_PSEUDO_CALL in the SRC field of the
-             instruction.  But beware the infamous nibble-swapping of
-             eBPF and take endianness into account here.
-
-             Note that the CALL instruction has only one operand, so
-             this code is executed only once per instruction.  */
-          where = fixP->fx_frag->fr_literal + fixP->fx_where + 1;
-          where[0] = target_big_endian ? 0x01 : 0x10;
-          /* Fallthrough.  */
-        case BPF_OPERAND_DISP16:
-          /* The PC-relative displacement fields in jump instructions
-             shouldn't be in bytes.  Instead, they hold the number of
-             64-bit words to the target, _minus one_.  */ 
-          *valP = (((long) (*valP)) - 8) / 8;
+  if (fixP->fx_addsy == (symbolS *) NULL)
+    fixP->fx_done = 1;
+
+  if (fixP->fx_done)
+    {
+      /* We're finished with this fixup.  Install it because
+	 bfd_install_relocation won't be called to do it.  */
+      switch (fixP->fx_r_type)
+	{
+	case BFD_RELOC_8:
+	  md_number_to_chars (where, *valP, 1);
+	  break;
+	case BFD_RELOC_16:
+	  md_number_to_chars (where, *valP, 2);
+	  break;
+	case BFD_RELOC_32:
+	  md_number_to_chars (where, *valP, 4);
+	  break;
+	case BFD_RELOC_64:
+	  md_number_to_chars (where, *valP, 8);
+	  break;
+        case BFD_RELOC_BPF_DISP16:
+          md_number_to_chars (where + 2, (uint16_t) *valP, 2);
           break;
-        default:
+        case BFD_RELOC_BPF_DISP32:
+          md_number_to_chars (where + 4, (uint32_t) *valP, 4);
           break;
-        }
+        case BFD_RELOC_16_PCREL:
+          md_number_to_chars (where + 2, (uint32_t) *valP, 2);
+          break;
+	default:
+	  as_bad_where (fixP->fx_file, fixP->fx_line,
+			_("internal error: can't install fix for reloc type %d (`%s')"),
+			fixP->fx_r_type, bfd_get_reloc_code_name (fixP->fx_r_type));
+	  break;
+	}
     }
 
-  /* And now invoke CGEN's handler, which will eventually install
-     *valP into the corresponding operand.  */
-  gas_cgen_md_apply_fix (fixP, valP, seg);
+  /* Tuck `value' away for use by tc_gen_reloc.
+     See the comment describing fx_addnumber in write.h.
+     This field is misnamed (or misused :-).  */
+  fixP->fx_addnumber = *valP;
 }
 
-/*
-  The BPF pseudo grammar:
-
-	instruction  : bpf_alu_insn
-		     | bpf_alu32_insn
-		     | bpf_jump_insn
-		     | bpf_load_store_insn
-		     | bpf_load_store32_insn
-		     | bpf_non_generic_load
-		     | bpf_endianness_conv_insn
-		     | bpf_64_imm_load_insn
-		     | bpf_atomic_insn
-		     ;
-
-	bpf_alu_insn : BPF_REG bpf_alu_operator register_or_imm32
-		     ;
-
-	bpf_alu32_insn : BPF_REG32 bpf_alu_operator register32_or_imm32
-		       ;
-
-	bpf_jump_insn  : BPF_JA offset
-		       | IF BPF_REG bpf_jump_operator register_or_imm32 BPF_JA offset
-		       | IF BPF_REG32 bpf_jump_operator register_or_imm32 BPF_JA offset
-		       | BPF_CALL offset
-		       | BPF_EXIT
-		       ;
-
-	bpf_load_store_insn  : BPF_REG CHR_EQUAL bpf_size_cast BPF_CHR_OPEN_BR \
-			       register_and_offset BPF_CHR_CLSE_BR
-			     | bpf_size_cast register_and_offset CHR_EQUAL BPF_REG
-			     ;
-
-	bpf_load_store32_insn  : BPF_REG CHR_EQUAL bpf_size_cast BPF_CHR_OPEN_BR \
-				 register32_and_offset BPF_CHR_CLSE_BR
-			       | bpf_size_cast register_and_offset CHR_EQUAL BPF_REG32
-			     ;
-
-	bpf_non_generic_load : BPF_REG_R0 CHR_EQUAL bpf_size_cast BPF_LD BPF_CHR_OPEN_BR \
-			       imm32 BPF_CHR_CLSE_BR
-			     ;
-
-	bpf_endianness_conv_insn : BPF_REG_N bpf_endianness_mnem BPF_REG_N
-				 ;
-
-	bpf_64_imm_load_insn : BPF_REG imm64 BPF_LL
-			     ;
-
-	bpf_atomic_insn : BPF_LOCK bpf_size_cast_32_64 register_and_offset BPF_ADD BPF_REG
-
-	register_and_offset : BPF_CHR_OPEN_BR BPF_REG offset BPF_CHR_CLSE_BR
-			    ;
-
-	register32_and_offset : BPF_CHR_OPEN_BR BPF_REG32 offset BPF_CHR_CLSE_BR
-			      ;
-
-	bpf_size_cast : CHR_START BPF_CHR_OPEN_BR bpf_size CHR_START BPF_CHR_CLSE_BR
-		      ;
-
-	bpf_size_cast_32_64 : CHR_START BPF_CHR_OPEN_BR bpf_size_cast_32_64 CHR_STAR BPF_CHR_CLSE_BR
-			    ;
-
-	bpf_size_32_64 : BPF_CAST_U32
-		       | BPF_CAST_U64
-		       ;
-
-	bpf_size  : BPF_CAST_U8
-		  | BPF_CAST_U16
-		  | BPF_CAST_U32
-		  | BPF_CAST_U64
-		  ;
-
-	bpf_jump_operator : BPF_JEQ
-			  | BPF_JGT
-			  | BPF_JGE
-			  | BPF_JNE
-			  | BPF_JSGT
-			  | BPF_JSGE
-			  | BPF_JLT
-			  | BPF_JLE
-			  | BPF_JSLT
-			  | BPF_JSLE
-			  ;
-
-	bpf_alu_operator : BPF_ADD
-			 | BPF_SUB
-			 | BPF_MUL
-			 | BPF_DIV
-			 | BPF_OR
-			 | BPF_AND
-			 | BPF_LSH
-			 | BPF_RSH
-			 | BPF_NEG
-			 | BPF_MOD
-			 | BPF_XOR
-			 | BPF_ARSH
-			 | CHR_EQUAL
-			 ;
-
-	bpf_endianness_mnem : BPF_LE16
-			    | BPF_LE32
-			    | BPF_LE64
-			    | BPF_BE16
-			    | BPF_BE32
-			    | BPF_BE64
-			    ;
-
-	offset : BPF_EXPR
-	       | BPF_SYMBOL
-	       ;
-
-	register_or_imm32 : BPF_REG
-			  | expression
-			  ;
-
-	register32_or_imm32 : BPF_REG32
-			    | expression
-			    ;
-
-	imm32 : BPF_EXPR
-	      | BPF_SYMBOL
-	      ;
-
-	imm64 : BPF_EXPR
-	      | BPF_SYMBOL
-	      ;
-
-	register_or_expression : BPF_EXPR
-			       | BPF_REG
-			       ;
-
-	BPF_EXPR : GAS_EXPR
-
-*/
-
-enum bpf_token_type
-  {
-    /* Keep grouped to quickly access. */
-    BPF_ADD,
-    BPF_SUB,
-    BPF_MUL,
-    BPF_DIV,
-    BPF_OR,
-    BPF_AND,
-    BPF_LSH,
-    BPF_RSH,
-    BPF_MOD,
-    BPF_XOR,
-    BPF_MOV,
-    BPF_ARSH,
-    BPF_NEG,
-
-    BPF_REG,
-
-    BPF_IF,
-    BPF_GOTO,
-
-    /* Keep grouped to quickly access.  */
-    BPF_JEQ,
-    BPF_JGT,
-    BPF_JGE,
-    BPF_JLT,
-    BPF_JLE,
-    BPF_JSET,
-    BPF_JNE,
-    BPF_JSGT,
-    BPF_JSGE,
-    BPF_JSLT,
-    BPF_JSLE,
-
-    BPF_SYMBOL,
-    BPF_CHR_CLSE_BR,
-    BPF_CHR_OPEN_BR,
-
-    /* Keep grouped to quickly access.  */
-    BPF_CAST_U8,
-    BPF_CAST_U16,
-    BPF_CAST_U32,
-    BPF_CAST_U64,
-
-    /* Keep grouped to quickly access.  */
-    BPF_LE16,
-    BPF_LE32,
-    BPF_LE64,
-    BPF_BE16,
-    BPF_BE32,
-    BPF_BE64,
-
-    BPF_LOCK,
-
-    BPF_IND_CALL,
-    BPF_LD,
-    BPF_LL,
-    BPF_EXPR,
-    BPF_UNKNOWN,
-  };
-
-static int
-valid_expr (const char *e, const char **end_expr)
-{
-  invalid_expression = NULL;
-  char *hold = input_line_pointer;
-  expressionS exp;
-
-  input_line_pointer = (char *) e;
-  deferred_expression  (&exp);
-  *end_expr = input_line_pointer;
-  input_line_pointer = hold;
+/* Parse an operand expression.  Returns the first character that is
+   not part of the expression, or NULL in case of parse error.
 
-  return invalid_expression == NULL;
-}
+   See md_operand below to see how exp_parse_failed is used.  */
 
-static char *
-build_bpf_non_generic_load (char *src, enum bpf_token_type cast,
-			    const char *imm32)
-{
-  char *bpf_insn;
-  static const char *cast_rw[] = {"b", "h", "w", "dw"};
-
-  bpf_insn = xasprintf ("%s%s%s %s%s%s%s",
-			"ld",
-			src ? "ind" : "abs",
-			cast_rw[cast - BPF_CAST_U8],
-			src ? "%" : "",
-			src ? src : "",
-			src ? "," : "",
-			imm32);
-  return bpf_insn;
-}
+static int exp_parse_failed = 0;
 
 static char *
-build_bpf_atomic_insn (char *dst, char *src,
-		       enum bpf_token_type atomic_insn,
-		       enum bpf_token_type cast,
-		       const char *offset)
+parse_expression (char *s, expressionS *exp)
 {
-  char *bpf_insn;
-  static const char *cast_rw[] = {"w", "dw"};
-  static const char *mnem[] = {"xadd"};
-
-  bpf_insn = xasprintf ("%s%s [%%%s%s%s],%%%s", mnem[atomic_insn - BPF_ADD],
-			cast_rw[cast - BPF_CAST_U32], dst,
-			*offset != '+' ? "+" : "",
-			offset, src);
-  return bpf_insn;
-}
+  char *saved_input_line_pointer = input_line_pointer;
+  char *saved_s = s;
 
-static char *
-build_bpf_jmp_insn (char *dst, char *src,
-		    char *imm32, enum bpf_token_type op,
-		    const char *sym, const char *offset)
-{
-  char *bpf_insn;
-  static const char *mnem[] =
-    {
-      "jeq", "jgt", "jge", "jlt",
-      "jle", "jset", "jne", "jsgt",
-      "jsge", "jslt", "jsle"
-    };
-
-  const char *in32 = (*dst == 'w' ? "32" : "");
-
-  *dst = 'r';
-  if (src)
-    *src = 'r';
-
-  bpf_insn = xasprintf ("%s%s %%%s,%s%s,%s",
-			mnem[op - BPF_JEQ], in32, dst,
-			src ? "%" : "",
-			src ? src : imm32,
-			offset ? offset : sym);
-  return bpf_insn;
-}
+  exp_parse_failed = 0;
+  input_line_pointer = s;
+  expression (exp);
+  s = input_line_pointer;
+  input_line_pointer = saved_input_line_pointer;
 
-static char *
-build_bpf_arithm_insn (char *dst, char *src,
-		       int load64, const char *imm32,
-		       enum bpf_token_type type)
-{
-  char *bpf_insn;
-  static const char *mnem[] =
-    {
-      "add", "sub", "mul", "div",
-      "or", "and", "lsh", "rsh",
-      "mod", "xor", "mov", "arsh",
-      "neg",
-    };
-  const char *in32 = (*dst == 'w' ? "32" : "");
-
-  *dst = 'r';
-  if (src)
-    *src = 'r';
-
-  if (type == BPF_NEG)
-    bpf_insn = xasprintf ("%s%s %%%s", mnem[type - BPF_ADD], in32, dst);
-  else if (load64)
-    bpf_insn = xasprintf ("%s %%%s,%s", "lddw", dst, imm32);
-  else
-    bpf_insn = xasprintf ("%s%s %%%s,%s%s", mnem[type - BPF_ADD],
-			  in32, dst,
-			  src ? "%" : "",
-			  src ? src: imm32);
-  return bpf_insn;
-}
+  switch (exp->X_op == O_absent || exp_parse_failed)
+    return NULL;
 
-static char *
-build_bpf_endianness (char *dst, enum bpf_token_type endianness)
-{
-  char *bpf_insn;
-  static const char *size[] = {"16", "32", "64"};
-  int be = 1;
-
-  if (endianness == BPF_LE16
-      || endianness == BPF_LE32
-      || endianness == BPF_LE64)
-    be = 0;
-  else
-    gas_assert (endianness == BPF_BE16 || endianness == BPF_BE32 || endianness == BPF_BE64);
+  /* The expression parser may consume trailing whitespaces.  We have
+     to undo that since the instruction templates may be expecting
+     these whitespaces.  */
+  {
+    char *p;
+    for (p = s - 1; p >= saved_s && *p == ' '; --p)
+      --s;
+  }
 
-  bpf_insn = xasprintf ("%s %%%s,%s", be ? "endbe" : "endle",
-			dst, be ? size[endianness - BPF_BE16] : size[endianness - BPF_LE16]);
-  return bpf_insn;
+  return s;
 }
 
-static char *
-build_bpf_load_store_insn (char *dst, char *src,
-			   enum bpf_token_type cast,
-			   const char *offset, int isload)
-{
-  char *bpf_insn;
-  static const char *cast_rw[] = {"b", "h", "w", "dw"};
-
-  *dst = *src = 'r';
-  if (isload)
-    bpf_insn = xasprintf ("%s%s %%%s,[%%%s%s%s]", "ldx",
-			  cast_rw[cast - BPF_CAST_U8], dst, src,
-			  *offset != '+' ? "+" : "",
-			  offset);
-  else
-    bpf_insn = xasprintf ("%s%s [%%%s%s%s],%%%s", "stx",
-			  cast_rw[cast - BPF_CAST_U8], dst,
-			  *offset != '+' ? "+" : "",
-			  offset, src);
-  return bpf_insn;
-}
+/* Parse a BPF register name and return the corresponding register
+   number.  Return NULL in case of parse error, or a pointer to the
+   first character in S that is not part of the register name.  */
 
-static int
-look_for_reserved_word (const char *token, enum bpf_token_type *type)
+static char *
+parse_bpf_register (char *s, char rw, uint8_t *regno)
 {
-  int i;
-  static struct
-  {
-    const char *name;
-    enum bpf_token_type type;
-  } reserved_words[] =
+  if (asm_dialect == DIALECT_NORMAL)
     {
-      {
-	.name = "if",
-	.type = BPF_IF
-      },
-      {
-	.name = "goto",
-	.type = BPF_GOTO
-      },
-      {
-	.name = "le16",
-	.type = BPF_LE16
-      },
-      {
-	.name = "le32",
-	.type = BPF_LE32
-      },
-      {
-	.name = "le64",
-	.type = BPF_LE64
-      },
-      {
-	.name = "be16",
-	.type = BPF_BE16
-      },
-      {
-	.name = "be32",
-	.type = BPF_BE32
-      },
-      {
-	.name = "be64",
-	.type = BPF_BE64
-	},
-      {
-	.name = "lock",
-	.type = BPF_LOCK
-      },
-      {
-	.name = "callx",
-	.type = BPF_IND_CALL
-      },
-      {
-	.name = "skb",
-	.type = BPF_LD
-      },
-      {
-	.name = "ll",
-	.type = BPF_LL
-      },
-      {
-	.name = NULL,
-      }
-    };
+      rw = 'r';
+      if (*s != '%')
+	return NULL;
+      s += 1;
 
-  for (i = 0; reserved_words[i].name; ++i)
-    if (*reserved_words[i].name == *token
-	&& !strcmp (reserved_words[i].name, token))
-      {
-	*type = reserved_words[i].type;
-	return 1;
-      }
+      if (*s == 'f' && *(s + 1) == 'p')
+	{
+	  *regno = 10;
+	  s += 2;
+	  return s;
+	}
+    }
 
-  return 0;
-}
+  if (*s != rw)
+    return NULL;
+  s += 1;
 
-static int
-is_register (const char *token, int len)
-{
-  if (token[0] == 'r' || token[0] == 'w')
-    if ((len == 2 && isdigit (token[1]))
-	|| (len == 3 && token[1] == '1' && token[2] == '0'))
-      return 1;
+  if (*s == '1')
+    {
+      if (*(s + 1) == '0')
+        {
+          *regno = 10;
+          s += 2;
+        }
+      else
+        {
+          *regno = 1;
+          s += 1;
+        }
+    }
+  else if (*s >= '0' && *s <= '9')
+    {
+      *regno = *s - '0';
+      s += 1;
+    }
 
-  return 0;
+  return s;
 }
 
-static enum bpf_token_type
-is_cast (const char *token)
-{
-  static const char *cast_rw[] = {"u8", "u16", "u32", "u64"};
-  unsigned int i;
-
-  for (i = 0; i < ARRAY_SIZE (cast_rw); ++i)
-    if (!strcmp (token, cast_rw[i]))
-      return BPF_CAST_U8 + i;
+/* Collect a parse error message.  */
 
-  return BPF_UNKNOWN;
-}
+static int partial_match_length = 0;
+static char *errmsg = NULL;
 
-static enum bpf_token_type
-get_token (const char **insn, char *token, size_t *tlen)
+static void
+parse_error (int length, const char *fmt, ...)
 {
-#define GET()					\
-  (*str == '\0'					\
-   ? EOF					\
-   : *(unsigned char *)(str++))
-
-#define UNGET() (--str)
-
-#define START_EXPR()			       \
-  do					       \
-    {					       \
-      if (expr == NULL)			       \
-	expr = str - 1;			       \
-    } while (0)
-
-#define SCANNER_SKIP_WHITESPACE()		\
-  do						\
-    {						\
-      do					\
-	ch = GET ();				\
-      while (ch != EOF				\
-	     && ((ch) == ' ' || (ch) == '\t'));	\
-      if (ch != EOF)				\
-	UNGET ();				\
-    } while (0)
-
-  const char *str = *insn;
-  int ch, ch2 = 0;
-  enum bpf_token_type ttype = BPF_UNKNOWN;
-  size_t len = 0;
-  const char *expr = NULL;
-  const char *end_expr = NULL;
-  int state = 0;
-  int return_token = 0;
-
-  while (1)
+  if (length > partial_match_length)
     {
-      ch = GET ();
-
-      if (ch == EOF || len > MAX_TOKEN_SZ)
-	break;
-
-      switch (pseudoc_lex[(unsigned char) ch])
-	{
-	case LEX_IS_WHITESPACE:
-	  SCANNER_SKIP_WHITESPACE ();
-	  return_token = 1;
-
-	  switch (state)
-	    {
-	    case 12: /* >' ' */
-	      ttype = BPF_JGT;
-	      break;
-
-	    case 17: /* ==' ' */
-	      ttype = BPF_JEQ;
-	      break;
-
-	    case 18: /* <' ' */
-	      ttype = BPF_JLT;
-	      break;
-
-	    case 20: /* &' ' */
-	      ttype = BPF_JSET;
-	      break;
-
-	    case 22:  /* s<' '*/
-	      ttype = BPF_JSLT;
-	      break;
-
-	    case 14: /* s> ' ' */
-	      ttype = BPF_JSGT;
-	      break;
-
-	    case 16: /* =' ' */
-	      ttype = BPF_MOV;
-	      break;
-
-	    default:
-	      return_token = 0;
-	    }
-	  break;
-
-	case LEX_IS_EXCLA:
-	  token[len++] = ch;
-	  state = 21;
-	  break;
-
-	case LEX_IS_ARITHM_OP:
-	  if (state == 16)
-	    {
-	      /* ='-' is handle as '=' */
-	      UNGET ();
-	      ttype = BPF_MOV;
-	      return_token = 1;
-	      break;
-	    }
-
-	  START_EXPR();
-	  token[len++] = ch;
-	  switch (ch)
-	    {
-#define BPF_ARITHM_OP(op, type)			\
-	      case (op):			\
-		state = 6;			\
-		ttype = (type);			\
-		break;
-
-	      BPF_ARITHM_OP('+', BPF_ADD);
-	      BPF_ARITHM_OP('-', BPF_SUB);
-	      BPF_ARITHM_OP('*', BPF_MUL);
-	      BPF_ARITHM_OP('/', BPF_DIV);
-	      BPF_ARITHM_OP('|', BPF_OR);
-	      BPF_ARITHM_OP('%', BPF_MOD);
-	      BPF_ARITHM_OP('^', BPF_XOR);
-
-	    case '&':
-	      state = 20; /* '&' */
-	      break;
-
-	    case '<':
-	      switch (state)
-		{
-		case 0:
-		  state = 18; /* '<' */
-		  break;
-
-		case 18:
-		  state = 19; /* <'<' */
-		  break;
-
-		case 8:
-		  state = 22; /* s'<' */
-		  break;
-		}
-	      break;
-
-	    case '>':
-	      switch (state)
-		{
-		case 0:
-		  state = 12; /* '>' */
-		  break;
-
-		case 12:
-		  state = 13; /* >'>' */
-		  break;
-
-		case 8:
-		  state = 14; /* s'>' */
-		  break;
-
-		case 14:
-		  state = 15; /* s>'>' */
-		  break;
-		}
-	      break;
-	    }
-	  break;
-
-	case LEX_IS_STAR:
-	  switch (state)
-	    {
-	    case 0:
-	      token[len++] = ch;
-	      START_EXPR ();
-	      state = 2; /* '*', It could be the fist cast char.  */
-	      break;
-
-	    case 16: /* ='*' Not valid token.  */
-	      ttype = BPF_MOV;
-	      return_token = 1;
-	      UNGET ();
-	      break;
-
-	    case 4: /* *(uXX'*' */
-	      token[len++] = ch;
-	      state = 5;
-	      break;
-	    }
-	  break;
-
-	case LEX_IS_OPEN_BR:
-	  START_EXPR ();
-	  token[len++] = ch;
-	  return_token = 1;
-
-	  switch (state)
-	    {
-	    case 2:
-	      state = 3; /* *'(' second char of a cast or expr.  */
-	      return_token = 0;
-	      break;
-
-	    case 6:
-	      if (valid_expr (expr, &end_expr))
-		{
-		  len = end_expr - expr;
-		  memcpy (token, expr, len);
-		  ttype = BPF_EXPR;
-		  str = end_expr;
-		}
-	      else
-		{
-		  len = 0;
-		  while (*invalid_expression)
-		    token[len++] = *invalid_expression++;
-
-		  token[len] = 0;
-		  ttype = BPF_UNKNOWN;
-		}
-	      break;
-
-	    default:
-	      ttype = BPF_CHR_OPEN_BR;
-	      SCANNER_SKIP_WHITESPACE ();
-	      ch2 = GET ();
-
-	      if ((isdigit (ch2) || ch2 == '(')
-		  && valid_expr (expr, &end_expr))
-		{
-		  len = end_expr - expr;
-		  memcpy (token, expr, len);
-		  ttype = BPF_EXPR;
-		  str = end_expr;
-		}
-	      else
-		UNGET ();
-	    }
-	  break;
-
-	case LEX_IS_CLSE_BR:
-	  token[len++] = ch;
-
-	  if (state == 0)
-	    {
-	      ttype = BPF_CHR_CLSE_BR;
-	      return_token = 1;
-	    }
-	  else if (state == 5) /* *(uXX*')'  */
-	    return_token = 1;
-	  break;
-
-	case LEX_IS_EQUAL:
-	  token[len++] = ch;
-	  return_token = 1;
-
-	  switch (state)
-	    {
-	    case 0:
-	      state = 16; /* '=' */
-	      return_token = 0;
-	      break;
-
-	    case 16:
-	      state = 17; /* ='=' */
-	      return_token = 0;
-	      break;
-
-	    case 2: /* *'=' */
-	      ttype = BPF_MUL;
-	      break;
-
-	    case 10: /* s>>'=' */
-	      ttype = BPF_ARSH;
-	      break;
-
-	    case 12: /* >'=' */
-	      ttype = BPF_JGE;
-	      break;
-
-	    case 13: /* >>'=' */
-	      ttype = BPF_RSH;
-	      break;
-
-	    case 14: /* s>'=' */
-	      ttype = BPF_JSGE;
-	      break;
-
-	    case 15: /* s>>'=' */
-	      ttype = BPF_ARSH;
-	      break;
-
-	    case 18: /* <'=' */
-	      ttype = BPF_JLE;
-	      break;
-
-	    case 19: /* <<'=' */
-	      ttype = BPF_LSH;
-	      break;
-
-	    case 20: /* &'=' */
-	      ttype = BPF_AND;
-	      break;
-
-	    case 21: /* !'=' */
-	      ttype = BPF_JNE;
-	      break;
-
-	    case 22: /* s<'=' */
-	      ttype = BPF_JSLE;
-	      break;
-	    }
-	  break;
-
-	case LEX_IS_SYMBOL_COMPONENT:
-	  return_token = 1;
-
-	  switch (state)
-	    {
-	    case 17: /* =='sym' */
-	      ttype = BPF_JEQ;
-	      break;
-
-	    case 12: /* >'sym' */
-	      ttype = BPF_JGT;
-	      break;
-
-	    case 18: /* <'sym' */
-	      ttype = BPF_JLT;
-	      break;
-
-	    case 20: /* &'sym' */
-	      ttype = BPF_JSET;
-	      break;
-
-	    case 14: /*s>'sym' */
-	      ttype = BPF_JSGT;
-	      break;
-
-	    case 22:  /* s<'sym' */
-	      ttype = BPF_JSLT;
-	      break;
-
-	    case 16: /* ='sym' */
-	      ttype = BPF_MOV;
-	      break;
-
-	    default:
-	      return_token = 0;
-	    }
-
-	  if (return_token)
-	    {
-	      UNGET ();
-	      break;
-	    }
-
-	  START_EXPR ();
-	  token[len++] = ch;
-
-	  while ((ch2 = GET ()) != EOF)
-	    {
-	      int type;
-
-	      type = pseudoc_lex[(unsigned char) ch2];
-	      if (type != LEX_IS_SYMBOL_COMPONENT)
-		break;
-	      token[len++] = ch2;
-	    }
-
-	  if (ch2 != EOF)
-	    UNGET ();
-
-	  if (state == 0)
-	    {
-	      if (len == 1 && ch == 's')
-		state = 8; /* signed instructions: 's' */
-	      else
-		{
-		  ttype = BPF_SYMBOL;
-		  if (is_register (token, len))
-		    ttype = BPF_REG;
-		  else if (look_for_reserved_word (token, &ttype))
-		    ;
-		  else if ((pseudoc_lex[(unsigned char) *token] == LEX_IS_ARITHM_OP
-			    || *token == '(' || isdigit(*token))
-			   && valid_expr (expr, &end_expr))
-		    {
-		      len = end_expr - expr;
-		      token[len] = '\0';
-		      ttype = BPF_EXPR;
-		      str = end_expr;
-		    }
-
-		  return_token = 1;
-		}
-	    }
-	  else if (state == 3) /* *('sym' */
-	    {
-	      if ((ttype = is_cast (&token[2])) != BPF_UNKNOWN)
-		state = 4; /* *('uXX' */
-	      else
-		{
-		  ttype = BPF_EXPR;
-		  return_token = 1;
-		}
-	    }
-	  else if (state == 6)
-	    {
-	      if (ttype == BPF_SUB) /* neg */
-		{
-		  if (is_register (&token[1], len - 1))
-		    ttype =  BPF_NEG;
-		  else if (valid_expr(expr, &end_expr))
-		    {
-		      len = end_expr - expr;
-		      memcpy(token, expr, len);
-		      ttype = BPF_EXPR;
-		      str = end_expr;
-		    }
-		  else
-		    {
-		      len = 0;
-		      while (*invalid_expression)
-			token[len++] = *invalid_expression++;
-		      token[len] = 0;
-		      ttype = BPF_UNKNOWN;
-		    }
-		}
-	      else if (valid_expr (expr, &end_expr))
-		{
-		  len = end_expr - expr;
-		  memcpy(token, expr, len);
-		  ttype = BPF_EXPR;
-		  str = end_expr;
-		}
-	      else
-		ttype = BPF_UNKNOWN;
-
-	      return_token = 1;
-	    }
-	  break;
-	}
+      va_list args;
 
-      if (return_token)
-	{
-	  *tlen = len;
-	  *insn = str;
-	  break;
-	}
+      free (errmsg);
+      va_start (args, fmt);
+      errmsg = xvasprintf (fmt, args);
+      va_end (args);
+      partial_match_length = length;
     }
-
-  return ttype;
-
-#undef GET
-#undef UNGET
-#undef START_EXPR
-#undef SCANNER_SKIP_WHITESPACE
-#undef BPF_ARITHM_OP
 }
 
-/*
-  The parser represent a FSM for the grammar described above. So for example
-  the following rule:
-
-     ` bpf_alu_insn : BPF_REG bpf_alu_operator register_or_imm32'
-
-  Is parser as follows:
-
-      1. It starts in state 0.
+/* Assemble a machine instruction in STR and emit the frags/bytes it
+   assembles to.  */
 
-      2. Consumes next token, e.g: `BPF_REG' and set `state' variable to a
-      particular state to helps to identify, in this case, that a register
-      token has been read, a comment surrounded by a single quote in the
-      pseudo-c token is added along with the new `state' value to indicate
-      what the scanner has read, e.g.:
-
-          state = 6; // dst_reg = str_cast ( 'src_reg'
-
-      So, in `state 6' the scanner has consumed: a destination register
-      (BPF_REG), an equal character (BPF_MOV), a cast token (BPF_CAST), an
-      open parenthesis (BPF_CHR_OPEN_BR) and the source register (BPF_REG).
-
-      3. If the accumulated tokens represent a complete BPF pseudo-c syntax
-      instruction then, a validation of the terms is made, for example: if
-      the registers have the same sizes (32/64 bits), if a specific
-      destination register must be used, etc., after that, a builder:
-      build_bfp_{non_generic_load,atomic_insn,jmp_insn,arithm_insn,endianness,load_store_insn}
-      is invoked, internally, it translates the BPF pseudo-c instruction to
-      a BPF GAS instruction using the previous terms recollected by the
-      scanner.
-
-      4. If a successful build of BPF GAS instruction was done, a final
-      state is set to `ST_EOI' (End Of Instruction) meaning that is not
-      expecting for more tokens in such instruction.  Otherwise if the
-      conditions to calling builder are not satisfied an error is emitted
-      and `parse_err' is set.
-*/
-
-static char *
-bpf_pseudoc_to_normal_syntax (const char *str, char **errmsg)
+void
+md_assemble (char *str ATTRIBUTE_UNUSED)
 {
-#define syntax_err(format, ...)						\
-  do									\
-    {									\
-      if (! parse_err)							\
-	{								\
-	  parse_err = 1;						\
-	  errbuf = xasprintf (format, ##__VA_ARGS__);			\
-	}								\
-    } while (0)
-
-  enum bpf_token_type ttype;
-  enum bpf_token_type bpf_endianness = BPF_UNKNOWN,
-		      bpf_atomic_insn;
-  enum bpf_token_type bpf_jmp_op = BPF_JEQ; /* Arbitrary.  */
-  enum bpf_token_type bpf_cast = BPF_CAST_U8; /* Arbitrary.  */
-  enum bpf_token_type bpf_arithm_op = BPF_ADD; /* Arbitrary.  */
-  char *bpf_insn = NULL;
-  char *errbuf = NULL;
-  char src_reg[3] = {0};
-  char dst_reg[3] = {0};
-  char str_imm32[40] = {0};
-  char str_offset[40] = {0};
-  char str_symbol[MAX_TOKEN_SZ] = {0};
-  char token[MAX_TOKEN_SZ] = {0};
-  int state = 0;
-  int parse_err = 0;
-  size_t tlen;
-
-  while (*str)
+  /* There are two different syntaxes that can be used to write BPF
+     instructions.  One is very conventional and like any other
+     assembly language where each instruction is conformed by an
+     instruction mnemonic followed by its operands.  This is what we
+     call the "normal" syntax.  The other syntax tries to look like C
+     statements. We have to support both syntaxes in this assembler.
+
+     One of the many nuisances introduced by this eccentricity is that
+     in the pseudo-c syntax it is not possible to hash the opcodes
+     table by instruction mnemonic, because there is none.  So we have
+     no other choice than to try to parse all instruction opcodes
+     until one matches.  This is slow.
+
+     Another problem is that emitting detailed diagnostics becomes
+     tricky, since the lack of mnemonic means it is not clear what
+     instruction was intended by the user, and we cannot emit
+     diagnostics for every attempted template.  So if an instruction
+     is not parsed, we report the diagnostic corresponding to the
+     partially parsed instruction that was matched further.  */
+
+  unsigned int idx = 0;
+  struct bpf_insn insn;
+  const struct bpf_opcode *opcode;
+
+  /* Initialize the global diagnostic variables.  See the parse_error
+     function above.  */
+  partial_match_length = 0;
+  errmsg = NULL;
+
+#define PARSE_ERROR(...) parse_error (s - str, __VA_ARGS__)
+
+  while ((opcode = bpf_get_opcode (idx++)) != NULL)
     {
-      ttype = get_token (&str, token, &tlen);
-      if (ttype == BPF_UNKNOWN || state == ST_EOI)
-	{
-	  syntax_err ("unexpected token: '%s'", token);
-	  break;
-	}
-
-      switch (ttype)
-	{
-	case BPF_UNKNOWN:
-	case BPF_LL:
-	  break;
-
-	case BPF_REG:
-	  switch (state)
-	    {
-	    case 0:
-	      memcpy (dst_reg, token, tlen);
-	      state = 1; /* 'dst_reg' */
-	      break;
-
-	    case 3:
-	      /* dst_reg bpf_op 'src_reg' */
-	      memcpy (src_reg, token, tlen);
-	      if (*dst_reg == *src_reg)
-		bpf_insn = build_bpf_arithm_insn (dst_reg, src_reg, 0,
-						  NULL, bpf_arithm_op);
-	      else
-		{
-		  syntax_err ("different register sizes: '%s', '%s'",
-			      dst_reg, src_reg);
-		  break;
-		}
-	      state = ST_EOI;
-	      break;
-
-	    case 5:
-	      memcpy (src_reg, token, tlen);
-	      state = 6; /* dst_reg = str_cast ( 'src_reg' */
-	      break;
-
-	    case 9:
-	      memcpy (dst_reg, token, tlen);
-	      state = 10; /* str_cast ( 'dst_reg' */
-	      break;
-
-	    case 11:
-	      /* str_cast ( dst_reg offset ) = 'src_reg' */
-	      memcpy (src_reg, token, tlen);
-	      bpf_insn = build_bpf_load_store_insn (dst_reg, src_reg,
-						    bpf_cast, str_offset, 0);
-	      state = ST_EOI;
-	      break;
-
-	    case 14:
-	      memcpy (dst_reg, token, tlen);
-	      state = 15; /* if 'dst_reg' */
-	      break;
-
-	    case 16:
-	      memcpy (src_reg, token, tlen);
-	      state = 17; /* if dst_reg jmp_op 'src_reg' */
-	      break;
-
-	    case 24:
-	      /* dst_reg = endianness src_reg */
-	      memcpy (src_reg, token, tlen);
-	      if (*dst_reg == 'r' && !strcmp (dst_reg, src_reg))
-		bpf_insn = build_bpf_endianness (dst_reg, bpf_endianness);
-	      else
-		syntax_err ("invalid operand for instruction: '%s'", token);
-
-	      state = ST_EOI;
-	      break;
-
-	    case 28:
-	      memcpy (dst_reg, token, tlen);
-	      state = 29; /* lock str_cast ( 'dst_reg'  */
-	      break;
-
-	    case 32:
-	      {
-		/* lock str_cast ( dst_reg offset ) atomic_insn 'src_reg' */
-		int with_offset = *str_offset != '\0';
-
-		memcpy (src_reg, token, tlen);
-		if ((bpf_cast != BPF_CAST_U32
-		     && bpf_cast != BPF_CAST_U64)
-		    || *dst_reg != 'r'
-		    || *src_reg != 'r')
-		  syntax_err ("invalid wide atomic instruction");
-		else
-		  bpf_insn = build_bpf_atomic_insn (dst_reg, src_reg, bpf_atomic_insn,
-						    bpf_cast, with_offset ? str_offset : str_symbol);
-	      }
-
-	      state = ST_EOI;
-	      break;
-
-	    case 33:
-	      /* callx 'dst_reg' */
-	      bpf_insn = xasprintf ("%s %%%s", "call", token);
-	      state = ST_EOI;
-	      break;
-
-	    case 35:
-	      memcpy (src_reg, token, tlen);
-	      state = 36; /* dst_reg = str_cast skb [ 'src_reg' */
-	      break;
-	    }
-	  break;
-
-	case BPF_MOV:
-	case BPF_ADD:
-	case BPF_SUB:
-	case BPF_MUL:
-	case BPF_DIV:
-	case BPF_OR:
-	case BPF_AND:
-	case BPF_LSH:
-	case BPF_RSH:
-	case BPF_MOD:
-	case BPF_XOR:
-	case BPF_ARSH:
-	case BPF_NEG:
-	  switch (state)
-	    {
-	    case 1:
-	      state = 3;  /* dst_reg 'arith_op' */
-	      bpf_arithm_op = ttype;
-	      break;
-
-	    case 3:
-	      if (ttype == BPF_NEG)
-		{
-		  /* reg = -reg */
-		  bpf_arithm_op = ttype;
-		  memcpy (src_reg, token + 1, tlen - 1);
-		  if (strcmp (dst_reg, src_reg))
-		    {
-		      syntax_err ("found: '%s', expected: -%s", token, dst_reg);
-		      break;
-		    }
-
-		  bpf_insn = build_bpf_arithm_insn (dst_reg, src_reg, 0,
-						    NULL, bpf_arithm_op);
-		  state = ST_EOI;
-		}
-	      break;
-
-	    case 23:
-	      memcpy (src_reg, token, tlen);
-	      state = 11; /* str_cast ( dst_reg offset ) '=' */
-	      break;
-
-	    case 12:
-	      if (ttype == BPF_MOV)
-		state = 13; /* str_cast ( dst_reg offset ) '=' */
-	      break;
-
-	    case 31:
-	      bpf_atomic_insn = ttype;
-	      state = 32; /* lock str_cast ( dst_reg offset ) 'atomic_insn' */
-	      break;
-
-	    default:
-	      syntax_err ("unexpected '%s'", token);
-	      state = ST_EOI;
-	    }
-	  break;
-
-	case BPF_CAST_U8:
-	case BPF_CAST_U16:
-	case BPF_CAST_U32:
-	case BPF_CAST_U64:
-	  bpf_cast = ttype;
-	  switch (state)
-	    {
-	    case 3:
-	      state = 4; /* dst_reg = 'str_cast' */
-	      break;
-
-	    case 0:
-	      state = 8;  /* 'str_cast' */
-	      break;
-
-	    case 26:
-	      state = 27; /* lock 'str_cast' */
-	      break;
-	    }
-	  break;
-
-	case BPF_CHR_OPEN_BR:
-	  switch (state)
-	    {
-	    case 4:
-	      state = 5; /* dst_reg = str_cast '(' */
-	      break;
-
-	    case 8:
-	      state = 9; /* str_cast '(' */
-	      break;
-
-	    case 27:
-	      state = 28; /* lock str_cast '(' */
-	      break;
-
-	    case 34:
-	      state = 35; /* dst_reg = str_cast skb '[' */
-	      break;
-	    }
-	  break;
+      const char *p;
+      char *s;
+      const char *template
+        = (asm_dialect == DIALECT_PSEUDOC ? opcode->pseudoc : opcode->normal);
+
+      /* Do not try to match opcodes with a higher version than the
+         selected ISA spec.  */
+      if (opcode->version > isa_spec)
+        continue;
+
+      memset (&insn, 0, sizeof (struct bpf_insn));
+      insn.size = 8;
+      for (s = str, p = template; *p != '\0';)
+        {
+          if (*p == ' ')
+            {
+              /* Expect zero or more spaces.  */
+              while (*s != '\0' && (*s == ' ' || *s == '\t'))
+                s += 1;
+              p += 1;
+            }
+          else if (*p == '%')
+            {
+              if (*(p + 1) == '%')
+                {
+                  if (*s != '%')
+                    {
+                      PARSE_ERROR ("expected '%%'");
+                      break;
+                    }
+                  p += 2;
+                  s += 1;
+                }
+              else if (*(p + 1) == 'w')
+                {
+                  /* Expect zero or more spaces.  */
+                  while (*s != '\0' && (*s == ' ' || *s == '\t'))
+                    s += 1;
+                  p += 2;
+                }
+              else if (*(p + 1) == 'W')
+                {
+                  /* Expect one or more spaces.  */
+                  if (*s != ' ' && *s != '\t')
+                    {
+                      PARSE_ERROR ("expected white space, got '%s'",
+                                   s);
+                      break;
+                    }
+                  while (*s != '\0' && (*s == ' ' || *s == '\t'))
+                    s += 1;
+                  p += 2;
+                }
+              else if (strncmp (p, "%dr", 3) == 0)
+                {
+                  uint8_t regno;
+                  char *news = parse_bpf_register (s, 'r', &regno);
+
+                  if (news == NULL || (insn.has_dst && regno != insn.dst))
+                    {
+                      if (news != NULL)
+                        PARSE_ERROR ("expected register r%d, got r%d",
+                                     insn.dst, regno);
+                      else
+                        PARSE_ERROR ("expected register name, got '%s'", s);
+                      break;
+                    }
+                  s = news;
+                  insn.dst = regno;
+                  insn.has_dst = 1;
+                  p += 3;
+                }
+              else if (strncmp (p, "%sr", 3) == 0)
+                {
+                  uint8_t regno;
+                  char *news = parse_bpf_register (s, 'r', &regno);
+
+                  if (news == NULL || (insn.has_src && regno != insn.src))
+                    {
+                      if (news != NULL)
+                        PARSE_ERROR ("expected register r%d, got r%d",
+                                     insn.dst, regno);
+                      else
+                        PARSE_ERROR ("expected register name, got '%s'", s);
+                      break;
+                    }
+                  s = news;
+                  insn.src = regno;
+                  insn.has_src = 1;
+                  p += 3;
+                }
+              else if (strncmp (p, "%dw", 3) == 0)
+                {
+                  uint8_t regno;
+                  char *news = parse_bpf_register (s, 'w', &regno);
+
+                  if (news == NULL || (insn.has_dst && regno != insn.dst))
+                    {
+                      if (news != NULL)
+                        PARSE_ERROR ("expected register r%d, got r%d",
+                                     insn.dst, regno);
+                      else
+                        PARSE_ERROR ("expected register name, got '%s'", s);
+                      break;
+                    }
+                  s = news;
+                  insn.dst = regno;
+                  insn.has_dst = 1;
+                  p += 3;
+                }
+              else if (strncmp (p, "%sw", 3) == 0)
+                {
+                  uint8_t regno;
+                  char *news = parse_bpf_register (s, 'w', &regno);
+
+                  if (news == NULL || (insn.has_src && regno != insn.src))
+                    {
+                      if (news != NULL)
+                        PARSE_ERROR ("expected register r%d, got r%d",
+                                     insn.dst, regno);
+                      else
+                        PARSE_ERROR ("expected register name, got '%s'", s);
+                      break;
+                    }
+                  s = news;
+                  insn.src = regno;
+                  insn.has_src = 1;
+                  p += 3;
+                }
+              else if (strncmp (p, "%i32", 4) == 0
+                       || strncmp (p, "%I32", 4) == 0)
+                {
+                  if (p[1] == 'I')
+                    {
+                      while (*s == ' ' || *s == '\t')
+                        s += 1;
+                      if (*s != '+' && *s != '-')
+                        {
+                          PARSE_ERROR ("expected `+' or `-', got `%c'", *s);
+                          break;
+                        }
+                    }
+
+                  s = parse_expression (s, &insn.imm32);
+                  if (s == NULL)
+                    {
+                      PARSE_ERROR ("expected signed 32-bit immediate");
+                      break;
+                    }
+                  insn.has_imm32 = 1;
+                  p += 4;
+                }
+              else if (strncmp (p, "%o16", 4) == 0)
+                {
+                  while (*s == ' ' || *s == '\t')
+                    s += 1;
+                  if (*s != '+' && *s != '-')
+                    {
+                      PARSE_ERROR ("expected `+' or `-', got `%c'", *s);
+                      break;
+                    }
+
+                  s = parse_expression (s, &insn.offset16);
+                  if (s == NULL)
+                    {
+                      PARSE_ERROR ("expected signed 16-bit offset");
+                      break;
+                    }
+                  insn.has_offset16 = 1;
+                  p += 4;
+                }
+              else if (strncmp (p, "%d16", 4) == 0)
+                {
+                  s = parse_expression (s, &insn.disp16);
+                  if (s == NULL)
+                    {
+                      PARSE_ERROR ("expected signed 16-bit displacement");
+                      break;
+                    }
+                  insn.has_disp16 = 1;
+                  p += 4;
+                }
+              else if (strncmp (p, "%d32", 4) == 0)
+                {
+                  s = parse_expression (s, &insn.disp32);
+                  if (s == NULL)
+                    {
+                      PARSE_ERROR ("expected signed 32-bit displacement");
+                      break;
+                    }
+                  insn.has_disp32 = 1;
+                  p += 4;
+                }
+              else if (strncmp (p, "%i64", 4) == 0)
+                {
+                  s = parse_expression (s, &insn.imm64);
+                  if (s == NULL)
+                    {
+                      PARSE_ERROR ("expected signed 64-bit immediate");
+                      break;
+                    }
+                  insn.has_imm64 = 1;
+                  insn.size = 16;
+                  p += 4;
+                }
+              else
+                as_fatal (_("invalid %%-tag in BPF opcode '%s'\n"), template);
+            }
+          else
+            {
+              /* Match a literal character.  */
+              if (*s != *p)
+                {
+                  if (*s == '\0')
+                    PARSE_ERROR ("expected '%c'", *p);
+                  else if (*s == '%')
+                    {
+                      /* This is to workaround a bug in as_bad. */
+                      char tmp[3];
+
+                      tmp[0] = '%';
+                      tmp[1] = '%';
+                      tmp[2] = '\0';
+
+                      PARSE_ERROR ("expected '%c', got '%s'", *p, tmp);
+                    }
+                  else
+                    PARSE_ERROR ("expected '%c', got '%c'", *p, *s);
+                  break;
+                }
+              p += 1;
+              s += 1;
+            }
+        }
 
-	case BPF_CHR_CLSE_BR:
-	  switch (state)
-	    {
-	    case 7:
-	      /* dst_reg = str_cast ( imm32 ')' */
-	      bpf_insn = build_bpf_load_store_insn (dst_reg, src_reg,
-						    bpf_cast, str_imm32, 1);
-	      state = ST_EOI;
-	      break;
-
-	    case 11:
-	      state = 12; /* str_cast ( dst_reg imm32 ')' */
-	      break;
-
-	    case 21:
-	      /* dst_reg = str_cast ( src_reg  offset ')' */
-	      bpf_insn = build_bpf_load_store_insn (dst_reg, src_reg,
-						    bpf_cast, str_offset, 1);
-	      state = ST_EOI;
-	      break;
-
-	    case 22:
-	      state = 23; /* str_cast ( dst_reg offset ')' */
-	      break;
-
-	    case 30:
-	      state = 31; /* lock str_cast ( dst_reg offset ')' */
-	      break;
-
-	    case 37:
-	      /* dst_reg = str_cast skb [ src_reg imm32 ']' */
-	      if (*dst_reg != 'w' && !strcmp ("r0", dst_reg))
-		bpf_insn = build_bpf_non_generic_load (*src_reg != '\0' ? src_reg : NULL,
-						       bpf_cast, str_imm32);
-	      else
-		syntax_err ("invalid register operand: '%s'", dst_reg);
-
-	      state = ST_EOI;
-	      break;
-	    }
-	  break;
+      if (*p == '\0')
+        {
+          /* Allow white spaces at the end of the line.  */
+          while (*s != '\0' && (*s == ' ' || *s == '\t'))
+            s += 1;
+          if (*s == '\0')
+            /* We parsed an instruction successfully.  */
+            break;
+          PARSE_ERROR ("extra junk at end of line");
+        }
+    }
 
-	case BPF_EXPR:
-	  switch (state)
-	    {
-	    case 3:
-	      {
-		/* dst_reg bpf_arithm_op 'imm32' */
-		int load64 = 0;
-
-		memcpy (str_imm32, token, tlen);
-		memset (token, 0, tlen);
-
-		if ((ttype = get_token ([...]

[diff truncated at 100000 bytes]

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2023-07-21 10:24 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-21 10:24 [binutils-gdb] DesCGENization of the BPF binutils port Jose E. Marchesi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).