// Copyright (c) 2012-2013 ARM Limited
// All rights reserved
//
// The license below extends only to copyright in the software and shall
// not be construed as granting a license to any other intellectual
// property including but not limited to intellectual property relating
// to a hardware implementation of the functionality of the software
// licensed hereunder.  You may use the software subject to the license
// terms below provided that you ensure that this notice is replicated
// unmodified and in its entirety in all distributions of the software,
// modified or unmodified, in source code or in binary form.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Authors: Giacomo Gabrielli
//          Mbou Eyole

output header {{
namespace Aarch64
{
    // AdvSIMD three same
    template <typename DecoderFeatures>
    StaticInstPtr decodeNeon3Same(ExtMachInst machInst);
    // AdvSIMD three different
    inline StaticInstPtr decodeNeon3Diff(ExtMachInst machInst);
    // AdvSIMD two-reg misc
    inline StaticInstPtr decodeNeon2RegMisc(ExtMachInst machInst);
    // AdvSIMD across lanes
    inline StaticInstPtr decodeNeonAcrossLanes(ExtMachInst machInst);
    // AdvSIMD copy
    inline StaticInstPtr decodeNeonCopy(ExtMachInst machInst);
    // AdvSIMD vector x indexed element
    template <typename DecoderFeatures>
    StaticInstPtr decodeNeonIndexedElem(ExtMachInst machInst);
    // AdvSIMD modified immediate
    inline StaticInstPtr decodeNeonModImm(ExtMachInst machInst);
    // AdvSIMD shift by immediate
    inline StaticInstPtr decodeNeonShiftByImm(ExtMachInst machInst);
    // AdvSIMD TBL/TBX
    inline StaticInstPtr decodeNeonTblTbx(ExtMachInst machInst);
    // AdvSIMD ZIP/UZP/TRN
    inline StaticInstPtr decodeNeonZipUzpTrn(ExtMachInst machInst);
    // AdvSIMD EXT
    inline StaticInstPtr decodeNeonExt(ExtMachInst machInst);

    // AdvSIMD scalar three same
    inline StaticInstPtr decodeNeonSc3Same(ExtMachInst machInst);
    // AdvSIMD scalar three different
    inline StaticInstPtr decodeNeonSc3Diff(ExtMachInst machInst);
    // AdvSIMD scalar two-reg misc
    inline StaticInstPtr decodeNeonSc2RegMisc(ExtMachInst machInst);
    // AdvSIMD scalar pairwise
    inline StaticInstPtr decodeNeonScPwise(ExtMachInst machInst);
    // AdvSIMD scalar copy
    inline StaticInstPtr decodeNeonScCopy(ExtMachInst machInst);
    // AdvSIMD scalar x indexed element
    inline StaticInstPtr decodeNeonScIndexedElem(ExtMachInst machInst);
    // AdvSIMD scalar shift by immediate
    inline StaticInstPtr decodeNeonScShiftByImm(ExtMachInst machInst);

    // AdvSIMD load/store
    inline StaticInstPtr decodeNeonMem(ExtMachInst machInst);
}
}};

output decoder {{
namespace Aarch64
{
    template <typename DecoderFeatures>
    StaticInstPtr
    decodeNeon3Same(ExtMachInst machInst)
    {
        uint8_t q = bits(machInst, 30);
        uint8_t u = bits(machInst, 29);
        uint8_t size = bits(machInst, 23, 22);
        uint8_t opcode = bits(machInst, 15, 11);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);

        uint8_t size_q = (size << 1) | q;
        uint8_t sz_q = size_q & 0x3;

        switch (opcode) {
          case 0x00:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UhaddDX, UhaddQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<ShaddDX, ShaddQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x01:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeXReg<UqaddDX, UqaddQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeXReg<SqaddDX, SqaddQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x02:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UrhaddDX, UrhaddQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SrhaddDX, SrhaddQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x03:
            switch (size) {
              case 0x0:
                if (u) {
                    if (q)
                        return new EorQX<uint64_t>(machInst, vd, vn, vm);
                    else
                        return new EorDX<uint64_t>(machInst, vd, vn, vm);
                } else {
                    if (q)
                        return new AndQX<uint64_t>(machInst, vd, vn, vm);
                    else
                        return new AndDX<uint64_t>(machInst, vd, vn, vm);
                }
              case 0x1:
                if (u) {
                    if (q)
                        return new BslQX<uint64_t>(machInst, vd, vn, vm);
                    else
                        return new BslDX<uint64_t>(machInst, vd, vn, vm);
                } else {
                    if (q)
                        return new BicQX<uint64_t>(machInst, vd, vn, vm);
                    else
                        return new BicDX<uint64_t>(machInst, vd, vn, vm);
                }
              case 0x2:
                if (u) {
                    if (q)
                        return new BitQX<uint64_t>(machInst, vd, vn, vm);
                    else
                        return new BitDX<uint64_t>(machInst, vd, vn, vm);
                } else {
                    if (q)
                        return new OrrQX<uint64_t>(machInst, vd, vn, vm);
                    else
                        return new OrrDX<uint64_t>(machInst, vd, vn, vm);
                }
              case 0x3:
                if (u) {
                    if (q)
                        return new BifQX<uint64_t>(machInst, vd, vn, vm);
                    else
                        return new BifDX<uint64_t>(machInst, vd, vn, vm);
                } else {
                    if (q)
                        return new OrnQX<uint64_t>(machInst, vd, vn, vm);
                    else
                        return new OrnDX<uint64_t>(machInst, vd, vn, vm);
                }
            }
          case 0x04:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UhsubDX, UhsubQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<ShsubDX, ShsubQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x05:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeXReg<UqsubDX, UqsubQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeXReg<SqsubDX, SqsubQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x06:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeXReg<CmhiDX, CmhiQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeXReg<CmgtDX, CmgtQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x07:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeXReg<CmhsDX, CmhsQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeXReg<CmgeDX, CmgeQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x08:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeXReg<UshlDX, UshlQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeXReg<SshlDX, SshlQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x09:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeXReg<UqshlDX, UqshlQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeXReg<SqshlDX, SqshlQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x0a:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeXReg<UrshlDX, UrshlQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeXReg<SrshlDX, SrshlQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x0b:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeXReg<UqrshlDX, UqrshlQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeXReg<SqrshlDX, SqrshlQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x0c:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UmaxDX, UmaxQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SmaxDX, SmaxQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x0d:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UminDX, UminQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SminDX, SminQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x0e:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UabdDX, UabdQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SabdDX, SabdQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x0f:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UabaDX, UabaQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SabaDX, SabaQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x10:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeXReg<SubDX, SubQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonUThreeXReg<AddDX, AddQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x11:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeXReg<CmeqDX, CmeqQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonUThreeXReg<CmtstDX, CmtstQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x12:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<MlsDX, MlsQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonUThreeSReg<MlaDX, MlaQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x13:
            if (size == 0x3 || (size != 0x0 && bits(machInst, 29)))
                return new Unknown64(machInst);
            if (u) {
                if (q)
                    return new PmulQX<uint8_t>(machInst, vd, vn, vm);
                else
                    return new PmulDX<uint8_t>(machInst, vd, vn, vm);
            } else {
                return decodeNeonUThreeSReg<MulDX, MulQX>(
                    q, size, machInst, vd, vn, vm);
            }
          case 0x14:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UmaxpDX, UmaxpQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SmaxpDX, SmaxpQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x15:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UminpDX, UminpQX>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SminpDX, SminpQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x16:
            if (size == 0x3 || size == 0x0)
                return new Unknown64(machInst);
            if (u) {
                if (q)
                    return decodeNeonSThreeHAndWReg<SqrdmulhQX>(
                        size, machInst, vd, vn, vm);
                else
                    return decodeNeonSThreeHAndWReg<SqrdmulhDX>(
                        size, machInst, vd, vn, vm);
            } else {
                if (q)
                    return decodeNeonSThreeHAndWReg<SqdmulhQX>(
                        size, machInst, vd, vn, vm);
                else
                    return decodeNeonSThreeHAndWReg<SqdmulhDX>(
                        size, machInst, vd, vn, vm);
            }
          case 0x17:
            if (u || size_q == 0x6)
                return new Unknown64(machInst);
            else
                return decodeNeonUThreeXReg<AddpDX, AddpQX>(
                    q, size, machInst, vd, vn, vm);
          case 0x18:
            if (sz_q == 0x2)
                return new Unknown64(machInst);
            if (size < 0x2) {
                if (u)
                    return decodeNeonUThreeFpReg<FmaxnmpDX, FmaxnmpQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
                else
                    return decodeNeonUThreeFpReg<FmaxnmDX, FmaxnmQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
            } else {
                if (u)
                    return decodeNeonUThreeFpReg<FminnmpDX, FminnmpQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
                else
                    return decodeNeonUThreeFpReg<FminnmDX, FminnmQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
            }
          case 0x19:
            if (size < 0x2) {
                if (u || sz_q == 0x2)
                    return new Unknown64(machInst);
                else
                    return decodeNeonUThreeFpReg<FmlaDX, FmlaQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
            } else {
                if (u || sz_q == 0x2)
                    return new Unknown64(machInst);
                else
                    return decodeNeonUThreeFpReg<FmlsDX, FmlsQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
            }
          case 0x1a:
            if (sz_q == 0x2)
                return new Unknown64(machInst);
            if (size < 0x2) {
                if (u)
                    return decodeNeonUThreeFpReg<FaddpDX, FaddpQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
                else
                    return decodeNeonUThreeFpReg<FaddDX, FaddQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
            } else {
                if (u)
                    return decodeNeonUThreeFpReg<FabdDX, FabdQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
                else
                    return decodeNeonUThreeFpReg<FsubDX, FsubQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
            }
          case 0x1b:
            if (size < 0x2 && sz_q != 0x2) {
                if (u)
                    return decodeNeonUThreeFpReg<FmulDX, FmulQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
                else
                    return decodeNeonUThreeFpReg<FmulxDX, FmulxQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
            } else {
                return new Unknown64(machInst);
            }
          case 0x1c:
            if (size < 0x2) {
                if (u)
                    return decodeNeonUThreeFpReg<FcmgeDX, FcmgeQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
                else
                    return decodeNeonUThreeFpReg<FcmeqDX, FcmeqQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
            } else {
                if (u)
                    return decodeNeonUThreeFpReg<FcmgtDX, FcmgtQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
                else
                    return new Unknown64(machInst);
            }
          case 0x1d:
            if (size < 0x2) {
                if (u)
                    return decodeNeonUThreeFpReg<FacgeDX, FacgeQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
                else
                    return new Unknown64(machInst);
            } else {
                if (u)
                    return decodeNeonUThreeFpReg<FacgtDX, FacgtQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
                else
                    return new Unknown64(machInst);
            }
          case 0x1e:
            if (sz_q == 0x2)
                return new Unknown64(machInst);
            if (size < 0x2) {
                if (u)
                    return decodeNeonUThreeFpReg<FmaxpDX, FmaxpQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
                else
                    return decodeNeonUThreeFpReg<FmaxDX, FmaxQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
            } else {
                if (u)
                    return decodeNeonUThreeFpReg<FminpDX, FminpQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
                else
                    return decodeNeonUThreeFpReg<FminDX, FminQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
            }
          case 0x1f:
            if (sz_q == 0x2)
                return new Unknown64(machInst);
            if (size < 0x2) {
                if (u)
                    return decodeNeonUThreeFpReg<FdivDX, FdivQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
                else
                    return decodeNeonUThreeFpReg<FrecpsDX, FrecpsQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
            } else {
                if (u)
                    return new Unknown64(machInst);
                else
                    return decodeNeonUThreeFpReg<FrsqrtsDX, FrsqrtsQX>(
                        q, size & 0x1, machInst, vd, vn, vm);
            }
          default:
            return new Unknown64(machInst);
        }
    }

    StaticInstPtr
    decodeNeon3Diff(ExtMachInst machInst)
    {
        uint8_t q = bits(machInst, 30);
        uint8_t u = bits(machInst, 29);
        uint8_t size = bits(machInst, 23, 22);
        uint8_t opcode = bits(machInst, 15, 12);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);

        switch (opcode) {
          case 0x0:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UaddlX, Uaddl2X>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SaddlX, Saddl2X>(
                    q, size, machInst, vd, vn, vm);
          case 0x1:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UaddwX, Uaddw2X>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SaddwX, Saddw2X>(
                    q, size, machInst, vd, vn, vm);
          case 0x2:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UsublX, Usubl2X>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SsublX, Ssubl2X>(
                    q, size, machInst, vd, vn, vm);
          case 0x3:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UsubwX, Usubw2X>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SsubwX, Ssubw2X>(
                    q, size, machInst, vd, vn, vm);
          case 0x4:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<RaddhnX, Raddhn2X>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonUThreeSReg<AddhnX, Addhn2X>(
                    q, size, machInst, vd, vn, vm);
          case 0x5:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UabalX, Uabal2X>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SabalX, Sabal2X>(
                    q, size, machInst, vd, vn, vm);
          case 0x6:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<RsubhnX, Rsubhn2X>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonUThreeSReg<SubhnX, Subhn2X>(
                    q, size, machInst, vd, vn, vm);
          case 0x7:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UabdlX, Uabdl2X>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SabdlX, Sabdl2X>(
                    q, size, machInst, vd, vn, vm);
          case 0x8:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UmlalX, Umlal2X>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SmlalX, Smlal2X>(
                    q, size, machInst, vd, vn, vm);
          case 0x9:
            if (u || (size == 0x0 || size == 0x3)) {
                return new Unknown64(machInst);
            } else {
                if (q) {
                    return decodeNeonSThreeHAndWReg<Sqdmlal2X>(
                        size, machInst, vd, vn, vm);
                } else {
                    return decodeNeonSThreeHAndWReg<SqdmlalX>(
                        size, machInst, vd, vn, vm);
                }
            }
          case 0xa:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UmlslX, Umlsl2X>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SmlslX, Smlsl2X>(
                    q, size, machInst, vd, vn, vm);
          case 0xb:
            if (u || (size == 0x0 || size == 0x3)) {
                return new Unknown64(machInst);
            } else {
                if (q) {
                    return decodeNeonSThreeHAndWReg<Sqdmlsl2X>(
                        size, machInst, vd, vn, vm);
                } else {
                    return decodeNeonSThreeHAndWReg<SqdmlslX>(
                        size, machInst, vd, vn, vm);
                }
            }
          case 0xc:
            if (size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeSReg<UmullX, Umull2X>(
                    q, size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeSReg<SmullX, Smull2X>(
                    q, size, machInst, vd, vn, vm);
          case 0xd:
            if (u || (size == 0x0 || size == 0x3)) {
                return new Unknown64(machInst);
            } else {
                if (q) {
                    return decodeNeonSThreeHAndWReg<Sqdmull2X>(
                        size, machInst, vd, vn, vm);
                } else {
                    return decodeNeonSThreeHAndWReg<SqdmullX>(
                        size, machInst, vd, vn, vm);
                }
            }
          case 0xe:
            if (u || size != 0) {
                return new Unknown64(machInst);
            } else {
                if (q)
                    return new Pmull2X<uint8_t>(machInst, vd, vn, vm);
                else
                    return new PmullX<uint8_t>(machInst, vd, vn, vm);
            }
          default:
            return new Unknown64(machInst);
        }
    }

    StaticInstPtr
    decodeNeon2RegMisc(ExtMachInst machInst)
    {
        uint8_t q = bits(machInst, 30);
        uint8_t u = bits(machInst, 29);
        uint8_t size = bits(machInst, 23, 22);
        uint8_t opcode = bits(machInst, 16, 12);

        IntRegIndex vd = (IntRegIndex)(uint8_t)bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex)(uint8_t)bits(machInst, 9, 5);

        uint8_t size_q = (size << 1) | q;
        uint8_t sz_q = size_q & 0x3;
        uint8_t op = (uint8_t)((bits(machInst, 12) << 1) |
                               bits(machInst, 29));
        uint8_t switchVal = opcode | ((u ? 1 : 0) << 5);

        switch (switchVal) {
          case 0x00:
            if (op + size >= 3)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscSReg<Rev64DX, Rev64QX>(
                q, size, machInst, vd, vn);
          case 0x01:
            if (op + size >= 3)
                return new Unknown64(machInst);
            if (q)
                return new Rev16QX<uint8_t>(machInst, vd, vn);
            else
                return new Rev16DX<uint8_t>(machInst, vd, vn);
          case 0x02:
            if (size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscSReg<SaddlpDX, SaddlpQX>(
                q, size, machInst, vd, vn);
          case 0x03:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscXReg<SuqaddDX, SuqaddQX>(
                q, size, machInst, vd, vn);
          case 0x04:
            if (size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscSReg<ClsDX, ClsQX>(
                q, size, machInst, vd, vn);
          case 0x05:
            if (size != 0x0)
                return new Unknown64(machInst);
            if (q)
                return new CntQX<uint8_t>(machInst, vd, vn);
            else
                return new CntDX<uint8_t>(machInst, vd, vn);
          case 0x06:
            if (size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscSReg<SadalpDX, SadalpQX>(
                q, size, machInst, vd, vn);
          case 0x07:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscXReg<SqabsDX, SqabsQX>(
                q, size, machInst, vd, vn);
          case 0x08:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscXReg<CmgtZeroDX, CmgtZeroQX>(
                q, size, machInst, vd, vn);
          case 0x09:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscXReg<CmeqZeroDX, CmeqZeroQX>(
                q, size, machInst, vd, vn);
          case 0x0a:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscXReg<CmltZeroDX, CmltZeroQX>(
                q, size, machInst, vd, vn);
          case 0x0b:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscXReg<AbsDX, AbsQX>(
                q, size, machInst, vd, vn);
          case 0x0c:
            if (size < 0x2 || sz_q == 0x2)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscFpReg<FcmgtZeroDX, FcmgtZeroQX>(
                q, size & 0x1, machInst, vd, vn);
          case 0x0d:
            if (size < 0x2 || sz_q == 0x2)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscFpReg<FcmeqZeroDX, FcmeqZeroQX>(
                q, size & 0x1, machInst, vd, vn);
          case 0x0e:
            if (size < 0x2 || sz_q == 0x2)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscFpReg<FcmltZeroDX, FcmltZeroQX>(
                q, size & 0x1, machInst, vd, vn);
          case 0x0f:
            if (size < 0x2 || sz_q == 0x2)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscFpReg<FabsDX, FabsQX>(
                q, size & 0x1, machInst, vd, vn);
          case 0x12:
            if (size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscSReg<XtnX, Xtn2X>(
                q, size, machInst, vd, vn);
          case 0x14:
            if (size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscSReg<SqxtnX, Sqxtn2X>(
                q, size, machInst, vd, vn);
          case 0x16:
            if (size > 0x1)
                return new Unknown64(machInst);
            if (q) {
                if (size)
                    return new Fcvtn2X<uint32_t>(machInst, vd, vn);
                else
                    return new Fcvtn2X<uint16_t>(machInst, vd, vn);
            } else {
                if (size)
                    return new FcvtnX<uint32_t>(machInst, vd, vn);
                else
                    return new FcvtnX<uint16_t>(machInst, vd, vn);
            }
          case 0x17:
            if (size > 0x1)
                return new Unknown64(machInst);
            if (q) {
                if (size)
                    return new Fcvtl2X<uint32_t>(machInst, vd, vn);
                else
                    return new Fcvtl2X<uint16_t>(machInst, vd, vn);
            } else {
                if (size)
                    return new FcvtlX<uint32_t>(machInst, vd, vn);
                else
                    return new FcvtlX<uint16_t>(machInst, vd, vn);
            }
          case 0x18:
            if (sz_q == 0x2)
                return new Unknown64(machInst);
            if (size < 0x2)
                return decodeNeonUTwoMiscFpReg<FrintnDX, FrintnQX>(
                    q, size & 0x1, machInst, vd, vn);
            else
                return decodeNeonUTwoMiscFpReg<FrintpDX, FrintpQX>(
                    q, size & 0x1, machInst, vd, vn);
          case 0x19:
            if (sz_q == 0x2)
                return new Unknown64(machInst);
            if (size < 0x2)
                return decodeNeonUTwoMiscFpReg<FrintmDX, FrintmQX>(
                    q, size & 0x1, machInst, vd, vn);
            else
                return decodeNeonUTwoMiscFpReg<FrintzDX, FrintzQX>(
                    q, size & 0x1, machInst, vd, vn);
          case 0x1a:
            if (sz_q == 0x2)
                return new Unknown64(machInst);
            if (size < 0x2)
                return decodeNeonUTwoMiscFpReg<FcvtnsDX, FcvtnsQX>(
                    q, size & 0x1, machInst, vd, vn);
            else
                return decodeNeonUTwoMiscFpReg<FcvtpsDX, FcvtpsQX>(
                    q, size & 0x1, machInst, vd, vn);
          case 0x1b:
            if (sz_q == 0x2)
                return new Unknown64(machInst);
            if (size < 0x2)
                return decodeNeonUTwoMiscFpReg<FcvtmsDX, FcvtmsQX>(
                    q, size & 0x1, machInst, vd, vn);
            else
                return decodeNeonUTwoMiscFpReg<FcvtzsIntDX, FcvtzsIntQX>(
                    q, size & 0x1, machInst, vd, vn);
          case 0x1c:
            if (size < 0x2) {
                if (sz_q == 0x2)
                    return new Unknown64(machInst);
                return decodeNeonUTwoMiscFpReg<FcvtasDX, FcvtasQX>(
                    q, size & 0x1, machInst, vd, vn);
            } else {
                if (size & 0x1)
                    return new Unknown64(machInst);
                if (q)
                    return new UrecpeQX<uint32_t>(machInst, vd, vn);
                else
                    return new UrecpeDX<uint32_t>(machInst, vd, vn);
            }
          case 0x1d:
            if (sz_q == 0x2)
                return new Unknown64(machInst);
            if (size < 0x2) {
                if (q) {
                    if (size & 0x1)
                        return new ScvtfIntDQX<uint64_t>(machInst, vd, vn);
                    else
                        return new ScvtfIntSQX<uint32_t>(machInst, vd, vn);
                } else {
                    if (size & 0x1)
                        return new Unknown(machInst);
                    else
                        return new ScvtfIntDX<uint32_t>(machInst, vd, vn);
                }
            } else {
                return decodeNeonUTwoMiscFpReg<FrecpeDX, FrecpeQX>(
                    q, size & 0x1, machInst, vd, vn);
            }
          case 0x20:
            if (op + size >= 3)
                return new Unknown64(machInst);
            if (q) {
                if (size & 0x1)
                    return new Rev32QX<uint16_t>(machInst, vd, vn);
                else
                    return new Rev32QX<uint8_t>(machInst, vd, vn);
            } else {
                if (size & 0x1)
                    return new Rev32DX<uint16_t>(machInst, vd, vn);
                else
                    return new Rev32DX<uint8_t>(machInst, vd, vn);
            }
          case 0x22:
            if (size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscSReg<UaddlpDX, UaddlpQX>(
                q, size, machInst, vd, vn);
          case 0x23:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscXReg<UsqaddDX, UsqaddQX>(
                q, size, machInst, vd, vn);
            return new Unknown64(machInst);
          case 0x24:
            if (size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscSReg<ClzDX, ClzQX>(
                q, size, machInst, vd, vn);
          case 0x25:
            if (size == 0x0) {
                if (q)
                    return new MvnQX<uint64_t>(machInst, vd, vn);
                else
                    return new MvnDX<uint64_t>(machInst, vd, vn);
            } else if (size == 0x1) {
                if (q)
                    return new RbitQX<uint8_t>(machInst, vd, vn);
                else
                    return new RbitDX<uint8_t>(machInst, vd, vn);
            } else {
                return new Unknown64(machInst);
            }
          case 0x26:
            if (size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscSReg<UadalpDX, UadalpQX>(
                q, size, machInst, vd, vn);
          case 0x27:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscXReg<SqnegDX, SqnegQX>(
                q, size, machInst, vd, vn);
          case 0x28:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscXReg<CmgeZeroDX, CmgeZeroQX>(
                q, size, machInst, vd, vn);
          case 0x29:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscXReg<CmleZeroDX, CmleZeroQX>(
                q, size, machInst, vd, vn);
          case 0x2b:
            if (size_q == 0x6)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscXReg<NegDX, NegQX>(
                q, size, machInst, vd, vn);
          case 0x2c:
            if (size < 0x2 || sz_q == 0x2)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscFpReg<FcmgeZeroDX, FcmgeZeroQX>(
                q, size & 0x1, machInst, vd, vn);
          case 0x2d:
            if (size < 0x2 || sz_q == 0x2)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscFpReg<FcmleZeroDX, FcmleZeroQX>(
                q, size & 0x1, machInst, vd, vn);
          case 0x2f:
            if (size < 0x2 || size_q == 0x6)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscFpReg<FnegDX, FnegQX>(
                q, size & 0x1, machInst, vd, vn);
          case 0x32:
            if (size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonSTwoMiscSReg<SqxtunX, Sqxtun2X>(
                q, size, machInst, vd, vn);
          case 0x33:
            if (size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscSReg<ShllX, Shll2X>(
                q, size, machInst, vd, vn);
          case 0x34:
            if (size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscSReg<UqxtnX, Uqxtn2X>(
                q, size, machInst, vd, vn);
          case 0x36:
            if (size != 0x1)
                return new Unknown64(machInst);
            if (q)
                return new Fcvtxn2X<uint32_t>(machInst, vd, vn);
            else
                return new FcvtxnX<uint32_t>(machInst, vd, vn);
          case 0x38:
            if (size > 0x1 || sz_q == 0x2)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscFpReg<FrintaDX, FrintaQX>(
                q, size & 0x1, machInst, vd, vn);
          case 0x39:
            if (sz_q == 0x2)
                return new Unknown64(machInst);
            if (size < 0x2)
                return decodeNeonUTwoMiscFpReg<FrintxDX, FrintxQX>(
                    q, size & 0x1, machInst, vd, vn);
            else
                return decodeNeonUTwoMiscFpReg<FrintiDX, FrintiQX>(
                    q, size & 0x1, machInst, vd, vn);
          case 0x3a:
            if (sz_q == 0x2)
                return new Unknown64(machInst);
            if (size < 0x2)
                return decodeNeonUTwoMiscFpReg<FcvtnuDX, FcvtnuQX>(
                    q, size & 0x1, machInst, vd, vn);
            else
                return decodeNeonUTwoMiscFpReg<FcvtpuDX, FcvtpuQX>(
                    q, size & 0x1, machInst, vd, vn);
          case 0x3b:
            if (sz_q == 0x2)
                return new Unknown64(machInst);
            if (size < 0x2)
                return decodeNeonUTwoMiscFpReg<FcvtmuDX, FcvtmuQX>(
                    q, size & 0x1, machInst, vd, vn);
            else
                return decodeNeonUTwoMiscFpReg<FcvtzuIntDX, FcvtzuIntQX>(
                    q, size & 0x1, machInst, vd, vn);
          case 0x3c:
            if (size < 0x2) {
                return decodeNeonUTwoMiscFpReg<FcvtauDX, FcvtauQX>(
                    q, size & 0x1, machInst, vd, vn);
            } else if (size == 0x2) {
                if (q)
                    return new UrsqrteQX<uint32_t>(machInst, vd, vn);
                else
                    return new UrsqrteDX<uint32_t>(machInst, vd, vn);
            } else {
                return new Unknown64(machInst);
            }
          case 0x3d:
            if (sz_q == 0x2)
                return new Unknown64(machInst);
            if (size < 0x2)
                return decodeNeonUTwoMiscFpReg<UcvtfIntDX, UcvtfIntQX>(
                    q, size & 0x1, machInst, vd, vn);
            else
                return decodeNeonUTwoMiscFpReg<FrsqrteDX, FrsqrteQX>(
                    q, size & 0x1, machInst, vd, vn);
          case 0x3f:
            if (size < 0x2 || sz_q == 0x2)
                return new Unknown64(machInst);
            return decodeNeonUTwoMiscFpReg<FsqrtDX, FsqrtQX>(
                q, size & 0x1, machInst, vd, vn);
          default:
            return new Unknown64(machInst);
        }
    }

    StaticInstPtr
    decodeNeonAcrossLanes(ExtMachInst machInst)
    {
        uint8_t q = bits(machInst, 30);
        uint8_t u = bits(machInst, 29);
        uint8_t size = bits(machInst, 23, 22);
        uint8_t opcode = bits(machInst, 16, 12);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);

        uint8_t size_q = (size << 1) | q;
        uint8_t sz_q = size_q & 0x3;
        uint8_t switchVal = opcode | ((u ? 1 : 0) << 5);

        switch (switchVal) {
          case 0x03:
            if (size_q == 0x4 || size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonSAcrossLanesLongReg<SaddlvDX, SaddlvQX,
                                                 SaddlvBQX>(
                q, size, machInst, vd, vn);
          case 0x0a:
            if (size_q == 0x4 || size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonSAcrossLanesReg<SmaxvDX, SmaxvQX>(
                q, size, machInst, vd, vn);
          case 0x1a:
            if (size_q == 0x4 || size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonSAcrossLanesReg<SminvDX, SminvQX>(
                q, size, machInst, vd, vn);
          case 0x1b:
            if (size_q == 0x4 || size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonUAcrossLanesReg<AddvDX, AddvQX>(
                q, size, machInst, vd, vn);
          case 0x23:
            if (size_q == 0x4 || size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonUAcrossLanesLongReg<UaddlvDX, UaddlvQX,
                                                 UaddlvBQX>(
                q, size, machInst, vd, vn);
          case 0x2a:
            if (size_q == 0x4 || size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonUAcrossLanesReg<UmaxvDX, UmaxvQX>(
                q, size, machInst, vd, vn);
          case 0x2c:
            if (sz_q != 0x1)
                return new Unknown64(machInst);
            if (size < 0x2) {
                if (q)
                    return new FmaxnmvQX<uint32_t>(machInst, vd, vn);
                else
                    return new Unknown64(machInst);
            } else {
                if (q)
                    return new FminnmvQX<uint32_t>(machInst, vd, vn);
                else
                    return new Unknown64(machInst);
            }
          case 0x2f:
            if (sz_q != 0x1)
                return new Unknown64(machInst);
            if (size < 0x2) {
                if (q)
                    return new FmaxvQX<uint32_t>(machInst, vd, vn);
                else
                    return new Unknown64(machInst);
            } else {
                if (q)
                    return new FminvQX<uint32_t>(machInst, vd, vn);
                else
                    return new Unknown64(machInst);
            }
          case 0x3a:
            if (size_q == 0x4 || size == 0x3)
                return new Unknown64(machInst);
            return decodeNeonUAcrossLanesReg<UminvDX, UminvQX>(
                q, size, machInst, vd, vn);
          default:
            return new Unknown64(machInst);
        }
    }

    StaticInstPtr
    decodeNeonCopy(ExtMachInst machInst)
    {
        uint8_t q = bits(machInst, 30);
        uint8_t op = bits(machInst, 29);
        uint8_t imm5 = bits(machInst, 20, 16);
        uint8_t imm4 = bits(machInst, 14, 11);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);

        uint8_t imm5_pos = findLsbSet(imm5);
        uint8_t index1 = 0, index2 = 0;

        if (op) {
            if (!q || (imm4 & mask(imm5_pos)))
                return new Unknown64(machInst);

            index1 = bits(imm5, 4, imm5_pos + 1);  // dst
            index2 = bits(imm4, 3, imm5_pos);  // src

            switch (imm5_pos) {
              case 0:
                return new InsElemX<uint8_t>(machInst, vd, vn, index1, index2);
              case 1:
                return new InsElemX<uint16_t>(machInst, vd, vn, index1, index2);
              case 2:
                return new InsElemX<uint32_t>(machInst, vd, vn, index1, index2);
              case 3:
                return new InsElemX<uint64_t>(machInst, vd, vn, index1, index2);
              default:
                return new Unknown64(machInst);
            }
        }

        switch (imm4) {
          case 0x0:
            index1 = bits(imm5, 4, imm5_pos + 1);
            switch (imm5_pos) {
              case 0:
                if (q)
                    return new DupElemQX<uint8_t>(machInst, vd, vn, index1);
                else
                    return new DupElemDX<uint8_t>(machInst, vd, vn, index1);
              case 1:
                if (q)
                    return new DupElemQX<uint16_t>(machInst, vd, vn, index1);
                else
                    return new DupElemDX<uint16_t>(machInst, vd, vn, index1);
              case 2:
                if (q)
                    return new DupElemQX<uint32_t>(machInst, vd, vn, index1);
                else
                    return new DupElemDX<uint32_t>(machInst, vd, vn, index1);
              case 3:
                if (q)
                    return new DupElemQX<uint64_t>(machInst, vd, vn, index1);
                else
                    return new Unknown64(machInst);
              default:
                return new Unknown64(machInst);
            }
          case 0x1:
            switch (imm5) {
              case 0x1:
                if (q)
                    return new DupGprWQX<uint8_t>(machInst, vd, vn);
                else
                    return new DupGprWDX<uint8_t>(machInst, vd, vn);
              case 0x2:
                if (q)
                    return new DupGprWQX<uint16_t>(machInst, vd, vn);
                else
                    return new DupGprWDX<uint16_t>(machInst, vd, vn);
              case 0x4:
                if (q)
                    return new DupGprWQX<uint32_t>(machInst, vd, vn);
                else
                    return new DupGprWDX<uint32_t>(machInst, vd, vn);
              case 0x8:
                if (q)
                    return new DupGprXQX<uint64_t>(machInst, vd, vn);
                else
                    return new Unknown64(machInst);
            }
          case 0x3:
            index1 = imm5 >> (imm5_pos + 1);
            switch (imm5_pos) {
              case 0:
                return new InsGprWX<uint8_t>(machInst, vd, vn, index1);
              case 1:
                return new InsGprWX<uint16_t>(machInst, vd, vn, index1);
              case 2:
                return new InsGprWX<uint32_t>(machInst, vd, vn, index1);
              case 3:
                return new InsGprXX<uint64_t>(machInst, vd, vn, index1);
              default:
                return new Unknown64(machInst);
            }
          case 0x5:
            index1 = bits(imm5, 4, imm5_pos + 1);
            switch (imm5_pos) {
              case 0:
                if (q)
                    return new SmovXX<int8_t>(machInst, vd, vn, index1);
                else
                    return new SmovWX<int8_t>(machInst, vd, vn, index1);
              case 1:
                if (q)
                    return new SmovXX<int16_t>(machInst, vd, vn, index1);
                else
                    return new SmovWX<int16_t>(machInst, vd, vn, index1);
              case 2:
                if (q)
                    return new SmovXX<int32_t>(machInst, vd, vn, index1);
                else
                    return new Unknown64(machInst);
              default:
                return new Unknown64(machInst);
            }
          case 0x7:
            index1 = imm5 >> (imm5_pos + 1);

            if ((q && imm5_pos != 3) || (!q && imm5_pos >= 3))
                return new Unknown64(machInst);

            switch (imm5_pos) {
              case 0:
                return new UmovWX<uint8_t>(machInst, vd, vn, index1);
              case 1:
                return new UmovWX<uint16_t>(machInst, vd, vn, index1);
              case 2:
                return new UmovWX<uint32_t>(machInst, vd, vn, index1);
              case 3:
                return new UmovXX<uint64_t>(machInst, vd, vn, index1);
              default:
                return new Unknown64(machInst);
            }
          default:
            return new Unknown64(machInst);
        }
    }

    template <typename DecoderFeatures>
    StaticInstPtr
    decodeNeonIndexedElem(ExtMachInst machInst)
    {
        uint8_t q = bits(machInst, 30);
        uint8_t u = bits(machInst, 29);
        uint8_t size = bits(machInst, 23, 22);
        uint8_t L = bits(machInst, 21);
        uint8_t M = bits(machInst, 20);
        uint8_t opcode = bits(machInst, 15, 12);
        uint8_t H = bits(machInst, 11);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
        IntRegIndex vm_bf = (IntRegIndex) (uint8_t) bits(machInst, 19, 16);

        uint8_t index = 0;
        uint8_t index_fp = 0;
        uint8_t vmh = 0;
        uint8_t sz = size & 0x1;
        uint8_t sz_q = (sz << 1) | bits(machInst, 30);
        uint8_t sz_L = (sz << 1) | L;

        // Index and 2nd register operand for integer instructions
        if (size == 0x1) {
            index = (H << 2) | (L << 1) | M;
            // vmh = 0;
        } else if (size == 0x2) {
            index = (H << 1) | L;
            vmh = M;
        }
        IntRegIndex vm = (IntRegIndex) (uint8_t) (vmh << 4 | vm_bf);

        // Index and 2nd register operand for FP instructions
        vmh = M;
        if ((size & 0x1) == 0) {
            index_fp = (H << 1) | L;
        } else if (L == 0) {
            index_fp = H;
        }
        IntRegIndex vm_fp = (IntRegIndex) (uint8_t) (vmh << 4 | vm_bf);

        switch (opcode) {
          case 0x0:
            if (!u || (size == 0x0 || size == 0x3))
                return new Unknown64(machInst);
            else
                return decodeNeonUThreeImmHAndWReg<MlaElemDX, MlaElemQX>(
                    q, size, machInst, vd, vn, vm, index);
          case 0x1:
            if (!u && size >= 2 && sz_q != 0x2 && sz_L != 0x3)
                return decodeNeonUThreeImmFpReg<FmlaElemDX, FmlaElemQX>(
                    q, sz, machInst, vd, vn, vm_fp, index_fp);
            else
                return new Unknown64(machInst);
          case 0x2:
            if (size == 0x0 || size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeImmHAndWReg<UmlalElemX, UmlalElem2X>(
                    q, size, machInst, vd, vn, vm, index);
            else
                return decodeNeonSThreeImmHAndWReg<SmlalElemX, SmlalElem2X>(
                    q, size, machInst, vd, vn, vm, index);
          case 0x3:
            if (u || (size == 0x0 || size == 0x3))
                return new Unknown64(machInst);
            else
                return decodeNeonSThreeImmHAndWReg<SqdmlalElemX,
                                                   SqdmlalElem2X>(
                    q, size, machInst, vd, vn, vm, index);
          case 0x4:
            if (u && !(size == 0x0 || size == 0x3))
                return decodeNeonUThreeImmHAndWReg<MlsElemDX, MlsElemQX>(
                    q, size, machInst, vd, vn, vm, index);
            else
                return new Unknown64(machInst);
          case 0x5:
            if (!u && size >= 0x2 && sz_L != 0x3 && sz_q != 0x2)
                return decodeNeonUThreeImmFpReg<FmlsElemDX, FmlsElemQX>(
                    q, sz, machInst, vd, vn, vm_fp, index_fp);
            else
                return new Unknown64(machInst);
          case 0x6:
            if (size == 0x0 || size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeImmHAndWReg<UmlslElemX, UmlslElem2X>(
                    q, size, machInst, vd, vn, vm, index);
            else
                return decodeNeonSThreeImmHAndWReg<SmlslElemX, SmlslElem2X>(
                    q, size, machInst, vd, vn, vm, index);
          case 0x7:
            if (u || (size == 0x0 || size == 0x3))
                return new Unknown64(machInst);
            else
                return decodeNeonSThreeImmHAndWReg<SqdmlslElemX,
                                                   SqdmlslElem2X>(
                    q, size, machInst, vd, vn, vm, index);
          case 0x8:
            if (u || (size == 0x0 || size == 0x3))
                return new Unknown64(machInst);
            else
                return decodeNeonUThreeImmHAndWReg<MulElemDX, MulElemQX>(
                    q, size, machInst, vd, vn, vm, index);
          case 0x9:
            if (size >= 2 && sz_q != 0x2 && sz_L != 0x3) {
                if (u)
                    return decodeNeonUThreeImmFpReg<FmulxElemDX, FmulxElemQX>(
                        q, sz, machInst, vd, vn, vm_fp, index_fp);
                else
                    return decodeNeonUThreeImmFpReg<FmulElemDX, FmulElemQX>(
                        q, sz, machInst, vd, vn, vm_fp, index_fp);
            } else {
                return new Unknown64(machInst);
            }
          case 0xa:
            if (size == 0x0 || size == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeImmHAndWReg<UmullElemX, UmullElem2X>(
                    q, size, machInst, vd, vn, vm, index);
            else
                return decodeNeonSThreeImmHAndWReg<SmullElemX, SmullElem2X>(
                    q, size, machInst, vd, vn, vm, index);
          case 0xb:
            if (u || (size == 0x0 || size == 0x3))
                return new Unknown64(machInst);
            else
                return decodeNeonSThreeImmHAndWReg<SqdmullElemX, SqdmullElem2X>(
                    q, size, machInst, vd, vn, vm, index);
          case 0xc:
            if (u || (size == 0x0 || size == 0x3))
                return new Unknown64(machInst);
            else
                return decodeNeonSThreeImmHAndWReg<SqdmulhElemDX, SqdmulhElemQX>(
                    q, size, machInst, vd, vn, vm, index);
          case 0xd:
            if (u || (size == 0x0 || size == 0x3))
                return new Unknown64(machInst);
            else
                return decodeNeonSThreeImmHAndWReg<SqrdmulhElemDX, SqrdmulhElemQX>(
                    q, size, machInst, vd, vn, vm, index);
          default:
            return new Unknown64(machInst);
        }
    }

    StaticInstPtr
    decodeNeonModImm(ExtMachInst machInst)
    {
        uint8_t q = bits(machInst, 30);
        uint8_t op = bits(machInst, 29);
        uint8_t abcdefgh = (bits(machInst, 18, 16) << 5) |
                           bits(machInst, 9, 5);
        uint8_t cmode = bits(machInst, 15, 12);
        uint8_t o2 = bits(machInst, 11);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);

        if (o2 == 0x1 || (op == 0x1 && cmode == 0xf && !q))
            return new Unknown64(machInst);

        bool immValid = true;
        const uint64_t bigImm = simd_modified_imm(op, cmode, abcdefgh,
                                                  immValid,
                                                  true /* isAarch64 */);
        if (!immValid) {
            return new Unknown(machInst);
        }

        if (op) {
            if (bits(cmode, 3) == 0) {
                if (bits(cmode, 0) == 0) {
                    if (q)
                        return new MvniQX<uint64_t>(machInst, vd, bigImm);
                    else
                        return new MvniDX<uint64_t>(machInst, vd, bigImm);
                } else {
                    if (q)
                        return new BicImmQX<uint64_t>(machInst, vd, bigImm);
                    else
                        return new BicImmDX<uint64_t>(machInst, vd, bigImm);
                }
            } else {
                if (bits(cmode, 2) == 1) {
                    switch (bits(cmode, 1, 0)) {
                      case 0:
                      case 1:
                        if (q)
                            return new MvniQX<uint64_t>(machInst, vd, bigImm);
                        else
                            return new MvniDX<uint64_t>(machInst, vd, bigImm);
                      case 2:
                        if (q)
                            return new MoviQX<uint64_t>(machInst, vd, bigImm);
                        else
                            return new MoviDX<uint64_t>(machInst, vd, bigImm);
                      case 3:
                        if (q)
                            return new FmovQX<uint64_t>(machInst, vd, bigImm);
                        else
                            return new MoviDX<uint64_t>(machInst, vd, bigImm);
                    }
                } else {
                    if (bits(cmode, 0) == 0) {
                        if (q)
                            return new MvniQX<uint64_t>(machInst, vd, bigImm);
                        else
                            return new MvniDX<uint64_t>(machInst, vd, bigImm);
                    } else {
                        if (q)
                            return new BicImmQX<uint64_t>(machInst, vd,
                                                          bigImm);
                        else
                            return new BicImmDX<uint64_t>(machInst, vd,
                                                          bigImm);
                    }
                }
            }
        } else {
            if (bits(cmode, 3) == 0) {
                if (bits(cmode, 0) == 0) {
                    if (q)
                        return new MoviQX<uint64_t>(machInst, vd, bigImm);
                    else
                        return new MoviDX<uint64_t>(machInst, vd, bigImm);
                } else {
                    if (q)
                        return new OrrImmQX<uint64_t>(machInst, vd, bigImm);
                    else
                        return new OrrImmDX<uint64_t>(machInst, vd, bigImm);
                }
            } else {
                if (bits(cmode, 2) == 1) {
                    if (bits(cmode, 1, 0) == 0x3) {
                        if (q)
                            return new FmovQX<uint32_t>(machInst, vd, bigImm);
                        else
                            return new FmovDX<uint32_t>(machInst, vd, bigImm);
                    } else {
                        if (q)
                            return new MoviQX<uint64_t>(machInst, vd, bigImm);
                        else
                            return new MoviDX<uint64_t>(machInst, vd, bigImm);
                    }
                } else {
                    if (bits(cmode, 0) == 0) {
                        if (q)
                            return new MoviQX<uint64_t>(machInst, vd, bigImm);
                        else
                            return new MoviDX<uint64_t>(machInst, vd, bigImm);
                    } else {
                        if (q)
                            return new OrrImmQX<uint64_t>(machInst, vd,
                                                          bigImm);
                        else
                            return new OrrImmDX<uint64_t>(machInst, vd, bigImm);
                    }
                }
            }
        }
        return new Unknown(machInst);
    }

    StaticInstPtr
    decodeNeonShiftByImm(ExtMachInst machInst)
    {
        uint8_t q = bits(machInst, 30);
        uint8_t u = bits(machInst, 29);
        uint8_t immh = bits(machInst, 22, 19);
        uint8_t immb = bits(machInst, 18, 16);
        uint8_t opcode = bits(machInst, 15, 11);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);

        uint8_t immh3 = bits(machInst, 22);
        uint8_t immh3_q = (immh3 << 1) | q;
        uint8_t op_u = (bits(machInst, 12) << 1) | u;
        uint8_t size = findMsbSet(immh);
        int shiftAmt = 0;

        switch (opcode) {
          case 0x00:
            if (immh3_q == 0x2)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return decodeNeonUTwoShiftXReg<UshrDX, UshrQX>(
                    q, size, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonSTwoShiftXReg<SshrDX, SshrQX>(
                    q, size, machInst, vd, vn, shiftAmt);
          case 0x02:
            if (immh3_q == 0x2)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return decodeNeonUTwoShiftXReg<UsraDX, UsraQX>(
                    q, size, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonSTwoShiftXReg<SsraDX, SsraQX>(
                    q, size, machInst, vd, vn, shiftAmt);
          case 0x04:
            if (immh3_q == 0x2)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return decodeNeonUTwoShiftXReg<UrshrDX, UrshrQX>(
                    q, size, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonSTwoShiftXReg<SrshrDX, SrshrQX>(
                    q, size, machInst, vd, vn, shiftAmt);
          case 0x06:
            if (immh3_q == 0x2)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return decodeNeonUTwoShiftXReg<UrsraDX, UrsraQX>(
                    q, size, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonSTwoShiftXReg<SrsraDX, SrsraQX>(
                    q, size, machInst, vd, vn, shiftAmt);
          case 0x08:
            if (u && !(immh3_q == 0x2)) {
                shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
                return decodeNeonUTwoShiftXReg<SriDX, SriQX>(
                    q, size, machInst, vd, vn, shiftAmt);
            } else {
                return new Unknown64(machInst);
            }
          case 0x0a:
            if (immh3_q == 0x2)
                return new Unknown64(machInst);
            shiftAmt = ((immh << 3) | immb) - (8 << size);
            if (u)
                return decodeNeonUTwoShiftXReg<SliDX, SliQX>(
                    q, size, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonUTwoShiftXReg<ShlDX, ShlQX>(
                    q, size, machInst, vd, vn, shiftAmt);
          case 0x0c:
            if (u && !(immh3_q == 0x2 || op_u == 0x0)) {
                shiftAmt = ((immh << 3) | immb) - (8 << size);
                return decodeNeonSTwoShiftXReg<SqshluDX, SqshluQX>(
                    q, size, machInst, vd, vn, shiftAmt);
            } else {
                return new Unknown64(machInst);
            }
          case 0x0e:
            if (immh3_q == 0x2 || op_u == 0x0)
                return new Unknown64(machInst);
            shiftAmt = ((immh << 3) | immb) - (8 << size);
            if (u)
                return decodeNeonUTwoShiftXReg<UqshlImmDX, UqshlImmQX>(
                    q, size, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonSTwoShiftXReg<SqshlImmDX, SqshlImmQX>(
                    q, size, machInst, vd, vn, shiftAmt);
          case 0x10:
            if (immh3)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return decodeNeonSTwoShiftSReg<SqshrunX, Sqshrun2X>(
                    q, size, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonUTwoShiftSReg<ShrnX, Shrn2X>(
                    q, size, machInst, vd, vn, shiftAmt);
          case 0x11:
            if (immh3)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return decodeNeonSTwoShiftSReg<SqrshrunX, Sqrshrun2X>(
                    q, size, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonUTwoShiftSReg<RshrnX, Rshrn2X>(
                    q, size, machInst, vd, vn, shiftAmt);
          case 0x12:
            if (immh3)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return decodeNeonUTwoShiftSReg<UqshrnX, Uqshrn2X>(
                    q, size, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonSTwoShiftSReg<SqshrnX, Sqshrn2X>(
                    q, size, machInst, vd, vn, shiftAmt);
          case 0x13:
            if (immh3)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return decodeNeonUTwoShiftSReg<UqrshrnX, Uqrshrn2X>(
                    q, size, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonSTwoShiftSReg<SqrshrnX, Sqrshrn2X>(
                    q, size, machInst, vd, vn, shiftAmt);
          case 0x14:
            if (immh3)
                return new Unknown64(machInst);
            shiftAmt = ((immh << 3) | immb) - (8 << size);
            if (u)
                return decodeNeonUTwoShiftSReg<UshllX, Ushll2X>(
                    q, size, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonSTwoShiftSReg<SshllX, Sshll2X>(
                    q, size, machInst, vd, vn, shiftAmt);
          case 0x1c:
            if (immh < 0x4 || immh3_q == 0x2)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u) {
                return decodeNeonUTwoShiftFpReg<UcvtfFixedDX, UcvtfFixedQX>(
                    q, size & 0x1, machInst, vd, vn, shiftAmt);
            } else {
                if (q) {
                    if (size & 0x1)
                        return new ScvtfFixedDQX<uint64_t>(machInst, vd, vn,
                                                           shiftAmt);
                    else
                        return new ScvtfFixedSQX<uint32_t>(machInst, vd, vn,
                                                           shiftAmt);
                } else {
                    if (size & 0x1)
                        return new Unknown(machInst);
                    else
                        return new ScvtfFixedDX<uint32_t>(machInst, vd, vn,
                                                          shiftAmt);
                }
            }
          case 0x1f:
            if (immh < 0x4 || immh3_q == 0x2)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return decodeNeonUTwoShiftFpReg<FcvtzuFixedDX, FcvtzuFixedQX>(
                    q, size & 0x1, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonUTwoShiftFpReg<FcvtzsFixedDX, FcvtzsFixedQX>(
                    q, size & 0x1, machInst, vd, vn, shiftAmt);
          default:
            return new Unknown64(machInst);
        }
    }

    StaticInstPtr
    decodeNeonTblTbx(ExtMachInst machInst)
    {
        uint8_t q = bits(machInst, 30);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);

        uint8_t switchVal = bits(machInst, 14, 12);

        switch (switchVal) {
          case 0x0:
            if (q)
                return new Tbl1QX<uint8_t>(machInst, vd, vn, vm);
            else
                return new Tbl1DX<uint8_t>(machInst, vd, vn, vm);
          case 0x1:
            if (q)
                return new Tbx1QX<uint8_t>(machInst, vd, vn, vm);
            else
                return new Tbx1DX<uint8_t>(machInst, vd, vn, vm);
          case 0x2:
            if (q)
                return new Tbl2QX<uint8_t>(machInst, vd, vn, vm);
            else
                return new Tbl2DX<uint8_t>(machInst, vd, vn, vm);
          case 0x3:
            if (q)
                return new Tbx2QX<uint8_t>(machInst, vd, vn, vm);
            else
                return new Tbx2DX<uint8_t>(machInst, vd, vn, vm);
          case 0x4:
            if (q)
                return new Tbl3QX<uint8_t>(machInst, vd, vn, vm);
            else
                return new Tbl3DX<uint8_t>(machInst, vd, vn, vm);
          case 0x5:
            if (q)
                return new Tbx3QX<uint8_t>(machInst, vd, vn, vm);
            else
                return new Tbx3DX<uint8_t>(machInst, vd, vn, vm);
          case 0x6:
            if (q)
                return new Tbl4QX<uint8_t>(machInst, vd, vn, vm);
            else
                return new Tbl4DX<uint8_t>(machInst, vd, vn, vm);
          case 0x7:
            if (q)
                return new Tbx4QX<uint8_t>(machInst, vd, vn, vm);
            else
                return new Tbx4DX<uint8_t>(machInst, vd, vn, vm);
          default:
            return new Unknown64(machInst);
        }

        return new Unknown64(machInst);
    }

    StaticInstPtr
    decodeNeonZipUzpTrn(ExtMachInst machInst)
    {
        uint8_t q = bits(machInst, 30);
        uint8_t size = bits(machInst, 23, 22);
        uint8_t opcode = bits(machInst, 14, 12);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);

        switch (opcode) {
          case 0x1:
            return decodeNeonUThreeXReg<Uzp1DX, Uzp1QX>(
                q, size, machInst, vd, vn, vm);
          case 0x2:
            return decodeNeonUThreeXReg<Trn1DX, Trn1QX>(
                q, size, machInst, vd, vn, vm);
          case 0x3:
            return decodeNeonUThreeXReg<Zip1DX, Zip1QX>(
                q, size, machInst, vd, vn, vm);
          case 0x5:
            return decodeNeonUThreeXReg<Uzp2DX, Uzp2QX>(
                q, size, machInst, vd, vn, vm);
          case 0x6:
            return decodeNeonUThreeXReg<Trn2DX, Trn2QX>(
                q, size, machInst, vd, vn, vm);
          case 0x7:
            return decodeNeonUThreeXReg<Zip2DX, Zip2QX>(
                q, size, machInst, vd, vn, vm);
          default:
            return new Unknown64(machInst);
        }
        return new Unknown64(machInst);
    }

    StaticInstPtr
    decodeNeonExt(ExtMachInst machInst)
    {
        uint8_t q = bits(machInst, 30);
        uint8_t op2 = bits(machInst, 23, 22);
        uint8_t imm4 = bits(machInst, 14, 11);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);

        if (op2 != 0 || (q == 0x0 && bits(imm4, 3) == 0x1))
            return new Unknown64(machInst);

        uint8_t index = q ? imm4 : imm4 & 0x7;

        if (q) {
            return new ExtQX<uint8_t>(machInst, vd, vn, vm, index);
        } else {
            return new ExtDX<uint8_t>(machInst, vd, vn, vm, index);
        }
    }

    StaticInstPtr
    decodeNeonSc3Same(ExtMachInst machInst)
    {
        uint8_t u = bits(machInst, 29);
        uint8_t size = bits(machInst, 23, 22);
        uint8_t opcode = bits(machInst, 15, 11);
        uint8_t s = bits(machInst, 11);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);

        switch (opcode) {
          case 0x01:
            if (u)
                return decodeNeonUThreeUReg<UqaddScX>(
                    size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeUReg<SqaddScX>(
                    size, machInst, vd, vn, vm);
          case 0x05:
            if (u)
                return decodeNeonUThreeUReg<UqsubScX>(
                    size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeUReg<SqsubScX>(
                    size, machInst, vd, vn, vm);
          case 0x06:
            if (size != 0x3)
                return new Unknown64(machInst);
            if (u)
                return new CmhiDX<uint64_t>(machInst, vd, vn, vm);
            else
                return new CmgtDX<int64_t>(machInst, vd, vn, vm);
          case 0x07:
            if (size != 0x3)
                return new Unknown64(machInst);
            if (u)
                return new CmhsDX<uint64_t>(machInst, vd, vn, vm);
            else
                return new CmgeDX<int64_t>(machInst, vd, vn, vm);
          case 0x08:
            if (!s && size != 0x3)
                return new Unknown64(machInst);
            if (u)
                return new UshlDX<uint64_t>(machInst, vd, vn, vm);
            else
                return new SshlDX<int64_t>(machInst, vd, vn, vm);
          case 0x09:
            if (!s && size != 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeUReg<UqshlScX>(
                    size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeUReg<SqshlScX>(
                    size, machInst, vd, vn, vm);
          case 0x0a:
            if (!s && size != 0x3)
                return new Unknown64(machInst);
            if (u)
                return new UrshlDX<uint64_t>(machInst, vd, vn, vm);
            else
                return new SrshlDX<int64_t>(machInst, vd, vn, vm);
          case 0x0b:
            if (!s && size != 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeUReg<UqrshlScX>(
                    size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeUReg<SqrshlScX>(
                    size, machInst, vd, vn, vm);
          case 0x10:
            if (size != 0x3)
                return new Unknown64(machInst);
            if (u)
                return new SubDX<uint64_t>(machInst, vd, vn, vm);
            else
                return new AddDX<uint64_t>(machInst, vd, vn, vm);
          case 0x11:
            if (size != 0x3)
                return new Unknown64(machInst);
            if (u)
                return new CmeqDX<uint64_t>(machInst, vd, vn, vm);
            else
                return new CmtstDX<uint64_t>(machInst, vd, vn, vm);
          case 0x16:
            if (size == 0x3 || size == 0x0)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonSThreeHAndWReg<SqrdmulhScX>(
                    size, machInst, vd, vn, vm);
            else
                return decodeNeonSThreeHAndWReg<SqdmulhScX>(
                    size, machInst, vd, vn, vm);
          case 0x1a:
            if (!u || size < 0x2)
                return new Unknown64(machInst);
            else
                return decodeNeonUThreeScFpReg<FabdScX>(
                    size & 0x1, machInst, vd, vn, vm);
          case 0x1b:
            if (u || size > 0x1)
                return new Unknown64(machInst);
            else
                return decodeNeonUThreeScFpReg<FmulxScX>(
                    size & 0x1, machInst, vd, vn, vm);
          case 0x1c:
            if (size < 0x2) {
                if (u)
                    return decodeNeonUThreeScFpReg<FcmgeScX>(
                        size & 0x1, machInst, vd, vn, vm);
                else
                    return decodeNeonUThreeScFpReg<FcmeqScX>(
                        size & 0x1, machInst, vd, vn, vm);
            } else {
                if (u)
                    return decodeNeonUThreeScFpReg<FcmgtScX>(
                        size & 0x1, machInst, vd, vn, vm);
                else
                    return new Unknown64(machInst);
            }
          case 0x1d:
            if (!u)
                return new Unknown64(machInst);
            if (size < 0x2)
                return decodeNeonUThreeScFpReg<FacgeScX>(
                    size & 0x1, machInst, vd, vn, vm);
            else
                return decodeNeonUThreeScFpReg<FacgtScX>(
                    size & 0x1, machInst, vd, vn, vm);
          case 0x1f:
            if (u)
                return new Unknown64(machInst);
            if (size < 0x2)
                return decodeNeonUThreeScFpReg<FrecpsScX>(
                    size & 0x1, machInst, vd, vn, vm);
            else
                return decodeNeonUThreeScFpReg<FrsqrtsScX>(
                    size & 0x1, machInst, vd, vn, vm);
          default:
            return new Unknown64(machInst);
        }
    }

    StaticInstPtr
    decodeNeonSc3Diff(ExtMachInst machInst)
    {
        if (bits(machInst, 29))
            return new Unknown64(machInst);

        uint8_t size = bits(machInst, 23, 22);
        if (size == 0x0 || size == 0x3)
            return new Unknown64(machInst);

        uint8_t opcode = bits(machInst, 15, 12);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);

        switch (opcode) {
          case 0x9:
            return decodeNeonSThreeHAndWReg<SqdmlalScX>(size, machInst, vd, vn, vm);
          case 0xb:
            return decodeNeonSThreeHAndWReg<SqdmlslScX>(size, machInst, vd, vn, vm);
          case 0xd:
            return decodeNeonSThreeHAndWReg<SqdmullScX>(size, machInst, vd, vn, vm);
          default:
            return new Unknown64(machInst);
        }
    }

    StaticInstPtr
    decodeNeonSc2RegMisc(ExtMachInst machInst)
    {
        uint8_t u = bits(machInst, 29);
        uint8_t size = bits(machInst, 23, 22);
        uint8_t opcode = bits(machInst, 16, 12);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);

        uint8_t switchVal = opcode | ((u ? 1 : 0) << 5);
        switch (switchVal) {
          case 0x03:
            return decodeNeonUTwoMiscUReg<SuqaddScX>(size, machInst, vd, vn);
          case 0x07:
            return decodeNeonSTwoMiscUReg<SqabsScX>(size, machInst, vd, vn);
          case 0x08:
            if (size != 0x3)
                return new Unknown64(machInst);
            else
                return new CmgtZeroDX<int64_t>(machInst, vd, vn);
          case 0x09:
            if (size != 0x3)
                return new Unknown64(machInst);
            else
                return new CmeqZeroDX<int64_t>(machInst, vd, vn);
          case 0x0a:
            if (size != 0x3)
                return new Unknown64(machInst);
            else
                return new CmltZeroDX<int64_t>(machInst, vd, vn);
          case 0x0b:
            if (size != 0x3)
                return new Unknown64(machInst);
            else
                return new AbsDX<int64_t>(machInst, vd, vn);
          case 0x0c:
            if (size < 0x2)
                return new Unknown64(machInst);
            else
                return decodeNeonUTwoMiscScFpReg<FcmgtZeroScX>(
                    size & 0x1, machInst, vd, vn);
          case 0x0d:
            if (size < 0x2)
                return new Unknown64(machInst);
            else
                return decodeNeonUTwoMiscScFpReg<FcmeqZeroScX>(
                    size & 0x1, machInst, vd, vn);
          case 0x0e:
            if (size < 0x2)
                return new Unknown64(machInst);
            else
                return decodeNeonUTwoMiscScFpReg<FcmltZeroScX>(
                    size & 0x1, machInst, vd, vn);
          case 0x14:
            if (size == 0x3) {
                return new Unknown64(machInst);
            } else {
                switch (size) {
                  case 0x0:
                    return new SqxtnScX<int8_t>(machInst, vd, vn);
                  case 0x1:
                    return new SqxtnScX<int16_t>(machInst, vd, vn);
                  case 0x2:
                    return new SqxtnScX<int32_t>(machInst, vd, vn);
                }
            }
          case 0x1a:
            if (size < 0x2)
                return decodeNeonUTwoMiscScFpReg<FcvtnsScX>(
                    size & 0x1, machInst, vd, vn);
            else
                return decodeNeonUTwoMiscScFpReg<FcvtpsScX>(
                    size & 0x1, machInst, vd, vn);
          case 0x1b:
            if (size < 0x2)
                return decodeNeonUTwoMiscScFpReg<FcvtmsScX>(
                    size & 0x1, machInst, vd, vn);
            else
                return decodeNeonUTwoMiscScFpReg<FcvtzsIntScX>(
                    size & 0x1, machInst, vd, vn);
          case 0x1c:
            if (size < 0x2)
                return decodeNeonUTwoMiscScFpReg<FcvtasScX>(
                    size & 0x1, machInst, vd, vn);
            else
                return new Unknown64(machInst);
          case 0x1d:
            if (size < 0x2) {
                if (size & 0x1)
                    return new ScvtfIntScDX<uint64_t>(machInst, vd, vn);
                else
                    return new ScvtfIntScSX<uint32_t>(machInst, vd, vn);
            } else {
                return decodeNeonUTwoMiscScFpReg<FrecpeScX>(
                    size & 0x1, machInst, vd, vn);
            }
          case 0x1f:
            if (size < 0x2)
                return new Unknown64(machInst);
            else
                return decodeNeonUTwoMiscScFpReg<FrecpxX>(
                    size & 0x1, machInst, vd, vn);
          case 0x23:
            return decodeNeonUTwoMiscUReg<UsqaddScX>(size, machInst, vd, vn);
          case 0x27:
            return decodeNeonSTwoMiscUReg<SqnegScX>(size, machInst, vd, vn);
          case 0x28:
            if (size != 0x3)
                return new Unknown64(machInst);
            else
                return new CmgeZeroDX<int64_t>(machInst, vd, vn);
          case 0x29:
            if (size != 0x3)
                return new Unknown64(machInst);
            else
                return new CmleZeroDX<int64_t>(machInst, vd, vn);
          case 0x2b:
            if (size != 0x3)
                return new Unknown64(machInst);
            else
                return new NegDX<int64_t>(machInst, vd, vn);
          case 0x2c:
            if (size < 0x2)
                return new Unknown64(machInst);
            else
                return decodeNeonUTwoMiscScFpReg<FcmgeZeroScX>(
                    size & 0x1, machInst, vd, vn);
          case 0x2d:
            if (size < 0x2)
                return new Unknown64(machInst);
            else
                return decodeNeonUTwoMiscScFpReg<FcmleZeroScX>(
                    size & 0x1, machInst, vd, vn);
          case 0x32:
            if (size == 0x3) {
                return new Unknown64(machInst);
            } else {
                switch (size) {
                  case 0x0:
                    return new SqxtunScX<int8_t>(machInst, vd, vn);
                  case 0x1:
                    return new SqxtunScX<int16_t>(machInst, vd, vn);
                  case 0x2:
                    return new SqxtunScX<int32_t>(machInst, vd, vn);
                }
            }
          case 0x34:
            if (size == 0x3) {
                return new Unknown64(machInst);
            } else {
                switch (size) {
                  case 0x0:
                    return new UqxtnScX<uint8_t>(machInst, vd, vn);
                  case 0x1:
                    return new UqxtnScX<uint16_t>(machInst, vd, vn);
                  case 0x2:
                    return new UqxtnScX<uint32_t>(machInst, vd, vn);
                }
            }
          case 0x36:
            if (size != 0x1) {
                return new Unknown64(machInst);
            } else {
                return new FcvtxnScX<uint32_t>(machInst, vd, vn);
            }
          case 0x3a:
            if (size < 0x2)
                return decodeNeonUTwoMiscScFpReg<FcvtnuScX>(
                    size & 0x1, machInst, vd, vn);
            else
                return decodeNeonUTwoMiscScFpReg<FcvtpuScX>(
                    size & 0x1, machInst, vd, vn);
          case 0x3b:
            if (size < 0x2)
                return decodeNeonUTwoMiscScFpReg<FcvtmuScX>(
                    size & 0x1, machInst, vd, vn);
            else
                return decodeNeonUTwoMiscScFpReg<FcvtzuIntScX>(
                    size & 0x1, machInst, vd, vn);
          case 0x3c:
            if (size < 0x2)
                return decodeNeonUTwoMiscScFpReg<FcvtauScX>(
                    size & 0x1, machInst, vd, vn);
            else
                return new Unknown64(machInst);
          case 0x3d:
            if (size < 0x2)
                return decodeNeonUTwoMiscScFpReg<UcvtfIntScX>(
                    size & 0x1, machInst, vd, vn);
            else
                return decodeNeonUTwoMiscScFpReg<FrsqrteScX>(
                    size & 0x1, machInst, vd, vn);
          default:
            return new Unknown64(machInst);
        }
    }

    StaticInstPtr
    decodeNeonScPwise(ExtMachInst machInst)
    {
        uint8_t u = bits(machInst, 29);
        uint8_t size = bits(machInst, 23, 22);
        uint8_t opcode = bits(machInst, 16, 12);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);

        if (!u) {
            if (opcode == 0x1b && size == 0x3)
                return new AddpScQX<uint64_t>(machInst, vd, vn);
            else
                return new Unknown64(machInst);
        }

        uint8_t switchVal = (opcode << 0) | (size << 5);
        switch (switchVal) {
          case 0x0c:
          case 0x2c:
            return decodeNeonUTwoMiscPwiseScFpReg<FmaxnmpScDX, FmaxnmpScQX>(
                    size & 0x1, machInst, vd, vn);
          case 0x0d:
          case 0x2d:
            return decodeNeonUTwoMiscPwiseScFpReg<FaddpScDX, FaddpScQX>(
                    size & 0x1, machInst, vd, vn);
          case 0x0f:
          case 0x2f:
            return decodeNeonUTwoMiscPwiseScFpReg<FmaxpScDX, FmaxpScQX>(
                    size & 0x1, machInst, vd, vn);
          case 0x4c:
          case 0x6c:
            return decodeNeonUTwoMiscPwiseScFpReg<FminnmpScDX, FminnmpScQX>(
                    size & 0x1, machInst, vd, vn);
          case 0x4f:
          case 0x6f:
            return decodeNeonUTwoMiscPwiseScFpReg<FminpScDX, FminpScQX>(
                    size & 0x1, machInst, vd, vn);
          default:
            return new Unknown64(machInst);
        }
    }

    StaticInstPtr
    decodeNeonScCopy(ExtMachInst machInst)
    {
        if (bits(machInst, 14, 11) != 0 || bits(machInst, 29))
            return new Unknown64(machInst);

        uint8_t imm5 = bits(machInst, 20, 16);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);

        uint8_t size = findLsbSet(imm5);
        uint8_t index = bits(imm5, 4, size + 1);

        return decodeNeonUTwoShiftUReg<DupElemScX>(
            size, machInst, vd, vn, index);
    }

    StaticInstPtr
    decodeNeonScIndexedElem(ExtMachInst machInst)
    {
        uint8_t u = bits(machInst, 29);
        uint8_t size = bits(machInst, 23, 22);
        uint8_t L = bits(machInst, 21);
        uint8_t M = bits(machInst, 20);
        uint8_t opcode = bits(machInst, 15, 12);
        uint8_t H = bits(machInst, 11);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
        IntRegIndex vm_bf = (IntRegIndex) (uint8_t) bits(machInst, 19, 16);

        uint8_t index = 0;
        uint8_t index_fp = 0;
        uint8_t vmh = 0;
        uint8_t sz_L = bits(machInst, 22, 21);

        // Index and 2nd register operand for integer instructions
        if (size == 0x1) {
            index = (H << 2) | (L << 1) | M;
            // vmh = 0;
        } else if (size == 0x2) {
            index = (H << 1) | L;
            vmh = M;
        } else if (size == 0x3) {
            index = H;
            vmh = M;
        }
        IntRegIndex vm = (IntRegIndex) (uint8_t) (vmh << 4 | vm_bf);

        // Index and 2nd register operand for FP instructions
        vmh = M;
        if ((size & 0x1) == 0) {
            index_fp = (H << 1) | L;
        } else if (L == 0) {
            index_fp = H;
        }
        IntRegIndex vm_fp = (IntRegIndex) (uint8_t) (vmh << 4 | vm_bf);

        if (u && opcode != 9)
            return new Unknown64(machInst);

        switch (opcode) {
          case 0x1:
            if (size < 2 || sz_L == 0x3)
                return new Unknown64(machInst);
            else
                return decodeNeonUThreeImmScFpReg<FmlaElemScX>(
                    size & 0x1, machInst, vd, vn, vm_fp, index_fp);
          case 0x3:
            if (size == 0x0 || size == 0x3)
                return new Unknown64(machInst);
            else
                return decodeNeonSThreeImmHAndWReg<SqdmlalElemScX>(
                    size, machInst, vd, vn, vm, index);
          case 0x5:
            if (size < 2 || sz_L == 0x3)
                return new Unknown64(machInst);
            else
                return decodeNeonUThreeImmScFpReg<FmlsElemScX>(
                    size & 0x1, machInst, vd, vn, vm_fp, index_fp);
          case 0x7:
            if (size == 0x0 || size == 0x3)
                return new Unknown64(machInst);
            else
                return decodeNeonSThreeImmHAndWReg<SqdmlslElemScX>(
                    size, machInst, vd, vn, vm, index);
          case 0x9:
            if (size < 2 || sz_L == 0x3)
                return new Unknown64(machInst);
            if (u)
                return decodeNeonUThreeImmScFpReg<FmulxElemScX>(
                    size & 0x1, machInst, vd, vn, vm_fp, index_fp);
            else
                return decodeNeonUThreeImmScFpReg<FmulElemScX>(
                    size & 0x1, machInst, vd, vn, vm_fp, index_fp);
          case 0xb:
            if (size == 0x0 || size == 0x3)
                return new Unknown64(machInst);
            else
                return decodeNeonSThreeImmHAndWReg<SqdmullElemScX>(
                    size, machInst, vd, vn, vm, index);
          case 0xc:
            if (size == 0x0 || size == 0x3)
                return new Unknown64(machInst);
            else
                return decodeNeonSThreeImmHAndWReg<SqdmulhElemScX>(
                    size, machInst, vd, vn, vm, index);
          case 0xd:
            if (size == 0x0 || size == 0x3)
                return new Unknown64(machInst);
            else
                return decodeNeonSThreeImmHAndWReg<SqrdmulhElemScX>(
                    size, machInst, vd, vn, vm, index);
          default:
            return new Unknown64(machInst);
        }
    }

    StaticInstPtr
    decodeNeonScShiftByImm(ExtMachInst machInst)
    {
        bool u = bits(machInst, 29);
        uint8_t immh = bits(machInst, 22, 19);
        uint8_t immb = bits(machInst, 18, 16);
        uint8_t opcode = bits(machInst, 15, 11);

        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);

        uint8_t immh3 = bits(machInst, 22);
        uint8_t size = findMsbSet(immh);
        int shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);

        if (immh == 0x0)
            return new Unknown64(machInst);

        switch (opcode) {
          case 0x00:
            if (!immh3)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return new UshrDX<uint64_t>(machInst, vd, vn, shiftAmt);
            else
                return new SshrDX<int64_t>(machInst, vd, vn, shiftAmt);
          case 0x02:
            if (!immh3)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return new UsraDX<uint64_t>(machInst, vd, vn, shiftAmt);
            else
                return new SsraDX<int64_t>(machInst, vd, vn, shiftAmt);
          case 0x04:
            if (!immh3)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return new UrshrDX<uint64_t>(machInst, vd, vn, shiftAmt);
            else
                return new SrshrDX<int64_t>(machInst, vd, vn, shiftAmt);
          case 0x06:
            if (!immh3)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return new UrsraDX<uint64_t>(machInst, vd, vn, shiftAmt);
            else
                return new SrsraDX<int64_t>(machInst, vd, vn, shiftAmt);
          case 0x08:
            if (!immh3)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return new SriDX<uint64_t>(machInst, vd, vn, shiftAmt);
            else
                return new Unknown64(machInst);
          case 0x0a:
            if (!immh3)
                return new Unknown64(machInst);
            shiftAmt = ((immh << 3) | immb) - (8 << size);
            if (u)
                return new SliDX<uint64_t>(machInst, vd, vn, shiftAmt);
            else
                return new ShlDX<uint64_t>(machInst, vd, vn, shiftAmt);
          case 0x0c:
            if (u) {
                shiftAmt = ((immh << 3) | immb) - (8 << size);
                return decodeNeonSTwoShiftUReg<SqshluScX>(
                    size, machInst, vd, vn, shiftAmt);
            } else {
                return new Unknown64(machInst);
            }
          case 0x0e:
            shiftAmt = ((immh << 3) | immb) - (8 << size);
            if (u)
                return decodeNeonUTwoShiftUReg<UqshlImmScX>(
                    size, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonSTwoShiftUReg<SqshlImmScX>(
                    size, machInst, vd, vn, shiftAmt);
          case 0x10:
            if (!u || immh3)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            return decodeNeonSTwoShiftUSReg<SqshrunScX>(
                size, machInst, vd, vn, shiftAmt);
          case 0x11:
            if (!u || immh3)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            return decodeNeonSTwoShiftUSReg<SqrshrunScX>(
                size, machInst, vd, vn, shiftAmt);
          case 0x12:
            if (immh3)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return decodeNeonUTwoShiftUSReg<UqshrnScX>(
                    size, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonSTwoShiftUSReg<SqshrnScX>(
                    size, machInst, vd, vn, shiftAmt);
          case 0x13:
            if (immh3)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return decodeNeonUTwoShiftUSReg<UqrshrnScX>(
                    size, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonSTwoShiftUSReg<SqrshrnScX>(
                    size, machInst, vd, vn, shiftAmt);
          case 0x1c:
            if (immh < 0x4)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u) {
                return decodeNeonUTwoShiftUFpReg<UcvtfFixedScX>(
                    size & 0x1, machInst, vd, vn, shiftAmt);
            } else {
                if (size & 0x1)
                    return new ScvtfFixedScDX<uint64_t>(machInst, vd, vn,
                                                        shiftAmt);
                else
                    return new ScvtfFixedScSX<uint32_t>(machInst, vd, vn,
                                                        shiftAmt);
            }
          case 0x1f:
            if (immh < 0x4)
                return new Unknown64(machInst);
            shiftAmt = (8 << (size + 1)) - ((immh << 3) | immb);
            if (u)
                return decodeNeonUTwoShiftUFpReg<FcvtzuFixedScX>(
                    size & 0x1, machInst, vd, vn, shiftAmt);
            else
                return decodeNeonUTwoShiftUFpReg<FcvtzsFixedScX>(
                    size & 0x1, machInst, vd, vn, shiftAmt);
          default:
            return new Unknown64(machInst);
        }
    }

    StaticInstPtr
    decodeNeonMem(ExtMachInst machInst)
    {
        uint8_t dataSize = bits(machInst, 30) ? 128 : 64;
        bool multiple = bits(machInst, 24, 23) < 0x2;
        bool load = bits(machInst, 22);

        uint8_t numStructElems = 0;
        uint8_t numRegs = 0;

        if (multiple) {  // AdvSIMD load/store multiple structures
            uint8_t opcode = bits(machInst, 15, 12);
            uint8_t eSize = bits(machInst, 11, 10);
            bool wb = !(bits(machInst, 20, 16) == 0x0 && !bits(machInst, 23));

            switch (opcode) {
              case 0x0:  // LD/ST4 (4 regs)
                numStructElems = 4;
                numRegs = 4;
                break;
              case 0x2:  // LD/ST1 (4 regs)
                numStructElems = 1;
                numRegs = 4;
                break;
              case 0x4:  // LD/ST3 (3 regs)
                numStructElems = 3;
                numRegs = 3;
                break;
              case 0x6:  // LD/ST1 (3 regs)
                numStructElems = 1;
                numRegs = 3;
                break;
              case 0x7:  // LD/ST1 (1 reg)
                numStructElems = 1;
                numRegs = 1;
                break;
              case 0x8:  // LD/ST2 (2 regs)
                numStructElems = 2;
                numRegs = 2;
                break;
              case 0xa:  // LD/ST1 (2 regs)
                numStructElems = 1;
                numRegs = 2;
                break;
              default:
                return new Unknown64(machInst);
            }

            IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
            IntRegIndex rn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
            IntRegIndex rm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);

            if (load) {
                return new VldMult64(machInst, rn, vd, rm, eSize, dataSize,
                                     numStructElems, numRegs, wb);
            } else {
                return new VstMult64(machInst, rn, vd, rm, eSize, dataSize,
                                     numStructElems, numRegs, wb);
            }
        } else {  // AdvSIMD load/store single structure
            uint8_t scale = bits(machInst, 15, 14);
            uint8_t numStructElems = (((uint8_t) bits(machInst, 13) << 1) |
                                      (uint8_t) bits(machInst, 21)) + 1;
            uint8_t index = 0;
            bool wb = !(bits(machInst, 20, 16) == 0x0 && !bits(machInst, 23));
            bool replicate = false;

            switch (scale) {
              case 0x0:
                index = ((uint8_t) bits(machInst, 30) << 3) |
                    ((uint8_t) bits(machInst, 12) << 2) |
                    (uint8_t) bits(machInst, 11, 10);
                break;
              case 0x1:
                index = ((uint8_t) bits(machInst, 30) << 2) |
                    ((uint8_t) bits(machInst, 12) << 1) |
                    (uint8_t) bits(machInst, 11);
                break;
              case 0x2:
                if (bits(machInst, 10) == 0x0) {
                    index = ((uint8_t) bits(machInst, 30) << 1) |
                        bits(machInst, 12);
                } else {
                    index = (uint8_t) bits(machInst, 30);
                    scale = 0x3;
                }
                break;
              case 0x3:
                scale = bits(machInst, 11, 10);
                replicate = true;
                break;
              default:
                return new Unknown64(machInst);
            }

            uint8_t eSize = scale;

            IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
            IntRegIndex rn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
            IntRegIndex rm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);

            if (load) {
                return new VldSingle64(machInst, rn, vd, rm, eSize, dataSize,
                                       numStructElems, index, wb, replicate);
            } else {
                return new VstSingle64(machInst, rn, vd, rm, eSize, dataSize,
                                       numStructElems, index, wb, replicate);
            }
        }
    }
}
}};
