[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