bhushan created this revision.
bhushan added a reviewer: clayborg.
bhushan added subscribers: lldb-commits, jaydeep, sagar, nitesh.jain, 
mohit.bhakkad.
bhushan set the repository for this revision to rL LLVM.

In MIPS, when a breakpoint is hit in a delay slot then the PC points to the 
previous branch/jump instruction. In this case, CAUSE.BD bit is set and we can 
correct the PC accordingly. 
However doing a single step at this point will continue execution from the 
current PC and not from the target of previous branch/jump instruction. 
Solution to this is to not allow breakpoint in a delay slot and move it to 
previous branch/jump instruction (which will have same effect).

When user tries to set breakpoint by address then this patch checks if the 
instruction at that address is a delay slot instruction and if it is then the 
breakpoint is moved to its previous instruction.

Repository:
  rL LLVM

http://reviews.llvm.org/D12184

Files:
  include/lldb/Core/Disassembler.h
  include/lldb/Target/Target.h
  source/Core/Disassembler.cpp
  source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp
  source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h
  source/Target/Target.cpp

Index: source/Target/Target.cpp
===================================================================
--- source/Target/Target.cpp
+++ source/Target/Target.cpp
@@ -44,6 +44,8 @@
 #include "lldb/Interpreter/Property.h"
 #include "lldb/Symbol/ClangASTContext.h"
 #include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
 #include "lldb/Target/LanguageRuntime.h"
 #include "lldb/Target/ObjCLanguageRuntime.h"
 #include "lldb/Target/Process.h"
@@ -344,6 +346,10 @@
 Target::CreateBreakpoint (lldb::addr_t addr, bool internal, bool hardware)
 {
     Address so_addr;
+    uint32_t shift_size = 0;
+    
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+
     // Attempt to resolve our load address if possible, though it is ok if
     // it doesn't resolve to section/offset.
 
@@ -354,6 +360,17 @@
         // The address didn't resolve, so just set this as an absolute address
         so_addr.SetOffset (addr);
     }
+
+    // Check if the instruction at this address is in delay slot.
+    // If it is, then move the breakpoint to it's previous instruction.
+    shift_size = AdjustBreakpointInDelaySlot (addr);
+    if (shift_size)
+    {
+        so_addr.SetOffset (so_addr.GetOffset () - shift_size);
+        if (log)
+            log->Printf ("Target::%s Breakpoint at 0x%8.8" PRIx64 " is shifted to 0x%8.8" PRIx64 " \n", __FUNCTION__, addr, (addr - shift_size));
+    }
+
     BreakpointSP bp_sp (CreateBreakpoint(so_addr, internal, hardware));
     return bp_sp;
 }
@@ -2134,6 +2151,163 @@
     return opcode_addr;
 }
 
+uint32_t
+Target::AdjustBreakpointInDelaySlot (lldb::addr_t addr)
+{
+    uint32_t shift_size = 0;
+    Address resolved_addr;
+    SectionLoadList &section_load_list = GetSectionLoadList();
+
+    if (section_load_list.IsEmpty())
+        // No sections are loaded, so we must assume we are not running yet
+        // and need to operate only on file address.
+        m_images.ResolveFileAddress (addr, resolved_addr); 
+    else
+        section_load_list.ResolveLoadAddress(addr, resolved_addr);
+
+    switch (m_arch.GetMachine())
+    {
+    default:
+        break;
+    case llvm::Triple::mips:
+    case llvm::Triple::mipsel:
+    case llvm::Triple::mips64:
+    case llvm::Triple::mips64el:
+    {
+        addr_t function_start = 0;
+        addr_t current_offset = 0;
+        uint32_t loop_count = 0;
+        uint32_t arch_flags = m_arch.GetFlags ();
+        bool IsMips16 = arch_flags & ArchSpec::eMIPSAse_mips16;
+        bool IsMicromips = arch_flags & ArchSpec::eMIPSAse_micromips;
+
+        // Get the function boundaries to make sure we don't scan back before the beginning of the current function.
+        ModuleSP temp_addr_module_sp (resolved_addr.GetModule());
+        if (temp_addr_module_sp)
+        {
+            SymbolContext sc;
+            uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol;
+            uint32_t resolved_mask = temp_addr_module_sp->ResolveSymbolContextForAddress(resolved_addr, resolve_scope, sc);
+            if (sc.function)
+            {
+                function_start = sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress(this);
+                if (function_start == LLDB_INVALID_ADDRESS)
+                    function_start = sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
+            }
+            else if (sc.symbol)
+            {
+                Address sym_addr = sc.symbol->GetAddress();
+                function_start = sym_addr.GetFileAddress();
+            }
+            current_offset = addr - function_start;
+        }
+
+        // If breakpoint address is start of function then we dont have to do anything.
+        if (current_offset == 0)
+            return shift_size;
+        else
+            loop_count = current_offset / 2;
+
+        if (loop_count > 3)
+        {
+            // Scan previous 6 bytes
+            if (IsMips16 | IsMicromips)
+                loop_count = 3;
+            // For mips-only instructions are always 4 bytes, so scan previous 4 bytes only.
+            else
+                loop_count = 2;
+        }
+
+        // Create Disassembler Instance
+        lldb::DisassemblerSP disasm_sp (Disassembler::FindPlugin(m_arch, NULL, NULL));
+
+        ExecutionContext exe_ctx;
+        CalculateExecutionContext(exe_ctx);
+        InstructionList instruction_list;
+        InstructionSP prev_insn;
+        bool prefer_file_cache = true; // Read from file
+        uint32_t inst_to_choose = 0;
+
+        for (uint32_t i = 1; i <= loop_count; i++)
+        {
+            // Adjust the address to read from.
+            resolved_addr.Slide (-2);
+            AddressRange range(resolved_addr, i*2);
+            uint32_t insn_size = 0;
+
+            uint32_t bytes_disassembled = disasm_sp->ParseInstructions (&exe_ctx, range, NULL, prefer_file_cache);
+            
+            uint32_t num_insns = disasm_sp->GetInstructionList().GetSize();
+            if (num_insns)
+            {
+                prev_insn = disasm_sp->GetInstructionList().GetInstructionAtIndex(0);
+                insn_size = prev_insn->GetOpcode().GetByteSize();
+                if (i == 1 && insn_size == 2)
+                {
+                    // This looks like a valid 2-byte instruction (but it could be a part of upper 4 byte instruction).
+                    instruction_list.Append(prev_insn);
+                    inst_to_choose = 1;
+                }
+                else if (i == 2)
+                {
+                    // Here we may get one 4-byte instruction or two 2-byte instructions.
+                    if (num_insns == 2)
+                    {
+                        // Looks like there are two 2-byte instructions above our breakpoint target address.
+                        // Now the upper 2-byte instruction is either a valid 2-byte instruction or could be a part of it's upper 4-byte instruction.
+                        // In both cases we don't care because in this case lower 2-byte instruction is definitely a valid instruction
+                        // and whatever i=1 iteration has found out is true.
+                        inst_to_choose = 1;
+                        break;
+                    }
+                    else if (insn_size == 4)
+                    {
+                        // This instruction claims its a valid 4-byte instruction. But it could be a part of it's upper 4-byte instruction.
+                        // Lets try scanning upper 2 bytes to verify this.
+                        instruction_list.Append(prev_insn);
+                        inst_to_choose = 2;
+                    }
+                }
+                else if (i == 3)
+                {
+                    if (insn_size == 4)
+                        // FIXME: We reached here that means instruction at [target - 4] has already claimed to be a 4-byte instruction,
+                        // and now instruction at [target - 6] is also claiming that it's a 4-byte instruction. This can not be true.
+                        // In this case we can not decide the valid previous instruction so we let lldb set the breakpoint at the address given by user.
+                        inst_to_choose = 0;
+                    else
+                        // This is straight-forward 
+                        inst_to_choose = 2;
+                    break;
+                }
+            }
+            else
+            {
+                // Decode failed, bytes do not form a valid instruction. So whatever previous iteration has found out is true.
+                if (i > 1)
+                {
+                    inst_to_choose = i - 1;
+                    break;
+                }
+            }
+        }
+
+        // Check if we are able to find any valid instruction.
+        if (inst_to_choose)
+        {
+            if (inst_to_choose > instruction_list.GetSize())
+                inst_to_choose--;
+            prev_insn = instruction_list.GetInstructionAtIndex(inst_to_choose - 1);
+
+            if (prev_insn->HasDelaySlot())
+                shift_size = prev_insn->GetOpcode().GetByteSize();
+        }
+        break;
+    }
+    }
+    return shift_size;
+}
+
 SourceManager &
 Target::GetSourceManager ()
 {
Index: source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h
===================================================================
--- source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h
+++ source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h
@@ -49,6 +49,7 @@
         uint64_t PrintMCInst (llvm::MCInst &mc_inst, char *output_buffer, size_t out_buffer_len);
         void     SetStyle (bool use_hex_immed, HexImmediateStyle hex_style);
         bool     CanBranch (llvm::MCInst &mc_inst);
+        bool     HasDelaySlot (llvm::MCInst &mc_inst);
         bool     IsValid()
         {
             return m_is_valid;
Index: source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp
===================================================================
--- source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp
+++ source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp
@@ -52,6 +52,7 @@
         Instruction (address, addr_class),
         m_disasm_sp (disasm.shared_from_this()),
         m_does_branch (eLazyBoolCalculate),
+        m_has_delay_slot (eLazyBoolCalculate),
         m_is_valid (false),
         m_using_file_addr (false)
     {
@@ -99,6 +100,43 @@
         return m_does_branch == eLazyBoolYes;
     }
 
+    virtual bool
+    HasDelaySlot ()
+    {
+        if (m_has_delay_slot == eLazyBoolCalculate)
+        {
+            GetDisassemblerLLVMC().Lock(this, NULL);
+            DataExtractor data;
+            if (m_opcode.GetData(data))
+            {
+                bool is_alternate_isa;
+                lldb::addr_t pc = m_address.GetFileAddress();
+
+                DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa);
+                const uint8_t *opcode_data = data.GetDataStart();
+                const size_t opcode_data_len = data.GetByteSize();
+                llvm::MCInst inst;
+                const size_t inst_size = mc_disasm_ptr->GetMCInst (opcode_data,
+                                                                   opcode_data_len,
+                                                                   pc,
+                                                                   inst);
+                // if we didn't understand the instruction, say it doesn't have a delay slot...
+                if (inst_size == 0)
+                    m_has_delay_slot = eLazyBoolNo;
+                else
+                {
+                    const bool has_delay_slot = mc_disasm_ptr->HasDelaySlot(inst);
+                    if (has_delay_slot)
+                        m_has_delay_slot = eLazyBoolYes;
+                    else
+                        m_has_delay_slot = eLazyBoolNo;
+                }
+            }
+            GetDisassemblerLLVMC().Unlock();
+        }
+        return m_has_delay_slot == eLazyBoolYes;
+    }
+
     DisassemblerLLVMC::LLVMCDisassembler *
     GetDisasmToUse (bool &is_alternate_isa)
     {
@@ -409,6 +447,7 @@
 
     DisassemblerSP          m_disasm_sp; // for ownership
     LazyBool                m_does_branch;
+    LazyBool                m_has_delay_slot;
     bool                    m_is_valid;
     bool                    m_using_file_addr;
 };
@@ -543,6 +582,12 @@
 }
 
 bool
+DisassemblerLLVMC::LLVMCDisassembler::HasDelaySlot (llvm::MCInst &mc_inst)
+{
+    return m_instr_info_ap->get(mc_inst.getOpcode()).hasDelaySlot();
+}
+
+bool
 DisassemblerLLVMC::FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor)
 {
     llvm::Triple triple = arch.GetTriple();
Index: source/Core/Disassembler.cpp
===================================================================
--- source/Core/Disassembler.cpp
+++ source/Core/Disassembler.cpp
@@ -675,6 +675,13 @@
     return false;
 }
 
+bool
+Instruction::HasDelaySlot ()
+{
+    // Default is false.
+    return false;
+}
+
 OptionValueSP
 Instruction::ReadArray (FILE *in_file, Stream *out_stream, OptionValue::Type data_type)
 {
@@ -1318,7 +1325,14 @@
     // This is NOT a valid question for a pseudo instruction.
     return false;
 }
-    
+
+bool
+PseudoInstruction::HasDelaySlot ()
+{
+    // This is NOT a valid question for a pseudo instruction.
+    return false;
+}
+
 size_t
 PseudoInstruction::Decode (const lldb_private::Disassembler &disassembler,
                            const lldb_private::DataExtractor &data,
Index: include/lldb/Target/Target.h
===================================================================
--- include/lldb/Target/Target.h
+++ include/lldb/Target/Target.h
@@ -920,6 +920,14 @@
     lldb::addr_t
     GetOpcodeLoadAddress (lldb::addr_t load_addr, lldb::AddressClass addr_class = lldb::eAddressClassInvalid) const;
 
+    // Prevent user from setting breakpoint in delay slot.
+    // Check if the instruction at address is in delay slot.
+    // If it is, then return the byte size of it's previous instruction so that 
+    // breakpoint will be moved to it's previous instruction.
+    // If instruction at address is not in delay slot then return 0.
+    uint32_t
+    AdjustBreakpointInDelaySlot (lldb::addr_t addr);
+
 protected:
     //------------------------------------------------------------------
     /// Implementing of ModuleList::Notifier.
Index: include/lldb/Core/Disassembler.h
===================================================================
--- include/lldb/Core/Disassembler.h
+++ include/lldb/Core/Disassembler.h
@@ -142,6 +142,9 @@
     virtual bool
     DoesBranch () = 0;
 
+    virtual bool
+    HasDelaySlot ();
+
     virtual size_t
     Decode (const Disassembler &disassembler, 
             const DataExtractor& data,
@@ -266,6 +269,9 @@
     virtual bool
     DoesBranch ();
 
+    virtual bool
+    HasDelaySlot ();
+
     virtual void
     CalculateMnemonicOperandsAndComment (const ExecutionContext* exe_ctx)
     {
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to