Type a copy from a flang character literal as integer A memcpy whose destination has no known type and whose source is a flang character literal (a constant [N x i8] global @_QQcl...) fails with "Cannot deduce type of copy". Type analysis types the bytes of an i8 array of 16 bits or more as Anything, because such an array may hold the bytes of any type, and the memcpy handler purges Anything from the source type. When the type is otherwise unknown and the source is a flang character literal, treat the copy as integer data. The check uses the flang name prefix, so an i8 array that holds the bytes of a float (as other frontends emit for constants) is not typed as integer. Assisted-by: Claude Code (Opus 5.5)
diff --git a/enzyme/Enzyme/AdjointGenerator.h b/enzyme/Enzyme/AdjointGenerator.h index cbd80ae..53c02b5 100644 --- a/enzyme/Enzyme/AdjointGenerator.h +++ b/enzyme/Enzyme/AdjointGenerator.h
@@ -3707,6 +3707,18 @@ } if (!vd.isKnownPastPointer()) { + // A copy from a flang character literal (@_QQcl...) copies character + // data. Type analysis only sees the bytes of the literal as Anything, + // since an i8 array may hold the bytes of any type. + if (auto GV = dyn_cast<GlobalVariable>(getBaseObject(orig_src))) + if (GV->isConstant() && GV->hasInitializer() && + startsWith(GV->getName(), "_QQcl")) + if (auto CDS = + dyn_cast<ConstantDataSequential>(GV->getInitializer())) + if (CDS->isString()) { + vd = TypeTree(BaseType::Integer).Only(-1, &MTI); + goto known; + } if (looseTypeAnalysis) { for (auto val : {orig_dst, orig_src}) { #if LLVM_VERSION_MAJOR < 17
diff --git a/enzyme/test/Enzyme/ReverseMode/memcpy-flang-charlit.ll b/enzyme/test/Enzyme/ReverseMode/memcpy-flang-charlit.ll new file mode 100644 index 0000000..4b41d20 --- /dev/null +++ b/enzyme/test/Enzyme/ReverseMode/memcpy-flang-charlit.ll
@@ -0,0 +1,32 @@ +; RUN: if [ %llvmver -lt 16 ]; then %opt < %s %loadEnzyme -enzyme-preopt=false -enzyme -mem2reg -instsimplify -simplifycfg -S | FileCheck %s; fi +; RUN: %opt < %s %newLoadEnzyme -enzyme-preopt=false -passes="enzyme,function(mem2reg,instsimplify,%simplifycfg)" -S | FileCheck %s + +; A copy from a flang character literal (@_QQcl...) into memory of otherwise +; unknown type copies character data, which is an integer. + +@_QQclX616263 = linkonce_odr constant [3 x i8] c"abc" + +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1) + +define void @f(double* %x, i8* %buf) { +entry: + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %buf, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @_QQclX616263, i64 0, i64 0), i64 3, i1 false) + %v = load double, double* %x + %m = fmul double %v, %v + store double %m, double* %x + ret void +} + +declare void @__enzyme_autodiff(...) + +define void @df(double* %x, double* %dx, i8* %buf, i8* %dbuf) { +entry: + call void (...) @__enzyme_autodiff(void (double*, i8*)* @f, metadata !"enzyme_dup", double* %x, double* %dx, metadata !"enzyme_dup", i8* %buf, i8* %dbuf) + ret void +} + +; CHECK: define internal void @diffef(double* {{.*}}%x, double* {{.*}}%"x'", i8* {{.*}}%buf, i8* {{.*}}%"buf'") +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %"buf'", i8* align 1 getelementptr inbounds ([3 x i8], [3 x i8]* @_QQclX616263, i64 0, i64 0), i64 3, i1 false) +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %buf, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @_QQclX616263, i64 0, i64 0), i64 3, i1 false) +; CHECK-NEXT: %v = load double, double* %x