)]}'
{
  "log": [
    {
      "commit": "6a21ecc71f7fbced27455110c1c253ea28a161de",
      "tree": "b00ad05bb5dab10ea3becde1fd9e1fc09da23c05",
      "parents": [
        "272285f96c3f3cc191ccb70a4007d93c2678224a"
      ],
      "author": {
        "name": "Valentin Churavy",
        "email": "v.churavy@gmail.com",
        "time": "Mon Sep 07 12:40:36 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Sep 07 12:40:36 2026 +0200"
      },
      "message": "Compute AugmentWithJuliaObjectType offsets from the DataLayout (#3184)\n\n* Compute AugmentWithJuliaObjectType offsets from the DataLayout\n\nAugmentWithJuliaObjectType walked aggregate types to find the offsets of\ntheir Julia pointer fields, and got each offset by building a throwaway\nGEP and asking it to fold:\n\n    auto ud \u003d UndefValue::get(getUnqual(ST));\n    auto g2 \u003d GetElementPtrInst::Create(T, ud, vec);\n    APInt ai(DL.getIndexSizeInBits(g2-\u003egetPointerAddressSpace()), 0);\n    g2-\u003eaccumulateConstantOffset(DL, ai);\n    delete g2;\n\nThat allocates and frees an LLVM instruction (plus a constant vector and\nan undef operand) per struct field and per array type, for information\nthe DataLayout already has. It runs on every TypeAnalyzer::getAnalysis\ncall when -enzyme-julia-addr-load is set, so it is hot on Julia\nfrontends: a sampling profile of Enzyme differentiating an Oceananigans\nocean model charged 11s to GetElementPtrInst::Create and\naccumulateConstantOffset underneath it.\n\nUse the layout directly instead -- getStructLayout()-\u003egetElementOffset(i)\nfor structs, and getTypeAllocSize() as the stride for arrays, which is\nwhat a gep [0, 1] into the array computes.\n\nVerified on structs with padding, packed structs, nested structs, arrays\nof pointers, arrays of structs, and nested arrays: type analysis output\nis identical before and after, as is the lit suite (LLVM 16).\n\nAssisted-by: Claude Code (Opus 5)\n\n* Compute aggregate element offsets from the DataLayout everywhere\n\nFollow-up to the previous commit, which did this for\nAugmentWithJuliaObjectType only. Sixteen other sites built the same\nthrowaway GEP to recover a field offset the layout already knows:\n\n    auto ud \u003d UndefValue::get(getUnqual(T));\n    auto g2 \u003d GetElementPtrInst::Create(T, ud, vec);\n    APInt ai(DL.getIndexSizeInBits(g2-\u003egetPointerAddressSpace()), 0);\n    g2-\u003eaccumulateConstantOffset(DL, ai);\n    delete g2;\n\nEach one allocates and frees an LLVM instruction plus its constant index\nvector and undef operand, and most sit in a loop over every element of an\naggregate: constant aggregate and constant data sequential analysis,\nshufflevector, extractvalue, insertvalue, the struct-returning libm calls,\ndefaultTypeTreeForLLVM\u0027s struct/array/vector cases, the extractvalue and\ninsertvalue shadows in invertPointerM, and simplifyLoad of an extractvalue.\n\nAdd getAggregateElementOffset(DL, T, Idxs) to Utils.h, which walks the\nindices asking the layout directly -- getStructLayout()-\u003egetElementOffset()\nfor a struct field, and getTypeAllocSize() of the element as the stride for\nan array or vector index, which is what a gep applies to a sequential\nindex. Both single-index and multi-index (extract/insertvalue) callers use\nit, and no site allocates any more.\n\ninsertvalue also needed its \"offset of the next logical element\" walk\nrewritten to carry over unsigned indices rather than mutating a vector of\nConstantInts; carrying past the outermost index lands one past the\naggregate, which is what the old gep [1] over the aggregate computed.\n\nVerified against a build of the parent commit: type analysis output is\nidentical on padded, packed and nested structs, nested extractvalue and\ninsertvalue including carries out of the last element, shufflevector over\ndouble/float/ptr/i1 vectors, constant structs, arrays, vectors and vectors\nof i1, and struct-returning libm calls. The lit suite reports the same\n1077 failures before and after (all pre-existing typed-pointer spelling\ndrift on my local LLVM 22 config).\n\nAssisted-by: Claude Code (Opus 5)"
    },
    {
      "commit": "272285f96c3f3cc191ccb70a4007d93c2678224a",
      "tree": "727656f27f34d99db13faf4de79fde39d95f69cf",
      "parents": [
        "0c45114d4950015dbab7e4b7eb49018020a77182"
      ],
      "author": {
        "name": "Valentin Churavy",
        "email": "v.churavy@gmail.com",
        "time": "Fri Sep 04 22:34:11 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Sep 04 15:34:11 2026 -0500"
      },
      "message": "Make extractBLAS allocation-free (#3183)\n\nextractBLAS decided whether a name is a BLAS routine by generating every\nname it could recognise and comparing against it:\n\n    for (auto t : floatType)\n      for (auto f : extractable)\n        for (auto p : prefixes)\n          for (auto s : suffixes)\n            if (in \u003d\u003d (Twine(p) + t + f + s).str())\n\nThat is ~1250 heap-allocated std::strings per query, and the function runs\nfor every call instruction visited by type analysis and by\nis_use_directly_needed_in_reverse. On call-heavy modules that contain no\nBLAS at all it dominates compile time: a sampling profile of Enzyme\ndifferentiating an Oceananigans ocean model charged 1855s of a 2972s\ncompile -- 62% -- to extractBLAS, nearly all of it inside\nllvm::Twine::str().\n\nMatch instead by peeling the prefix, float-type character and suffix off the\nends of the name with consume_front/consume_back and looking up what is\nleft. No name the tables can spell decomposes in more than one way, so this\nis equivalent to the exhaustive comparison, but it allocates nothing.\n\nOn non-BLAS symbol names the query cost drops from 53.4us to 0.067us.\n\nAssisted-by: Claude Code (Opus 5)"
    },
    {
      "commit": "0c45114d4950015dbab7e4b7eb49018020a77182",
      "tree": "dfe6babed559670273b878c0007b26e8ba376218",
      "parents": [
        "3e7cc67727eadcdb6cc19bc24292a6fe7aed100c"
      ],
      "author": {
        "name": "Mark Abate",
        "email": "48031019+markabate@users.noreply.github.com",
        "time": "Thu Sep 03 03:34:24 2026 +0000"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Sep 03 03:34:24 2026 +0000"
      },
      "message": "Add clang enzyme_notypeanalysis and enzyme_ta_norecur attributes (#3072)\n\n* Add clang enzyme_notypeanalysis and enzyme_ta_norecur attributes\n\n* Combine enzyme_ta_norecur and enzyme_notypeanalysis clang attributes, add unit test\n\n* Only log updateAnalysis skip if enzyme-print-type is enabled\n\n* Fix enzyme_notypeanalysis logic"
    },
    {
      "commit": "3e7cc67727eadcdb6cc19bc24292a6fe7aed100c",
      "tree": "82d4d49756542d2ebe22ac0328e98c5ccdb9a3bd",
      "parents": [
        "4ef11d1d7efe3a44463b322238e0e842271e3042"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Aug 31 23:29:57 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 31 23:29:57 2026 -0500"
      },
      "message": "MLIR: give the affine atomic an ordering, and lower it to the enzyme one (#3179)\n\nenzyme.affine_atomic_rmw named no ordering, so the ordering an atomic\ncarried was dropped the moment its address became a map -- silently, since\nnothing in the op could hold it. Give it the same AtomicOrdering the\nnon-affine enzyme atomic has.\n\nIts lowering dropped the ordering too, by going to memref.atomic_rmw, which\nhas nowhere to put one. Lower to enzyme.atomic_rmw instead, which does, and\ncarry the alignment and fastmath across with it.\n\nThe same lowering also hardcoded addf for every atomic it lowered, so an\naddi or a maximumf became a float add. It now lowers the kind the op names.\n\nBoth derivatives of the op are ordered as the primal was: the adjoint\naccumulates where the primal accumulated, and the shadow does what the\nprimal does on the shadow buffer. The gradient accumulations for loads keep\nmonotonic, a load having no ordering to carry.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "4ef11d1d7efe3a44463b322238e0e842271e3042",
      "tree": "2f2dc68f753471251a21ad1d32d50cf1878f639c",
      "parents": [
        "277ecb5335a75883852c59436e358f82939dd10d"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Aug 31 22:46:48 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 31 22:46:48 2026 -0500"
      },
      "message": "Use GlobalVariable::getAlign, which replaced getAlignment (#3178)\n\nLLVM removed GlobalObject::getAlignment(); its replacement getAlign()\nreturns a MaybeAlign, which is already what the surrounding code wants:\nthe guards keep testing for a specified alignment and the uses take the\nAlign out of the optional instead of rebuilding one from an unsigned.\n\n\nClaude-Session: https://claude.ai/code/session_013AyMHG2YuT1uZgr3DY2aeQ\n\nCo-authored-by: William S. Moses \u003cmoses.williamsteven@gmail.com\u003e\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "277ecb5335a75883852c59436e358f82939dd10d",
      "tree": "fe405588d50f86bfbca66804369344eb4ea7e43a",
      "parents": [
        "e55a99620d2b48789132227be1f5e6984ee2b94a"
      ],
      "author": {
        "name": "Paul Berg",
        "email": "naydex.mc+github@gmail.com",
        "time": "Wed Aug 26 14:44:41 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 07:44:41 2026 -0500"
      },
      "message": "[mlir] Batch AD ops (#3175)\n\n* Fix batch cache key when in different function op interfaces\n\n* Batch enzyme AD ops\n\nbatch(diff(f)) -\u003e diff(batch(f))\n\n* add test"
    },
    {
      "commit": "e55a99620d2b48789132227be1f5e6984ee2b94a",
      "tree": "f05f6654cb4a9f37151376851202925e6d90c8d2",
      "parents": [
        "fc6bb335b90ef09c2c16b413a408cafcc836086b"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Tue Aug 25 21:13:50 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 21:13:50 2026 -0500"
      },
      "message": "Forward terminator handler: shadow only results the terminator forwards (#3173)\n\n* Forward terminator handler: shadow only results the terminator forwards\n\nThe parent may carry more results than its terminator has operands --\nenzymexla\u0027s gpu wrapper yields nothing for its token-like index result\n-- and the strict count asserts aborted debug builds differentiating\nsuch regions (Enzyme-JAX diffrules_enzymexla_gpu_wrapper_fwd under\n-c dbg), on both the region-branch-interface path and the fallback.\nPair up to the terminator\u0027s operand count; unpaired trailing results\nhave nothing to shadow.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* Forward-mode AD of gpu.launch, testing the unpaired-terminator fix\n\nThe launch operands and optional token result carry no tangents, so the\ntangent of a launch is the launch itself with a differentiated body. Its\ngpu.terminator has no operands at all, so an async launch is a region op\nwith more results than its terminator forwards: the new test dies on the\nold pairing assert and passes with the fix.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "fc6bb335b90ef09c2c16b413a408cafcc836086b",
      "tree": "ac7475c3c8893179d206c69552a1ecf7975048d7",
      "parents": [
        "44156f33ef7c6e2823f2948a4daa6b1bace28dbb"
      ],
      "author": {
        "name": "Copilot",
        "email": "198982749+Copilot@users.noreply.github.com",
        "time": "Tue Aug 25 13:10:53 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 13:10:53 2026 -0500"
      },
      "message": "Emit diagnostic instead of segfaulting when CreateReverseDiff/CreateForwardDiff returns null (#3172)\n\n* Initial plan\n\n* Add null checks for revFn/forwardFn in callReverseHandler/callForwardHandler\n\nCo-authored-by: wsmoses \u003c1260124+wsmoses@users.noreply.github.com\u003e\n\n* Fix null_revfn.mlir test: use arith.remf to trigger CreateReverseDiff null\n\nCo-authored-by: wsmoses \u003c1260124+wsmoses@users.noreply.github.com\u003e\n\n---------\n\nCo-authored-by: copilot-swe-agent[bot] \u003c198982749+Copilot@users.noreply.github.com\u003e\nCo-authored-by: wsmoses \u003c1260124+wsmoses@users.noreply.github.com\u003e"
    },
    {
      "commit": "44156f33ef7c6e2823f2948a4daa6b1bace28dbb",
      "tree": "828ea2f39932e9365344bec13f782a978d6c8ecc",
      "parents": [
        "cea1c8ef598cbdf82fa6f814b9d59a82c7da11f8"
      ],
      "author": {
        "name": "Copilot",
        "email": "198982749+Copilot@users.noreply.github.com",
        "time": "Tue Aug 25 11:07:48 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 11:07:48 2026 -0500"
      },
      "message": "Fix tanh derivative: use 1-tanh(x)^2 instead of 1/cosh(x)^2 for numerical stability (#2908)\n\nCo-authored-by: copilot-swe-agent[bot] \u003c198982749+Copilot@users.noreply.github.com\u003e"
    },
    {
      "commit": "cea1c8ef598cbdf82fa6f814b9d59a82c7da11f8",
      "tree": "0dc98ca83432b0ff450231c8855b764c08f6f712",
      "parents": [
        "c47f4c650a1d1ead283d35b7e9e431591f677b34"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Tue Aug 25 22:30:10 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 08:30:10 2026 -0500"
      },
      "message": "Drop RustDebugInfo.cpp from the bazel build (#3171)\n\n* Drop RustDebugInfo.cpp from the bazel build\n\nThe file was removed in #3157; the bazel srcs list still compiled it,\nbreaking downstream bazel builds (Enzyme-JAX CI).\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* buildifier\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "c47f4c650a1d1ead283d35b7e9e431591f677b34",
      "tree": "3d4ba10e2ebf02aee9b7169031374f406b1ab691",
      "parents": [
        "c445feb0b66287b1705a120ddc088ef629cac5e9"
      ],
      "author": {
        "name": "Mark Abate",
        "email": "48031019+markabate@users.noreply.github.com",
        "time": "Tue Aug 25 06:01:43 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 08:01:43 2026 -0500"
      },
      "message": "Remove use of deprecated llvm::PointerUnion::get (#3157)\n\n* Remove use of deprecated llvm::PointerUnion::get\n\nPointerUnion::get was removed in https://github.com/llvm/llvm-project/commit/bf88636fa3f4b1dabf86c60b04176f0ee2b3e887\nwith the recommendation to use llvm::cast instead. This change should be backwards compatible with older LLVM versions\nsince PointerUnion::get was just cast with an assert.\n\n* Remove RustDebugInfo\n\n* Remove the RustDebugInfo type-analysis tests\n\nThey exercise the debug-info-driven rust type analysis the previous\ncommit removed.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: William S. Moses \u003cgh@wsmoses.com\u003e\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "c445feb0b66287b1705a120ddc088ef629cac5e9",
      "tree": "365dc564f97e5c7ce3f7e04a1431a41a16d6ac0e",
      "parents": [
        "16d3db7389fe295234eed42a08e9ca4abd6bfee8"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Tue Aug 25 22:00:57 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 08:00:57 2026 -0500"
      },
      "message": "MLIR: never merge identical blocks in the region-inlining sweeps (#3166)\n\n* MLIR: never merge identical blocks in the region-inlining sweeps\n\nThe greedy driver\u0027s default aggressive region simplification merges\nstructurally identical blocks module-wide, including a host function\u0027s\ncold error tails that differ only in constants. LLVM cannot split such\nshared tails apart again, and its machine passes hoist the merged tail\u0027s\nsetup into the hot path, taxing every call. Run the drivers of\ninline-enzyme-regions and the enzyme postpasses at normal simplification\ninstead.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* Match CI clang-format; update inactive_arg to the unmerged block shape\n\nThe three drivers\u0027 setRegionSimplificationLevel calls fit one line under\nthe CI clang-format. inactive_arg previously asserted the very block\nmerge this change removes (both pushes funneled through one block with\nan argument); the pushes now stay in their own arms, which is the point.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* Drop explanatory comment\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "16d3db7389fe295234eed42a08e9ca4abd6bfee8",
      "tree": "4b659f629b00e7e34dc52ce0d95608976c6d6803",
      "parents": [
        "ee0d9551c47e8d4e43f2252f738dc6137e41c69c"
      ],
      "author": {
        "name": "Joe Wallwork",
        "email": "22053413+joewallwork@users.noreply.github.com",
        "time": "Tue Aug 25 08:26:31 2026 +0000"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 08:26:31 2026 +0000"
      },
      "message": "Add Fortran OpenMP tests (#3160)\n\n* Add initial OpenMP version of square test\n\n* Add version with optimisations\n\n* Add version with explicit interface\n\n* Check number of OMP threads\n\n* O2 and O3 don\u0027t currently work for Fortran+OpenMP\n\n* Hook up batch mode tests\n\n* Use activity descriptors in explicit interface test\n\n* apt install libomp-dev in Fortran CI"
    },
    {
      "commit": "ee0d9551c47e8d4e43f2252f738dc6137e41c69c",
      "tree": "7d22dcaf84d6303d40a384b71c600366e330b1d1",
      "parents": [
        "71f672bffd78591363ca03fbb75e32d751839bac"
      ],
      "author": {
        "name": "Joe Wallwork",
        "email": "22053413+joewallwork@users.noreply.github.com",
        "time": "Tue Aug 25 07:47:18 2026 +0000"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 07:47:18 2026 +0000"
      },
      "message": "Add workflow for Shell linting (#3144)\n\n* Heed shellcheck warnings\n\n* Add Shellcheck workflow\n\n---------\n\nCo-authored-by: Valentin Churavy \u003cv.churavy@gmail.com\u003e"
    },
    {
      "commit": "71f672bffd78591363ca03fbb75e32d751839bac",
      "tree": "0e8a8f454dfc8e5b2b6e9427ae14048851a6230c",
      "parents": [
        "3fdebd6e593f47dca5ac7586e3644fec552cfc44"
      ],
      "author": {
        "name": "Vimarsh Sathia",
        "email": "vimarsh.sathia@gmail.com",
        "time": "Tue Aug 18 20:25:03 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 19 09:25:03 2026 +0900"
      },
      "message": "Add MLIR derivatives for `math.exp2` and `math.log2` (#3155)"
    },
    {
      "commit": "3fdebd6e593f47dca5ac7586e3644fec552cfc44",
      "tree": "1c32ee9a4e2219522228bcde3b8777cf115f2cc1",
      "parents": [
        "25933271ffb8a475d66766e0c75929e81d4df432"
      ],
      "author": {
        "name": "Nicholson Koukpaizan",
        "email": "72402802+nkoukpaizan@users.noreply.github.com",
        "time": "Sun Aug 16 01:10:31 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Aug 16 14:10:31 2026 +0900"
      },
      "message": "Update LLVM 19 guards for trigonometric functions (#3153)\n\n* Update LLVM 19 guards for trigonometric functions.\n\n* tan intrinsic in InstructionDerivatives and tan19.ll test."
    },
    {
      "commit": "25933271ffb8a475d66766e0c75929e81d4df432",
      "tree": "de83c276dbc96a0c8e4002635c0f5de51714440d",
      "parents": [
        "33c495b1c04968d81e5e5705a5157fc064d1f885"
      ],
      "author": {
        "name": "Paul Berg",
        "email": "naydex.mc+github@gmail.com",
        "time": "Fri Aug 14 18:35:02 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 14 18:35:02 2026 +0200"
      },
      "message": "mlir: Use the checkpoint period for the outer trip count (#3151)\n\nhttps://github.com/EnzymeAD/Enzyme/pull/3092 wrongly used the checkpoint\nperiod as the inner trip count. This is unoptimal since then the outer\ncaches will be of size ceil(N / p) instead of p."
    },
    {
      "commit": "33c495b1c04968d81e5e5705a5157fc064d1f885",
      "tree": "3a0a38668dc553a17a541b0c573299c9bbbcf389",
      "parents": [
        "0338786b2c26f9aed9d1c9483a241b2284722d74"
      ],
      "author": {
        "name": "Paul Berg",
        "email": "naydex.mc+github@gmail.com",
        "time": "Thu Aug 13 06:43:10 2026 +0000"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 13 06:43:10 2026 +0000"
      },
      "message": "mlir: Allocate the $b$ checkpoints at once in binomial checkpointing (#3147)\n\n* mlir: Add batch allocation optimization in binomial checkpointing\n\n* fix unused"
    },
    {
      "commit": "0338786b2c26f9aed9d1c9483a241b2284722d74",
      "tree": "bdf0861300347257c5b4954a4b4867347b4b898b",
      "parents": [
        "af01140019db7228d9aa1dc09bda721c716dc2ac"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Tue Aug 11 13:56:54 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 11 13:56:54 2026 -0500"
      },
      "message": "PreserveNVVM: the primal of a custom rule is pinned in two places (#3149)"
    },
    {
      "commit": "af01140019db7228d9aa1dc09bda721c716dc2ac",
      "tree": "25be933c6a34c5f6fcfd517a07b92e95a6b86688",
      "parents": [
        "d101bb0d2c2d7adba126b7ab25b4ef36401a511c"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Tue Aug 11 11:41:01 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 11 11:41:01 2026 -0500"
      },
      "message": "PreserveNVVM: let a pipeline without custom rules drop their linkage (#3148)\n\nRegistering a custom derivative holds both halves external so the rule\ncan still be resolved when differentiation gets there. A pipeline that\ndoes not implement custom rules has nothing to resolve them with, and\nholding the functions in place only keeps dead code -- and, for a rule\nwhose derivative launches a kernel, that kernel -- alive.\n\nThe globals are consumed either way: left standing they are a definition\nof the same name in every translation unit that saw the declaration.\n\nDefault is unchanged; only a caller that asks opts out.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: William S. Moses \u003cmoses.williamsteven@gmail.com\u003e\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "d101bb0d2c2d7adba126b7ab25b4ef36401a511c",
      "tree": "69574228fea3d472bf0784bde0111aed7e414f22",
      "parents": [
        "7500f319a4c1cb799bc3688a1ef779b80af1cb17"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Tue Aug 11 00:13:16 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 11 00:13:16 2026 -0500"
      },
      "message": "MLIR: forward mode for llvm.extractvalue and llvm.insertvalue (#3141)"
    },
    {
      "commit": "7500f319a4c1cb799bc3688a1ef779b80af1cb17",
      "tree": "0072b2ae32d705f90ea5d55486194ed2ec4d55a2",
      "parents": [
        "c0a377ed3573cb1142ad36247ef8faa5ecd0b6e2"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Aug 10 23:21:05 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 10 23:21:05 2026 -0500"
      },
      "message": "MLIR: forward mode for affine.parallel (#3140)"
    },
    {
      "commit": "c0a377ed3573cb1142ad36247ef8faa5ecd0b6e2",
      "tree": "54aaa785fdd908529f3b672cb4bc895a4213c892",
      "parents": [
        "cd98af9e59424f2b5d3d51c0a99923aa57009d8e"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Aug 10 16:53:32 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 10 16:53:32 2026 -0500"
      },
      "message": "Add a sumMIT integration test for the Enzyme AD material tests (#3122)\n\n* Add a sumMIT integration test for the Enzyme AD material tests\n\nsumMIT (MIT-PSAAP-IV/sumMIT) gained Enzyme-based material consistency\ntests, which check Enzyme\u0027s forward and reverse derivatives of a\nmaterial\u0027s Constitutive routine against finite differences. Build and\nrun them here so changes to Enzyme are caught against that workload.\n\nThe script follows the existing gridkit/mfem pattern: it installs\nsumMIT\u0027s documented dependencies, builds pyre from source since it is\nnot packaged for Ubuntu, clones sumMIT at its branch tip, and generates\nthe toolchain file sumMIT\u0027s CMake expects with SUMMIT_ENZYME_LLD_PLUGIN\npointed at the freshly built LLDEnzyme. The Enzyme test drivers are\nEXCLUDE_FROM_ALL, so it asks ctest which ones are registered and builds\nonly those rather than all of sumMIT\u0027s tests.\n\nsumMIT is a private repository, so the clone needs SUMMIT_TOKEN. Forks\ndo not receive secrets, so the step is skipped rather than failed there.\nThe tests currently live on sumMIT\u0027s enzyme-ad branch; SUMMIT_BRANCH\nmoves to main once that lands.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_0153oy7Y659Q3AriKGbkUJpg\n\n* Fail the sumMIT job on a missing token instead of skipping it\n\nGating the test step on the secret being non-empty meant an absent or\nexpired SUMMIT_TOKEN turned the job green without running anything,\nwhich is indistinguishable from the tests passing. This was not\nhypothetical: the first run on this branch reported success while the\nstep was skipped, because the secret did not exist yet.\n\nSkip only for pull requests from forks, which genuinely cannot be given\nsecrets, and let every other trigger fail. Check the token in the script\ntoo, so the failure reads as an expired token rather than as git\nprompting for credentials.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_0153oy7Y659Q3AriKGbkUJpg\n\n* Clone sumMIT with its full history so its version detection works\n\nsummit_getVersion runs git describe --tags --long --always and requires\nthe result to look like v\u003cmajor\u003e.\u003cminor\u003e.\u003cmicro\u003e-\u003cn\u003e-g\u003csha\u003e. A shallow\nclone has no tags, so --always returns a bare sha, which exits zero and\ntherefore bypasses sumMIT\u0027s own fallback before failing its regex check:\n\n  CMake Error at .cmake/summit_init.cmake:114 (message):\n    Invalid version string: 3577d25\n\nFetching tags into a shallow clone does not help, since describe also\nneeds a tag reachable from HEAD. With full history the same commit\ndescribes as v2.2.0-47-g3577d25fb, which sumMIT accepts. This is the\nsame failure already fixed for the pyre clone.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_0153oy7Y659Q3AriKGbkUJpg\n\n---------\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "cd98af9e59424f2b5d3d51c0a99923aa57009d8e",
      "tree": "2bf0deee0e8b964d1802b1fe5e0e941a34a7f050",
      "parents": [
        "d06faa4202473123af32d8b2c5f46c54f7c7178e"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Aug 10 14:49:44 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 10 14:49:44 2026 -0500"
      },
      "message": "MLIR: one seam for the shadow of an inactive stored value (#3139)\n\nThe shadow of a stored value nothing differentiates is zero when the\ntype is immutable, and the primal value itself when it is mutable: the\nshadow memory\u0027s structural fields must read as the primal\u0027s. The memory\nidentity forward handler refused mutable such stores outright, which\nMFEM\u0027s enzyme compatibility test hit storing an inactive data pointer\ninto differentiated memory.\n\nEvery site of the convention -- the memory identity forward handler,\nthe shadow stores of llvm.store/memref.store/affine.store, and the\nmemcpy tangent\u0027s undifferentiated source -- now goes through\noputils::inactiveStoredValueShadow: zero for immutable values, the\nprimal (concatenated across the batch width) for mutable ones, warning\nthat if the given activity was wrong only runtime activity could catch\nit. When a runtime activity implementation exists (as EnzymeLLVM\u0027s\ndoes), it has a single seam, builder in hand, to take over.\n\nThe store shadow placements also stop conflating their reasons to do\nnothing: a type without autodiff semantics is a hard failure instead of\na silent skip -- createShadowValues now returns LogicalResult for that\n-- while an undifferentiated destination, or an immutable value whose\nshadow the reverse sweep\u0027s adjoint owns, are simply not their work.\n\nNOTE: the createShadowValues signature change needs the paired\nEnzyme-JAX update to its external models when bumping.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "d06faa4202473123af32d8b2c5f46c54f7c7178e",
      "tree": "fc1a0a738558fc0a5d2356314008709709370c42",
      "parents": [
        "7d8105c76001fa4e21f5e8bf1fa2c16043294f31"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Aug 10 11:40:40 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 10 11:40:40 2026 -0500"
      },
      "message": "MLIR: memcpy derivatives and shadow stores of pointers (#3132)\n\n* MLIR: one getBaseObject for gradient utils and alias queries\n\nThe alias utility kept its own base-object walk, which knew memref.cast\nbut not the other view-like ops (subviews, memory_space_cast) that\nGradientUtils\u0027 getBaseObject sees through. Move that walk into oputils\nand use it for both.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* MLIR: forward-mode tangent for llvm.intr.memcpy; comdat detach for func.func derivatives\n\nDifferentiating a CUDA kernel launch through the raising pipeline walks\nthe capture struct the launcher packs its arguments into: active data\npointers are stored into alloca\u0027d structs and the structs are memcpy\u0027d\ntogether. Forward mode had no rule for the copy and stopped at \u0027could\nnot compute the adjoint\u0027, as MFEM\u0027s dfem multikernel tests do.\n\nThe tangent of a memcpy is a memcpy of the shadows -- float bytes carry\ntheir tangents, pointer bytes their shadow pointers, one copy serves\nboth. A source nothing differentiates has no shadow to copy from:\nfloat-free copies (destination provably an alloca of a float-free type)\ntake the primal bytes so structural fields stay usable through the\nshadow object; otherwise the shadow is cleared, a zero tangent for\nwherever the floats sit.\n\nReverse mode still refuses: doing it right needs to know where in the\ncopied bytes the floats sit, which is type-analysis infrastructure\nEnzymeMLIR does not have yet.\n\nAlso: a func.func raised from an llvm.func still holds the primal\u0027s\ncomdat as a plain attribute, and the derivative clone inherits it. The\nderivative does not belong in the primal\u0027s deduplication group, and a\nfunc.func cannot express a comdat anyway; what remains is a dangling\nnested symbol reference that breaks symbol-use walks such as\ngpu-kernel-outlining\u0027s. detachFromPrimalDefinition drops it.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "7d8105c76001fa4e21f5e8bf1fa2c16043294f31",
      "tree": "1f5fa392afc7ad9bcf84cfd7e6a557bbc586cb4f",
      "parents": [
        "f94a37ee7268a137f58e450a21c9ca3122f5fcc4"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Aug 10 11:03:03 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 10 11:03:03 2026 -0500"
      },
      "message": "MLIR: shadow stores of mutable values in reverse mode (#3136)\n\nStoring an active pointer into memory has no float adjoint to\naccumulate; its derivative story is structural, like llvm.getelementptr:\nthe shadow memory must hold the shadow pointer at the same spot, so\nshadow loads traverse shadow structures. Previously nothing wrote the\nshadow slot -- llvm.store, memref.store, and affine.store all had the\nsame gap (the latter two as an explicit TODO) -- and\nreverse-differentiating code that round-trips a data pointer through a\nstruct, as the capture struct a CUDA kernel launch packs its arguments\ninto, read back a null shadow data pointer. A value nothing\ndifferentiates is its own shadow, keeping inactive fields readable\nthrough the shadow object.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "f94a37ee7268a137f58e450a21c9ca3122f5fcc4",
      "tree": "52c11989af0b81f90a11ffbd7d326f9713a67c87",
      "parents": [
        "01f39e772ffe550c4e232c3d4e7493e1b1fab1fa"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Aug 10 10:45:30 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 10 10:45:30 2026 -0500"
      },
      "message": "MLIR: derivatives for enzyme.affine_atomic_rmw (#3134)\n\nThe op is emitted as the derivative of affine loads under atomic-add\naccumulation, but appearing in a primal -- a kernel that itself\naccumulates with atomics, as MFEM\u0027s dfem hyperelasticity tests do --\nneither mode had a rule for it and differentiation stopped.\n\nReverse: the location\u0027s adjoint serves both reads. The added value takes\nit (d_v +\u003d dm[i], a plain load: the reverse sweep mirrors the forward\nstructure, so the barriers that ordered the primal\u0027s atomics order the\nshadow\u0027s), and it keeps flowing to whatever wrote the location before.\nA used result read the pre-add value, so its adjoint joins the\nlocation\u0027s, atomically for the same reason the primal add was atomic.\n\nForward: the tangent of the added value joins the shadow location\nthrough the same atomic add, whose result carries the tangent of the\nvalue the primal read out.\n\nNon-add kinds still refuse, now saying why.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "01f39e772ffe550c4e232c3d4e7493e1b1fab1fa",
      "tree": "1487e47792492fccc8093d93e03cc7c7d2e84c20",
      "parents": [
        "fa6e20ebd80f0e28219abaf174d95baf1513ce92"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Aug 10 10:22:41 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 10 10:22:41 2026 -0500"
      },
      "message": "MLIR: one getBaseObject for gradient utils and alias queries (#3133)\n\nThe alias utility kept its own base-object walk, which knew memref.cast\nbut not the other view-like ops (subviews, memory_space_cast) that\nGradientUtils\u0027 getBaseObject sees through. Move that walk into oputils\nand use it for both.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "fa6e20ebd80f0e28219abaf174d95baf1513ce92",
      "tree": "676fa97cfaf5dc41890210afe62cde585d0ad958",
      "parents": [
        "22c3d4211f64b32838a59f9f0903a5dbf3927f4a"
      ],
      "author": {
        "name": "Manuel Drehwald",
        "email": "git@manuel.drehwald.info",
        "time": "Mon Aug 10 01:15:45 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 10 05:15:45 2026 +0000"
      },
      "message": "Use hasTerminator() in freeCache (#3125)\n\nLLVM\u0027s BasicBlock::getTerminator() asserts hasTerminator() rather than\nreturning null, so the null-check idiom aborts with\n\n  BasicBlock.h: getTerminator:\n  `hasTerminator() \u0026\u0026 \"cannot get terminator of non-well-formed block\"\u0027\n\nwhenever freeCache runs on a reverse block that is still under construction and\nso legitimately has no terminator yet. Reached via forceAugmentedReturns -\u003e\ngetContext -\u003e getDynamicLoopLimit -\u003e createCacheForScope, differentiating a\nloop whose trip count cannot be derived statically.\n\nThis was the last remaining use of the old idiom; the other sites already go\nthrough the Utils.h hasTerminator() wrapper, which this now matches. The\npreceding size() check becomes redundant, since hasTerminator() already implies\na non-empty block.\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "22c3d4211f64b32838a59f9f0903a5dbf3927f4a",
      "tree": "1e01be8be38c18948fb508ac308aaf254987a032",
      "parents": [
        "e0d0dee107455041f8ab3c728c808ab6b6788d10"
      ],
      "author": {
        "name": "Manuel Drehwald",
        "email": "git@manuel.drehwald.info",
        "time": "Mon Aug 10 00:34:56 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 10 04:34:56 2026 +0000"
      },
      "message": "Use original function\u0027s LoopInfo in legalRecompute (#3124)\n\nGradientUtils::legalRecompute maps `origStart` and the load back into the\noriginal function via isOriginal, and the dominance check on the line just\nabove already uses OrigDT -- but the allInstructionsBetween call that follows\npassed CacheUtility\u0027s `LI`, which is LoopInfo over newFunc. Querying it with an\noldFunc block asserts in LoopInfoBase::verifyBlockNumberEpoch:\n\n  ParentPtr \u003d\u003d BBParent \u0026\u0026 \"loop info queried with block of other function\"\n\nAll ten other allInstructionsBetween call sites already pass *OrigLI.\n\nFound while differentiating a Rust integral kernel: an inner loop whose trip\ncount is loaded from memory the loop itself may clobber is uncacheable, so the\nload is recomputed, and that recompute reaches legalRecompute with a\nforward-mode builder.\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "e0d0dee107455041f8ab3c728c808ab6b6788d10",
      "tree": "024cd9224ec41e9bcf2d2a9734d300731ad59b3e",
      "parents": [
        "54e6d5c5d00843b4fe322df95dd0d20873c9a820"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sun Aug 09 18:10:32 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Aug 09 18:10:32 2026 -0500"
      },
      "message": "MLIR: honor enzyme_dupnoneed by eliding primal stores (#3121)\n\nA pointer argument marked enzyme_dupnoneed is the caller declaring it\nwill not use the primal contents. LLVM-Enzyme already exploits this;\nMLIR mode ignored it, so every tangent/adjoint function also recomputed\nand stored the full primal alongside the derivative.\n\n- MGradientUtils::getBaseObject peels ViewLikeOpInterface ops (which\n  covers downstream casts such as enzymexla.pointer2memref) and LLVM\n  GEP/bitcast/addrspacecast to find the underlying object;\n  getDiffeTypeOfBase looks up the activity the caller declared for it;\n  mayReadBase (cached) walks the function for any op that may read it.\n  primalStoreElidable \u003d declared DUP_NONEED and never read back.\n- Forward mode: memoryIdentityForwardHandler (llvm/memref/affine store)\n  erases the primal store when every written pointer is elidable;\n  llvm.intr.memset likewise keeps only the shadow clear.\n- Reverse mode: the store handlers of all three dialects skip the\n  augmented-forward primal store the same way; the adjoint reads only\n  the shadow, so nothing needs the value.\n- Calls: the forward and reverse call handlers forward DUP_NONEED to\n  the subfunction differentiation when the passed pointer\u0027s base was\n  declared unneeded, so callees can elide their own stores; call sites\n  still pass primal+shadow (dupnoneed changes what may be skipped, not\n  the signature).\n\nFixes #3120\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "54e6d5c5d00843b4fe322df95dd0d20873c9a820",
      "tree": "3504e7cf6cf9d182f96fd8636cc0a90017cacada",
      "parents": [
        "1b8c5bfa9a8396aeebf61b7b887180c1a392b25e"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sun Aug 09 17:09:17 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Aug 09 17:09:17 2026 -0500"
      },
      "message": "Support LLVM 24\u0027s BranchInst split via branch helpers, keeping every earlier major (#3119)\n\n* Support LLVM 24\u0027s BranchInst split via branch helpers, keeping every earlier major\n\nLLVM 24 split BranchInst into CondBrInst and UncondBrInst (and finished the\nptrtoaddr migration: getLosslessPtrToIntExpr is now getPtrToAddrExpr, and\ninstruction creation takes iterator insert positions).\n\nUtils.h gains free helpers that ask the questions Enzyme asks of a branch\nin one spelling for every supported LLVM: isAnyBranch, isConditionalBranch,\nisUnconditionalBranch, getBranchCondition, setBranchCondition, and\ncreateUnconditionalBranch, each version-guarded internally. Successors need\nno helper -- Instruction::getSuccessor and getNumSuccessors say them\ngenerically on both sides of the split. Call sites hold plain Instruction\npointers; the InstVisitor hooks and the two renamed APIs carry explicit\nguards. enzyme-mlir CI moves its llvm-project pin to 084f6484, the same\ncommit XLA pins.\n\nBuilt against LLVM 084f6484 (post-split); the pre-24 helper branches spell\nexactly the previous code.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* check_llvm_api: a receiver may not reach across an argument list\n\nThe terminator-null-test receiver pattern allowed bare parentheses, so it\nmatched from inside an enclosing call\u0027s arguments and flagged argument uses\nlike isAnyBranch(foo-\u003egetTerminator()) as null tests. A receiver is now a\nchain of segments -- BB-\u003e, blocks[0]-\u003e, foo()-\u003e, A::B. -- which cannot\nabsorb an unmatched open paren. The six documented wrong forms still flag;\nargument and insert-point uses do not.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* createUnconditionalBranch: name InsertPosition only where it exists\n\nllvm::InsertPosition arrives later than some supported majors, so the\npre-24 helper templates over the insert-before argument and lets that\nLLVM\u0027s BranchInst::Create say what it accepts.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* Iterate branch successors by index; successors() postdates some majors\n\nInstruction::successors() is not there on LLVM 15; getNumSuccessors and\ngetSuccessor are, on every supported major and both sides of the 24 split.\nVerified locally against LLVM 15, 21, and 24.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "1b8c5bfa9a8396aeebf61b7b887180c1a392b25e",
      "tree": "0443edc9be3b615c5b7601b4b34c93446215144e",
      "parents": [
        "3587b69039b76cd87b32445f87d613dfd02cd930"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sun Aug 09 12:54:37 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Aug 09 12:54:37 2026 -0500"
      },
      "message": "MLIR: differentiate through debug intrinsics (#3117)\n\n* MLIR: differentiate through debug intrinsics\n\nCompiling with -g stopped both modes at llvm.intr.dbg.value with \"could\nnot compute the adjoint\". A debug intrinsic narrates the primal -- it names\nwhich source variable a value stands for and computes nothing -- so the\nprimal copy keeps saying it and the derivative has nothing to add.\n\nDeclare dbg.value/dbg.declare/dbg.label inactive (so the reverse pass skips\nthem) and give them a no-op forward tangent interface: like the lifetime\nmarkers, inactivity alone answers \"what does it make active\", not \"what is\nits tangent\", and an op whose operand is active is still asked for one.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* Own the reverse answer for debug intrinsics too\n\nThe activity tables say these ops are inactive, but the dataflow analyzer\ndoes not consult ActivityOpInterface when deciding what to skip (a TODO in\nDenseForwardActivityAnalysis), so under enzyme{dataflow} the reverse pass\nstill asked dbg.value for an adjoint. Attach a no-op\nReverseAutoDiffOpInterface so the op answers for itself in both modes,\nregardless of which analyzer ran.\n\nVerified end to end: the reported __enzyme_autodiff(square, x) C sample\ncompiles with -O2 -g through the Reactant plugin (dataflow activity),\nprints dsquare(x) \u003d\u003d 2x exactly, and keeps its debug info.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "3587b69039b76cd87b32445f87d613dfd02cd930",
      "tree": "79744b91079d7b98d123d86d311b85444f8e8942",
      "parents": [
        "ab0bd5b88e8c0d632a90ac68edb7225b3fdfaa11"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sun Aug 09 12:54:28 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Aug 09 12:54:28 2026 -0500"
      },
      "message": "ReverseRetOpt: bail on ops whose outputs do not line up with their activities (#3118)\n\nenzyme.autodiff has no verifier relating its result count to its activity\nattributes, so a canonicalization pattern can meet an op whose ranges do\nnot line up -- the shape a __enzyme_autodiff call raises to when the\nreturn is mis-said as enzyme_active rather than enzyme_activenoneed\nsegfaulted ReverseRetOpt walking getOutputs()/getInputs() past their\nends, on as little as differentiating square(x) \u003d x*x. Bounds-check the\nindexed reads and return failure, leaving the op for the AD pass to\ndiagnose.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "ab0bd5b88e8c0d632a90ac68edb7225b3fdfaa11",
      "tree": "b8a2909c6f69210494cec3c33de0ab9d79bcd1b3",
      "parents": [
        "7f91b3b906cb53204fd5a82d1424851b85a6c668"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sun Aug 09 01:36:31 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Aug 09 01:36:31 2026 -0500"
      },
      "message": "MLIR: carry access alignment into memref/LLVM/affine load-store adjoints (#3115)\n\n* MLIR: carry access alignment into memref/LLVM load-store adjoints\n\nThe reverse-mode adjoints for memref.load/store and llvm.load/store build\nshadow load/store ops from the primal but did not forward the primal\u0027s\nalignment attribute, so the shadow buffer was accessed at the element\u0027s\nnatural alignment. An over-aligned primal (e.g. a vectorized access) then\nhas an under-aligned adjoint -- the same defect class as Enzyme-JAX #2817,\nwhere a dropped alloca alignment faulted an aligned SSE access.\n\nThe memref atomic path already forwarded loadOp.getAlignmentAttr(); the\nnon-atomic load/store accumulation and the store adjoint\u0027s load+zero now do\ntoo, and the LLVM load/store adjoints forward getAlignment().\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* MLIR: carry alignment into the affine load-store adjoints too\n\nThe affine reverse-mode adjoints have the same non-atomic gap: the atomic\npath already forwarded the discardable \"alignment\" attribute the raising\npath records, but the non-atomic load/store accumulation and the store\nadjoint\u0027s load+zero dropped it. Forward it onto the reverse memref ops\n(setAlignmentAttr) and, when the reverse stays affine, re-attach the\ndiscardable attribute for LowerAlignedAffineAccesses to promote later.\n\nTest: affine_load_store_alignment.mlir differentiates an {alignment \u003d 16}\naffine load/store pair and checks the shadow accesses keep alignment \u003d 16.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "7f91b3b906cb53204fd5a82d1424851b85a6c668",
      "tree": "7b60d27803c6b655d7ff57ecc18cdbf9bf114f0d",
      "parents": [
        "26050c49b59454447456e3756398ef6b6691137b"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sat Aug 08 23:21:49 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 08 23:21:49 2026 -0500"
      },
      "message": "Recognize the CUDA deallocations (#3112)\n\nEnzyme already gives a CUDA allocation a shadow -- cuMemAlloc, cudaMalloc\nand friends are handled in handleKnownCallDerivatives -- but the frees that\nrelease one were never added to isDeallocationFunction. Differentiating a\nfunction that frees device memory therefore reported a missing derivative,\nand the shadow allocation was leaked.\n\nAdding cuMemFree{,_v2,Async} and cudaFree{,Async,Host} lets the existing\ndeallocation path handle them, which frees the shadow alongside the primal\nthrough the checked-free wrapper. Two adjustments were needed for the\ndriver API, whose CUdeviceptr is an integer rather than a pointer:\n\n  - getOrInsertCheckedFree put nocapture on the wrapper\u0027s parameters, which\n    is invalid on a non-pointer and produced a module that failed the\n    verifier.\n\n  - Type analysis marks integer arguments of a deallocation as Integer.\n    That is right for a size or a flag, but the first argument is the\n    allocation being freed, so for these it is a pointer.\n\n\nClaude-Session: https://claude.ai/code/session_01R6a8BiaAKpUTgP86mQ9ZKP\n\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "26050c49b59454447456e3756398ef6b6691137b",
      "tree": "5c829d8cb8179be38f2d38b3a87b44b154c19559",
      "parents": [
        "6ff84befc50327fc633aa201996ed3a3e351ba34"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sat Aug 08 21:27:23 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 08 21:27:23 2026 -0500"
      },
      "message": "Declare helper entry points under the caller\u0027s naming convention (#3111)\n\nEnzyme sometimes needs a second entry point from a library it is already\ncalling: the deriv/size variants next to a GSL Legendre evaluation, the\nMPI_Op_create behind an MPI reduction, the free that undoes an allocation\nor the stream destroy that undoes a stream create.\n\nA frontend does not have to reach that library by its plain symbol name.\nJulia names its lazily bound ccalls \"ejlstr$\u003cfunction\u003e$\u003clibrary\u003e\" and loads\nthose libraries RTLD_LOCAL, so a plainly named declaration is not reachable\nvia dlsym and fails when the module is JIT linked; the MPI profiling\ninterface likewise expects PMPI_ callers to keep calling PMPI_ entry\npoints. Utils already had getRenamedPerCallingConv for this, and the MPI\nand BLAS derivative paths use it, but several other sites still declared\ntheir helper with a plain name.\n\ngetOrInsertPerCallingConv wraps the pattern those paths were open coding --\nrename, then record the plain name in enzyme_math so the declaration is\nstill recognized afterwards -- and is applied to the sites that were\nmissing it:\n\n  - gsl_sf_legendre_deriv_array_e and gsl_sf_legendre_array_n, emitted\n    alongside gsl_sf_legendre_array_e.\n  - MPI_Op_create, emitted for the sum operation of a reduction.\n    getOrInsertOpFloatSum now takes the MPI entry point it is being built\n    for so it can name it to match.\n  - cuStreamDestroy, emitted to undo a cuStreamCreate.\n  - The memsets that zero a freshly allocated shadow buffer, and the frees\n    that release it, for the whole cuMemAlloc/cudaMalloc family. Those\n    memsets now also match the ABI of the allocation they pair with:\n    cuMemsetD8 takes an unsigned int length where cuMemsetD8_v2 takes a\n    size_t.\n\nSites that declare something other than a library entry point are left\nalone: LLVM and Julia intrinsics, Enzyme\u0027s own runtime helpers, and libc,\nnone of which are renamed by a frontend. The Rust and Swift deallocations\nhave the same shape but no convention currently applies to them, so they\nare also left as they are.\n\n\nClaude-Session: https://claude.ai/code/session_01R6a8BiaAKpUTgP86mQ9ZKP\n\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "6ff84befc50327fc633aa201996ed3a3e351ba34",
      "tree": "297e4985d8deed36dda4a1d0c477021f7df088f9",
      "parents": [
        "a33f4ea48ba1ddddb350aef1de587b3c365848a9"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sat Aug 08 17:43:03 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 08 17:43:03 2026 -0500"
      },
      "message": "Utils: read argument-memory effects in isReadOnly/isWriteOnly (#3103)\n\n* Utils: read argument-memory effects in isReadOnly/isWriteOnly\n\nLater LLVM folds readonly/readnone into the memory(...) attribute, and\nits per-location effects can say argument memory is only read (or only\nwritten) even when the function touches other memory. isReadOnly and\nisWriteOnly consulted only the whole-function queries and parameter\nattributes, so a call marked memory(argmem: read, inaccessiblemem:\nreadwrite) still counted as writing its pointer arguments -- and an\ninactive observer of active memory demanded a derivative it cannot\nhave. Consult the ArgMem row for the per-argument questions, using the\ncallee\u0027s effects only under a matching calling convention, as the\nattribute path already does.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: argument bytes must be write-free through Other too\n\nmemory(argmem: read) only constrains accesses the callee makes through\nits argument pointers. The same bytes can still be written through an\naccess classified as Other -- a captured pointer, a global alias -- so\nisReadOnly/isWriteOnly now require the ArgMem and Other locations to\nboth be write-free (read-free). InaccessibleMem stays exempt: memory\nthat is inaccessible from outside the callee cannot alias an argument.\n\nThe argmemro test grows the negative case: an observer declared\nmemory(readwrite, argmem: read) forces the loaded value to be cached in\nthe forward pass instead of recomputed in the reverse. Its CHECK lines\nare also rewritten spelling-agnostically -- LLVM 16 under\n-opaque-pointers\u003d0 prints \u0027double* nocapture readonly\u0027 where newer LLVM\nprints \u0027ptr readonly captures(none)\u0027, which is what failed the LLVM 16\nCI job.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "a33f4ea48ba1ddddb350aef1de587b3c365848a9",
      "tree": "c6adaf6c61d438aadea19390eb64553b903428ab",
      "parents": [
        "22af85afbbdf434be1dd5c5bd30b6fc9a42f762e"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sat Aug 08 15:05:29 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 08 15:05:29 2026 -0500"
      },
      "message": "Differentiate an llvm.call (#3079)\n\n* Differentiate an llvm.call\n\nDifferentiating a call is the same work whichever dialect spelled it, but\nonly func.call had the models for it, so an llvm.call stopped at \"could\nnot compute the adjoint for this operation\". MFEM\u0027s unit tests are all\nllvm.call and get through on the ones activity analysis rules out; the\nfirst that mattered stopped the build.\n\nWhat differs between the dialects is how a call names its callee and how a\ncall to the derivative is written, and both are already asked through\ninterfaces -- CallOpInterface for the first and\nAutoDiffFunctionInterface::createCall for the second. So the models move\nto CallAutoDiffImplementations, written once over the call op type, and\nare attached from func and from LLVM.\n\nThree things that only this path reaches:\n\n  * The reverse pass keeps a copy of a pointer argument, and could only\n    size one that llvm_ext.alloc made or that something annotated. An\n    alloca says how much it is -- the size of the element type, that many\n    times -- so it no longer has to be told.\n\n  * llvm_ext is what that copy is written in, and the pass never said it\n    builds ops of it. A pipeline that raised through llvm_ext has it\n    loaded already, which is why only running the pass on its own noticed.\n\n  * insertInit and getCacheType read a location and a context out of the\n    initialization block, which reads off the end while it is still empty.\n    The location can be unknown and the context is the type\u0027s own.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: register call handlers from tablegen\n\nA CallOp record in the derivatives tablegen now attaches the generic\nforward and reverse call handlers, like BranchOp and ReturnOp do for\ntheirs; llvm.call is declared with one line in LLVMDerivatives.td\ninstead of hand-registered.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: func.call through the tablegen CallOp record too\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: fold the call handlers into the core files\n\nNo separate CallAutoDiffImplementations.{h,cpp}: the declarations and\nexternal models live with the other dialect-independent handlers in\nCoreDialectsAutoDiffImplementations, and the definitions with theirs.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: inline createCallToFunction, rename volatile_args to overwritten_args\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: getDirectCallee as a static fn, diagnostics returned directly\n\nAn InFlightDiagnostic already converts to failure; return it instead of\npairing every emitError with a return failure().\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: cache the mutable shadows beside their primals\n\nA mutable shadow is a value of the forward pass -- the shadow of a\npointer derived or allocated inside a loop body -- and the reverse pass\ncannot always rebuild or even reference it. The augmented forward puts\nit by beside the primal copy; the reverse call pops both. The shadow\nbuffer itself is shared, so it is the pointer that is cached, not a\ncopy. The new test reverses a call on a loop-local alloca, whose\nper-iteration shadow the reversed loop reads back at the reversed\nindex.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: refuse a reverse call whose callee touches foreign memory\n\nThe reverse call runs long after the forward one, against whatever\nmemory looks like by then; the only state carried across is the cached\nargument values and shadows. Accept a callee that is readnone, or whose\nbody ops are free of memory effects, or whose loads and stores go\nthrough pointers derived from its own arguments -- exactly the state\nthe caches preserve -- and refuse the rest loudly.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: refuse any memory-touching callee in split reverse\n\nEven argument memory may be overwritten between the forward call and the\nreverse one; deciding which of it was is the overwritten-args analysis\nthe LLVM side has and this side does not yet. Until it does, only a\nreadnone callee -- or one whose body is free of memory effects -- is\ndifferentiable in reverse. The positive test differentiates a pure\ncallee; the alloca and global variants pin the refusal.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: read the memory attribute on the call and the callee\n\nLater LLVM spells readnone as a memory-effects attribute, on the\nfunction and sometimes on the call site alone; either saying none for\nevery location clears the callee.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: split the alloca-extent sizing out; cache nothing for a refused callee\n\nThe alloca-extent lookup in findPtrExtent is its own change and moves to\nits own PR. With memory-touching callees refused, nothing in this PR\nneeds it: the cache step now consults the same predicate as the adjoint\nand puts nothing by for a callee that will be refused, instead of\ncloning arguments the refusal is about.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* clang-format\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* merge fixup: refuse a bodyless callee at the call site\n\nThe folded call handlers replace the func-dialect ones #3104 amended on\nmain, so they must say what those said: the call-site error for a callee\nwith no body and no registered derivative, in both modes. Without it the\nfunction-level guard fires instead, with a different message at a\ndifferent location, and the merged unknown_call tests fail.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: ask the callee for a nested call\u0027s memory effects\n\nisMemoryEffectFree answers conservatively for a call op -- its effects\nare its callee\u0027s, which the flat walk never looked at, so any callee\ncontaining another call was refused even when everything reachable is\npure arithmetic (multiret2), including a function calling itself\n(recursion). Walk into direct callees instead, with a visited set: a\ncycle contributes no effects beyond those already under check.\nfunc_call_write stays refused; it really does store.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: XFAIL func_call_write until overwritten-args caching lands\n\nThe callee stores through its dup memref argument -- the case the\nreverse-mode memory guard exists for. Tracked in #3109.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "22af85afbbdf434be1dd5c5bd30b6fc9a42f762e",
      "tree": "dbc7ba91b0063941815e3436c59b05c5624709f6",
      "parents": [
        "db618fa4e8fa8170ff3db4d8517eeb0bc6e937f1"
      ],
      "author": {
        "name": "Valentin Churavy",
        "email": "v.churavy@gmail.com",
        "time": "Sat Aug 08 22:05:21 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 08 15:05:21 2026 -0500"
      },
      "message": "Route getFirstNonPHI through a Utils.h wrapper (#3081)\n\n* Route getFirstNonPHI through a Utils.h wrapper\n\nLLVM removed the deprecated BasicBlock::getFirstNonPHI (llvm-project\n62c5ede9fd14), leaving only the iterator-flavoured getFirstNonPHIIt.\nAdd a getFirstNonPHI(BasicBlock*) shim alongside the existing\ngetFirstNonPHIOrDbg wrappers and move the 19 call sites onto it. The\niterator overload has been available since LLVM 18, so older LLVMs keep\nthe old spelling.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n\n* Lint member calls to the getFirstNonPHI family\n\nPer review: the wrapper only helps if nothing bypasses it, so teach\ncheck_llvm_api.py (the \u0027Direct use of wrapped LLVM APIs\u0027 CI job) to flag\nmember-call spellings of getFirstNonPHI, getFirstNonPHIIt and\ngetFirstNonPHIOrDbg -- no one of them exists on every supported LLVM.\nThe free-function wrappers do not match, getFirstNonPHIOrDbgOrLifetime\nis deliberately not matched, and the wrapper bodies carry the usual\nNOLINTNEXTLINE.\n\nThe new check immediately caught one stray the conversion missed:\nCacheUtility.cpp\u0027s SetInsertPoint(Header-\u003egetFirstNonPHIOrDbg()), now\nrouted through the wrapper too.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\nCo-authored-by: William S. Moses \u003cgh@wsmoses.com\u003e"
    },
    {
      "commit": "db618fa4e8fa8170ff3db4d8517eeb0bc6e937f1",
      "tree": "f08ccc8a2f78770ab49dd7c863b8866cfbeae59f",
      "parents": [
        "124dc4984cad504abf1b567930f7a01a521b6dae"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sat Aug 08 14:25:05 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 08 14:25:05 2026 -0500"
      },
      "message": "Size a cloned or zeroed alloca from its type (#3108)\n\nAn llvm.alloca with no llvm_ext.ptr_size_hint could not be cloned for\ncheckpointing: findPtrExtent only knew hints and llvm_ext.alloc sizes,\nand cloneValue errored with \u0027cannot find size of ptr\u0027. But an alloca\nalready says how much it is -- the DataLayout size of the element type,\nthat many times -- so compute that, as a last resort behind the hints.\n\nzeroInPlace had its own copy of this arithmetic, with two bugs the new\nallocaExtent does not have: a same-width llvm.sext of an i64 array size\n(invalid IR, caught by the verifier) and an element size approximated\nby getApproxSize()/8 instead of the DataLayout. It now calls\nallocaExtent too.\n\nAlso declare the LLVM and LLVMExt dialects as dependents of\nenzyme-wrap: cloning a pointer creates llvm_ext.alloc/memcpy (and now\nllvm.mul), and if the input module happens to contain no llvm_ext op,\nthe dialect was never loaded and op creation failed -- a fatal error in\na Debug build and a hard-to-track segfault in a Release one. The\nexisting checkpointing tests never saw this because their size hints\nload the dialect at parse time.\n\nSplit out of #3079 per review: the extent computation is independent of\nllvm.func call support. Note for reviewers: on this branch the alloca\npath is reachable through checkpointing of loops that mutate a buffer\n(the new test); under #3079 it also becomes what caches pointer\narguments of differentiated calls once overwritten-args support lands.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "124dc4984cad504abf1b567930f7a01a521b6dae",
      "tree": "a7a273e6448c1aed0a59237e049b0323d0f1dc7c",
      "parents": [
        "e2ae4c064778fece371fc3f1c4c4d5a7fd7b4642"
      ],
      "author": {
        "name": "Paul Berg",
        "email": "naydex.mc+github@gmail.com",
        "time": "Sat Aug 08 19:57:36 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 08 12:57:36 2026 -0500"
      },
      "message": "mlir: unify checkpointing implementations (#3092)\n\n* Add loop type agnostic interface for checkpointing\n\n* loop-checkpointing: open the mixin up to a non-memref dialect\n\nLoopCheckpointing was written against the two loops it had: bounds it can\nread off the op, a body block whose terminator it creates, `index`\narithmetic, and memref buffers to snapshot into. stablehlo.while has none\nof those -- its induction variable is a regular iteration argument, its\nbody arrives with a terminator already attached, it counts in tensor\u003ci64\u003e,\nand a \"buffer\" write there produces a new value rather than mutating one.\n\nRoute every overridable member through FinalClass:: (they were resolving to\nthe mixin\u0027s own, so nothing could be overridden), and add hooks for the\nattribute names, the body\u0027s shape, where the next body op goes, whether the\ncaller\u0027s seed mappings get published, how gradient slots are primed, and\nwhere a body op\u0027s reverse rule puts its caches.\n\nBinomial now goes through a storage abstraction -- create/store/load/destroy\nplus storesAreLoopCarried(), which threads the stores through the scaffold\nloops as a trailing group of iteration arguments when a write yields a new\nvalue -- along with scalar-arithmetic hooks and scaffold for/while builders.\nmemref and scf remain the defaults, so scf.for and affine.for emit exactly\nwhat they did before.\n\nPeriodic gains a dynamic trip count. The decomposition moves into a\nPeriodicSchedule: with a period stated, ceil(N/period) segments each clamped\nto min(period, N - base) at runtime. affine.for opts out, its bounds being\nAffineMaps rather than values.\n\nTwo behavior changes fall out of this, both fixes:\n\n  - enzyme.checkpoint_period is honoured for periodic checkpointing.\n    cachePeriodic read the attribute and then ignored it, always splitting\n    at sqrt(N); scf_for_checkpointing_mutable_memory.mlir\u0027s expectations\n    move accordingly.\n\n  - the reverse segment index was nOuter - j, one past the end whenever\n    there is no trailing segment (a period that divides the trip count, or\n    a perfect-square sqrt split). The replayed induction variable came out\n    shifted by a whole period -- silently, since it is dead whenever the\n    body does not read it, which is why no test caught it.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n\n* loop-checkpointing: clang-format\n\nLoopCheckpointing.h in full -- it was clean before this branch. The scf and\naffine files only where this branch touched them: both carried formatting\ndrift from before it, and reformatting the rest would bury the change.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n\n* fmt\n\n---------\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "e2ae4c064778fece371fc3f1c4c4d5a7fd7b4642",
      "tree": "2118b8b583b6e9c07a2903bc07e7ed4b34d183f9",
      "parents": [
        "002eed69cabd19b8ad78df9a810fec424dec63b6"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sat Aug 08 12:45:36 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 08 12:45:36 2026 -0500"
      },
      "message": "Describe the __enzyme_* call markers in one place (#3086)\n\n* Describe the __enzyme_* call markers in one place\n\nThe arguments of an __enzyme_autodiff/__enzyme_fwddiff call are a small\nlanguage: marker globals that either name the activity of the argument\nafter them or configure the call, the arguments they speak for, and the\nshadows that go with those. The LLVM pass that lowers the call reads it,\nand so must the MLIR raising that turns the same call into an\nenzyme.autodiff or enzyme.fwddiff op.\n\nWritten twice they drift, and a marker read as an argument -- or an\nargument read as a marker -- is a wrong derivative rather than an error.\nSo the names, what each one takes, and where enzyme_interleave puts the\nshadows move to EnzymeCallMarkers.h, over no IR in particular, and each\nreader drives its own walk with them. Enzyme.cpp keeps only what each\nmarker then does with what it took.\n\nTwo things the MLIR side needs to differentiate an llvm.func, which is\nwhat a raised call names: enzyme.fwddiff now accepts any function rather\nthan only a func.func, as enzyme.autodiff already did, and the forward\nhandler asks the function how it is called instead of writing a func.call\nto it.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: the marker grammar speaks DIFFE_TYPE directly\n\nNo invented MarkerActivity with per-reader mappings: DIFFE_TYPE moves\ninto the marker header -- it is a plain enum with no dependencies --\nUtils.h includes it from there, and both readers consume the activities\nas they are.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "002eed69cabd19b8ad78df9a810fec424dec63b6",
      "tree": "f5f93cbe50b497ef98098abd4537fc66ba9d2585",
      "parents": [
        "9a5a4fc033d62ec2032562371701b04402feddf9"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sat Aug 08 12:31:54 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 08 12:31:54 2026 -0500"
      },
      "message": "MLIR: differentiating a bodyless function is an error, not a crash or a zero (#3104)\n\n* MLIR: differentiating a bodyless function is an error, not a crash or a zero\n\nA call to a function with no body and no registered derivative has no\ntangent or adjoint to construct. The forward and reverse call handlers\nnow say so at the call site; CreateForwardDiff and CreateReverseDiff\nreport the function instead of llvm_unreachable (which in release builds\nis not a diagnostic but undefined behavior); and their callers treat a\nnull result as failure. Before this, differentiating such a call crashed\nthe compiler -- and one wrongly considered inactive silently produced\nzero derivatives, the shape of MFEM\u0027s dFEM gradient bug, where an\nunraised libm sqrt cost a term of every gradient.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* review: drop duplicated null guards, test direct bodyless differentiation\n\nThe enzyme.fwddiff/enzyme.autodiff of a function that has no body at all\ntakes the CreateForwardDiff/CreateReverseDiff error path directly; the\nnew tests pin that alongside the existing call-inside-a-body ones.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "9a5a4fc033d62ec2032562371701b04402feddf9",
      "tree": "96474282565b501d783c2980365efd3c670a25d2",
      "parents": [
        "c1c312e777bbe078705236ec3b551344d2a4e583"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sat Aug 08 12:31:39 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 08 12:31:39 2026 -0500"
      },
      "message": "MLIR: forward-mode tangent for arith.select (#3107)\n\n* MLIR: forward-mode tangent for arith.select\n\nThe condition carries no tangent; the result\u0027s tangent follows the same\nchoice between the branch tangents, a constant branch contributing zero.\nA select of mutable values refuses loudly rather than guessing.\nReverse-generated adjoints are full of these selects, and forward-over-\nreverse differentiates them again -- MFEM\u0027s dFEM Hessian tests could not\nbuild their second derivatives without this.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* MLIR: derivatives for math.powf and math.log\n\nd/dx pow(x, y) \u003d y pow(x, y-1); d/dy \u003d pow(x, y) log(x), with the x \u003d\u003d 0\nguard pinning to zero where the log has none -- the same idiom as the\nexisting sqrt rule. d/dx log(x) \u003d dx/x. MFEM\u0027s neo-hookean energy could\nnot differentiate without pow.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* select fwd: one rule for values and pointers\n\nA pointer\u0027s shadow follows the choice the primal made the same way a\nvalue\u0027s tangent does, and invertPointerM hands back the appropriate\nnull or primal for a constant branch -- no reason to refuse mutable\ntypes.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* select fwd: spell out the per-operand cases\n\nAn active branch contributes its shadow via invertPointerM; a constant\nimmutable branch a null value; a constant mutable branch has no shadow\nof its own, so hand back the primal and warn, pending proper runtime\nactivity handling.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* select fwd: operandTangent as a static function\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "c1c312e777bbe078705236ec3b551344d2a4e583",
      "tree": "dbcf34a6ef526326da9cc251cdccc03b67479bdb",
      "parents": [
        "5525ebadc8fd0ca92b3ccfbe51317b0da950bc12"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sat Aug 08 12:29:19 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 08 12:29:19 2026 -0500"
      },
      "message": "MLIR dataflow activity: a summarized function still needs its own analysis (#3105)\n\n* MLIR dataflow activity: a summarized function still needs its own analysis\n\nThe dataflow activity analyzer walks the callgraph of the function being\ndifferentiated and skips any node already carrying a serialized pointer\nsummary. The summary is a cache for CALLERS; the function itself still\nneeds its per-value origin maps. Nested differentiation reaches exactly\nthat state: the enclosing analysis summarizes the first-order function\nthe inner enzyme.fwddiff generated, and when the outer differentiation\nthen analyzes it as the root, the skip left its maps empty, every value\nlooked inactive, and the second derivative of anything came out zero --\nsilently. MFEM\u0027s dFEM second-derivative tests lost their Hessians this\nway.\n\nSkip only nodes other than the root; the root always gets analyzed.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* Terminate points-to traversal, memoize activity queries\n\ntraversePointsToSets walked the points-to graph with no memory of where\nit had been: a cycle -- which reverse-mode generated code produces\nbetween shadow and primal structures -- spun it forever, and converging\nchains revisited their shared tails once per path. MFEM\u0027s dFEM\nsecond-derivative TU sat in this loop past 50 minutes; with a visited\nset it clears the pipeline in 3.6.\n\nThe activity queries themselves are fixed once the analyzer is built and\nwalk that same graph, and differentiation asks about every value, often\nrepeatedly -- cache their answers. Also strip a stale self-summary\nbefore re-analyzing the root, so the fresh analysis does not read it as\ncallee information about itself.\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Fable 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "5525ebadc8fd0ca92b3ccfbe51317b0da950bc12",
      "tree": "56076874fedec26e9c6feb2dd4f0efa4904c80eb",
      "parents": [
        "324a16781c2b7ab591f9a241d6d67a68482efa2d"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Thu Aug 06 00:55:00 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 06 00:55:00 2026 -0500"
      },
      "message": "Give the derivative its own comdat rather than the primal\u0027s (#3100)\n\n* Give the derivative its own comdat rather than the primal\u0027s\n\nA comdat says which of the identical copies of a symbol the linker should keep,\nand it keeps or discards the whole group at once. CloneFunctionWithReturns\nbuilds the derivative with F-\u003ecloneWithoutRegions(), which copies every\nattribute the primal has -- the comdat among them -- so the derivative landed\nin the primal\u0027s group.\n\nOnly the translation units that differentiate the primal put a derivative in\nthat group. A unit that merely calls the primal offers a group of one under the\nsame key, and if that is the copy the linker keeps, the derivative goes with the\ncopy it discarded:\n\n  undefined reference to `fwddiffe_ZNK4mfem6future14tensor_ndarray...set_layout\u0027\n\nfrom the very object that defines it. Five of MFEM\u0027s unit-test objects offered\nthe derivative-less group for that key and two offered the group with the\nderivative (EnzymeAD/Enzyme-JAX#2778).\n\nThe derivative is its own symbol and wants its own group, keyed on its own\nname: it still dedupes against the other units that built the same derivative,\nand nothing else can take it away. Dropping the comdat instead would not do --\nthe derivative is external, and two units defining it would then collide.\n\nWhat a clone inherits that names the primal rather than itself is particular to\nthe kind of function, so this is a new AutoDiffFunctionInterface method rather\nthan a dialect check in the generic clone: llvm.func gives up its comdat,\nfunc.func has no linkage of its own to give up.\n\nThe LLVM implementation never had this because it builds the gradient with\nFunction::Create and so never copies a comdat in the first place.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* Delete enzyme/Enzyme/epv\n\n* Delete enzyme/Enzyme/epu\n\n---------\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "324a16781c2b7ab591f9a241d6d67a68482efa2d",
      "tree": "2bc2a5cefea1bbaba60663aad487c8602bd872ce",
      "parents": [
        "4f444818bf24d510b351483ff2f228924da9abb2"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 19:58:12 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 19:58:12 2026 -0500"
      },
      "message": "Remove a pop nobody reads when its cache has no other pop (#3099)\n\nReverse mode caches the branch it took, then finds the adjoint of both arms\nempty. What is left is a cache pushed in one block and popped in another, whose\npopped value nobody reads. PopSimplify cannot pair those -- it gives up when\nthe push and the pop are in different blocks, to avoid pairing across something\nthat may run more than once -- so nothing removed the pop, the pop held the\npushes, the pushes held the init, and the init reached LLVM translation:\n\n  cannot be converted to LLVM IR: missing `LLVMTranslationDialectInterface`\n  registration for dialect for op: enzyme.init\n\nwhich is what MFEM\u0027s tests/unit/enzyme/test_enzyme_reverse_tape.cpp failed with\n(EnzymeAD/Enzyme-JAX#2778).\n\nA pop nobody reads is worth keeping only for what it does to the cache: it\nmoves the stack on for whatever pops next. When it is the one pop that cache\nhas, there is no next, and the whole cache is dead -- so the pushes and the\ninit go with it, in the one rewrite. Taking the pop alone would leave a cache\npushed and never popped, which is worse than what was there before. A cache\nheld by anything other than a push or a pop could be read in a way this cannot\nsee, so those are left alone.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "4f444818bf24d510b351483ff2f228924da9abb2",
      "tree": "20c8e099e702420c3cbfd48de39435a6cc5a46f9",
      "parents": [
        "1d575edd84d4105788a299d276d7849a2f8ca2eb"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 19:57:38 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 19:57:38 2026 -0500"
      },
      "message": "Give llvm.intr.memset a forward-mode rule (#3098)\n\nAfter a memset the memory holds a fixed byte pattern, which depends on no\ninput, so its tangent is zero everywhere the memset reached. Forward mode says\nthat by clearing the shadow over the same range -- whatever derivative the\nmemory carried before is gone along with the value it belonged to.\n\nllvm.intr.memset is declared InactiveOp, which attaches an ActivityOpInterface\nand nothing else, so forward mode had no rule for it and stopped at \"could not\ncompute the adjoint for this operation\" on three of MFEM\u0027s dFEM test files\n(EnzymeAD/Enzyme-JAX#2778), which memset their local tensors before filling\nthem. Being inactive is about what an op makes active, not about whether the\nderivative needs it said.\n\nThe byte written says nothing about the tangent, so the shadow gets zeros\nwhatever pattern the primal gets. A byte that is itself differentiated would\nhave to have its tangent splatted across the shadow; nothing produces that\ntoday, and guessing at it would quietly zero a derivative, so that case is an\nerror rather than a silent answer.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "1d575edd84d4105788a299d276d7849a2f8ca2eb",
      "tree": "681a24cdde780a5b2db8bc2030c6a3b3b3e0f303",
      "parents": [
        "4313651e36fa072608afb9174048bfc5106f5aa7"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 19:25:31 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 19:25:31 2026 -0500"
      },
      "message": "Keep a branch\u0027s condition when neither successor takes an argument (#3096)\n\nbranchingForwardHandler copies the operands ahead of the first one any\nsuccessor forwards -- for cf.cond_br that is the condition, for a switch the\nvalue -- and it located that boundary by scanning for a successor that forwards\nsomething:\n\n  size_t non_forwarded \u003d 0;\n  for (...) {\n    auto ops \u003d binst.getSuccessorOperands(i).getForwardedOperands();\n    if (ops.empty()) continue;\n    non_forwarded \u003d ops.getBeginOperandIndex();\n    break;\n  }\n\nWhen no successor takes an argument the scan finds nothing and the boundary\nstays at 0, so no operand is copied at all. The tangent came out as\n\n  \"cf.cond_br\"()[^bb1, ^bb2] \u003c{operandSegmentSizes \u003d array\u003ci32: 0, 0, 0\u003e}\u003e\n\nwhich fails the verifier with \"expected 1 or more operands, but found 0\". Three\nof MFEM\u0027s dFEM test files hit this on a bounds check whose two arms take\nnothing (EnzymeAD/Enzyme-JAX#2778).\n\nThere being no forwarded operand does not mean there is no boundary; it means\nthe boundary is past every operand the op has.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "4313651e36fa072608afb9174048bfc5106f5aa7",
      "tree": "e513a3860c410cf74e195ce635376763adffe8e4",
      "parents": [
        "73a5c7e8f25758ef624893a5b787b70e1ba340c6"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 19:22:43 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 19:22:43 2026 -0500"
      },
      "message": "Say an unknown summary set the way the readers of summaries read it (#3097)\n\nserializeSetNaive says an unknown or undefined set as the bare marker string,\nand every reader of these summaries looks for exactly that. serializeMapOfSetsNaive\nsaid it as a one-element list instead:\n\n  [#distinct, [\"\u003cunknown\u003e\"]]     rather than     [#distinct, \"\u003cunknown\u003e\"]\n\nso the marker branch in both deserializePointsTo functions was dead, and the\nlist read as a set of one known element. That element, a StringAttr, was then\ncast unchecked to DistinctAttr or OriginAttr and its arg number used as an\nindex -- 336934464 into a six-element vector, in MFEM\u0027s dFEM tests, which\nsegfaulted three of them (EnzymeAD/Enzyme-JAX#2778). One of the two readers was\nalso comparing against \"unknown\"/\"undefined\" without the angle brackets, so\neven given the right shape it would not have recognised them.\n\nReading the marker correctly then hands the consumers an unknown set where they\nhad only ever seen defined ones, and getElements asserts on those, so the three\nplaces that translate a callee\u0027s summary to its caller now answer it: unknown\norigins stay unknown, and memory whose sinks the callee could not name leaves\nevery one of the caller\u0027s classes a candidate. Dropping it would have been the\nunsound direction -- \"came from nowhere\" reads as inactive.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "73a5c7e8f25758ef624893a5b787b70e1ba340c6",
      "tree": "cfa80bdd750e591f3b6bdace3788bad31f44876f",
      "parents": [
        "21bd8a9addccd5b7e3e71e4efb5df7ad1703621d"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 18:35:32 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 18:35:32 2026 -0500"
      },
      "message": "Decide whether to skip an op with the analyzer everything else uses (#3095)\n\nisConstantInstruction dispatches to the dataflow activity analyzer where\nthere is one and to the older analyzer otherwise, as isConstantValue does.\nThe forward-mode skip in visitChild called the older analyzer directly, so\nwith -reactant-dataflow it decided the two halves of one condition by two\ndifferent analyses:\n\n  all_of(results, isConstantValue)                        // dataflow\n  \u0026\u0026 ... \u0026\u0026 activityAnalyzer-\u003eisConstantOperation(TR, op) // not\n\nThat is how a load whose pointer is inactive stayed unskipped and reached\nmemoryIdentityForwardHandler, which has no shadow to make of a constant\npointer and said so:\n\n  Unsupported constant arg to memory identity forward handler(opidx\u003d0, ...)\n\nnineteen times over MFEM\u0027s dFEM tests. The result of a load of a constant\nis constant, and the dataflow analyzer says so; it was not the one asked.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "21bd8a9addccd5b7e3e71e4efb5df7ad1703621d",
      "tree": "841a917813610fdb0b4bdcb9d7b82300d6804e7b",
      "parents": [
        "b88dd07f85504537ed5f87209eaac5eac67408df"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 18:27:23 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 18:27:23 2026 -0500"
      },
      "message": "Give lifetime markers a forward-mode rule (#3093)\n\nllvm.intr.lifetime.start/end are declared InactiveOp, which attaches an\nActivityOpInterface and nothing else. Forward mode then had no rule for them\nand stopped at \"could not compute the adjoint for this operation\" -- MFEM\u0027s\ndFEM kernels hit this on every stack buffer.\n\nBeing inactive is about what an op makes active, not about whether the\nderivative needs it said: a barrier is inactive too and still has to be there.\nA lifetime marker says when the memory it names is live, and the shadow is\nmemory that lives exactly as long, so the tangent is the same marker said\nagain of the shadow. When the pointer is constant there is no shadow whose\nlifetime this could be, and the primal marker alone is the whole answer.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "b88dd07f85504537ed5f87209eaac5eac67408df",
      "tree": "101f3216247e95492d0264cc63dcefa880a4ee84",
      "parents": [
        "84c6fae0fc907757c318164fc8ac86281fdd655e"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 18:09:38 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 18:09:38 2026 -0500"
      },
      "message": "Do not walk one map while inserting into it (#3094)\n\n#3090 made MapOfSetsLattice::join walk the other side\u0027s map and try_emplace\ninto this one, which is right where they are different objects and not\nwhere they are the same: the insert can rehash the map being walked.\n\nJoining a lattice with itself says nothing new, so say that, which settles\nit. Kept separate from #3090 rather than folded in, since that has landed.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "84c6fae0fc907757c318164fc8ac86281fdd655e",
      "tree": "b8f486eb482fe6f0ac7f8ce564b5f3c5991d451d",
      "parents": [
        "f411cef2f47c7cf977efc3af96d84aeafb75ca50"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 16:19:18 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 16:19:18 2026 -0500"
      },
      "message": "Give each differentiated function its own dataflow solver (#3091)\n\nMEnzymeLogic holds one DataFlowSolver for a whole run of the pass, and\nevery function differentiated runs its activity annotations into it. A\nDataFlowSolver keeps every lattice it has ever made, so the state grows\nwith each function, and the map-of-sets joins that the annotation analyses\nspend their time in grow with it.\n\nNothing is gained by the sharing: the annotation analyses are not\ninterprocedural and say so themselves --\n\n  assert(!solver.getConfig().isInterprocedural());\n\n-- so nothing carries from one function to the next.\n\nThe solver cannot simply be reset on the Logic, since differentiating a\ncallee while differentiating its caller would leave the outer analyzer\nholding a reference to a solver that had been replaced. Hand it to the\ngutils that uses it instead, which owns it for exactly as long as it is\nneeded.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "f411cef2f47c7cf977efc3af96d84aeafb75ca50",
      "tree": "82c93e6b69f27295687e7bc6ee3d8047736912fc",
      "parents": [
        "d486232acbd5c793774575c18b61bc1ae029debf"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 15:49:01 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 15:49:01 2026 -0500"
      },
      "message": "Join a map of sets by what the other side has (#3090)\n\nMapOfSetsLattice::join took the union of both sides\u0027 keys into a set and\nwalked that, looking each key up in both maps. A key only the left side has\nis then left exactly as it was -- the loop says so -- so all that work was\nfor the keys that were going to be skipped.\n\nA fixpoint joins into an accumulated lattice over and over with something\nsmall, so paying the size of the accumulation each time is the whole cost.\nWalk the other side\u0027s keys and try_emplace: present, join; absent, insert.\n\nSame result, one lookup per key of the right-hand side, and no set built to\nfind them.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "d486232acbd5c793774575c18b61bc1ae029debf",
      "tree": "be96a736e95a64b75bc5b3504824ee76ab81b423",
      "parents": [
        "caf58be33fb30067f7cf80743f35a5c7a69e27d5"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 15:40:31 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 15:40:31 2026 -0500"
      },
      "message": "Prefer annotations for Enzyme\u0027s registration attributes, fixing templates (#3085)\n\nenzyme_inactive, enzyme_inactive_noblock, enzyme_nofree and\nenzyme_sparse_accumulate were lowered to a global whose initializer is\nthe address of the annotated declaration. A templated declaration has no\naddress until it is instantiated, so each of these rejected any use\ninside a template with \"use of attribute \u0027...\u0027 in a templated context not\nyet supported\" -- for a function template, a member function or static\ndata member of a class template, or a variable template.\n\nLower them to an annotation instead. Clang propagates annotations through\ntemplate instantiation for us, which is why the attributes that already\nlower to one (enzyme_function_like, enzyme_shouldrecompute,\nenzyme_elementwise_read) have always worked in templates.\n\nClang only emits annotations for the entities a translation unit defines\nthough, so a declaration defined elsewhere still needs the global to\nforce the reference, as in customglob.cpp\u0027s\n\n  __attribute__((enzyme_inactive)) extern MyMemoryType host_mem_type;\n\nWhether a function declarator will have a body is not known while its\nattributes are processed, since that happens before the body is parsed,\nso decide once the declaration reaches the AST consumer. A class member\nnever reaches the consumer on its own and is handled where it is parsed.\n\nAn enzyme_inactive registration global also marks the body of the\nfunction, which the pre-existing enzyme_inactive annotation does not, so\nadd enzyme_inactivefn and enzyme_inactivenoblockfn annotations which do,\nsharing markFunctionInactive with the global path. The meaning of the\nbare enzyme_inactive annotation is unchanged.\n\nThe four attributes had a near-identical copy of the lowering each;\nfactor it into registerEnzymeGlobal and handleEnzymeMarkerAttr. That\nleaves enzyme_function_like\u0027s pre-LLVM 12 fallback as the only user of\nStructKind. As LLVM 15 is the oldest supported version, and that fallback\nrefers to an undefined \u0027stringkind\u0027 and so could never have compiled,\ndrop it along with StructKind, which clang otherwise reports as an unused\nconstant.\n\n\nClaude-Session: https://claude.ai/code/session_01SCicE3j1cDSaLivwBpTgSG\n\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "caf58be33fb30067f7cf80743f35a5c7a69e27d5",
      "tree": "cc37cf4ff877b8b48f5d4ab09fb78957af744c3b",
      "parents": [
        "3174c815b9bacaa465013593b68f37c564de3709"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 14:29:39 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 14:29:39 2026 -0500"
      },
      "message": "A result nothing differentiates has no shadow to be given (#3088)\n\nforceAugmentedReturns makes a shadow placeholder for a result only where\nthe result is not constant. memoryIdentityForwardHandler set a shadow on\nevery result of the op it handles, constant or not, and setDiffe then went\nlooking for the placeholder that was never made:\n\n  auto found \u003d invertedPointers.lookupOrNull(val);\n  assert(found !\u003d nullptr);              // says nothing in a release build\n  auto placeholder \u003d found.getDefiningOp\u003cenzyme::PlaceholderOp\u003e();\n\nso a release build read a null Value instead. setDiffe asserts the same\nthing one frame up, for the same reason.\n\nAn op gets here for what its operands read, not for what it returns, so a\nconstant result is ordinary: MFEM\u0027s dFEM kernels load an index out of the\nvery memory being differentiated, and an i64 is nothing to differentiate.\nThe shadow op is still worth building; only the setting is skipped.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "3174c815b9bacaa465013593b68f37c564de3709",
      "tree": "6deeabb0d00dd29a18214fa3e461182bdf6d923c",
      "parents": [
        "6d05b52924c6d96701a977cc47f6c95f559bb7af"
      ],
      "author": {
        "name": "Valentin Churavy",
        "email": "v.churavy@gmail.com",
        "time": "Wed Aug 05 20:39:44 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 20:39:44 2026 +0200"
      },
      "message": "Provide a fpm.toml for the Fortran interface (#3080)"
    },
    {
      "commit": "6d05b52924c6d96701a977cc47f6c95f559bb7af",
      "tree": "4444fe2c9f2a804afd65c63554beb494ed65a66d",
      "parents": [
        "9f8e67c576cbf4af08a143be20fef76b9d85d192"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 13:02:21 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 13:02:21 2026 -0500"
      },
      "message": "Differentiate an llvm.func in forward mode (#3087)\n\nWhat enzyme.fwddiff names is not always a func.func: anything raised from\nLLVM gives an llvm.func. Forward mode turned that down twice over. The\nverifier looked the callee up as a func.func, so the op did not even parse\npast verification, and the pass wrote a func.call to whatever came back,\nwhich is not how an llvm.func is called.\n\nReverse mode has always taken either -- AutoDiffOp::verifySymbolUses looks\nup a FunctionOpInterface, and HandleAutoDiffReverse asks the function how\nit is called through AutoDiffFunctionInterface::createCall, which\nllvm.func implements. Forward mode now does the same.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "9f8e67c576cbf4af08a143be20fef76b9d85d192",
      "tree": "caf3b4cb6aeeb358c47f8937dd0efe376319559c",
      "parents": [
        "715e9c030335b72858b41946e9a8cdcb453c7e95"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 11:32:05 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 11:32:05 2026 -0500"
      },
      "message": "Load adjoint: a mutable type takes a shadow, not an accumulation (#3078)\n\n* A load adjoint has nothing to accumulate into a mutable type\n\nA mutable type\u0027s derivative is a shadow, not a number: loading a pointer\nout of a pointer gives an active value whose gradient is the shadow\npointer, and there is no adding one pointer to another. The three store\nadjoints all say so already --\n\n  if (!iface.isMutable()) { ...accumulate... }\n\n-- and the three load adjoints did not, so each reached\n`PointerTypeInterface::createAddOp`, which is `llvm_unreachable(\"TODO\")`,\nand MFEM\u0027s fem/test_fe_compatibility.cpp took the trap several frames from\nanything that named a pointer.\n\nSay the same thing on the load side. No working case is lost: the only\ntypes this newly skips are the ones whose createAddOp could not have\nreturned.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* Give the loaded shadow a value instead of a placeholder\n\nSkipping the accumulation is only half of it: the loaded pointer still had\nno shadow, so it kept the enzyme.placeholder that stood in for one and\nnothing downstream could resolve it.\n\nWhat a load of a mutable type reads is itself a handle on active memory,\nso what stands for it is the handle held at the same place in the shadow\n-- the same load, off the shadow address. That is what llvm.getelementptr\nalready does one op along, and the two now agree.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n* Cover affine.load too\n\nThe change touches three load interfaces and the test named two. affine\nis the one MFEM actually goes through, and the one whose shadow has to\nsurvive being cached across the loop.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\n---------\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "715e9c030335b72858b41946e9a8cdcb453c7e95",
      "tree": "8be1ae77e738591f83d608ab0efde8ab2788a839",
      "parents": [
        "84b85f8b6c7b17ffcab46151d72326e28416ea58"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 11:16:24 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 11:16:24 2026 -0500"
      },
      "message": "Give math.fma a derivative (#3077)\n\n`math.fma(a, b, c)` is `a * b + c` and had no rule, so anything reaching\nit stopped at \"could not compute the adjoint for this operation\". MFEM\u0027s\ndFEM kernels reach it all over -- the Jacobian determinants and the\ngeometric factors are written as fused multiply-adds -- and neither\ntest_functional_gradient nor test_second_derivative would compile.\n\nEach factor takes the adjoint scaled by the other; the addend carries it\nthrough untouched. Forward mode falls out of the reverse rules the way it\ndoes for the rest of this file.\n\n\nClaude-Session: https://claude.ai/code/session_016zErYp7upmqr4NHfhod9UD\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "84b85f8b6c7b17ffcab46151d72326e28416ea58",
      "tree": "d3338e5864242a995b6087791f5dc43eed4b65e7",
      "parents": [
        "1361deeecd9fce13f63b5745f587dc22833714b8"
      ],
      "author": {
        "name": "Benjamin Coveler",
        "email": "SpeedyTurtle599@users.noreply.github.com",
        "time": "Wed Aug 05 16:54:46 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 10:54:46 2026 -0500"
      },
      "message": "Add exp10 intrinsic for LLVM \u003e\u003d18 (#2998) (#3001)"
    },
    {
      "commit": "1361deeecd9fce13f63b5745f587dc22833714b8",
      "tree": "366de6f59abf55f0f98e3af32654cc3f1e015e43",
      "parents": [
        "7a2a6265c2945899fa8bfec15ef0291f18f3a718"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Wed Aug 05 00:04:12 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 05 00:04:12 2026 -0500"
      },
      "message": "Report mismatched activity as a warning, not just an -Rpass remark (#3076)\n\nEmitWarning routes every message through an llvm::OptimizationRemark\ngated on isPassedOptRemarkEnabled(\"enzyme\"), so mismatched-activity\ndiagnostics were silently dropped unless the user happened to pass\n-Rpass\u003denzyme. Users therefore had no indication that a value Enzyme\nproved inactive was being used where an active value may be required,\nand hence that runtime activity may be needed.\n\nRework EmitWarningAlways into a general warning helper with Instruction\nand Function overloads. It emits an EnzymeWarning\n(DiagnosticInfoUnsupported at DS_Warning), which clang surfaces as an\nordinary warning with source location. Route the MixedActivityError\nsites through it and append a hint pointing at enzyme_runtime_activity.\n\nThis is a warning rather than an error, so compilation still succeeds.\n\n\nClaude-Session: https://claude.ai/code/session_01SCicE3j1cDSaLivwBpTgSG\n\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "7a2a6265c2945899fa8bfec15ef0291f18f3a718",
      "tree": "a671ff2259cde05b3f89c5929166618e01499d29",
      "parents": [
        "d4f78fe2166863a29adcf02c9d660567e335b29e"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Tue Aug 04 21:16:41 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 04 21:16:41 2026 -0500"
      },
      "message": "Drop identity address-space casts in the Julia alloca lowering (#3074)"
    },
    {
      "commit": "d4f78fe2166863a29adcf02c9d660567e335b29e",
      "tree": "e17f8dbf27fd8398a2c95b592210cf2e183f35b9",
      "parents": [
        "50c11dc266ecdb29b87aeeadc91610bc21f98b89"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Tue Aug 04 20:11:43 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 04 20:11:43 2026 -0500"
      },
      "message": "Use Utils.h wrappers for version-sensitive LLVM APIs, and lint for it (#3071)\n\nTwo LLVM APIs cannot be called directly, because their signature or their\ncontract changed across the LLVM 15..main range Enzyme supports. Utils.h wraps\nboth; a number of call sites bypassed the wrappers.\n\ngetTerminator() as a null test\n------------------------------\n\n  if (REB-\u003egetTerminator())\n    EB.SetInsertPoint(REB-\u003egetTerminator());\n\nThrough LLVM 22 getTerminator() returned null for a block with no terminator,\nso this read as \"does this block have a terminator\". As of LLVM 23 it asserts:\n\n  Assertion `hasTerminator() \u0026\u0026 \"cannot get terminator of non-well-formed\n  block\"\u0027 failed.\n\nso the test crashes on exactly the input it was written to handle. The\nhasTerminator() helper in Utils.h already existed for this; these five sites\nwere never converted. Only null tests are affected -- dyn_cast\u003cBranchInst\u003e(\nBB-\u003egetTerminator()) and friends assume a terminator exists and test its type,\nwhich stays correct.\n\nThis makes ReverseMode/lcssa-fictitious-phi.ll pass on LLVM 23/24; it aborted\nin both its RUN lines before.\n\nchangePointerAddrSpace\n----------------------\n\nEleven sites built a pointer type by hand, each behind an #if on\nLLVM_VERSION_MAJOR and a supportsTypedPointers() branch. Six of them were\ndoing one thing: taking a pointer type and moving it to another address space,\ncarrying the pointee over where there still is one. Add a helper that says so,\nand collapse the branching at those sites.\n\ngetInt8PtrTy(Ctx, AS) would also have produced the right type there -- on\nthose paths opaque pointers are necessarily active and PointerType::get\ndiscards its element type (Type.cpp converts typed pointers to opaque ones\nitself) -- but it reads as \"make an i8*\", which would leave an accidental loss\nof the pointee indistinguishable from a deliberate opaque pointer. The\nremaining sites use the existing helpers, and one redundant #if in\nconvertSRetTypeFromString collapses for the same reason.\n\nLinter\n------\n\nscripts/check_llvm_api.py flags both patterns, wired into the Lint workflow\nnext to check_emission_order.py. It reports all 16 pre-existing sites when run\nagainst the parent commit and is clean on this one. False positives are\nsuppressed with // NOLINT(terminator-null-test) or // NOLINT(enzyme-pointer-type).\n\n\nClaude-Session: https://claude.ai/code/session_01P65yn8LELKWU1dq6AQvA5f\n\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "50c11dc266ecdb29b87aeeadc91610bc21f98b89",
      "tree": "7bc6dd301857f9c62f257ce806c480bb80d7ae14",
      "parents": [
        "381ed799b2bd9fbb3f1f8d26cc282ff1e456f6bc"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Tue Aug 04 18:05:57 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 04 18:05:57 2026 -0500"
      },
      "message": "Serialize TypeTree offsets as signed constants (#3070)\n\n* Serialize TypeTree offsets as signed constants\n\nTypeTree offsets may be negative -- -1 denotes \"any offset\" -- but\nTypeTree::toMD emitted them via ConstantInt::get without IsSigned, so -1\nbecame uint64_t 0xFFFFFFFFFFFFFFFF for an i32.\n\nLLVM \u003c\u003d 21 silently truncated this (ConstantInt::get passed\nimplicitTrunc\u003dtrue). As of LLVM 23 the default is implicitTrunc\u003dfalse,\nso the same call now trips\n\n  APInt.h: Assertion `llvm::isUIntN(BitWidth, val) \u0026\u0026\n           \"Value is not an N-bit unsigned value\"\u0027 failed.\n\nThis became reachable on LLVM \u003e\u003d 23 in e4afe548, which caches constant\nglobal type analysis via GV-\u003esetMetadata(\"enzyme_type\", ...). Rust\u0027s\nautodiff support hits it on essentially every kernel, since its panic\nlocation globals get a TypeTree containing -1 offsets.\n\nSign-extension is bit-identical to the previous truncating behavior\n(-1 -\u003e 0xFFFFFFFF) and matches insertFromMD, which already reads the\noffsets back with getSExtValue.\n\nAlso fix two other ConstantInt::get(Ty, -1) sites found by audit that\nwould assert the same way on LLVM \u003e\u003d 23 for sub-64-bit types.\n\nFixes #3060\n\nCo-Authored-By: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_01P65yn8LELKWU1dq6AQvA5f\n\n* Fix LLVM 15/16 lit invocation for the new test\n\nThe test uses opaque pointers, but %newLoadEnzyme appends\n-opaque-pointers\u003d0 on LLVM 16, so `ptr` failed to parse. Use the\n%OPnewLoadEnzyme substitution that exists for exactly this case, rather\nthan an explicit -opaque-pointers flag (which LLVM \u003e\u003d 17 rejects).\n\nCo-Authored-By: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_01P65yn8LELKWU1dq6AQvA5f\n\n---------\n\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "381ed799b2bd9fbb3f1f8d26cc282ff1e456f6bc",
      "tree": "60e3d5b1b0a42d1dd73a9afee01428d5073be755",
      "parents": [
        "8ead65c3c84852df2b90cfb0c5d7908aa306897b"
      ],
      "author": {
        "name": "Paul Berg",
        "email": "naydex.mc+github@gmail.com",
        "time": "Tue Aug 04 22:34:46 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 04 15:34:46 2026 -0500"
      },
      "message": "Fix compilation warnings (#3068)\n\n- op capture is not used\n - numbits variable is not used"
    },
    {
      "commit": "8ead65c3c84852df2b90cfb0c5d7908aa306897b",
      "tree": "4c6f128d334c697cd693c3afadedf8deb1b29c52",
      "parents": [
        "83ebc86e20fff87e134730fe61b8fa53a93e85b2"
      ],
      "author": {
        "name": "Joe Wallwork",
        "email": "22053413+joewallwork@users.noreply.github.com",
        "time": "Tue Aug 04 16:18:59 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 04 15:18:59 2026 +0000"
      },
      "message": "Add batch mode test (#3053)\n\n* Add batch mode test\n\n* Disable ifx for batch test\n\n* Add docs on batching"
    },
    {
      "commit": "83ebc86e20fff87e134730fe61b8fa53a93e85b2",
      "tree": "46e1a174a69374ed0aaebdbb1a43fa222473b502",
      "parents": [
        "38a4b38484e5bd6b042015a92042589f9124be5e"
      ],
      "author": {
        "name": "Joe Wallwork",
        "email": "22053413+joewallwork@users.noreply.github.com",
        "time": "Tue Aug 04 15:19:53 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 04 14:19:53 2026 +0000"
      },
      "message": "Separate out Fortran tests at different optimisation levels (#3054)\n\n* Separate out dot test\n\n* Separate out square tests\n\n* Separate out norm tests\n\n* Separate out allocatable array tests\n\n* Drop unnecessary declarations and add TODOs\n\n* Separate out flang plugin variants of allocatable array tests"
    },
    {
      "commit": "38a4b38484e5bd6b042015a92042589f9124be5e",
      "tree": "f34128edc0d0a7dc1488e41e80ff3d74c05fbc47",
      "parents": [
        "1db6e649de614009527b24004ee85360d6a81de1"
      ],
      "author": {
        "name": "Joe Wallwork",
        "email": "22053413+joewallwork@users.noreply.github.com",
        "time": "Tue Aug 04 14:03:38 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 04 13:03:38 2026 +0000"
      },
      "message": "Add more Fortran tests (#3020)\n\n* Add dot example\n\n* Add reverse mode norm tests\n\n* Extend dot test as in Enzyme-Tutorial\n\n* Use unique function and program names; consistent formatting\n\n* Add missing implicit nones\n\n* Add default accessibility statements\n\n* Drop ifx 2023.1.0 from CI\n\n* Add flang plugin test variants for dot and norm examples"
    },
    {
      "commit": "1db6e649de614009527b24004ee85360d6a81de1",
      "tree": "d9a8342b103ab13c565f303baa8a2a0fc24fc87a",
      "parents": [
        "5c6240c914b6c52703dfaa75eaf7f743854b0390"
      ],
      "author": {
        "name": "Valentin Churavy",
        "email": "v.churavy@gmail.com",
        "time": "Tue Aug 04 12:26:19 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 04 10:26:19 2026 +0000"
      },
      "message": "build an actual flang plugin instead of symlinking (#3056)\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "5c6240c914b6c52703dfaa75eaf7f743854b0390",
      "tree": "5224d0277ea7a0be1dfd8fd48fed52f44d542a19",
      "parents": [
        "4f4e9c43b37ae070eee5bc90db22a1f839d20539"
      ],
      "author": {
        "name": "Paul Berg",
        "email": "naydex.mc+github@gmail.com",
        "time": "Tue Aug 04 10:36:51 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 04 10:36:51 2026 +0200"
      },
      "message": "Add pass to hoist allocations (#3051)\n\n* Add pass to hoist allocations\n\n* fix allow list\n\n* add test"
    },
    {
      "commit": "4f4e9c43b37ae070eee5bc90db22a1f839d20539",
      "tree": "06338aa11f78718d98db8eddff5adfe29980ebae",
      "parents": [
        "dcf44fb5c851162a83aefa301ee5c48b2fa731a9"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Aug 03 16:44:04 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 03 16:44:04 2026 -0500"
      },
      "message": "Preserve attributes on loops built while differentiating (#3059)\n\nTwo places dropped them.\n\nreplaceWithNewOperands widens a loop with the cache index by constructing a new\nloop, taking the old one\u0027s region and replacing it. It is the same loop with\nextra iteration arguments, but it was built with no attributes at all, so\neverything set on it was lost the moment the removal pass needed to widen it.\nFixed for all four variants: scf.for, scf.parallel, affine.for and\naffine.parallel.\n\nThe affine implementation also had no propagation onto the reverse loop, where\nSCF has preserveAttributesButCheckpointing and calls it at ten of its twelve\nloop creation sites. Mirror the helper and call it on revFor.\n\nThis is why it was not noticed: enzyme.disable_mincut, the attribute it matters\nmost for, is backstopped by hasMinCut walking parent ops, so the setting is\nstill found when the loop\u0027s own copy is gone. No existing golden checks these\nattributes either, and FileCheck matches by substring, so an existing\n\"CHECK-NEXT: }\" matches \"} {enzyme.disable_mincut \u003d true}\" whether or not the\nattribute survived.\n\nThe new tests use an attribute with no such backstop. Without the fix the\noutput loops carry no attributes at all; with it they carry what the input loop\nhad.\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "dcf44fb5c851162a83aefa301ee5c48b2fa731a9",
      "tree": "5ad8e5e3cdad8322ea34aea05adab56ac8491312",
      "parents": [
        "8c8ce94022ca73f86f3882a1ff2d01f6ff2ee473"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Aug 03 14:10:26 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 03 14:10:26 2026 -0500"
      },
      "message": "MLIR fast math derivatives (#3057)\n\n* MLIR fast math derivatives\n\n* mlir: keep fast-math when rewriting a subtraction from zero, and update tests\n\narith.subf\u0027s simplification to arith.negf dropped the flags of the op it\nreplaced, which is how the only unflagged arithmetic left in a differentiated\nkernel got there.\n\nThe test updates are mechanical: derivative expressions now print their\nfast-math flags.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n\n* mlir: give Enzyme\u0027s atomic accumulations fast-math flags, and fold away the ones that add zero\n\nThe reverse pass accumulates gradients through enzyme.atomic_rmw and\nenzyme.affine_atomic_rmw, which had nowhere to record the fast-math flags of\nthe arithmetic they stand for. They now carry them, like every other op the\nderivative builds.\n\nThat makes an accumulation of zero recognizable, which is what a reverse pass\nproduces for every inactive channel of a seed. Adding zero leaves memory alone,\nso the read-modify-write is only a read -- and where nobody wanted the read, it\nis nothing at all. Signed zeros are the catch, so the float case asks for nsz;\nthe integer case needs no flags. An ordering an observer could synchronize\nagainst is left alone, since the memref dialect cannot spell an atomic load.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "8c8ce94022ca73f86f3882a1ff2d01f6ff2ee473",
      "tree": "9ccdcf9f56a0496aebab658b2fee798651ac65ec",
      "parents": [
        "a19edda70155e5e3d35e5199ffecce06b58160d4"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Aug 03 13:31:33 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 03 13:31:33 2026 -0500"
      },
      "message": "Make the min cut graph\u0027s adjacency iteration deterministic (#3058)\n\nNode wraps a Value or Operation pointer, so iterating a SmallPtrSet\u003cNode\u003e\nvisits neighbours in address order. Under ASLR that order changes from run to\nrun, so the max flow explores augmenting paths differently and, whenever\nseveral minimum cuts have the same capacity, settles on a different one. Every\nchoice is correct and they all cache the same number of values, but which\nvalues get cached varies, and the generated IR varies with it.\n\nThe effect is easy to see through Enzyme-JAX: differentiating a checkpointed\nloop whose cached values depend on the induction variable produced 9 distinct\noutputs over 12 runs of the same binary on the same input. Disabling ASLR made\nit deterministic, confirming address order as the source. With the adjacency\nsets insertion-ordered, the same 12 runs produce 1 distinct output.\n\nThe remaining SmallPtrSet\u003cNode\u003e uses are membership-only \"done\" sets that are\nnever iterated, so they are left alone.\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "a19edda70155e5e3d35e5199ffecce06b58160d4",
      "tree": "41336f8ad29614e1ba7082430df5b3d9d3c0d1cc",
      "parents": [
        "f9c069480da76873fd85d99a2716300bb209cf33"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sun Aug 02 19:37:27 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Aug 02 19:37:27 2026 -0500"
      },
      "message": "mlir: Simplify enzyme ops before each remover, not only once per function (#3050)\n\nThe pattern-based simplifications (PopSimplify and friends) run once over the\nwhole operation at the start of RemoveUnusedEnzymeOpsPass. That covers\neverything when AD ran at function scope: by the time any remover runs, a push\nand pop that merely hand a value from one point of a block to another are gone.\n\nWhen the differentiated code sits inside a region instead -- an init nested in a\nkernel body rather than at function scope -- it does not. The removers run in\npost order, and an inner one hands its caches outwards, so pairs that are now\nplainly forwardable appear in an enclosing op after that one simplification has\nalready happened. The enclosing remover then treats them as caches to pair with\na matching loop or branch, which for those pairs does not exist.\n\nRun the same patterns over the enzyme ops inside an op just before removing from\nit, for ops that have an init nested in them. applyOpPatternsGreedily rather\nthan applyPatternsGreedily, since the latter only accepts an IsolatedFromAbove\ntarget and these are loops and branches; the driver is passed as a listener so\nits worklist does not keep ops the patterns erase.\n\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "f9c069480da76873fd85d99a2716300bb209cf33",
      "tree": "ad70bdb8b7a5f71511e48dc28b5e53e90ddcfa5c",
      "parents": [
        "d9bbeb00f441590063ad4873c6949cd731467525"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sun Aug 02 17:45:52 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Aug 02 17:45:52 2026 -0500"
      },
      "message": "mlir: Only delegate loop-carried caches to the loop remover (#3049)\n\nannotateRegionOpsInLoops marks every non-looping region branch op inside a loop\nwith preserve_cache, standing its remover down so the enclosing loop\u0027s remover\ntakes the caches -- which is what lets mincut see them all together. That is\nonly valid for caches the loop remover can pair: it matches pushes in a forward\nloop with pops in the reverse one, so the cache has to be initialized outside\nthe loop to outlive an iteration.\n\nA cache whose init is inside the loop belongs to that iteration alone. Removal\nstops at the init\u0027s parent and never reaches the loop, so standing the region op\ndown leaves nothing to remove it: the pushes and pops survive to the loop\nremover, which finds no reverse loop holding the pops and dereferences a null\nop. Inlining a differentiated function into a loop body produces exactly this\nshape, bringing the ifs the callee cached across into the caller\u0027s loop.\n\nAnnotate only when no cache in the region op is loop-local. Both ends are\nchecked -- the push and the pop of such a cache sit in different region ops, so\nstanding either down strands it -- and the walk stops at nested loops, whose\ncaches are their own remover\u0027s business.\n\nAlso report the unpairable case in ForLikeEnzymeOpsRemover instead of running\ninto the null: with no loop holding the pops, every use of otherForOp below is\nthrough a null op, and the assert that would have caught it is compiled out in a\nrelease build.\n\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "d9bbeb00f441590063ad4873c6949cd731467525",
      "tree": "8e9008e926cda4d93b38ee8050afa10d42e86a28",
      "parents": [
        "4974fa08229b46bc8a7aebb91865afcc1f9cacaa"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sun Aug 02 11:29:40 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Aug 02 11:29:40 2026 -0500"
      },
      "message": "Properly consider ptrsizehit addr (#3047)\n\n* Properly consider ptrsizehit addr\n\n* fix\n\n* fmt\n\n* fix\n\n* fix"
    },
    {
      "commit": "4974fa08229b46bc8a7aebb91865afcc1f9cacaa",
      "tree": "4ff7140dce9c0fa7d157f90db0cb88f7cfed7f6e",
      "parents": [
        "24f30b2ad4f64b6b02ccc75b270337f2fbc2f6a5"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sat Aug 01 18:42:43 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 01 18:42:43 2026 -0500"
      },
      "message": "Bind IRBuilder calls that share an argument list to locals (#3046)\n\n* Bind IRBuilder calls that share an argument list to locals\n\nSame hazard as the MLIR side, one layer down: an IRBuilder Create* call built\ninside another call\u0027s argument list has no guaranteed evaluation order, so\nwhich instruction is inserted first is up to the compiler. It matters more\nhere, since most FileCheck tests in this repo pin generated LLVM IR.\n\n20 sites across AdjointGenerator, EnzymeLogic, FunctionUtils, GradientUtils\nand Utils. Each is bound to a local in the order the instructions were\npreviously emitted, so the generated IR is unchanged and no test expectation\nmoves.\n\ncheck_emission_order.py gains the container-iteration check from the\nEnzyme-JAX copy, so the two stay in sync. --include-llvm-builder is still not\nwired into CI: with IRBuilder counted as an emitter, four loops over\npointer-keyed containers (CacheUtility PotentialMins, FunctionUtils\nMTIReplacements and Preds, GradientUtils loopShadowZeroInits) show up, and\nthose change emission order when fixed, so they need their own pass.\n\nCo-Authored-By: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e\n\n* Confirm the receiver is a builder before calling it an emitter\n\nMatches the Enzyme-JAX copy: Create* keys off a receiver this file declares\nas a builder, static Instruction::Create factories are recognised, and a\nbuilder constructed inside a loop body (which anchors each op to its own item)\ndowngrades a container finding from error to warning.\n\nWith that, --include-llvm-builder leaves one error in enzyme/Enzyme:\nGradientUtils.cpp:3244, where the builder is constructed outside the loop so\ninstruction order really is iteration order.\n\nCo-Authored-By: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e\n\n* Iterate loopShadowZeroInits in insertion order and gate the LLVM builder check\n\nThe one remaining loop whose iteration order becomes instruction order: the\nbuilder is constructed outside it, so every iteration appends into the same\nblock. It is a SmallSetVector now.\n\nThe set is populated only by a commented-out insert, so this cannot change\nbehaviour today -- it is correct for whenever that code comes back, and it\nclears the last error blocking the check.\n\nWith that, the lint job runs --include-llvm-builder, so argument-evaluation\norder and pointer-keyed iteration are both gated for IRBuilder code as well.\n13 collect-only loops remain as warnings.\n\nVerified against the LLVM 15 lit suite: 1033 passed, 10 expectedly failed,\n5 failed, both with and without this branch\u0027s changes, and the same five\ntests either way (they fail on unmodified main too).\n\nCo-Authored-By: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "24f30b2ad4f64b6b02ccc75b270337f2fbc2f6a5",
      "tree": "adec47ace1b301ebfd21f6ac88631b0c2fcf39f8",
      "parents": [
        "cdad01eab824a7e0ea6df8293dae8ac77e25801a"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sat Aug 01 17:44:11 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 01 17:44:11 2026 -0500"
      },
      "message": "Add a lint job for order-dependent IR emission (#3045)\n\n* Bind op-creating calls to locals so IR emission order is deterministic\n\nSCFAutoDiffOpInterfaceImpl built several ops directly in the argument list of\nanother op-creating call (scf::ForOp bounds, arith::SelectOp operands, the\ncheckpointed induction-variable arithmetic). C++ leaves the evaluation order of\nfunction arguments unspecified, so which op gets inserted into the block first\nis the compiler\u0027s choice: the same source emits the ops in a different textual\norder depending on the toolchain. That makes the generated IR -- and any CHECK\nline pinning it -- build-dependent.\n\nBind each such op to a local first so the source fixes the order. No semantic\nchange: the same ops with the same operands are created, only the insertion\norder is now pinned.\n\nCo-Authored-By: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e\n\n* Add a lint job for order-dependent IR emission\n\nBuilding two or more ops directly in one argument list leaves their insertion\norder to the compiler, since C++ does not sequence argument evaluation. The\nemitted IR -- and any CHECK line pinning it -- then depends on the toolchain.\n\nenzyme/scripts/check_emission_order.py brace-matches every call expression and\nreports argument lists with two or more sibling op emitters (`X::create(...)`,\n`builder.create\u003cX\u003e(...)`, `rewriter.replaceOpWithNewOp\u003cX\u003e(...)`, and any\nfunction or lambda in the tree whose body does one of those). Lambda literals\npassed as arguments do not count: their body runs inside the callee, after\nargument evaluation. False positives can be suppressed with\n`// NOLINT(emission-order)`.\n\n--include-llvm-builder extends the same check to LLVM IRBuilder Create* calls;\nenzyme/Enzyme has 20 such sites today, so that mode is opt-in until they are\ncleaned up.\n\nCo-Authored-By: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "cdad01eab824a7e0ea6df8293dae8ac77e25801a",
      "tree": "7f22da6f84a5415cf486bf554a46d79387aa9fda",
      "parents": [
        "4c5c97a71cf2450692c78baa3ea8ca9398c22d10"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sat Aug 01 17:43:55 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 01 17:43:55 2026 -0500"
      },
      "message": "Bind op-creating calls to locals so IR emission order is deterministic (#3044)\n\nSCFAutoDiffOpInterfaceImpl built several ops directly in the argument list of\nanother op-creating call (scf::ForOp bounds, arith::SelectOp operands, the\ncheckpointed induction-variable arithmetic). C++ leaves the evaluation order of\nfunction arguments unspecified, so which op gets inserted into the block first\nis the compiler\u0027s choice: the same source emits the ops in a different textual\norder depending on the toolchain. That makes the generated IR -- and any CHECK\nline pinning it -- build-dependent.\n\nBind each such op to a local first so the source fixes the order. No semantic\nchange: the same ops with the same operands are created, only the insertion\norder is now pinned.\n\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "4c5c97a71cf2450692c78baa3ea8ca9398c22d10",
      "tree": "dfd56d794532fa66b8b6f6af53bfa770061c79db",
      "parents": [
        "a9b96ed28ed25bd9e393d7fd14778acef97505ed"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Sat Aug 01 15:30:04 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 01 15:30:04 2026 -0500"
      },
      "message": "mlir: Fix Binomial checkpointing schedule and support for mutable memory + memory space (#3042)\n\n* wip\n\n* more *fixes*\n\n* Index binomial checkpoints by the stack pointer, not the reverse IV\n\ncacheBinomial snapshotted each mutable outside-ref with cloneValue +\ninitAndPushCache inside outerFwd, which is bounded by the budget, so the cache\ngot `budget` pushes. reverseBinomial popped it at the top of revOuter, which is\nbounded by the trip count, so it got `numIters` pops. The push/pop lowering\nsizes the buffer from the push loop and indexes it with (revBound-1) - iv, so\nevery iteration past the budget read out of bounds -- garbage pointers handed to\nthe replay kernels and then freed, which on CUDA corrupted the allocator and\nsurfaced much later as a segfault in cudaEventQuery.\n\nReplace the implicit stack with an explicitly indexed buffer, mirroring what\nckptBufs/idxBuf already do:\n\n  - one memref\u003cbudget x refTy\u003e of clone *handles* per mutable ref, filled\n    eagerly (unrolled, so no loop-invariant allocation exists for hoisting or\n    alloca-promotion to collapse into one slot), pushed once outside outerFwd;\n  - taking a checkpoint copies into slot k rather than allocating;\n  - the reverse pops the buffer before revOuter and reads slot capo \u003d sp - 1,\n    never a function of the reverse induction variable;\n  - each ref gets a working clone the reverse owns and re-primes from slot capo,\n    because the replay writes through the ref and slot capo is re-read whenever\n    the remat placed finer checkpoints above it;\n  - the remat\u0027s re-place stores the mutable-ref snapshot alongside the state it\n    already wrote at slot acapo, so a re-placed slot cannot pair step-`pos`\n    state with a stale snapshot;\n  - shadows become a single push/pop: invertPointerM is loop-invariant, and it\n    had the same push/pop count mismatch;\n  - teardown frees the working clone, then every slot, then the buffer.\n\nReuse needs a copy-into-existing operation, so add copyValue to\nClonableTypeInterface: memref.copy for memrefs, llvm_ext.memcpy for pointers\nwith the extent from findPtrExtent(src) falling back to dst, since a handle\nloaded out of a buffer has no recoverable extent.\n\nFold the duplicated capture split into splitOutsideRefs and the hardcoded\nnumIterArgs+1 offsets into one BinomialCacheLayout, asserted on both sides. That\nremoves the variable-width mutable-ref region being re-derived in five places,\none of which tested isConstantValue on the popped value instead of the original\nref.\n\nlower-llvm-ext gains a memref.load edge in inferMemorySpace (buffer through\nenzyme.pop/push and pure views to its memref.alloc, then the values of that\nalloc\u0027s stores, which must agree), because the teardown frees loaded handles and\nwould otherwise abort the pass. Also refuse to alloca-promote an alloc with no\nvisible free, since such a pointer escapes into a buffer.\n\nThe two binomial mutable-memory tests asserted the buggy index\n(arith.subi %c9, %arg3 into a 4-slot buffer); rewritten to pin sp - 1. Adds an\n!llvm.ptr mutable-ref test covering the CUDA shape plus a --lower-llvm-ext RUN\nline for the space inference, and a budget \u003e trip-count test pinning the eager\nallocation.\n\nCo-Authored-By: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_01JPukfNcjWtDEom3UY1xDh9\n\n* binomial_progress: return the advance distance, not the repetition count\n\nThe op computed max{ j : C(j+budget-1, j) \u003c\u003d num_steps } -- the Revolve\n*repetition* count t, i.e. how many times each step gets recomputed. Every\ncaller used it as an advance distance in steps. Since t grows like\nnum_steps^(1/budget) rather than like num_steps, the resulting schedule was\nback-loaded: for trip 400 / budget 4 the advances came out [11, 26, 362, 1], so\ncheckpoints landed at steps 0, 11, 37, 399 and the 362-step stretch in between\nhad no interior checkpoint. With budgetRem \u003d\u003d 2 there and the old formula\nreturning n-1, the reverse pass replayed that whole stretch for every single\nstep -- 65990 replayed steps where the optimum is 2408, and a recompute factor\ngrowing like n^1.1 instead of n^(1/(budget-1)).\n\nReturn a point in the Revolve window instead. With beta(s,t) \u003d C(s+t,t) and t\nminimal such that beta(budget,t) \u003e\u003d num_steps, every advance in\n\n    [ num_steps - beta(budget-1, t) , beta(budget, t-1) ]\n\nattains that optimal t, and the window is non-empty by Pascal\u0027s rule. Take its\nmidpoint: either edge can collapse onto the clamp and spend a checkpoint on a\none-step advance, while the midpoint stays within ~2% of the dynamic-programming\noptimum for total recomputation (measured 1.00-1.08x across trip 20..1600 and\nbudgets 4..12, against 2.4-96x for the old formula).\n\nBoth new divisions are exact in integers: C(s+t,t) steps from C(s+t-1,t-1) by\n*(s+t)/t, and beta(s-1,t) \u003d beta*s/(s+t), beta(s,t-1) \u003d beta*t/(s+t).\n\nTwo boundary cases keep the callers\u0027 loop structure working unchanged, which is\nwhy no driver had to be touched:\n\n  - budget \u003c\u003d 1 advances the whole remainder. With one checkpoint left the\n    stretch is replayed from it anyway, and this is what makes the per-slot\n    advances sum to exactly the trip count across `budget` slots.\n  - the advance is capped at num_steps - (budget - 1), leaving a step for each\n    slot still to be placed. Without it the advances can exhaust the interval\n    before the slots run out, and a driver walking one slot per iteration then\n    records slots at a step past the end -- holding the final state rather than a\n    checkpoint, which showed up as gradients differing in the 4th significant\n    figure for trip/budget below ~3.\n\nThe guard for the degenerate cases has to be a branch rather than a select: with\nbudget \u003c\u003d 1 the loop body leaves beta at 1 and would never terminate.\n\nVerified by execution, not just by reading: the dynamic lowering was run through\nmlir-runner over num_steps in [2,1000] x budget in [2,12] and agrees with the\nfolder and with an independent reference on all 10989 points.\n\nLBM (nTimeSteps 800, budget 4): kernel launches 133580 -\u003e 6458, AD time\n40.59s -\u003e 3.69s, i.e. 2.5x plain reverse mode instead of 28x, at 2665 MiB\nagainst plain\u0027s 30404 MiB. Gradients unchanged at every size except\ntrip \u003c budget, which is not a supported configuration.\n\nCo-Authored-By: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_01JPukfNcjWtDEom3UY1xDh9\n\n* Clamp the checkpoint budget to the trip count unconditionally\n\nbudgetV bounds both the forward placement loop and the reverse stack pointer,\nbut was only clamped to min(budget, numIters) behind enzyme.use_safe_budgeting\n-- an attribute nothing in either repository ever sets, so the clamp was dead.\n\nWith a budget above the trip count the placement loop therefore ran more\niterations than there are steps. The advances hit the 1-step floor, the interval\nwas exhausted before the slots ran out, and the leftover slots were recorded at\na step past the last one -- holding the final state rather than a checkpoint.\nThe reverse pass starts at capo \u003d budgetV - 1, so it replayed from one of those\nstale slots and silently produced a wrong gradient.\n\nMaking the clamp unconditional is a no-op whenever budget \u003c\u003d trip count (the\nreplay counts are identical) and fixes every case below it. Buffers are still\nsized by the static budget, so the unused slots are still allocated and freed;\nonly the loop bounds change.\n\nMeasured on LBM against non-checkpointed reverse mode, gradients now agree\nexactly at every size tried. Previously wrong: trip 2 (-0.246577 vs -0.247052),\ntrip 3 (0.324146 vs 0.323983) at budget 4, and trip 10 (0.116377 vs 0.116144) at\nbudget 12. Large sizes are unchanged -- trip 400 still 6458 launches in 3.5s.\n\nThe budget-exceeds-trip test no longer needs to opt in, so drop the attribute\nfrom it; it now covers the default path.\n\nCo-Authored-By: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_01JPukfNcjWtDEom3UY1xDh9\n\n* remove unecessary changes\n\n* remove llvm_ext test\n\n* fmt\n\n* fix memory space propagation\n\n* mlir: Accept ranked tensors in enzyme.binomial_progress\n\nThe op accepted scalars and unranked tensors, so it rejected tensor\u003ci64\u003e --\na rank-0 *ranked* tensor, which is the form stablehlo callers use:\n\n  \u0027enzyme.binomial_progress\u0027 op operand #0 must be signless integer, index,\n  or unranked tensor thereof, but got \u0027tensor\u003ci64\u003e\u0027\n\nRelax the constraint from UnrankedTensorOf to TensorOf, which covers both\nranked and unranked, so the stablehlo path can emit this op directly instead\nof carrying a duplicate of the Revolve split. The change is strictly widening:\nscalar integers, index, tensor\u003ci64\u003e and tensor\u003c*xi64\u003e all verify.\n\nlower-enzyme-binomial-progress is unaffected -- it lowers the scalar form onto\nscf/arith and already skips every TensorType, ranked or not.\n\nCo-Authored-By: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e\nClaude-Session: https://claude.ai/code/session_01CYV2q9AJwCW3osyfZ6Ario\n\n* Use memory space in type\n\n---------\n\nCo-authored-by: Paul Berg \u003c9824244+Pangoraw@users.noreply.github.com\u003e\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "a9b96ed28ed25bd9e393d7fd14778acef97505ed",
      "tree": "83a3e9dd4702dd6dad337f1b2763cf6ab196cf43",
      "parents": [
        "6598bf0273524f420b11221b03c402b3e6e2ce8b"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Fri Jul 31 14:09:43 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 31 14:09:43 2026 -0500"
      },
      "message": "[TA] Don\u0027t recurse infinitely on self-referential constant globals (#3038)\n\n* [TA] Don\u0027t recurse infinitely on self-referential constant globals\n\ngetConstantAnalysis memoizes the type of a constant global only after its\ninitializer has been fully analyzed. An initializer that refers back to its\nown global therefore re-enters the analysis of that global with nothing\nmemoized yet, and recurses until the stack overflows:\n\n  @tab \u003d constant [1 x i64] [i64 sub (i64 0, i64 ptrtoint (ptr @tab to i64))]\n\nSuch relative-pointer jump tables are emitted by LLVM when lowering a switch\nover three or more constant tables, so this is reachable from ordinary source\n(see the Rust reproducer in the issue).\n\nDetect a cyclic initializer up front and fall back to treating the global as\nan ordinary pointer of unknown pointee, which is what a non-constant global\nalready gets. Non-cyclic globals are unaffected.\n\nFixes #3027\n\nCo-Authored-By: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e\n\n* [TA] Break constant global cycles by seeding the type, not by bailing\n\nReplace the up front cycle detection with the simpler observation that a\nglobal is a pointer regardless of what its initializer says. Recording that\nmuch as enzyme_type before analyzing the initializer makes a self referential\ninitializer terminate on the existing metadata early return.\n\nSince the seed is an unconditionally true fact rather than a guess, everything\ndeduced from it stays sound, and the seed is overwritten with the full type\nonce the initializer has been analyzed. This is both smaller and strictly more\nprecise than bailing out: a self referential aggregate keeps the types of its\nremaining fields instead of decaying to an opaque pointer. The test covers\nthis with a struct holding both a self pointer and a double, whose deduced\ntype is now {0: Pointer, 8: Float@double}.\n\nCo-Authored-By: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: William S. Moses \u003cmoses.williamsteven@gmail.com\u003e\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "6598bf0273524f420b11221b03c402b3e6e2ce8b",
      "tree": "59cbb83e7c4b232ab8303859afdc7839789c9ac7",
      "parents": [
        "da4a9aa48ba08056d5bd3f7a5168b5e7a66a2d0e"
      ],
      "author": {
        "name": "Jacob Mai Peng",
        "email": "jacobmpeng@gmail.com",
        "time": "Fri Jul 31 15:00:38 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 31 14:00:38 2026 -0500"
      },
      "message": "mlir: fix overly conservative check when marking readonly (#3039)"
    },
    {
      "commit": "da4a9aa48ba08056d5bd3f7a5168b5e7a66a2d0e",
      "tree": "08303ac195e823a86d3393467d3ec0d5b860a1a0",
      "parents": [
        "8335ea9368f4a25cc53924eb0e948bb41a8ccaba"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Fri Jul 31 10:19:14 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 31 10:19:14 2026 -0500"
      },
      "message": "Don\u0027t add non-instructions to the min-cut recompute graph (#3035)\n\n`pushLoopyPHIPreheader` walks the preheader incoming value of a loopy\nreduction PHI and inserts it into `Intermediates`. That set is\ncontractually a set of instructions -- `GradientUtils::computeMinCache`\niterates it and does `cast\u003cInstruction\u003e` on each element.\n\nWhen the reduction PHI starts from a constant (or an argument), e.g.\n\n  %acc \u003d phi double [ 0.000000e+00, %entry ], [ %sel, %loop ]\n  %sel \u003d select i1 %cmp, double %acc, double %ld\n\nthe constant was inserted into `Intermediates`, tripping\n\"cast\u003cTy\u003e() argument of incompatible type!\" in computeMinCache.\n\nConstants and arguments are always available in the reverse pass and\nthus never need to be cached or recomputed, so stop the walk when the\nstart value is not an instruction.\n\nFixes #3014\n\nCo-authored-by: Claude Opus 5 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "8335ea9368f4a25cc53924eb0e948bb41a8ccaba",
      "tree": "6b8f880353f1fad9b1bc9afb5f481e1fee758d3d",
      "parents": [
        "a90dc1da109aedf466299a7a23c9a5378ca69076"
      ],
      "author": {
        "name": "Christopher Albert",
        "email": "albert@tugraz.at",
        "time": "Fri Jul 31 06:44:21 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 30 23:44:21 2026 -0500"
      },
      "message": "Relax dynamic-loop derivative signature check (#3021)"
    },
    {
      "commit": "a90dc1da109aedf466299a7a23c9a5378ca69076",
      "tree": "fe7efbd8710ad85755fbc3898a7a571cdefa4de9",
      "parents": [
        "755e0cb14ba0f83f5abb17bea77e17d9e4711d67"
      ],
      "author": {
        "name": "Christopher Albert",
        "email": "albert@tugraz.at",
        "time": "Fri Jul 31 06:43:54 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 30 23:43:54 2026 -0500"
      },
      "message": "Avoid getTerminator assertion on incomplete derivative block (#3023)\n\n* Avoid querying terminator on incomplete block\n\n* Use typed pointer in unreachable callee test"
    },
    {
      "commit": "755e0cb14ba0f83f5abb17bea77e17d9e4711d67",
      "tree": "2113365728cc947a69c5a052e3916d3aeb2fd666",
      "parents": [
        "e2a7fced454e5b2452883be456156d743f9acb95"
      ],
      "author": {
        "name": "Jacob Mai Peng",
        "email": "jacobmpeng@gmail.com",
        "time": "Thu Jul 30 22:49:03 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 30 21:49:03 2026 -0500"
      },
      "message": "mlir: loop-invariant cache for cached allocas (#3030)"
    },
    {
      "commit": "e2a7fced454e5b2452883be456156d743f9acb95",
      "tree": "5d28a424754d5b52c389f1caf5d78ee3d763ef6a",
      "parents": [
        "74e17f5b5d26aa8ffd148b64e937060f34968f0e"
      ],
      "author": {
        "name": "Aiden Grossman",
        "email": "aidengrossman@google.com",
        "time": "Thu Jul 30 16:22:31 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 30 18:22:31 2026 -0500"
      },
      "message": "fix (#3033)"
    },
    {
      "commit": "74e17f5b5d26aa8ffd148b64e937060f34968f0e",
      "tree": "d75a2251129e1d173a01a815fc124540c6bd522a",
      "parents": [
        "f3955b21ef5a3f8b076e03d7d8dd1b0b15d64fc7"
      ],
      "author": {
        "name": "Aiden Grossman",
        "email": "aidengrossman@google.com",
        "time": "Thu Jul 30 16:21:55 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 30 18:21:55 2026 -0500"
      },
      "message": "Remove deprecated use of make_scope_exit (#3034)\n\n* Remove deprecated use of make_scope_exit\n\nWrap it in an ifdef. This was removed in upstream LLVM recently\n(289a4568f7d18d1d42687f3a2fc2c72bcbadaa28).\n\n* feedback"
    },
    {
      "commit": "f3955b21ef5a3f8b076e03d7d8dd1b0b15d64fc7",
      "tree": "f4935bfeb2dadb6c8969eee51f8f922f99ddd4fd",
      "parents": [
        "0ed71b22bb1f0952c9e85fea0ab9dfcf2f80feb2"
      ],
      "author": {
        "name": "Aiden Grossman",
        "email": "agrossman154@yahoo.com",
        "time": "Thu Jul 30 08:27:45 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 30 10:27:45 2026 -0500"
      },
      "message": "Prefer OptionalPassInfoMixin (#2996)\n\n* Prefer OptionalPassInfoMixin\n\nThis became preferred in LLVM 23 and PassInfoMixin will be moving to a\ndetail namespace in LLVM 24, which means compilation errors without this\npatch.\n\n* version support\n\n* fix"
    },
    {
      "commit": "0ed71b22bb1f0952c9e85fea0ab9dfcf2f80feb2",
      "tree": "1d4bdfee5db834528957b03ce67382a6b46d9816",
      "parents": [
        "e4afe5484901b1e5645cd6f71ec8263030e4b233"
      ],
      "author": {
        "name": "Jacob Mai Peng",
        "email": "jacobmpeng@gmail.com",
        "time": "Wed Jul 29 14:17:46 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 29 13:17:46 2026 -0500"
      },
      "message": "[mlir] Make non-looping, non-nested RegionBranchOpInterface ops movable by min-cut (#3024)\n\n* Recur on isMovable when checking internal ops\n* Handle nested ifs, add values defined outside of RegionBranch ops to\n  graph"
    },
    {
      "commit": "e4afe5484901b1e5645cd6f71ec8263030e4b233",
      "tree": "e1046559e1d6911bba0296350eff961c42c4aafe",
      "parents": [
        "65d455a2bdb6f4f045dbaeccfe63d0205bfb16c2"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Tue Jul 28 17:17:49 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 28 17:17:49 2026 -0500"
      },
      "message": "Speed up large constant ta (#3002)\n\n* Speed up large constant ta\n\n* fixup\n\n* Fix brittle TBAA checks in string activity test\n\n* Relax TBAA metadata IDs in string activity test\n\n* Fix brittle metadata checks in failing ReverseMode tests\n\n* Relax brittle metadata check in bitcastfn test\n\n---------\n\nCo-authored-by: copilot-swe-agent[bot] \u003c198982749+Copilot@users.noreply.github.com\u003e"
    },
    {
      "commit": "65d455a2bdb6f4f045dbaeccfe63d0205bfb16c2",
      "tree": "e7499bf88c632c4eacb978d41112076bf7d44cf2",
      "parents": [
        "76e110a9108a89677fe04747f7b1a260b576a496"
      ],
      "author": {
        "name": "Jacob Mai Peng",
        "email": "jacobmpeng@gmail.com",
        "time": "Tue Jul 28 15:30:20 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 28 19:30:20 2026 +0000"
      },
      "message": "mlir: mark loads of readonly pointers as movable during mincut (#2734)"
    },
    {
      "commit": "76e110a9108a89677fe04747f7b1a260b576a496",
      "tree": "648e571fa093e7b47b5d2c7cd155b69f2514e691",
      "parents": [
        "a7c8be5f892c46df8d7f6a9f33dc4e569fc91e17"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Jul 27 22:17:09 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 27 22:17:09 2026 -0500"
      },
      "message": "[MLIR][Mincut] Prefer last equal sized element in chain to cache (#3017)"
    },
    {
      "commit": "a7c8be5f892c46df8d7f6a9f33dc4e569fc91e17",
      "tree": "6186f4b728393ac93b148bf7d4417abd605d5ba0",
      "parents": [
        "891aa61e8ec2a67274a45a1938f21b46192779db"
      ],
      "author": {
        "name": "Jacob Mai Peng",
        "email": "jacobmpeng@gmail.com",
        "time": "Mon Jul 27 18:52:10 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 27 17:52:10 2026 -0500"
      },
      "message": "mlir: mark outlined functions from enzyme.autodiff_region private (#3015)"
    },
    {
      "commit": "891aa61e8ec2a67274a45a1938f21b46192779db",
      "tree": "989fed1ba0b6b0727a0652f542badd1dcb18de9f",
      "parents": [
        "8551ebbedbd8bd034fd3c592f52443258c702b2b"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Jul 27 17:51:50 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 27 17:51:50 2026 -0500"
      },
      "message": "[MLIR][Mincut] Add test for optimality issue for mincut (#3013)\n\n* [MLIR][Mincut] Add test for optimality issue for mincut\n\n* fix\n\n* [MLIR][Mincut] Node-split the graph so caching a value costs one\n\nThe EnzymeMLIR mincut (RemovalUtils.cpp) built its graph with one node per\nvalue and an edge per use, so a value consumed by N operations was charged\nN to cache (one per outgoing edge). The min-cut therefore preferred caching\nseveral downstream values over a single, cheaper upstream one -- e.g. an\ninput reused by many operations -- caching more across the loop boundary\nthan necessary. This was a memory-optimality bug; gradients were correct.\n\nFix it with the standard node-splitting reduction, as already done by the\nLLVM Enzyme mincut in DifferentialUseAnalysis.cpp: every value V is split\ninto V_in -\u003e V_out joined by a single unit-capacity edge, so all of V\u0027s flow\nfunnels through it and caching V costs exactly one regardless of fan-out.\nOperations are left unsplit (they are free routers), so a min-cut edge may\nbe an internal, use, or def edge; each still identifies exactly one value to\ncache.\n\nThe split is confined to a new self-contained minCutValues() helper used\nonly for the max-flow computation; the downstream cache refinement/cloning\nstill operate on the original value/op graph and are unchanged.\n\nUpdate scf_for_mincut_multiuse.mlir to check the now-optimal single cache.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* [MLIR][Mincut] Assert operations are never split in the flow graph\n\nRoute all flow-node construction through makeFlowNode(), which asserts the\ncore invariant of the node-splitting reduction: only values are split into\nan incoming/outgoing pair. An operation has a single endpoint and must never\nappear with outgoing\u003dtrue.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* [MLIR][Mincut] Split makeFlowNode into typed overloads\n\nReplace the single makeFlowNode(Node, bool) + runtime assert with two typed\noverloads that enforce the invariant by construction:\n\n  - makeFlowNode(Operation *)     -\u003e the operation\u0027s single node (no split)\n  - makeFlowNode(Value, bool)     -\u003e a value\u0027s incoming/outgoing endpoint\n\nAn operation therefore can never be given two endpoints and a value can never\nbe constructed without specifying its side. The flowIn/flowOut helpers keep\ndispatching on the type-erased graph Node to the appropriate overload.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* [MLIR][Mincut] Inline addSplitEdge lambda into graph build\n\nInline the internal-split-edge helper directly at its two use sites (tail and\nhead of each edge), which is easier to step through in a debugger.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* [MLIR][Mincut] Fold the split bit into Node; drop separate FlowNode\n\nMerge the node-split \"outgoing\" side selector directly into the graph `Node`\n(packed with the op/value pointer in a PointerIntPair, so Node stays\npointer-sized and pointer-like). The min-cut flow graph is now a plain\n`Graph` over the same `Node`, so the existing bfs/dump/dumpGraphviz utilities\napply to it -- dumpGraphviz now renders the split as [val:in]/[val:out]\nnodes, which is handy for debugging.\n\nNode exposes isValue()/isOperation()/getValue()/getOperation()/dynValue()\nand construction stays via Node(Operation*) (single node) and\nNode(Value, bool) (split endpoint). DenseMap tracks empty/tombstone buckets\nout-of-band in this LLVM, so DenseMapInfo\u003cNode\u003e only needs getHashValue/\nisEqual (matching raw-pointer / PointerIntPair infos).\n\nminCutValues drops the bespoke FlowNode/FlowGraph/flowBFS and reuses bfs()\ndirectly; the max-flow now mirrors the original in-place loop but on the\nnode-split graph.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* [MLIR][Mincut] clang-format\n\nReflow comments to satisfy clang-format (v16, LLVM style) as run by the\nClang-Format CI.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "8551ebbedbd8bd034fd3c592f52443258c702b2b",
      "tree": "5528195bd4fcffde34c68f89d84b4e3b6b4020c3",
      "parents": [
        "1316e4268635285bf8dda22053112f14b65c974a"
      ],
      "author": {
        "name": "Paul Berg",
        "email": "naydex.mc+github@gmail.com",
        "time": "Mon Jul 27 20:34:10 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 27 18:34:10 2026 +0000"
      },
      "message": "Fixes for mutable memory in scf.for reverse (#3009)\n\n* Fixes for mutable memory in scf.for reverse\n\n* remove dyn dims stuff\n\n* Revert \"remove dyn dims stuff\"\n\nThis reverts commit e0f08d5eaa66d6ed28a69849766fb0cc52393f0d.\n\n* also fix inner iv rematerialization"
    },
    {
      "commit": "1316e4268635285bf8dda22053112f14b65c974a",
      "tree": "96e0f26ef45590d966c3e6a2e90cb1f800f848d2",
      "parents": [
        "777ceded34617370d43dbed00f3ee79de493f1c2"
      ],
      "author": {
        "name": "William Moses",
        "email": "gh@wsmoses.com",
        "time": "Mon Jul 27 09:11:01 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 27 09:11:01 2026 -0500"
      },
      "message": "Update to later llvm (#3007)\n\n* Update to later llvm\n\n* Update MLIR tests for arith.addf(arith.negf(x), y) -\u003e arith.subf(y, x) canonicalization"
    }
  ],
  "next": "777ceded34617370d43dbed00f3ee79de493f1c2"
}
