Repository: dyninst/dyninst
Pull request #1929: Fix PowerPC branch instruction semantics
Author: login: hainest
Labels: name: bug
name: PowerPC
name: instructionAPI
description: This issue is directly related to instructionAPI
Created: 2025-03-17T06:20:25Z | Merged: 2025-04-02T17:39:20Z

This should have been part of 64499aa304.

Fixes #1072

Linked issue #1072: jump table analysis fails checking bclr opcode for ppcle so
For the dyninst ppcle target, jump table analysis during code parsing fails with:
Dyninst::DataflowAPI::RoseInsnPPCFactory::handleSpecialCases(entryID, SgAsmInstruction*, SgAsmOperandList*): Assertion `power_op_bclr == iapi_opcode' failed.

This patch bypasses the problem:
--- dyninst-11.0.0/dataflowAPI/src/RoseInsnFactory.C.orig	2021-06-09 15:54:21.753883619 -0400
+++ dyninst-11.0.0/dataflowAPI/src/RoseInsnFactory.C	2021-06-23 14:17:37.854933719 -0400
@@ -317,5 +317,5 @@
     // It looks like the ROSE semantics code will infer the target from 
     // the bo field. So, what is passed in as the third operands does not matter
-    if(branch_target) {
+    if(branch_target || iapi_opcode == power_op_bc) {
       rose_operands->append_operand(new SgAsmDoubleWordValueExpression(branch_target));
     } else if(power_op_bcctr == iapi_opcode) {

The problem is dependent on how libdw is built
 https://scox.fedorapeople.org/dyninst/libdw-0.185.ppc64le.so
Linked issue comments: user: login: stanfordcox
created_at: 2021-07-02T12:57:15Z
body: [dyninst-11.0.0-rosebc.patch.txt](https://github.com/dyninst/dyninst/files/6754673/dyninst-11.0.0-rosebc.patch.txt)

author_association: CONTRIBUTOR

Diff:
diff --git a/instructionAPI/src/Instruction.C b/instructionAPI/src/Instruction.C
index 7aa1ce42ba..9a29144f7b 100644
--- a/instructionAPI/src/Instruction.C
+++ b/instructionAPI/src/Instruction.C
@@ -489,9 +489,12 @@ namespace Dyninst { namespace InstructionAPI {
           return c_CallInsn;
         }
       }
-      if(m_InsnOp.getID() == power_op_bclr) {
-        return c_ReturnInsn;
+    }
+    if(m_InsnOp.getID() == power_op_bclr) {
+      if(this->allowsFallThrough()) {
+        return c_BranchInsn;
       }
+      return c_ReturnInsn;
     }
     if(isSoftwareInterrupt(*this)) {
       return c_InterruptInsn;
diff --git a/instructionAPI/src/InstructionDecoder-power.C b/instructionAPI/src/InstructionDecoder-power.C
index 3c67b51ba4..d29cd333d2 100644
--- a/instructionAPI/src/InstructionDecoder-power.C
+++ b/instructionAPI/src/InstructionDecoder-power.C
@@ -294,28 +294,143 @@ which are both 0).
       std::mem_fn (*curFn)(this);
     }
 
-    // bclr is either a conventional branch or used as a return from a procedure
     if(current->op == power_op_bclr) {
       /*********************************************************************
-       *  Power ISA Version 3.1, Book I. May 2020. 2.4 Branch Instructions
+       *  Power ISA Version 3.1C, Book I. May 2024
+       *  2.4 Branch Instructions
+       *
+       *  bclr   BO,BI,BH (LK=0)
+       *  bclrl  BO,BI,BH (LK=1)
+       *
+       *  Extended form examples:
+       *    bclr  4,6
+       *    bltlr
+       *    bnelr cr2
+       *    bdnzlr
+       *
+       *
+       *  Programming Note (pg 38)
+       *  ------------------------
+       *
+       *  1. Do not use bclrl as a subroutine call
+       *
+       *  2. The conventional return idiom is to use `bclr` with BH=0b00
        *
-       *  The conventional return sequence is to use `bclr` (Branch
-       *  Conditional to Link Register) with BH=0b00.
        *********************************************************************/
-      auto target = makeRegisterExpression(ppc32::lr);
-      insn_in_progress->addSuccessor(std::move(target), false, true, bcIsConditional, false);
+      constexpr bool is_call = true;
+      constexpr bool is_indirect = true;
+      constexpr bool is_conditional = true;
+      constexpr bool is_fallthrough = true;
+
+      bool const LK = field<31, 31>(insn);
+
+      // link register is _always_ read, but conditionally written
+      {
+        auto link_register = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_read = true;
+        constexpr bool is_implicit = true;
+        bool const is_written = (LK == 1);
+        insn_in_progress->appendOperand(std::move(link_register), is_read, is_written, is_implicit);
+      }
+
+      if(LK == 1) {
+        // 1. Do not use bclrl as a subroutine call
+        auto lr = makeRegisterExpression(ppc32::lr);
+        insn_in_progress->addSuccessor(std::move(lr), !is_call, is_indirect, is_conditional, !is_fallthrough);
+      }
+      else {
+        // 2. The conventional return sequence is to use `bclr` with BH=0b00
+        bool const is_subroutine_return = [this]() {
+          auto const bh_field = field<19,20>(insn);
+          return bh_field == 0;
+        }();
+
+        if(is_subroutine_return) {
+          // This is _not_ a fallthrough conditional
+          this->bcIsConditional = false;
+
+          // Indirectly branch through the link register
+          auto lr = makeRegisterExpression(ppc32::lr);
+          insn_in_progress->addSuccessor(lr, !is_call, is_indirect, !is_conditional, !is_fallthrough);
+        }
+      }
 
       if(bcIsConditional) {
-        insn_in_progress->addSuccessor(makeFallThroughExpr(), false, false, false, true);
+        auto target = makeFallThroughExpr();
+        insn_in_progress->addSuccessor(std::move(target), !is_call, !is_indirect, !is_conditional, is_fallthrough);
       }
     }
+
     if(current->op == power_op_bcctr) {
-      insn_in_progress->addSuccessor(makeRegisterExpression(ppc32::ctr), field<31, 31>(insn) == 1,
-                                     true, bcIsConditional, false);
-      if(bcIsConditional) {
-        insn_in_progress->addSuccessor(makeFallThroughExpr(), false, false, false, true);
+      /*********************************************************************
+       *  Power ISA Version 3.1C, Book I. May 2024
+       *  2.4 Branch Instructions
+       *
+       *    bcctr   BO,BI,BH (LK=0)
+       *    bcctrl  BO,BI,BH (LK=1)
+       *
+       *    Extended form examples:
+       *      blctr
+       *      bnectr cr0
+       *
+       *  Programming Note (pg 38)
+       *  ------------------------
+       *
+       *  1. Computed goto’s, case statements, etc.
+       *
+       *     Put the branch address in CTR and set LK=0 (BH=0b11, if appropriate)
+       *
+       *  2. Indirect subroutine linkage
+       *
+       *     Place the final target address in CTR and use bcctr (LK=0).
+       *
+       *     NOTE: This form is not meant to fall through (e.g., it's used in PLT
+       *     thunks), so usually also has BH=0b1z1zz as per Figure 2.5 to indicate
+       *     "branch always".
+       *
+       *  3. Function call
+       *
+       *     For direct calls, put the target address in CTR and use bcctrl (LK=1).
+       ****************************************************************************/
+      constexpr bool is_call = true;
+      constexpr bool is_indirect = true;
+      constexpr bool is_conditional = true;
+      constexpr bool is_fallthrough = true;
+
+      bool const LK = field<31, 31>(insn);
+
+      auto add_lr_operand = [this, LK](bool const is_read) {
+        auto link_register = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_implicit = true;
+        bool const is_written = (LK == 1);
+        insn_in_progress->appendOperand(link_register, is_read, is_written, is_implicit);
+      };
+
+      if(LK == 1) {
+        // Case 3 - Function call
+        auto ctr = makeRegisterExpression(ppc32::ctr);
+        insn_in_progress->addSuccessor(std::move(ctr), is_call, is_indirect, !is_conditional, !is_fallthrough);
+        add_lr_operand(true);
+      } else {
+        bool const always_branch = [this]() {
+          auto const bo_field = field<6,10>(insn);
+          auto const mask = 0x14;  // bits 0,1,3 are ignored (0b10100)
+          return (bo_field & mask) == mask;
+        }();
+
+        if(always_branch) {
+          // Case 2 - Indirect subroutine linkage
+          auto ctr = makeRegisterExpression(ppc32::ctr);
+          insn_in_progress->addSuccessor(std::move(ctr), !is_call, is_indirect, !is_conditional, !is_fallthrough);
+          add_lr_operand(false);
+        } else {
+          // Case 1 - Computed goto’s, case statements, etc.
+          auto ctr = makeRegisterExpression(ppc32::ctr);
+          insn_in_progress->addSuccessor(std::move(ctr), !is_call, is_indirect, is_conditional, is_fallthrough);
+        }
       }
     }
+
     if(current->op == power_op_addic || current->op == power_op_andi_rc ||
        current->op == power_op_andis_rc || current->op == power_op_stwcx_rc ||
        current->op == power_op_stdcx_rc) {
@@ -1163,36 +1278,86 @@ which are both 0).
   }
 
   void InstructionDecoder_power::BO() {
-    bcIsConditional = true;
-    invertBranchCondition = false;
-    if(!field<8, 8>(insn)) {
-      Expression::Ptr ctr = makeRegisterExpression(makePowerRegID(ppc32::ctr, 0));
-      if(field<9, 9>(insn)) {
-        insn_in_progress->getOperation().mnemonic = "bdz";
+    /*********************************************************************
+     *  Power ISA Version 3.1C, Book I. May 2024
+     *  2.4 Branch Instructions
+     *
+     *  Figure 2.5: BO field encodings
+     *
+     *  "z" denotes a bit that is ignored.
+     *  The "a" and "t" bits are ignored by us.
+     *
+     *  BO    | Description
+     *  ------------------------
+     *  0000z | Decrement CTR; branch if CTR != 0 and CR_{BI} == 0
+     *  0001z | Decrement CTR; branch if CTR == 0 and CR_{BI} == 0
+     *  001at | Branch if CR_{BI} == 0
+     *  0100z | Decrement CTR; branch if CTR != 0 and CR_{BI} == 1
+     *  0101z | Decrement CTR; branch if CTR == 0 and CR_{BI} == 1
+     *  011at | Branch if CR_{BI} == 1
+     *  1a00t | Decrement CTR; branch if CTR != 0
+     *  1a01t | Decrement CTR; branch if CTR == 0
+     *  1z1zz | Branch always
+     */
+    auto const bo_field = field<6,10>(insn);
+
+    bool const writes_ctr = [this, bo_field](){
+      // ctr is not written for bcctr, even when the BO table indicates it does
+      auto const id = field<0,5>(insn);
+      auto const xo = field<21,30>(insn);
+      if(id == 19 && xo == 528) return false;
+
+      // Otherwise, check BO_2
+      return field<2, 2>(bo_field) == 0;
+    }();
+
+    bool const branch_always = (!writes_ctr && field<0,0>(bo_field) == 1);
+    bcIsConditional = !branch_always;
+
+    invertBranchCondition = [bo_field](){
+      if((bo_field & 0x1E) == 0x00) return true;  // 0000z
+      if((bo_field & 0x1E) == 0x08) return true;  // 0100z
+      if((bo_field & 0x10) == 0x10) return true;  // 1a00t
+      return false;
+    }();
+
+    // The count register (ctr) is conditionally read and written
+    {
+      auto ctr = makeRegisterExpression(makePowerRegID(ppc32::ctr, 0));
+      bool const is_read = [this](){
+        // I-form doesn't read it
+        auto const id = field<0,5>(insn);
+        bool const is_b_form = (id == 16);
+        bool const is_xl_form = (id == 19);
+        return is_b_form || is_xl_form;
+      }();
+
+      insn_in_progress->appendOperand(std::move(ctr), is_read, writes_ctr);
+    }
+
+    auto &mnemonic = insn_in_progress->getOperation().mnemonic;
+    if(writes_ctr) {
+      if(field<4, 4>(bo_field) != 0) {
+        mnemonic = "bdz";
       } else {
-        insn_in_progress->getOperation().mnemonic = "bdn";
+        mnemonic = "bdn";
       }
-      insn_in_progress->appendOperand(ctr, true, true);
     }
-    if(!(field<6, 6>(insn))) {
-      invertBranchCondition = !field<7, 7>(insn);
-      if(insn_in_progress->getOperation().mnemonic == "bc") {
-        insn_in_progress->getOperation().mnemonic = "b";
+
+    bool const reads_crbi = (field<0,0>(bo_field) == 0);
+    if(reads_crbi) {
+      if(mnemonic == "bc") {
+        mnemonic = "b";
       }
       insn_in_progress->appendOperand(makeBIExpr(), true, false);
     }
-    if(field<8, 8>(insn) && field<6, 6>(insn)) {
-      size_t found = insn_in_progress->getOperation().mnemonic.rfind("c");
+
+    if(!bcIsConditional) {
+      size_t found = mnemonic.rfind("c");
       if(found != std::string::npos) {
-        insn_in_progress->getOperation().mnemonic.erase(found, 1);
+        mnemonic.erase(found, 1);
       }
-      bcIsConditional = false;
-    } else {
-      bool taken = (field<6, 6>(insn) && field<8, 8>(insn)) || field<16, 16>(insn);
-      taken ^= field<10, 10>(insn) ? true : false;
-      insn_in_progress->getOperation().mnemonic += (taken ? "+" : "-");
     }
-    return;
   }
 
   void InstructionDecoder_power::syscall() {
diff --git a/parseAPI/src/IA_power.C b/parseAPI/src/IA_power.C
index 2bc0b76a6b..42e39d5383 100644
--- a/parseAPI/src/IA_power.C
+++ b/parseAPI/src/IA_power.C
@@ -309,91 +309,9 @@ bool IA_power::isReturnAddrSave(Address& retAddr) const
   return ret;
 }
 
-bool IA_power::isReturn(Dyninst::ParseAPI::Function * context, Dyninst::ParseAPI::Block* currBlk) const
+bool IA_power::isReturn(Dyninst::ParseAPI::Function *, Dyninst::ParseAPI::Block*) const
 {
-  /* Check for leaf node or lw - mflr - blr pattern */
-  if (curInsn().isReturn()) {
-	parsing_printf(" Not BLR - returning false \n");
-	return false;
-   }
-  Function *func = context;
-  parsing_printf
-    ("isblrReturn at 0x%lx Addr 0x%lx 0x%lx Function addr 0x%lx leaf %d \n",
-     current, getAddr (), currBlk->start (), context->addr (),
-     func->_is_leaf_function);
-  if (!func->_is_leaf_function)
-    {
-      parsing_printf ("\t LR saved for %s \n", func->name().c_str());
-      // Check for lwz from Stack - mtlr - blr 
-      RegisterAST::Ptr regLR, regSP, reg11;
-		regLR = ppc32_LR; regSP = ppc32_SP; reg11 = ppc32_R11;
-
-      std::set < RegisterAST::Ptr > regs;
-      RegisterAST::Ptr sourceLRReg;
-
-      Instruction ci = curInsn ();
-      bool foundMTLR = false;
-      allInsns_t::reverse_iterator iter;
-      Address blockStart = currBlk->start ();
-      const unsigned char *b =
-	(const unsigned char *) (this->_isrc->
-				 getPtrToInstruction (blockStart));
-      InstructionDecoder decCopy (b, currBlk->size (),
-				  this->_isrc->getArch ());
-      IA_power copy (decCopy, blockStart, _obj, _cr, _isrc, _curBlk);
-      while (!copy.hasCFT ())
-	{
-	  copy.advance ();
-	}
-      for(iter = copy.allInsns.rbegin(); iter != copy.allInsns.rend(); iter++)
-	{
-	  parsing_printf ("\t\tchecking insn 0x%lx: %s \n", iter->first,
-		  iter->second.format ().c_str ());
-	  if (iter->second.getOperation().getID () == power_op_mtspr &&
-	      iter->second.isWritten (regLR))
-	    {
-	      iter->second.getReadSet (regs);
-	      if (regs.size () != 1)
-		{
-		  parsing_printf
-		    ("expected mtspr to read 1 register, insn is %s\n",
-		     ci.format ().c_str ());
-		  return false;
-		}
-	      sourceLRReg = *(regs.begin ());
-	      parsing_printf ("\t\t\t **** Found MTLR saved from %s \n",
-		      sourceLRReg->format ().c_str ());
-	      foundMTLR = true;
-	    }
-	  else if (foundMTLR &&
-		   iter->second.readsMemory () &&
-		   (iter->second.isRead (regSP) ||
-		    (iter->second.isRead (reg11))) &&
-		   iter->second.isWritten (sourceLRReg))
-	    {
-	      parsing_printf ("\t\t\t **** Found lwz - RETURNING TRUE\n");
-	      return true;
-	    }
-	}
-
-      parsing_printf (" Slicing for Addr 0x%lx startAddr 0x%lx ret addr 0x%lx func %s\n",
-	      getAddr (), currBlk->start (), func->_ret_addr, func->name().c_str());
-      bool ret = sliceReturn(currBlk, func->_ret_addr, func);
-
-      func->invalidateCache();
-      if (ret) {
-	parsing_printf ("\t\t\t **** Slicing - is a return instruction\n");
-	return true;
-      } else {
-	parsing_printf ("\t\t\t **** Slicing - is not a return instruction\n");
-	return false;	
-      }
-    }
-  else
-    {
-      parsing_printf ("\t leaf node  RETURNING TRUE \n");
-      return true;
-    }
+  return curInsn().isReturn();
 }
 
 bool IA_power::isFakeCall() const


Reviews:
user: login: bbiiggppiigg
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-03-17T15:26:50Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T16:16:51Z
user: login: kupsch
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T16:52:28Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T16:54:31Z
user: login: bbiiggppiigg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-30T07:44:32Z
user: login: bbiiggppiigg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-30T07:52:54Z
user: login: bbiiggppiigg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-30T07:54:55Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-30T18:50:57Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-30T18:51:48Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-30T19:15:15Z
user: login: bbiiggppiigg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-31T00:46:59Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T04:56:55Z
user: login: bbiiggppiigg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-01T12:59:08Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T13:01:49Z
user: login: bbiiggppiigg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-01T13:06:12Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T13:31:19Z
user: login: hainest
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T13:47:55Z
user: login: bbiiggppiigg
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-02T08:13:05Z

Review comments:
diff_hunk: @@ -305,7 +305,13 @@ which are both 0).
       auto target = makeRegisterExpression(ppc32::lr);
       insn_in_progress->addSuccessor(std::move(target), false, true, bcIsConditional, false);
 
-      if(bcIsConditional) {
+      bool const is_subroutine_return = [this]() {
path: instructionAPI/src/InstructionDecoder-power.C
user: login: bbiiggppiigg
body: Just wondering, is there any reason to use a lambda here instead of checking
if(field<19,20>(insn)==0) with a comment specifying what it is checking?
created_at: 2025-03-17T15:26:39Z
author_association: MEMBER
diff_hunk: @@ -305,7 +305,13 @@ which are both 0).
       auto target = makeRegisterExpression(ppc32::lr);
       insn_in_progress->addSuccessor(std::move(target), false, true, bcIsConditional, false);
 
-      if(bcIsConditional) {
+      bool const is_subroutine_return = [this]() {
path: instructionAPI/src/InstructionDecoder-power.C
user: login: hainest
body: I prefer self-documenting code when I can. Also, I made a couple of mistakes when I did the short version, so it was better for me, as well.
created_at: 2025-03-17T16:16:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -302,11 +302,29 @@ which are both 0).
        *  The conventional return sequence is to use `bclr` (Branch
        *  Conditional to Link Register) with BH=0b00.
        *********************************************************************/
-      auto target = makeRegisterExpression(ppc32::lr);
-      insn_in_progress->addSuccessor(std::move(target), false, true, bcIsConditional, false);
+      bool const is_subroutine_return = [this]() {
+        auto const bh_field = field<19,20>(insn);
+        return bh_field == 0;
+      }();
+
+      bool const is_conditional = !is_subroutine_return;
+
+      {
+        auto target = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_call = false;
+        constexpr bool is_indirect = true;
+        constexpr bool is_fallthrough = false;
+        insn_in_progress->addSuccessor(std::move(target), is_call, is_indirect, is_conditional,
path: instructionAPI/src/InstructionDecoder-power.C
user: login: kupsch
body: Is `std::move` useful in this case?
created_at: 2025-03-17T16:52:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -302,11 +302,29 @@ which are both 0).
        *  The conventional return sequence is to use `bclr` (Branch
        *  Conditional to Link Register) with BH=0b00.
        *********************************************************************/
-      auto target = makeRegisterExpression(ppc32::lr);
-      insn_in_progress->addSuccessor(std::move(target), false, true, bcIsConditional, false);
+      bool const is_subroutine_return = [this]() {
+        auto const bh_field = field<19,20>(insn);
+        return bh_field == 0;
+      }();
+
+      bool const is_conditional = !is_subroutine_return;
+
+      {
+        auto target = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_call = false;
+        constexpr bool is_indirect = true;
+        constexpr bool is_fallthrough = false;
+        insn_in_progress->addSuccessor(std::move(target), is_call, is_indirect, is_conditional,
path: instructionAPI/src/InstructionDecoder-power.C
user: login: hainest
body: Strictly, it's a micro-optimization, but it saves a single ref update in the shared_ptr.
created_at: 2025-03-17T16:54:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -294,28 +294,159 @@ which are both 0).
       std::mem_fn (*curFn)(this);
     }
 
-    // bclr is either a conventional branch or used as a return from a procedure
     if(current->op == power_op_bclr) {
       /*********************************************************************
-       *  Power ISA Version 3.1, Book I. May 2020. 2.4 Branch Instructions
+       *  Power ISA Version 3.1C, Book I. May 2024
+       *  2.4 Branch Instructions
+       *
+       *  bclr   BO,BI,BH (LK=0)
+       *  bclrl  BO,BI,BH (LK=1)
+       *
+       *  Extended form examples:
+       *    bclr  4,6
+       *    bltlr
+       *    bnelr cr2
+       *    bdnzlr
+       *
+       *
+       *  Programming Note (pg 38)
+       *  ------------------------
+       *
+       *  1. Do not use bclrl as a subroutine call
+       *
+       *  2. The conventional return idiom is to use `bclr` with BH=0b00
        *
-       *  The conventional return sequence is to use `bclr` (Branch
-       *  Conditional to Link Register) with BH=0b00.
        *********************************************************************/
-      auto target = makeRegisterExpression(ppc32::lr);
-      insn_in_progress->addSuccessor(std::move(target), false, true, bcIsConditional, false);
+      bool const LK = field<31, 31>(insn);
+
+      // link register is _always_ read, but conditionally written
+      {
+        auto link_register = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_read = true;
+        constexpr bool is_implicit = true;
+        bool const is_written = (LK == 1);
+        insn_in_progress->appendOperand(std::move(link_register), is_read, is_written, is_implicit);
+      }
+
+      if(LK == 1) {
+        // 1. Do not use bclrl as a subroutine call
+        constexpr bool is_call = false;
+
+        auto lr = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_indirect = true;
+        constexpr bool is_conditional = true;
+        constexpr bool is_fallthrough = false;
+        insn_in_progress->addSuccessor(std::move(lr), is_call, is_indirect, is_conditional, is_fallthrough);
+      }
+      else {
+        // 2. The conventional return sequence is to use `bclr` with BH=0b00
+        bool const is_subroutine_return = [this]() {
+          auto const bh_field = field<19,20>(insn);
+          return bh_field == 0;
+        }();
+
+        if(is_subroutine_return) {
+          // This is _not_ a fallthrough conditional
+          this->bcIsConditional = false;
+
+          // Indirectly branch through the link register
+          auto lr = makeRegisterExpression(ppc32::lr);
+          constexpr bool is_call = false;
+          constexpr bool is_indirect = true;
+          constexpr bool is_conditional = false;
+          constexpr bool is_fallthrough = false;
+          insn_in_progress->addSuccessor(lr, is_call, is_indirect, is_conditional, is_fallthrough);
+        }
+      }
 
       if(bcIsConditional) {
-        insn_in_progress->addSuccessor(makeFallThroughExpr(), false, false, false, true);
+        auto target = makeFallThroughExpr();
+        constexpr bool is_call = false;
+        constexpr bool is_indirect = true;
path: instructionAPI/src/InstructionDecoder-power.C
user: login: bbiiggppiigg
body: I assume fallthrough should not be indirect as in the original code?
created_at: 2025-03-30T07:44:32Z
author_association: MEMBER
diff_hunk: @@ -316,12 +316,18 @@ bool IA_power::isReturn(Dyninst::ParseAPI::Function * context, Dyninst::ParseAPI
 	parsing_printf(" Not BLR - returning false \n");
 	return false;
    }
+
   Function *func = context;
-  parsing_printf
-    ("isblrReturn at 0x%lx Addr 0x%lx 0x%lx Function addr 0x%lx leaf %d \n",
-     current, getAddr (), currBlk->start (), context->addr (),
-     func->_is_leaf_function);
-  if (!func->_is_leaf_function)
+
+  parsing_printf("Checking return idioms for %s@0x%lx in block=0x%lx, context=0x%lx\n",
+                 context->name().c_str(), getAddr(), currBlk->start(), context->addr());
+
+  if(func->_is_leaf_function) {
path: parseAPI/src/IA_power.C
user: login: bbiiggppiigg
body: You added a check for the case of leaf function and return early, but it seems like you didnt remove the original code in the else clause of if(_is_leaf_function) that does the same thing?

And maybe we should stick to just one of func or context for consistency (even though they seem to have the same value as func is assigned in line 319).
created_at: 2025-03-30T07:52:54Z
author_association: MEMBER
diff_hunk: @@ -1165,23 +1296,31 @@ which are both 0).
   void InstructionDecoder_power::BO() {
     bcIsConditional = true;
     invertBranchCondition = false;
-    if(!field<8, 8>(insn)) {
-      Expression::Ptr ctr = makeRegisterExpression(makePowerRegID(ppc32::ctr, 0));
+    bool const writes_ctr = (field<8, 8>(insn) == 0);
+
+    if(writes_ctr) {
       if(field<9, 9>(insn)) {
         insn_in_progress->getOperation().mnemonic = "bdz";
       } else {
         insn_in_progress->getOperation().mnemonic = "bdn";
       }
-      insn_in_progress->appendOperand(ctr, true, true);
     }
+
+    // The count register (ctr) is always read, but conditionally written
+    {
+      auto ctr = makeRegisterExpression(makePowerRegID(ppc32::ctr, 0));
+      constexpr bool is_read = true;
+      insn_in_progress->appendOperand(std::move(ctr), is_read, writes_ctr);
+    }
+
     if(!(field<6, 6>(insn))) {
       invertBranchCondition = !field<7, 7>(insn);
       if(insn_in_progress->getOperation().mnemonic == "bc") {
         insn_in_progress->getOperation().mnemonic = "b";
       }
       insn_in_progress->appendOperand(makeBIExpr(), true, false);
     }
-    if(field<8, 8>(insn) && field<6, 6>(insn)) {
+    if(writes_ctr && field<6, 6>(insn)) {
path: instructionAPI/src/InstructionDecoder-power.C
user: login: bbiiggppiigg
body: writes_ctr checks for field<8,8>(insn) ==0, so here it should be (!writes_ctr) I assume?
created_at: 2025-03-30T07:54:55Z
author_association: MEMBER
diff_hunk: @@ -294,28 +294,159 @@ which are both 0).
       std::mem_fn (*curFn)(this);
     }
 
-    // bclr is either a conventional branch or used as a return from a procedure
     if(current->op == power_op_bclr) {
       /*********************************************************************
-       *  Power ISA Version 3.1, Book I. May 2020. 2.4 Branch Instructions
+       *  Power ISA Version 3.1C, Book I. May 2024
+       *  2.4 Branch Instructions
+       *
+       *  bclr   BO,BI,BH (LK=0)
+       *  bclrl  BO,BI,BH (LK=1)
+       *
+       *  Extended form examples:
+       *    bclr  4,6
+       *    bltlr
+       *    bnelr cr2
+       *    bdnzlr
+       *
+       *
+       *  Programming Note (pg 38)
+       *  ------------------------
+       *
+       *  1. Do not use bclrl as a subroutine call
+       *
+       *  2. The conventional return idiom is to use `bclr` with BH=0b00
        *
-       *  The conventional return sequence is to use `bclr` (Branch
-       *  Conditional to Link Register) with BH=0b00.
        *********************************************************************/
-      auto target = makeRegisterExpression(ppc32::lr);
-      insn_in_progress->addSuccessor(std::move(target), false, true, bcIsConditional, false);
+      bool const LK = field<31, 31>(insn);
+
+      // link register is _always_ read, but conditionally written
+      {
+        auto link_register = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_read = true;
+        constexpr bool is_implicit = true;
+        bool const is_written = (LK == 1);
+        insn_in_progress->appendOperand(std::move(link_register), is_read, is_written, is_implicit);
+      }
+
+      if(LK == 1) {
+        // 1. Do not use bclrl as a subroutine call
+        constexpr bool is_call = false;
+
+        auto lr = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_indirect = true;
+        constexpr bool is_conditional = true;
+        constexpr bool is_fallthrough = false;
+        insn_in_progress->addSuccessor(std::move(lr), is_call, is_indirect, is_conditional, is_fallthrough);
+      }
+      else {
+        // 2. The conventional return sequence is to use `bclr` with BH=0b00
+        bool const is_subroutine_return = [this]() {
+          auto const bh_field = field<19,20>(insn);
+          return bh_field == 0;
+        }();
+
+        if(is_subroutine_return) {
+          // This is _not_ a fallthrough conditional
+          this->bcIsConditional = false;
+
+          // Indirectly branch through the link register
+          auto lr = makeRegisterExpression(ppc32::lr);
+          constexpr bool is_call = false;
+          constexpr bool is_indirect = true;
+          constexpr bool is_conditional = false;
+          constexpr bool is_fallthrough = false;
+          insn_in_progress->addSuccessor(lr, is_call, is_indirect, is_conditional, is_fallthrough);
+        }
+      }
 
       if(bcIsConditional) {
-        insn_in_progress->addSuccessor(makeFallThroughExpr(), false, false, false, true);
+        auto target = makeFallThroughExpr();
+        constexpr bool is_call = false;
+        constexpr bool is_indirect = true;
path: instructionAPI/src/InstructionDecoder-power.C
user: login: hainest
body: It's unclear what "indirect" means here. Except for printing, Dyninst only uses that information in only one place: https://github.com/dyninst/dyninst/blob/v13.0.0/dyninstAPI/src/Relocation/Widgets/CFWidget.C#L150. 

The comment there is

```
   // Indirect branch:
   //   1) Just go for it... we have no control, really
```

Super helpful. Given this, I interpret an "indirect" control flow target to be contained in some location that must be dereferenced before being used (e.g., branching through a register) as opposed to being a literal offset or absolute target. Hence, fallthrough targets are always indirect since they go through the PC (or the LR for ppc).
created_at: 2025-03-30T18:50:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -316,12 +316,18 @@ bool IA_power::isReturn(Dyninst::ParseAPI::Function * context, Dyninst::ParseAPI
 	parsing_printf(" Not BLR - returning false \n");
 	return false;
    }
+
   Function *func = context;
-  parsing_printf
-    ("isblrReturn at 0x%lx Addr 0x%lx 0x%lx Function addr 0x%lx leaf %d \n",
-     current, getAddr (), currBlk->start (), context->addr (),
-     func->_is_leaf_function);
-  if (!func->_is_leaf_function)
+
+  parsing_printf("Checking return idioms for %s@0x%lx in block=0x%lx, context=0x%lx\n",
+                 context->name().c_str(), getAddr(), currBlk->start(), context->addr());
+
+  if(func->_is_leaf_function) {
path: parseAPI/src/IA_power.C
user: login: hainest
body: This is still a draft, so that code is actually going away. I wish GitHub would let you remove a review request, but it won't. Sorry.
created_at: 2025-03-30T18:51:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1165,23 +1296,31 @@ which are both 0).
   void InstructionDecoder_power::BO() {
     bcIsConditional = true;
     invertBranchCondition = false;
-    if(!field<8, 8>(insn)) {
-      Expression::Ptr ctr = makeRegisterExpression(makePowerRegID(ppc32::ctr, 0));
+    bool const writes_ctr = (field<8, 8>(insn) == 0);
+
+    if(writes_ctr) {
       if(field<9, 9>(insn)) {
         insn_in_progress->getOperation().mnemonic = "bdz";
       } else {
         insn_in_progress->getOperation().mnemonic = "bdn";
       }
-      insn_in_progress->appendOperand(ctr, true, true);
     }
+
+    // The count register (ctr) is always read, but conditionally written
+    {
+      auto ctr = makeRegisterExpression(makePowerRegID(ppc32::ctr, 0));
+      constexpr bool is_read = true;
+      insn_in_progress->appendOperand(std::move(ctr), is_read, writes_ctr);
+    }
+
     if(!(field<6, 6>(insn))) {
       invertBranchCondition = !field<7, 7>(insn);
       if(insn_in_progress->getOperation().mnemonic == "bc") {
         insn_in_progress->getOperation().mnemonic = "b";
       }
       insn_in_progress->appendOperand(makeBIExpr(), true, false);
     }
-    if(field<8, 8>(insn) && field<6, 6>(insn)) {
+    if(writes_ctr && field<6, 6>(insn)) {
path: instructionAPI/src/InstructionDecoder-power.C
user: login: hainest
body: That's a good catch. It turns out not to matter because the only place it's used is where I have separate logic to determine if it's conditional or not. I'll update this whole function to be more readable and correct.
created_at: 2025-03-30T19:15:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -294,28 +294,159 @@ which are both 0).
       std::mem_fn (*curFn)(this);
     }
 
-    // bclr is either a conventional branch or used as a return from a procedure
     if(current->op == power_op_bclr) {
       /*********************************************************************
-       *  Power ISA Version 3.1, Book I. May 2020. 2.4 Branch Instructions
+       *  Power ISA Version 3.1C, Book I. May 2024
+       *  2.4 Branch Instructions
+       *
+       *  bclr   BO,BI,BH (LK=0)
+       *  bclrl  BO,BI,BH (LK=1)
+       *
+       *  Extended form examples:
+       *    bclr  4,6
+       *    bltlr
+       *    bnelr cr2
+       *    bdnzlr
+       *
+       *
+       *  Programming Note (pg 38)
+       *  ------------------------
+       *
+       *  1. Do not use bclrl as a subroutine call
+       *
+       *  2. The conventional return idiom is to use `bclr` with BH=0b00
        *
-       *  The conventional return sequence is to use `bclr` (Branch
-       *  Conditional to Link Register) with BH=0b00.
        *********************************************************************/
-      auto target = makeRegisterExpression(ppc32::lr);
-      insn_in_progress->addSuccessor(std::move(target), false, true, bcIsConditional, false);
+      bool const LK = field<31, 31>(insn);
+
+      // link register is _always_ read, but conditionally written
+      {
+        auto link_register = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_read = true;
+        constexpr bool is_implicit = true;
+        bool const is_written = (LK == 1);
+        insn_in_progress->appendOperand(std::move(link_register), is_read, is_written, is_implicit);
+      }
+
+      if(LK == 1) {
+        // 1. Do not use bclrl as a subroutine call
+        constexpr bool is_call = false;
+
+        auto lr = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_indirect = true;
+        constexpr bool is_conditional = true;
+        constexpr bool is_fallthrough = false;
+        insn_in_progress->addSuccessor(std::move(lr), is_call, is_indirect, is_conditional, is_fallthrough);
+      }
+      else {
+        // 2. The conventional return sequence is to use `bclr` with BH=0b00
+        bool const is_subroutine_return = [this]() {
+          auto const bh_field = field<19,20>(insn);
+          return bh_field == 0;
+        }();
+
+        if(is_subroutine_return) {
+          // This is _not_ a fallthrough conditional
+          this->bcIsConditional = false;
+
+          // Indirectly branch through the link register
+          auto lr = makeRegisterExpression(ppc32::lr);
+          constexpr bool is_call = false;
+          constexpr bool is_indirect = true;
+          constexpr bool is_conditional = false;
+          constexpr bool is_fallthrough = false;
+          insn_in_progress->addSuccessor(lr, is_call, is_indirect, is_conditional, is_fallthrough);
+        }
+      }
 
       if(bcIsConditional) {
-        insn_in_progress->addSuccessor(makeFallThroughExpr(), false, false, false, true);
+        auto target = makeFallThroughExpr();
+        constexpr bool is_call = false;
+        constexpr bool is_indirect = true;
path: instructionAPI/src/InstructionDecoder-power.C
user: login: bbiiggppiigg
body: I thiink fallthrough targets are always direct snice they are literally the next instruction and does not need to go through the process of resolving indirect jump target.
created_at: 2025-03-31T00:46:59Z
author_association: MEMBER
diff_hunk: @@ -294,28 +294,159 @@ which are both 0).
       std::mem_fn (*curFn)(this);
     }
 
-    // bclr is either a conventional branch or used as a return from a procedure
     if(current->op == power_op_bclr) {
       /*********************************************************************
-       *  Power ISA Version 3.1, Book I. May 2020. 2.4 Branch Instructions
+       *  Power ISA Version 3.1C, Book I. May 2024
+       *  2.4 Branch Instructions
+       *
+       *  bclr   BO,BI,BH (LK=0)
+       *  bclrl  BO,BI,BH (LK=1)
+       *
+       *  Extended form examples:
+       *    bclr  4,6
+       *    bltlr
+       *    bnelr cr2
+       *    bdnzlr
+       *
+       *
+       *  Programming Note (pg 38)
+       *  ------------------------
+       *
+       *  1. Do not use bclrl as a subroutine call
+       *
+       *  2. The conventional return idiom is to use `bclr` with BH=0b00
        *
-       *  The conventional return sequence is to use `bclr` (Branch
-       *  Conditional to Link Register) with BH=0b00.
        *********************************************************************/
-      auto target = makeRegisterExpression(ppc32::lr);
-      insn_in_progress->addSuccessor(std::move(target), false, true, bcIsConditional, false);
+      bool const LK = field<31, 31>(insn);
+
+      // link register is _always_ read, but conditionally written
+      {
+        auto link_register = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_read = true;
+        constexpr bool is_implicit = true;
+        bool const is_written = (LK == 1);
+        insn_in_progress->appendOperand(std::move(link_register), is_read, is_written, is_implicit);
+      }
+
+      if(LK == 1) {
+        // 1. Do not use bclrl as a subroutine call
+        constexpr bool is_call = false;
+
+        auto lr = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_indirect = true;
+        constexpr bool is_conditional = true;
+        constexpr bool is_fallthrough = false;
+        insn_in_progress->addSuccessor(std::move(lr), is_call, is_indirect, is_conditional, is_fallthrough);
+      }
+      else {
+        // 2. The conventional return sequence is to use `bclr` with BH=0b00
+        bool const is_subroutine_return = [this]() {
+          auto const bh_field = field<19,20>(insn);
+          return bh_field == 0;
+        }();
+
+        if(is_subroutine_return) {
+          // This is _not_ a fallthrough conditional
+          this->bcIsConditional = false;
+
+          // Indirectly branch through the link register
+          auto lr = makeRegisterExpression(ppc32::lr);
+          constexpr bool is_call = false;
+          constexpr bool is_indirect = true;
+          constexpr bool is_conditional = false;
+          constexpr bool is_fallthrough = false;
+          insn_in_progress->addSuccessor(lr, is_call, is_indirect, is_conditional, is_fallthrough);
+        }
+      }
 
       if(bcIsConditional) {
-        insn_in_progress->addSuccessor(makeFallThroughExpr(), false, false, false, true);
+        auto target = makeFallThroughExpr();
+        constexpr bool is_call = false;
+        constexpr bool is_indirect = true;
path: instructionAPI/src/InstructionDecoder-power.C
user: login: hainest
body: My biggest problem is that `InstructionDecoder_power::makeFallThroughExpr` assumes that the fallthrough target can be addressed via the PC, but Power doesn't have one[1]. Since it's not direct, then I considered it indirect. But a re-read of the decode algorithms for conditional branch instructions doesn't actually indicate that. I'll change this back to being direct to make it consistent with the other architectures. As an aside, I plan to change the  CFT interface when updating to Capstone to make all of this more explicit.

[1] From the docs:
```
Obtaining the address of the next instruction:
Use the following form of Branch and Link.

    bcl 20,31,$+4
```
where `$` is an assembler directive.
created_at: 2025-03-31T04:56:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -309,91 +309,9 @@ bool IA_power::isReturnAddrSave(Address& retAddr) const
   return ret;
 }
 
-bool IA_power::isReturn(Dyninst::ParseAPI::Function * context, Dyninst::ParseAPI::Block* currBlk) const
+bool IA_power::isReturn(Dyninst::ParseAPI::Function *, Dyninst::ParseAPI::Block*) const
 {
-  /* Check for leaf node or lw - mflr - blr pattern */
path: parseAPI/src/IA_power.C
user: login: bbiiggppiigg
body: Is this pattern no longer being used in powerpc so we don't need to check it anymore?
created_at: 2025-04-01T12:59:08Z
author_association: MEMBER
diff_hunk: @@ -309,91 +309,9 @@ bool IA_power::isReturnAddrSave(Address& retAddr) const
   return ret;
 }
 
-bool IA_power::isReturn(Dyninst::ParseAPI::Function * context, Dyninst::ParseAPI::Block* currBlk) const
+bool IA_power::isReturn(Dyninst::ParseAPI::Function *, Dyninst::ParseAPI::Block*) const
 {
-  /* Check for leaf node or lw - mflr - blr pattern */
path: parseAPI/src/IA_power.C
user: login: hainest
body: Correct. This is from ancient POWER and hasn't been used since v2.0 (~Power7) which is the oldest we support.
created_at: 2025-04-01T13:01:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -294,28 +294,158 @@ which are both 0).
       std::mem_fn (*curFn)(this);
     }
 
-    // bclr is either a conventional branch or used as a return from a procedure
     if(current->op == power_op_bclr) {
       /*********************************************************************
-       *  Power ISA Version 3.1, Book I. May 2020. 2.4 Branch Instructions
+       *  Power ISA Version 3.1C, Book I. May 2024
+       *  2.4 Branch Instructions
+       *
+       *  bclr   BO,BI,BH (LK=0)
+       *  bclrl  BO,BI,BH (LK=1)
+       *
+       *  Extended form examples:
+       *    bclr  4,6
+       *    bltlr
+       *    bnelr cr2
+       *    bdnzlr
+       *
+       *
+       *  Programming Note (pg 38)
+       *  ------------------------
+       *
+       *  1. Do not use bclrl as a subroutine call
+       *
+       *  2. The conventional return idiom is to use `bclr` with BH=0b00
        *
-       *  The conventional return sequence is to use `bclr` (Branch
-       *  Conditional to Link Register) with BH=0b00.
        *********************************************************************/
-      auto target = makeRegisterExpression(ppc32::lr);
-      insn_in_progress->addSuccessor(std::move(target), false, true, bcIsConditional, false);
+      bool const LK = field<31, 31>(insn);
+
+      // link register is _always_ read, but conditionally written
+      {
+        auto link_register = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_read = true;
+        constexpr bool is_implicit = true;
+        bool const is_written = (LK == 1);
+        insn_in_progress->appendOperand(std::move(link_register), is_read, is_written, is_implicit);
+      }
+
+      if(LK == 1) {
+        // 1. Do not use bclrl as a subroutine call
+        constexpr bool is_call = false;
+
+        auto lr = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_indirect = true;
+        constexpr bool is_conditional = true;
+        constexpr bool is_fallthrough = false;
+        insn_in_progress->addSuccessor(std::move(lr), is_call, is_indirect, is_conditional, is_fallthrough);
+      }
+      else {
+        // 2. The conventional return sequence is to use `bclr` with BH=0b00
+        bool const is_subroutine_return = [this]() {
+          auto const bh_field = field<19,20>(insn);
+          return bh_field == 0;
+        }();
+
+        if(is_subroutine_return) {
+          // This is _not_ a fallthrough conditional
+          this->bcIsConditional = false;
+
+          // Indirectly branch through the link register
+          auto lr = makeRegisterExpression(ppc32::lr);
+          constexpr bool is_call = false;
+          constexpr bool is_indirect = true;
+          constexpr bool is_conditional = false;
+          constexpr bool is_fallthrough = false;
+          insn_in_progress->addSuccessor(lr, is_call, is_indirect, is_conditional, is_fallthrough);
+        }
+      }
 
       if(bcIsConditional) {
-        insn_in_progress->addSuccessor(makeFallThroughExpr(), false, false, false, true);
+        auto target = makeFallThroughExpr();
+        constexpr bool is_call = false;
+        constexpr bool is_indirect = false;
+        constexpr bool is_conditional = false;
+        constexpr bool is_fallthrough = true;
+        insn_in_progress->addSuccessor(std::move(target), is_call, is_indirect, is_conditional, is_fallthrough);
       }
     }
+
     if(current->op == power_op_bcctr) {
-      insn_in_progress->addSuccessor(makeRegisterExpression(ppc32::ctr), field<31, 31>(insn) == 1,
-                                     true, bcIsConditional, false);
-      if(bcIsConditional) {
-        insn_in_progress->addSuccessor(makeFallThroughExpr(), false, false, false, true);
+      /*********************************************************************
+       *  Power ISA Version 3.1C, Book I. May 2024
+       *  2.4 Branch Instructions
+       *
+       *    bcctr   BO,BI,BH (LK=0)
+       *    bcctrl  BO,BI,BH (LK=1)
+       *
+       *    Extended form examples:
+       *      blctr
+       *      bnectr cr0
+       *
+       *  Programming Note (pg 38)
+       *  ------------------------
+       *
+       *  1. Computed goto’s, case statements, etc.
+       *
+       *     Put the branch address in CTR and set LK=0 (BH=0b11, if appropriate)
+       *
+       *  2. Indirect subroutine linkage
+       *
+       *     Place the final target address in CTR and use bcctr (LK=0).
+       *
+       *     NOTE: This form is not meant to fall through (e.g., it's used in PLT
+       *     thunks), so usually also has BH=0b1z1zz as per Figure 2.5 to indicate
+       *     "branch always".
+       *
+       *  3. Function call
+       *
+       *     For direct calls, put the target address in CTR and use bcctrl (LK=1).
+       ****************************************************************************/
+      bool const LK = field<31, 31>(insn);
+
+      auto add_lr_operand = [this, LK](bool const is_read) {
+        auto link_register = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_implicit = true;
+        bool const is_written = (LK == 1);
+        insn_in_progress->appendOperand(link_register, is_read, is_written, is_implicit);
+      };
+
+      if(LK == 1) {
+        // Case 3 - Function call
+        auto ctr = makeRegisterExpression(ppc32::ctr);
+        constexpr bool is_indirect = true;
+        constexpr bool is_call = true;
+        bool const is_fallthrough = false;
path: instructionAPI/src/InstructionDecoder-power.C
user: login: bbiiggppiigg
body: Any reason why this is not constexpr like the others?
And just a thought, would it be more clear if we define is_indirect/is_call/is_fallthrough/is_conditoinal all as true the start of the function, and then do something like appendOperand(..., is_call, is_indirect, !is_conditiona, !is_fallthrough);
created_at: 2025-04-01T13:06:12Z
author_association: MEMBER
diff_hunk: @@ -294,28 +294,158 @@ which are both 0).
       std::mem_fn (*curFn)(this);
     }
 
-    // bclr is either a conventional branch or used as a return from a procedure
     if(current->op == power_op_bclr) {
       /*********************************************************************
-       *  Power ISA Version 3.1, Book I. May 2020. 2.4 Branch Instructions
+       *  Power ISA Version 3.1C, Book I. May 2024
+       *  2.4 Branch Instructions
+       *
+       *  bclr   BO,BI,BH (LK=0)
+       *  bclrl  BO,BI,BH (LK=1)
+       *
+       *  Extended form examples:
+       *    bclr  4,6
+       *    bltlr
+       *    bnelr cr2
+       *    bdnzlr
+       *
+       *
+       *  Programming Note (pg 38)
+       *  ------------------------
+       *
+       *  1. Do not use bclrl as a subroutine call
+       *
+       *  2. The conventional return idiom is to use `bclr` with BH=0b00
        *
-       *  The conventional return sequence is to use `bclr` (Branch
-       *  Conditional to Link Register) with BH=0b00.
        *********************************************************************/
-      auto target = makeRegisterExpression(ppc32::lr);
-      insn_in_progress->addSuccessor(std::move(target), false, true, bcIsConditional, false);
+      bool const LK = field<31, 31>(insn);
+
+      // link register is _always_ read, but conditionally written
+      {
+        auto link_register = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_read = true;
+        constexpr bool is_implicit = true;
+        bool const is_written = (LK == 1);
+        insn_in_progress->appendOperand(std::move(link_register), is_read, is_written, is_implicit);
+      }
+
+      if(LK == 1) {
+        // 1. Do not use bclrl as a subroutine call
+        constexpr bool is_call = false;
+
+        auto lr = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_indirect = true;
+        constexpr bool is_conditional = true;
+        constexpr bool is_fallthrough = false;
+        insn_in_progress->addSuccessor(std::move(lr), is_call, is_indirect, is_conditional, is_fallthrough);
+      }
+      else {
+        // 2. The conventional return sequence is to use `bclr` with BH=0b00
+        bool const is_subroutine_return = [this]() {
+          auto const bh_field = field<19,20>(insn);
+          return bh_field == 0;
+        }();
+
+        if(is_subroutine_return) {
+          // This is _not_ a fallthrough conditional
+          this->bcIsConditional = false;
+
+          // Indirectly branch through the link register
+          auto lr = makeRegisterExpression(ppc32::lr);
+          constexpr bool is_call = false;
+          constexpr bool is_indirect = true;
+          constexpr bool is_conditional = false;
+          constexpr bool is_fallthrough = false;
+          insn_in_progress->addSuccessor(lr, is_call, is_indirect, is_conditional, is_fallthrough);
+        }
+      }
 
       if(bcIsConditional) {
-        insn_in_progress->addSuccessor(makeFallThroughExpr(), false, false, false, true);
+        auto target = makeFallThroughExpr();
+        constexpr bool is_call = false;
+        constexpr bool is_indirect = false;
+        constexpr bool is_conditional = false;
+        constexpr bool is_fallthrough = true;
+        insn_in_progress->addSuccessor(std::move(target), is_call, is_indirect, is_conditional, is_fallthrough);
       }
     }
+
     if(current->op == power_op_bcctr) {
-      insn_in_progress->addSuccessor(makeRegisterExpression(ppc32::ctr), field<31, 31>(insn) == 1,
-                                     true, bcIsConditional, false);
-      if(bcIsConditional) {
-        insn_in_progress->addSuccessor(makeFallThroughExpr(), false, false, false, true);
+      /*********************************************************************
+       *  Power ISA Version 3.1C, Book I. May 2024
+       *  2.4 Branch Instructions
+       *
+       *    bcctr   BO,BI,BH (LK=0)
+       *    bcctrl  BO,BI,BH (LK=1)
+       *
+       *    Extended form examples:
+       *      blctr
+       *      bnectr cr0
+       *
+       *  Programming Note (pg 38)
+       *  ------------------------
+       *
+       *  1. Computed goto’s, case statements, etc.
+       *
+       *     Put the branch address in CTR and set LK=0 (BH=0b11, if appropriate)
+       *
+       *  2. Indirect subroutine linkage
+       *
+       *     Place the final target address in CTR and use bcctr (LK=0).
+       *
+       *     NOTE: This form is not meant to fall through (e.g., it's used in PLT
+       *     thunks), so usually also has BH=0b1z1zz as per Figure 2.5 to indicate
+       *     "branch always".
+       *
+       *  3. Function call
+       *
+       *     For direct calls, put the target address in CTR and use bcctrl (LK=1).
+       ****************************************************************************/
+      bool const LK = field<31, 31>(insn);
+
+      auto add_lr_operand = [this, LK](bool const is_read) {
+        auto link_register = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_implicit = true;
+        bool const is_written = (LK == 1);
+        insn_in_progress->appendOperand(link_register, is_read, is_written, is_implicit);
+      };
+
+      if(LK == 1) {
+        // Case 3 - Function call
+        auto ctr = makeRegisterExpression(ppc32::ctr);
+        constexpr bool is_indirect = true;
+        constexpr bool is_call = true;
+        bool const is_fallthrough = false;
path: instructionAPI/src/InstructionDecoder-power.C
user: login: hainest
body: > Any reason why this is not constexpr like the others?

Nope, that was just a mistake.

> And just a thought, would it be more clear if we define is_indirect/is_call/is_fallthrough/is_conditoinal all as true the start of the function, and then do something like appendOperand(..., is_call, is_indirect, !is_conditiona, !is_fallthrough);

Good idea! I should do that since I did that in the tests.
created_at: 2025-04-01T13:31:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -294,28 +294,158 @@ which are both 0).
       std::mem_fn (*curFn)(this);
     }
 
-    // bclr is either a conventional branch or used as a return from a procedure
     if(current->op == power_op_bclr) {
       /*********************************************************************
-       *  Power ISA Version 3.1, Book I. May 2020. 2.4 Branch Instructions
+       *  Power ISA Version 3.1C, Book I. May 2024
+       *  2.4 Branch Instructions
+       *
+       *  bclr   BO,BI,BH (LK=0)
+       *  bclrl  BO,BI,BH (LK=1)
+       *
+       *  Extended form examples:
+       *    bclr  4,6
+       *    bltlr
+       *    bnelr cr2
+       *    bdnzlr
+       *
+       *
+       *  Programming Note (pg 38)
+       *  ------------------------
+       *
+       *  1. Do not use bclrl as a subroutine call
+       *
+       *  2. The conventional return idiom is to use `bclr` with BH=0b00
        *
-       *  The conventional return sequence is to use `bclr` (Branch
-       *  Conditional to Link Register) with BH=0b00.
        *********************************************************************/
-      auto target = makeRegisterExpression(ppc32::lr);
-      insn_in_progress->addSuccessor(std::move(target), false, true, bcIsConditional, false);
+      bool const LK = field<31, 31>(insn);
+
+      // link register is _always_ read, but conditionally written
+      {
+        auto link_register = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_read = true;
+        constexpr bool is_implicit = true;
+        bool const is_written = (LK == 1);
+        insn_in_progress->appendOperand(std::move(link_register), is_read, is_written, is_implicit);
+      }
+
+      if(LK == 1) {
+        // 1. Do not use bclrl as a subroutine call
+        constexpr bool is_call = false;
+
+        auto lr = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_indirect = true;
+        constexpr bool is_conditional = true;
+        constexpr bool is_fallthrough = false;
+        insn_in_progress->addSuccessor(std::move(lr), is_call, is_indirect, is_conditional, is_fallthrough);
+      }
+      else {
+        // 2. The conventional return sequence is to use `bclr` with BH=0b00
+        bool const is_subroutine_return = [this]() {
+          auto const bh_field = field<19,20>(insn);
+          return bh_field == 0;
+        }();
+
+        if(is_subroutine_return) {
+          // This is _not_ a fallthrough conditional
+          this->bcIsConditional = false;
+
+          // Indirectly branch through the link register
+          auto lr = makeRegisterExpression(ppc32::lr);
+          constexpr bool is_call = false;
+          constexpr bool is_indirect = true;
+          constexpr bool is_conditional = false;
+          constexpr bool is_fallthrough = false;
+          insn_in_progress->addSuccessor(lr, is_call, is_indirect, is_conditional, is_fallthrough);
+        }
+      }
 
       if(bcIsConditional) {
-        insn_in_progress->addSuccessor(makeFallThroughExpr(), false, false, false, true);
+        auto target = makeFallThroughExpr();
+        constexpr bool is_call = false;
+        constexpr bool is_indirect = false;
+        constexpr bool is_conditional = false;
+        constexpr bool is_fallthrough = true;
+        insn_in_progress->addSuccessor(std::move(target), is_call, is_indirect, is_conditional, is_fallthrough);
       }
     }
+
     if(current->op == power_op_bcctr) {
-      insn_in_progress->addSuccessor(makeRegisterExpression(ppc32::ctr), field<31, 31>(insn) == 1,
-                                     true, bcIsConditional, false);
-      if(bcIsConditional) {
-        insn_in_progress->addSuccessor(makeFallThroughExpr(), false, false, false, true);
+      /*********************************************************************
+       *  Power ISA Version 3.1C, Book I. May 2024
+       *  2.4 Branch Instructions
+       *
+       *    bcctr   BO,BI,BH (LK=0)
+       *    bcctrl  BO,BI,BH (LK=1)
+       *
+       *    Extended form examples:
+       *      blctr
+       *      bnectr cr0
+       *
+       *  Programming Note (pg 38)
+       *  ------------------------
+       *
+       *  1. Computed goto’s, case statements, etc.
+       *
+       *     Put the branch address in CTR and set LK=0 (BH=0b11, if appropriate)
+       *
+       *  2. Indirect subroutine linkage
+       *
+       *     Place the final target address in CTR and use bcctr (LK=0).
+       *
+       *     NOTE: This form is not meant to fall through (e.g., it's used in PLT
+       *     thunks), so usually also has BH=0b1z1zz as per Figure 2.5 to indicate
+       *     "branch always".
+       *
+       *  3. Function call
+       *
+       *     For direct calls, put the target address in CTR and use bcctrl (LK=1).
+       ****************************************************************************/
+      bool const LK = field<31, 31>(insn);
+
+      auto add_lr_operand = [this, LK](bool const is_read) {
+        auto link_register = makeRegisterExpression(ppc32::lr);
+        constexpr bool is_implicit = true;
+        bool const is_written = (LK == 1);
+        insn_in_progress->appendOperand(link_register, is_read, is_written, is_implicit);
+      };
+
+      if(LK == 1) {
+        // Case 3 - Function call
+        auto ctr = makeRegisterExpression(ppc32::ctr);
+        constexpr bool is_indirect = true;
+        constexpr bool is_call = true;
+        bool const is_fallthrough = false;
path: instructionAPI/src/InstructionDecoder-power.C
user: login: hainest
body: Updated.
created_at: 2025-04-01T13:47:55Z
author_association: CONTRIBUTOR

PR comments:
user: login: hainest
created_at: 2025-04-01T12:15:28Z
body: With https://github.com/dyninst/testsuite/pull/256, this reduces the failures from ~99% to <1%. There are no regressions on 18 machines.

---

@bbiiggppiigg @kupsch @wxrdnx @rochauha Have you had any trouble running the testsuite on friday, tabbycat, and/or follis? I've found that about 10 tests run and then it just stops. The testsuite logs don't show anything abnormal.
author_association: CONTRIBUTOR
user: login: kupsch
created_at: 2025-04-01T14:59:00Z
body: @hainest I see the same test suite problem of it not running most of the tests.  18 tests run and PASS and then it stops.  It looks like ptrace is being restricted due to an update or a CSL configuration change. I found this in `stderr.log`:

```
Warning: your Linux system provides limited ptrace functionality as a security
measure. This measure prevents ProcControl and Dyninst from attaching to binaries.
To temporarily disable this measure (until a reboot), execute the following command:
        echo 0 > /proc/sys/kernel/yama/ptrace_scope
To permanently disable this measure, edit the file "/etc/sysctl.d/10-ptrace.conf"
and follow the directions in that file.
For more information, see https://wiki.ubuntu.com/SecurityTeam/Roadmap/KernelHardening
```

I'll open an issue with the CSL.
author_association: CONTRIBUTOR
user: login: hainest
created_at: 2025-04-02T17:38:33Z
body: Thanks for the feedback, all.

@kupsch I just tried to run the test suite on follis, but it still failed early. I'm going to go ahead and merge this since I've not seen any regressions on the other x86 machines.
author_association: CONTRIBUTOR
user: login: kupsch
created_at: 2025-04-02T19:14:37Z
body: @hainest, I too only see 18 tests run and PASSed. The ptrace setting was made, and I can attach to another process with gdb `gdb -p ...` so the setting seems to be active.  The ptrace failure message is no longer present in the testsuite logs, so something else must be going wrong.
author_association: CONTRIBUTOR
user: login: hainest
created_at: 2025-04-02T19:25:41Z
body: @kupsch Have you tried running without this commit?
author_association: CONTRIBUTOR
user: login: kupsch
created_at: 2025-04-02T19:54:28Z
body: WIth the previous commit (fb151...), 18 run with 2 failures (power_cft and power_decode).  I'll try going back further.
author_association: CONTRIBUTOR
user: login: kupsch
created_at: 2025-04-03T14:54:08Z
body: @hainest I tried instances of dyninst and the testsuite from many months ago, where there were 1552 PASSED tests and 0 FAILED on x86, and I still see 18 PASSED and 0 FAILED. I'll investigate further next week.
author_association: CONTRIBUTOR