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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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) { _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
