Skip to content

Fix dangling tracker references in early-return recursive functions - #1928

Open
Vedant2005goyal wants to merge 1 commit into
vgvassilev:masterfrom
Vedant2005goyal:gradbench
Open

Fix dangling tracker references in early-return recursive functions#1928
Vedant2005goyal wants to merge 1 commit into
vgvassilev:masterfrom
Vedant2005goyal:gradbench

Conversation

@Vedant2005goyal

Copy link
Copy Markdown
Contributor

This PR fixes a segmentation fault encountered while differentiating recursive functions with early returns (e.g., backtracking type functions). When differentiating such functions Clad wraps the reverse sweep in a lambda (auto _rev0 = [&]). Previously, clad::restore_tracker declarations and ValueAndAdjoint temporaries generated for operator[] calls were emitted inside nested forward-sweep blocks and it leads to null pointer dereferencing, resulting in a segmentation fault.

@github-actions

Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@github-actions

Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@github-actions

Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@github-actions

Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@github-actions

Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@github-actions

Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@github-actions

Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@github-actions

Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@@ -0,0 +1,54 @@
// RUN: %cladclang -std=c++17 -O0 -I%S/../../include/ %s -o %t

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That test does not feel complete. Here is what I think we will need:

  --- a/test/Gradient/RecursiveFunctionEarlyReturn.C
  +++ b/test/Gradient/RecursiveFunctionEarlyReturn.C
  @@
  -// RUN: %cladclang -std=c++17 -O0 -I%S/../../include/ %s -o %t
  -// RUN: %t 
  +// RUN: %cladclang -std=c++17 -O0 -I%S/../../include/ %s -o %t 2>&1 | %filecheck %s
  +// RUN: %t | %filecheck_exec %s
   #include "clad/Differentiator/Differentiator.h"
  +#include <cstdio>
   #include <vector>
  @@
  -// CHECK: void func_grad(
  +// CHECK: void func_grad_0_2(
   // CHECK: clad::restore_tracker _tracker0 = {};
   // CHECK: auto _rev0 = [&] {
   // CHECK: _tracker0.restore();
  @@ (in main, after d_fn.execute)
  +  printf("d_A = {%.2f, %.2f, %.2f}\n", d_A[0], d_A[1], d_A[2]);
  +  // CHECK-EXEC: d_A = {74.00, 13.00, 2.00}
  +  printf("d_v = {%.2f}\n", d_v[0]);
  +  // CHECK-EXEC: d_v = {6.00}
  @@ (after d_func1.execute)
  +  printf("d_x = %.2f\n", d_x); // func1(x,3) = 8x
  +  // CHECK-EXEC: d_x = 8.00
  -}
  \ No newline at end of file
  +}

// CHECK-NEXT: clad::restore_tracker _tracker0 = {};
// CHECK-NEXT: MyStructWrapper _d_s = {{.*0., 0..*}};
// CHECK-NEXT: MyStructWrapper s;
// CHECK-NEXT: clad::restore_tracker _tracker0 = {};

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests do not have early returns and yet we change them -- we should sccope the change only to things with early returns.

@Vedant2005goyal Vedant2005goyal Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @vgvassilev for the review.
I restored those tests to their original form

// CHECK-NEXT: float _t3;
// CHECK-NEXT: float _t4;
// CHECK-NEXT: float _t3 = 0.F;
// CHECK-NEXT: float _t4 = 0.F;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to initialize these?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They were done so to prevent segmentation fault in CI's since Hessians inherently use early returns for their reverse sweeps.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems like a potentially bigger problem... Why only these two get initialized?

@Vedant2005goyal
Vedant2005goyal force-pushed the gradbench branch 3 times, most recently from f4d41d5 to 5c00df6 Compare July 31, 2026 13:21
@github-actions

Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

…rns. Previously, Clad generated reverse-sweep lambdas that captured uninitialized memory, causing a segmentation fault.
@github-actions

Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants