[Lldb-commits] [lldb] [lldb/aarch64] Add STR/LDR instructions for FP register to Emulator (PR #168187)

Igor Kudrin via lldb-commits lldb-commits at lists.llvm.org
Mon Nov 17 18:57:35 PST 2025


https://github.com/igorkudrin updated https://github.com/llvm/llvm-project/pull/168187

>From 613fd777a2dd7d6666f0e29dc6815f1d9f69293d Mon Sep 17 00:00:00 2001
From: Igor Kudrin <ikudrin at accesssoftek.com>
Date: Fri, 14 Nov 2025 23:43:38 -0800
Subject: [PATCH 1/3] [lldb/aarch64] Add STR/LDR instructions for FP register
 to Emulator

A function prologue can begin with a pre-index STR instruction for a
floating-point register. To construct an unwind plan from assembly
correctly, the instruction emulator must support such instructions.
---
 .../ARM64/EmulateInstructionARM64.cpp         |  41 +++++--
 .../ARM64/TestArm64InstEmulation.cpp          | 106 ++++++++++++++++++
 2 files changed, 136 insertions(+), 11 deletions(-)

diff --git a/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp b/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
index a8901beda3970..7d3e72ccdc7dc 100644
--- a/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
+++ b/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
@@ -346,6 +346,16 @@ EmulateInstructionARM64::GetOpcodeForInstruction(const uint32_t opcode) {
        &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_OFF>,
        "LDR <Xt>, [<Xn|SP>{, #<pimm>}]"},
 
+      {0x3f200c00, 0x3c000400, No_VFP,
+       &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_POST>,
+       "LDR|STR <Bt|Ht|St|Dt|Qt>, [<Xn|SP>], #<simm>"},
+      {0x3f200c00, 0x3c000c00, No_VFP,
+       &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_PRE>,
+       "LDR|STR <Bt|Ht|St|Dt|Qt>, [<Xn|SP>, #<simm>]!"},
+      {0x3f000000, 0x3d000000, No_VFP,
+       &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_OFF>,
+       "LDR|STR <Bt|Ht|St|Dt|Qt>, [<Xn|SP>{, #<pimm>}]"},
+
       {0xfc000000, 0x14000000, No_VFP, &EmulateInstructionARM64::EmulateB,
        "B <label>"},
       {0xff000010, 0x54000000, No_VFP, &EmulateInstructionARM64::EmulateBcond,
@@ -930,9 +940,27 @@ template <EmulateInstructionARM64::AddrMode a_mode>
 bool EmulateInstructionARM64::EmulateLDRSTRImm(const uint32_t opcode) {
   uint32_t size = Bits32(opcode, 31, 30);
   uint32_t opc = Bits32(opcode, 23, 22);
+  uint32_t vr = Bit32(opcode, 26);
   uint32_t n = Bits32(opcode, 9, 5);
   uint32_t t = Bits32(opcode, 4, 0);
 
+  MemOp memop;
+  if (vr) {
+    if (Bit32(opc, 1) == 1)
+      size += 4;
+    if (size > 4)
+      return false;
+    memop = Bit32(opc, 0) == 1 ? MemOp_LOAD : MemOp_STORE;
+  } else {
+    if (Bit32(opc, 1) == 0) {
+      memop = Bit32(opc, 0) == 1 ? MemOp_LOAD : MemOp_STORE;
+    } else {
+      memop = MemOp_LOAD;
+      if (size == 2 && Bit32(opc, 0) == 1)
+        return false;
+    }
+  }
+
   bool wback;
   bool postindex;
   uint64_t offset;
@@ -955,16 +983,6 @@ bool EmulateInstructionARM64::EmulateLDRSTRImm(const uint32_t opcode) {
     break;
   }
 
-  MemOp memop;
-
-  if (Bit32(opc, 1) == 0) {
-    memop = Bit32(opc, 0) == 1 ? MemOp_LOAD : MemOp_STORE;
-  } else {
-    memop = MemOp_LOAD;
-    if (size == 2 && Bit32(opc, 0) == 1)
-      return false;
-  }
-
   Status error;
   bool success = false;
   uint64_t address;
@@ -989,7 +1007,8 @@ bool EmulateInstructionARM64::EmulateLDRSTRImm(const uint32_t opcode) {
     return false;
 
   std::optional<RegisterInfo> reg_info_Rt =
-      GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + t);
+      vr ? GetRegisterInfo(eRegisterKindLLDB, fpu_d0_arm64 + t)
+         : GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + t);
   if (!reg_info_Rt)
     return false;
 
diff --git a/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp b/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp
index eaf23fd72d6d1..5d15e9621002b 100644
--- a/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp
+++ b/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp
@@ -856,3 +856,109 @@ TEST_F(TestArm64InstEmulation, TestCFAResetToSP) {
   EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
   EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
 }
+
+TEST_F(TestArm64InstEmulation, TestPrologueStartsWithStrD8) {
+  ArchSpec arch("aarch64");
+  std::unique_ptr<UnwindAssemblyInstEmulation> engine(
+      static_cast<UnwindAssemblyInstEmulation *>(
+          UnwindAssemblyInstEmulation::CreateInstance(arch)));
+  ASSERT_NE(nullptr, engine);
+
+  const UnwindPlan::Row *row;
+  AddressRange sample_range;
+  UnwindPlan unwind_plan(eRegisterKindLLDB);
+  UnwindPlan::Row::AbstractRegisterLocation regloc;
+
+  // The sample function is built with 'clang --target aarch64 -O1':
+  //
+  //   int bar(float x);
+  //   int foo(float x) {
+  //     return bar(x) + bar(x);
+  //   }
+  //
+  // The function uses one floating point register and spills it with
+  // 'str d8, [sp, #-0x20]!'.
+
+  uint8_t data[] = {
+      // prologue
+      0xe8, 0x0f, 0x1e, 0xfc, //  0: fc1e0fe8    str  d8, [sp, #-0x20]!
+      0xfd, 0xfb, 0x00, 0xa9, //  4: a900fbfd    stp  x29, x30, [sp, #0x8]
+      0xf3, 0x0f, 0x00, 0xf9, //  8: f9000ff3    str  x19, [sp, #0x18]
+      0xfd, 0x23, 0x00, 0x91, // 12: 910023fd    add  x29, sp, #0x8
+
+      // epilogue
+      0xfd, 0xfb, 0x40, 0xa9, // 16: a940fbfd    ldp  x29, x30, [sp, #0x8]
+      0xf3, 0x0f, 0x40, 0xf9, // 20: f9400ff3    ldr  x19, [sp, #0x18]
+      0xe8, 0x07, 0x42, 0xfc, // 24: fc4207e8    ldr  d8, [sp], #0x20
+      0xc0, 0x03, 0x5f, 0xd6, // 28: d65f03c0    ret
+  };
+
+  // UnwindPlan we expect:
+  //   0: CFA=sp +0 =>
+  //   4: CFA=sp+32 => d8=[CFA-32]
+  //   8: CFA=sp+32 => fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]
+  //  12: CFA=sp+32 => x19=[CFA-8] fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]
+  //  16: CFA=fp+24 => x19=[CFA-8] fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]
+  //  20: CFA=sp+32 => x19=[CFA-8] fp=<same> lr=<same> d8=[CFA-32]
+  //  24: CFA=sp+32 => x19=<same> fp=<same> lr=<same> d8=[CFA-32]
+  //  28: CFA=sp +0 => x19=<same> fp=<same> lr=<same> d8=<same>
+
+  sample_range = AddressRange(0x1000, sizeof(data));
+
+  EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
+      sample_range, data, sizeof(data), unwind_plan));
+
+  //   4: CFA=sp+32 => d8=[CFA-32]
+  row = unwind_plan.GetRowForFunctionOffset(4);
+  EXPECT_EQ(4, row->GetOffset());
+  EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
+  EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
+  EXPECT_EQ(32, row->GetCFAValue().GetOffset());
+
+  EXPECT_TRUE(row->GetRegisterInfo(fpu_d8_arm64, regloc));
+  EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
+  EXPECT_EQ(-32, regloc.GetOffset());
+
+  //  16: CFA=fp+24 => x19=[CFA-8] fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]
+  row = unwind_plan.GetRowForFunctionOffset(16);
+  EXPECT_EQ(16, row->GetOffset());
+  EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
+  EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
+  EXPECT_EQ(24, row->GetCFAValue().GetOffset());
+
+  EXPECT_TRUE(row->GetRegisterInfo(gpr_x19_arm64, regloc));
+  EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
+  EXPECT_EQ(-8, regloc.GetOffset());
+
+  EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));
+  EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
+  EXPECT_EQ(-24, regloc.GetOffset());
+
+  EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));
+  EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
+  EXPECT_EQ(-16, regloc.GetOffset());
+
+  EXPECT_TRUE(row->GetRegisterInfo(fpu_d8_arm64, regloc));
+  EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
+  EXPECT_EQ(-32, regloc.GetOffset());
+
+  //  28: CFA=sp +0 => x19=<same> fp=<same> lr=<same> d8=<same>
+  row = unwind_plan.GetRowForFunctionOffset(28);
+  EXPECT_EQ(28, row->GetOffset());
+  EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
+  EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
+  EXPECT_EQ(0, row->GetCFAValue().GetOffset());
+
+  if (row->GetRegisterInfo(gpr_x19_arm64, regloc)) {
+    EXPECT_TRUE(regloc.IsSame());
+  }
+  if (row->GetRegisterInfo(gpr_fp_arm64, regloc)) {
+    EXPECT_TRUE(regloc.IsSame());
+  }
+  if (row->GetRegisterInfo(gpr_lr_arm64, regloc)) {
+    EXPECT_TRUE(regloc.IsSame());
+  }
+  if (row->GetRegisterInfo(fpu_d8_arm64, regloc)) {
+    EXPECT_TRUE(regloc.IsSame());
+  }
+}

>From 890160e496ebde0c982d3a5bef9c927f2a63d90b Mon Sep 17 00:00:00 2001
From: Igor Kudrin <ikudrin at accesssoftek.com>
Date: Mon, 17 Nov 2025 18:54:13 -0800
Subject: [PATCH 2/3] fixup! disable clang-format for 'data[]'

---
 lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp b/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp
index 5d15e9621002b..033c300ad6926 100644
--- a/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp
+++ b/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp
@@ -879,6 +879,7 @@ TEST_F(TestArm64InstEmulation, TestPrologueStartsWithStrD8) {
   // The function uses one floating point register and spills it with
   // 'str d8, [sp, #-0x20]!'.
 
+  // clang-format off
   uint8_t data[] = {
       // prologue
       0xe8, 0x0f, 0x1e, 0xfc, //  0: fc1e0fe8    str  d8, [sp, #-0x20]!
@@ -892,6 +893,7 @@ TEST_F(TestArm64InstEmulation, TestPrologueStartsWithStrD8) {
       0xe8, 0x07, 0x42, 0xfc, // 24: fc4207e8    ldr  d8, [sp], #0x20
       0xc0, 0x03, 0x5f, 0xd6, // 28: d65f03c0    ret
   };
+  // clang-format on
 
   // UnwindPlan we expect:
   //   0: CFA=sp +0 =>

>From 85e0c00653824de7a7f49d867b47b4a8677c8f22 Mon Sep 17 00:00:00 2001
From: Igor Kudrin <ikudrin at accesssoftek.com>
Date: Mon, 17 Nov 2025 18:57:04 -0800
Subject: [PATCH 3/3] fixup! Better explain cases when opc<1> == 1

---
 .../Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp b/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
index 7d3e72ccdc7dc..f124424a37f58 100644
--- a/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
+++ b/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
@@ -946,10 +946,12 @@ bool EmulateInstructionARM64::EmulateLDRSTRImm(const uint32_t opcode) {
 
   MemOp memop;
   if (vr) {
-    if (Bit32(opc, 1) == 1)
-      size += 4;
-    if (size > 4)
+    // opc<1> == 1 && size != 0 is an undefined encoding.
+    if (Bit32(opc, 1) == 1 && size != 0)
       return false;
+    // opc<1> == 1 && size == 0 encode the 128-bit variant.
+    if (Bit32(opc, 1) == 1)
+      size = 4;
     memop = Bit32(opc, 0) == 1 ? MemOp_LOAD : MemOp_STORE;
   } else {
     if (Bit32(opc, 1) == 0) {



More information about the lldb-commits mailing list