From 704258461e904c37e94e78e0fe70e2ad5ae86def Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 23 Apr 2026 10:36:27 -0400 Subject: [PATCH 1/8] fix umax issue --- enzyme/Enzyme/GradientUtils.cpp | 7 ++ .../Enzyme/ReverseMode/loosetypes_umax.ll | 84 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll diff --git a/enzyme/Enzyme/GradientUtils.cpp b/enzyme/Enzyme/GradientUtils.cpp index bf4fb3c9e93..40b1d9e1e2e 100644 --- a/enzyme/Enzyme/GradientUtils.cpp +++ b/enzyme/Enzyme/GradientUtils.cpp @@ -6573,6 +6573,13 @@ end:; return Constant::getNullValue(getShadowType(oval->getType())); } + if (looseTypeAnalysis && oval->getType()->isIntOrIntVectorTy()) { + auto *shadow = Constant::getNullValue(getShadowType(oval->getType())); + invertedPointers.insert( + std::make_pair((const Value *)oval, InvertedPointerVH(this, shadow))); + return shadow; + } + if (CustomErrorHandler) { std::string str; raw_string_ostream ss(str); diff --git a/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll b/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll new file mode 100644 index 00000000000..7a8d8f8ffeb --- /dev/null +++ b/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll @@ -0,0 +1,84 @@ +; RUN: %opt < %s %newLoadEnzyme -passes="enzyme,function(mem2reg,instsimplify)" -enzyme-preopt=false -enzyme-loose-types -S | FileCheck %s +; +; Test that reverse-mode AD with -enzyme-loose-types handles @llvm.umax.i32 +; intrinsic calls on integer values without crashing in invertPointerM. +; +; The pattern: a struct passed via enzyme_dup contains both float* and i32* +; fields. The function does active float computations, derives an i32 flag +; from a float comparison, then uses load -> @llvm.umax.i32 -> store on the +; i32* field (SerialOperations::AtomicMax pattern). +; +; With loose-types, visitCommonStore calls invertPointerM on the i32 result of +; @llvm.umax.i32. Without the fix, this hits: +; assert(0 && "cannot find deal with ptr that isnt arg") +; +; The fix returns a zero shadow for integer values under loose-types analysis. + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare i32 @llvm.umax.i32(i32, i32) + +define void @compute_alpha(ptr noalias %op, i32 %face_idx, ptr noalias %nbr) { +entry: + %out_gep = getelementptr i8, ptr %op, i64 0 + %out_ptr = load ptr, ptr %out_gep, align 8 + %flags_gep = getelementptr i8, ptr %op, i64 8 + %flags_ptr = load ptr, ptr %flags_gep, align 8 + %clip_gep = getelementptr i8, ptr %op, i64 16 + %clip_val = load float, ptr %clip_gep, align 4 + %idx = zext i32 %face_idx to i64 + %face_gep = getelementptr float, ptr %out_ptr, i64 %idx + %face_val = load float, ptr %face_gep, align 4 + %dot = fmul float %face_val, %clip_val + %cmp = fcmp ole float %dot, 0.0 + br i1 %cmp, label %left_handed, label %normal + +left_handed: + br label %merge + +normal: + br label %merge + +merge: + %result = phi float [ %clip_val, %left_handed ], [ %dot, %normal ] + %flag = phi i32 [ 1, %left_handed ], [ 0, %normal ] + store float %result, ptr %face_gep, align 4 + %n0 = load i32, ptr %nbr, align 4 + %n0_ext = zext i32 %n0 to i64 + %flags_elem = getelementptr i32, ptr %flags_ptr, i64 %n0_ext + %old_flag = load i32, ptr %flags_elem, align 4 + %new_flag = tail call i32 @llvm.umax.i32(i32 %old_flag, i32 %flag) + store i32 %new_flag, ptr %flags_elem, align 4 + ret void +} + +define void @caller(ptr %op, ptr %d_op, i32 %face_idx, ptr %nbr) { +entry: + call void (...) @__enzyme_autodiff( + ptr @compute_alpha, + metadata !"enzyme_dup", ptr %op, ptr %d_op, + metadata !"enzyme_const", i32 %face_idx, + metadata !"enzyme_const", ptr %nbr + ) + ret void +} + +declare void @__enzyme_autodiff(...) + +; CHECK: define internal void @diffecompute_alpha(ptr noalias +; CHECK-SAME: %op, ptr +; CHECK-SAME: %"op'" +; CHECK-SAME: i32 %face_idx, ptr noalias +; CHECK-SAME: %nbr) + +; Verify the primal block correctly computes and stores i32 umax with zero shadow +; CHECK: %new_flag = tail call i32 @llvm.umax.i32(i32 %old_flag, i32 %flag) +; CHECK-NEXT: store i32 0, ptr %"flags_elem'ipg" +; CHECK-NEXT: store i32 %new_flag, ptr %flags_elem + +; Verify the reverse block correctly propagates float derivatives +; CHECK: invertentry: +; CHECK: %[[m0:.+]] = fmul fast float %[[dres:.+]], %clip_val +; CHECK: %[[m1:.+]] = fmul fast float %[[dres]], %face_val +; CHECK: ret void From 7fb4d1983a92bdfe5539e814046aeae29253ab5d Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 23 Apr 2026 11:50:07 -0400 Subject: [PATCH 2/8] update the tests --- enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll b/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll index 7a8d8f8ffeb..68fdf23683c 100644 --- a/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll +++ b/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll @@ -1,4 +1,6 @@ -; RUN: %opt < %s %newLoadEnzyme -passes="enzyme,function(mem2reg,instsimplify)" -enzyme-preopt=false -enzyme-loose-types -S | FileCheck %s +; RUN: if [ %llvmver -lt 16 ]; then %opt < %s %loadEnzyme -enzyme-preopt=false -enzyme -mem2reg -instsimplify -S -enzyme-loose-types | FileCheck %s; fi +; RUN: %opt < %s %newLoadEnzyme -enzyme-preopt=false -passes="enzyme,function(mem2reg,instsimplify)" -S -enzyme-loose-types | FileCheck %s +; UNSUPPORTED: llvm16 ; ; Test that reverse-mode AD with -enzyme-loose-types handles @llvm.umax.i32 ; intrinsic calls on integer values without crashing in invertPointerM. From f9381992dccff603fe8b099feda40129a8f322cb Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 23 Apr 2026 12:56:02 -0400 Subject: [PATCH 3/8] update the test --- enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll b/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll index 68fdf23683c..33b0b68bf82 100644 --- a/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll +++ b/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll @@ -1,6 +1,5 @@ -; RUN: if [ %llvmver -lt 16 ]; then %opt < %s %loadEnzyme -enzyme-preopt=false -enzyme -mem2reg -instsimplify -S -enzyme-loose-types | FileCheck %s; fi +; RUN: if [ %llvmver -ne 16 ]; then %opt < %s %loadEnzyme -enzyme-preopt=false -enzyme -mem2reg -instsimplify -S -enzyme-loose-types | FileCheck %s; fi ; RUN: %opt < %s %newLoadEnzyme -enzyme-preopt=false -passes="enzyme,function(mem2reg,instsimplify)" -S -enzyme-loose-types | FileCheck %s -; UNSUPPORTED: llvm16 ; ; Test that reverse-mode AD with -enzyme-loose-types handles @llvm.umax.i32 ; intrinsic calls on integer values without crashing in invertPointerM. From 5d1ed803a37d41d78502cddc4e7e8194325095b3 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 23 Apr 2026 13:06:50 -0400 Subject: [PATCH 4/8] update the test --- enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll b/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll index 33b0b68bf82..8dcf7ac253e 100644 --- a/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll +++ b/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll @@ -1,5 +1,5 @@ -; RUN: if [ %llvmver -ne 16 ]; then %opt < %s %loadEnzyme -enzyme-preopt=false -enzyme -mem2reg -instsimplify -S -enzyme-loose-types | FileCheck %s; fi -; RUN: %opt < %s %newLoadEnzyme -enzyme-preopt=false -passes="enzyme,function(mem2reg,instsimplify)" -S -enzyme-loose-types | FileCheck %s +; RUN: if [ %llvmver -lt 16 ]; then %opt < %s %loadEnzyme -enzyme-preopt=false -enzyme -mem2reg -instsimplify -S -enzyme-loose-types | FileCheck %s; fi +; RUN: if [ %llvmver -ne 16 ]; then %opt < %s %newLoadEnzyme -enzyme-preopt=false -passes="enzyme,function(mem2reg,instsimplify)" -S -enzyme-loose-types | FileCheck %s; fi ; ; Test that reverse-mode AD with -enzyme-loose-types handles @llvm.umax.i32 ; intrinsic calls on integer values without crashing in invertPointerM. From 94a91ce8a73cb302b218e5bc9ef8b2245a072510 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Wed, 29 Apr 2026 15:38:55 -0400 Subject: [PATCH 5/8] resolve the comments Co-authored-by: Copilot --- enzyme/Enzyme/GradientUtils.cpp | 35 +++++++++++++++---- .../Enzyme/ReverseMode/loosetypes_umax.ll | 8 +++-- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/enzyme/Enzyme/GradientUtils.cpp b/enzyme/Enzyme/GradientUtils.cpp index 40b1d9e1e2e..b9eac9e6b15 100644 --- a/enzyme/Enzyme/GradientUtils.cpp +++ b/enzyme/Enzyme/GradientUtils.cpp @@ -6323,6 +6323,34 @@ Value *GradientUtils::invertPointerM(Value *const oval, IRBuilder<> &BuilderM, switch (II->getIntrinsicID()) { default: goto end; +#if LLVM_VERSION_MAJOR >= 12 + case Intrinsic::smax: + case Intrinsic::smin: + case Intrinsic::umax: + case Intrinsic::umin: { + Value *val0 = invertPointerM(II->getArgOperand(0), bb, nullShadow); + Value *val1 = invertPointerM(II->getArgOperand(1), bb, nullShadow); + assert(val0->getType() == val1->getType()); + + auto rule = [&](Value *val0, Value *val1) { + Value *args[] = {val0, val1}; + auto shadow = cast( + bb.CreateCall(II->getCalledFunction(), args, + II->getName() + "'ipbi")); + shadow->setAttributes(II->getAttributes()); + shadow->setCallingConv(II->getCallingConv()); + shadow->setTailCallKind(II->getTailCallKind()); + shadow->setDebugLoc(getNewFromOriginal(II->getDebugLoc())); + return shadow; + }; + + Value *shadow = applyChainRule(II->getType(), bb, rule, val0, val1); + + invertedPointers.insert( + std::make_pair((const Value *)oval, InvertedPointerVH(this, shadow))); + return shadow; + } +#endif #if LLVM_VERSION_MAJOR < 20 case Intrinsic::nvvm_ldg_global_i: case Intrinsic::nvvm_ldg_global_p: @@ -6573,13 +6601,6 @@ end:; return Constant::getNullValue(getShadowType(oval->getType())); } - if (looseTypeAnalysis && oval->getType()->isIntOrIntVectorTy()) { - auto *shadow = Constant::getNullValue(getShadowType(oval->getType())); - invertedPointers.insert( - std::make_pair((const Value *)oval, InvertedPointerVH(this, shadow))); - return shadow; - } - if (CustomErrorHandler) { std::string str; raw_string_ostream ss(str); diff --git a/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll b/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll index 8dcf7ac253e..6741dc3a5b5 100644 --- a/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll +++ b/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll @@ -13,7 +13,8 @@ ; @llvm.umax.i32. Without the fix, this hits: ; assert(0 && "cannot find deal with ptr that isnt arg") ; -; The fix returns a zero shadow for integer values under loose-types analysis. +; The fix handles llvm.umax as a binop-like intrinsic during shadow +; reconstruction, instead of falling through to the generic no-shadow path. target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" @@ -73,9 +74,10 @@ declare void @__enzyme_autodiff(...) ; CHECK-SAME: i32 %face_idx, ptr noalias ; CHECK-SAME: %nbr) -; Verify the primal block correctly computes and stores i32 umax with zero shadow +; Verify the primal block computes both the original and shadow i32 umax values. ; CHECK: %new_flag = tail call i32 @llvm.umax.i32(i32 %old_flag, i32 %flag) -; CHECK-NEXT: store i32 0, ptr %"flags_elem'ipg" +; CHECK-NEXT: %[[NEW_FLAG_IP:.+]] = {{(tail )?}}call i32 @llvm.umax.i32(i32 %{{.+}}, i32 %{{.+}}) +; CHECK-NEXT: store i32 %[[NEW_FLAG_IP]], ptr %"flags_elem'ipg" ; CHECK-NEXT: store i32 %new_flag, ptr %flags_elem ; Verify the reverse block correctly propagates float derivatives From bfc173e96debff5ed658b2ac73c8b0d17c4905dd Mon Sep 17 00:00:00 2001 From: Min Xu Date: Wed, 29 Apr 2026 15:47:26 -0400 Subject: [PATCH 6/8] clang-format --- enzyme/Enzyme/GradientUtils.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/enzyme/Enzyme/GradientUtils.cpp b/enzyme/Enzyme/GradientUtils.cpp index b9eac9e6b15..ff815291b17 100644 --- a/enzyme/Enzyme/GradientUtils.cpp +++ b/enzyme/Enzyme/GradientUtils.cpp @@ -6334,9 +6334,8 @@ Value *GradientUtils::invertPointerM(Value *const oval, IRBuilder<> &BuilderM, auto rule = [&](Value *val0, Value *val1) { Value *args[] = {val0, val1}; - auto shadow = cast( - bb.CreateCall(II->getCalledFunction(), args, - II->getName() + "'ipbi")); + auto shadow = cast(bb.CreateCall( + II->getCalledFunction(), args, II->getName() + "'ipbi")); shadow->setAttributes(II->getAttributes()); shadow->setCallingConv(II->getCallingConv()); shadow->setTailCallKind(II->getTailCallKind()); From 4f86be7445a46cb09fe0596463a20ab4dac2d0b1 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Wed, 29 Apr 2026 16:55:05 -0400 Subject: [PATCH 7/8] update --- enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll b/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll index 6741dc3a5b5..fc5a72c757f 100644 --- a/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll +++ b/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll @@ -1,5 +1,5 @@ ; RUN: if [ %llvmver -lt 16 ]; then %opt < %s %loadEnzyme -enzyme-preopt=false -enzyme -mem2reg -instsimplify -S -enzyme-loose-types | FileCheck %s; fi -; RUN: if [ %llvmver -ne 16 ]; then %opt < %s %newLoadEnzyme -enzyme-preopt=false -passes="enzyme,function(mem2reg,instsimplify)" -S -enzyme-loose-types | FileCheck %s; fi +; RUN: if [ %llvmver -ge 15 ]; then %opt < %s %newLoadEnzyme -enzyme-preopt=false -passes="enzyme,function(mem2reg,instsimplify)" -S -enzyme-loose-types | FileCheck %s; fi ; ; Test that reverse-mode AD with -enzyme-loose-types handles @llvm.umax.i32 ; intrinsic calls on integer values without crashing in invertPointerM. @@ -74,11 +74,11 @@ declare void @__enzyme_autodiff(...) ; CHECK-SAME: i32 %face_idx, ptr noalias ; CHECK-SAME: %nbr) -; Verify the primal block computes both the original and shadow i32 umax values. -; CHECK: %new_flag = tail call i32 @llvm.umax.i32(i32 %old_flag, i32 %flag) -; CHECK-NEXT: %[[NEW_FLAG_IP:.+]] = {{(tail )?}}call i32 @llvm.umax.i32(i32 %{{.+}}, i32 %{{.+}}) -; CHECK-NEXT: store i32 %[[NEW_FLAG_IP]], ptr %"flags_elem'ipg" -; CHECK-NEXT: store i32 %new_flag, ptr %flags_elem +; Verify the primal block no longer uses the old zero-shadow fallback. +; CHECK: %new_flag = {{(tail )?}}call i32 @llvm.umax.i32(i32 %old_flag, i32 %flag) +; CHECK-NOT: store i32 0, ptr %"flags_elem'ipg" +; CHECK: store i32 {{.+}}, ptr %"flags_elem'ipg" +; CHECK: store i32 %new_flag, ptr %flags_elem ; Verify the reverse block correctly propagates float derivatives ; CHECK: invertentry: From 964ab803a5d1aa99fe2414cced711aa41aa2f0c5 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Thu, 30 Apr 2026 08:15:15 -0400 Subject: [PATCH 8/8] update Co-authored-by: Copilot --- .../Enzyme/ReverseMode/loosetypes_umax.ll | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll b/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll index fc5a72c757f..16165f9e119 100644 --- a/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll +++ b/enzyme/test/Enzyme/ReverseMode/loosetypes_umax.ll @@ -21,17 +21,20 @@ target triple = "x86_64-unknown-linux-gnu" declare i32 @llvm.umax.i32(i32, i32) -define void @compute_alpha(ptr noalias %op, i32 %face_idx, ptr noalias %nbr) { +define void @compute_alpha(i8* noalias %op, i32 %face_idx, i32* noalias %nbr) { entry: - %out_gep = getelementptr i8, ptr %op, i64 0 - %out_ptr = load ptr, ptr %out_gep, align 8 - %flags_gep = getelementptr i8, ptr %op, i64 8 - %flags_ptr = load ptr, ptr %flags_gep, align 8 - %clip_gep = getelementptr i8, ptr %op, i64 16 - %clip_val = load float, ptr %clip_gep, align 4 + %out_gep = getelementptr i8, i8* %op, i64 0 + %out_ptr_gep = bitcast i8* %out_gep to float** + %out_ptr = load float*, float** %out_ptr_gep, align 8 + %flags_gep = getelementptr i8, i8* %op, i64 8 + %flags_ptr_gep = bitcast i8* %flags_gep to i32** + %flags_ptr = load i32*, i32** %flags_ptr_gep, align 8 + %clip_gep = getelementptr i8, i8* %op, i64 16 + %clip_ptr = bitcast i8* %clip_gep to float* + %clip_val = load float, float* %clip_ptr, align 4 %idx = zext i32 %face_idx to i64 - %face_gep = getelementptr float, ptr %out_ptr, i64 %idx - %face_val = load float, ptr %face_gep, align 4 + %face_gep = getelementptr float, float* %out_ptr, i64 %idx + %face_val = load float, float* %face_gep, align 4 %dot = fmul float %face_val, %clip_val %cmp = fcmp ole float %dot, 0.0 br i1 %cmp, label %left_handed, label %normal @@ -45,40 +48,40 @@ normal: merge: %result = phi float [ %clip_val, %left_handed ], [ %dot, %normal ] %flag = phi i32 [ 1, %left_handed ], [ 0, %normal ] - store float %result, ptr %face_gep, align 4 - %n0 = load i32, ptr %nbr, align 4 + store float %result, float* %face_gep, align 4 + %n0 = load i32, i32* %nbr, align 4 %n0_ext = zext i32 %n0 to i64 - %flags_elem = getelementptr i32, ptr %flags_ptr, i64 %n0_ext - %old_flag = load i32, ptr %flags_elem, align 4 + %flags_elem = getelementptr i32, i32* %flags_ptr, i64 %n0_ext + %old_flag = load i32, i32* %flags_elem, align 4 %new_flag = tail call i32 @llvm.umax.i32(i32 %old_flag, i32 %flag) - store i32 %new_flag, ptr %flags_elem, align 4 + store i32 %new_flag, i32* %flags_elem, align 4 ret void } -define void @caller(ptr %op, ptr %d_op, i32 %face_idx, ptr %nbr) { +define void @caller(i8* %op, i8* %d_op, i32 %face_idx, i32* %nbr) { entry: call void (...) @__enzyme_autodiff( - ptr @compute_alpha, - metadata !"enzyme_dup", ptr %op, ptr %d_op, + i8* bitcast (void (i8*, i32, i32*)* @compute_alpha to i8*), + metadata !"enzyme_dup", i8* %op, i8* %d_op, metadata !"enzyme_const", i32 %face_idx, - metadata !"enzyme_const", ptr %nbr + metadata !"enzyme_const", i32* %nbr ) ret void } declare void @__enzyme_autodiff(...) -; CHECK: define internal void @diffecompute_alpha(ptr noalias -; CHECK-SAME: %op, ptr -; CHECK-SAME: %"op'" -; CHECK-SAME: i32 %face_idx, ptr noalias +; CHECK: define internal void @diffecompute_alpha( +; CHECK-SAME: %op, +; CHECK-SAME: %"op'", +; CHECK-SAME: i32 %face_idx, ; CHECK-SAME: %nbr) ; Verify the primal block no longer uses the old zero-shadow fallback. ; CHECK: %new_flag = {{(tail )?}}call i32 @llvm.umax.i32(i32 %old_flag, i32 %flag) -; CHECK-NOT: store i32 0, ptr %"flags_elem'ipg" -; CHECK: store i32 {{.+}}, ptr %"flags_elem'ipg" -; CHECK: store i32 %new_flag, ptr %flags_elem +; CHECK-NOT: store i32 0, {{(ptr|i32\*)}} %"flags_elem'ipg" +; CHECK: store i32 {{.+}}, {{(ptr|i32\*)}} %"flags_elem'ipg" +; CHECK: store i32 %new_flag, {{(ptr|i32\*)}} %flags_elem ; Verify the reverse block correctly propagates float derivatives ; CHECK: invertentry: