[Lldb-commits] Instruction emulation of arm64 'stp d8, d9, [sp, #-0x70]!' style instruction

Jason Molenda via lldb-commits lldb-commits at lists.llvm.org
Tue Oct 11 18:15:26 PDT 2016

Hi Tamas, I'm writing some unit tests for the unwind source generators - x86 last week, arm64 this week, and I noticed with this prologue:

    0x192b45c0c <+0>:  0x6db923e9   stp    d9, d8, [sp, #-0x70]!
    0x192b45c10 <+4>:  0xa9016ffc   stp    x28, x27, [sp, #0x10]
    0x192b45c14 <+8>:  0xa90267fa   stp    x26, x25, [sp, #0x20]
    0x192b45c18 <+12>: 0xa9035ff8   stp    x24, x23, [sp, #0x30]
    0x192b45c1c <+16>: 0xa90457f6   stp    x22, x21, [sp, #0x40]
    0x192b45c20 <+20>: 0xa9054ff4   stp    x20, x19, [sp, #0x50]
    0x192b45c24 <+24>: 0xa9067bfd   stp    x29, x30, [sp, #0x60]
    0x192b45c28 <+28>: 0x910183fd   add    x29, sp, #0x60            ; =0x60 
    0x192b45c2c <+32>: 0xd10a83ff   sub    sp, sp, #0x2a0            ; =0x2a0 

EmulateInstructionARM64::EmulateLDPSTP interprets this as a save of v31.  The use of reg 31 is an easy bug, the arm manual C7.2.284 ("STP (SIMD&FP)") gives us an "opc" (0b00 == 32-bit registers, 0b01 == 64-bit registers, 0b10 == 128-bit registers), an immediate value, and three registers (Rt2, Rn, Rt).  In the above example, these work out to Rt2 == 8 (d8), Rn == 31 ("sp"), Rt == 9 (d9).  The unwinder is incorrectly saying v31 right now because it's using Rn -

  if (vector) {
    if (!GetRegisterInfo(eRegisterKindDWARF, arm64_dwarf::v0 + n, reg_info_Rt))
      return false;
    if (!GetRegisterInfo(eRegisterKindDWARF, arm64_dwarf::v0 + n, reg_info_Rt2))
      return false;

This would normally take up 32 bytes of stack space and cause big problems, but because we're writing the same reg twice, I think we luck out and only take 16 bytes of the stack.

We don't have dwarf register numbers for s0..31, d0..31, so we can't track this instruction's behavior 100% correctly but maybe if we said that 

That would be an easy fix, like

  if (vector) {
    if (!GetRegisterInfo(eRegisterKindDWARF, arm64_dwarf::v0 + t, reg_info_Rt))
      return false;
    if (!GetRegisterInfo(eRegisterKindDWARF, arm64_dwarf::v0 + t2, reg_info_Rt2))
      return false;

We don't have dwarf register numbers for s0..31, d0..31, so I don't think we can correctly track this instruction's actions today.  Maybe we should put a save of v8 at CFA-112 and a save of v9 at CFA-104.  As long as the target is operating in little endian mode, when we go to get the contents of v8/v9 we're only actually USING the lower 64 bits so it'll work out, right?  I think I have that right.  We'll be reading garbage in the upper 64 bits - the register reading code won't have any knowledge of the fact that we only have the lower 32/64 bits available to us.

Throwing the problem out there, would like to hear what you think.  I don't want to encode buggy behavior in a unit test ;) so I'd like it for us to think about what correct behavior would be, and do that before I write the test.

More information about the lldb-commits mailing list