diff --git a/.github/workflows/enzyme-linux.yml b/.github/workflows/enzyme-linux.yml index 3b2ef92eb375..2de0b61011cf 100644 --- a/.github/workflows/enzyme-linux.yml +++ b/.github/workflows/enzyme-linux.yml @@ -46,5 +46,7 @@ jobs: run: cd enzyme/build && make -j`nproc` - name: make check-typeanalysis run: cd enzyme/build && make check-typeanalysis -j`nproc` + - name: make check-activityanalysis + run: cd enzyme/build && make check-activityanalysis -j`nproc` - name: make check-enzyme run: cd enzyme/build && make check-enzyme -j`nproc` diff --git a/enzyme/Enzyme/ActivityAnalysis.cpp b/enzyme/Enzyme/ActivityAnalysis.cpp index 0dbcc30e3fce..060d478d9b01 100644 --- a/enzyme/Enzyme/ActivityAnalysis.cpp +++ b/enzyme/Enzyme/ActivityAnalysis.cpp @@ -114,7 +114,8 @@ std::set KnownInactiveFunctions = {"__assert_fail", "MPI_Finalize", "_msize", "ftnio_fmt_write64", - "f90_strcmp_klen"}; + "f90_strcmp_klen", + "vprintf"}; /// Is the use of value val as an argument of call CI known to be inactive /// This tool can only be used when in DOWN mode diff --git a/enzyme/Enzyme/ActivityAnalysisPrinter.cpp b/enzyme/Enzyme/ActivityAnalysisPrinter.cpp new file mode 100644 index 000000000000..eaa6dda445e0 --- /dev/null +++ b/enzyme/Enzyme/ActivityAnalysisPrinter.cpp @@ -0,0 +1,170 @@ +//===- TypeAnalysisPrinter.cpp - Printer utility pass for Type Analysis +//----===// +// +// Enzyme Project +// +// Part of the Enzyme Project, under the Apache License v2.0 with LLVM +// Exceptions. See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// If using this code in an academic setting, please cite the following: +// @incollection{enzymeNeurips, +// title = {Instead of Rewriting Foreign Code for Machine Learning, +// Automatically Synthesize Fast Gradients}, +// author = {Moses, William S. and Churavy, Valentin}, +// booktitle = {Advances in Neural Information Processing Systems 33}, +// year = {2020}, +// note = {To appear in}, +// } +// +//===----------------------------------------------------------------------===// +// +// This file contains a utility LLVM pass for printing derived Type Analysis +// results of a given function. +// +//===----------------------------------------------------------------------===// +#include + +#include "SCEV/ScalarEvolution.h" +#include "SCEV/ScalarEvolutionExpander.h" + +#include "llvm/ADT/SmallVector.h" + +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Metadata.h" + +#include "llvm/Support/Debug.h" +#include "llvm/Transforms/Scalar.h" + +#include "llvm/Analysis/BasicAliasAnalysis.h" +#include "llvm/Analysis/GlobalsModRef.h" +#include "llvm/Analysis/ScalarEvolution.h" + +#include "llvm/Support/CommandLine.h" + +#include "ActivityAnalysis.h" +#include "FunctionUtils.h" +#include "TypeAnalysis/TypeAnalysis.h" +#include "Utils.h" + +using namespace llvm; +#ifdef DEBUG_TYPE +#undef DEBUG_TYPE +#endif +#define DEBUG_TYPE "activity-analysis-results" + +/// Function TypeAnalysis will be starting its run from +static llvm::cl::opt + FunctionToAnalyze("activity-analysis-func", cl::init(""), cl::Hidden, + cl::desc("Which function to analyze/print")); + +namespace { + +class ActivityAnalysisPrinter : public FunctionPass { +public: + static char ID; + ActivityAnalysisPrinter() : FunctionPass(ID) {} + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + } + + bool runOnFunction(Function &F) override { + if (F.getName() != FunctionToAnalyze) + return /*changed*/ false; + +#if LLVM_VERSION_MAJOR >= 10 + auto &TLI = getAnalysis().getTLI(F); +#else + auto &TLI = getAnalysis().getTLI(); +#endif + + FnTypeInfo type_args(&F); + for (auto &a : type_args.Function->args()) { + TypeTree dt; + if (a.getType()->isFPOrFPVectorTy()) { + dt = ConcreteType(a.getType()->getScalarType()); + } else if (a.getType()->isPointerTy()) { + auto et = cast(a.getType())->getElementType(); + if (et->isFPOrFPVectorTy()) { + dt = TypeTree(ConcreteType(et->getScalarType())).Only(-1); + } else if (et->isPointerTy()) { + dt = TypeTree(ConcreteType(BaseType::Pointer)).Only(-1); + } + } else if (a.getType()->isIntOrIntVectorTy()) { + dt = ConcreteType(BaseType::Integer); + } + type_args.Arguments.insert( + std::pair(&a, dt.Only(-1))); + // TODO note that here we do NOT propagate constants in type info (and + // should consider whether we should) + type_args.KnownValues.insert( + std::pair>(&a, {})); + } + + TypeTree dt; + if (F.getReturnType()->isFPOrFPVectorTy()) { + dt = ConcreteType(F.getReturnType()->getScalarType()); + } else if (F.getReturnType()->isPointerTy()) { + auto et = cast(F.getReturnType())->getElementType(); + if (et->isFPOrFPVectorTy()) { + dt = TypeTree(ConcreteType(et->getScalarType())).Only(-1); + } else if (et->isPointerTy()) { + dt = TypeTree(ConcreteType(BaseType::Pointer)).Only(-1); + } + } else if (F.getReturnType()->isIntOrIntVectorTy()) { + dt = ConcreteType(BaseType::Integer); + } + type_args.Return = dt.Only(-1); + + TypeAnalysis TA(TLI); + TypeResults TR = TA.analyzeFunction(type_args); + + llvm::SmallPtrSet ConstantValues; + llvm::SmallPtrSet ActiveValues; + for (auto &a : type_args.Function->args()) { + if (a.getType()->isIntOrIntVectorTy()) { + ConstantValues.insert(&a); + } else { + ActiveValues.insert(&a); + } + } + + PreProcessCache PPC; + bool ActiveReturns = F.getReturnType()->isFPOrFPVectorTy(); + ActivityAnalyzer ATA(PPC.FAM.getResult(F), TLI, ConstantValues, + ActiveValues, ActiveReturns); + + for (auto &a : F.args()) { + bool icv = ATA.isConstantValue(TR, &a); + llvm::errs().flush(); + llvm::outs() << a << ": icv:" << icv << "\n"; + llvm::outs().flush(); + } + for (auto &BB : F) { + llvm::outs() << BB.getName() << "\n"; + for (auto &I : BB) { + bool ici = ATA.isConstantInstruction(TR, &I); + bool icv = ATA.isConstantValue(TR, &I); + llvm::errs().flush(); + llvm::outs() << I << ": icv:" << icv << " ici:" << ici << "\n"; + llvm::outs().flush(); + } + } + return /*changed*/ false; + } +}; + +} // namespace + +char ActivityAnalysisPrinter::ID = 0; + +static RegisterPass + X("print-activity-analysis", "Print Activity Analysis Results"); diff --git a/enzyme/Enzyme/FunctionUtils.cpp b/enzyme/Enzyme/FunctionUtils.cpp index 62a89e01c6f9..9c99e38b5454 100644 --- a/enzyme/Enzyme/FunctionUtils.cpp +++ b/enzyme/Enzyme/FunctionUtils.cpp @@ -51,6 +51,8 @@ #include "llvm/Analysis/TypeBasedAliasAnalysis.h" +#include "llvm/Analysis/CFLSteensAliasAnalysis.h" + #if LLVM_VERSION_MAJOR > 6 #include "llvm/Analysis/PhiValues.h" #endif @@ -121,6 +123,12 @@ static cl::opt EnzymeInlineCount("enzyme-inline-count", cl::init(10000), cl::Hidden, cl::desc("Limit of number of functions to inline")); +#if LLVM_VERSION_MAJOR >= 8 +static cl::opt EnzymePHIRestructure( + "enzyme-phi-restructure", cl::init(false), cl::Hidden, + cl::desc("Whether to restructure phi's to have better unwrap behavior")); +#endif + /// Is the use of value val as an argument of call CI potentially captured bool couldFunctionArgumentCapture(llvm::CallInst *CI, llvm::Value *val) { Function *F = CI->getCalledFunction(); @@ -411,8 +419,6 @@ OldAllocationSize(Value *Ptr, CallInst *Loc, Function *NewF, IntegerType *T, continue; } - // llvm::errs() << *NewF->getParent() << "\n"; - // llvm::errs() << *NewF << "\n"; EmitFailure("DynamicReallocSize", Loc->getDebugLoc(), Loc, "could not statically determine size of realloc ", *Loc, " - because of - ", *next.first); @@ -667,12 +673,17 @@ PreProcessCache::PreProcessCache() { // disable for now, consider enabling in future // FAM.registerPass([] { return SCEVAA(); }); + // FAM.registerPass([] { return CFLSteensAA(); }); + FAM.registerPass([] { auto AM = AAManager(); AM.registerFunctionAnalysis(); AM.registerFunctionAnalysis(); - // AM.registerFunctionAnalysis(); AM.registerModuleAnalysis(); + + // broken for different reasons + // AM.registerFunctionAnalysis(); + // AM.registerFunctionAnalysis(); return AM; }); @@ -1193,6 +1204,130 @@ Function *PreProcessCache::preprocessForClone(Function *F, bool topLevel) { FAM.invalidate(*NewF, PA); } +#if LLVM_VERSION_MAJOR >= 8 + if (EnzymePHIRestructure) { + if (false) { + reset:; + PreservedAnalyses PA; + FAM.invalidate(*NewF, PA); + } + + SmallVector MultiBlocks; + for (auto &B : *NewF) { + if (B.hasNPredecessorsOrMore(3)) + MultiBlocks.push_back(&B); + } + + LoopInfo &LI = FAM.getResult(*NewF); + for (BasicBlock *B : MultiBlocks) { + + // Map of function edges to list of values possible + std::map, + std::set> + done; + { + std::deque, + BasicBlock *>> + Q; // newblock, target + + for (auto P : predecessors(B)) { + Q.emplace_back(std::make_pair(P, B), P); + } + + for (std::tuple< + std::pair, + BasicBlock *> + trace; + Q.size() > 0;) { + trace = Q.front(); + Q.pop_front(); + auto edge = std::get<0>(trace); + auto block = edge.first; + auto target = std::get<1>(trace); + + if (done[edge].count(target)) + continue; + done[edge].insert(target); + + Loop *blockLoop = LI.getLoopFor(block); + + for (BasicBlock *Pred : predecessors(block)) { + // Don't go up the backedge as we can use the last value if desired + // via lcssa + if (blockLoop && blockLoop->getHeader() == block && + blockLoop == LI.getLoopFor(Pred)) + continue; + + Q.push_back( + std::tuple, BasicBlock *>( + std::make_pair(Pred, block), target)); + } + } + } + + SmallPtrSet Preds; + for (auto &pair : done) { + Preds.insert(pair.first.first); + } + + for (auto BB : Preds) { + bool illegal = false; + SmallPtrSet UnionSet; + size_t numSuc = 0; + for (BasicBlock *sucI : successors(BB)) { + numSuc++; + const auto &SI = done[std::make_pair(BB, sucI)]; + if (SI.size() == 0) { + // sucI->getName(); + illegal = true; + break; + } + for (auto si : SI) { + UnionSet.insert(si); + + for (BasicBlock *sucJ : successors(BB)) { + if (sucI == sucJ) + continue; + if (done[std::make_pair(BB, sucJ)].count(si)) { + illegal = true; + goto endIllegal; + } + } + } + } + endIllegal:; + + if (!illegal && numSuc > 1 && !B->hasNPredecessors(UnionSet.size())) { + BasicBlock *Ins = + BasicBlock::Create(BB->getContext(), "tmpblk", BB->getParent()); + IRBuilder<> Builder(Ins); + for (auto &phi : B->phis()) { + auto nphi = Builder.CreatePHI(phi.getType(), 2); + SmallVector Blocks; + + for (auto blk : UnionSet) { + nphi->addIncoming(phi.getIncomingValueForBlock(blk), blk); + phi.removeIncomingValue(blk, /*deleteifempty*/ false); + } + + phi.addIncoming(nphi, Ins); + } + Builder.CreateBr(B); + for (auto blk : UnionSet) { + auto term = blk->getTerminator(); + for (unsigned Idx = 0, NumSuccessors = term->getNumSuccessors(); + Idx != NumSuccessors; ++Idx) + if (term->getSuccessor(Idx) == B) + term->setSuccessor(Idx, Ins); + } + goto reset; + } + } + } + } +#endif + if (EnzymePrint) llvm::errs() << "after simplification :\n" << *NewF << "\n"; diff --git a/enzyme/Enzyme/GradientUtils.cpp b/enzyme/Enzyme/GradientUtils.cpp index ed273f9ebc01..263d1a7eae80 100644 --- a/enzyme/Enzyme/GradientUtils.cpp +++ b/enzyme/Enzyme/GradientUtils.cpp @@ -60,6 +60,10 @@ llvm::cl::opt EnzymeLoopInvariantCache( "enzyme-loop-invariant-cache", cl::init(true), cl::Hidden, cl::desc("Attempt to hoist cache outside of loop")); +llvm::cl::opt EnzymeInactiveDynamic( + "enzyme-inactive-dynamic", cl::init(true), cl::Hidden, + cl::desc("Force wholy inactive dynamic loops to have 0 iter reverse pass")); + bool isPotentialLastLoopValue(Value *val, const BasicBlock *loc, const LoopInfo &LI) { if (Instruction *inst = dyn_cast(val)) { @@ -144,9 +148,10 @@ Value *GradientUtils::unwrapM(Value *const val, IRBuilder<> &BuilderM, #define getOpFullest(vtmp, frominst, check) \ ({ \ Value *v = vtmp; \ - if (auto originst = dyn_cast(frominst)) \ + BasicBlock *origParent = frominst; \ + if (origParent) \ if (auto opinst = dyn_cast(v)) { \ - v = fixLCSSA(opinst, originst->getParent()); \ + v = fixLCSSA(opinst, origParent); \ if (check) \ assert(v != val); \ } \ @@ -175,8 +180,20 @@ Value *GradientUtils::unwrapM(Value *const val, IRBuilder<> &BuilderM, ___res; \ }) #define getOpFull(vtmp, frominst) getOpFullest(vtmp, frominst, true) -#define getOpUnchecked(vtmp) getOpFullest(vtmp, val, false) -#define getOp(vtmp) getOpFullest(vtmp, val, true) +#define getOpUnchecked(vtmp) \ + ({ \ + BasicBlock *parent = nullptr; \ + if (auto originst = dyn_cast(val)) \ + parent = originst->getParent(); \ + getOpFullest(vtmp, parent, false); \ + }) +#define getOp(vtmp) \ + ({ \ + BasicBlock *parent = nullptr; \ + if (auto originst = dyn_cast(val)) \ + parent = originst->getParent(); \ + getOpFullest(vtmp, parent, true); \ + }) if (isa(val) || isa(val)) { unwrap_cache[std::make_pair(val, BuilderM.GetInsertBlock())] = val; @@ -804,6 +821,49 @@ Value *GradientUtils::unwrapM(Value *const val, IRBuilder<> &BuilderM, assert(val->getType() == toret->getType()); return toret; } + if (auto SI = dyn_cast(equivalentTerminator)) { + // llvm::errs() << *SI << "\n"; + + auto cond = getOp(SI->getCondition()); + if (cond == nullptr) { + // llvm::errs() << " +nc " << *cond << "\n"; + goto endCheck; + } + + Value *val = + getOpFull(phi->getIncomingValueForBlock( + *done[std::make_pair(equivalentTerminator->getParent(), + SI->getDefaultDest())] + .begin()), + *done[std::make_pair(equivalentTerminator->getParent(), + SI->getDefaultDest())] + .begin()); + if (val == nullptr) { + // llvm::errs() << " +ndv " << *cond << "\n"; + goto endCheck; + } + + for (auto scase : SI->cases()) { + auto NC = getOpFull( + phi->getIncomingValueForBlock( + *done[std::make_pair(equivalentTerminator->getParent(), + scase.getCaseSuccessor())] + .begin()), + *done[std::make_pair(equivalentTerminator->getParent(), + scase.getCaseSuccessor())] + .begin()); + + if (NC == nullptr) { + // llvm::errs() << " +nc " << *cond << "\n"; + goto endCheck; + } + + val = BuilderM.CreateSelect( + BuilderM.CreateICmpEQ(cond, scase.getCaseValue()), NC, val); + } + unwrap_cache[cidx] = val; + return val; + } goto endCheck; } diff --git a/enzyme/Enzyme/GradientUtils.h b/enzyme/Enzyme/GradientUtils.h index dd3f048d2ad7..247d34a8f523 100644 --- a/enzyme/Enzyme/GradientUtils.h +++ b/enzyme/Enzyme/GradientUtils.h @@ -79,6 +79,8 @@ extern std::map &, CallInst *, ArrayRef)>> shadowHandlers; +extern llvm::cl::opt EnzymeInactiveDynamic; + static inline std::string to_string(DerivativeMode mode) { switch (mode) { case DerivativeMode::Forward: @@ -261,6 +263,8 @@ class GradientUtils : public CacheUtility { } bool assumeDynamicLoopOfSizeOne(llvm::Loop *L) const override { + if (!EnzymeInactiveDynamic) + return false; auto OL = OrigLI.getLoopFor(isOriginal(L->getHeader())); assert(OL); for (auto OB : OL->getBlocks()) { @@ -845,9 +849,9 @@ class GradientUtils : public CacheUtility { internal_isConstantValue[&I] = const_value; internal_isConstantInstruction[&I] = const_inst; - // if (printconst) - // llvm::errs() << I << " cv=" << const_value << " ci=" << const_inst << - // "\n"; + if (printconst) + llvm::errs() << I << " cv=" << const_value << " ci=" << const_inst + << "\n"; } } } @@ -1019,7 +1023,8 @@ class GradientUtils : public CacheUtility { } std::map> lcssaFixes; - Value *fixLCSSA(Instruction *inst, BasicBlock *forwardBlock) { + Value *fixLCSSA(Instruction *inst, BasicBlock *forwardBlock, + bool mergeIfTrue = false) { assert(inst->getName() != ""); LoopContext lc; bool inLoop = getContext(inst->getParent(), lc); @@ -1087,13 +1092,34 @@ class GradientUtils : public CacheUtility { } } if (val == nullptr) { - val = fixLCSSA(inst, pred); + val = fixLCSSA(inst, pred, /*mergeIfPossible*/ true); assert(val->getType() == inst->getType()); } assert(val->getType() == inst->getType()); lcssaPHI->addIncoming(val, pred); } + if (mergeIfTrue) { + Value *val = lcssaPHI; + for (auto &v : lcssaPHI->incoming_values()) { + if (v == lcssaPHI) + continue; + if (val == lcssaPHI) + val = v; + if (v != val) { + val = nullptr; + break; + } + } + if (val && val != lcssaPHI) { + if (lcssaPHI->hasNUses(0)) { + lcssaFixes[inst].erase(forwardBlock); + lcssaPHI->eraseFromParent(); + } + return val; + } + } + return lcssaPHI; } } diff --git a/enzyme/Enzyme/LibraryFuncs.h b/enzyme/Enzyme/LibraryFuncs.h index 827d8cf4524a..7b1b756dc457 100644 --- a/enzyme/Enzyme/LibraryFuncs.h +++ b/enzyme/Enzyme/LibraryFuncs.h @@ -340,7 +340,7 @@ static inline bool writesToMemoryReadBy(llvm::AAResults &AA, } } } - if (called && isCertainMallocOrFree(called)) { + if (called && isCertainPrintMallocOrFree(called)) { return false; } if (called && isMemFreeLibMFunction(called->getName())) { diff --git a/enzyme/Enzyme/TypeAnalysis/TypeAnalysisPrinter.cpp b/enzyme/Enzyme/TypeAnalysis/TypeAnalysisPrinter.cpp index ddeb44b02683..a0f90609f77b 100644 --- a/enzyme/Enzyme/TypeAnalysis/TypeAnalysisPrinter.cpp +++ b/enzyme/Enzyme/TypeAnalysis/TypeAnalysisPrinter.cpp @@ -1,4 +1,4 @@ -//===- TypeAnalysisPrinter.cpp - Printer utility pass for Type Analysis +//===- ActivityAnalysisPrinter.cpp - Printer utility pass for Type Analysis //----===// // // Enzyme Project @@ -19,7 +19,7 @@ // //===----------------------------------------------------------------------===// // -// This file contains a utility LLVM pass for printing derived Type Analysis +// This file contains a utility LLVM pass for printing derived Activity Analysis // results of a given function. // //===----------------------------------------------------------------------===// @@ -55,7 +55,7 @@ using namespace llvm; #endif #define DEBUG_TYPE "type-analysis-results" -/// Function TypeAnalysis will be starting its run from +/// Function ActivityAnalysis will be starting its run from llvm::cl::opt FunctionToAnalyze("type-analysis-func", cl::init(""), cl::Hidden, cl::desc("Which function to analyze/print")); diff --git a/enzyme/Enzyme/Utils.h b/enzyme/Enzyme/Utils.h index 2d814dde5c33..c20e6bbeb088 100644 --- a/enzyme/Enzyme/Utils.h +++ b/enzyme/Enzyme/Utils.h @@ -391,8 +391,6 @@ static inline bool isCertainMallocOrFree(llvm::Function *called) { if (called == nullptr) return false; if (called->getName() == "printf" || called->getName() == "puts" || - called->getName().startswith("_ZN3std2io5stdio6_print") || - called->getName().startswith("_ZN4core3fmt") || called->getName() == "malloc" || called->getName() == "_Znwm" || called->getName() == "_ZdlPv" || called->getName() == "_ZdlPvm" || called->getName() == "free" || @@ -425,8 +423,8 @@ static inline bool isCertainPrintOrFree(llvm::Function *called) { if (called->getName() == "printf" || called->getName() == "puts" || called->getName().startswith("_ZN3std2io5stdio6_print") || called->getName().startswith("_ZN4core3fmt") || - called->getName() == "_ZdlPv" || called->getName() == "_ZdlPvm" || - called->getName() == "free") + called->getName() == "vprintf" || called->getName() == "_ZdlPv" || + called->getName() == "_ZdlPvm" || called->getName() == "free") return true; switch (called->getIntrinsicID()) { case llvm::Intrinsic::dbg_declare: @@ -454,9 +452,9 @@ static inline bool isCertainPrintMallocOrFree(llvm::Function *called) { if (called->getName() == "printf" || called->getName() == "puts" || called->getName().startswith("_ZN3std2io5stdio6_print") || called->getName().startswith("_ZN4core3fmt") || - called->getName() == "malloc" || called->getName() == "_Znwm" || - called->getName() == "_ZdlPv" || called->getName() == "_ZdlPvm" || - called->getName() == "free" || + called->getName() == "vprintf" || called->getName() == "malloc" || + called->getName() == "_Znwm" || called->getName() == "_ZdlPv" || + called->getName() == "_ZdlPvm" || called->getName() == "free" || shadowHandlers.find(called->getName().str()) != shadowHandlers.end()) return true; switch (called->getIntrinsicID()) { diff --git a/enzyme/test/ActivityAnalysis/CMakeLists.txt b/enzyme/test/ActivityAnalysis/CMakeLists.txt new file mode 100644 index 000000000000..7aca111b188d --- /dev/null +++ b/enzyme/test/ActivityAnalysis/CMakeLists.txt @@ -0,0 +1,8 @@ +# Run regression and unit tests +add_lit_testsuite(check-activityanalysis "Running enzyme regression tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${ENZYME_TEST_DEPS} + ARGS -v +) + +set_target_properties(check-activityanalysis PROPERTIES FOLDER "Tests") diff --git a/enzyme/test/ActivityAnalysis/insertextract.ll b/enzyme/test/ActivityAnalysis/insertextract.ll new file mode 100644 index 000000000000..904e6c489bee --- /dev/null +++ b/enzyme/test/ActivityAnalysis/insertextract.ll @@ -0,0 +1,24 @@ +; RUN: %opt < %s %loadEnzyme -print-activity-analysis -activity-analysis-func=matvec -o /dev/null | FileCheck %s + +define internal double @matvec(double* noalias %b) { +entry: + %.fca.1.insert.i = insertvalue { double* } undef, double* %b, 0 + %i7 = extractvalue { double* } %.fca.1.insert.i, 0 + %unusedWeird = bitcast double* %i7 to double* + %.fca.1.insert.i15 = insertvalue { double* } undef, double* %i7, 0 + %i23 = extractvalue { double* } %.fca.1.insert.i15, 0 + %i29 = load double, double* %i23, align 8 + %arrayidx.i.i.i = getelementptr inbounds double, double* %i23, i64 1 + ret double %i29 +} + +; CHECK: double* %b: icv:0 +; CHECK-NEXT: entry +; CHECK-NEXT: %.fca.1.insert.i = insertvalue { double* } undef, double* %b, 0: icv:0 ici:1 +; CHECK-NEXT: %i7 = extractvalue { double* } %.fca.1.insert.i, 0: icv:0 ici:1 +; CHECK-NEXT: %unusedWeird = bitcast double* %i7 to double*: icv:0 ici:1 +; CHECK-NEXT: %.fca.1.insert.i15 = insertvalue { double* } undef, double* %i7, 0: icv:0 ici:1 +; CHECK-NEXT: %i23 = extractvalue { double* } %.fca.1.insert.i15, 0: icv:0 ici:1 +; CHECK-NEXT: %i29 = load double, double* %i23, align 8: icv:0 ici:0 +; CHECK-NEXT: %arrayidx.i.i.i = getelementptr inbounds double, double* %i23, i64 1: icv:0 ici:1 +; CHECK-NEXT: ret double %i29: icv:1 ici:1 \ No newline at end of file diff --git a/enzyme/test/CMakeLists.txt b/enzyme/test/CMakeLists.txt index fe9e19ffe16a..61eb19f24f74 100644 --- a/enzyme/test/CMakeLists.txt +++ b/enzyme/test/CMakeLists.txt @@ -7,6 +7,7 @@ configure_lit_site_cfg( set(ENZYME_TEST_DEPS LLVMEnzyme-${LLVM_VERSION_MAJOR}) +add_subdirectory(ActivityAnalysis) add_subdirectory(TypeAnalysis) add_subdirectory(Enzyme) add_subdirectory(Integration) diff --git a/enzyme/test/Integration/sumtil2.c b/enzyme/test/Integration/sumtil2.c new file mode 100644 index 000000000000..fd436b9fe052 --- /dev/null +++ b/enzyme/test/Integration/sumtil2.c @@ -0,0 +1,46 @@ +// RUN: %clang -std=c11 -ffast-math -O0 %s -S -emit-llvm -o - | %opt - %loadEnzyme -enzyme -S | %lli - +// RUN: %clang -std=c11 -ffast-math -O1 %s -S -emit-llvm -o - | %opt - %loadEnzyme -enzyme -S | %lli - +// RUN: %clang -std=c11 -ffast-math -O2 %s -S -emit-llvm -o - | %opt - %loadEnzyme -enzyme -S | %lli - +// RUN: %clang -std=c11 -ffast-math -O3 %s -S -emit-llvm -o - | %opt - %loadEnzyme -enzyme -S | %lli - +// RUN: %clang -std=c11 -ffast-math -O0 %s -S -emit-llvm -o - | %opt - %loadEnzyme -enzyme -enzyme-inline=1 -S | %lli - +// RUN: %clang -std=c11 -ffast-math -O1 %s -S -emit-llvm -o - | %opt - %loadEnzyme -enzyme -enzyme-inline=1 -S | %lli - +// RUN: %clang -std=c11 -ffast-math -O2 %s -S -emit-llvm -o - | %opt - %loadEnzyme -enzyme -enzyme-inline=1 -S | %lli - +// RUN: %clang -std=c11 -ffast-math -O3 %s -S -emit-llvm -o - | %opt - %loadEnzyme -enzyme -enzyme-inline=1 -S | %lli - + +#include +#include +#include + +#include "test_utils.h" + +extern void __enzyme_autodiff(void*, double*, double*, int); +/*double max(double x, double y) { + return (x > y) ? x : y; +}*/ + +double sumtil(double* vec, int size) { + double ret = 0.0; + for (int i = 0; i < size; i++) { + ret += vec[i]; + if (ret > 15) break; + ret += vec[i]; + } + return ret; +} + +int main() { + double vec[] = {1, 2., 3., 4., 5.}; + double d_vec[] = {0., 0., 0., 0., 0.}; + __enzyme_autodiff(sumtil, vec, d_vec, 5); + + for(int i=0; i<5; i++) { + printf("d_reduce_max(%i)=%f\n", i, d_vec[i]); + } + fflush(0); + double ans[] = {2, 2, 2, 1, 0}; + for(int i=0; i<5; i++) { + printf("i=%d d_vec=%f ans=%f\n", i, d_vec[i], ans[i]); + APPROX_EQ(d_vec[i], ans[i], 1e-7); + } + printf("done\n"); +} \ No newline at end of file