blob: fbe869a8ea6aea141c4d049ca018fc905a888b28 [file] [edit]
//===- CApi.cpp - Enzyme API exported to C for external use -----------===//
//
// 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 defines various utility functions of Enzyme for access via C
//
//===----------------------------------------------------------------------===//
#include "CApi.h"
#include "Utils.h"
#if LLVM_VERSION_MAJOR >= 16
#define private public
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"
#undef private
#else
#include "SCEV/ScalarEvolution.h"
#include "SCEV/ScalarEvolutionExpander.h"
#endif
#include "DiffeGradientUtils.h"
#include "DifferentialUseAnalysis.h"
#include "EnzymeLogic.h"
#include "GradientUtils.h"
#include "LibraryFuncs.h"
#if LLVM_VERSION_MAJOR >= 16
#include "llvm/Analysis/TargetLibraryInfo.h"
#else
#include "SCEV/TargetLibraryInfo.h"
#endif
#include "TraceInterface.h"
// #include "llvm/ADT/Triple.h"
#include "llvm/Analysis/CallGraph.h"
#include "llvm/Analysis/GlobalsModRef.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/Attributor.h"
#define addAttribute addAttributeAtIndex
#define removeAttribute removeAttributeAtIndex
#define getAttribute getAttributeAtIndex
#define hasAttribute hasAttributeAtIndex
using namespace llvm;
TargetLibraryInfo eunwrap(LLVMTargetLibraryInfoRef P) {
return TargetLibraryInfo(*reinterpret_cast<TargetLibraryInfoImpl *>(P));
}
EnzymeLogic &eunwrap(EnzymeLogicRef LR) { return *(EnzymeLogic *)LR; }
TraceInterface *eunwrap(EnzymeTraceInterfaceRef Ref) {
return (TraceInterface *)Ref;
}
TypeAnalysis &eunwrap(EnzymeTypeAnalysisRef TAR) {
return *(TypeAnalysis *)TAR;
}
AugmentedReturn *eunwrap(EnzymeAugmentedReturnPtr ARP) {
return (AugmentedReturn *)ARP;
}
EnzymeAugmentedReturnPtr ewrap(const AugmentedReturn &AR) {
return (EnzymeAugmentedReturnPtr)(&AR);
}
ConcreteType eunwrap(CConcreteType CDT, llvm::LLVMContext &ctx) {
switch (CDT) {
case DT_Anything:
return BaseType::Anything;
case DT_Integer:
return BaseType::Integer;
case DT_Pointer:
return BaseType::Pointer;
case DT_Half:
return ConcreteType(llvm::Type::getHalfTy(ctx));
case DT_Float:
return ConcreteType(llvm::Type::getFloatTy(ctx));
case DT_Double:
return ConcreteType(llvm::Type::getDoubleTy(ctx));
case DT_X86_FP80:
return ConcreteType(llvm::Type::getX86_FP80Ty(ctx));
case DT_BFloat16:
return ConcreteType(llvm::Type::getBFloatTy(ctx));
case DT_FP128:
return ConcreteType(llvm::Type::getFP128Ty(ctx));
case DT_Unknown:
return BaseType::Unknown;
}
llvm_unreachable("Unknown concrete type to unwrap");
}
std::vector<int> eunwrap(IntList IL) {
std::vector<int> v;
for (size_t i = 0; i < IL.size; i++) {
v.push_back((int)IL.data[i]);
}
return v;
}
std::set<int64_t> eunwrap64(IntList IL) {
std::set<int64_t> v;
for (size_t i = 0; i < IL.size; i++) {
v.insert((int64_t)IL.data[i]);
}
return v;
}
TypeTree eunwrap(CTypeTreeRef CTT) { return *(TypeTree *)CTT; }
CConcreteType ewrap(const ConcreteType &CT) {
if (auto flt = CT.isFloat()) {
if (flt->isHalfTy())
return DT_Half;
if (flt->isFloatTy())
return DT_Float;
if (flt->isDoubleTy())
return DT_Double;
if (flt->isX86_FP80Ty())
return DT_X86_FP80;
if (flt->isBFloatTy())
return DT_BFloat16;
if (flt->isFP128Ty())
return DT_FP128;
} else {
switch (CT.SubTypeEnum) {
case BaseType::Integer:
return DT_Integer;
case BaseType::Pointer:
return DT_Pointer;
case BaseType::Anything:
return DT_Anything;
case BaseType::Unknown:
return DT_Unknown;
case BaseType::Float:
llvm_unreachable("Illegal conversion of concretetype");
}
}
llvm_unreachable("Illegal conversion of concretetype");
}
IntList ewrap(const std::vector<int> &offsets) {
IntList IL;
IL.size = offsets.size();
IL.data = new int64_t[IL.size];
for (size_t i = 0; i < offsets.size(); i++) {
IL.data[i] = offsets[i];
}
return IL;
}
CTypeTreeRef ewrap(const TypeTree &TT) {
return (CTypeTreeRef)(new TypeTree(TT));
}
FnTypeInfo eunwrap(CFnTypeInfo CTI, llvm::Function *F) {
FnTypeInfo FTI(F);
// auto &ctx = F->getContext();
FTI.Return = eunwrap(CTI.Return);
size_t argnum = 0;
for (auto &arg : F->args()) {
FTI.Arguments[&arg] = eunwrap(CTI.Arguments[argnum]);
FTI.KnownValues[&arg] = eunwrap64(CTI.KnownValues[argnum]);
argnum++;
}
return FTI;
}
extern "C" {
void EnzymeSetCLBool(void *ptr, uint8_t val) {
auto cl = (llvm::cl::opt<bool> *)ptr;
cl->setValue((bool)val);
}
uint8_t EnzymeGetCLBool(void *ptr) {
auto cl = (llvm::cl::opt<bool> *)ptr;
return (uint8_t)(bool)cl->getValue();
}
void EnzymeSetCLInteger(void *ptr, int64_t val) {
auto cl = (llvm::cl::opt<int> *)ptr;
cl->setValue((int)val);
}
int64_t EnzymeGetCLInteger(void *ptr) {
auto cl = (llvm::cl::opt<int> *)ptr;
return (int64_t)cl->getValue();
}
void EnzymeSetCLString(void *ptr, const char *val) {
if (auto *clopt = static_cast<cl::opt<std::string> *>(ptr))
clopt->setValue(val);
}
EnzymeLogicRef CreateEnzymeLogic(uint8_t PostOpt) {
return (EnzymeLogicRef)(new EnzymeLogic((bool)PostOpt));
}
void EnzymeLogicSetExternalContext(EnzymeLogicRef Ref, void *ExternalContext) {
eunwrap(Ref).ExternalContext = ExternalContext;
}
void *EnzymeLogicGetExternalContext(EnzymeLogicRef Ref) {
return eunwrap(Ref).ExternalContext;
}
EnzymeTraceInterfaceRef FindEnzymeStaticTraceInterface(LLVMModuleRef M) {
return (EnzymeTraceInterfaceRef)(new StaticTraceInterface(unwrap(M)));
}
EnzymeTraceInterfaceRef CreateEnzymeStaticTraceInterface(
LLVMContextRef C, LLVMValueRef getTraceFunction,
LLVMValueRef getChoiceFunction, LLVMValueRef insertCallFunction,
LLVMValueRef insertChoiceFunction, LLVMValueRef insertArgumentFunction,
LLVMValueRef insertReturnFunction, LLVMValueRef insertFunctionFunction,
LLVMValueRef insertChoiceGradientFunction,
LLVMValueRef insertArgumentGradientFunction, LLVMValueRef newTraceFunction,
LLVMValueRef freeTraceFunction, LLVMValueRef hasCallFunction,
LLVMValueRef hasChoiceFunction) {
return (EnzymeTraceInterfaceRef)(new StaticTraceInterface(
*unwrap(C), cast<Function>(unwrap(getTraceFunction)),
cast<Function>(unwrap(getChoiceFunction)),
cast<Function>(unwrap(insertCallFunction)),
cast<Function>(unwrap(insertChoiceFunction)),
cast<Function>(unwrap(insertArgumentFunction)),
cast<Function>(unwrap(insertReturnFunction)),
cast<Function>(unwrap(insertFunctionFunction)),
cast<Function>(unwrap(insertChoiceGradientFunction)),
cast<Function>(unwrap(insertArgumentGradientFunction)),
cast<Function>(unwrap(newTraceFunction)),
cast<Function>(unwrap(freeTraceFunction)),
cast<Function>(unwrap(hasCallFunction)),
cast<Function>(unwrap(hasChoiceFunction))));
};
EnzymeTraceInterfaceRef
CreateEnzymeDynamicTraceInterface(LLVMValueRef interface, LLVMValueRef F) {
return (EnzymeTraceInterfaceRef)(new DynamicTraceInterface(
unwrap(interface), cast<Function>(unwrap(F))));
}
void ClearEnzymeLogic(EnzymeLogicRef Ref) { eunwrap(Ref).clear(); }
void EnzymeLogicErasePreprocessedFunctions(EnzymeLogicRef Ref) {
auto &Logic = eunwrap(Ref);
for (const auto &pair : Logic.PPC.cache)
pair.second->eraseFromParent();
}
void FreeEnzymeLogic(EnzymeLogicRef Ref) { delete (EnzymeLogic *)Ref; }
void FreeTraceInterface(EnzymeTraceInterfaceRef Ref) {
delete (TraceInterface *)Ref;
}
EnzymeTypeAnalysisRef CreateTypeAnalysis(EnzymeLogicRef Log,
char **customRuleNames,
CustomRuleType *customRules,
size_t numRules) {
EnzymeLogic &Logic = eunwrap(Log);
TypeAnalysis *TA = new TypeAnalysis(Logic);
for (size_t i = 0; i < numRules; i++) {
CustomRuleType rule = customRules[i];
TA->CustomRules[customRuleNames[i]] =
[=](int direction, TypeTree &returnTree, ArrayRef<TypeTree> argTrees,
ArrayRef<std::set<int64_t>> knownValues, CallBase *call,
TypeAnalyzer *TA) -> uint8_t {
CTypeTreeRef creturnTree = (CTypeTreeRef)(&returnTree);
CTypeTreeRef *cargs = new CTypeTreeRef[argTrees.size()];
IntList *kvs = new IntList[argTrees.size()];
for (size_t i = 0; i < argTrees.size(); ++i) {
cargs[i] = (CTypeTreeRef)(&(argTrees[i]));
kvs[i].size = knownValues[i].size();
kvs[i].data = new int64_t[kvs[i].size];
size_t j = 0;
for (auto val : knownValues[i]) {
kvs[i].data[j] = val;
j++;
}
}
uint8_t result = rule(direction, creturnTree, cargs, kvs, argTrees.size(),
wrap(call), TA);
delete[] cargs;
for (size_t i = 0; i < argTrees.size(); ++i) {
delete[] kvs[i].data;
}
delete[] kvs;
return result;
};
}
return (EnzymeTypeAnalysisRef)TA;
}
void ClearTypeAnalysis(EnzymeTypeAnalysisRef TAR) { eunwrap(TAR).clear(); }
void FreeTypeAnalysis(EnzymeTypeAnalysisRef TAR) {
TypeAnalysis *TA = (TypeAnalysis *)TAR;
delete TA;
}
EnzymeLogicRef EnzymeTypeAnalysisGetLogic(EnzymeTypeAnalysisRef TAR) {
return (EnzymeLogicRef) & ((TypeAnalysis *)TAR)->Logic;
}
void *EnzymeAnalyzeTypes(EnzymeTypeAnalysisRef TAR, CFnTypeInfo CTI,
LLVMValueRef F) {
FnTypeInfo FTI(eunwrap(CTI, cast<Function>(unwrap(F))));
return (void *)((TypeAnalysis *)TAR)->analyzeFunction(FTI).analyzer;
}
void *EnzymeGradientUtilsTypeAnalyzer(GradientUtils *G) {
return (void *)&G->TR.analyzer;
}
EnzymeTypeAnalysisRef EnzymeGetTypeAnalysisFromTypeAnalyzer(void *TAR) {
return (EnzymeTypeAnalysisRef) & ((TypeAnalyzer *)TAR)->interprocedural;
}
void EnzymeGradientUtilsErase(GradientUtils *G, LLVMValueRef I) {
return G->erase(cast<Instruction>(unwrap(I)));
}
void EnzymeGradientUtilsEraseWithPlaceholder(GradientUtils *G, LLVMValueRef I,
LLVMValueRef orig, uint8_t erase) {
return G->eraseWithPlaceholder(cast<Instruction>(unwrap(I)),
cast<Instruction>(unwrap(orig)),
"_replacementABI", erase != 0);
}
void EnzymeGradientUtilsReplaceAWithB(GradientUtils *G, LLVMValueRef A,
LLVMValueRef B) {
return G->replaceAWithB(unwrap(A), unwrap(B));
}
void EnzymeRegisterAllocationHandler(char *Name, CustomShadowAlloc AHandle,
CustomShadowFree FHandle) {
shadowHandlers[Name] = [=](IRBuilder<> &B, CallInst *CI,
ArrayRef<Value *> Args,
GradientUtils *gutils) -> llvm::Value * {
SmallVector<LLVMValueRef, 3> refs;
for (auto a : Args)
refs.push_back(wrap(a));
return unwrap(
AHandle(wrap(&B), wrap(CI), Args.size(), refs.data(), gutils));
};
if (FHandle)
shadowErasers[Name] = [=](IRBuilder<> &B,
Value *ToFree) -> llvm::CallInst * {
return cast_or_null<CallInst>(unwrap(FHandle(wrap(&B), wrap(ToFree))));
};
}
void EnzymeRegisterCallHandler(const char *Name,
CustomAugmentedFunctionForward FwdHandle,
CustomFunctionReverse RevHandle) {
auto &pair = customCallHandlers[Name];
pair.first = [=](IRBuilder<> &B, CallInst *CI, GradientUtils &gutils,
Value *&normalReturn, Value *&shadowReturn,
Value *&tape) -> bool {
LLVMValueRef normalR = wrap(normalReturn);
LLVMValueRef shadowR = wrap(shadowReturn);
LLVMValueRef tapeR = wrap(tape);
uint8_t noMod =
FwdHandle(wrap(&B), wrap(CI), &gutils, &normalR, &shadowR, &tapeR);
normalReturn = unwrap(normalR);
shadowReturn = unwrap(shadowR);
tape = unwrap(tapeR);
return noMod != 0;
};
pair.second = [=](IRBuilder<> &B, CallInst *CI, DiffeGradientUtils &gutils,
Value *tape) {
RevHandle(wrap(&B), wrap(CI), &gutils, wrap(tape));
};
}
void EnzymeRegisterFwdCallHandler(char *Name, CustomFunctionForward FwdHandle) {
auto &pair = customFwdCallHandlers[Name];
pair = [=](IRBuilder<> &B, CallInst *CI, GradientUtils &gutils,
Value *&normalReturn, Value *&shadowReturn) -> bool {
LLVMValueRef normalR = wrap(normalReturn);
LLVMValueRef shadowR = wrap(shadowReturn);
uint8_t noMod = FwdHandle(wrap(&B), wrap(CI), &gutils, &normalR, &shadowR);
normalReturn = unwrap(normalR);
shadowReturn = unwrap(shadowR);
return noMod != 0;
};
}
void EnzymeRegisterDiffUseCallHandler(char *Name,
CustomFunctionDiffUse Handle) {
auto &pair = customDiffUseHandlers[Name];
pair = [=](const CallInst *CI, const GradientUtils *gutils, const Value *arg,
bool isshadow, DerivativeMode mode, bool &useDefault) -> bool {
uint8_t useDefaultC = 0;
uint8_t noMod = Handle(wrap(CI), gutils, wrap(arg), isshadow,
(CDerivativeMode)(mode), &useDefaultC);
useDefault = useDefaultC != 0;
return noMod != 0;
};
}
uint8_t EnzymeGradientUtilsGetRuntimeActivity(GradientUtils *gutils) {
return gutils->runtimeActivity;
}
void *EnzymeGradientUtilsGetExternalContext(GradientUtils *gutils) {
return gutils->Logic.ExternalContext;
}
uint8_t EnzymeGradientUtilsGetStrongZero(GradientUtils *gutils) {
return gutils->strongZero;
}
uint8_t EnzymeGradientUtilsGetAtomicAdd(GradientUtils *gutils) {
return gutils->AtomicAdd;
}
uint64_t EnzymeGradientUtilsGetWidth(GradientUtils *gutils) {
return gutils->getWidth();
}
EnzymeLogicRef EnzymeGradientUtilsGetLogic(GradientUtils *gutils) {
return (EnzymeLogicRef)&gutils->Logic;
}
LLVMTypeRef EnzymeGradientUtilsGetShadowType(GradientUtils *gutils,
LLVMTypeRef T) {
return wrap(gutils->getShadowType(unwrap(T)));
}
LLVMTypeRef EnzymeGetShadowType(uint64_t width, LLVMTypeRef T) {
return wrap(GradientUtils::getShadowType(unwrap(T), width));
}
LLVMValueRef EnzymeGradientUtilsNewFromOriginal(GradientUtils *gutils,
LLVMValueRef val) {
return wrap(gutils->getNewFromOriginal(unwrap(val)));
}
void EnzymeReplaceOriginalToNew(GradientUtils *gutils, LLVMValueRef origC,
LLVMValueRef repC) {
auto orig = cast<Instruction>(unwrap(origC));
auto rep = cast<Instruction>(unwrap(repC));
auto found = gutils->originalToNewFn.find(orig);
assert(found != gutils->originalToNewFn.end());
auto newCall = found->second;
gutils->originalToNewFn[orig] = rep;
gutils->newToOriginalFn.erase(newCall);
gutils->newToOriginalFn[rep] = orig;
}
CDerivativeMode EnzymeGradientUtilsGetMode(GradientUtils *gutils) {
return (CDerivativeMode)gutils->mode;
}
CDIFFE_TYPE
EnzymeGradientUtilsGetDiffeType(GradientUtils *G, LLVMValueRef oval,
uint8_t foreignFunction) {
return (CDIFFE_TYPE)(G->getDiffeType(unwrap(oval), foreignFunction != 0));
}
CDIFFE_TYPE
EnzymeGradientUtilsGetReturnDiffeType(GradientUtils *G, LLVMValueRef oval,
uint8_t *needsPrimal,
uint8_t *needsShadow,
CDerivativeMode mode) {
bool needsPrimalB;
bool needsShadowB;
auto res = (CDIFFE_TYPE)(G->getReturnDiffeType(
unwrap(oval), &needsPrimalB, &needsShadowB, (DerivativeMode)mode));
if (needsPrimal)
*needsPrimal = needsPrimalB;
if (needsShadow)
*needsShadow = needsShadowB;
return res;
}
void EnzymeGradientUtilsSetDebugLocFromOriginal(GradientUtils *gutils,
LLVMValueRef val,
LLVMValueRef orig) {
return cast<Instruction>(unwrap(val))
->setDebugLoc(gutils->getNewFromOriginal(
cast<Instruction>(unwrap(orig))->getDebugLoc()));
}
LLVMValueRef EnzymeInsertValue(LLVMBuilderRef B, LLVMValueRef val,
LLVMValueRef val2, unsigned *sz, int64_t length,
const char *name) {
return wrap(unwrap(B)->CreateInsertValue(
unwrap(val), unwrap(val2), ArrayRef<unsigned>(sz, sz + length), name));
}
LLVMValueRef EnzymeGradientUtilsLookup(GradientUtils *gutils, LLVMValueRef val,
LLVMBuilderRef B) {
return wrap(gutils->lookupM(unwrap(val), *unwrap(B)));
}
LLVMValueRef EnzymeGradientUtilsInvertPointer(GradientUtils *gutils,
LLVMValueRef val,
LLVMBuilderRef B) {
return wrap(gutils->invertPointerM(unwrap(val), *unwrap(B)));
}
LLVMValueRef EnzymeGradientUtilsDiffe(DiffeGradientUtils *gutils,
LLVMValueRef val, LLVMBuilderRef B) {
return wrap(gutils->diffe(unwrap(val), *unwrap(B)));
}
void EnzymeGradientUtilsAddToDiffe(DiffeGradientUtils *gutils, LLVMValueRef val,
LLVMValueRef diffe, LLVMBuilderRef B,
LLVMTypeRef T) {
gutils->addToDiffe(unwrap(val), unwrap(diffe), *unwrap(B), unwrap(T));
}
void EnzymeGradientUtilsAddToInvertedPointerDiffe(
DiffeGradientUtils *gutils, LLVMValueRef orig, LLVMValueRef origVal,
LLVMTypeRef addingType, unsigned start, unsigned size, LLVMValueRef origptr,
LLVMValueRef dif, LLVMBuilderRef BuilderM, unsigned align,
LLVMValueRef mask) {
MaybeAlign align2;
if (align)
align2 = MaybeAlign(align);
auto inst = cast_or_null<Instruction>(unwrap(orig));
gutils->addToInvertedPtrDiffe(inst, unwrap(origVal), unwrap(addingType),
start, size, unwrap(origptr), unwrap(dif),
*unwrap(BuilderM), align2, unwrap(mask));
}
void EnzymeGradientUtilsAddToInvertedPointerDiffeTT(
DiffeGradientUtils *gutils, LLVMValueRef orig, LLVMValueRef origVal,
CTypeTreeRef vd, unsigned LoadSize, LLVMValueRef origptr,
LLVMValueRef prediff, LLVMBuilderRef BuilderM, unsigned align,
LLVMValueRef premask) {
MaybeAlign align2;
if (align)
align2 = MaybeAlign(align);
auto inst = cast_or_null<Instruction>(unwrap(orig));
gutils->addToInvertedPtrDiffe(inst, unwrap(origVal), *(TypeTree *)vd,
LoadSize, unwrap(origptr), unwrap(prediff),
*unwrap(BuilderM), align2, unwrap(premask));
}
void EnzymeGradientUtilsSetDiffe(DiffeGradientUtils *gutils, LLVMValueRef val,
LLVMValueRef diffe, LLVMBuilderRef B) {
gutils->setDiffe(unwrap(val), unwrap(diffe), *unwrap(B));
}
uint8_t EnzymeGradientUtilsIsConstantValue(GradientUtils *gutils,
LLVMValueRef val) {
return gutils->isConstantValue(unwrap(val));
}
uint8_t EnzymeGradientUtilsIsConstantInstruction(GradientUtils *gutils,
LLVMValueRef val) {
return gutils->isConstantInstruction(cast<Instruction>(unwrap(val)));
}
LLVMBasicBlockRef EnzymeGradientUtilsAllocationBlock(GradientUtils *gutils) {
return wrap(gutils->inversionAllocs);
}
uint8_t EnzymeGradientUtilsGetUncacheableArgs(GradientUtils *gutils,
LLVMValueRef orig, uint8_t *data,
uint64_t size) {
if (gutils->mode == DerivativeMode::ForwardMode ||
gutils->mode == DerivativeMode::ForwardModeError)
return 0;
if (!gutils->overwritten_args_map_ptr)
return 0;
CallInst *call = cast<CallInst>(unwrap(orig));
assert(gutils->overwritten_args_map_ptr);
auto found = gutils->overwritten_args_map_ptr->find(call);
if (found == gutils->overwritten_args_map_ptr->end()) {
llvm::errs() << " oldFunc " << *gutils->oldFunc << "\n";
for (auto &pair : *gutils->overwritten_args_map_ptr) {
llvm::errs() << " + " << *pair.first << "\n";
}
llvm::errs() << " could not find call orig in overwritten_args_map_ptr "
<< *call << "\n";
}
assert(found != gutils->overwritten_args_map_ptr->end());
const std::vector<bool> &overwritten_args = found->second.second;
if (size != overwritten_args.size()) {
llvm::errs() << " orig: " << *call << "\n";
llvm::errs() << " size: " << size
<< " overwritten_args.size(): " << overwritten_args.size()
<< "\n";
}
assert(size == overwritten_args.size());
for (uint64_t i = 0; i < size; i++) {
data[i] = overwritten_args[i];
}
return 1;
}
CTypeTreeRef EnzymeGradientUtilsAllocAndGetTypeTree(GradientUtils *gutils,
LLVMValueRef val) {
auto v = unwrap(val);
TypeTree TT = gutils->TR.query(v);
TypeTree *pTT = new TypeTree(TT);
return (CTypeTreeRef)pTT;
}
void EnzymeGradientUtilsDumpTypeResults(GradientUtils *gutils) {
gutils->TR.dump();
}
void EnzymeGradientUtilsSubTransferHelper(
GradientUtils *gutils, CDerivativeMode mode, LLVMTypeRef secretty,
uint64_t intrinsic, uint64_t dstAlign, uint64_t srcAlign, uint64_t offset,
uint8_t dstConstant, LLVMValueRef shadow_dst, LLVMValueRef primal_dst,
uint8_t srcConstant, LLVMValueRef shadow_src, LLVMValueRef primal_src,
LLVMValueRef length, LLVMValueRef isVolatile, LLVMValueRef MTI,
uint8_t allowForward, uint8_t shadowsLookedUp) {
auto orig = unwrap(MTI);
assert(orig);
SubTransferHelper(gutils, (DerivativeMode)mode, unwrap(secretty),
(Intrinsic::ID)intrinsic, (unsigned)dstAlign,
(unsigned)srcAlign, (unsigned)offset, (bool)dstConstant,
unwrap(shadow_dst), unwrap(primal_dst), (bool)srcConstant,
unwrap(shadow_src), unwrap(primal_src), unwrap(length),
unwrap(isVolatile), cast<CallInst>(orig),
(bool)allowForward, (bool)shadowsLookedUp);
}
LLVMBasicBlockRef EnzymeGradientUtilsAddReverseBlock(GradientUtils *gutils,
LLVMBasicBlockRef block,
const char *name,
uint8_t forkCache,
uint8_t push) {
return wrap(gutils->addReverseBlock(cast<BasicBlock>(unwrap(block)), name,
forkCache, push));
}
void EnzymeGradientUtilsSetReverseBlock(GradientUtils *gutils,
LLVMBasicBlockRef block) {
auto endBlock = cast<BasicBlock>(unwrap(block));
auto found = gutils->reverseBlockToPrimal.find(endBlock);
assert(found != gutils->reverseBlockToPrimal.end());
auto &vec = gutils->reverseBlocks[found->second];
assert(vec.size());
vec.push_back(endBlock);
}
LLVMValueRef EnzymeCreateForwardDiff(
EnzymeLogicRef Logic, LLVMValueRef request_req, LLVMBuilderRef request_ip,
LLVMValueRef todiff, CDIFFE_TYPE retType, CDIFFE_TYPE *constant_args,
size_t constant_args_size, EnzymeTypeAnalysisRef TA, uint8_t returnValue,
CDerivativeMode mode, uint8_t freeMemory, uint8_t runtimeActivity,
uint8_t strongZero, unsigned width, LLVMTypeRef additionalArg,
CFnTypeInfo typeInfo, uint8_t subsequent_calls_may_write,
uint8_t *_overwritten_args, size_t overwritten_args_size,
EnzymeAugmentedReturnPtr augmented) {
SmallVector<DIFFE_TYPE, 4> nconstant_args((DIFFE_TYPE *)constant_args,
(DIFFE_TYPE *)constant_args +
constant_args_size);
std::vector<bool> overwritten_args;
assert(overwritten_args_size == cast<Function>(unwrap(todiff))->arg_size());
for (uint64_t i = 0; i < overwritten_args_size; i++) {
overwritten_args.push_back(_overwritten_args[i]);
}
return wrap(eunwrap(Logic).CreateForwardDiff(
RequestContext(cast_or_null<Instruction>(unwrap(request_req)),
unwrap(request_ip)),
cast<Function>(unwrap(todiff)), (DIFFE_TYPE)retType, nconstant_args,
eunwrap(TA), returnValue, (DerivativeMode)mode, freeMemory,
runtimeActivity, strongZero, width, unwrap(additionalArg),
eunwrap(typeInfo, cast<Function>(unwrap(todiff))),
subsequent_calls_may_write, overwritten_args, eunwrap(augmented)));
}
LLVMValueRef EnzymeCreatePrimalAndGradient(
EnzymeLogicRef Logic, LLVMValueRef request_req, LLVMBuilderRef request_ip,
LLVMValueRef todiff, CDIFFE_TYPE retType, CDIFFE_TYPE *constant_args,
size_t constant_args_size, EnzymeTypeAnalysisRef TA, uint8_t returnValue,
uint8_t dretUsed, CDerivativeMode mode, uint8_t runtimeActivity,
uint8_t strongZero, unsigned width, uint8_t freeMemory,
LLVMTypeRef additionalArg, uint8_t forceAnonymousTape, CFnTypeInfo typeInfo,
uint8_t subsequent_calls_may_write, uint8_t *_overwritten_args,
size_t overwritten_args_size, EnzymeAugmentedReturnPtr augmented,
uint8_t AtomicAdd) {
std::vector<DIFFE_TYPE> nconstant_args((DIFFE_TYPE *)constant_args,
(DIFFE_TYPE *)constant_args +
constant_args_size);
std::vector<bool> overwritten_args;
assert(overwritten_args_size == cast<Function>(unwrap(todiff))->arg_size());
for (uint64_t i = 0; i < overwritten_args_size; i++) {
overwritten_args.push_back(_overwritten_args[i]);
}
return wrap(eunwrap(Logic).CreatePrimalAndGradient(
RequestContext(cast_or_null<Instruction>(unwrap(request_req)),
unwrap(request_ip)),
(ReverseCacheKey){
.todiff = cast<Function>(unwrap(todiff)),
.retType = (DIFFE_TYPE)retType,
.constant_args = nconstant_args,
.subsequent_calls_may_write = (bool)subsequent_calls_may_write,
.overwritten_args = overwritten_args,
.returnUsed = (bool)returnValue,
.shadowReturnUsed = (bool)dretUsed,
.mode = (DerivativeMode)mode,
.width = width,
.freeMemory = (bool)freeMemory,
.AtomicAdd = (bool)AtomicAdd,
.additionalType = unwrap(additionalArg),
.forceAnonymousTape = (bool)forceAnonymousTape,
.typeInfo = eunwrap(typeInfo, cast<Function>(unwrap(todiff))),
.runtimeActivity = (bool)runtimeActivity,
.strongZero = (bool)strongZero},
eunwrap(TA), eunwrap(augmented)));
}
EnzymeAugmentedReturnPtr EnzymeCreateAugmentedPrimal(
EnzymeLogicRef Logic, LLVMValueRef request_req, LLVMBuilderRef request_ip,
LLVMValueRef todiff, CDIFFE_TYPE retType, CDIFFE_TYPE *constant_args,
size_t constant_args_size, EnzymeTypeAnalysisRef TA, uint8_t returnUsed,
uint8_t shadowReturnUsed, CFnTypeInfo typeInfo,
uint8_t subsequent_calls_may_write, uint8_t *_overwritten_args,
size_t overwritten_args_size, uint8_t forceAnonymousTape,
uint8_t runtimeActivity, uint8_t strongZero, unsigned width,
uint8_t AtomicAdd) {
SmallVector<DIFFE_TYPE, 4> nconstant_args((DIFFE_TYPE *)constant_args,
(DIFFE_TYPE *)constant_args +
constant_args_size);
std::vector<bool> overwritten_args;
assert(overwritten_args_size == cast<Function>(unwrap(todiff))->arg_size());
for (uint64_t i = 0; i < overwritten_args_size; i++) {
overwritten_args.push_back(_overwritten_args[i]);
}
auto F = cast<Function>(unwrap(todiff));
std::vector<bool> nowrite_shadows(F->arg_size(), false);
return ewrap(eunwrap(Logic).CreateAugmentedPrimal(
RequestContext(cast_or_null<Instruction>(unwrap(request_req)),
unwrap(request_ip)),
F, (DIFFE_TYPE)retType, nconstant_args,
eunwrap(TA), returnUsed, shadowReturnUsed,
eunwrap(typeInfo, F),
subsequent_calls_may_write, overwritten_args, nowrite_shadows,
forceAnonymousTape, runtimeActivity, strongZero, width, AtomicAdd));
}
LLVMValueRef EnzymeCreateBatch(EnzymeLogicRef Logic, LLVMValueRef request_req,
LLVMBuilderRef request_ip, LLVMValueRef tobatch,
unsigned width, CBATCH_TYPE *arg_types,
size_t arg_types_size, CBATCH_TYPE retType) {
return wrap(eunwrap(Logic).CreateBatch(
RequestContext(cast_or_null<Instruction>(unwrap(request_req)),
unwrap(request_ip)),
cast<Function>(unwrap(tobatch)), width,
ArrayRef<BATCH_TYPE>((BATCH_TYPE *)arg_types,
(BATCH_TYPE *)arg_types + arg_types_size),
(BATCH_TYPE)retType));
}
LLVMValueRef EnzymeCreateTrace(
EnzymeLogicRef Logic, LLVMValueRef request_req, LLVMBuilderRef request_ip,
LLVMValueRef totrace, LLVMValueRef *sample_functions,
size_t sample_functions_size, LLVMValueRef *observe_functions,
size_t observe_functions_size, const char *active_random_variables[],
size_t active_random_variables_size, CProbProgMode mode, uint8_t autodiff,
EnzymeTraceInterfaceRef interface) {
SmallPtrSet<Function *, 4> SampleFunctions;
for (size_t i = 0; i < sample_functions_size; i++) {
SampleFunctions.insert(cast<Function>(unwrap(sample_functions[i])));
}
SmallPtrSet<Function *, 4> ObserveFunctions;
for (size_t i = 0; i < observe_functions_size; i++) {
ObserveFunctions.insert(cast<Function>(unwrap(observe_functions[i])));
}
StringSet<> ActiveRandomVariables;
for (size_t i = 0; i < active_random_variables_size; i++) {
ActiveRandomVariables.insert(active_random_variables[i]);
}
return wrap(eunwrap(Logic).CreateTrace(
RequestContext(cast_or_null<Instruction>(unwrap(request_req)),
unwrap(request_ip)),
cast<Function>(unwrap(totrace)), SampleFunctions, ObserveFunctions,
ActiveRandomVariables, (ProbProgMode)mode, (bool)autodiff,
eunwrap(interface)));
}
LLVMValueRef
EnzymeExtractFunctionFromAugmentation(EnzymeAugmentedReturnPtr ret) {
auto AR = (AugmentedReturn *)ret;
return wrap(AR->fn);
}
LLVMTypeRef
EnzymeExtractUnderlyingTapeTypeFromAugmentation(EnzymeAugmentedReturnPtr ret) {
auto AR = (AugmentedReturn *)ret;
return wrap(AR->tapeType);
}
LLVMTypeRef
EnzymeExtractTapeTypeFromAugmentation(EnzymeAugmentedReturnPtr ret) {
auto AR = (AugmentedReturn *)ret;
auto found = AR->returns.find(AugmentedStruct::Tape);
if (found == AR->returns.end()) {
return wrap((Type *)nullptr);
}
if (found->second == -1) {
return wrap(AR->fn->getReturnType());
}
return wrap(
cast<StructType>(AR->fn->getReturnType())->getTypeAtIndex(found->second));
}
void EnzymeExtractReturnInfo(EnzymeAugmentedReturnPtr ret, int64_t *data,
uint8_t *existed, size_t len) {
assert(len == 3);
auto AR = (AugmentedReturn *)ret;
AugmentedStruct todo[] = {AugmentedStruct::Tape, AugmentedStruct::Return,
AugmentedStruct::DifferentialReturn};
for (size_t i = 0; i < len; i++) {
auto found = AR->returns.find(todo[i]);
if (found != AR->returns.end()) {
existed[i] = true;
data[i] = (int64_t)found->second;
} else {
existed[i] = false;
}
}
}
static MDNode *extractMDNode(MetadataAsValue *MAV) {
Metadata *MD = MAV->getMetadata();
assert((isa<MDNode>(MD) || isa<ConstantAsMetadata>(MD)) &&
"Expected a metadata node or a canonicalized constant");
if (MDNode *N = dyn_cast<MDNode>(MD))
return N;
return MDNode::get(MAV->getContext(), MD);
}
CTypeTreeRef EnzymeTypeTreeFromMD(LLVMValueRef Val) {
TypeTree *Ret = new TypeTree();
MDNode *N = Val ? extractMDNode(unwrap<MetadataAsValue>(Val)) : nullptr;
Ret->insertFromMD(N);
return (CTypeTreeRef)N;
}
LLVMValueRef EnzymeTypeTreeToMD(CTypeTreeRef CTR, LLVMContextRef ctx) {
auto MD = ((TypeTree *)CTR)->toMD(*unwrap(ctx));
return wrap(MetadataAsValue::get(MD->getContext(), MD));
}
CTypeTreeRef EnzymeNewTypeTree() { return (CTypeTreeRef)(new TypeTree()); }
CTypeTreeRef EnzymeNewTypeTreeCT(CConcreteType CT, LLVMContextRef ctx) {
return (CTypeTreeRef)(new TypeTree(eunwrap(CT, *unwrap(ctx))));
}
CTypeTreeRef EnzymeNewTypeTreeTR(CTypeTreeRef CTR) {
return (CTypeTreeRef)(new TypeTree(*(TypeTree *)(CTR)));
}
void EnzymeFreeTypeTree(CTypeTreeRef CTT) { delete (TypeTree *)CTT; }
uint8_t EnzymeSetTypeTree(CTypeTreeRef dst, CTypeTreeRef src) {
return *(TypeTree *)dst = *(TypeTree *)src;
}
uint8_t EnzymeMergeTypeTree(CTypeTreeRef dst, CTypeTreeRef src) {
return ((TypeTree *)dst)->orIn(*(TypeTree *)src, /*PointerIntSame*/ false);
}
uint8_t EnzymeCheckedMergeTypeTree(CTypeTreeRef dst, CTypeTreeRef src,
uint8_t *legalP) {
bool legal = true;
bool res =
((TypeTree *)dst)
->checkedOrIn(*(TypeTree *)src, /*PointerIntSame*/ false, legal);
*legalP = legal;
return res;
}
void EnzymeTypeTreeOnlyEq(CTypeTreeRef CTT, int64_t x) {
// TODO only inst
*(TypeTree *)CTT = ((TypeTree *)CTT)->Only(x, nullptr);
}
void EnzymeTypeTreeData0Eq(CTypeTreeRef CTT) {
*(TypeTree *)CTT = ((TypeTree *)CTT)->Data0();
}
void EnzymeTypeTreeLookupEq(CTypeTreeRef CTT, int64_t size, const char *dl) {
*(TypeTree *)CTT = ((TypeTree *)CTT)->Lookup(size, DataLayout(dl));
}
void EnzymeTypeTreeCanonicalizeInPlace(CTypeTreeRef CTT, int64_t size,
const char *dl) {
((TypeTree *)CTT)->CanonicalizeInPlace(size, DataLayout(dl));
}
CConcreteType EnzymeTypeTreeInner0(CTypeTreeRef CTT) {
return ewrap(((TypeTree *)CTT)->Inner0());
}
void EnzymeTypeTreeShiftIndiciesEq(CTypeTreeRef CTT, const char *datalayout,
int64_t offset, int64_t maxSize,
uint64_t addOffset) {
DataLayout DL(datalayout);
*(TypeTree *)CTT =
((TypeTree *)CTT)->ShiftIndices(DL, offset, maxSize, addOffset);
}
void EnzymeTypeTreeInsertEq(CTypeTreeRef CTT, const int64_t *indices,
size_t len, CConcreteType ct, LLVMContextRef ctx) {
std::vector<int> seq;
for (size_t i = 0; i < len; i++) {
seq.push_back(indices[i]);
}
((TypeTree *)CTT)->insert(seq, eunwrap(ct, *unwrap(ctx)));
}
const char *EnzymeTypeTreeToString(CTypeTreeRef src) {
std::string tmp = ((TypeTree *)src)->str();
char *cstr = new char[tmp.length() + 1];
std::strcpy(cstr, tmp.c_str());
return cstr;
}
// TODO deprecated
void EnzymeTypeTreeToStringFree(const char *cstr) { delete[] cstr; }
const char *EnzymeTypeAnalyzerToString(void *src) {
auto TA = (TypeAnalyzer *)src;
std::string str;
raw_string_ostream ss(str);
TA->dump(ss);
ss.str();
char *cstr = new char[str.length() + 1];
std::strcpy(cstr, str.c_str());
return cstr;
}
EnzymeLogicRef EnzymeTypeAnalyzerGetLogic(void *src) {
auto TA = (TypeAnalyzer *)src;
return (EnzymeLogicRef)&TA->interprocedural.Logic;
}
const char *EnzymeGradientUtilsInvertedPointersToString(GradientUtils *gutils,
void *src) {
std::string str;
raw_string_ostream ss(str);
for (auto z : gutils->invertedPointers) {
ss << "available inversion for " << *z.first << " of " << *z.second << "\n";
}
ss.str();
char *cstr = new char[str.length() + 1];
std::strcpy(cstr, str.c_str());
return cstr;
}
LLVMValueRef EnzymeGradientUtilsCallWithInvertedBundles(
GradientUtils *gutils, LLVMValueRef func, LLVMTypeRef funcTy,
LLVMValueRef *args_vr, uint64_t args_size, LLVMValueRef orig_vr,
CValueType *valTys, uint64_t valTys_size, LLVMBuilderRef B,
uint8_t lookup) {
auto orig = cast<CallInst>(unwrap(orig_vr));
ArrayRef<ValueType> ar((ValueType *)valTys, valTys_size);
IRBuilder<> &BR = *unwrap(B);
auto Defs = gutils->getInvertedBundles(orig, ar, BR, lookup != 0);
SmallVector<Value *, 1> args;
for (size_t i = 0; i < args_size; i++) {
args.push_back(unwrap(args_vr[i]));
}
auto callval = unwrap(func);
auto res =
BR.CreateCall(cast<FunctionType>(unwrap(funcTy)), callval, args, Defs);
return wrap(res);
}
void EnzymeStringFree(const char *cstr) { delete[] cstr; }
void EnzymeMoveBefore(LLVMValueRef inst1, LLVMValueRef inst2,
LLVMBuilderRef B) {
Instruction *I1 = cast<Instruction>(unwrap(inst1));
Instruction *I2 = cast<Instruction>(unwrap(inst2));
if (I1 != I2) {
if (B != nullptr) {
IRBuilder<> &BR = *unwrap(B);
if (I1->getIterator() == BR.GetInsertPoint()) {
if (I2->getNextNode() == nullptr)
BR.SetInsertPoint(I1->getParent());
else
BR.SetInsertPoint(I1->getNextNode());
}
}
I1->moveBefore(I2);
}
}
void EnzymeSetStringMD(LLVMValueRef Inst, const char *Kind, LLVMValueRef Val) {
MDNode *N = Val ? extractMDNode(unwrap<MetadataAsValue>(Val)) : nullptr;
Value *V = unwrap(Inst);
if (auto I = dyn_cast<Instruction>(V))
I->setMetadata(Kind, N);
else
cast<GlobalVariable>(V)->setMetadata(Kind, N);
}
LLVMValueRef EnzymeGetStringMD(LLVMValueRef Inst, const char *Kind) {
auto *I = unwrap<Instruction>(Inst);
assert(I && "Expected instruction");
if (auto *MD = I->getMetadata(Kind))
return wrap(MetadataAsValue::get(I->getContext(), MD));
return nullptr;
}
void EnzymeSetMustCache(LLVMValueRef inst1) {
Instruction *I1 = cast<Instruction>(unwrap(inst1));
I1->setMetadata("enzyme_mustcache", MDNode::get(I1->getContext(), {}));
}
uint8_t EnzymeHasFromStack(LLVMValueRef inst1) {
Instruction *I1 = cast<Instruction>(unwrap(inst1));
return hasMetadata(I1, "enzyme_fromstack") != 0;
}
void EnzymeCloneFunctionDISubprogramInto(LLVMValueRef NF, LLVMValueRef F) {
auto &OldFunc = *cast<Function>(unwrap(F));
auto &NewFunc = *cast<Function>(unwrap(NF));
auto OldSP = OldFunc.getSubprogram();
if (!OldSP)
return;
DIBuilder DIB(*OldFunc.getParent(), /*AllowUnresolved=*/false,
OldSP->getUnit());
auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray({}));
DISubprogram::DISPFlags SPFlags = DISubprogram::SPFlagDefinition |
DISubprogram::SPFlagOptimized |
DISubprogram::SPFlagLocalToUnit;
auto NewSP = DIB.createFunction(
OldSP->getUnit(), NewFunc.getName(), NewFunc.getName(), OldSP->getFile(),
/*LineNo=*/0, SPType, /*ScopeLine=*/0, DINode::FlagZero, SPFlags);
NewFunc.setSubprogram(NewSP);
DIB.finalizeSubprogram(NewSP);
return;
}
void EnzymeReplaceFunctionImplementation(LLVMModuleRef M) {
ReplaceFunctionImplementation(*unwrap(M));
}
void EnzymeDetectReadonlyOrThrow(LLVMModuleRef M) {
DetectReadonlyOrThrow(*unwrap(M));
}
void EnzymeDumpModuleRef(LLVMModuleRef M) {
llvm::errs() << *unwrap(M) << "\n";
}
void EnzymeDumpValueRef(LLVMValueRef M) { llvm::errs() << *unwrap(M) << "\n"; }
void EnzymeDumpTypeRef(LLVMTypeRef M) { llvm::errs() << *unwrap(M) << "\n"; }
static bool runAttributorOnFunctions(InformationCache &InfoCache,
SetVector<Function *> &Functions,
AnalysisGetter &AG,
CallGraphUpdater &CGUpdater,
bool DeleteFns, bool IsModulePass) {
if (Functions.empty())
return false;
// Create an Attributor and initially empty information cache that is filled
// while we identify default attribute opportunities.
AttributorConfig AC(CGUpdater);
AC.RewriteSignatures = false;
AC.IsModulePass = IsModulePass;
AC.DeleteFns = DeleteFns;
Attributor A(Functions, InfoCache, AC);
for (Function *F : Functions) {
// Populate the Attributor with abstract attribute opportunities in the
// function and the information cache with IR information.
A.identifyDefaultAbstractAttributes(*F);
}
ChangeStatus Changed = A.run();
return Changed == ChangeStatus::CHANGED;
}
extern "C" void RunAttributorOnModule(LLVMModuleRef M0) {
auto &M = *unwrap(M0);
AnalysisGetter AG;
SetVector<Function *> Functions;
for (Function &F : M)
Functions.insert(&F);
CallGraphUpdater CGUpdater;
BumpPtrAllocator Allocator;
InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ nullptr);
runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater,
/* DeleteFns*/ true,
/* IsModulePass */ true);
}
struct MyAttributorLegacyPass : public ModulePass {
static char ID;
MyAttributorLegacyPass() : ModulePass(ID) {}
bool runOnModule(Module &M) override {
if (skipModule(M))
return false;
AnalysisGetter AG;
SetVector<Function *> Functions;
for (Function &F : M)
Functions.insert(&F);
CallGraphUpdater CGUpdater;
BumpPtrAllocator Allocator;
InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ nullptr);
return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater,
/* DeleteFns*/ true,
/* IsModulePass */ true);
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
// FIXME: Think about passes we will preserve and add them here.
AU.addRequired<TargetLibraryInfoWrapperPass>();
}
};
extern "C++" char MyAttributorLegacyPass::ID = 0;
void EnzymeAddAttributorLegacyPass(LLVMPassManagerRef PM) {
unwrap(PM)->add(new MyAttributorLegacyPass());
}
LLVMMetadataRef EnzymeMakeNonConstTBAA(LLVMMetadataRef MD) {
auto M = cast<MDNode>(unwrap(MD));
if (M->getNumOperands() != 4)
return MD;
auto CAM = dyn_cast<ConstantAsMetadata>(M->getOperand(3));
if (!CAM)
return MD;
if (!CAM->getValue()->isOneValue())
return MD;
SmallVector<Metadata *, 4> MDs;
for (auto &M : M->operands())
MDs.push_back(M);
MDs[3] =
ConstantAsMetadata::get(ConstantInt::get(CAM->getValue()->getType(), 0));
return wrap(MDNode::get(M->getContext(), MDs));
}
void EnzymeCopyMetadata(LLVMValueRef inst1, LLVMValueRef inst2) {
cast<Instruction>(unwrap(inst1))
->copyMetadata(*cast<Instruction>(unwrap(inst2)));
}
void EnzymeCopyAlignment(LLVMValueRef inst1, LLVMValueRef inst2) {
cast<AllocaInst>(unwrap(inst1))
->setAlignment(cast<AllocaInst>(unwrap(inst2))->getAlign());
}
void EnzymeTakeName(LLVMValueRef inst1, LLVMValueRef inst2) {
unwrap(inst1)->takeName(unwrap(inst2));
}
LLVMMetadataRef EnzymeAnonymousAliasScopeDomain(const char *str,
LLVMContextRef ctx) {
MDBuilder MDB(*unwrap(ctx));
MDNode *scope = MDB.createAnonymousAliasScopeDomain(str);
return wrap(scope);
}
LLVMMetadataRef EnzymeAnonymousAliasScope(LLVMMetadataRef domain,
const char *str) {
auto dom = cast<MDNode>(unwrap(domain));
MDBuilder MDB(dom->getContext());
MDNode *scope = MDB.createAnonymousAliasScope(dom, str);
return wrap(scope);
}
uint8_t EnzymeLowerSparsification(LLVMValueRef F, uint8_t replaceAll) {
return LowerSparsification(cast<Function>(unwrap(F)), replaceAll != 0);
}
void EnzymeAttributeKnownFunctions(LLVMValueRef FC) {
attributeKnownFunctions(*cast<Function>(unwrap(FC)));
}
void EnzymeSetCalledFunction(LLVMValueRef C_CI, LLVMValueRef C_F,
uint64_t *argrem, uint64_t num_argrem) {
auto CI = cast<CallInst>(unwrap(C_CI));
auto F = cast<Function>(unwrap(C_F));
auto Attrs = CI->getAttributes();
AttributeList NewAttrs;
if (CI->getType() == F->getReturnType()) {
for (auto attr : Attrs.getAttributes(AttributeList::ReturnIndex))
NewAttrs = NewAttrs.addAttribute(F->getContext(),
AttributeList::ReturnIndex, attr);
}
for (auto attr : Attrs.getAttributes(AttributeList::FunctionIndex))
NewAttrs = NewAttrs.addAttribute(F->getContext(),
AttributeList::FunctionIndex, attr);
size_t argremsz = 0;
size_t nexti = 0;
SmallVector<Value *, 1> vals;
for (size_t i = 0, end = CI->arg_size(); i < end; i++) {
if (argremsz < num_argrem) {
if (i == argrem[argremsz]) {
argremsz++;
continue;
}
}
for (auto attr : Attrs.getAttributes(AttributeList::FirstArgIndex + i))
NewAttrs = NewAttrs.addAttribute(
F->getContext(), AttributeList::FirstArgIndex + nexti, attr);
vals.push_back(CI->getArgOperand(i));
nexti++;
}
assert(argremsz == num_argrem);
IRBuilder<> B(CI);
SmallVector<OperandBundleDef, 1> Bundles;
for (unsigned I = 0, E = CI->getNumOperandBundles(); I != E; ++I)
Bundles.emplace_back(CI->getOperandBundleAt(I));
auto NC = B.CreateCall(F, vals, Bundles);
NC->setAttributes(NewAttrs);
NC->copyMetadata(*CI);
if (CI->getType() == F->getReturnType())
CI->replaceAllUsesWith(NC);
if (!NC->getType()->isVoidTy())
NC->takeName(CI);
NC->setCallingConv(CI->getCallingConv());
CI->eraseFromParent();
}
// clones a function to now miss the return or args
LLVMValueRef EnzymeCloneFunctionWithoutReturnOrArgs(LLVMValueRef FC,
uint8_t keepReturnU,
uint64_t *argrem,
uint64_t num_argrem) {
auto F = cast<Function>(unwrap(FC));
auto FT = F->getFunctionType();
bool keepReturn = keepReturnU != 0;
size_t argremsz = 0;
size_t nexti = 0;
SmallVector<Type *, 1> types;
auto Attrs = F->getAttributes();
AttributeList NewAttrs;
for (size_t i = 0, end = FT->getNumParams(); i < end; i++) {
if (argremsz < num_argrem) {
if (i == argrem[argremsz]) {
argremsz++;
continue;
}
}
for (auto attr : Attrs.getAttributes(AttributeList::FirstArgIndex + i))
NewAttrs = NewAttrs.addAttribute(
F->getContext(), AttributeList::FirstArgIndex + nexti, attr);
types.push_back(F->getFunctionType()->getParamType(i));
nexti++;
}
if (keepReturn) {
for (auto attr : Attrs.getAttributes(AttributeList::ReturnIndex))
NewAttrs = NewAttrs.addAttribute(F->getContext(),
AttributeList::ReturnIndex, attr);
}
for (auto attr : Attrs.getAttributes(AttributeList::FunctionIndex))
NewAttrs = NewAttrs.addAttribute(F->getContext(),
AttributeList::FunctionIndex, attr);
FunctionType *FTy = FunctionType::get(
keepReturn ? F->getReturnType() : Type::getVoidTy(F->getContext()), types,
FT->isVarArg());
// Create the new function
Function *NewF = Function::Create(FTy, F->getLinkage(), F->getAddressSpace(),
F->getName(), F->getParent());
ValueToValueMapTy VMap;
// Loop over the arguments, copying the names of the mapped arguments over...
nexti = 0;
argremsz = 0;
Function::arg_iterator DestI = NewF->arg_begin();
for (const Argument &I : F->args()) {
if (argremsz < num_argrem) {
if (I.getArgNo() == argrem[argremsz]) {
VMap[&I] = UndefValue::get(I.getType());
argremsz++;
continue;
}
}
DestI->setName(I.getName()); // Copy the name over...
VMap[&I] = &*DestI++; // Add mapping to VMap
}
SmallVector<ReturnInst *, 8> Returns; // Ignore returns cloned.
CloneFunctionInto(NewF, F, VMap, CloneFunctionChangeType::LocalChangesOnly,
Returns, "", nullptr);
if (!keepReturn) {
for (auto &B : *NewF) {
if (auto RI = dyn_cast<ReturnInst>(B.getTerminator())) {
IRBuilder<> B(RI);
auto NRI = B.CreateRetVoid();
NRI->copyMetadata(*RI);
RI->eraseFromParent();
}
}
}
NewF->setAttributes(NewAttrs);
if (!keepReturn)
for (auto &Arg : NewF->args())
Arg.removeAttr(Attribute::Returned);
SmallVector<std::pair<unsigned, MDNode *>, 1> MD;
F->getAllMetadata(MD);
for (auto pair : MD)
if (pair.first != LLVMContext::MD_dbg)
NewF->addMetadata(pair.first, *pair.second);
NewF->takeName(F);
NewF->setCallingConv(F->getCallingConv());
if (!keepReturn)
NewF->addFnAttr("enzyme_retremove", "");
if (num_argrem) {
SmallVector<uint64_t, 1> previdx;
if (Attrs.hasAttribute(AttributeList::FunctionIndex, "enzyme_parmremove")) {
auto attr =
Attrs.getAttribute(AttributeList::FunctionIndex, "enzyme_parmremove");
auto prevstr = attr.getValueAsString();
SmallVector<StringRef, 1> sub;
prevstr.split(sub, ",");
for (auto s : sub) {
uint64_t ival;
bool b = s.getAsInteger(10, ival);
(void)b;
assert(!b);
previdx.push_back(ival);
}
}
SmallVector<uint64_t, 1> nextidx;
for (size_t i = 0; i < num_argrem; i++) {
auto val = argrem[i];
nextidx.push_back(val);
}
size_t prevcnt = 0;
size_t nextcnt = 0;
SmallVector<uint64_t, 1> out;
while (prevcnt < previdx.size() && nextcnt < nextidx.size()) {
if (previdx[prevcnt] <= nextidx[nextcnt] + prevcnt) {
out.push_back(previdx[prevcnt]);
prevcnt++;
} else {
out.push_back(nextidx[nextcnt] + prevcnt);
nextcnt++;
}
}
while (prevcnt < previdx.size()) {
out.push_back(previdx[prevcnt]);
prevcnt++;
}
while (nextcnt < nextidx.size()) {
out.push_back(nextidx[nextcnt] + prevcnt);
nextcnt++;
}
std::string remstr;
for (auto arg : out) {
if (remstr.size())
remstr += ",";
remstr += std::to_string(arg);
}
NewF->addFnAttr("enzyme_parmremove", remstr);
}
return wrap(NewF);
}
LLVMTypeRef EnzymeAllocaType(LLVMValueRef V) {
return wrap(cast<AllocaInst>(unwrap(V))->getAllocatedType());
}
LLVMValueRef EnzymeComputeByteOffsetOfGEP(LLVMBuilderRef B_r, LLVMValueRef V_r,
LLVMTypeRef T_r) {
IRBuilder<> &B = *unwrap(B_r);
auto T = cast<IntegerType>(unwrap(T_r));
auto width = T->getBitWidth();
auto uw = unwrap(V_r);
GEPOperator *gep = isa<GetElementPtrInst>(uw)
? cast<GEPOperator>(cast<GetElementPtrInst>(uw))
: cast<GEPOperator>(cast<ConstantExpr>(uw));
auto &DL = B.GetInsertBlock()->getParent()->getParent()->getDataLayout();
#if LLVM_VERSION_MAJOR >= 20
SmallMapVector<Value *, APInt, 4> VariableOffsets;
#else
MapVector<Value *, APInt> VariableOffsets;
#endif
APInt Offset(width, 0);
bool success = collectOffset(gep, DL, width, VariableOffsets, Offset);
(void)success;
assert(success);
Value *start = ConstantInt::get(T, Offset);
for (auto &pair : VariableOffsets)
start = B.CreateAdd(
start, B.CreateMul(pair.first, ConstantInt::get(T, pair.second)));
return wrap(start);
}
}
extern "C" {
LLVMValueRef EnzymeBuildExtractValue(LLVMBuilderRef B, LLVMValueRef AggVal,
unsigned *Index, unsigned Size,
const char *Name) {
return wrap(unwrap(B)->CreateExtractValue(
unwrap(AggVal), ArrayRef<unsigned>(Index, Size), Name));
}
LLVMValueRef EnzymeBuildInsertValue(LLVMBuilderRef B, LLVMValueRef AggVal,
LLVMValueRef EltVal, unsigned *Index,
unsigned Size, const char *Name) {
return wrap(unwrap(B)->CreateInsertValue(
unwrap(AggVal), unwrap(EltVal), ArrayRef<unsigned>(Index, Size), Name));
}
}