""" Check that when a debug server provides XML that only defines SVE Z registers,
    and does not include Neon V registers, lldb creates sub-registers to represent
    the V registers as the bottom 128 bits of the Z registers.

    qemu-aarch64 is one such debug server.

    This also doubles as a test that lldb has a fallback path for registers of
    unknown type that are > 128 bits, as the SVE registers are here.
"""

from textwrap import dedent
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
from lldbsuite.test.gdbclientutils import *
from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase


class Responder(MockGDBServerResponder):
    def __init__(self):
        super().__init__()
        self.vg = 4
        self.pc = 0xA0A0A0A0A0A0A0A0

    def qXferRead(self, obj, annex, offset, length):
        if annex == "target.xml":
            # Note that QEMU sends the current SVE size in XML and the debugger
            # then reads vg to know the latest size.
            return (
                dedent(
                    """\
              <?xml version="1.0"?>
              <target version="1.0">
                <architecture>aarch64</architecture>
                <feature name="org.gnu.gdb.aarch64.core">
                  <reg name="pc" regnum="0" bitsize="64"/>
                  <reg name="vg" regnum="1" bitsize="64"/>
                  <reg name="z0" regnum="2" bitsize="2048" type="not_a_type"/>
                </feature>
              </target>"""
                ),
                False,
            )

        return (None,)

    def readRegister(self, regnum):
        return "E01"

    def readRegisters(self):
        return "".join(
            [
                # 64 bit PC.
                f"{self.pc:x}",
                # 64 bit vg
                f"0{self.vg}00000000000000",
                # Enough data for 256 and 512 bit SVE.
                "".join([f"{n:02x}" * 4 for n in range(1, 17)]),
            ]
        )

    def cont(self):
        # vg is expedited so that lldb can resize the SVE registers.
        return f"T02thread:1ff0d;threads:1ff0d;thread-pcs:{self.pc};01:0{self.vg}00000000000000;"

    def writeRegisters(self, registers_hex):
        # We get a block of data containing values in regnum order.
        self.vg = int(registers_hex[16:18])
        return "OK"


class TestXMLRegisterFlags(GDBRemoteTestBase):
    def check_regs(self, vg):
        # Each 32 bit chunk repeats n.
        z0_value = " ".join(
            [" ".join([f"0x{n:02x}"] * 4) for n in range(1, (vg * 2) + 1)]
        )

        self.expect(
            "register read vg z0 v0 s0 d0",
            substrs=[
                f"  vg = 0x000000000000000{vg}\n"
                "  z0 = {" + z0_value + "}\n"
                "  v0 = {0x01 0x01 0x01 0x01 0x02 0x02 0x02 0x02 0x03 0x03 0x03 0x03 0x04 0x04 0x04 0x04}\n"
                "  s0 = 2.36942783E-38\n"
                "  d0 = 5.3779407333977203E-299\n"
            ],
        )

        self.expect("register read s0 --format uint32", substrs=["s0 = {0x01010101}"])
        self.expect(
            "register read d0 --format uint64",
            substrs=["d0 = {0x0202020201010101}"],
        )

    @skipIfXmlSupportMissing
    @skipIfRemote
    @skipIfLLVMTargetMissing("AArch64")
    def test_v_sub_registers(self):
        self.server.responder = Responder()
        target = self.dbg.CreateTarget("")

        if self.TraceOn():
            self.runCmd("log enable gdb-remote packets")
            self.addTearDownHook(lambda: self.runCmd("log disable gdb-remote packets"))

        process = self.connect(target)
        lldbutil.expect_state_changes(
            self, self.dbg.GetListener(), process, [lldb.eStateStopped]
        )

        self.check_regs(4)

        # Now increase the SVE length and continue. The mock will respond with a new
        # vg and lldb will reconfigure the register defs. This should not break the
        # sub-registers.

        self.runCmd("register write vg 8")
        self.expect("continue", substrs=["stop reason = signal SIGINT"])

        self.check_regs(8)
