)]}'
{
  "commit": "84d16757719ca263cea4dc2df496223c0efa740b",
  "tree": "e6379b20eb0886dee9a0e05ac281d3dc29d6e584",
  "parents": [
    "625a5c311e6df32aa28214d017c65303bb5d6451"
  ],
  "author": {
    "name": "Matt Turner",
    "email": "mattst88@gmail.com",
    "time": "Fri Aug 14 15:27:12 2026 -0400"
  },
  "committer": {
    "name": "Sam James",
    "email": "sam@gentoo.org",
    "time": "Wed Aug 26 23:59:19 2026 +0100"
  },
  "message": "alpha: don\u0027t delete a caller\u0027s gp reload after a call\n\nelf64_alpha_relax_with_lituse turns\n\n    ldq  t12,lit(gp)          bsr  ra,callee+8\n    jsr  ra,(t12)       \u003d\u003d\u003e   unop\n    ldah gp,hi(ra)            unop\n    lda  gp,lo(gp)\n\nwhenever elf64_alpha_relax_opt_call returns a non-zero destination, i.e.\nwhen the callee starts with our gp (or is marked NOPV).  That justifies\nentering the callee past its own ldgp, since the ldgp would recompute the\ngp we already have, but it does not justify deleting the reload.  $gp is\ncaller-saved; the callee is under no obligation to return with it intact.\n\nA callee ending in a tail call\n\n    ldq  ra,0(sp)\n    lda  sp,N(sp)\n    ldq  t12,lit(gp)\n    jmp  zero,(t12)\n\nhands control to a function that runs its own ldgp and returns straight to\nour caller.  When the tail target is in a different sub-GOT the caller\nresumes with the wrong gp, and every later disp(gp) names a slot in the\nwrong sub-GOT.  The value loaded is a valid GOT entry belonging to some\nother reference, so nothing traps until it is used, typically an indirect\ncall landing in .data.rel.ro.\n\nThis needs a multi-sub-GOT link to be observable, which is why it went\nunnoticed.  It was found in libLLVM.so, where SelectionDAG::getConstant\ncalled a gp-sharing callee that tail-called into another sub-GOT and then\njumped through a slot holding a vtable pointer.\n\nRecovering the optimization needs proof that the callee returns with gp\nintact.  A callee containing no tail call, direct or indirect, always\ndoes, but establishing that requires scanning the target function here.\nUntil then, keep the reload.  It costs no code size: the two instructions\nare already emitted and were only being rewritten to unops.\n\nThe new test only needs a single sub-GOT: it checks that ld --relax still\nconverts the jsr to a bsr into foo+8 while leaving the ldah/lda pair\nalone.\n\n\t* elf64-alpha.c (elf64_alpha_relax_with_lituse): Don\u0027t turn the\n\tGPDISP ldah/lda pair into unops when the callee shares our gp.\n\nld/\n\t* testsuite/ld-alpha/relax-gpdisp.s: New test.\n\t* testsuite/ld-alpha/relax-gpdisp.d: New test.\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "07e145fc58631b9c793b1c11b5af4b7acc86e837",
      "old_mode": 33188,
      "old_path": "bfd/elf64-alpha.c",
      "new_id": "c43d427989ddb099aac37890baabc7d447774e5f",
      "new_mode": 33188,
      "new_path": "bfd/elf64-alpha.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "b4198171e842bcec06ae06c60fda34a9b26f3f75",
      "new_mode": 33188,
      "new_path": "ld/testsuite/ld-alpha/relax-gpdisp.d"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "f8faf542dde5cc1465b0a25cc1c6491370861676",
      "new_mode": 33188,
      "new_path": "ld/testsuite/ld-alpha/relax-gpdisp.s"
    }
  ]
}
