)]}'
{
  "commit": "c34a67dc6e8d9d03db6f4292b37050d9f2b6b4b3",
  "tree": "a9919b54627815aede8557597b162a28638a7e18",
  "parents": [
    "7f91b3b906cb53204fd5a82d1424851b85a6c668"
  ],
  "author": {
    "name": "William S. Moses",
    "email": "gh@wsmoses.com",
    "time": "Fri Aug 07 23:51:40 2026 -0500"
  },
  "committer": {
    "name": "William S. Moses",
    "email": "gh@wsmoses.com",
    "time": "Sat Aug 08 23:22:50 2026 -0500"
  },
  "message": "Differentiate the CUDA memcpy family in forward mode\n\nNeither the driver API (cuMemcpy{HtoD,DtoH,DtoD}[Async][_v2], and the\nunified cuMemcpy[Async]) nor the runtime API (cudaMemcpy[Async]) had a\nderivative, so staging data through device memory failed outright with\n\"No forward mode derivative found\". A CUDA transfer behaves exactly like\na memcpy, except that the shadow copy has to go back through the CUDA API\nrather than becoming an llvm.memcpy, since at least one side generally\nlives in memory the host cannot address.\n\nvisitMemTransferCommon grows an optional emitter hook so the CUDA path\nreuses its type segmentation, activity handling and zeroing of inactive\nfloat sources, and only the emission of the copy itself differs. Type\nanalysis learns to propagate the pointee type between the two sides, that\na CUdeviceptr is a pointer despite being an integer, and to stop marking\ntrailing arguments as integers before the stream pointer that a CUDA\ntransfer carries where a memcpy carries only volatility.\n\nTwo things this uncovered:\n\n  - cuMemFree{,_v2,Async} and cudaFree{,Async,Host} were not recognized as\n    deallocations, so freeing a device allocation inside a differentiated\n    function reported a missing derivative immediately after the transfer\n    was handled. Recognizing them also required guarding the nocapture\n    attribute in getOrInsertCheckedFree, which is invalid on the integer a\n    CUdeviceptr is passed as.\n\n  - A frontend may reach a library through a renamed declaration -- Julia\n    names its lazily bound ccalls \"ejlstr$\u003cfunction\u003e$\u003clibrary\u003e\" and loads\n    those libraries RTLD_LOCAL -- so a plainly named helper declared next\n    to such a call is not reachable via dlsym and fails when the module is\n    JIT linked. getOrInsertPerCallingConv declares a helper under the\n    convention of the call it accompanies, which also fixes the\n    pre-existing memset emitted for a shadow allocation. That memset now\n    matches the ABI of the allocation it pairs with as well: cuMemsetD8\n    takes an unsigned int length where cuMemsetD8_v2 takes a size_t.\n\nReverse mode still reports a missing derivative rather than answering\nincorrectly; accumulating into device memory needs a copy that runs on the\ndevice, which cannot be emitted from the host.\n\nCo-Authored-By: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_01R6a8BiaAKpUTgP86mQ9ZKP\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "788c39d126f1c53e543d6cb7e66ff471e2736cce",
      "old_mode": 33188,
      "old_path": "enzyme/Enzyme/AdjointGenerator.h",
      "new_id": "d9a470e265c848a378377d81118d59146fe52ca8",
      "new_mode": 33188,
      "new_path": "enzyme/Enzyme/AdjointGenerator.h"
    },
    {
      "type": "modify",
      "old_id": "b9553cf20c7917a48efaee0864eb0692ae15190c",
      "old_mode": 33188,
      "old_path": "enzyme/Enzyme/CallDerivatives.cpp",
      "new_id": "a1b10cdac43abba8b727a03e1884f2c32d69effe",
      "new_mode": 33188,
      "new_path": "enzyme/Enzyme/CallDerivatives.cpp"
    },
    {
      "type": "modify",
      "old_id": "b5061871418d6b2117f48dbd176c4d5f14a12d82",
      "old_mode": 33188,
      "old_path": "enzyme/Enzyme/TypeAnalysis/TypeAnalysis.cpp",
      "new_id": "6800535f086cdd27a565d3dba23041ca43b80cf0",
      "new_mode": 33188,
      "new_path": "enzyme/Enzyme/TypeAnalysis/TypeAnalysis.cpp"
    },
    {
      "type": "modify",
      "old_id": "9103c59a3ed9f74b550e47154204d44d5bd56fb2",
      "old_mode": 33188,
      "old_path": "enzyme/Enzyme/TypeAnalysis/TypeAnalysis.h",
      "new_id": "4f5761e3bf0252bc155827a421165054f1ad0700",
      "new_mode": 33188,
      "new_path": "enzyme/Enzyme/TypeAnalysis/TypeAnalysis.h"
    },
    {
      "type": "modify",
      "old_id": "39148a439df6a76802cdf4ca9fda858aae626152",
      "old_mode": 33188,
      "old_path": "enzyme/Enzyme/Utils.cpp",
      "new_id": "ef88933414d1e25c8a17a059eb0764e4be2baf30",
      "new_mode": 33188,
      "new_path": "enzyme/Enzyme/Utils.cpp"
    },
    {
      "type": "modify",
      "old_id": "dd9a0f684867beb22d6ea4058f13dfc64c001c0f",
      "old_mode": 33188,
      "old_path": "enzyme/Enzyme/Utils.h",
      "new_id": "d0e95110b0fee81779b50aaa27af4c5593a31777",
      "new_mode": 33188,
      "new_path": "enzyme/Enzyme/Utils.h"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "261b1ef882e26d9bb24979e3dc7c9e82c3c4bab1",
      "new_mode": 33188,
      "new_path": "enzyme/test/Enzyme/ForwardMode/cuda/memcpy_driver.ll"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "0168fb78a08321695f7e0fc32c94fc64a73db890",
      "new_mode": 33188,
      "new_path": "enzyme/test/Enzyme/ForwardMode/cuda/memcpy_renamed.ll"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "e357781c1e4918847de0ac816dc39a977608ac99",
      "new_mode": 33188,
      "new_path": "enzyme/test/Enzyme/ForwardMode/cuda/memcpy_runtime.ll"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "e6eaa57ca9a1dbd9b0660202eb83e6f09024b760",
      "new_mode": 33188,
      "new_path": "enzyme/test/Integration/ForwardMode/cudamemcpy.cpp"
    }
  ]
}
