Skip to content

Commit d3c2973

Browse files
authored
[lldb/aarch64] Add STR/LDR instructions for FP registers to Emulator (#168187)
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.
1 parent 3e8dc4d commit d3c2973

File tree

2 files changed

+140
-11
lines changed

2 files changed

+140
-11
lines changed

lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,16 @@ EmulateInstructionARM64::GetOpcodeForInstruction(const uint32_t opcode) {
346346
&EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_OFF>,
347347
"LDR <Xt>, [<Xn|SP>{, #<pimm>}]"},
348348

349+
{0x3f200c00, 0x3c000400, No_VFP,
350+
&EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_POST>,
351+
"LDR|STR <Bt|Ht|St|Dt|Qt>, [<Xn|SP>], #<simm>"},
352+
{0x3f200c00, 0x3c000c00, No_VFP,
353+
&EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_PRE>,
354+
"LDR|STR <Bt|Ht|St|Dt|Qt>, [<Xn|SP>, #<simm>]!"},
355+
{0x3f000000, 0x3d000000, No_VFP,
356+
&EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_OFF>,
357+
"LDR|STR <Bt|Ht|St|Dt|Qt>, [<Xn|SP>{, #<pimm>}]"},
358+
349359
{0xfc000000, 0x14000000, No_VFP, &EmulateInstructionARM64::EmulateB,
350360
"B <label>"},
351361
{0xff000010, 0x54000000, No_VFP, &EmulateInstructionARM64::EmulateBcond,
@@ -930,9 +940,29 @@ template <EmulateInstructionARM64::AddrMode a_mode>
930940
bool EmulateInstructionARM64::EmulateLDRSTRImm(const uint32_t opcode) {
931941
uint32_t size = Bits32(opcode, 31, 30);
932942
uint32_t opc = Bits32(opcode, 23, 22);
943+
uint32_t vr = Bit32(opcode, 26);
933944
uint32_t n = Bits32(opcode, 9, 5);
934945
uint32_t t = Bits32(opcode, 4, 0);
935946

947+
MemOp memop;
948+
if (vr) {
949+
// opc<1> == 1 && size != 0 is an undefined encoding.
950+
if (Bit32(opc, 1) == 1 && size != 0)
951+
return false;
952+
// opc<1> == 1 && size == 0 encode the 128-bit variant.
953+
if (Bit32(opc, 1) == 1)
954+
size = 4;
955+
memop = Bit32(opc, 0) == 1 ? MemOp_LOAD : MemOp_STORE;
956+
} else {
957+
if (Bit32(opc, 1) == 0) {
958+
memop = Bit32(opc, 0) == 1 ? MemOp_LOAD : MemOp_STORE;
959+
} else {
960+
memop = MemOp_LOAD;
961+
if (size == 2 && Bit32(opc, 0) == 1)
962+
return false;
963+
}
964+
}
965+
936966
bool wback;
937967
bool postindex;
938968
uint64_t offset;
@@ -955,16 +985,6 @@ bool EmulateInstructionARM64::EmulateLDRSTRImm(const uint32_t opcode) {
955985
break;
956986
}
957987

958-
MemOp memop;
959-
960-
if (Bit32(opc, 1) == 0) {
961-
memop = Bit32(opc, 0) == 1 ? MemOp_LOAD : MemOp_STORE;
962-
} else {
963-
memop = MemOp_LOAD;
964-
if (size == 2 && Bit32(opc, 0) == 1)
965-
return false;
966-
}
967-
968988
Status error;
969989
bool success = false;
970990
uint64_t address;
@@ -989,7 +1009,8 @@ bool EmulateInstructionARM64::EmulateLDRSTRImm(const uint32_t opcode) {
9891009
return false;
9901010

9911011
std::optional<RegisterInfo> reg_info_Rt =
992-
GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + t);
1012+
vr ? GetRegisterInfo(eRegisterKindLLDB, fpu_d0_arm64 + t)
1013+
: GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + t);
9931014
if (!reg_info_Rt)
9941015
return false;
9951016

lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,3 +856,111 @@ TEST_F(TestArm64InstEmulation, TestCFAResetToSP) {
856856
EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
857857
EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
858858
}
859+
860+
TEST_F(TestArm64InstEmulation, TestPrologueStartsWithStrD8) {
861+
ArchSpec arch("aarch64");
862+
std::unique_ptr<UnwindAssemblyInstEmulation> engine(
863+
static_cast<UnwindAssemblyInstEmulation *>(
864+
UnwindAssemblyInstEmulation::CreateInstance(arch)));
865+
ASSERT_NE(nullptr, engine);
866+
867+
const UnwindPlan::Row *row;
868+
AddressRange sample_range;
869+
UnwindPlan unwind_plan(eRegisterKindLLDB);
870+
UnwindPlan::Row::AbstractRegisterLocation regloc;
871+
872+
// The sample function is built with 'clang --target aarch64 -O1':
873+
//
874+
// int bar(float x);
875+
// int foo(float x) {
876+
// return bar(x) + bar(x);
877+
// }
878+
//
879+
// The function uses one floating point register and spills it with
880+
// 'str d8, [sp, #-0x20]!'.
881+
882+
// clang-format off
883+
uint8_t data[] = {
884+
// prologue
885+
0xe8, 0x0f, 0x1e, 0xfc, // 0: fc1e0fe8 str d8, [sp, #-0x20]!
886+
0xfd, 0xfb, 0x00, 0xa9, // 4: a900fbfd stp x29, x30, [sp, #0x8]
887+
0xf3, 0x0f, 0x00, 0xf9, // 8: f9000ff3 str x19, [sp, #0x18]
888+
0xfd, 0x23, 0x00, 0x91, // 12: 910023fd add x29, sp, #0x8
889+
890+
// epilogue
891+
0xfd, 0xfb, 0x40, 0xa9, // 16: a940fbfd ldp x29, x30, [sp, #0x8]
892+
0xf3, 0x0f, 0x40, 0xf9, // 20: f9400ff3 ldr x19, [sp, #0x18]
893+
0xe8, 0x07, 0x42, 0xfc, // 24: fc4207e8 ldr d8, [sp], #0x20
894+
0xc0, 0x03, 0x5f, 0xd6, // 28: d65f03c0 ret
895+
};
896+
// clang-format on
897+
898+
// UnwindPlan we expect:
899+
// 0: CFA=sp +0 =>
900+
// 4: CFA=sp+32 => d8=[CFA-32]
901+
// 8: CFA=sp+32 => fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]
902+
// 12: CFA=sp+32 => x19=[CFA-8] fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]
903+
// 16: CFA=fp+24 => x19=[CFA-8] fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]
904+
// 20: CFA=sp+32 => x19=[CFA-8] fp=<same> lr=<same> d8=[CFA-32]
905+
// 24: CFA=sp+32 => x19=<same> fp=<same> lr=<same> d8=[CFA-32]
906+
// 28: CFA=sp +0 => x19=<same> fp=<same> lr=<same> d8=<same>
907+
908+
sample_range = AddressRange(0x1000, sizeof(data));
909+
910+
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
911+
sample_range, data, sizeof(data), unwind_plan));
912+
913+
// 4: CFA=sp+32 => d8=[CFA-32]
914+
row = unwind_plan.GetRowForFunctionOffset(4);
915+
EXPECT_EQ(4, row->GetOffset());
916+
EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
917+
EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
918+
EXPECT_EQ(32, row->GetCFAValue().GetOffset());
919+
920+
EXPECT_TRUE(row->GetRegisterInfo(fpu_d8_arm64, regloc));
921+
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
922+
EXPECT_EQ(-32, regloc.GetOffset());
923+
924+
// 16: CFA=fp+24 => x19=[CFA-8] fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]
925+
row = unwind_plan.GetRowForFunctionOffset(16);
926+
EXPECT_EQ(16, row->GetOffset());
927+
EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
928+
EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
929+
EXPECT_EQ(24, row->GetCFAValue().GetOffset());
930+
931+
EXPECT_TRUE(row->GetRegisterInfo(gpr_x19_arm64, regloc));
932+
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
933+
EXPECT_EQ(-8, regloc.GetOffset());
934+
935+
EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));
936+
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
937+
EXPECT_EQ(-24, regloc.GetOffset());
938+
939+
EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));
940+
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
941+
EXPECT_EQ(-16, regloc.GetOffset());
942+
943+
EXPECT_TRUE(row->GetRegisterInfo(fpu_d8_arm64, regloc));
944+
EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
945+
EXPECT_EQ(-32, regloc.GetOffset());
946+
947+
// 28: CFA=sp +0 => x19=<same> fp=<same> lr=<same> d8=<same>
948+
row = unwind_plan.GetRowForFunctionOffset(28);
949+
EXPECT_EQ(28, row->GetOffset());
950+
EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
951+
EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
952+
EXPECT_EQ(0, row->GetCFAValue().GetOffset());
953+
954+
if (row->GetRegisterInfo(gpr_x19_arm64, regloc)) {
955+
EXPECT_TRUE(regloc.IsSame());
956+
}
957+
if (row->GetRegisterInfo(gpr_fp_arm64, regloc)) {
958+
EXPECT_TRUE(regloc.IsSame());
959+
}
960+
if (row->GetRegisterInfo(gpr_lr_arm64, regloc)) {
961+
EXPECT_TRUE(regloc.IsSame());
962+
}
963+
if (row->GetRegisterInfo(fpu_d8_arm64, regloc)) {
964+
EXPECT_TRUE(regloc.IsSame());
965+
}
966+
}

0 commit comments

Comments
 (0)